@flow-industries/id 0.21.2 → 0.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +52 -0
  2. package/contracts/v1/openapi.json +818 -0
  3. package/contracts/v1/reports.json +188 -0
  4. package/contracts/v1/sdk-exports.json +1165 -0
  5. package/dist/sdk/client/create-flow.js +2 -5
  6. package/dist/sdk/client/dialog-host.js +11 -6
  7. package/dist/sdk/client/flow-widget.js +3 -3
  8. package/dist/sdk/client/focus-session.d.ts +18 -0
  9. package/dist/sdk/client/focus-session.js +140 -0
  10. package/dist/sdk/client/focus.d.ts +11 -0
  11. package/dist/sdk/client/focus.js +30 -0
  12. package/dist/sdk/client/iframe-host.d.ts +2 -1
  13. package/dist/sdk/client/iframe-host.js +11 -1
  14. package/dist/sdk/client/index.d.ts +4 -1
  15. package/dist/sdk/client/index.js +2 -0
  16. package/dist/sdk/client/json-api.d.ts +1 -1
  17. package/dist/sdk/client/json-api.js +3 -1
  18. package/dist/sdk/client/open-profile.js +10 -7
  19. package/dist/sdk/client/profile-button.js +16 -9
  20. package/dist/sdk/client/reports.d.ts +9 -0
  21. package/dist/sdk/client/reports.js +30 -0
  22. package/dist/sdk/client/rooms.js +7 -26
  23. package/dist/sdk/client/study-search.d.ts +20 -0
  24. package/dist/sdk/client/study-search.js +116 -0
  25. package/dist/sdk/contracts/http.d.ts +155 -0
  26. package/dist/sdk/contracts/http.js +91 -0
  27. package/dist/sdk/contracts/reports.d.ts +130 -0
  28. package/dist/sdk/contracts/reports.js +67 -0
  29. package/dist/sdk/dialog/remote/Messenger.d.ts +3 -0
  30. package/dist/sdk/dialog/remote/Messenger.js +9 -3
  31. package/dist/sdk/react/use-cooldown.d.ts +1 -0
  32. package/dist/sdk/react/use-cooldown.js +12 -0
  33. package/dist/sdk/types/auth.d.ts +12 -0
  34. package/dist/sdk/types/dialog.d.ts +25 -0
  35. package/dist/sdk/types/focus.d.ts +109 -0
  36. package/dist/sdk/types/focus.js +0 -0
  37. package/dist/sdk/types/index.d.ts +8 -3
  38. package/dist/sdk/types/landing.d.ts +9 -0
  39. package/dist/sdk/types/landing.js +0 -0
  40. package/dist/sdk/types/otp.d.ts +20 -0
  41. package/dist/sdk/types/otp.js +0 -0
  42. package/dist/sdk/types/reports.d.ts +7 -0
  43. package/dist/sdk/types/reports.js +0 -0
  44. package/dist/sdk/types/settings.d.ts +162 -0
  45. package/dist/sdk/types/settings.js +0 -0
  46. package/dist/sdk/types/xp.d.ts +8 -0
  47. package/package.json +21 -9
@@ -0,0 +1,109 @@
1
+ import type { ActionCatalogResponse, ActionEventRequest, ActionEventResponse, ActionSubjectRef, ActiveActionResponse, CreateStudySubjectInput, FinishActionRequest, FinishActionResponse, ListStudySessionsOptions, ListStudySubjectsOptions, StartActionInput, StartActionResult, StudyCatalog, StudyCategory, StudyField, StudyRange, StudySessionPage, StudySubject, StudySubjectList, StudySubjectRemovalResponse, StudySubjectResponse, StudySummary, UpdateStudySubjectInput } from "./index";
2
+ /** The running session as the header pill and the panel see it: the shape of
3
+ * `GET /api/actions/me`, of a start's `session` and of a 409's `active`. */
4
+ export type ActiveAction = ActiveActionResponse;
5
+ /** A start as the UI requests it. The surface is not the caller's to choose:
6
+ * `createFocusApi` stamps every start with the injected host surface. `sessionId` may be
7
+ * omitted; the SDK mints the idempotency key. */
8
+ export type StartFocusRequest = Omit<StartActionInput, "surface">;
9
+ export interface FocusApi {
10
+ catalog(): Promise<ActionCatalogResponse>;
11
+ /** The caller's running session, or null when idle. */
12
+ active(): Promise<ActiveAction | null>;
13
+ /** Resolves to the 409 conflict as a value; every other refusal throws. */
14
+ start(request: StartFocusRequest): Promise<StartActionResult>;
15
+ event(request: ActionEventRequest): Promise<ActionEventResponse>;
16
+ finish(request: FinishActionRequest): Promise<FinishActionResponse>;
17
+ studyCatalog(): Promise<StudyCatalog>;
18
+ subjects(options?: ListStudySubjectsOptions): Promise<StudySubjectList>;
19
+ createSubject(request: CreateStudySubjectInput): Promise<StudySubjectResponse>;
20
+ /** Rename (`{ name }`), re-anchor (`{ field }`), archive or restore
21
+ * (`{ archived }`) a subject. Archiving is what the picker means by
22
+ * "remove"; the host subject-management surface also offers deletion. */
23
+ updateSubject(id: string, patch: UpdateStudySubjectInput): Promise<StudySubjectResponse>;
24
+ /** Removes a subject outright when nothing references it; auth archives it
25
+ * instead when sessions do, which the `outcome` reports. */
26
+ deleteSubject(id: string): Promise<StudySubjectRemovalResponse>;
27
+ /** Finished study time per subject and per category; the running session
28
+ * is not in it and is folded in live from the provider. */
29
+ summary(range: StudyRange): Promise<StudySummary>;
30
+ /** One subject's finished sessions, newest first, keyset-paged on
31
+ * `nextBefore`. */
32
+ sessions(options: ListStudySessionsOptions): Promise<StudySessionPage>;
33
+ }
34
+ export interface CatalogFieldRef {
35
+ category: StudyCategory;
36
+ field: StudyField;
37
+ }
38
+ export type StudySearchHit = {
39
+ kind: "subject";
40
+ id: string;
41
+ subject: StudySubject;
42
+ ref: CatalogFieldRef | null;
43
+ score: number;
44
+ } | {
45
+ kind: "field";
46
+ id: string;
47
+ ref: CatalogFieldRef;
48
+ /** The alias that matched when the label itself did not. */
49
+ alias: string | null;
50
+ score: number;
51
+ };
52
+ export interface ClockAnchor {
53
+ accruedSeconds: number;
54
+ paused: boolean;
55
+ readAt: number;
56
+ }
57
+ export type FocusPhase = "unknown" | "idle" | "active";
58
+ /** What Stop leaves behind, shown exactly once before it is dismissed. */
59
+ export interface FinishedSummary {
60
+ source: string;
61
+ label: string | null;
62
+ subject: ActionSubjectRef | null;
63
+ xpGranted: number;
64
+ durationSeconds: number;
65
+ }
66
+ export interface FocusSessionState {
67
+ phase: FocusPhase;
68
+ active: ActiveAction | null;
69
+ anchor: ClockAnchor | null;
70
+ finished: FinishedSummary | null;
71
+ /** The other session a plain start ran into, awaiting "switch?". */
72
+ conflict: ActiveAction | null;
73
+ busy: boolean;
74
+ failed: boolean;
75
+ }
76
+ export type FocusSessionEvent = {
77
+ type: "read";
78
+ active: ActiveAction | null;
79
+ at: number;
80
+ } | {
81
+ type: "read-failed";
82
+ } | {
83
+ type: "request";
84
+ } | {
85
+ type: "request-failed";
86
+ } | {
87
+ type: "started";
88
+ session: ActiveAction;
89
+ at: number;
90
+ } | {
91
+ type: "conflict";
92
+ active: ActiveAction;
93
+ } | {
94
+ type: "paused";
95
+ at: number;
96
+ } | {
97
+ type: "resumed";
98
+ at: number;
99
+ } | {
100
+ type: "finished";
101
+ result: FinishActionResponse;
102
+ at: number;
103
+ } | {
104
+ type: "dismiss-finished";
105
+ } | {
106
+ type: "dismiss-conflict";
107
+ } | {
108
+ type: "reset";
109
+ };
File without changes
@@ -1,19 +1,24 @@
1
1
  export type { AccountDeleteRequest, AccountDeleteResponse, AccountErasureCounts, AccountErasureEntry, AccountErasureFeed, AccountErasureResult, AccountExport, AccountExportAccount, AccountExportActionEvent, AccountExportActionSession, AccountExportAttendance, AccountExportAuthoredEvent, AccountExportCharacterStateTime, AccountExportFollow, AccountExportGameEvent, AccountExportOtherService, AccountExportOwnedRoom, AccountExportPasskey, AccountExportRoomEvents, AccountExportRoomMembership, AccountExportRoomPlayerState, AccountExportRoomPresence, AccountExportRoomRestriction, AccountExportRoomSettings, AccountExportRooms, AccountExportRsvp, AccountExportSettingsSurface, AccountExportStudySubject, AccountExportXpGrant, AccountExportXpSummary, } from "./account-data";
2
2
  export type { ActionCurve, ActionEventRequest, ActionEventResponse, ActionsApi, ActiveActionState, FinishActionRequest, ListStudySessionsOptions, ListStudySubjectsOptions, StartActionInput, StartActionResult, StudyApi, StudySubjectResponse, UseActiveActionOptions, XpEstimateContext, } from "./actions";
3
- 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
+ export type { AccountRegistration, AccountSession, AccountSessionsResponse, AdditionalSession, AuthConfig, AuthResponse, AuthResponseWithWebAuthn, BioNormalization, BioRejection, BioWriteRefusal, BioWriteResult, FlowCredential, FlowUser, PasskeyEnrollment, PasskeyPluginOptions, RevokeAllSessionsResponse, RevokeSessionResponse, SecurityActivityEntry, SecurityActivityResponse, SessionUser, VerifiedFlowJWT, VerifyOptions, WebAuthnSignature, } from "./auth";
4
4
  export type { BodyRegion, CosmeticItem, CosmeticMaterial, CosmeticPaint, CosmeticSlot, EquipConflict, EquippedCosmetic, EquippedCosmetics, EquipRegion, PaintSlot, SettingsConflict, } from "./cosmetics";
5
5
  export { BODY_REGIONS, COSMETIC_MATERIALS } from "./cosmetics";
6
- export type { BoundaryError, DialogCustomFeatures, DialogCustomLabels, DialogError, DialogReferrer, DialogState, Loadable, ProfileData, ProfileIdentity, } from "./dialog";
6
+ export type { AccountDataDownload, AccountDataIdentity, BoundaryError, DialogCustomFeatures, DialogCustomLabels, DialogError, DialogReferrer, DialogState, Loadable, ProfileData, ProfileIdentity, SignInWithEmailProps, SignUpProps, } from "./dialog";
7
7
  export type { AuthErrorCode, AuthEventName, AuthEventRecord, AuthMode, AuthOutcome, BeaconBody, FunnelStep, } from "./events";
8
+ export type { ActiveAction, CatalogFieldRef, ClockAnchor, FinishedSummary, FocusApi, FocusPhase, FocusSessionEvent, FocusSessionState, StartFocusRequest, StudySearchHit, } from "./focus";
8
9
  export type { GameEventReport, GameLifecycleEventName, GameLifecycleFeedEvent, GameLifecycleFeedPage, GamePlayerSession, } from "./game";
9
10
  export { GAME_LIFECYCLE_EVENT_NAMES } from "./game";
11
+ export type { AccountProfileData } from "./landing";
10
12
  export type { Bridge, BridgeParameters, FlowAccount, FlowRemote, FlowRemoteConfig, FromWindowOptions, MessageResponse, Messenger, OneOf, Payload, QueuedRequest, ReadyOptions, RemoteFlowState, RemoteState, Schema, Storage, Topic, WithReady, } from "./messenger";
13
+ export type { OtpVerificationFailure, OtpVerificationResult, VerificationAdapter, } from "./otp";
11
14
  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";
12
15
  export { getConnectCapabilities, isPersonalSignParams, isSendCallsParams, isSendTransactionParams, } from "./protocol";
16
+ export type { ReportOperation, ReportRequest, ReportResponse, ReportsApi, ReportTransport, } from "./reports";
13
17
  export type { CreateRoomEventInput, RoomEventArchive, RoomEventAttendance, RoomEventFrequency, RoomEventInstance, RoomEventInterest, RoomEventInterestState, RoomEventList, RoomEventOccurrence, RoomEventRecurrence, RoomEventRoomRef, RoomEventRun, RoomEventSeries, RoomEventStatus, SetInterestInput, UpdateRoomEventInput, } from "./room-events";
14
18
  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";
15
19
  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";
16
20
  export type { GracefulShutdownCleanup, GracefulShutdownOptions, GracefulShutdownServer, GracefulShutdownSignal, ResolvedFlowSession, ResolveSessionOptions, SessionRouteOptions, SessionRouteResponse, SessionRouteSession, } from "./server";
21
+ export type { CosmeticOffer, PaintDescriptor, SettingDescriptor, Settings, SettingsMigration, StoredSettings, SurfaceRegistry, } from "./settings";
17
22
  export type { CreateStudySubjectInput, ListStudySessionsInput, StudyCatalog, StudyCategory, StudyCategoryTotals, StudyField, StudyRange, StudySessionEntry, StudySessionPage, StudySessionsResult, StudySubject, StudySubjectCreation, StudySubjectList, StudySubjectRefusal, StudySubjectRemoval, StudySubjectRemovalResponse, StudySubjectTotals, StudySubjectUpdate, StudySummary, UpdateStudySubjectInput, } from "./study";
18
23
  export type { CoinAsset, IdentifiedTx, TxApprove, TxConvert, TxSend, TxSwap, } from "./tx";
19
- 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";
24
+ export type { ActionCatalogEntry, ActionCatalogResponse, ActionDayContext, ActionDurations, ActionEventKind, ActionSessionState, ActionSubjectRef, ActionSurface, ActionTimelineEvent, ActionTransitionKind, ActiveActionResponse, ActiveSessionConflict, CurrentAction, FinishActionResponse, LevelProgress, PublicProfile, PublicProfileRefusal, PublicProfileResult, PublicProfileSignal, PublicUserProfile, StartActionResponse, StartUserSessionResult, UserActionSurface, XpGrantResult, XpMode, XpRecentGrant, XpSummary, } from "./xp";
@@ -0,0 +1,9 @@
1
+ import type { FlowUser } from "./auth";
2
+ export type AccountProfileData = FlowUser & {
3
+ email?: string | null;
4
+ emailVerified?: boolean;
5
+ subscribedToNews?: boolean;
6
+ createdAt?: string;
7
+ image?: string | null;
8
+ walletAddress?: string | null;
9
+ };
File without changes
@@ -0,0 +1,20 @@
1
+ import type { AuthErrorCode } from "./events";
2
+ export type OtpVerificationFailure = {
3
+ ok: false;
4
+ errorCode: AuthErrorCode;
5
+ apiCode: "INVALID_OTP" | "OTP_EXPIRED" | "TOO_MANY_ATTEMPTS";
6
+ status: 400 | 403;
7
+ message: string;
8
+ attemptsRemaining?: number;
9
+ };
10
+ export type OtpVerificationResult = {
11
+ ok: true;
12
+ } | OtpVerificationFailure;
13
+ export interface VerificationAdapter {
14
+ findVerificationValue(identifier: string): Promise<{
15
+ id: string;
16
+ value: string;
17
+ expiresAt: Date;
18
+ } | null | undefined>;
19
+ deleteVerificationValue(id: string): Promise<void>;
20
+ }
File without changes
@@ -0,0 +1,7 @@
1
+ import type { z } from "zod";
2
+ import type { httpOperations } from "../contracts/http";
3
+ export type ReportOperation = keyof typeof httpOperations;
4
+ export type ReportRequest<T extends ReportOperation> = z.input<(typeof httpOperations)[T]["request"]>;
5
+ export type ReportResponse<T extends ReportOperation> = z.output<(typeof httpOperations)[T]["response"]>;
6
+ export type ReportTransport = (input: URL | RequestInfo, init?: RequestInit) => Promise<Response>;
7
+ export type ReportsApi = <T extends ReportOperation>(operation: T, body: ReportRequest<T>) => Promise<ReportResponse<T>>;
File without changes
@@ -0,0 +1,162 @@
1
+ import type { RawValue } from "../json";
2
+ import type { CosmeticItem, CosmeticMaterial, CosmeticSlot, EquipRegion, SettingsConflict } from "./cosmetics";
3
+ export interface PaintDescriptor {
4
+ key: string;
5
+ group: string;
6
+ label: string;
7
+ description?: string;
8
+ type: "paint";
9
+ default: string;
10
+ options: {
11
+ value: string;
12
+ label: string;
13
+ }[];
14
+ /**
15
+ * The item this colour lands on, and the material on it that is recoloured.
16
+ * Both travel on the descriptor rather than being read back out of the key: a
17
+ * form grouping its pickers by item, and a renderer deciding which surface a
18
+ * colour applies to, each want one of them, and neither should be taking a
19
+ * key apart to get it.
20
+ */
21
+ cosmetic: string;
22
+ material: CosmeticMaterial;
23
+ }
24
+ export interface CosmeticOffer extends CosmeticItem {
25
+ /**
26
+ * Every equip region this item cannot share a body with — its own, plus
27
+ * everything they overlap. Another item is blocked by this one exactly when
28
+ * its `regions` intersect this list, so a client tests a set instead of
29
+ * re-implementing the rule.
30
+ */
31
+ conflicts: readonly EquipRegion[];
32
+ /**
33
+ * The colour pickers this item brings once it is worn: the SAME descriptors
34
+ * {@link SurfaceRegistry.derived} emits for it, from the same builder, so a
35
+ * client rendering its draft and a client reading the derived list for a
36
+ * saved loadout draw the same rows.
37
+ *
38
+ * An offer, not a setting. A colour is a setting the player HAS only while
39
+ * its item is equipped — that is what {@link sanitize} prunes on — and
40
+ * nothing here widens the stored blob or the write path.
41
+ */
42
+ paint: readonly PaintDescriptor[];
43
+ }
44
+ export type SettingDescriptor = {
45
+ key: string;
46
+ group: string;
47
+ label: string;
48
+ description?: string;
49
+ type: "number";
50
+ default: number;
51
+ min: number;
52
+ max: number;
53
+ step: number;
54
+ } | {
55
+ key: string;
56
+ group: string;
57
+ label: string;
58
+ description?: string;
59
+ type: "boolean";
60
+ default: boolean;
61
+ } | {
62
+ key: string;
63
+ group: string;
64
+ label: string;
65
+ description?: string;
66
+ type: "enum";
67
+ default: string;
68
+ options: {
69
+ value: string;
70
+ label: string;
71
+ }[];
72
+ } | {
73
+ key: string;
74
+ group: string;
75
+ label: string;
76
+ description?: string;
77
+ /** Free text with a shape, for values no fixed option list can cover —
78
+ * a Discord snowflake, a channel id. `pattern` is a source string (not a
79
+ * RegExp) so the descriptor stays JSON-serializable for the schema
80
+ * endpoint the settings form reads. */
81
+ type: "text";
82
+ default: string;
83
+ maxLength: number;
84
+ pattern?: string;
85
+ } | {
86
+ key: string;
87
+ group: string;
88
+ label: string;
89
+ description?: string;
90
+ /** One cosmetic slot: the stored value is a catalog cosmetic id, or ""
91
+ * for nothing equipped. `items` is the catalog projection the customizer
92
+ * reads off the schema endpoint so it can render a draft loadout with no
93
+ * catalog data of its own — which picks would collide, and which colour
94
+ * pickers a pick brings with it. UX only; the conflict rule is enforced
95
+ * on save, and a draft is never a claim about what may be saved.
96
+ *
97
+ * The default is an item id, not "": a player who has never dressed is
98
+ * wearing the starting outfit, and saying so here is what lets "" mean
99
+ * the one thing it should — a player who chose to wear nothing there. */
100
+ type: "cosmetic";
101
+ default: string;
102
+ slot: CosmeticSlot;
103
+ /**
104
+ * Whether an avatar may go with this slot empty. False is the slot a
105
+ * client offers a "None" chip for; true is one where `""` is refused on
106
+ * save. Projected here rather than restated in the customizer for the
107
+ * reason {@link CosmeticOffer.conflicts} is: the rule belongs to the
108
+ * slot, and a second hand-maintained copy of it goes quiet (AUTH-222).
109
+ */
110
+ required: boolean;
111
+ items: readonly CosmeticOffer[];
112
+ } | PaintDescriptor;
113
+ export type StoredSettings = Record<string, RawValue>;
114
+ export type Settings = Record<string, number | boolean | string>;
115
+ export interface SettingsMigration {
116
+ to: number;
117
+ up: (stored: StoredSettings) => StoredSettings;
118
+ }
119
+ export interface SurfaceRegistry {
120
+ /**
121
+ * Bumped only when a setting in this surface is renamed or retyped. A stored
122
+ * blob stamps the version it was last normalized to; {@link normalize} walks
123
+ * an old blob up to this version on read, preserving the user's chosen value
124
+ * instead of dropping it. Adding or removing a setting does NOT bump it.
125
+ */
126
+ version: number;
127
+ settings: readonly SettingDescriptor[];
128
+ /**
129
+ * The steps {@link normalize} walks an out-of-date blob up through, in
130
+ * ascending {@link SettingsMigration.to} order. A surface that has never
131
+ * renamed anything has none, and its blobs need no walk.
132
+ */
133
+ migrations?: readonly SettingsMigration[];
134
+ /**
135
+ * Enum defaults are drawn from the option list seeded by the user id instead
136
+ * of the descriptor default, so every unsaved user gets their own stable
137
+ * random pick — identical on every read and on every surface that reads it.
138
+ */
139
+ randomDefaults?: boolean;
140
+ /**
141
+ * Cross-field rule for the whole surface, evaluated on the MERGED blob by
142
+ * the one settings write path. Per-descriptor {@link sanitize} cannot
143
+ * express it: an equip-region collision is a relation between two keys, not
144
+ * a property of either. Living on the surface rather than in a route is what
145
+ * makes it impossible for a second write path to skip.
146
+ */
147
+ validate?: (settings: Settings) => SettingsConflict | null;
148
+ /**
149
+ * The settings this surface has only because of what is stored in it — the
150
+ * appearance surface's colour pickers for the cosmetics a player is
151
+ * currently wearing.
152
+ *
153
+ * Derived rather than fixed is the whole mechanism: a setting that exists
154
+ * only while its cosmetic is equipped is dropped by {@link sanitize} the
155
+ * moment the cosmetic is not, so unequipping an item takes its colour with
156
+ * it and re-equipping cannot resurrect a colour the player never chose.
157
+ *
158
+ * Reads the surface's FIXED settings only, never its own output, so the
159
+ * descriptor list is resolved in one pass and cannot recurse.
160
+ */
161
+ derived?: (settings: Settings) => readonly SettingDescriptor[];
162
+ }
File without changes
@@ -212,3 +212,11 @@ export interface CurrentAction {
212
212
  since: string;
213
213
  subject?: string;
214
214
  }
215
+ export interface ActionTimelineEvent {
216
+ kind: ActionEventKind;
217
+ at: Date;
218
+ }
219
+ export interface ActionDurations {
220
+ accruedSeconds: number;
221
+ focusedSeconds: number;
222
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flow-industries/id",
3
- "version": "0.21.2",
3
+ "version": "0.22.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -50,10 +50,18 @@
50
50
  "./start/server": {
51
51
  "types": "./dist/sdk/start/server.d.ts",
52
52
  "import": "./dist/sdk/start/server.js"
53
- }
53
+ },
54
+ "./focus": {
55
+ "types": "./dist/sdk/client/focus.d.ts",
56
+ "import": "./dist/sdk/client/focus.js"
57
+ },
58
+ "./contracts/v1/reports.json": "./contracts/v1/reports.json",
59
+ "./contracts/v1/openapi.json": "./contracts/v1/openapi.json",
60
+ "./contracts/v1/sdk-exports.json": "./contracts/v1/sdk-exports.json"
54
61
  },
55
62
  "files": [
56
- "dist/sdk"
63
+ "dist/sdk",
64
+ "contracts"
57
65
  ],
58
66
  "scripts": {
59
67
  "dev": "./dev.sh",
@@ -67,7 +75,7 @@
67
75
  "worker": "bun run src/worker.ts",
68
76
  "db:generate": "bunx @better-auth/cli generate",
69
77
  "db:migrate:gen": "bunx drizzle-kit generate",
70
- "db:migrate": "bunx drizzle-kit migrate",
78
+ "db:migrate": "bun scripts/db-migrate.ts",
71
79
  "db:push": "bunx drizzle-kit push",
72
80
  "db:studio": "bunx drizzle-kit studio",
73
81
  "usernames:refresh": "bun run scripts/generate-reserved-usernames.ts",
@@ -76,7 +84,11 @@
76
84
  "test:sdk-package": "bash scripts/test-sdk-package.sh",
77
85
  "format": "biome format --write",
78
86
  "check": "biome check --write",
79
- "typecheck": "tsr generate && tsc --noEmit"
87
+ "typecheck": "tsr generate && tsc --noEmit",
88
+ "contracts:generate": "bun scripts/generate-contracts.ts",
89
+ "contracts:check": "bun scripts/generate-contracts.ts --check && bun scripts/check-contract-compatibility.ts",
90
+ "db:check": "bun scripts/db-check.ts",
91
+ "test:migrations": "bun test scripts/migrations.test.ts scripts/migration-drift.test.ts"
80
92
  },
81
93
  "peerDependencies": {
82
94
  "viem": ">=2.47.10",
@@ -118,7 +130,9 @@
118
130
  "tailwindcss": "^4.1.18",
119
131
  "tw-animate-css": "^1.4.0",
120
132
  "typescript": "^5",
121
- "vite": "^7.3.1"
133
+ "vite": "^7.3.1",
134
+ "react": "^19.2.4",
135
+ "react-dom": "^19.2.4"
122
136
  },
123
137
  "trustedDependencies": [
124
138
  "esbuild",
@@ -126,7 +140,7 @@
126
140
  ],
127
141
  "dependencies": {
128
142
  "@aws-sdk/client-s3": "^3.1073.0",
129
- "@flow-industries/ui": "^0.21.0",
143
+ "@flow-industries/ui": "^0.21.1",
130
144
  "@hono/otel": "^1.1.2",
131
145
  "@openobserve/browser-logs": "^0.3.1",
132
146
  "@openobserve/browser-rum": "^0.3.1",
@@ -151,8 +165,6 @@
151
165
  "motion": "^12.33.0",
152
166
  "pino": "^10.3.1",
153
167
  "postgres": "^3.4.8",
154
- "react": "^19.2.4",
155
- "react-dom": "^19.2.4",
156
168
  "react-intersection-observer": "^10.0.2",
157
169
  "sharp": "^0.35.2",
158
170
  "tempo.ts": "^0.14.2",