@flow-industries/id 0.23.4 → 0.24.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.
@@ -0,0 +1,312 @@
1
+ [
2
+ {
3
+ "id": "hair_original",
4
+ "name": "Original sweep",
5
+ "description": "A broad, tousled fringe sweeps across a compact crown with chunky tapered ends."
6
+ },
7
+ {
8
+ "id": "hair_textured_crop",
9
+ "name": "Textured crop",
10
+ "description": "Five thick forward-swept locks break up a short, rounded crown."
11
+ },
12
+ {
13
+ "id": "hair_side_part_bob",
14
+ "name": "Side-part bob",
15
+ "description": "A deep front sweep curves around one cheek above a rounded, jaw-length back."
16
+ },
17
+ {
18
+ "id": "hair_rounded_afro",
19
+ "name": "Rounded afro",
20
+ "description": "A full, softly rounded halo gives the head an even, generous silhouette."
21
+ },
22
+ {
23
+ "id": "hair_high_ponytail",
24
+ "name": "High ponytail",
25
+ "description": "A swept crown gathers into a thick high tail with three curved ends."
26
+ },
27
+ {
28
+ "id": "hair_twin_buns",
29
+ "name": "Twin buns",
30
+ "description": "Two round buns sit behind a smooth, center-parted crown."
31
+ },
32
+ {
33
+ "id": "hair_short_locs",
34
+ "name": "Short locs",
35
+ "description": "Thick, rounded locs fall from the crown into a compact, uneven fringe."
36
+ },
37
+ {
38
+ "id": "hair_video_soft_side_swept_pixie",
39
+ "name": "Swept pixie",
40
+ "description": "A compact crop pairs a broad diagonal fringe with short tapered temples."
41
+ },
42
+ {
43
+ "id": "hair_video_asymmetric_long_front_undercut",
44
+ "name": "Long-front undercut",
45
+ "description": "One long front curtain contrasts with a closely swept opposite side."
46
+ },
47
+ {
48
+ "id": "hair_video_center_part_high_ponytail",
49
+ "name": "Center-part ponytail",
50
+ "description": "Two clean crown sweeps gather into a high tail behind an open forehead."
51
+ },
52
+ {
53
+ "id": "hair_video_side_part_rounded_bob",
54
+ "name": "Soft sweep bob",
55
+ "description": "A gentle side part carries a wide fringe into rounded, jaw-length sides."
56
+ },
57
+ {
58
+ "id": "hair_video_tucked_bob_with_long_curtain_pieces",
59
+ "name": "Long-curtain bob",
60
+ "description": "Two straight front locks hang below a shorter, tucked-back bob."
61
+ },
62
+ {
63
+ "id": "hair_video_wavy_shoulder_shag",
64
+ "name": "Wavy shag",
65
+ "description": "A broken fringe opens into broad waves and outward-turning shoulder ends."
66
+ },
67
+ {
68
+ "id": "hair_video_short_low_pigtails_with_pointed_fringe",
69
+ "name": "Low pointed pigtails",
70
+ "description": "Short low tails flare behind the ears beneath a pointed central bang."
71
+ },
72
+ {
73
+ "id": "hair_video_sleek_high_topknot",
74
+ "name": "Sleek topknot",
75
+ "description": "A close swept crown rises into a single compact bun."
76
+ },
77
+ {
78
+ "id": "hair_video_raised_braided_crown_with_rear_length",
79
+ "name": "Raised braid crown",
80
+ "description": "A thick raised braid frames the crown above a straight rear fall."
81
+ },
82
+ {
83
+ "id": "hair_video_short_center_curtains",
84
+ "name": "Short curtains",
85
+ "description": "Two curved front locks divide a tidy short crop around a clear center part."
86
+ },
87
+ {
88
+ "id": "hair_video_tousled_triangular_crop",
89
+ "name": "Tousled crop",
90
+ "description": "An uneven fringe and outward-tipped crown create a loose triangular shape."
91
+ },
92
+ {
93
+ "id": "hair_video_center_part_tucked_shoulder_hair",
94
+ "name": "Tucked shoulder fall",
95
+ "description": "Smooth parted sides sweep back into a broad shoulder-length curtain."
96
+ },
97
+ {
98
+ "id": "hair_video_windswept_short_quiff",
99
+ "name": "Windswept quiff",
100
+ "description": "A tall diagonal crest sweeps across a short crown with lifted tips."
101
+ },
102
+ {
103
+ "id": "hair_video_layered_hime_shag",
104
+ "name": "Layered hime shag",
105
+ "description": "Heavy bangs and short flared side layers sit above two long straight locks."
106
+ },
107
+ {
108
+ "id": "hair_video_windblown_headband_crop",
109
+ "name": "Windswept headband",
110
+ "description": "A crown band holds back a short cut with curled side pieces and outward tips."
111
+ },
112
+ {
113
+ "id": "hair_video_headband_rounded_pixie",
114
+ "name": "Headband pixie",
115
+ "description": "A simple band crosses a rounded pixie with short bangs and tucked ends."
116
+ },
117
+ {
118
+ "id": "hair_video_long_straight_hair_with_curtain_fringe",
119
+ "name": "Long curtain fringe",
120
+ "description": "Long straight lengths frame a pair of broad, inward-turning curtain bangs."
121
+ },
122
+ {
123
+ "id": "hair_video_ribbon_twin_buns_and_bob",
124
+ "name": "Ribbon bun bob",
125
+ "description": "Two ribboned high buns rise above a rounded bob and full fringe."
126
+ },
127
+ {
128
+ "id": "hair_video_rounded_bowl_crop",
129
+ "name": "Rounded bowl",
130
+ "description": "A smooth domed crop ends in a dense, even fringe and short sides."
131
+ },
132
+ {
133
+ "id": "hair_video_high_arcing_twin_ponytails",
134
+ "name": "Arcing twin tails",
135
+ "description": "Two high tails arc widely outward behind long center-parted front locks."
136
+ },
137
+ {
138
+ "id": "hair_video_face_framing_twin_braids",
139
+ "name": "Face-framing braids",
140
+ "description": "Two cheek-framing sections descend into slim braids beneath a smooth center part."
141
+ },
142
+ {
143
+ "id": "hair_video_side_high_ponytail_with_divided_bangs",
144
+ "name": "Side ponytail",
145
+ "description": "A high tail falls to one side behind thick, divided bangs."
146
+ },
147
+ {
148
+ "id": "hair_video_blunt_bang_collar_bob",
149
+ "name": "Collar bob",
150
+ "description": "Straight sides narrow toward the collar beneath a compact blunt fringe."
151
+ },
152
+ {
153
+ "id": "hair_video_high_ponytail_with_cheek_crescents",
154
+ "name": "Crescent ponytail",
155
+ "description": "A high tail combines short curved cheek locks with two longer front pieces."
156
+ },
157
+ {
158
+ "id": "hair_video_swept_fringe_high_updo",
159
+ "name": "Swept-fringe updo",
160
+ "description": "A gathered high crown balances a heavy diagonal fringe and long cheek locks."
161
+ },
162
+ {
163
+ "id": "hair_video_broad_windswept_wave_crop",
164
+ "name": "Rolling wave crop",
165
+ "description": "Broad sideways waves give a short cut a wide crown and upturned ends."
166
+ },
167
+ {
168
+ "id": "hair_video_slicked_back_long_hair",
169
+ "name": "Slicked-back long",
170
+ "description": "A smooth backward sweep opens the forehead above long straight rear lengths."
171
+ },
172
+ {
173
+ "id": "hair_video_low_side_buns_with_long_front_ribbons",
174
+ "name": "Low buns and ribbons",
175
+ "description": "Low side buns sit behind two long, straight face-framing locks."
176
+ },
177
+ {
178
+ "id": "hair_video_blunt_hime_fringe_with_long_back",
179
+ "name": "Blunt hime",
180
+ "description": "Straight bangs and short cheek panels contrast with a much longer back."
181
+ },
182
+ {
183
+ "id": "hair_video_center_curtain_rounded_mushroom_bob",
184
+ "name": "Curtain mushroom bob",
185
+ "description": "Full rounded sides curl around a deep center opening and short jaw-length ends."
186
+ },
187
+ {
188
+ "id": "hair_video_low_braid_halo_with_flipped_shoulder_ends",
189
+ "name": "Flipped braid halo",
190
+ "description": "A low crown braid frames parted hair with broad outward-flipped lower ends."
191
+ },
192
+ {
193
+ "id": "hair_video_curtain_fringe_curled_twin_tails",
194
+ "name": "Curled curtain tails",
195
+ "description": "Short high tails curl outward behind long, inward-rolling curtain bangs."
196
+ },
197
+ {
198
+ "id": "hair_video_twin_tails_with_heart_shaped_forelock",
199
+ "name": "Heart-fringe twin tails",
200
+ "description": "Paired front curls form a small heart-shaped opening beneath rounded twin tails."
201
+ },
202
+ {
203
+ "id": "hair_video_long_twin_tails_with_parted_fringe",
204
+ "name": "Parted-fringe twin tails",
205
+ "description": "Two narrow high tails descend beside a thick fringe with a slim center opening."
206
+ },
207
+ {
208
+ "id": "hair_video_long_straight_blunt_fringe",
209
+ "name": "Long blunt fringe",
210
+ "description": "A straight full fringe joins two long vertical side panels and a broad rear fall."
211
+ },
212
+ {
213
+ "id": "hair_video_high_twin_tails_with_long_face_ribbons",
214
+ "name": "Ribbon-lock twin tails",
215
+ "description": "High curved tails sit behind two long, straight temple locks and short bangs."
216
+ },
217
+ {
218
+ "id": "hair_video_tucked_crop_with_long_side_locks",
219
+ "name": "Tucked crop",
220
+ "description": "A close rounded crop leaves two narrow front locks hanging toward the collar."
221
+ },
222
+ {
223
+ "id": "hair_video_wide_looping_high_twin_tails",
224
+ "name": "Looping twin tails",
225
+ "description": "Two broad high tails loop outward beside a full rounded fringe."
226
+ },
227
+ {
228
+ "id": "hair_video_eye_covering_winged_shag",
229
+ "name": "Veiled wing shag",
230
+ "description": "A low, heavy fringe covers the upper face while short side layers curl outward."
231
+ },
232
+ {
233
+ "id": "hair_video_open_center_choppy_shag",
234
+ "name": "Parted wing shag",
235
+ "description": "A raised center tuft and long parted bangs meet broad, upturned side layers."
236
+ },
237
+ {
238
+ "id": "hair_video_high_bun_with_diagonal_fringe",
239
+ "name": "Diagonal-fringe bun",
240
+ "description": "A round high bun sits above a diagonal front sweep and two temple tendrils."
241
+ },
242
+ {
243
+ "id": "hair_video_straight_chin_length_blunt_bob",
244
+ "name": "Blunt chin bob",
245
+ "description": "A flat full fringe meets straight narrow sides that end just below the jaw."
246
+ },
247
+ {
248
+ "id": "hair_video_ribbon_high_ponytail_with_swept_forehead",
249
+ "name": "Ribbon ponytail",
250
+ "description": "A smooth open forehead leads into a high ribboned tail curved behind the head."
251
+ },
252
+ {
253
+ "id": "hair_video_long_layered_hair_with_short_outward_cheek_flicks",
254
+ "name": "Long flicked layers",
255
+ "description": "Short cheek-level flicks stand out above long straight panels and a blunt fringe."
256
+ },
257
+ {
258
+ "id": "hair_video_ribbon_twin_braids_with_exposed_forehead",
259
+ "name": "Ribbon twin braids",
260
+ "description": "Two small bows sit above hanging braids behind a clean swept-back crown."
261
+ },
262
+ {
263
+ "id": "hair_video_high_bun_with_round_full_fringe",
264
+ "name": "Full-fringe bun",
265
+ "description": "A large round bun rises behind full curved bangs and inward-turning cheek locks."
266
+ },
267
+ {
268
+ "id": "hair_video_lifted_half_up_crown_with_long_rolled_sides",
269
+ "name": "Lifted half-up",
270
+ "description": "A raised swept crest tops two thick, inward-curved side falls."
271
+ },
272
+ {
273
+ "id": "hair_video_center_part_shoulder_waves",
274
+ "name": "Shoulder waves",
275
+ "description": "A clear center part opens into broad S-shaped waves and rounded lower curls."
276
+ },
277
+ {
278
+ "id": "hair_video_sleek_swept_up_open_forehead",
279
+ "name": "Swept crest",
280
+ "description": "A compact crown rises backward into one high rounded crest above an open forehead."
281
+ },
282
+ {
283
+ "id": "hair_video_asymmetric_windswept_undercut",
284
+ "name": "Asymmetric wind sweep",
285
+ "description": "A lifted sideways crest balances one long curved lock and an open opposite temple."
286
+ },
287
+ {
288
+ "id": "hair_video_tousled_short_cut_with_crown_braid",
289
+ "name": "Braided tousle",
290
+ "description": "A short crown braid crosses parted bangs above broad, upturned side locks."
291
+ },
292
+ {
293
+ "id": "hair_video_long_rounded_curtain_with_central_fringe_point",
294
+ "name": "Point-fringe long",
295
+ "description": "A low central bang stands between shorter fringe pieces above long curved sides."
296
+ },
297
+ {
298
+ "id": "hair_video_deep_side_part_rounded_bob",
299
+ "name": "Deep sweep bob",
300
+ "description": "One broad curling fringe crosses the face inside a rounded chin-length bob."
301
+ },
302
+ {
303
+ "id": "hair_video_braided_crown_with_long_straight_fall",
304
+ "name": "Long braid crown",
305
+ "description": "A braided crown band sits above parted front locks and long straight side panels."
306
+ },
307
+ {
308
+ "id": "hair_video_large_twin_spiral_drills",
309
+ "name": "Spiral twin tails",
310
+ "description": "Two large tapered tails carry broad spiral ridges beside a smooth parted crown."
311
+ }
312
+ ]
@@ -0,0 +1,11 @@
1
+ import type { SettingDescriptor, Settings, StoredSettings, SurfaceRegistry } from "../types";
2
+ export declare const REGISTRIES: Map<string, SurfaceRegistry>;
3
+ export declare function getRegistry(surface: string): SurfaceRegistry | undefined;
4
+ export declare function defaults(registry: SurfaceRegistry, seed?: string): Settings;
5
+ export declare function asStored<T>(input: T): StoredSettings;
6
+ export declare function descriptorsFor(registry: SurfaceRegistry, stored: StoredSettings, seed?: string): readonly SettingDescriptor[];
7
+ export declare function sanitize<T>(registry: SurfaceRegistry, input: T): Partial<Settings>;
8
+ export declare function withDefaults(registry: SurfaceRegistry, stored: StoredSettings, seed?: string): Settings;
9
+ export declare function normalize(registry: SurfaceRegistry, stored: StoredSettings, fromVersion: number): StoredSettings;
10
+ export type { CosmeticOffer, PaintDescriptor, SettingDescriptor, Settings, SettingsMigration, StoredSettings, SurfaceRegistry, } from "../types";
11
+ export { appearanceSurface } from "./appearance";
@@ -0,0 +1,136 @@
1
+ import { isNum, isString } from "../json";
2
+ import { appearanceSurface } from "./appearance";
3
+ import { GAME_SETTINGS, NOTIFICATION_SETTINGS, STUDY_SETTINGS, } from "./surfaces";
4
+ export const REGISTRIES = new Map(Object.entries({
5
+ game: { version: 1, settings: GAME_SETTINGS },
6
+ notifications: { version: 1, settings: NOTIFICATION_SETTINGS },
7
+ appearance: appearanceSurface(),
8
+ study: { version: 1, settings: STUDY_SETTINGS },
9
+ }));
10
+ export function getRegistry(surface) {
11
+ return REGISTRIES.get(surface);
12
+ }
13
+ function seededIndex(seed, length) {
14
+ let hash = 2166136261;
15
+ for (let i = 0; i < seed.length; i++) {
16
+ hash ^= seed.charCodeAt(i);
17
+ hash = Math.imul(hash, 16777619);
18
+ }
19
+ return (hash >>> 0) % length;
20
+ }
21
+ function defaultsOf(list, registry, seed) {
22
+ const out = {};
23
+ for (const descriptor of list) {
24
+ if (registry.randomDefaults && seed && descriptor.type === "enum") {
25
+ const pick = descriptor.options[seededIndex(`${seed}:${descriptor.key}`, descriptor.options.length)];
26
+ out[descriptor.key] = pick ? pick.value : descriptor.default;
27
+ }
28
+ else {
29
+ out[descriptor.key] = descriptor.default;
30
+ }
31
+ }
32
+ return out;
33
+ }
34
+ export function defaults(registry, seed) {
35
+ return defaultsOf(registry.settings, registry, seed);
36
+ }
37
+ export function asStored(input) {
38
+ return Object.fromEntries(Object.entries(Object(input)));
39
+ }
40
+ export function descriptorsFor(registry, stored, seed) {
41
+ if (!registry.derived)
42
+ return registry.settings;
43
+ const fixed = Object.assign(defaultsOf(registry.settings, registry, seed), sanitizeOf(registry.settings, stored));
44
+ return [...registry.settings, ...registry.derived(fixed)];
45
+ }
46
+ function clamp(value, min, max) {
47
+ return Math.min(max, Math.max(min, value));
48
+ }
49
+ /**
50
+ * A number or a numeric string. `Number(null)`, `Number("")` and
51
+ * `Number(false)` are all 0, and 0 is a real setting (a slot cap of none), so
52
+ * blank and non-numeric values count as missing rather than as zero.
53
+ */
54
+ function numericValue(raw) {
55
+ if (isNum(raw))
56
+ return raw;
57
+ if (!isString(raw) || raw.trim() === "")
58
+ return null;
59
+ const parsed = Number(raw);
60
+ return Number.isFinite(parsed) ? parsed : null;
61
+ }
62
+ function sanitizeOf(list, source) {
63
+ const out = {};
64
+ for (const descriptor of list) {
65
+ if (!(descriptor.key in source))
66
+ continue;
67
+ const raw = source[descriptor.key];
68
+ if (descriptor.type === "number") {
69
+ const coerced = numericValue(raw);
70
+ if (coerced === null)
71
+ continue;
72
+ // An integer step is a count, and consumers parse counts as integers: a
73
+ // stored fraction would read as malformed and fall back to the default.
74
+ const value = Number.isInteger(descriptor.step)
75
+ ? Math.round(coerced)
76
+ : coerced;
77
+ out[descriptor.key] = clamp(value, descriptor.min, descriptor.max);
78
+ }
79
+ else if (descriptor.type === "boolean") {
80
+ out[descriptor.key] = Boolean(raw);
81
+ }
82
+ else if (descriptor.type === "text") {
83
+ if (!isString(raw))
84
+ continue;
85
+ const trimmed = raw.trim();
86
+ // An empty string is a legitimate value here: it is how a room clears its
87
+ // Discord wiring without the surface needing a separate "unset" verb.
88
+ if (trimmed === "") {
89
+ out[descriptor.key] = "";
90
+ continue;
91
+ }
92
+ if (trimmed.length > descriptor.maxLength)
93
+ continue;
94
+ if (descriptor.pattern && !new RegExp(descriptor.pattern).test(trimmed)) {
95
+ continue;
96
+ }
97
+ out[descriptor.key] = trimmed;
98
+ }
99
+ else if (descriptor.type === "cosmetic") {
100
+ if (!isString(raw))
101
+ continue;
102
+ // "" is the unequip verb, not a missing value.
103
+ if (raw === "") {
104
+ out[descriptor.key] = "";
105
+ continue;
106
+ }
107
+ const item = descriptor.items.find((entry) => entry.id === raw);
108
+ if (item)
109
+ out[descriptor.key] = item.id;
110
+ }
111
+ else {
112
+ const option = descriptor.options.find((entry) => entry.value === raw);
113
+ if (option)
114
+ out[descriptor.key] = option.value;
115
+ }
116
+ }
117
+ return out;
118
+ }
119
+ export function sanitize(registry, input) {
120
+ const source = asStored(input);
121
+ return sanitizeOf(descriptorsFor(registry, source), source);
122
+ }
123
+ export function withDefaults(registry, stored, seed) {
124
+ const list = descriptorsFor(registry, stored, seed);
125
+ return Object.assign(defaultsOf(list, registry, seed), sanitizeOf(list, stored));
126
+ }
127
+ export function normalize(registry, stored, fromVersion) {
128
+ let blob = stored;
129
+ for (const step of registry.migrations ?? []) {
130
+ if (step.to <= fromVersion)
131
+ continue;
132
+ blob = step.up(blob);
133
+ }
134
+ return blob;
135
+ }
136
+ export { appearanceSurface } from "./appearance";
@@ -0,0 +1,4 @@
1
+ import type { SettingDescriptor } from "../types";
2
+ export declare const GAME_SETTINGS: readonly SettingDescriptor[];
3
+ export declare const NOTIFICATION_SETTINGS: readonly SettingDescriptor[];
4
+ export declare const STUDY_SETTINGS: readonly SettingDescriptor[];
@@ -0,0 +1,37 @@
1
+ export const GAME_SETTINGS = [
2
+ {
3
+ key: "mouse_sensitivity",
4
+ group: "controls",
5
+ label: "Mouse sensitivity",
6
+ type: "number",
7
+ default: 1.0,
8
+ min: 0.1,
9
+ max: 3.0,
10
+ step: 0.1,
11
+ },
12
+ ];
13
+ export const NOTIFICATION_SETTINGS = [
14
+ {
15
+ key: "room_events",
16
+ group: "events",
17
+ label: "Event reminders",
18
+ description: "How to be reminded about events you are interested in",
19
+ type: "enum",
20
+ default: "in_app",
21
+ options: [
22
+ { value: "in_app", label: "In the menu" },
23
+ { value: "discord", label: "Discord" },
24
+ { value: "off", label: "Off" },
25
+ ],
26
+ },
27
+ ];
28
+ export const STUDY_SETTINGS = [
29
+ {
30
+ key: "share_subject",
31
+ group: "privacy",
32
+ label: "Share what you practice",
33
+ description: "Let people in your rooms see the subject you are practicing, not just that you are practicing",
34
+ type: "boolean",
35
+ default: false,
36
+ },
37
+ ];
@@ -244,6 +244,17 @@ export type AccountExport = {
244
244
  version: 1;
245
245
  exportedAt: string;
246
246
  account: AccountExportAccount;
247
+ externalAccounts: {
248
+ providerId: string;
249
+ accountId: string;
250
+ displayName: string | null;
251
+ createdAt: string;
252
+ }[];
253
+ atprotoIdentities: {
254
+ did: string;
255
+ handle: string | null;
256
+ createdAt: string;
257
+ }[];
247
258
  passkeys: AccountExportPasskey[];
248
259
  sessions: AccountSession[];
249
260
  settings: Record<string, AccountExportSettingsSurface>;
@@ -0,0 +1,9 @@
1
+ import type { InternalStateData, Jwk, Session } from "@atproto/oauth-client";
2
+ import type { OAuthCredentialStore } from "../oauth/stores";
3
+ export type AtprotoSavedState = Omit<InternalStateData, "dpopKey"> & {
4
+ dpopJwk: Jwk;
5
+ };
6
+ export type AtprotoSavedSession = Omit<Session, "dpopKey"> & {
7
+ dpopJwk: Jwk;
8
+ };
9
+ export type AtprotoCredentialStore<T> = OAuthCredentialStore<T>;
File without changes
@@ -0,0 +1,25 @@
1
+ export type AtprotoStartRequest = {
2
+ identifier: string;
3
+ intent: "login" | "link";
4
+ transaction?: string;
5
+ expectedUserId?: string;
6
+ };
7
+ export type AtprotoCompletion = {
8
+ user: {
9
+ id: string;
10
+ username: string;
11
+ isGuest: false;
12
+ };
13
+ } | {
14
+ linked: true;
15
+ };
16
+ export type AtprotoStatus = {
17
+ enabled: boolean;
18
+ connection: {
19
+ did: string;
20
+ handle: string | null;
21
+ status: string;
22
+ } | null;
23
+ canUnlink: boolean;
24
+ unlinkBlockReason: string | null;
25
+ };
File without changes
@@ -74,6 +74,7 @@ export type AuthResponse = {
74
74
  additionalSessions?: AdditionalSession[];
75
75
  };
76
76
  export type AuthConfig = {
77
+ atprotoEnabled?: boolean;
77
78
  rpId: string;
78
79
  rpName: string;
79
80
  };
@@ -81,12 +81,13 @@ export type CosmeticMaterial = (typeof COSMETIC_MATERIALS)[number];
81
81
  * that shrank whenever a slot emptied would churn the contract every time
82
82
  * content lands.
83
83
  */
84
- export type CosmeticSlot = "hat" | "face" | "top" | "bottom" | "feet";
84
+ export type CosmeticSlot = "hat" | "hair" | "face" | "top" | "bottom" | "feet";
85
85
  /** One item in the catalog. */
86
86
  export interface CosmeticItem {
87
87
  id: string;
88
88
  slot: CosmeticSlot;
89
89
  name: string;
90
+ description?: string;
90
91
  /**
91
92
  * ALWAYS plural, even for a single region, and the only shape there is: TF2
92
93
  * carried a singular `equip_region` key alongside the plural one, and items
@@ -1,5 +1,6 @@
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 { AtprotoCompletion, AtprotoStartRequest, AtprotoStatus, } from "./atproto";
3
4
  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
5
  export type { BodyRegion, CosmeticItem, CosmeticMaterial, CosmeticPaint, CosmeticSlot, EquipConflict, EquippedCosmetic, EquippedCosmetics, EquipRegion, PaintSlot, SettingsConflict, } from "./cosmetics";
5
6
  export { BODY_REGIONS, COSMETIC_MATERIALS } from "./cosmetics";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flow-industries/id",
3
- "version": "0.23.4",
3
+ "version": "0.24.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -88,7 +88,7 @@
88
88
  "contracts:generate": "bun scripts/generate-contracts.ts",
89
89
  "contracts:check": "bun scripts/generate-contracts.ts --check && bun scripts/check-contract-compatibility.ts",
90
90
  "db:check": "bun scripts/db-check.ts",
91
- "test:migrations": "bun test scripts/migrations.test.ts scripts/migration-drift.test.ts"
91
+ "test:migrations": "bun test scripts/migrations.test.ts scripts/migration-drift.test.ts scripts/oauth-migration.test.ts"
92
92
  },
93
93
  "peerDependencies": {
94
94
  "viem": ">=2.47.10",
@@ -139,6 +139,9 @@
139
139
  "sharp"
140
140
  ],
141
141
  "dependencies": {
142
+ "@atproto-labs/handle-resolver": "^0.4.9",
143
+ "@atproto/jwk-jose": "^0.2.4",
144
+ "@atproto/oauth-client": "^0.8.7",
142
145
  "@aws-sdk/client-s3": "^3.1073.0",
143
146
  "@flow-industries/ui": "^0.23.3",
144
147
  "@hono/otel": "^1.1.2",
@@ -160,6 +163,7 @@
160
163
  "drizzle-orm": "^0.45.1",
161
164
  "geist": "^1.7.0",
162
165
  "hono": "^4.11.5",
166
+ "ipaddr.js": "^2.5.0",
163
167
  "jose": "^6.1.3",
164
168
  "lucide-react": "^0.563.0",
165
169
  "motion": "^12.33.0",