@emeryld/rrroutes-client 2.2.11 → 2.2.13
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 +17 -14
- package/dist/index.cjs +112 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +112 -45
- package/dist/index.mjs.map +1 -1
- package/dist/sockets/socketedRoute/socket.client.helper.d.ts +13 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -392,21 +392,24 @@ const listRooms = client.build(registry.byKey['GET /v1/rooms'], { staleTime: 120
|
|
|
392
392
|
|
|
393
393
|
const useSocketedRooms = buildSocketedRoute({
|
|
394
394
|
built: listRooms,
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
395
|
+
toRooms: (page) => ({
|
|
396
|
+
rooms: page.items.map((r) => r.id), // derive rooms from data (feeds supported)
|
|
397
|
+
joinMeta: { source: 'rooms:list' },
|
|
398
|
+
leaveMeta: { source: 'rooms:list' },
|
|
399
|
+
}),
|
|
399
400
|
useSocketClient,
|
|
400
|
-
applyMessage:
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
items
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
401
|
+
applyMessage: {
|
|
402
|
+
'chat:message': (prev, payload) => {
|
|
403
|
+
if (!prev) return prev;
|
|
404
|
+
// Example: bump unread count in cache
|
|
405
|
+
const apply = (items: any[]) =>
|
|
406
|
+
items.map((room) =>
|
|
407
|
+
room.id === payload.roomId ? { ...room, unread: (room.unread ?? 0) + 1 } : room,
|
|
408
|
+
);
|
|
409
|
+
return 'pages' in prev
|
|
410
|
+
? { ...prev, pages: prev.pages.map((p) => ({ ...p, items: apply(p.items) })) }
|
|
411
|
+
: { ...prev, items: apply(prev.items) };
|
|
412
|
+
},
|
|
410
413
|
},
|
|
411
414
|
});
|
|
412
415
|
|
package/dist/index.cjs
CHANGED
|
@@ -71,8 +71,17 @@ var defaultFetcher = async (req) => {
|
|
|
71
71
|
// src/routesV3.client.index.ts
|
|
72
72
|
var import_react = require("react");
|
|
73
73
|
var import_react_query = require("@tanstack/react-query");
|
|
74
|
+
var import_zod = require("zod");
|
|
74
75
|
var import_rrroutes_contract = require("@emeryld/rrroutes-contract");
|
|
75
76
|
var toUpper = (m) => m.toUpperCase();
|
|
77
|
+
var defaultFeedQuerySchema = import_zod.z.object({
|
|
78
|
+
cursor: import_zod.z.string().optional(),
|
|
79
|
+
limit: import_zod.z.coerce.number().min(1).max(100).default(20)
|
|
80
|
+
});
|
|
81
|
+
var defaultFeedOutputSchema = import_zod.z.object({
|
|
82
|
+
items: import_zod.z.array(import_zod.z.unknown()),
|
|
83
|
+
nextCursor: import_zod.z.string().optional()
|
|
84
|
+
});
|
|
76
85
|
function zParse(value, schema) {
|
|
77
86
|
return schema ? schema.parse(value) : value;
|
|
78
87
|
}
|
|
@@ -174,6 +183,37 @@ function extractArgs(args) {
|
|
|
174
183
|
function toArgsTuple(args) {
|
|
175
184
|
return typeof args === "undefined" ? [] : [args];
|
|
176
185
|
}
|
|
186
|
+
function augmentFeedQuerySchema(schema) {
|
|
187
|
+
if (!schema) return defaultFeedQuerySchema;
|
|
188
|
+
if (schema instanceof import_zod.z.ZodObject) {
|
|
189
|
+
const shape = schema.shape ? schema.shape : schema._def?.shape?.();
|
|
190
|
+
return schema.extend({
|
|
191
|
+
...shape ?? {},
|
|
192
|
+
cursor: defaultFeedQuerySchema.shape.cursor,
|
|
193
|
+
limit: defaultFeedQuerySchema.shape.limit
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
return import_zod.z.intersection(schema, defaultFeedQuerySchema);
|
|
197
|
+
}
|
|
198
|
+
function augmentFeedOutputSchema(schema) {
|
|
199
|
+
if (!schema) return defaultFeedOutputSchema;
|
|
200
|
+
if (schema instanceof import_zod.z.ZodObject) {
|
|
201
|
+
const shape = schema.shape ? schema.shape : schema._def?.shape?.();
|
|
202
|
+
const hasItems = Boolean(shape?.items);
|
|
203
|
+
if (hasItems) return schema.extend({ nextCursor: import_zod.z.string().optional() });
|
|
204
|
+
return import_zod.z.object({
|
|
205
|
+
items: import_zod.z.array(schema),
|
|
206
|
+
nextCursor: import_zod.z.string().optional()
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
if (schema instanceof import_zod.z.ZodArray) {
|
|
210
|
+
return import_zod.z.object({
|
|
211
|
+
items: schema,
|
|
212
|
+
nextCursor: import_zod.z.string().optional()
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return defaultFeedOutputSchema;
|
|
216
|
+
}
|
|
177
217
|
function buildUrl(leaf, baseUrl, params, query) {
|
|
178
218
|
const normalizedParams = zParse(params, leaf.cfg.paramsSchema);
|
|
179
219
|
const normalizedQuery = zParse(query, leaf.cfg.querySchema);
|
|
@@ -202,8 +242,13 @@ function createRouteClient(opts) {
|
|
|
202
242
|
function buildInternal(leaf, rqOpts, meta) {
|
|
203
243
|
const isGet = leaf.method === "get";
|
|
204
244
|
const isFeed = !!leaf.cfg.feed;
|
|
245
|
+
const leafCfg = isFeed ? {
|
|
246
|
+
...leaf.cfg,
|
|
247
|
+
querySchema: augmentFeedQuerySchema(leaf.cfg.querySchema),
|
|
248
|
+
outputSchema: augmentFeedOutputSchema(leaf.cfg.outputSchema)
|
|
249
|
+
} : leaf.cfg;
|
|
205
250
|
const method = toUpper(leaf.method);
|
|
206
|
-
const expectsArgs = Boolean(
|
|
251
|
+
const expectsArgs = Boolean(leafCfg.paramsSchema || leafCfg.querySchema);
|
|
207
252
|
const leafLabel = `${leaf.method.toUpperCase()} ${String(leaf.path)}`;
|
|
208
253
|
const debugName = meta?.name;
|
|
209
254
|
const emit = (event) => emitDebug(event, debugName);
|
|
@@ -247,13 +292,18 @@ function createRouteClient(opts) {
|
|
|
247
292
|
const a = extractArgs(tuple);
|
|
248
293
|
const params = a?.params;
|
|
249
294
|
const query = options?.queryOverride ?? a?.query;
|
|
250
|
-
const { url, normalizedQuery, normalizedParams } = buildUrl(
|
|
295
|
+
const { url, normalizedQuery, normalizedParams } = buildUrl(
|
|
296
|
+
{ ...leaf, cfg: leafCfg },
|
|
297
|
+
baseUrl,
|
|
298
|
+
params,
|
|
299
|
+
query
|
|
300
|
+
);
|
|
251
301
|
let payload;
|
|
252
|
-
const acceptsBody = Boolean(
|
|
302
|
+
const acceptsBody = Boolean(leafCfg.bodySchema);
|
|
253
303
|
const requiresBody = options?.requireBody ?? (!isGet && acceptsBody);
|
|
254
304
|
if (typeof options?.body !== "undefined") {
|
|
255
|
-
const normalizedBody = zParse(options.body,
|
|
256
|
-
const isMultipart = Array.isArray(
|
|
305
|
+
const normalizedBody = zParse(options.body, leafCfg.bodySchema);
|
|
306
|
+
const isMultipart = Array.isArray(leafCfg.bodyFiles) && leafCfg.bodyFiles.length > 0;
|
|
257
307
|
payload = isMultipart ? toFormData(normalizedBody) : normalizedBody;
|
|
258
308
|
} else if (requiresBody) {
|
|
259
309
|
throw new Error("Body is required when invoking a mutation fetch.");
|
|
@@ -277,7 +327,7 @@ function createRouteClient(opts) {
|
|
|
277
327
|
const out = await fetcher(
|
|
278
328
|
payload === void 0 ? { url, method } : { url, method, body: payload }
|
|
279
329
|
);
|
|
280
|
-
const parsed = zParse(out,
|
|
330
|
+
const parsed = zParse(out, leafCfg.outputSchema);
|
|
281
331
|
emit(
|
|
282
332
|
decorateDebugEvent(
|
|
283
333
|
{
|
|
@@ -313,7 +363,7 @@ function createRouteClient(opts) {
|
|
|
313
363
|
}
|
|
314
364
|
};
|
|
315
365
|
const fetchGet = (...tupleWithBody) => {
|
|
316
|
-
const acceptsBody = Boolean(
|
|
366
|
+
const acceptsBody = Boolean(leafCfg.bodySchema);
|
|
317
367
|
const tupleLength = tupleWithBody.length;
|
|
318
368
|
const maybeBodyIndex = expectsArgs ? 1 : 0;
|
|
319
369
|
const hasBodyCandidate = acceptsBody && tupleLength > maybeBodyIndex;
|
|
@@ -346,7 +396,12 @@ function createRouteClient(opts) {
|
|
|
346
396
|
},
|
|
347
397
|
[]
|
|
348
398
|
);
|
|
349
|
-
const { normalizedQuery, normalizedParams } = buildUrl(
|
|
399
|
+
const { normalizedQuery, normalizedParams } = buildUrl(
|
|
400
|
+
{ ...leaf, cfg: leafCfg },
|
|
401
|
+
baseUrl,
|
|
402
|
+
params,
|
|
403
|
+
query
|
|
404
|
+
);
|
|
350
405
|
const queryResult = (0, import_react_query.useInfiniteQuery)({
|
|
351
406
|
...buildOptions,
|
|
352
407
|
queryKey: getQueryKeys(...tuple),
|
|
@@ -400,7 +455,7 @@ function createRouteClient(opts) {
|
|
|
400
455
|
},
|
|
401
456
|
[]
|
|
402
457
|
);
|
|
403
|
-
buildUrl(leaf, baseUrl, params, query);
|
|
458
|
+
buildUrl({ ...leaf, cfg: leafCfg }, baseUrl, params, query);
|
|
404
459
|
const queryResult = (0, import_react_query.useQuery)({
|
|
405
460
|
...buildOptions,
|
|
406
461
|
queryKey: getQueryKeys(...tuple),
|
|
@@ -488,9 +543,9 @@ function toFormData(body) {
|
|
|
488
543
|
}
|
|
489
544
|
|
|
490
545
|
// src/sockets/socket.client.sys.ts
|
|
491
|
-
var
|
|
492
|
-
var roomValueSchema =
|
|
493
|
-
var buildRoomPayloadSchema = (metaSchema) =>
|
|
546
|
+
var import_zod2 = require("zod");
|
|
547
|
+
var roomValueSchema = import_zod2.z.union([import_zod2.z.array(import_zod2.z.string()), import_zod2.z.string()]);
|
|
548
|
+
var buildRoomPayloadSchema = (metaSchema) => import_zod2.z.object({
|
|
494
549
|
rooms: roomValueSchema,
|
|
495
550
|
meta: metaSchema
|
|
496
551
|
});
|
|
@@ -731,72 +786,84 @@ function normalizeRooms(rooms) {
|
|
|
731
786
|
}
|
|
732
787
|
return normalized;
|
|
733
788
|
}
|
|
789
|
+
function mergeRoomState(prev, toRoomsResult) {
|
|
790
|
+
const merged = new Set(prev.rooms);
|
|
791
|
+
for (const r of normalizeRooms(toRoomsResult.rooms)) merged.add(r);
|
|
792
|
+
return {
|
|
793
|
+
rooms: Array.from(merged),
|
|
794
|
+
joinMeta: toRoomsResult.joinMeta ?? prev.joinMeta,
|
|
795
|
+
leaveMeta: toRoomsResult.leaveMeta ?? prev.leaveMeta
|
|
796
|
+
};
|
|
797
|
+
}
|
|
734
798
|
function roomsFromData(data, toRooms) {
|
|
735
|
-
if (data == null) return [];
|
|
736
|
-
|
|
799
|
+
if (data == null) return { rooms: [] };
|
|
800
|
+
let state = { rooms: [] };
|
|
737
801
|
const add = (input) => {
|
|
738
|
-
|
|
802
|
+
state = mergeRoomState(state, toRooms(input));
|
|
739
803
|
};
|
|
740
804
|
const maybePages = data?.pages;
|
|
741
805
|
if (Array.isArray(maybePages)) {
|
|
742
|
-
for (const page of maybePages) add(
|
|
743
|
-
return
|
|
806
|
+
for (const page of maybePages) add(page);
|
|
807
|
+
return state;
|
|
744
808
|
}
|
|
745
|
-
add(
|
|
746
|
-
return
|
|
809
|
+
add(data);
|
|
810
|
+
return state;
|
|
747
811
|
}
|
|
748
812
|
function buildSocketedRoute(options) {
|
|
749
|
-
const { built,
|
|
813
|
+
const { built, toRooms, applyMessage, useSocketClient: useSocketClient2 } = options;
|
|
750
814
|
return (...useArgs) => {
|
|
751
815
|
const client = useSocketClient2();
|
|
752
816
|
const endpointResult = built.useEndpoint(...useArgs);
|
|
753
817
|
const argsKey = (0, import_react2.useMemo)(() => JSON.stringify(useArgs[0] ?? null), [useArgs]);
|
|
754
|
-
const [
|
|
818
|
+
const [roomState, setRoomState] = (0, import_react2.useState)(
|
|
755
819
|
() => roomsFromData(endpointResult.data, toRooms)
|
|
756
820
|
);
|
|
757
|
-
const roomsKey = (0, import_react2.useMemo)(() => rooms.join("|"), [rooms]);
|
|
821
|
+
const roomsKey = (0, import_react2.useMemo)(() => roomState.rooms.join("|"), [roomState.rooms]);
|
|
822
|
+
const joinMetaKey = (0, import_react2.useMemo)(() => JSON.stringify(roomState.joinMeta ?? null), [roomState.joinMeta]);
|
|
823
|
+
const leaveMetaKey = (0, import_react2.useMemo)(() => JSON.stringify(roomState.leaveMeta ?? null), [roomState.leaveMeta]);
|
|
758
824
|
(0, import_react2.useEffect)(() => {
|
|
759
825
|
const unsubscribe = endpointResult.onReceive((data) => {
|
|
760
|
-
|
|
761
|
-
const next = normalizeRooms(toRooms(data));
|
|
762
|
-
if (next.length === 0) return prev;
|
|
763
|
-
const merged = new Set(prev);
|
|
764
|
-
next.forEach((r) => merged.add(r));
|
|
765
|
-
return Array.from(merged);
|
|
766
|
-
});
|
|
826
|
+
setRoomState((prev) => mergeRoomState(prev, toRooms(data)));
|
|
767
827
|
});
|
|
768
828
|
return unsubscribe;
|
|
769
829
|
}, [endpointResult, toRooms]);
|
|
770
830
|
(0, import_react2.useEffect)(() => {
|
|
771
|
-
|
|
831
|
+
setRoomState(roomsFromData(endpointResult.data, toRooms));
|
|
772
832
|
}, [endpointResult.data, toRooms]);
|
|
773
833
|
(0, import_react2.useEffect)(() => {
|
|
774
|
-
if (rooms.length === 0) return;
|
|
834
|
+
if (roomState.rooms.length === 0) return;
|
|
835
|
+
const { joinMeta, leaveMeta } = roomState;
|
|
836
|
+
if (!joinMeta || !leaveMeta) return;
|
|
775
837
|
let active = true;
|
|
776
838
|
(async () => {
|
|
777
839
|
try {
|
|
778
|
-
await client.joinRooms(rooms, joinMeta);
|
|
840
|
+
await client.joinRooms(roomState.rooms, joinMeta);
|
|
779
841
|
} catch {
|
|
780
842
|
}
|
|
781
843
|
})();
|
|
782
844
|
return () => {
|
|
783
|
-
if (!active || rooms.length === 0) return;
|
|
845
|
+
if (!active || roomState.rooms.length === 0) return;
|
|
784
846
|
active = false;
|
|
785
|
-
void client.leaveRooms(rooms, leaveMeta).catch(() => {
|
|
847
|
+
void client.leaveRooms(roomState.rooms, leaveMeta).catch(() => {
|
|
786
848
|
});
|
|
787
849
|
};
|
|
788
|
-
}, [client, joinMeta, leaveMeta,
|
|
850
|
+
}, [client, roomsKey, roomState.joinMeta, roomState.leaveMeta, joinMetaKey, leaveMetaKey]);
|
|
789
851
|
(0, import_react2.useEffect)(() => {
|
|
790
|
-
const
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
852
|
+
const entries = Object.entries(applyMessage).filter(
|
|
853
|
+
([_event, fn]) => typeof fn === "function"
|
|
854
|
+
);
|
|
855
|
+
const unsubscribes = entries.map(
|
|
856
|
+
([ev, fn]) => client.on(ev, (payload, meta) => {
|
|
857
|
+
built.setData((prev) => {
|
|
858
|
+
const next = fn(prev, payload, meta);
|
|
859
|
+
setRoomState(roomsFromData(next, toRooms));
|
|
860
|
+
return next;
|
|
861
|
+
}, ...useArgs);
|
|
862
|
+
})
|
|
863
|
+
);
|
|
864
|
+
return () => unsubscribes.forEach((u) => u?.());
|
|
865
|
+
}, [client, applyMessage, built, argsKey, toRooms]);
|
|
866
|
+
return { ...endpointResult, rooms: roomState.rooms };
|
|
800
867
|
};
|
|
801
868
|
}
|
|
802
869
|
|