@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.
- package/README.md +19 -0
- package/dist/sdk/client/access-key.d.ts +1 -1
- package/dist/sdk/client/access-key.js +20 -6
- package/dist/sdk/client/create-flow.js +19 -11
- package/dist/sdk/client/dialog-host.js +8 -2
- package/dist/sdk/client/flow-widget.js +2 -1
- package/dist/sdk/client/idb.d.ts +2 -2
- package/dist/sdk/client/idb.js +17 -2
- package/dist/sdk/client/profile-button.js +1 -1
- package/dist/sdk/client/rooms.js +19 -4
- package/dist/sdk/client/session.js +8 -5
- package/dist/sdk/client/signing.js +22 -7
- package/dist/sdk/client/store.js +4 -5
- package/dist/sdk/cookies.d.ts +3 -1
- package/dist/sdk/cookies.js +11 -9
- package/dist/sdk/dialog/remote/Messenger.d.ts +0 -5
- package/dist/sdk/dialog/remote/Messenger.js +26 -14
- package/dist/sdk/driver-error.d.ts +8 -0
- package/dist/sdk/driver-error.js +17 -0
- package/dist/sdk/hex.d.ts +6 -0
- package/dist/sdk/hex.js +6 -0
- package/dist/sdk/id-host.d.ts +1 -1
- package/dist/sdk/id-host.js +4 -5
- package/dist/sdk/json.d.ts +15 -0
- package/dist/sdk/json.js +3 -0
- package/dist/sdk/react/flow-widget.js +2 -2
- package/dist/sdk/react/hooks.d.ts +8 -4
- package/dist/sdk/react/hooks.js +1 -10
- package/dist/sdk/react/profile-button.js +2 -2
- package/dist/sdk/server.js +2 -1
- package/dist/sdk/session-core.d.ts +1 -6
- package/dist/sdk/session-core.js +7 -4
- package/dist/sdk/session-route.d.ts +0 -20
- package/dist/sdk/session-route.js +25 -9
- package/dist/sdk/start/graceful-shutdown.d.ts +8 -0
- package/dist/sdk/start/graceful-shutdown.js +81 -0
- package/dist/sdk/start/index.d.ts +2 -0
- package/dist/sdk/start/index.js +3 -2
- package/dist/sdk/token-expiry.js +3 -1
- package/dist/sdk/types/cosmetics.d.ts +162 -0
- package/dist/sdk/types/cosmetics.js +78 -0
- package/dist/sdk/types/events.d.ts +4 -1
- package/dist/sdk/types/game.d.ts +58 -0
- package/dist/sdk/types/game.js +8 -0
- package/dist/sdk/types/index.d.ts +8 -4
- package/dist/sdk/types/index.js +2 -0
- package/dist/sdk/types/messenger.d.ts +1 -1
- package/dist/sdk/types/protocol.d.ts +6 -5
- package/dist/sdk/types/protocol.js +10 -4
- package/dist/sdk/types/room-events.d.ts +49 -1
- package/dist/sdk/types/rooms.d.ts +66 -3
- package/dist/sdk/types/rooms.js +0 -1
- package/dist/sdk/types/sdk.d.ts +2 -1
- package/dist/sdk/types/server.d.ts +18 -0
- package/dist/sdk/types/xp.d.ts +8 -7
- package/dist/sdk/verify.js +1 -0
- package/dist/sdk/wagmi/index.d.ts +7 -3
- package/dist/sdk/wagmi/index.js +19 -32
- package/package.json +15 -8
- package/dist/sdk/client/refresh-store.d.ts +0 -22
- package/dist/sdk/client/refresh-store.js +0 -66
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cosmetic items, the space they occupy on the model, and the equipped set a
|
|
3
|
+
* game server receives on `/api/session/verify`.
|
|
4
|
+
*
|
|
5
|
+
* Two region vocabularies live here and must never be collapsed into one:
|
|
6
|
+
* {@link EquipRegion} is *item vs item* (can these be worn together), while
|
|
7
|
+
* {@link BodyRegion} is *cosmetic vs body* (what geometry must stop drawing so
|
|
8
|
+
* nothing pokes through). TF2 keeps them separate — equip regions and
|
|
9
|
+
* bodygroups — and conflating them is what produces the clipping bugs.
|
|
10
|
+
*
|
|
11
|
+
* {@link CosmeticMaterial} is a third vocabulary and belongs to neither: it
|
|
12
|
+
* names a *surface* of the model rather than a space on it, and it is what a
|
|
13
|
+
* colour is applied to.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Every body area a cosmetic may suppress — the bodygroup half's whole
|
|
17
|
+
* vocabulary, head down.
|
|
18
|
+
*
|
|
19
|
+
* SOURCE OF TRUTH: the `regions` keys of
|
|
20
|
+
* `game/games/arena/player/luna_bodygroups.tres`, which is authored against
|
|
21
|
+
* the shipped model. This is a mirror, kept as a constant so auth can resolve
|
|
22
|
+
* a loadout without reaching into the game repo at runtime.
|
|
23
|
+
*
|
|
24
|
+
* WHEN THE MODEL IS RE-CUT: update this list to the new `regions` keys, then
|
|
25
|
+
* fix whatever `hides` in the catalog no longer resolves. `cosmetics.test.ts`
|
|
26
|
+
* fails on the first entry that names a region absent here, and the type below
|
|
27
|
+
* makes the same mistake a typecheck error — which is the whole point of
|
|
28
|
+
* mirroring the names into one constant. AUTH-210 is what happens without it:
|
|
29
|
+
* the catalog was written from a nine-region proposal in
|
|
30
|
+
* `docs/internal/cosmetics/bodygroups.md` section 6, the fifteen-region cut
|
|
31
|
+
* that actually shipped shared not one name with it, and every cosmetic
|
|
32
|
+
* silently suppressed nothing.
|
|
33
|
+
*/
|
|
34
|
+
export const BODY_REGIONS = [
|
|
35
|
+
"head",
|
|
36
|
+
"ears",
|
|
37
|
+
"hair",
|
|
38
|
+
"body_neck",
|
|
39
|
+
"body_chest",
|
|
40
|
+
"body_arm_upper_l",
|
|
41
|
+
"body_arm_upper_r",
|
|
42
|
+
"body_arm_lower_l",
|
|
43
|
+
"body_arm_lower_r",
|
|
44
|
+
"body_hand_l",
|
|
45
|
+
"body_hand_r",
|
|
46
|
+
"body_pelvis",
|
|
47
|
+
"body_leg_upper_l",
|
|
48
|
+
"body_leg_upper_r",
|
|
49
|
+
"body_leg_lower_l",
|
|
50
|
+
"body_leg_lower_r",
|
|
51
|
+
"body_foot_l",
|
|
52
|
+
"body_foot_r",
|
|
53
|
+
];
|
|
54
|
+
/**
|
|
55
|
+
* Every material the shipped model set exposes, body and cosmetic alike — the
|
|
56
|
+
* surfaces a colour can be applied to.
|
|
57
|
+
*
|
|
58
|
+
* SOURCE OF TRUTH: the glTF material names inside
|
|
59
|
+
* `game/games/arena/resources/models/LUNA12.7.glb` (`skin`, `brownhair`,
|
|
60
|
+
* `tshirt`, `shorts`) and the per-cosmetic GLBs beside it
|
|
61
|
+
* (`cosmetics/LUNA_boots.glb` names `Shoes`, `cosmetics/LUNA_glasses.glb`
|
|
62
|
+
* names `outline`). `Shoes` is capitalised because the model capitalises it: a
|
|
63
|
+
* tidied-up copy here would name a material no renderer can find, and the
|
|
64
|
+
* paint would silently land nowhere.
|
|
65
|
+
*
|
|
66
|
+
* WHEN THE MODEL IS RE-CUT: update this list to the new material names, then
|
|
67
|
+
* fix whatever `paintable` in the catalog no longer resolves. This is the same
|
|
68
|
+
* mirror-and-guard {@link BODY_REGIONS} is, kept for the same reason —
|
|
69
|
+
* AUTH-210 is what a hardcoded copy with no test looks like a month later.
|
|
70
|
+
*/
|
|
71
|
+
export const COSMETIC_MATERIALS = [
|
|
72
|
+
"skin",
|
|
73
|
+
"brownhair",
|
|
74
|
+
"tshirt",
|
|
75
|
+
"shorts",
|
|
76
|
+
"Shoes",
|
|
77
|
+
"outline",
|
|
78
|
+
];
|
|
@@ -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" | "room_event.activated" | "room_event.completed" | "room_event.reminder_dispatched" | "room_event.reminder_failed";
|
|
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" | "room.restriction.issued" | "room.restriction.lifted" | "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 {
|
|
@@ -32,6 +32,9 @@ export interface AuthEventRecord {
|
|
|
32
32
|
actorId: string | null;
|
|
33
33
|
roleFrom: GlobalRole | null;
|
|
34
34
|
roleTo: GlobalRole | null;
|
|
35
|
+
roomId: string | null;
|
|
36
|
+
restrictionKind: string | null;
|
|
37
|
+
restrictionExpiresAt: string | null;
|
|
35
38
|
ip: string | null;
|
|
36
39
|
country: string | null;
|
|
37
40
|
userAgent: string | null;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { RawRecord } from "../json";
|
|
2
|
+
/**
|
|
3
|
+
* Game-server lifecycle telemetry. The same record shape is POSTed to the
|
|
4
|
+
* OpenObserve `game_events` stream (ops, TTL'd) and to this server's durable
|
|
5
|
+
* ingest (`POST /api/game/events`), so one emitter feeds both and a new event
|
|
6
|
+
* kind needs no wire change here.
|
|
7
|
+
*/
|
|
8
|
+
/** One lifecycle record as the game server reports it. `sessionId` pairs a
|
|
9
|
+
* player's join with their leave, which is what makes time-present a self-join
|
|
10
|
+
* instead of a guess about reused peer ids. Anything not named here rides in
|
|
11
|
+
* `detail`. */
|
|
12
|
+
export interface GameEventReport {
|
|
13
|
+
/** The reporter's idempotency key. Resending a batch with stable ids is a
|
|
14
|
+
* no-op; omit it and the server mints one, making the retry a duplicate. */
|
|
15
|
+
id?: string;
|
|
16
|
+
event: string;
|
|
17
|
+
ts: string;
|
|
18
|
+
serverId: string;
|
|
19
|
+
sessionId?: string;
|
|
20
|
+
roomId?: string;
|
|
21
|
+
roomSlug?: string;
|
|
22
|
+
sub?: string;
|
|
23
|
+
reason?: string;
|
|
24
|
+
detail?: RawRecord;
|
|
25
|
+
}
|
|
26
|
+
/** A player's stay in a room, reconstructed by pairing the join and leave rows
|
|
27
|
+
* that share a `sessionId`. A same-server shutdown closes a missing leave;
|
|
28
|
+
* `leftAt` remains null only while the session is open or when neither closing
|
|
29
|
+
* record survived. */
|
|
30
|
+
export interface GamePlayerSession {
|
|
31
|
+
sessionId: string;
|
|
32
|
+
userId: string;
|
|
33
|
+
roomId: string | null;
|
|
34
|
+
roomSlug: string | null;
|
|
35
|
+
joinedAt: string;
|
|
36
|
+
leftAt: string | null;
|
|
37
|
+
reason: string | null;
|
|
38
|
+
}
|
|
39
|
+
export declare const GAME_LIFECYCLE_EVENT_NAMES: readonly ["game.server.boot", "game.server.shutdown", "game.room.open", "game.room.close", "game.player.join", "game.player.leave"];
|
|
40
|
+
export type GameLifecycleEventName = (typeof GAME_LIFECYCLE_EVENT_NAMES)[number];
|
|
41
|
+
/** Product-safe projection of a durable lifecycle record. Raw `detail` is
|
|
42
|
+
* intentionally absent from this service-to-service contract. */
|
|
43
|
+
export interface GameLifecycleFeedEvent {
|
|
44
|
+
id: string;
|
|
45
|
+
event: GameLifecycleEventName;
|
|
46
|
+
occurredAt: string;
|
|
47
|
+
serverId: string;
|
|
48
|
+
sessionId: string | null;
|
|
49
|
+
roomId: string | null;
|
|
50
|
+
roomSlug: string | null;
|
|
51
|
+
userId: string | null;
|
|
52
|
+
reason: string | null;
|
|
53
|
+
}
|
|
54
|
+
export interface GameLifecycleFeedPage {
|
|
55
|
+
events: GameLifecycleFeedEvent[];
|
|
56
|
+
nextCursor: string;
|
|
57
|
+
hasMore: boolean;
|
|
58
|
+
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
export type { AccountSession, AccountSessionsResponse, AdditionalSession, AuthConfig, AuthResponse, AuthResponseWithWebAuthn, FlowCredential, FlowUser, PasskeyPluginOptions, RevokeAllSessionsResponse, RevokeSessionResponse, SecurityActivityEntry, SecurityActivityResponse, SessionUser, VerifiedFlowJWT, VerifyOptions, WebAuthnSignature, } from "./auth";
|
|
2
|
+
export type { BodyRegion, CosmeticItem, CosmeticMaterial, CosmeticPaint, CosmeticSlot, EquipConflict, EquippedCosmetic, EquippedCosmetics, EquipRegion, PaintSlot, SettingsConflict, } from "./cosmetics";
|
|
3
|
+
export { BODY_REGIONS, COSMETIC_MATERIALS } from "./cosmetics";
|
|
2
4
|
export type { BoundaryError, DialogCustomFeatures, DialogCustomLabels, DialogError, DialogReferrer, DialogState, Loadable, ProfileData, ProfileIdentity, } from "./dialog";
|
|
3
5
|
export type { AuthErrorCode, AuthEventName, AuthEventRecord, AuthMode, AuthOutcome, BeaconBody, FunnelStep, } from "./events";
|
|
6
|
+
export type { GameEventReport, GameLifecycleEventName, GameLifecycleFeedEvent, GameLifecycleFeedPage, GamePlayerSession, } from "./game";
|
|
7
|
+
export { GAME_LIFECYCLE_EVENT_NAMES } from "./game";
|
|
4
8
|
export type { Bridge, BridgeParameters, FlowAccount, FlowRemote, FlowRemoteConfig, FromWindowOptions, MessageResponse, Messenger, OneOf, Payload, QueuedRequest, ReadyOptions, RemoteFlowState, RemoteState, Schema, Storage, Topic, WithReady, } from "./messenger";
|
|
5
9
|
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
10
|
export { getConnectCapabilities, isPersonalSignParams, isSendCallsParams, isSendTransactionParams, } from "./protocol";
|
|
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";
|
|
11
|
+
export type { CreateRoomEventInput, RoomEventArchive, RoomEventAttendance, RoomEventFrequency, RoomEventInstance, RoomEventInterest, RoomEventInterestState, RoomEventList, RoomEventOccurrence, RoomEventRecurrence, RoomEventRoomRef, RoomEventRun, RoomEventSeries, RoomEventStatus, SetInterestInput, UpdateRoomEventInput, } from "./room-events";
|
|
12
|
+
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";
|
|
9
13
|
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";
|
|
10
|
-
export type { ResolvedFlowSession, ResolveSessionOptions, SessionRouteOptions, SessionRouteResponse, SessionRouteSession, } from "./server";
|
|
14
|
+
export type { GracefulShutdownCleanup, GracefulShutdownOptions, GracefulShutdownServer, GracefulShutdownSignal, ResolvedFlowSession, ResolveSessionOptions, SessionRouteOptions, SessionRouteResponse, SessionRouteSession, } from "./server";
|
|
11
15
|
export type { CoinAsset, IdentifiedTx, TxApprove, TxConvert, TxSend, TxSwap, } from "./tx";
|
|
12
|
-
export type { ActionDayContext, ActionEventKind, ActionSessionState, ActiveActionResponse, LevelProgress, PublicProfile, XpGrantResult, XpRecentGrant, XpSummary, } from "./xp";
|
|
16
|
+
export type { ActionDayContext, ActionEventKind, ActionSessionState, ActionTransitionKind, ActiveActionResponse, LevelProgress, PublicProfile, XpGrantResult, XpRecentGrant, XpSummary, } from "./xp";
|
package/dist/sdk/types/index.js
CHANGED
|
@@ -166,7 +166,7 @@ export type RemoteState = {
|
|
|
166
166
|
export type Storage = {
|
|
167
167
|
getItem: <value>(name: string) => Promise<value | null> | value | null;
|
|
168
168
|
removeItem: (name: string) => Promise<void> | void;
|
|
169
|
-
setItem: (name: string, value:
|
|
169
|
+
setItem: <value>(name: string, value: value) => Promise<void> | void;
|
|
170
170
|
sizeLimit: number;
|
|
171
171
|
storages?: readonly Storage[];
|
|
172
172
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RawRecord, RawValue } from "../json";
|
|
1
2
|
import type { AdditionalSession, FlowCredential, FlowUser, WebAuthnSignature } from "./auth";
|
|
2
3
|
import type { Address } from "./sdk";
|
|
3
4
|
export type MethodName = "wallet_connect" | "personal_sign" | "eth_signTypedData" | "eth_sendTransaction" | "wallet_sendCalls" | "wallet_guest" | "wallet_signout";
|
|
@@ -51,7 +52,7 @@ export type TypedData = {
|
|
|
51
52
|
domain: TypedDataDomain;
|
|
52
53
|
types: Record<string, readonly TypedDataField[]>;
|
|
53
54
|
primaryType: string;
|
|
54
|
-
message:
|
|
55
|
+
message: RawRecord;
|
|
55
56
|
};
|
|
56
57
|
export type SignTypedDataRequest = [address: string, typedData: TypedData];
|
|
57
58
|
export type SignTypedDataResponse = `0x${string}`;
|
|
@@ -69,7 +70,7 @@ export type SendCallsRequest = [
|
|
|
69
70
|
{
|
|
70
71
|
calls: readonly Call[];
|
|
71
72
|
chainId?: string;
|
|
72
|
-
capabilities?:
|
|
73
|
+
capabilities?: RawRecord;
|
|
73
74
|
}
|
|
74
75
|
];
|
|
75
76
|
export type SendCallsResponse = {
|
|
@@ -123,9 +124,9 @@ export type MethodResult = {
|
|
|
123
124
|
export type RpcRequest = {
|
|
124
125
|
id: string | number;
|
|
125
126
|
method: string;
|
|
126
|
-
params?: readonly
|
|
127
|
+
params?: readonly RawValue[];
|
|
127
128
|
jsonrpc?: string;
|
|
128
|
-
[key: string]:
|
|
129
|
+
[key: string]: RawValue | readonly RawValue[];
|
|
129
130
|
};
|
|
130
131
|
export type SendTransactionParams = {
|
|
131
132
|
to?: string;
|
|
@@ -140,7 +141,7 @@ export type SendCallsParams = {
|
|
|
140
141
|
data?: string;
|
|
141
142
|
}[];
|
|
142
143
|
chainId?: string;
|
|
143
|
-
capabilities?:
|
|
144
|
+
capabilities?: RawRecord;
|
|
144
145
|
};
|
|
145
146
|
/**
|
|
146
147
|
* Unwraps the connect capabilities from a wallet_connect RPC request (they ride
|
|
@@ -4,22 +4,28 @@
|
|
|
4
4
|
* accessKeyHash, guestToken), so the cast lives here rather than being repeated.
|
|
5
5
|
*/
|
|
6
6
|
export function getConnectCapabilities(request) {
|
|
7
|
-
|
|
7
|
+
const first = request?.params?.[0];
|
|
8
|
+
const found = Object.entries(Object(first)).find(([k]) => k === "capabilities")?.[1];
|
|
9
|
+
/* SAFETY: the field is read off this app's own wallet_connect params, whose capabilities
|
|
10
|
+
block is defined by ConnectCapabilities; a missing one answers undefined. */
|
|
11
|
+
return found;
|
|
8
12
|
}
|
|
9
13
|
export function isPersonalSignParams(params) {
|
|
10
14
|
if (!params || params.length < 2)
|
|
11
15
|
return false;
|
|
12
|
-
return
|
|
16
|
+
return String(params[0]) === params[0] && String(params[1]) === params[1];
|
|
13
17
|
}
|
|
14
18
|
export function isSendTransactionParams(params) {
|
|
15
19
|
if (!params || params.length < 1)
|
|
16
20
|
return false;
|
|
17
21
|
const p = params[0];
|
|
18
|
-
return p != null &&
|
|
22
|
+
return p != null && Object(p) === p;
|
|
19
23
|
}
|
|
20
24
|
export function isSendCallsParams(params) {
|
|
21
25
|
if (!params || params.length < 1)
|
|
22
26
|
return false;
|
|
23
27
|
const p = params[0];
|
|
24
|
-
|
|
28
|
+
if (p == null || Object(p) !== p)
|
|
29
|
+
return false;
|
|
30
|
+
return Array.isArray(Object.entries(p).find(([k]) => k === "calls")?.[1]);
|
|
25
31
|
}
|
|
@@ -66,12 +66,50 @@ export interface RoomEventInstance extends RoomEventInterest {
|
|
|
66
66
|
/** Written once the instance completes — the history view reads these rather
|
|
67
67
|
* than scanning presence. Null while it has not finished. */
|
|
68
68
|
attendance: RoomEventAttendance | null;
|
|
69
|
+
/** When the instance actually ran. Null until something has happened to it.
|
|
70
|
+
* This — not `attendance` — is the authoritative answer to "did this run, and
|
|
71
|
+
* when". */
|
|
72
|
+
run: RoomEventRun | null;
|
|
69
73
|
}
|
|
70
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* The authoritative run window, stamped by the scheduler at the instant of each
|
|
76
|
+
* transition.
|
|
77
|
+
*
|
|
78
|
+
* `activatedAt` is null on an occurrence the worker slept through and caught up
|
|
79
|
+
* in one step (scheduled straight to completed): nothing observed it start, so
|
|
80
|
+
* nothing claims to know. Rows that completed before this was recorded are null
|
|
81
|
+
* on all three and cannot be backfilled — the instants were never captured.
|
|
82
|
+
*/
|
|
83
|
+
export interface RoomEventRun {
|
|
84
|
+
activatedAt: string | null;
|
|
85
|
+
completedAt: string | null;
|
|
86
|
+
cancelledAt: string | null;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* When players were in the room, derived from game lifecycle edges and merged
|
|
90
|
+
* with presence samples where reporting gaps remain.
|
|
91
|
+
*
|
|
92
|
+
* **This is not the run window.** These instants are the first and last player
|
|
93
|
+
* *seen*; an occurrence that ran for two hours with one player who left after
|
|
94
|
+
* fifteen minutes reports fifteen minutes here, and one nobody attended reports
|
|
95
|
+
* null for both while still having genuinely run. Read {@link RoomEventRun} for
|
|
96
|
+
* whether and when it ran.
|
|
97
|
+
*/
|
|
71
98
|
export interface RoomEventAttendance {
|
|
72
99
|
attendeeCount: number;
|
|
73
100
|
peakConcurrent: number;
|
|
101
|
+
/** First player seen in the room. */
|
|
102
|
+
firstPlayerSeenAt: string | null;
|
|
103
|
+
/** Last player seen in the room. */
|
|
104
|
+
lastPlayerSeenAt: string | null;
|
|
105
|
+
/**
|
|
106
|
+
* @deprecated Misread as the run window once already, which is why the pair
|
|
107
|
+
* above exists. Same values, kept so existing consumers keep working; read
|
|
108
|
+
* `firstPlayerSeenAt`/`lastPlayerSeenAt`, or {@link RoomEventRun} if what you
|
|
109
|
+
* want is when the event ran.
|
|
110
|
+
*/
|
|
74
111
|
startedAt: string | null;
|
|
112
|
+
/** @deprecated See {@link RoomEventAttendance.startedAt}. */
|
|
75
113
|
endedAt: string | null;
|
|
76
114
|
}
|
|
77
115
|
/** An instance standing on its own in a listing, carrying enough of its series
|
|
@@ -85,6 +123,11 @@ export interface RoomEventOccurrence extends RoomEventInstance {
|
|
|
85
123
|
timezone: string;
|
|
86
124
|
/** True when the parent series repeats, so a card can badge it as recurring. */
|
|
87
125
|
recurring: boolean;
|
|
126
|
+
/** The series' rule, denormalized so a card can say *how* it repeats without
|
|
127
|
+
* a second read. Null for a one-off, and then `recurring` is false too — the
|
|
128
|
+
* flag stays because it is what a caller branches on, the rule is what it
|
|
129
|
+
* renders. */
|
|
130
|
+
recurrence: RoomEventRecurrence | null;
|
|
88
131
|
createdBy: string | null;
|
|
89
132
|
}
|
|
90
133
|
/** The authored series, with the instances it has materialized so far. */
|
|
@@ -110,6 +153,11 @@ export interface RoomEventSeries {
|
|
|
110
153
|
export interface RoomEventList {
|
|
111
154
|
events: RoomEventOccurrence[];
|
|
112
155
|
}
|
|
156
|
+
/** The archive listing: finished and cancelled occurrences, newest first, with
|
|
157
|
+
* the keyset cursor for the next page (`null` once the history is exhausted). */
|
|
158
|
+
export interface RoomEventArchive extends RoomEventList {
|
|
159
|
+
nextCursor: string | null;
|
|
160
|
+
}
|
|
113
161
|
export interface CreateRoomEventInput {
|
|
114
162
|
title: string;
|
|
115
163
|
description?: string | null;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RawRecord } from "../json";
|
|
1
2
|
/** Room registry types shared by the server routes and the client SDK. */
|
|
2
3
|
export type RoomVisibility = "public" | "unlisted" | "private";
|
|
3
4
|
/**
|
|
@@ -109,11 +110,65 @@ export interface RoomPresenceSnapshot {
|
|
|
109
110
|
slug: string;
|
|
110
111
|
channels: Record<RoomChannel, RoomPresenceEntry[]>;
|
|
111
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Where a player was last standing in one room, as handed back on the verify
|
|
115
|
+
* room context. A hint for the game server's placement pass, not a command:
|
|
116
|
+
* the world is hand-authored and changes between deploys, so the coordinate is
|
|
117
|
+
* re-validated against the live physics space before anyone is put on it.
|
|
118
|
+
* `worldId` records which build of the world produced it; `at` is how fresh it
|
|
119
|
+
* is. Feet position in metres, `yaw` in radians.
|
|
120
|
+
*/
|
|
121
|
+
export interface RoomPlayerPosition {
|
|
122
|
+
x: number;
|
|
123
|
+
y: number;
|
|
124
|
+
z: number;
|
|
125
|
+
yaw: number;
|
|
126
|
+
worldId: string;
|
|
127
|
+
at: string;
|
|
128
|
+
}
|
|
129
|
+
/** One entry of `POST /api/rooms/position/report`, keyed by the Flow `sub`. */
|
|
130
|
+
export interface PlayerPositionReport {
|
|
131
|
+
roomId: string;
|
|
132
|
+
sub: string;
|
|
133
|
+
x: number;
|
|
134
|
+
y: number;
|
|
135
|
+
z: number;
|
|
136
|
+
yaw: number;
|
|
137
|
+
worldId: string;
|
|
138
|
+
}
|
|
139
|
+
export interface VoiceDecisionSubject {
|
|
140
|
+
roomId: string;
|
|
141
|
+
sub: string;
|
|
142
|
+
}
|
|
143
|
+
export type VoiceDecisionReason = "room_not_found" | "user_not_found" | "banned" | "private_not_member";
|
|
144
|
+
/** Current server-authoritative room admission and shared text/voice mute
|
|
145
|
+
* state for one active LiveKit participant. */
|
|
146
|
+
export interface VoiceRoomDecision extends VoiceDecisionSubject {
|
|
147
|
+
admitted: boolean;
|
|
148
|
+
muted: boolean;
|
|
149
|
+
mutedUntil: string | null;
|
|
150
|
+
reason?: VoiceDecisionReason;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* One server-resolved permission over an admitted room session, keyed by the
|
|
154
|
+
* Talk operation it authorizes (see `src/rooms/capabilities.ts` for the
|
|
155
|
+
* derivation). Resolved in admission and re-resolved on every restriction
|
|
156
|
+
* recheck — the client's copy is explanatory only and never the enforcement
|
|
157
|
+
* boundary.
|
|
158
|
+
*/
|
|
159
|
+
export type RoomCapability = "history" | "reply" | "react" | "mention" | "attach" | "pin" | "moderate" | "search";
|
|
160
|
+
export type RoomCapabilities = Record<RoomCapability, boolean>;
|
|
112
161
|
/**
|
|
113
162
|
* Room admission context returned by POST /api/session/verify when the caller
|
|
114
163
|
* passes a `room` slug: whether the verified subject may enter, and with which
|
|
115
|
-
* role and per-room
|
|
116
|
-
*
|
|
164
|
+
* role and per-room settings (the admission-facing surfaces merged flat —
|
|
165
|
+
* see `src/rooms/admission.ts`). `settings` is delivered only through this
|
|
166
|
+
* server-to-server path — surface blobs may hold secret-ish values, and
|
|
167
|
+
* `lastPosition` is scoped to the room being joined so it never answers "where
|
|
168
|
+
* is this user" across the fleet. `capabilities` is the resolved permission
|
|
169
|
+
* set for the session (all-false when `ok` is false — no session exists);
|
|
170
|
+
* `mutedUntil` carries a live mute's expiry (null for permanent or none) so
|
|
171
|
+
* consumers can show feedback until it lapses.
|
|
117
172
|
*/
|
|
118
173
|
export interface VerifyRoomContext {
|
|
119
174
|
ok: boolean;
|
|
@@ -123,7 +178,10 @@ export interface VerifyRoomContext {
|
|
|
123
178
|
visibility: RoomVisibility | null;
|
|
124
179
|
role: RoomRole | null;
|
|
125
180
|
muted: boolean;
|
|
181
|
+
mutedUntil: string | null;
|
|
182
|
+
capabilities: RoomCapabilities;
|
|
126
183
|
settings: Record<string, number | boolean | string>;
|
|
184
|
+
lastPosition: RoomPlayerPosition | null;
|
|
127
185
|
}
|
|
128
186
|
export interface RoomSurfaceSettings {
|
|
129
187
|
surface: string;
|
|
@@ -147,7 +205,7 @@ export interface RoomsApi {
|
|
|
147
205
|
members(slug: string): Promise<RoomMembers>;
|
|
148
206
|
presence(slug: string): Promise<RoomPresenceSnapshot>;
|
|
149
207
|
settings(slug: string, surface: string): Promise<RoomSurfaceSettings>;
|
|
150
|
-
saveSettings(slug: string, surface: string, settings:
|
|
208
|
+
saveSettings(slug: string, surface: string, settings: RawRecord): Promise<RoomSurfaceSettings>;
|
|
151
209
|
transfer(slug: string, userId: string): Promise<void>;
|
|
152
210
|
claim(slug: string): Promise<void>;
|
|
153
211
|
setRole(slug: string, userId: string, role: "moderator" | "member"): Promise<void>;
|
|
@@ -162,4 +220,9 @@ export interface RoomsApi {
|
|
|
162
220
|
expiresInSeconds?: number;
|
|
163
221
|
}): Promise<void>;
|
|
164
222
|
unmute(slug: string, userId: string): Promise<void>;
|
|
223
|
+
timeout(slug: string, userId: string, options: {
|
|
224
|
+
seconds: number;
|
|
225
|
+
reason?: string;
|
|
226
|
+
}): Promise<void>;
|
|
227
|
+
removeTimeout(slug: string, userId: string): Promise<void>;
|
|
165
228
|
}
|
package/dist/sdk/types/rooms.js
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/** Room registry types shared by the server routes and the client SDK. */
|
package/dist/sdk/types/sdk.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { Chain, Hex, PrepareTransactionRequestParameters, SendTransactionParameters, SignTypedDataParameters, Transport, WalletClient } from "viem";
|
|
3
3
|
import type { SendCallsParameters } from "viem/actions";
|
|
4
|
+
import type { RawValue } from "../json";
|
|
4
5
|
import type { AdditionalSession, FlowCredential, FlowUser, WebAuthnSignature } from "./auth";
|
|
5
6
|
import type { ConnectCapabilities, ConnectResponse } from "./protocol";
|
|
6
7
|
import type { RoomsApi } from "./rooms";
|
|
@@ -262,7 +263,7 @@ export type FlowWidgetHandle = {
|
|
|
262
263
|
* Sends a widget-specific message (e.g. `{ type: "xp-refresh" }`), pinned to
|
|
263
264
|
* the Flow ID origin.
|
|
264
265
|
*/
|
|
265
|
-
post: (message:
|
|
266
|
+
post: (message: RawValue) => void;
|
|
266
267
|
/**
|
|
267
268
|
* Recolors the mounted widget in place, for the same reason the pill exposes
|
|
268
269
|
* it: a remount reloads the frame and flashes it out of the host's layout.
|
|
@@ -60,3 +60,21 @@ export type SessionRouteResponse = {
|
|
|
60
60
|
*/
|
|
61
61
|
additionalSessions?: AdditionalSession[];
|
|
62
62
|
};
|
|
63
|
+
export type GracefulShutdownSignal = "SIGTERM" | "SIGINT";
|
|
64
|
+
export type GracefulShutdownServer = {
|
|
65
|
+
/** Stop accepting connections; `true` also closes active connections. */
|
|
66
|
+
stop(closeActiveConnections?: boolean): Promise<void> | void;
|
|
67
|
+
};
|
|
68
|
+
export type GracefulShutdownOptions = {
|
|
69
|
+
/**
|
|
70
|
+
* Total time allowed for active HTTP requests and `onShutdown` to finish.
|
|
71
|
+
* Defaults to 20 seconds, leaving Kubernetes headroom around the app drain.
|
|
72
|
+
*/
|
|
73
|
+
timeoutMs?: number;
|
|
74
|
+
/**
|
|
75
|
+
* Consumer cleanup started after the server stops accepting connections.
|
|
76
|
+
* Close WebSockets, flush presence, and finish other process-owned state here.
|
|
77
|
+
*/
|
|
78
|
+
onShutdown?: (signal: GracefulShutdownSignal) => Promise<void> | void;
|
|
79
|
+
};
|
|
80
|
+
export type GracefulShutdownCleanup = () => void;
|
package/dist/sdk/types/xp.d.ts
CHANGED
|
@@ -43,17 +43,18 @@ export interface ActionDayContext {
|
|
|
43
43
|
priorMinutes: number;
|
|
44
44
|
flowScore: number;
|
|
45
45
|
}
|
|
46
|
-
/** Lifecycle state of a durable action session. `abandoned` is set
|
|
47
|
-
*
|
|
48
|
-
* session (e.g. a server crash). */
|
|
46
|
+
/** Lifecycle state of a durable action session. `abandoned` is set when the
|
|
47
|
+
* janitor finds a session older than the safety window; it never grants XP. */
|
|
49
48
|
export type ActionSessionState = "active" | "finished" | "abandoned";
|
|
50
49
|
/** An action-session transition kind, appended to the action_event log.
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
export type ActionEventKind = "start" | "focus_lost" | "focus_gained" | "finish" | "abandon";
|
|
50
|
+
* Paused intervals do not accrue; focus transitions split the accrued time for
|
|
51
|
+
* stats without stopping it. */
|
|
52
|
+
export type ActionEventKind = "start" | "pause" | "resume" | "focus_lost" | "focus_gained" | "finish" | "abandon";
|
|
53
|
+
/** Reporter-supplied transitions accepted while an action is active. */
|
|
54
|
+
export type ActionTransitionKind = Extract<ActionEventKind, "pause" | "resume" | "focus_lost" | "focus_gained">;
|
|
54
55
|
/** Response of GET /api/actions/active — the caller's current in-progress
|
|
55
56
|
* action (or null when idle), plus the day context so the timer can render the
|
|
56
|
-
* live XP estimate from the
|
|
57
|
+
* live XP estimate from the timestamp-derived accrued duration. (Always an
|
|
57
58
|
* `active` session by definition, so the lifecycle state isn't carried.) */
|
|
58
59
|
export interface ActiveActionResponse extends ActionDayContext {
|
|
59
60
|
source: string;
|
package/dist/sdk/verify.js
CHANGED
|
@@ -33,5 +33,6 @@ export async function verifyFlowJWT(token, opts) {
|
|
|
33
33
|
});
|
|
34
34
|
if (!payload.sub)
|
|
35
35
|
throw new Error("JWT missing sub");
|
|
36
|
+
/* SAFETY: jwtVerify already checked the signature, issuer and audience against this issuer's own keys, and `sub` is checked above. */
|
|
36
37
|
return payload;
|
|
37
38
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RawValue } from "../json";
|
|
1
2
|
import type { FlowConnectorParameters } from "../types";
|
|
2
3
|
/**
|
|
3
4
|
* Wagmi connector adapter for Flow. Drop into your wagmi config with
|
|
@@ -17,9 +18,12 @@ import type { FlowConnectorParameters } from "../types";
|
|
|
17
18
|
* wagmi state stays in sync when login/logout happens via the SDK rather
|
|
18
19
|
* than through wagmi's own `connect()`.
|
|
19
20
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
/** The minimal EIP-1193 surface wagmi needs from this connector. */
|
|
22
|
+
type FlowProvider = {
|
|
23
|
+
request: (...args: RawValue[]) => Promise<RawValue>;
|
|
24
|
+
};
|
|
25
|
+
export declare function flowConnector(parameters?: FlowConnectorParameters): import("@wagmi/core").CreateConnectorFn<FlowProvider, Record<string, unknown>, Record<string, unknown>>;
|
|
23
26
|
export declare namespace flowConnector {
|
|
24
27
|
var type: "flow";
|
|
25
28
|
}
|
|
29
|
+
export {};
|