@emeryld/rrroutes-client 2.4.1 → 2.4.4
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/dist/index.cjs +929 -480
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +924 -476
- package/dist/index.mjs.map +1 -1
- package/dist/routesV3.client.d.ts +4 -0
- package/dist/routesV3.client.get.d.ts +15 -0
- package/dist/routesV3.client.infiniteGet.d.ts +15 -0
- package/dist/routesV3.client.mutation.d.ts +15 -0
- package/dist/routesV3.client.shared.d.ts +44 -0
- package/dist/routesV3.client.types.d.ts +15 -0
- package/package.json +1 -1
- package/dist/routesV3.client.index.d.ts +0 -15
package/dist/index.cjs
CHANGED
|
@@ -95,10 +95,13 @@ var defaultFetcher = async (req) => {
|
|
|
95
95
|
}
|
|
96
96
|
};
|
|
97
97
|
|
|
98
|
-
// src/routesV3.client.
|
|
99
|
-
var
|
|
98
|
+
// src/routesV3.client.get.ts
|
|
99
|
+
var import_rrroutes_contract2 = require("@emeryld/rrroutes-contract");
|
|
100
100
|
var import_react_query = require("@tanstack/react-query");
|
|
101
101
|
var import_react = require("react");
|
|
102
|
+
|
|
103
|
+
// src/routesV3.client.shared.ts
|
|
104
|
+
var import_rrroutes_contract = require("@emeryld/rrroutes-contract");
|
|
102
105
|
var toUpper = (m) => m.toUpperCase();
|
|
103
106
|
function toSearchString(query) {
|
|
104
107
|
if (!query) return "";
|
|
@@ -109,34 +112,895 @@ function toSearchString(query) {
|
|
|
109
112
|
params.append(k, v);
|
|
110
113
|
continue;
|
|
111
114
|
}
|
|
112
|
-
if (typeof v === "number" || typeof v === "boolean") {
|
|
113
|
-
params.append(k, String(v));
|
|
114
|
-
continue;
|
|
115
|
+
if (typeof v === "number" || typeof v === "boolean") {
|
|
116
|
+
params.append(k, String(v));
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
params.append(k, JSON.stringify(v));
|
|
120
|
+
}
|
|
121
|
+
const s = params.toString();
|
|
122
|
+
return s ? `?${s}` : "";
|
|
123
|
+
}
|
|
124
|
+
function stripKey(obj, key) {
|
|
125
|
+
if (!obj) return obj;
|
|
126
|
+
const { [key]: _omit, ...rest } = obj;
|
|
127
|
+
return rest;
|
|
128
|
+
}
|
|
129
|
+
var defaultGetNextCursor = (p) => {
|
|
130
|
+
if (!p || typeof p !== "object") return void 0;
|
|
131
|
+
const record = p;
|
|
132
|
+
if ("nextCursor" in record) {
|
|
133
|
+
return record.nextCursor;
|
|
134
|
+
}
|
|
135
|
+
if ("meta" in record) {
|
|
136
|
+
const meta = record.meta;
|
|
137
|
+
if (meta && typeof meta === "object" && "nextCursor" in meta) {
|
|
138
|
+
return meta.nextCursor;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return void 0;
|
|
142
|
+
};
|
|
143
|
+
function extractArgs(args) {
|
|
144
|
+
return args[0];
|
|
145
|
+
}
|
|
146
|
+
function toArgsTuple(args) {
|
|
147
|
+
return typeof args === "undefined" ? [] : [args];
|
|
148
|
+
}
|
|
149
|
+
function buildUrl(leaf, baseUrl, params, query) {
|
|
150
|
+
const normalizedParams = leaf.cfg.paramsSchema ? (0, import_rrroutes_contract.lowProfileParse)(leaf.cfg.paramsSchema, params) : {};
|
|
151
|
+
const normalizedQuery = leaf.cfg.querySchema ? (0, import_rrroutes_contract.lowProfileParse)(leaf.cfg.querySchema, query) : {};
|
|
152
|
+
const path = (0, import_rrroutes_contract.compilePath)(
|
|
153
|
+
leaf.path,
|
|
154
|
+
normalizedParams ?? {}
|
|
155
|
+
);
|
|
156
|
+
const url = `${baseUrl ?? ""}${path}${toSearchString(normalizedQuery)}`;
|
|
157
|
+
return { url, normalizedQuery, normalizedParams };
|
|
158
|
+
}
|
|
159
|
+
function toFormData(body) {
|
|
160
|
+
const fd = new FormData();
|
|
161
|
+
for (const [k, v] of Object.entries(body ?? {})) {
|
|
162
|
+
if (v == null) continue;
|
|
163
|
+
if (Array.isArray(v))
|
|
164
|
+
v.forEach((item, i) => fd.append(`${k}[${i}]`, item));
|
|
165
|
+
else fd.append(k, v);
|
|
166
|
+
}
|
|
167
|
+
return fd;
|
|
168
|
+
}
|
|
169
|
+
function getPathParamNames(path) {
|
|
170
|
+
const names = /* @__PURE__ */ new Set();
|
|
171
|
+
const re = /:([A-Za-z0-9_]+)/g;
|
|
172
|
+
let match;
|
|
173
|
+
while ((match = re.exec(path)) !== null) {
|
|
174
|
+
names.add(match[1]);
|
|
175
|
+
}
|
|
176
|
+
return names;
|
|
177
|
+
}
|
|
178
|
+
function normalizeFlatQuery(query) {
|
|
179
|
+
if (query == null) return void 0;
|
|
180
|
+
if (typeof query !== "object" || Array.isArray(query)) {
|
|
181
|
+
throw new Error("Query must be a plain object (Record<string, string>).");
|
|
182
|
+
}
|
|
183
|
+
const result = {};
|
|
184
|
+
for (const [k, v] of Object.entries(query)) {
|
|
185
|
+
if (v == null) continue;
|
|
186
|
+
if (typeof v !== "string") {
|
|
187
|
+
throw new Error(
|
|
188
|
+
`Query param "${k}" must be a string; received type "${typeof v}".`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
result[k] = v;
|
|
192
|
+
}
|
|
193
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
194
|
+
}
|
|
195
|
+
function compileRawPath(path, params) {
|
|
196
|
+
const placeholders = getPathParamNames(path);
|
|
197
|
+
if (!params || typeof params !== "object" || Array.isArray(params)) {
|
|
198
|
+
if (placeholders.size > 0) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
`Missing path parameters for "${path}": ${[...placeholders].join(
|
|
201
|
+
", "
|
|
202
|
+
)}`
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
return path;
|
|
206
|
+
}
|
|
207
|
+
const paramObj = params;
|
|
208
|
+
const providedNames = new Set(Object.keys(paramObj));
|
|
209
|
+
for (const name of providedNames) {
|
|
210
|
+
if (!placeholders.has(name)) {
|
|
211
|
+
throw new Error(
|
|
212
|
+
`Unexpected path parameter "${name}" for template "${path}".`
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
const value = paramObj[name];
|
|
216
|
+
if (value != null && (typeof value === "object" || Array.isArray(value))) {
|
|
217
|
+
throw new Error(
|
|
218
|
+
`Path parameter "${name}" must be a primitive; received "${typeof value}".`
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
for (const name of placeholders) {
|
|
223
|
+
if (!providedNames.has(name)) {
|
|
224
|
+
throw new Error(
|
|
225
|
+
`Missing value for path parameter "${name}" in template "${path}".`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (placeholders.size === 0) {
|
|
230
|
+
return path;
|
|
231
|
+
}
|
|
232
|
+
return (0, import_rrroutes_contract.compilePath)(path, paramObj);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/routesV3.client.get.ts
|
|
236
|
+
function buildGetLeaf(leaf, rqOpts, env) {
|
|
237
|
+
const leafCfg = leaf.cfg;
|
|
238
|
+
const method = toUpper(leaf.method);
|
|
239
|
+
const expectsArgs = Boolean(leafCfg.paramsSchema || leafCfg.querySchema);
|
|
240
|
+
const {
|
|
241
|
+
baseUrl,
|
|
242
|
+
validateResponses,
|
|
243
|
+
fetcher,
|
|
244
|
+
queryClient,
|
|
245
|
+
emit,
|
|
246
|
+
decorateDebugEvent,
|
|
247
|
+
isVerboseDebug,
|
|
248
|
+
leafLabel
|
|
249
|
+
} = env;
|
|
250
|
+
emit({ type: "build", leaf: leafLabel });
|
|
251
|
+
const getQueryKeys = (...tuple) => {
|
|
252
|
+
const a = extractArgs(tuple);
|
|
253
|
+
const params = a?.params;
|
|
254
|
+
const query = a?.query;
|
|
255
|
+
return (0, import_rrroutes_contract2.buildCacheKey)({
|
|
256
|
+
leaf,
|
|
257
|
+
params,
|
|
258
|
+
query
|
|
259
|
+
});
|
|
260
|
+
};
|
|
261
|
+
const invalidateExact = async (...tuple) => {
|
|
262
|
+
const queryKey = getQueryKeys(...tuple);
|
|
263
|
+
await queryClient.invalidateQueries({ queryKey, exact: true });
|
|
264
|
+
emit({ type: "invalidate", key: queryKey, exact: true });
|
|
265
|
+
};
|
|
266
|
+
const setData = (...args) => {
|
|
267
|
+
const [updater, ...rest] = args;
|
|
268
|
+
const k = getQueryKeys(...rest);
|
|
269
|
+
const next = queryClient.setQueryData(
|
|
270
|
+
k,
|
|
271
|
+
(prev) => typeof updater === "function" ? updater(prev) : updater
|
|
272
|
+
);
|
|
273
|
+
emit({ type: "setData", key: k });
|
|
274
|
+
return next;
|
|
275
|
+
};
|
|
276
|
+
const buildOnReceive = rqOpts?.onReceive ?? void 0;
|
|
277
|
+
const fetchEndpoint = async (tuple, options) => {
|
|
278
|
+
const a = extractArgs(tuple);
|
|
279
|
+
const params = a?.params;
|
|
280
|
+
const query = options?.queryOverride ?? a?.query;
|
|
281
|
+
const { url, normalizedQuery, normalizedParams } = buildUrl(
|
|
282
|
+
{ ...leaf, cfg: leafCfg },
|
|
283
|
+
baseUrl,
|
|
284
|
+
params,
|
|
285
|
+
query
|
|
286
|
+
);
|
|
287
|
+
let payload;
|
|
288
|
+
const acceptsBody = Boolean(leafCfg.bodySchema);
|
|
289
|
+
const requiresBody = options?.requireBody ?? (!acceptsBody ? false : true);
|
|
290
|
+
if (typeof options?.body !== "undefined") {
|
|
291
|
+
const normalizedBody = leafCfg.bodySchema ? (0, import_rrroutes_contract2.lowProfileParse)(leafCfg.bodySchema, options.body) : void 0;
|
|
292
|
+
const isMultipart = Array.isArray(leafCfg.bodyFiles) && leafCfg.bodyFiles.length > 0;
|
|
293
|
+
if (isMultipart && normalizedBody && typeof normalizedBody === "object") {
|
|
294
|
+
payload = toFormData(normalizedBody);
|
|
295
|
+
} else {
|
|
296
|
+
payload = normalizedBody;
|
|
297
|
+
}
|
|
298
|
+
} else if (requiresBody && acceptsBody) {
|
|
299
|
+
throw new Error("Body is required when invoking a mutation fetch.");
|
|
300
|
+
}
|
|
301
|
+
const startedAt = Date.now();
|
|
302
|
+
const detail = isVerboseDebug ? { params, normalizedParams, query, normalizedQuery, baseUrl } : void 0;
|
|
303
|
+
emit(
|
|
304
|
+
decorateDebugEvent(
|
|
305
|
+
{
|
|
306
|
+
type: "fetch",
|
|
307
|
+
stage: "start",
|
|
308
|
+
method,
|
|
309
|
+
url,
|
|
310
|
+
leaf: leafLabel,
|
|
311
|
+
...payload !== void 0 ? { body: payload } : {}
|
|
312
|
+
},
|
|
313
|
+
detail
|
|
314
|
+
)
|
|
315
|
+
);
|
|
316
|
+
try {
|
|
317
|
+
const out = await fetcher(
|
|
318
|
+
payload === void 0 ? { url, method } : { url, method, body: payload }
|
|
319
|
+
);
|
|
320
|
+
emit(
|
|
321
|
+
decorateDebugEvent(
|
|
322
|
+
{
|
|
323
|
+
type: "fetch",
|
|
324
|
+
stage: "fetched",
|
|
325
|
+
method,
|
|
326
|
+
url,
|
|
327
|
+
leaf: leafLabel,
|
|
328
|
+
durationMs: Date.now() - startedAt
|
|
329
|
+
},
|
|
330
|
+
isVerboseDebug ? {
|
|
331
|
+
params: normalizedParams,
|
|
332
|
+
query: normalizedQuery,
|
|
333
|
+
output: out
|
|
334
|
+
} : void 0
|
|
335
|
+
)
|
|
336
|
+
);
|
|
337
|
+
if (validateResponses) {
|
|
338
|
+
if (!leafCfg.outputSchema) {
|
|
339
|
+
throw new Error(
|
|
340
|
+
`No output schema defined for leaf ${leafLabel}, cannot validate response.`
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
out.data = (0, import_rrroutes_contract2.lowProfileParse)(
|
|
344
|
+
leafCfg.outputSchema,
|
|
345
|
+
out.data
|
|
346
|
+
);
|
|
347
|
+
emit(
|
|
348
|
+
decorateDebugEvent(
|
|
349
|
+
{
|
|
350
|
+
type: "fetch",
|
|
351
|
+
stage: "parsed",
|
|
352
|
+
method,
|
|
353
|
+
url,
|
|
354
|
+
leaf: leafLabel,
|
|
355
|
+
durationMs: Date.now() - startedAt
|
|
356
|
+
},
|
|
357
|
+
isVerboseDebug ? {
|
|
358
|
+
params: normalizedParams,
|
|
359
|
+
query: normalizedQuery,
|
|
360
|
+
output: out
|
|
361
|
+
} : void 0
|
|
362
|
+
)
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
options?.onReceive?.(out.data);
|
|
366
|
+
return out.data;
|
|
367
|
+
} catch (error) {
|
|
368
|
+
emit(
|
|
369
|
+
decorateDebugEvent(
|
|
370
|
+
{
|
|
371
|
+
type: "fetch",
|
|
372
|
+
stage: "error",
|
|
373
|
+
method,
|
|
374
|
+
url,
|
|
375
|
+
leaf: leafLabel,
|
|
376
|
+
durationMs: Date.now() - startedAt,
|
|
377
|
+
...payload !== void 0 ? { body: payload } : {},
|
|
378
|
+
error
|
|
379
|
+
},
|
|
380
|
+
detail
|
|
381
|
+
)
|
|
382
|
+
);
|
|
383
|
+
throw error;
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
const fetchGet = (...tupleWithBody) => {
|
|
387
|
+
const acceptsBody = Boolean(leafCfg.bodySchema);
|
|
388
|
+
const tupleLength = tupleWithBody.length;
|
|
389
|
+
const maybeBodyIndex = expectsArgs ? 1 : 0;
|
|
390
|
+
const hasBodyCandidate = acceptsBody && tupleLength > maybeBodyIndex;
|
|
391
|
+
const body = hasBodyCandidate ? tupleWithBody[tupleLength - 1] : void 0;
|
|
392
|
+
const tuple = hasBodyCandidate ? tupleWithBody.slice(0, tupleLength - 1) : tupleWithBody;
|
|
393
|
+
return fetchEndpoint(tuple, {
|
|
394
|
+
body,
|
|
395
|
+
onReceive: buildOnReceive,
|
|
396
|
+
requireBody: false
|
|
397
|
+
});
|
|
398
|
+
};
|
|
399
|
+
const useEndpoint = (...useArgs) => {
|
|
400
|
+
const args = useArgs[0];
|
|
401
|
+
const tuple = toArgsTuple(args);
|
|
402
|
+
const queryKeys = getQueryKeys(...tuple);
|
|
403
|
+
emit({
|
|
404
|
+
type: "useEndpoint",
|
|
405
|
+
leaf: leafLabel,
|
|
406
|
+
variant: "get",
|
|
407
|
+
keys: queryKeys
|
|
408
|
+
});
|
|
409
|
+
const buildOptions = rqOpts ?? {};
|
|
410
|
+
const listenersRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
|
|
411
|
+
const notifyOnReceive = (0, import_react.useCallback)((data) => {
|
|
412
|
+
buildOptions?.onReceive?.(data);
|
|
413
|
+
listenersRef.current.forEach((listener) => listener(data));
|
|
414
|
+
}, []);
|
|
415
|
+
const registerOnReceive = (0, import_react.useCallback)(
|
|
416
|
+
(listener) => {
|
|
417
|
+
listenersRef.current.add(listener);
|
|
418
|
+
return () => {
|
|
419
|
+
listenersRef.current.delete(listener);
|
|
420
|
+
};
|
|
421
|
+
},
|
|
422
|
+
[]
|
|
423
|
+
);
|
|
424
|
+
const queryResult = (0, import_react_query.useQuery)(
|
|
425
|
+
{
|
|
426
|
+
...buildOptions,
|
|
427
|
+
queryKey: getQueryKeys(...tuple),
|
|
428
|
+
placeholderData: import_react_query.keepPreviousData,
|
|
429
|
+
queryFn: () => fetchEndpoint(tuple, {
|
|
430
|
+
onReceive: notifyOnReceive
|
|
431
|
+
})
|
|
432
|
+
},
|
|
433
|
+
queryClient
|
|
434
|
+
);
|
|
435
|
+
return { ...queryResult, onReceive: registerOnReceive };
|
|
436
|
+
};
|
|
437
|
+
return {
|
|
438
|
+
getQueryKeys,
|
|
439
|
+
invalidate: invalidateExact,
|
|
440
|
+
setData,
|
|
441
|
+
useEndpoint,
|
|
442
|
+
fetch: fetchGet
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// src/routesV3.client.infiniteGet.ts
|
|
447
|
+
var import_rrroutes_contract3 = require("@emeryld/rrroutes-contract");
|
|
448
|
+
var import_react_query2 = require("@tanstack/react-query");
|
|
449
|
+
var import_react2 = require("react");
|
|
450
|
+
function mergePageOutputs(prev, next) {
|
|
451
|
+
if (prev == null) return next;
|
|
452
|
+
if (next == null) return prev;
|
|
453
|
+
if (Array.isArray(prev) && Array.isArray(next)) {
|
|
454
|
+
return [...prev, ...next];
|
|
455
|
+
}
|
|
456
|
+
if (typeof prev !== "object" || typeof next !== "object") {
|
|
457
|
+
return next;
|
|
458
|
+
}
|
|
459
|
+
const merged = { ...prev };
|
|
460
|
+
for (const key of Object.keys(next)) {
|
|
461
|
+
const pv = prev[key];
|
|
462
|
+
const nv = next[key];
|
|
463
|
+
if (Array.isArray(pv) && Array.isArray(nv)) {
|
|
464
|
+
merged[key] = [...pv, ...nv];
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
if (pv && typeof pv === "object" && !Array.isArray(pv) && nv && typeof nv === "object" && !Array.isArray(nv)) {
|
|
468
|
+
if (key === "meta") {
|
|
469
|
+
merged[key] = mergePageOutputs(pv, nv);
|
|
470
|
+
} else {
|
|
471
|
+
merged[key] = nv;
|
|
472
|
+
}
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
merged[key] = nv;
|
|
476
|
+
}
|
|
477
|
+
return merged;
|
|
478
|
+
}
|
|
479
|
+
function buildInfiniteGetLeaf(leaf, rqOpts, env) {
|
|
480
|
+
const leafCfg = leaf.cfg;
|
|
481
|
+
const method = toUpper(leaf.method);
|
|
482
|
+
const expectsArgs = Boolean(leafCfg.paramsSchema || leafCfg.querySchema);
|
|
483
|
+
const {
|
|
484
|
+
baseUrl,
|
|
485
|
+
validateResponses,
|
|
486
|
+
fetcher,
|
|
487
|
+
queryClient,
|
|
488
|
+
emit,
|
|
489
|
+
decorateDebugEvent,
|
|
490
|
+
isVerboseDebug,
|
|
491
|
+
leafLabel
|
|
492
|
+
} = env;
|
|
493
|
+
emit({ type: "build", leaf: leafLabel });
|
|
494
|
+
const infiniteOptions = rqOpts ?? {};
|
|
495
|
+
const {
|
|
496
|
+
cursorParam,
|
|
497
|
+
getNextPageParam,
|
|
498
|
+
initialPageParam,
|
|
499
|
+
splitPageSize,
|
|
500
|
+
splitPageSizeParam,
|
|
501
|
+
...passthroughOptions
|
|
502
|
+
} = infiniteOptions;
|
|
503
|
+
const feedCursorParam = cursorParam ?? "pagination_cursor";
|
|
504
|
+
const feedNextPageParam = getNextPageParam ?? ((lastPage) => defaultGetNextCursor(lastPage));
|
|
505
|
+
const cursorFromPage = (page) => feedNextPageParam(page);
|
|
506
|
+
const feedInitialPageParam = typeof initialPageParam === "undefined" ? void 0 : initialPageParam;
|
|
507
|
+
const feedQueryOptions = passthroughOptions;
|
|
508
|
+
const effectiveSplitPageSize = typeof splitPageSize === "number" && splitPageSize > 0 ? splitPageSize : void 0;
|
|
509
|
+
const effectiveSplitPageSizeParam = splitPageSizeParam ?? "pageSize";
|
|
510
|
+
const getQueryKeys = (...tuple) => {
|
|
511
|
+
const a = extractArgs(tuple);
|
|
512
|
+
const params = a?.params;
|
|
513
|
+
const query = a?.query;
|
|
514
|
+
const qForKey = stripKey(query, feedCursorParam);
|
|
515
|
+
return (0, import_rrroutes_contract3.buildCacheKey)({
|
|
516
|
+
leaf,
|
|
517
|
+
params,
|
|
518
|
+
query: qForKey
|
|
519
|
+
});
|
|
520
|
+
};
|
|
521
|
+
const invalidateExact = async (...tuple) => {
|
|
522
|
+
const queryKey = getQueryKeys(...tuple);
|
|
523
|
+
await queryClient.invalidateQueries({ queryKey, exact: true });
|
|
524
|
+
emit({ type: "invalidate", key: queryKey, exact: true });
|
|
525
|
+
};
|
|
526
|
+
const setData = (...args) => {
|
|
527
|
+
const [updater, ...rest] = args;
|
|
528
|
+
const k = getQueryKeys(...rest);
|
|
529
|
+
const next = queryClient.setQueryData(
|
|
530
|
+
k,
|
|
531
|
+
(prev) => typeof updater === "function" ? updater(prev) : updater
|
|
532
|
+
);
|
|
533
|
+
emit({ type: "setData", key: k });
|
|
534
|
+
return next;
|
|
535
|
+
};
|
|
536
|
+
const buildOnReceive = rqOpts?.onReceive ?? void 0;
|
|
537
|
+
const fetchEndpoint = async (tuple, options) => {
|
|
538
|
+
const a = extractArgs(tuple);
|
|
539
|
+
const params = a?.params;
|
|
540
|
+
const query = options?.queryOverride ?? a?.query;
|
|
541
|
+
const { url, normalizedQuery, normalizedParams } = buildUrl(
|
|
542
|
+
{ ...leaf, cfg: leafCfg },
|
|
543
|
+
baseUrl,
|
|
544
|
+
params,
|
|
545
|
+
query
|
|
546
|
+
);
|
|
547
|
+
let payload;
|
|
548
|
+
const acceptsBody = Boolean(leafCfg.bodySchema);
|
|
549
|
+
const requiresBody = options?.requireBody ?? (!acceptsBody ? false : true);
|
|
550
|
+
if (typeof options?.body !== "undefined") {
|
|
551
|
+
const normalizedBody = leafCfg.bodySchema ? (0, import_rrroutes_contract3.lowProfileParse)(leafCfg.bodySchema, options.body) : void 0;
|
|
552
|
+
const isMultipart = Array.isArray(leafCfg.bodyFiles) && leafCfg.bodyFiles.length > 0;
|
|
553
|
+
if (isMultipart && normalizedBody && typeof normalizedBody === "object") {
|
|
554
|
+
payload = toFormData(normalizedBody);
|
|
555
|
+
} else {
|
|
556
|
+
payload = normalizedBody;
|
|
557
|
+
}
|
|
558
|
+
} else if (requiresBody && acceptsBody) {
|
|
559
|
+
throw new Error("Body is required when invoking a mutation fetch.");
|
|
560
|
+
}
|
|
561
|
+
const startedAt = Date.now();
|
|
562
|
+
const detail = isVerboseDebug ? { params, normalizedParams, query, normalizedQuery, baseUrl } : void 0;
|
|
563
|
+
emit(
|
|
564
|
+
decorateDebugEvent(
|
|
565
|
+
{
|
|
566
|
+
type: "fetch",
|
|
567
|
+
stage: "start",
|
|
568
|
+
method,
|
|
569
|
+
url,
|
|
570
|
+
leaf: leafLabel,
|
|
571
|
+
...payload !== void 0 ? { body: payload } : {}
|
|
572
|
+
},
|
|
573
|
+
detail
|
|
574
|
+
)
|
|
575
|
+
);
|
|
576
|
+
try {
|
|
577
|
+
const out = await fetcher(
|
|
578
|
+
payload === void 0 ? { url, method } : { url, method, body: payload }
|
|
579
|
+
);
|
|
580
|
+
emit(
|
|
581
|
+
decorateDebugEvent(
|
|
582
|
+
{
|
|
583
|
+
type: "fetch",
|
|
584
|
+
stage: "fetched",
|
|
585
|
+
method,
|
|
586
|
+
url,
|
|
587
|
+
leaf: leafLabel,
|
|
588
|
+
durationMs: Date.now() - startedAt
|
|
589
|
+
},
|
|
590
|
+
isVerboseDebug ? {
|
|
591
|
+
params: normalizedParams,
|
|
592
|
+
query: normalizedQuery,
|
|
593
|
+
output: out
|
|
594
|
+
} : void 0
|
|
595
|
+
)
|
|
596
|
+
);
|
|
597
|
+
if (validateResponses) {
|
|
598
|
+
if (!leafCfg.outputSchema) {
|
|
599
|
+
throw new Error(
|
|
600
|
+
`No output schema defined for leaf ${leafLabel}, cannot validate response.`
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
out.data = (0, import_rrroutes_contract3.lowProfileParse)(
|
|
604
|
+
leafCfg.outputSchema,
|
|
605
|
+
out.data
|
|
606
|
+
);
|
|
607
|
+
emit(
|
|
608
|
+
decorateDebugEvent(
|
|
609
|
+
{
|
|
610
|
+
type: "fetch",
|
|
611
|
+
stage: "parsed",
|
|
612
|
+
method,
|
|
613
|
+
url,
|
|
614
|
+
leaf: leafLabel,
|
|
615
|
+
durationMs: Date.now() - startedAt
|
|
616
|
+
},
|
|
617
|
+
isVerboseDebug ? {
|
|
618
|
+
params: normalizedParams,
|
|
619
|
+
query: normalizedQuery,
|
|
620
|
+
output: out
|
|
621
|
+
} : void 0
|
|
622
|
+
)
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
options?.onReceive?.(out.data);
|
|
626
|
+
return out.data;
|
|
627
|
+
} catch (error) {
|
|
628
|
+
emit(
|
|
629
|
+
decorateDebugEvent(
|
|
630
|
+
{
|
|
631
|
+
type: "fetch",
|
|
632
|
+
stage: "error",
|
|
633
|
+
method,
|
|
634
|
+
url,
|
|
635
|
+
leaf: leafLabel,
|
|
636
|
+
durationMs: Date.now() - startedAt,
|
|
637
|
+
...payload !== void 0 ? { body: payload } : {},
|
|
638
|
+
error
|
|
639
|
+
},
|
|
640
|
+
detail
|
|
641
|
+
)
|
|
642
|
+
);
|
|
643
|
+
throw error;
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
const fetchGet = (...tupleWithBody) => {
|
|
647
|
+
const acceptsBody = Boolean(leafCfg.bodySchema);
|
|
648
|
+
const tupleLength = tupleWithBody.length;
|
|
649
|
+
const maybeBodyIndex = expectsArgs ? 1 : 0;
|
|
650
|
+
const hasBodyCandidate = acceptsBody && tupleLength > maybeBodyIndex;
|
|
651
|
+
const body = hasBodyCandidate ? tupleWithBody[tupleLength - 1] : void 0;
|
|
652
|
+
const tuple = hasBodyCandidate ? tupleWithBody.slice(0, tupleLength - 1) : tupleWithBody;
|
|
653
|
+
return fetchEndpoint(tuple, {
|
|
654
|
+
body,
|
|
655
|
+
onReceive: buildOnReceive,
|
|
656
|
+
requireBody: false
|
|
657
|
+
});
|
|
658
|
+
};
|
|
659
|
+
const useEndpoint = (...useArgs) => {
|
|
660
|
+
const args = useArgs[0];
|
|
661
|
+
const tuple = toArgsTuple(args);
|
|
662
|
+
const queryKeys = getQueryKeys(...tuple);
|
|
663
|
+
emit({
|
|
664
|
+
type: "useEndpoint",
|
|
665
|
+
leaf: leafLabel,
|
|
666
|
+
variant: "infiniteGet",
|
|
667
|
+
keys: queryKeys
|
|
668
|
+
});
|
|
669
|
+
const params = args?.params;
|
|
670
|
+
const query = args?.query;
|
|
671
|
+
const buildOptions = feedQueryOptions ?? {};
|
|
672
|
+
const listenersRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
673
|
+
const notifyOnReceive = (0, import_react2.useCallback)((data) => {
|
|
674
|
+
buildOptions?.onReceive?.(data);
|
|
675
|
+
listenersRef.current.forEach((listener) => listener(data));
|
|
676
|
+
}, []);
|
|
677
|
+
const registerOnReceive = (0, import_react2.useCallback)(
|
|
678
|
+
(listener) => {
|
|
679
|
+
listenersRef.current.add(listener);
|
|
680
|
+
return () => {
|
|
681
|
+
listenersRef.current.delete(listener);
|
|
682
|
+
};
|
|
683
|
+
},
|
|
684
|
+
[]
|
|
685
|
+
);
|
|
686
|
+
const { normalizedQuery, normalizedParams } = buildUrl(
|
|
687
|
+
{ ...leaf, cfg: leafCfg },
|
|
688
|
+
baseUrl,
|
|
689
|
+
params,
|
|
690
|
+
query
|
|
691
|
+
);
|
|
692
|
+
const queryResult = (0, import_react_query2.useInfiniteQuery)(
|
|
693
|
+
{
|
|
694
|
+
...buildOptions,
|
|
695
|
+
placeholderData: buildOptions.placeholderData ?? import_react_query2.keepPreviousData,
|
|
696
|
+
initialPageParam: feedInitialPageParam,
|
|
697
|
+
getNextPageParam: (lastPage) => cursorFromPage(lastPage),
|
|
698
|
+
queryKey: queryKeys,
|
|
699
|
+
queryFn: ({ pageParam }) => {
|
|
700
|
+
if (!effectiveSplitPageSize) {
|
|
701
|
+
const pageQuery = {
|
|
702
|
+
...normalizedQuery,
|
|
703
|
+
...pageParam ? { [feedCursorParam]: pageParam } : {}
|
|
704
|
+
};
|
|
705
|
+
return fetchEndpoint(tuple, {
|
|
706
|
+
queryOverride: pageQuery,
|
|
707
|
+
onReceive: notifyOnReceive
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
const basePageSizeRaw = normalizedQuery?.[effectiveSplitPageSizeParam];
|
|
711
|
+
const basePageSize = typeof basePageSizeRaw === "number" ? basePageSizeRaw : basePageSizeRaw != null ? Number(basePageSizeRaw) : void 0;
|
|
712
|
+
if (!basePageSize || !Number.isFinite(basePageSize) || basePageSize <= effectiveSplitPageSize) {
|
|
713
|
+
const pageQuery = {
|
|
714
|
+
...normalizedQuery,
|
|
715
|
+
...pageParam ? { [feedCursorParam]: pageParam } : {}
|
|
716
|
+
};
|
|
717
|
+
return fetchEndpoint(tuple, {
|
|
718
|
+
queryOverride: pageQuery,
|
|
719
|
+
onReceive: notifyOnReceive
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
const totalTarget = basePageSize;
|
|
723
|
+
const pageParamForThisPage = pageParam;
|
|
724
|
+
const runSplitFetch = async () => {
|
|
725
|
+
let remaining = totalTarget;
|
|
726
|
+
let currentCursor = pageParam;
|
|
727
|
+
let aggregated;
|
|
728
|
+
while (remaining > 0) {
|
|
729
|
+
const thisCallSize = Math.min(remaining, effectiveSplitPageSize);
|
|
730
|
+
const splitQuery = {
|
|
731
|
+
...normalizedQuery,
|
|
732
|
+
...currentCursor ? { [feedCursorParam]: currentCursor } : {},
|
|
733
|
+
[effectiveSplitPageSizeParam]: thisCallSize
|
|
734
|
+
};
|
|
735
|
+
const page = await fetchEndpoint(tuple, {
|
|
736
|
+
queryOverride: splitQuery,
|
|
737
|
+
onReceive: notifyOnReceive
|
|
738
|
+
});
|
|
739
|
+
aggregated = aggregated ? mergePageOutputs(aggregated, page) : page;
|
|
740
|
+
remaining -= thisCallSize;
|
|
741
|
+
const nextCursor = cursorFromPage(page);
|
|
742
|
+
currentCursor = nextCursor;
|
|
743
|
+
const k = queryKeys;
|
|
744
|
+
queryClient.setQueryData(k, (prev) => {
|
|
745
|
+
if (!aggregated) return prev;
|
|
746
|
+
if (!prev) {
|
|
747
|
+
return {
|
|
748
|
+
pages: [aggregated],
|
|
749
|
+
pageParams: [pageParamForThisPage]
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
const idx = prev.pageParams.findIndex(
|
|
753
|
+
(p) => p === pageParamForThisPage || p == null && pageParamForThisPage == null
|
|
754
|
+
);
|
|
755
|
+
if (idx === -1) {
|
|
756
|
+
return {
|
|
757
|
+
pages: [...prev.pages, aggregated],
|
|
758
|
+
pageParams: [...prev.pageParams, pageParamForThisPage]
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
const newPages = [...prev.pages];
|
|
762
|
+
newPages[idx] = aggregated;
|
|
763
|
+
return { ...prev, pages: newPages };
|
|
764
|
+
});
|
|
765
|
+
if (!nextCursor) {
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
return aggregated;
|
|
770
|
+
};
|
|
771
|
+
return runSplitFetch();
|
|
772
|
+
}
|
|
773
|
+
},
|
|
774
|
+
queryClient
|
|
775
|
+
);
|
|
776
|
+
return { ...queryResult, onReceive: registerOnReceive };
|
|
777
|
+
};
|
|
778
|
+
return {
|
|
779
|
+
getQueryKeys,
|
|
780
|
+
invalidate: invalidateExact,
|
|
781
|
+
setData,
|
|
782
|
+
useEndpoint,
|
|
783
|
+
fetch: fetchGet
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// src/routesV3.client.mutation.ts
|
|
788
|
+
var import_rrroutes_contract4 = require("@emeryld/rrroutes-contract");
|
|
789
|
+
var import_react_query3 = require("@tanstack/react-query");
|
|
790
|
+
var import_react3 = require("react");
|
|
791
|
+
function buildMutationLeaf(leaf, rqOpts, env) {
|
|
792
|
+
const leafCfg = leaf.cfg;
|
|
793
|
+
const method = toUpper(leaf.method);
|
|
794
|
+
const expectsArgs = Boolean(leafCfg.paramsSchema || leafCfg.querySchema);
|
|
795
|
+
const {
|
|
796
|
+
baseUrl,
|
|
797
|
+
validateResponses,
|
|
798
|
+
fetcher,
|
|
799
|
+
queryClient,
|
|
800
|
+
emit,
|
|
801
|
+
decorateDebugEvent,
|
|
802
|
+
isVerboseDebug,
|
|
803
|
+
leafLabel
|
|
804
|
+
} = env;
|
|
805
|
+
emit({ type: "build", leaf: leafLabel });
|
|
806
|
+
const getQueryKeys = (...tuple) => {
|
|
807
|
+
const a = extractArgs(tuple);
|
|
808
|
+
const params = a?.params;
|
|
809
|
+
const query = a?.query;
|
|
810
|
+
return (0, import_rrroutes_contract4.buildCacheKey)({
|
|
811
|
+
leaf,
|
|
812
|
+
params,
|
|
813
|
+
query
|
|
814
|
+
});
|
|
815
|
+
};
|
|
816
|
+
const invalidateExact = async (...tuple) => {
|
|
817
|
+
const queryKey = getQueryKeys(...tuple);
|
|
818
|
+
await queryClient.invalidateQueries({ queryKey, exact: true });
|
|
819
|
+
emit({ type: "invalidate", key: queryKey, exact: true });
|
|
820
|
+
};
|
|
821
|
+
const setData = (...args) => {
|
|
822
|
+
const [updater, ...rest] = args;
|
|
823
|
+
const k = getQueryKeys(...rest);
|
|
824
|
+
const next = queryClient.setQueryData(
|
|
825
|
+
k,
|
|
826
|
+
(prev) => typeof updater === "function" ? updater(prev) : updater
|
|
827
|
+
);
|
|
828
|
+
emit({ type: "setData", key: k });
|
|
829
|
+
return next;
|
|
830
|
+
};
|
|
831
|
+
const mutationBuildOptions = rqOpts ?? {};
|
|
832
|
+
const fetchEndpoint = async (tuple, options) => {
|
|
833
|
+
const a = extractArgs(tuple);
|
|
834
|
+
const params = a?.params;
|
|
835
|
+
const query = options?.queryOverride ?? a?.query;
|
|
836
|
+
const { url, normalizedQuery, normalizedParams } = buildUrl(
|
|
837
|
+
{ ...leaf, cfg: leafCfg },
|
|
838
|
+
baseUrl,
|
|
839
|
+
params,
|
|
840
|
+
query
|
|
841
|
+
);
|
|
842
|
+
let payload;
|
|
843
|
+
const acceptsBody = Boolean(leafCfg.bodySchema);
|
|
844
|
+
const requiresBody = options?.requireBody ?? (!acceptsBody ? false : true);
|
|
845
|
+
if (typeof options?.body !== "undefined") {
|
|
846
|
+
const normalizedBody = leafCfg.bodySchema ? (0, import_rrroutes_contract4.lowProfileParse)(leafCfg.bodySchema, options.body) : void 0;
|
|
847
|
+
const isMultipart = Array.isArray(leafCfg.bodyFiles) && leafCfg.bodyFiles.length > 0;
|
|
848
|
+
if (isMultipart && normalizedBody && typeof normalizedBody === "object") {
|
|
849
|
+
payload = toFormData(normalizedBody);
|
|
850
|
+
} else {
|
|
851
|
+
payload = normalizedBody;
|
|
852
|
+
}
|
|
853
|
+
} else if (requiresBody && acceptsBody) {
|
|
854
|
+
throw new Error("Body is required when invoking a mutation fetch.");
|
|
855
|
+
}
|
|
856
|
+
const startedAt = Date.now();
|
|
857
|
+
const detail = isVerboseDebug ? { params, normalizedParams, query, normalizedQuery, baseUrl } : void 0;
|
|
858
|
+
emit(
|
|
859
|
+
decorateDebugEvent(
|
|
860
|
+
{
|
|
861
|
+
type: "fetch",
|
|
862
|
+
stage: "start",
|
|
863
|
+
method,
|
|
864
|
+
url,
|
|
865
|
+
leaf: leafLabel,
|
|
866
|
+
...payload !== void 0 ? { body: payload } : {}
|
|
867
|
+
},
|
|
868
|
+
detail
|
|
869
|
+
)
|
|
870
|
+
);
|
|
871
|
+
try {
|
|
872
|
+
const out = await fetcher(
|
|
873
|
+
payload === void 0 ? { url, method } : { url, method, body: payload }
|
|
874
|
+
);
|
|
875
|
+
emit(
|
|
876
|
+
decorateDebugEvent(
|
|
877
|
+
{
|
|
878
|
+
type: "fetch",
|
|
879
|
+
stage: "fetched",
|
|
880
|
+
method,
|
|
881
|
+
url,
|
|
882
|
+
leaf: leafLabel,
|
|
883
|
+
durationMs: Date.now() - startedAt
|
|
884
|
+
},
|
|
885
|
+
isVerboseDebug ? {
|
|
886
|
+
params: normalizedParams,
|
|
887
|
+
query: normalizedQuery,
|
|
888
|
+
output: out
|
|
889
|
+
} : void 0
|
|
890
|
+
)
|
|
891
|
+
);
|
|
892
|
+
if (validateResponses) {
|
|
893
|
+
if (!leafCfg.outputSchema) {
|
|
894
|
+
throw new Error(
|
|
895
|
+
`No output schema defined for leaf ${leafLabel}, cannot validate response.`
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
out.data = (0, import_rrroutes_contract4.lowProfileParse)(
|
|
899
|
+
leafCfg.outputSchema,
|
|
900
|
+
out.data
|
|
901
|
+
);
|
|
902
|
+
emit(
|
|
903
|
+
decorateDebugEvent(
|
|
904
|
+
{
|
|
905
|
+
type: "fetch",
|
|
906
|
+
stage: "parsed",
|
|
907
|
+
method,
|
|
908
|
+
url,
|
|
909
|
+
leaf: leafLabel,
|
|
910
|
+
durationMs: Date.now() - startedAt
|
|
911
|
+
},
|
|
912
|
+
isVerboseDebug ? {
|
|
913
|
+
params: normalizedParams,
|
|
914
|
+
query: normalizedQuery,
|
|
915
|
+
output: out
|
|
916
|
+
} : void 0
|
|
917
|
+
)
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
options?.onReceive?.(out.data);
|
|
921
|
+
return out.data;
|
|
922
|
+
} catch (error) {
|
|
923
|
+
emit(
|
|
924
|
+
decorateDebugEvent(
|
|
925
|
+
{
|
|
926
|
+
type: "fetch",
|
|
927
|
+
stage: "error",
|
|
928
|
+
method,
|
|
929
|
+
url,
|
|
930
|
+
leaf: leafLabel,
|
|
931
|
+
durationMs: Date.now() - startedAt,
|
|
932
|
+
...payload !== void 0 ? { body: payload } : {},
|
|
933
|
+
error
|
|
934
|
+
},
|
|
935
|
+
detail
|
|
936
|
+
)
|
|
937
|
+
);
|
|
938
|
+
throw error;
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
const fetchMutation = async (...tupleWithBody) => {
|
|
942
|
+
if (tupleWithBody.length === 0) {
|
|
943
|
+
throw new Error("Body is required when invoking a mutation fetch.");
|
|
115
944
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
945
|
+
const bodyIndex = tupleWithBody.length - 1;
|
|
946
|
+
const tuple = tupleWithBody.slice(0, bodyIndex);
|
|
947
|
+
const body = tupleWithBody[bodyIndex];
|
|
948
|
+
const result = await fetchEndpoint(tuple, {
|
|
949
|
+
body,
|
|
950
|
+
onReceive: (data) => mutationBuildOptions?.onReceive?.(data),
|
|
951
|
+
requireBody: true
|
|
952
|
+
});
|
|
953
|
+
return result;
|
|
954
|
+
};
|
|
955
|
+
const useEndpoint = (...useArgs) => {
|
|
956
|
+
const args = useArgs[0];
|
|
957
|
+
const tuple = toArgsTuple(args);
|
|
958
|
+
const mutationKey = getQueryKeys(...tuple);
|
|
959
|
+
emit({
|
|
960
|
+
type: "useEndpoint",
|
|
961
|
+
leaf: leafLabel,
|
|
962
|
+
variant: "mutation",
|
|
963
|
+
keys: mutationKey
|
|
964
|
+
});
|
|
965
|
+
const listenersRef = (0, import_react3.useRef)(/* @__PURE__ */ new Set());
|
|
966
|
+
const notifyListeners = (0, import_react3.useCallback)((data) => {
|
|
967
|
+
listenersRef.current.forEach((listener) => listener(data));
|
|
968
|
+
}, []);
|
|
969
|
+
const registerOnReceive = (0, import_react3.useCallback)(
|
|
970
|
+
(listener) => {
|
|
971
|
+
listenersRef.current.add(listener);
|
|
972
|
+
return () => {
|
|
973
|
+
listenersRef.current.delete(listener);
|
|
974
|
+
};
|
|
975
|
+
},
|
|
976
|
+
[]
|
|
977
|
+
);
|
|
978
|
+
const mutationResult = (0, import_react_query3.useMutation)(
|
|
979
|
+
{
|
|
980
|
+
...mutationBuildOptions ?? {},
|
|
981
|
+
mutationKey,
|
|
982
|
+
mutationFn: async (body) => {
|
|
983
|
+
const result = await fetchMutation(
|
|
984
|
+
...[...tuple, body]
|
|
985
|
+
);
|
|
986
|
+
notifyListeners(result);
|
|
987
|
+
return result;
|
|
988
|
+
}
|
|
989
|
+
},
|
|
990
|
+
queryClient
|
|
991
|
+
);
|
|
992
|
+
return { ...mutationResult, onReceive: registerOnReceive };
|
|
993
|
+
};
|
|
994
|
+
return {
|
|
995
|
+
getQueryKeys,
|
|
996
|
+
invalidate: invalidateExact,
|
|
997
|
+
setData,
|
|
998
|
+
useEndpoint,
|
|
999
|
+
fetch: fetchMutation
|
|
1000
|
+
};
|
|
125
1001
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const record = p;
|
|
129
|
-
if ("nextCursor" in record) {
|
|
130
|
-
return record.nextCursor;
|
|
131
|
-
}
|
|
132
|
-
if ("meta" in record) {
|
|
133
|
-
const meta = record.meta;
|
|
134
|
-
if (meta && typeof meta === "object" && "nextCursor" in meta) {
|
|
135
|
-
return meta.nextCursor;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return void 0;
|
|
139
|
-
};
|
|
1002
|
+
|
|
1003
|
+
// src/routesV3.client.ts
|
|
140
1004
|
var defaultDebugLogger = (event) => {
|
|
141
1005
|
if (typeof console === "undefined") return;
|
|
142
1006
|
const fn = console.debug ?? console.log;
|
|
@@ -198,22 +1062,6 @@ function createDebugEmitter(option, environment) {
|
|
|
198
1062
|
}
|
|
199
1063
|
return disabled;
|
|
200
1064
|
}
|
|
201
|
-
function extractArgs(args) {
|
|
202
|
-
return args[0];
|
|
203
|
-
}
|
|
204
|
-
function toArgsTuple(args) {
|
|
205
|
-
return typeof args === "undefined" ? [] : [args];
|
|
206
|
-
}
|
|
207
|
-
function buildUrl(leaf, baseUrl, params, query) {
|
|
208
|
-
const normalizedParams = leaf.cfg.paramsSchema ? (0, import_rrroutes_contract.lowProfileParse)(leaf.cfg.paramsSchema, params) : {};
|
|
209
|
-
const normalizedQuery = leaf.cfg.querySchema ? (0, import_rrroutes_contract.lowProfileParse)(leaf.cfg.querySchema, query) : {};
|
|
210
|
-
const path = (0, import_rrroutes_contract.compilePath)(
|
|
211
|
-
leaf.path,
|
|
212
|
-
normalizedParams ?? {}
|
|
213
|
-
);
|
|
214
|
-
const url = `${baseUrl ?? ""}${path}${toSearchString(normalizedQuery)}`;
|
|
215
|
-
return { url, normalizedQuery, normalizedParams };
|
|
216
|
-
}
|
|
217
1065
|
function createRouteClient(opts) {
|
|
218
1066
|
const queryClient = opts.queryClient;
|
|
219
1067
|
const fetcher = opts.fetcher ?? defaultFetcher;
|
|
@@ -234,365 +1082,41 @@ function createRouteClient(opts) {
|
|
|
234
1082
|
emitDebug({ type: "invalidate", key: queryKey, exact });
|
|
235
1083
|
}
|
|
236
1084
|
function buildInternal(leaf, rqOpts, meta) {
|
|
237
|
-
const isGet = leaf.method === "get";
|
|
238
|
-
const isFeed = !!leaf.cfg.feed;
|
|
239
|
-
const leafCfg = leaf.cfg;
|
|
240
|
-
const validateResponses = opts.validateResponses ?? true;
|
|
241
|
-
const method = toUpper(leaf.method);
|
|
242
|
-
const expectsArgs = Boolean(leafCfg.paramsSchema || leafCfg.querySchema);
|
|
243
1085
|
const leafLabel = `${leaf.method.toUpperCase()} ${String(leaf.path)}`;
|
|
244
1086
|
const debugName = meta?.name;
|
|
245
1087
|
const emit = (event) => emitDebug(event, debugName);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
1088
|
+
const isGet = leaf.method === "get";
|
|
1089
|
+
const isFeed = !!leaf.cfg.feed;
|
|
1090
|
+
const validateResponses = opts.validateResponses ?? true;
|
|
1091
|
+
const env = {
|
|
1092
|
+
baseUrl,
|
|
1093
|
+
validateResponses,
|
|
1094
|
+
fetcher,
|
|
1095
|
+
queryClient,
|
|
1096
|
+
emit,
|
|
1097
|
+
decorateDebugEvent,
|
|
1098
|
+
isVerboseDebug,
|
|
1099
|
+
leafLabel
|
|
1100
|
+
};
|
|
251
1101
|
if (isGet && isFeed) {
|
|
252
|
-
|
|
253
|
-
const {
|
|
254
|
-
cursorParam,
|
|
255
|
-
getNextPageParam,
|
|
256
|
-
initialPageParam,
|
|
257
|
-
...passthroughOptions
|
|
258
|
-
} = infiniteOptions;
|
|
259
|
-
feedCursorParam = cursorParam ?? "pagination_cursor";
|
|
260
|
-
feedNextPageParam = getNextPageParam ?? ((lastPage) => defaultGetNextCursor(lastPage));
|
|
261
|
-
feedInitialPageParam = typeof initialPageParam === "undefined" ? void 0 : initialPageParam;
|
|
262
|
-
feedQueryOptions = passthroughOptions;
|
|
263
|
-
}
|
|
264
|
-
const getQueryKeys = (...tuple) => {
|
|
265
|
-
const a = extractArgs(tuple);
|
|
266
|
-
const params = a?.params;
|
|
267
|
-
const query = a?.query;
|
|
268
|
-
const qForKey = isGet && isFeed ? stripKey(query, feedCursorParam) : query;
|
|
269
|
-
return (0, import_rrroutes_contract.buildCacheKey)({
|
|
1102
|
+
return buildInfiniteGetLeaf(
|
|
270
1103
|
leaf,
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
});
|
|
274
|
-
};
|
|
275
|
-
const invalidateExact = async (...tuple) => {
|
|
276
|
-
const queryKey = getQueryKeys(...tuple);
|
|
277
|
-
await queryClient.invalidateQueries({ queryKey, exact: true });
|
|
278
|
-
emit({ type: "invalidate", key: queryKey, exact: true });
|
|
279
|
-
};
|
|
280
|
-
const setData = (...args) => {
|
|
281
|
-
const [updater, ...rest] = args;
|
|
282
|
-
const k = getQueryKeys(...rest);
|
|
283
|
-
let next;
|
|
284
|
-
if (isGet && isFeed) {
|
|
285
|
-
next = queryClient.setQueryData(
|
|
286
|
-
k,
|
|
287
|
-
(prev) => typeof updater === "function" ? updater(prev) : updater
|
|
288
|
-
);
|
|
289
|
-
} else {
|
|
290
|
-
next = queryClient.setQueryData(
|
|
291
|
-
k,
|
|
292
|
-
(prev) => typeof updater === "function" ? updater(prev) : updater
|
|
293
|
-
);
|
|
294
|
-
}
|
|
295
|
-
emit({ type: "setData", key: k });
|
|
296
|
-
return next;
|
|
297
|
-
};
|
|
298
|
-
const buildOnReceive = rqOpts?.onReceive;
|
|
299
|
-
const fetchEndpoint = async (tuple, options) => {
|
|
300
|
-
const a = extractArgs(tuple);
|
|
301
|
-
const params = a?.params;
|
|
302
|
-
const query = options?.queryOverride ?? a?.query;
|
|
303
|
-
const { url, normalizedQuery, normalizedParams } = buildUrl(
|
|
304
|
-
{ ...leaf, cfg: leafCfg },
|
|
305
|
-
baseUrl,
|
|
306
|
-
params,
|
|
307
|
-
query
|
|
308
|
-
);
|
|
309
|
-
let payload;
|
|
310
|
-
const acceptsBody = Boolean(leafCfg.bodySchema);
|
|
311
|
-
const requiresBody = options?.requireBody ?? (!isGet && acceptsBody);
|
|
312
|
-
if (typeof options?.body !== "undefined") {
|
|
313
|
-
const normalizedBody = leafCfg.bodySchema ? (0, import_rrroutes_contract.lowProfileParse)(leafCfg.bodySchema, options.body) : void 0;
|
|
314
|
-
const isMultipart = Array.isArray(leafCfg.bodyFiles) && leafCfg.bodyFiles.length > 0;
|
|
315
|
-
if (isMultipart && normalizedBody && typeof normalizedBody === "object") {
|
|
316
|
-
payload = toFormData(normalizedBody);
|
|
317
|
-
} else {
|
|
318
|
-
payload = normalizedBody;
|
|
319
|
-
}
|
|
320
|
-
} else if (requiresBody) {
|
|
321
|
-
throw new Error("Body is required when invoking a mutation fetch.");
|
|
322
|
-
}
|
|
323
|
-
const startedAt = Date.now();
|
|
324
|
-
const detail = isVerboseDebug ? { params: normalizedParams, query: normalizedQuery } : void 0;
|
|
325
|
-
emit(
|
|
326
|
-
decorateDebugEvent(
|
|
327
|
-
{
|
|
328
|
-
type: "fetch",
|
|
329
|
-
stage: "start",
|
|
330
|
-
method,
|
|
331
|
-
url,
|
|
332
|
-
leaf: leafLabel,
|
|
333
|
-
...payload !== void 0 ? { body: payload } : {}
|
|
334
|
-
},
|
|
335
|
-
detail
|
|
336
|
-
)
|
|
1104
|
+
rqOpts,
|
|
1105
|
+
env
|
|
337
1106
|
);
|
|
338
|
-
try {
|
|
339
|
-
const out = await fetcher(
|
|
340
|
-
payload === void 0 ? { url, method } : { url, method, body: payload }
|
|
341
|
-
);
|
|
342
|
-
emit(
|
|
343
|
-
decorateDebugEvent(
|
|
344
|
-
{
|
|
345
|
-
type: "fetch",
|
|
346
|
-
stage: "fetched",
|
|
347
|
-
method,
|
|
348
|
-
url,
|
|
349
|
-
leaf: leafLabel,
|
|
350
|
-
durationMs: Date.now() - startedAt
|
|
351
|
-
},
|
|
352
|
-
isVerboseDebug ? {
|
|
353
|
-
params: normalizedParams,
|
|
354
|
-
query: normalizedQuery,
|
|
355
|
-
output: out
|
|
356
|
-
} : void 0
|
|
357
|
-
)
|
|
358
|
-
);
|
|
359
|
-
if (validateResponses) {
|
|
360
|
-
if (!leafCfg.outputSchema) {
|
|
361
|
-
throw new Error(
|
|
362
|
-
`No output schema defined for leaf ${leafLabel}, cannot validate response.`
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
out.data = (0, import_rrroutes_contract.lowProfileParse)(
|
|
366
|
-
leafCfg.outputSchema,
|
|
367
|
-
out.data
|
|
368
|
-
);
|
|
369
|
-
emit(
|
|
370
|
-
decorateDebugEvent(
|
|
371
|
-
{
|
|
372
|
-
type: "fetch",
|
|
373
|
-
stage: "parsed",
|
|
374
|
-
method,
|
|
375
|
-
url,
|
|
376
|
-
leaf: leafLabel,
|
|
377
|
-
durationMs: Date.now() - startedAt
|
|
378
|
-
},
|
|
379
|
-
isVerboseDebug ? {
|
|
380
|
-
params: normalizedParams,
|
|
381
|
-
query: normalizedQuery,
|
|
382
|
-
output: out
|
|
383
|
-
} : void 0
|
|
384
|
-
)
|
|
385
|
-
);
|
|
386
|
-
}
|
|
387
|
-
options?.onReceive?.(out.data);
|
|
388
|
-
return out.data;
|
|
389
|
-
} catch (error) {
|
|
390
|
-
emit(
|
|
391
|
-
decorateDebugEvent(
|
|
392
|
-
{
|
|
393
|
-
type: "fetch",
|
|
394
|
-
stage: "error",
|
|
395
|
-
method,
|
|
396
|
-
url,
|
|
397
|
-
leaf: leafLabel,
|
|
398
|
-
durationMs: Date.now() - startedAt,
|
|
399
|
-
...payload !== void 0 ? { body: payload } : {},
|
|
400
|
-
error
|
|
401
|
-
},
|
|
402
|
-
detail
|
|
403
|
-
)
|
|
404
|
-
);
|
|
405
|
-
throw error;
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
|
-
const fetchGet = (...tupleWithBody) => {
|
|
409
|
-
const acceptsBody = Boolean(leafCfg.bodySchema);
|
|
410
|
-
const tupleLength = tupleWithBody.length;
|
|
411
|
-
const maybeBodyIndex = expectsArgs ? 1 : 0;
|
|
412
|
-
const hasBodyCandidate = acceptsBody && tupleLength > maybeBodyIndex;
|
|
413
|
-
const body = hasBodyCandidate ? tupleWithBody[tupleLength - 1] : void 0;
|
|
414
|
-
const tuple = hasBodyCandidate ? tupleWithBody.slice(0, tupleLength - 1) : tupleWithBody;
|
|
415
|
-
return fetchEndpoint(tuple, {
|
|
416
|
-
body,
|
|
417
|
-
onReceive: buildOnReceive,
|
|
418
|
-
requireBody: false
|
|
419
|
-
});
|
|
420
|
-
};
|
|
421
|
-
if (isGet && isFeed) {
|
|
422
|
-
const useEndpoint2 = (...useArgs) => {
|
|
423
|
-
const args = useArgs[0];
|
|
424
|
-
const tuple = toArgsTuple(args);
|
|
425
|
-
const queryKeys = getQueryKeys(...tuple);
|
|
426
|
-
emit({
|
|
427
|
-
type: "useEndpoint",
|
|
428
|
-
leaf: leafLabel,
|
|
429
|
-
variant: "infiniteGet",
|
|
430
|
-
keys: queryKeys
|
|
431
|
-
});
|
|
432
|
-
const params = args?.params;
|
|
433
|
-
const query = args?.query;
|
|
434
|
-
const buildOptions = feedQueryOptions ?? {};
|
|
435
|
-
const listenersRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
|
|
436
|
-
const notifyOnReceive = (0, import_react.useCallback)((data) => {
|
|
437
|
-
buildOptions?.onReceive?.(data);
|
|
438
|
-
listenersRef.current.forEach((listener) => listener(data));
|
|
439
|
-
}, []);
|
|
440
|
-
const registerOnReceive = (0, import_react.useCallback)(
|
|
441
|
-
(listener) => {
|
|
442
|
-
listenersRef.current.add(listener);
|
|
443
|
-
return () => {
|
|
444
|
-
listenersRef.current.delete(listener);
|
|
445
|
-
};
|
|
446
|
-
},
|
|
447
|
-
[]
|
|
448
|
-
);
|
|
449
|
-
const { normalizedQuery, normalizedParams } = buildUrl(
|
|
450
|
-
{ ...leaf, cfg: leafCfg },
|
|
451
|
-
baseUrl,
|
|
452
|
-
params,
|
|
453
|
-
query
|
|
454
|
-
);
|
|
455
|
-
const queryResult = (0, import_react_query.useInfiniteQuery)(
|
|
456
|
-
{
|
|
457
|
-
...buildOptions,
|
|
458
|
-
placeholderData: buildOptions.placeholderData ?? import_react_query.keepPreviousData,
|
|
459
|
-
initialPageParam: feedInitialPageParam,
|
|
460
|
-
getNextPageParam: (lastPage) => (feedNextPageParam ?? defaultGetNextCursor)(lastPage),
|
|
461
|
-
queryKey: queryKeys,
|
|
462
|
-
queryFn: ({ pageParam }) => {
|
|
463
|
-
const pageQuery = {
|
|
464
|
-
...normalizedQuery,
|
|
465
|
-
...pageParam ? { [feedCursorParam]: pageParam } : {}
|
|
466
|
-
};
|
|
467
|
-
return fetchEndpoint(tuple, {
|
|
468
|
-
queryOverride: pageQuery,
|
|
469
|
-
onReceive: notifyOnReceive
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
// NOTE: TData is InfiniteData<T>, so we don't need a select here.
|
|
473
|
-
},
|
|
474
|
-
queryClient
|
|
475
|
-
);
|
|
476
|
-
return { ...queryResult, onReceive: registerOnReceive };
|
|
477
|
-
};
|
|
478
|
-
return {
|
|
479
|
-
getQueryKeys,
|
|
480
|
-
invalidate: invalidateExact,
|
|
481
|
-
setData,
|
|
482
|
-
useEndpoint: useEndpoint2,
|
|
483
|
-
fetch: fetchGet
|
|
484
|
-
};
|
|
485
1107
|
}
|
|
486
1108
|
if (isGet) {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
emit({
|
|
492
|
-
type: "useEndpoint",
|
|
493
|
-
leaf: leafLabel,
|
|
494
|
-
variant: "get",
|
|
495
|
-
keys: queryKeys
|
|
496
|
-
});
|
|
497
|
-
const params = args?.params;
|
|
498
|
-
const query = args?.query;
|
|
499
|
-
const buildOptions = rqOpts ?? {};
|
|
500
|
-
const listenersRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
|
|
501
|
-
const notifyOnReceive = (0, import_react.useCallback)((data) => {
|
|
502
|
-
buildOptions?.onReceive?.(data);
|
|
503
|
-
listenersRef.current.forEach((listener) => listener(data));
|
|
504
|
-
}, []);
|
|
505
|
-
const registerOnReceive = (0, import_react.useCallback)(
|
|
506
|
-
(listener) => {
|
|
507
|
-
listenersRef.current.add(listener);
|
|
508
|
-
return () => {
|
|
509
|
-
listenersRef.current.delete(listener);
|
|
510
|
-
};
|
|
511
|
-
},
|
|
512
|
-
[]
|
|
513
|
-
);
|
|
514
|
-
const queryResult = (0, import_react_query.useQuery)(
|
|
515
|
-
{
|
|
516
|
-
...buildOptions,
|
|
517
|
-
queryKey: getQueryKeys(...tuple),
|
|
518
|
-
placeholderData: import_react_query.keepPreviousData,
|
|
519
|
-
queryFn: () => fetchEndpoint(tuple, {
|
|
520
|
-
onReceive: notifyOnReceive
|
|
521
|
-
})
|
|
522
|
-
},
|
|
523
|
-
queryClient
|
|
524
|
-
);
|
|
525
|
-
return { ...queryResult, onReceive: registerOnReceive };
|
|
526
|
-
};
|
|
527
|
-
return {
|
|
528
|
-
getQueryKeys,
|
|
529
|
-
invalidate: invalidateExact,
|
|
530
|
-
setData,
|
|
531
|
-
useEndpoint: useEndpoint2,
|
|
532
|
-
fetch: fetchGet
|
|
533
|
-
};
|
|
534
|
-
}
|
|
535
|
-
const mutationBuildOptions = rqOpts ?? {};
|
|
536
|
-
const fetchMutation = async (...tupleWithBody) => {
|
|
537
|
-
if (tupleWithBody.length === 0) {
|
|
538
|
-
throw new Error("Body is required when invoking a mutation fetch.");
|
|
539
|
-
}
|
|
540
|
-
const bodyIndex = tupleWithBody.length - 1;
|
|
541
|
-
const tuple = tupleWithBody.slice(0, bodyIndex);
|
|
542
|
-
const body = tupleWithBody[bodyIndex];
|
|
543
|
-
const result = await fetchEndpoint(tuple, {
|
|
544
|
-
body,
|
|
545
|
-
onReceive: (data) => mutationBuildOptions?.onReceive?.(data),
|
|
546
|
-
requireBody: true
|
|
547
|
-
});
|
|
548
|
-
return result;
|
|
549
|
-
};
|
|
550
|
-
const useEndpoint = (...useArgs) => {
|
|
551
|
-
const args = useArgs[0];
|
|
552
|
-
const tuple = toArgsTuple(args);
|
|
553
|
-
const mutationKey = getQueryKeys(...tuple);
|
|
554
|
-
emit({
|
|
555
|
-
type: "useEndpoint",
|
|
556
|
-
leaf: leafLabel,
|
|
557
|
-
variant: "mutation",
|
|
558
|
-
keys: mutationKey
|
|
559
|
-
});
|
|
560
|
-
const listenersRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
|
|
561
|
-
const notifyListeners = (0, import_react.useCallback)((data) => {
|
|
562
|
-
listenersRef.current.forEach((listener) => listener(data));
|
|
563
|
-
}, []);
|
|
564
|
-
const registerOnReceive = (0, import_react.useCallback)(
|
|
565
|
-
(listener) => {
|
|
566
|
-
listenersRef.current.add(listener);
|
|
567
|
-
return () => {
|
|
568
|
-
listenersRef.current.delete(listener);
|
|
569
|
-
};
|
|
570
|
-
},
|
|
571
|
-
[]
|
|
572
|
-
);
|
|
573
|
-
const mutationResult = (0, import_react_query.useMutation)(
|
|
574
|
-
{
|
|
575
|
-
...mutationBuildOptions,
|
|
576
|
-
mutationKey,
|
|
577
|
-
mutationFn: async (body) => {
|
|
578
|
-
const result = await fetchMutation(
|
|
579
|
-
...[...tuple, body]
|
|
580
|
-
);
|
|
581
|
-
notifyListeners(result);
|
|
582
|
-
return result;
|
|
583
|
-
}
|
|
584
|
-
},
|
|
585
|
-
queryClient
|
|
1109
|
+
return buildGetLeaf(
|
|
1110
|
+
leaf,
|
|
1111
|
+
rqOpts,
|
|
1112
|
+
env
|
|
586
1113
|
);
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
useEndpoint,
|
|
594
|
-
fetch: fetchMutation
|
|
595
|
-
};
|
|
1114
|
+
}
|
|
1115
|
+
return buildMutationLeaf(
|
|
1116
|
+
leaf,
|
|
1117
|
+
rqOpts,
|
|
1118
|
+
env
|
|
1119
|
+
);
|
|
596
1120
|
}
|
|
597
1121
|
const fetchRaw = async (input) => {
|
|
598
1122
|
const { path, method, query, body, params } = input;
|
|
@@ -610,7 +1134,7 @@ function createRouteClient(opts) {
|
|
|
610
1134
|
const url = `${baseUrl ?? ""}${compiledPath}${search}`;
|
|
611
1135
|
const leafLabel = `${methodUpper} ${path}`;
|
|
612
1136
|
const startedAt = Date.now();
|
|
613
|
-
const detail = isVerboseDebug ? { params, query
|
|
1137
|
+
const detail = isVerboseDebug ? { params, flatQuery, query, baseUrl } : void 0;
|
|
614
1138
|
emitDebug(
|
|
615
1139
|
decorateDebugEvent(
|
|
616
1140
|
{
|
|
@@ -676,81 +1200,6 @@ function buildRouter(routeClient, routes) {
|
|
|
676
1200
|
meta
|
|
677
1201
|
));
|
|
678
1202
|
}
|
|
679
|
-
function toFormData(body) {
|
|
680
|
-
const fd = new FormData();
|
|
681
|
-
for (const [k, v] of Object.entries(body ?? {})) {
|
|
682
|
-
if (v == null) continue;
|
|
683
|
-
if (Array.isArray(v))
|
|
684
|
-
v.forEach((item, i) => fd.append(`${k}[${i}]`, item));
|
|
685
|
-
else fd.append(k, v);
|
|
686
|
-
}
|
|
687
|
-
return fd;
|
|
688
|
-
}
|
|
689
|
-
function getPathParamNames(path) {
|
|
690
|
-
const names = /* @__PURE__ */ new Set();
|
|
691
|
-
const re = /:([A-Za-z0-9_]+)/g;
|
|
692
|
-
let match;
|
|
693
|
-
while ((match = re.exec(path)) !== null) {
|
|
694
|
-
names.add(match[1]);
|
|
695
|
-
}
|
|
696
|
-
return names;
|
|
697
|
-
}
|
|
698
|
-
function normalizeFlatQuery(query) {
|
|
699
|
-
if (query == null) return void 0;
|
|
700
|
-
if (typeof query !== "object" || Array.isArray(query)) {
|
|
701
|
-
throw new Error("Query must be a plain object (Record<string, string>).");
|
|
702
|
-
}
|
|
703
|
-
const result = {};
|
|
704
|
-
for (const [k, v] of Object.entries(query)) {
|
|
705
|
-
if (v == null) continue;
|
|
706
|
-
if (typeof v !== "string") {
|
|
707
|
-
throw new Error(
|
|
708
|
-
`Query param "${k}" must be a string; received type "${typeof v}".`
|
|
709
|
-
);
|
|
710
|
-
}
|
|
711
|
-
result[k] = v;
|
|
712
|
-
}
|
|
713
|
-
return Object.keys(result).length > 0 ? result : void 0;
|
|
714
|
-
}
|
|
715
|
-
function compileRawPath(path, params) {
|
|
716
|
-
const placeholders = getPathParamNames(path);
|
|
717
|
-
if (!params || typeof params !== "object" || Array.isArray(params)) {
|
|
718
|
-
if (placeholders.size > 0) {
|
|
719
|
-
throw new Error(
|
|
720
|
-
`Missing path parameters for "${path}": ${[...placeholders].join(
|
|
721
|
-
", "
|
|
722
|
-
)}`
|
|
723
|
-
);
|
|
724
|
-
}
|
|
725
|
-
return path;
|
|
726
|
-
}
|
|
727
|
-
const paramObj = params;
|
|
728
|
-
const providedNames = new Set(Object.keys(paramObj));
|
|
729
|
-
for (const name of providedNames) {
|
|
730
|
-
if (!placeholders.has(name)) {
|
|
731
|
-
throw new Error(
|
|
732
|
-
`Unexpected path parameter "${name}" for template "${path}".`
|
|
733
|
-
);
|
|
734
|
-
}
|
|
735
|
-
const value = paramObj[name];
|
|
736
|
-
if (value != null && (typeof value === "object" || Array.isArray(value))) {
|
|
737
|
-
throw new Error(
|
|
738
|
-
`Path parameter "${name}" must be a primitive; received "${typeof value}".`
|
|
739
|
-
);
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
for (const name of placeholders) {
|
|
743
|
-
if (!providedNames.has(name)) {
|
|
744
|
-
throw new Error(
|
|
745
|
-
`Missing value for path parameter "${name}" in template "${path}".`
|
|
746
|
-
);
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
if (placeholders.size === 0) {
|
|
750
|
-
return path;
|
|
751
|
-
}
|
|
752
|
-
return (0, import_rrroutes_contract.compilePath)(path, paramObj);
|
|
753
|
-
}
|
|
754
1203
|
|
|
755
1204
|
// src/sockets/socket.client.sys.ts
|
|
756
1205
|
var import_zod = require("zod");
|
|
@@ -1025,7 +1474,7 @@ function useSocketConnection(args) {
|
|
|
1025
1474
|
}
|
|
1026
1475
|
|
|
1027
1476
|
// src/sockets/socketedRoute/socket.client.helper.ts
|
|
1028
|
-
var
|
|
1477
|
+
var import_react4 = require("react");
|
|
1029
1478
|
function normalizeRooms(rooms) {
|
|
1030
1479
|
if (rooms == null) return [];
|
|
1031
1480
|
const list = Array.isArray(rooms) ? rooms : [rooms];
|
|
@@ -1083,20 +1532,20 @@ function buildSocketedRoute(options) {
|
|
|
1083
1532
|
const endpointResult = built.useEndpoint(
|
|
1084
1533
|
...useArgs
|
|
1085
1534
|
);
|
|
1086
|
-
const argsKey = (0,
|
|
1087
|
-
const [roomState, setRoomState] = (0,
|
|
1535
|
+
const argsKey = (0, import_react4.useMemo)(() => JSON.stringify(useArgs[0] ?? null), [useArgs]);
|
|
1536
|
+
const [roomState, setRoomState] = (0, import_react4.useState)(
|
|
1088
1537
|
() => roomsFromData(endpointResult.data, toRooms)
|
|
1089
1538
|
);
|
|
1090
|
-
const roomsKey = (0,
|
|
1091
|
-
const joinMetaKey = (0,
|
|
1539
|
+
const roomsKey = (0, import_react4.useMemo)(() => roomState.rooms.join("|"), [roomState.rooms]);
|
|
1540
|
+
const joinMetaKey = (0, import_react4.useMemo)(
|
|
1092
1541
|
() => JSON.stringify(roomState.joinMeta ?? null),
|
|
1093
1542
|
[roomState.joinMeta]
|
|
1094
1543
|
);
|
|
1095
|
-
const leaveMetaKey = (0,
|
|
1544
|
+
const leaveMetaKey = (0, import_react4.useMemo)(
|
|
1096
1545
|
() => JSON.stringify(roomState.leaveMeta ?? null),
|
|
1097
1546
|
[roomState.leaveMeta]
|
|
1098
1547
|
);
|
|
1099
|
-
(0,
|
|
1548
|
+
(0, import_react4.useEffect)(() => {
|
|
1100
1549
|
const unsubscribe = endpointResult.onReceive((data) => {
|
|
1101
1550
|
setRoomState(
|
|
1102
1551
|
(prev) => mergeRoomState(prev, toRooms(data))
|
|
@@ -1104,12 +1553,12 @@ function buildSocketedRoute(options) {
|
|
|
1104
1553
|
});
|
|
1105
1554
|
return unsubscribe;
|
|
1106
1555
|
}, [endpointResult, toRooms]);
|
|
1107
|
-
(0,
|
|
1556
|
+
(0, import_react4.useEffect)(() => {
|
|
1108
1557
|
setRoomState(
|
|
1109
1558
|
roomsFromData(endpointResult.data, toRooms)
|
|
1110
1559
|
);
|
|
1111
1560
|
}, [endpointResult.data, toRooms]);
|
|
1112
|
-
(0,
|
|
1561
|
+
(0, import_react4.useEffect)(() => {
|
|
1113
1562
|
if (roomState.rooms.length === 0) return;
|
|
1114
1563
|
const { joinMeta, leaveMeta } = roomState;
|
|
1115
1564
|
if (!joinMeta || !leaveMeta) return;
|
|
@@ -1134,7 +1583,7 @@ function buildSocketedRoute(options) {
|
|
|
1134
1583
|
joinMetaKey,
|
|
1135
1584
|
leaveMetaKey
|
|
1136
1585
|
]);
|
|
1137
|
-
(0,
|
|
1586
|
+
(0, import_react4.useEffect)(() => {
|
|
1138
1587
|
const entries = Object.entries(applySocket).filter(
|
|
1139
1588
|
([_event, fn]) => typeof fn === "function"
|
|
1140
1589
|
);
|