akanjs 2.2.5 → 2.2.7-rc.0
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.
|
@@ -17,6 +17,7 @@ import { type ErrorConstructor, HttpClient } from "./httpClient";
|
|
|
17
17
|
import { WsClient } from "./wsClient";
|
|
18
18
|
|
|
19
19
|
type FetchHandler = (...args: unknown[]) => PromiseOrObject<unknown>;
|
|
20
|
+
type FetchHandlerFactory = () => FetchHandler;
|
|
20
21
|
type UnknownRecord = Record<string, unknown>;
|
|
21
22
|
|
|
22
23
|
const isNullableArg = (arg: SerializedArg) => arg.nullable ?? arg.type === "search";
|
|
@@ -46,13 +47,18 @@ type ClientSignalMap<SigType extends { fetch: any }> = {
|
|
|
46
47
|
|
|
47
48
|
/** Runtime fetch client that registers serialized Akan signals as HTTP/WebSocket methods. */
|
|
48
49
|
export class FetchClient {
|
|
50
|
+
static #sharedSerializedSignal: { [key: string]: SerializedSignal } = {};
|
|
51
|
+
static #sharedRegistryVersion = 0;
|
|
49
52
|
readonly logger = new Logger("FetchClient");
|
|
50
53
|
readonly origin: string;
|
|
51
54
|
readonly http: HttpClient;
|
|
52
55
|
readonly ws: WsClient;
|
|
53
|
-
readonly handler: Record<string, FetchHandler
|
|
56
|
+
readonly handler: Record<string, FetchHandler>;
|
|
54
57
|
readonly slice: Record<string, SliceMeta> = {};
|
|
55
58
|
readonly sortKeyMap = new Map<string, string[]>();
|
|
59
|
+
readonly #handlerStore: Record<string, FetchHandler> = {};
|
|
60
|
+
readonly #handlerFactory = new Map<string, FetchHandlerFactory>();
|
|
61
|
+
#sharedRegistryAppliedVersion = 0;
|
|
56
62
|
serializedSignal: { [key: string]: SerializedSignal } = {};
|
|
57
63
|
jwt: string | null = null;
|
|
58
64
|
|
|
@@ -66,16 +72,52 @@ export class FetchClient {
|
|
|
66
72
|
this.http = new HttpClient(origin, ErrorCls);
|
|
67
73
|
const wsUri = `${origin.replace("http://", "ws://").replace("https://", "wss://")}/ws`;
|
|
68
74
|
this.ws = new WsClient(wsUri, ErrorCls);
|
|
69
|
-
this
|
|
75
|
+
Object.assign(this.#handlerStore, handler);
|
|
76
|
+
this.handler = this.#makeHandlerProxy();
|
|
70
77
|
this.applySignal(serializedSignal);
|
|
71
78
|
}
|
|
79
|
+
static resetSharedRegistry() {
|
|
80
|
+
FetchClient.#sharedSerializedSignal = {};
|
|
81
|
+
FetchClient.#sharedRegistryVersion++;
|
|
82
|
+
}
|
|
83
|
+
static #mergeSerializedSignalInto(
|
|
84
|
+
serializedSignal: { [key: string]: SerializedSignal },
|
|
85
|
+
refName: string,
|
|
86
|
+
signal: SerializedSignal,
|
|
87
|
+
) {
|
|
88
|
+
const current = serializedSignal[refName];
|
|
89
|
+
serializedSignal[refName] = current
|
|
90
|
+
? {
|
|
91
|
+
...current,
|
|
92
|
+
...signal,
|
|
93
|
+
endpoint: { ...current.endpoint, ...signal.endpoint },
|
|
94
|
+
slice: current.slice || signal.slice ? { ...current.slice, ...signal.slice } : undefined,
|
|
95
|
+
filter:
|
|
96
|
+
current.filter || signal.filter
|
|
97
|
+
? {
|
|
98
|
+
filter: { ...current.filter?.filter, ...signal.filter?.filter },
|
|
99
|
+
sortKeys: [...new Set([...(current.filter?.sortKeys ?? []), ...(signal.filter?.sortKeys ?? [])])],
|
|
100
|
+
}
|
|
101
|
+
: undefined,
|
|
102
|
+
getGuards: signal.getGuards ?? current.getGuards,
|
|
103
|
+
cruGuards: signal.cruGuards ?? current.cruGuards,
|
|
104
|
+
}
|
|
105
|
+
: signal;
|
|
106
|
+
}
|
|
72
107
|
setErrorConstructor(ErrorCls?: ErrorConstructor) {
|
|
73
108
|
this.ErrorCls = ErrorCls;
|
|
74
109
|
this.http.setErrorConstructor(ErrorCls);
|
|
75
110
|
this.ws.setErrorConstructor(ErrorCls);
|
|
76
111
|
}
|
|
77
|
-
applySignal(serializedSignal: { [key: string]: SerializedSignal }) {
|
|
78
|
-
Object.
|
|
112
|
+
applySignal(serializedSignal: { [key: string]: SerializedSignal }, { share = true }: { share?: boolean } = {}) {
|
|
113
|
+
if (share && Object.keys(serializedSignal).length > 0) {
|
|
114
|
+
for (const [refName, signal] of Object.entries(serializedSignal))
|
|
115
|
+
FetchClient.#mergeSerializedSignalInto(FetchClient.#sharedSerializedSignal, refName, signal);
|
|
116
|
+
FetchClient.#sharedRegistryVersion++;
|
|
117
|
+
this.#sharedRegistryAppliedVersion = FetchClient.#sharedRegistryVersion;
|
|
118
|
+
}
|
|
119
|
+
for (const [refName, signal] of Object.entries(serializedSignal))
|
|
120
|
+
FetchClient.#mergeSerializedSignalInto(this.serializedSignal, refName, signal);
|
|
79
121
|
for (const [refName, signal] of Object.entries(serializedSignal)) {
|
|
80
122
|
for (const [key, endpoint] of Object.entries(signal.endpoint))
|
|
81
123
|
this.#registerEndpoint(key, endpoint, signal.prefix);
|
|
@@ -93,6 +135,44 @@ export class FetchClient {
|
|
|
93
135
|
}
|
|
94
136
|
return this;
|
|
95
137
|
}
|
|
138
|
+
#makeHandlerProxy() {
|
|
139
|
+
return new Proxy(this.#handlerStore, {
|
|
140
|
+
get: (target, prop) => {
|
|
141
|
+
if (typeof prop !== "string") return undefined;
|
|
142
|
+
return target[prop] ?? this.#getOrCreateHandler(prop);
|
|
143
|
+
},
|
|
144
|
+
has: (target, prop) => {
|
|
145
|
+
if (typeof prop !== "string") return prop in target;
|
|
146
|
+
return prop in target || this.#handlerFactory.has(prop);
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
#syncSharedRegistry() {
|
|
151
|
+
if (this.#sharedRegistryAppliedVersion === FetchClient.#sharedRegistryVersion) return;
|
|
152
|
+
this.applySignal(FetchClient.#sharedSerializedSignal, { share: false });
|
|
153
|
+
this.#sharedRegistryAppliedVersion = FetchClient.#sharedRegistryVersion;
|
|
154
|
+
}
|
|
155
|
+
#getOrCreateHandler(key: string): FetchHandler | undefined {
|
|
156
|
+
const current = this.#handlerStore[key];
|
|
157
|
+
if (current) return current;
|
|
158
|
+
const factory = this.#handlerFactory.get(key);
|
|
159
|
+
if (factory) {
|
|
160
|
+
const handler = factory();
|
|
161
|
+
this.#handlerStore[key] = handler;
|
|
162
|
+
return handler;
|
|
163
|
+
}
|
|
164
|
+
this.#syncSharedRegistry();
|
|
165
|
+
const syncedFactory = this.#handlerFactory.get(key);
|
|
166
|
+
if (!syncedFactory) return undefined;
|
|
167
|
+
const handler = syncedFactory();
|
|
168
|
+
this.#handlerStore[key] = handler;
|
|
169
|
+
return handler;
|
|
170
|
+
}
|
|
171
|
+
#requireHandler<T extends FetchHandler = FetchHandler>(key: string, owner: string): T {
|
|
172
|
+
const handler = this.#getOrCreateHandler(key);
|
|
173
|
+
if (!handler) throw new Error(`${owner} requires fetch handler "${key}", but it is not registered`);
|
|
174
|
+
return handler as T;
|
|
175
|
+
}
|
|
96
176
|
connect() {
|
|
97
177
|
this.ws.connect();
|
|
98
178
|
}
|
|
@@ -176,58 +256,66 @@ export class FetchClient {
|
|
|
176
256
|
#registerEndpoint(key: string, endpoint: SerializedEndpoint, prefix?: string) {
|
|
177
257
|
switch (endpoint.type) {
|
|
178
258
|
case "query": {
|
|
179
|
-
|
|
259
|
+
this.#handlerFactory.set(key, () => this.#makeHttpFn(key, endpoint, prefix));
|
|
260
|
+
return;
|
|
180
261
|
}
|
|
181
262
|
case "mutation": {
|
|
182
|
-
|
|
263
|
+
this.#handlerFactory.set(key, () => this.#makeHttpFn(key, endpoint, prefix));
|
|
264
|
+
return;
|
|
183
265
|
}
|
|
184
266
|
case "pubsub": {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
|
|
267
|
+
this.#handlerFactory.set(`subscribe${capitalize(key)}`, () => {
|
|
268
|
+
const roomArgs = endpoint.args.filter((arg) => arg.type === "room");
|
|
269
|
+
const roomArgLength = roomArgs.length;
|
|
270
|
+
const serializerMap = this.#makeArgSerializer(endpoint.args);
|
|
271
|
+
const parseReturn = this.#makeReturnParser(endpoint.returns);
|
|
272
|
+
const wrappedListeners = new WeakMap<(data: unknown) => void, (data: unknown) => void>();
|
|
273
|
+
return async (...argData: unknown[]) => {
|
|
274
|
+
const args = argData.slice(0, roomArgLength);
|
|
275
|
+
const handleEvent = argData[roomArgLength] as (data: unknown) => void;
|
|
276
|
+
const fetchPolicy = argData[roomArgLength + 1] as FetchPolicy | undefined;
|
|
277
|
+
const data = roomArgs.map((arg, idx) => serializerMap.get(arg.name)?.(args[idx]) ?? null);
|
|
278
|
+
const wrapped = (data: unknown) => {
|
|
279
|
+
const parsedReturn = parseReturn(data, { crystalize: fetchPolicy?.crystalize ?? true });
|
|
280
|
+
handleEvent(parsedReturn);
|
|
281
|
+
};
|
|
282
|
+
wrappedListeners.set(handleEvent, wrapped);
|
|
283
|
+
this.ws.subscribe({
|
|
284
|
+
key,
|
|
285
|
+
data,
|
|
286
|
+
handleEvent: wrapped,
|
|
287
|
+
});
|
|
288
|
+
return () =>
|
|
289
|
+
this.ws.unsubscribe({ key, data, handleEvent: wrappedListeners.get(handleEvent) ?? handleEvent });
|
|
198
290
|
};
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
key,
|
|
202
|
-
data,
|
|
203
|
-
handleEvent: wrapped,
|
|
204
|
-
});
|
|
205
|
-
return () =>
|
|
206
|
-
this.ws.unsubscribe({ key, data, handleEvent: wrappedListeners.get(handleEvent) ?? handleEvent });
|
|
207
|
-
};
|
|
208
|
-
return Object.assign(this.handler, { [`subscribe${capitalize(key)}`]: pubsubFn });
|
|
291
|
+
});
|
|
292
|
+
return;
|
|
209
293
|
}
|
|
210
294
|
case "message": {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
this.ws.emit(key, data);
|
|
220
|
-
};
|
|
221
|
-
const listenFn = (handleEvent: (data: unknown) => void, fetchPolicy: FetchPolicy = {}) => {
|
|
222
|
-
const wrapped = (data: unknown) => {
|
|
223
|
-
const parsedReturn = parseReturn(data, { crystalize: fetchPolicy?.crystalize ?? true });
|
|
224
|
-
handleEvent(parsedReturn);
|
|
295
|
+
this.#handlerFactory.set(key, () => {
|
|
296
|
+
const msgArgs = endpoint.args.filter((arg) => arg.type === "msg");
|
|
297
|
+
const msgArgLength = msgArgs.length;
|
|
298
|
+
const serializerMap = this.#makeArgSerializer(endpoint.args);
|
|
299
|
+
return async (...argData: unknown[]) => {
|
|
300
|
+
const args = argData.slice(0, msgArgLength);
|
|
301
|
+
const data = msgArgs.map((arg, idx) => serializerMap.get(arg.name)?.(args[idx]) ?? null);
|
|
302
|
+
this.ws.emit(key, data);
|
|
225
303
|
};
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
304
|
+
});
|
|
305
|
+
this.#handlerFactory.set(`listen${capitalize(key)}`, () => {
|
|
306
|
+
const parseReturn = this.#makeReturnParser(endpoint.returns);
|
|
307
|
+
const wrappedListeners = new WeakMap<(data: unknown) => void, (data: unknown) => void>();
|
|
308
|
+
return ((handleEvent: (data: unknown) => void, fetchPolicy: FetchPolicy = {}) => {
|
|
309
|
+
const wrapped = (data: unknown) => {
|
|
310
|
+
const parsedReturn = parseReturn(data, { crystalize: fetchPolicy?.crystalize ?? true });
|
|
311
|
+
handleEvent(parsedReturn);
|
|
312
|
+
};
|
|
313
|
+
wrappedListeners.set(handleEvent, wrapped);
|
|
314
|
+
this.ws.on(key, wrapped);
|
|
315
|
+
return () => this.ws.off(key, wrappedListeners.get(handleEvent) ?? handleEvent);
|
|
316
|
+
}) as FetchHandler;
|
|
317
|
+
});
|
|
318
|
+
return;
|
|
231
319
|
}
|
|
232
320
|
default:
|
|
233
321
|
this.logger.error(`Unsupported endpoint type: ${endpoint.type}`);
|
|
@@ -319,66 +407,88 @@ export class FetchClient {
|
|
|
319
407
|
mergeModel: `merge${capRefName}`,
|
|
320
408
|
};
|
|
321
409
|
const endpoint = FetchClient.getBaseEndpoint(refName, signal);
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
);
|
|
325
|
-
Object.assign(this.handler, handler);
|
|
326
|
-
|
|
327
|
-
if (signal.cruGuards)
|
|
328
|
-
Object.assign(this.handler, {
|
|
329
|
-
[names.viewModel]: async (id: string, option?: FetchPolicy) => {
|
|
330
|
-
const modelObj = await this.handler[names.model](id, { ...option, crystalize: false });
|
|
331
|
-
const model = new cnst.full(modelObj as object);
|
|
332
|
-
return {
|
|
333
|
-
[refName]: model,
|
|
334
|
-
[`${refName}View`]: { refName, [`${refName}Obj`]: modelObj, [`${refName}ViewAt`]: new Date() },
|
|
335
|
-
};
|
|
336
|
-
},
|
|
337
|
-
[names.getModelView]: async (id: string, option?: FetchPolicy) => {
|
|
338
|
-
const modelObj = await this.handler[names.model](id, { ...option, crystalize: false });
|
|
339
|
-
return { refName, [`${refName}Obj`]: modelObj, [`${refName}ViewAt`]: new Date() };
|
|
340
|
-
},
|
|
341
|
-
[names.editModel]: async (id: string, option?: FetchPolicy) => {
|
|
342
|
-
const modelObj = await this.handler[names.model](id, { ...option, crystalize: false });
|
|
343
|
-
const model = new cnst.full(modelObj as object);
|
|
344
|
-
return {
|
|
345
|
-
[refName]: model,
|
|
346
|
-
[`${refName}Edit`]: { refName, [`${refName}Obj`]: modelObj, [`${refName}ViewAt`]: new Date() },
|
|
347
|
-
};
|
|
348
|
-
},
|
|
349
|
-
[names.getModelEdit]: async (id: string, option?: FetchPolicy) => {
|
|
350
|
-
const modelObj = await this.handler[names.model](id, { ...option, crystalize: false });
|
|
351
|
-
return { refName, [`${refName}Obj`]: modelObj, [`${refName}ViewAt`]: new Date() };
|
|
352
|
-
},
|
|
353
|
-
});
|
|
410
|
+
Object.entries(endpoint).forEach(([key, value]) => {
|
|
411
|
+
this.#handlerFactory.set(key, () => this.#makeHttpFn(key, value, signal.prefix));
|
|
412
|
+
});
|
|
354
413
|
|
|
355
|
-
if (signal.cruGuards)
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
414
|
+
if (signal.cruGuards) {
|
|
415
|
+
this.#handlerFactory.set(
|
|
416
|
+
names.viewModel,
|
|
417
|
+
() =>
|
|
418
|
+
(async (id: string, option?: FetchPolicy) => {
|
|
419
|
+
const modelFn = this.#requireHandler(names.model, names.viewModel);
|
|
420
|
+
const modelObj = await modelFn(id, { ...option, crystalize: false });
|
|
421
|
+
const model = new cnst.full(modelObj as object);
|
|
422
|
+
return {
|
|
423
|
+
[refName]: model,
|
|
424
|
+
[`${refName}View`]: { refName, [`${refName}Obj`]: modelObj, [`${refName}ViewAt`]: new Date() },
|
|
425
|
+
};
|
|
426
|
+
}) as FetchHandler,
|
|
427
|
+
);
|
|
428
|
+
this.#handlerFactory.set(
|
|
429
|
+
names.getModelView,
|
|
430
|
+
() =>
|
|
431
|
+
(async (id: string, option?: FetchPolicy) => {
|
|
432
|
+
const modelFn = this.#requireHandler(names.model, names.getModelView);
|
|
433
|
+
const modelObj = await modelFn(id, { ...option, crystalize: false });
|
|
434
|
+
return { refName, [`${refName}Obj`]: modelObj, [`${refName}ViewAt`]: new Date() };
|
|
435
|
+
}) as FetchHandler,
|
|
436
|
+
);
|
|
437
|
+
this.#handlerFactory.set(
|
|
438
|
+
names.editModel,
|
|
439
|
+
() =>
|
|
440
|
+
(async (id: string, option?: FetchPolicy) => {
|
|
441
|
+
const modelFn = this.#requireHandler(names.model, names.editModel);
|
|
442
|
+
const modelObj = await modelFn(id, { ...option, crystalize: false });
|
|
443
|
+
const model = new cnst.full(modelObj as object);
|
|
444
|
+
return {
|
|
445
|
+
[refName]: model,
|
|
446
|
+
[`${refName}Edit`]: { refName, [`${refName}Obj`]: modelObj, [`${refName}ViewAt`]: new Date() },
|
|
447
|
+
};
|
|
448
|
+
}) as FetchHandler,
|
|
449
|
+
);
|
|
450
|
+
this.#handlerFactory.set(
|
|
451
|
+
names.getModelEdit,
|
|
452
|
+
() =>
|
|
453
|
+
(async (id: string, option?: FetchPolicy) => {
|
|
454
|
+
const modelFn = this.#requireHandler(names.model, names.getModelEdit);
|
|
455
|
+
const modelObj = await modelFn(id, { ...option, crystalize: false });
|
|
456
|
+
return { refName, [`${refName}Obj`]: modelObj, [`${refName}ViewAt`]: new Date() };
|
|
457
|
+
}) as FetchHandler,
|
|
458
|
+
);
|
|
459
|
+
this.#handlerFactory.set(
|
|
460
|
+
names.mergeModel,
|
|
461
|
+
() =>
|
|
462
|
+
(async (modelOrId: string | { id: string }, data: UnknownRecord, option?: FetchPolicy) => {
|
|
463
|
+
const id = typeof modelOrId === "string" ? modelOrId : modelOrId.id;
|
|
464
|
+
const updateFn = this.#requireHandler(names.updateModel, names.mergeModel);
|
|
465
|
+
return await updateFn(id, data, option);
|
|
466
|
+
}) as FetchHandler,
|
|
467
|
+
);
|
|
468
|
+
}
|
|
362
469
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
470
|
+
this.#handlerFactory.set(
|
|
471
|
+
names.addModelFiles,
|
|
472
|
+
() =>
|
|
473
|
+
(async (fileList: FileList, parentId?: string, option?: FetchPolicy) => {
|
|
474
|
+
const cap = resolveFileUploadCapability(this.serializedSignal);
|
|
475
|
+
const endpoint = cap ? this.serializedSignal[cap.refName]?.endpoint[cap.endpointKey] : undefined;
|
|
476
|
+
if (!cap || !endpoint)
|
|
477
|
+
throw new Error(
|
|
478
|
+
"File upload is not configured. Mark an upload mutation with { fileUpload: true } (e.g. shared FileEndpoint.addFiles).",
|
|
479
|
+
);
|
|
480
|
+
const { fields, buildMetas } = fileUploadContract;
|
|
481
|
+
const formData = new FormData();
|
|
482
|
+
for (let i = 0; i < fileList.length; i++) formData.append(fields.files, fileList[i]);
|
|
483
|
+
formData.append(fields.metas, JSON.stringify(buildMetas(fileList)));
|
|
484
|
+
formData.append(fields.type, refName);
|
|
485
|
+
if (parentId) formData.append(fields.parentId, parentId);
|
|
486
|
+
const url = FetchClient.makeHttpUrl(cap.endpointKey, endpoint, cap.prefix, new Map());
|
|
487
|
+
return await this.http.post(url, formData, { headers: this.#makeAuthHeaders(option) });
|
|
488
|
+
}) as FetchHandler,
|
|
489
|
+
);
|
|
381
490
|
}
|
|
491
|
+
|
|
382
492
|
static getEndpointFromSlice(
|
|
383
493
|
refName: string,
|
|
384
494
|
suffix: string,
|
|
@@ -418,16 +528,13 @@ export class FetchClient {
|
|
|
418
528
|
};
|
|
419
529
|
|
|
420
530
|
const endpoint = FetchClient.getEndpointFromSlice(refName, suffix, slice);
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
);
|
|
424
|
-
Object.assign(this.handler, handler);
|
|
531
|
+
Object.entries(endpoint).forEach(([key, value]) => {
|
|
532
|
+
this.#handlerFactory.set(key, () => this.#makeHttpFn(key, value, prefix));
|
|
533
|
+
});
|
|
425
534
|
|
|
426
535
|
const argLength = slice.args.length;
|
|
427
536
|
this.slice[sliceName] = { refName, sliceName, argLength };
|
|
428
|
-
|
|
429
|
-
const insightFn = this.handler[names.insight] as (...args: unknown[]) => Promise<unknown>;
|
|
430
|
-
const initFn = async (...argData: unknown[]) => {
|
|
537
|
+
this.#handlerFactory.set(names.init, () => async (...argData: unknown[]) => {
|
|
431
538
|
const queryArgs = normalizeQueryArgs(
|
|
432
539
|
Array.from({ length: Math.min(argData.length, argLength) }, (_, idx) => argData[idx]),
|
|
433
540
|
slice.args,
|
|
@@ -436,6 +543,8 @@ export class FetchClient {
|
|
|
436
543
|
const option = (argData[argLength] ?? {}) as { page?: number; limit?: number; sort?: string; insight?: boolean };
|
|
437
544
|
const { page = 1, limit = 20, sort = "latest", insight: fetchInsight = true } = option;
|
|
438
545
|
const skip = (page - 1) * limit;
|
|
546
|
+
const listFn = this.#requireHandler<(...args: unknown[]) => Promise<unknown[]>>(names.list, names.init);
|
|
547
|
+
const insightFn = this.#requireHandler<(...args: unknown[]) => Promise<unknown>>(names.insight, names.init);
|
|
439
548
|
|
|
440
549
|
const [modelObjList, modelObjInsight] = (await Promise.all([
|
|
441
550
|
listFn(...fetchQueryArgs, skip, limit, sort, { ...option, crystalize: false }),
|
|
@@ -465,13 +574,14 @@ export class FetchClient {
|
|
|
465
574
|
[`${refName}List${capSuffix}`]: modelList,
|
|
466
575
|
[`${refName}Insight${capSuffix}`]: modelInsight,
|
|
467
576
|
};
|
|
468
|
-
};
|
|
469
|
-
|
|
470
|
-
[
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
577
|
+
});
|
|
578
|
+
this.#handlerFactory.set(names.getInit, () => async (...args: unknown[]) => {
|
|
579
|
+
const initFn = this.#requireHandler<(...args: unknown[]) => Promise<Record<string, unknown>>>(
|
|
580
|
+
names.init,
|
|
581
|
+
names.getInit,
|
|
582
|
+
);
|
|
583
|
+
const result = await initFn(...args);
|
|
584
|
+
return result[`${refName}Init${capSuffix}`];
|
|
475
585
|
});
|
|
476
586
|
}
|
|
477
587
|
#makeArgSerializer(args: SerializedArg[]) {
|
|
@@ -540,34 +650,18 @@ export class FetchClient {
|
|
|
540
650
|
>(...signals: Signals): FetchProxy<MergeAllFetchTypes<Signals>, GetSliceMetaObjFromDatabaseSignals<Signals>> {
|
|
541
651
|
const serializedSignal: { [key: string]: SerializedSignal } = {};
|
|
542
652
|
const handler: Record<string, FetchHandler> = {};
|
|
543
|
-
const mergeSerializedSignal = (refName: string, signal: SerializedSignal) => {
|
|
544
|
-
const current = serializedSignal[refName];
|
|
545
|
-
serializedSignal[refName] = current
|
|
546
|
-
? {
|
|
547
|
-
...current,
|
|
548
|
-
...signal,
|
|
549
|
-
endpoint: { ...current.endpoint, ...signal.endpoint },
|
|
550
|
-
slice: current.slice || signal.slice ? { ...current.slice, ...signal.slice } : undefined,
|
|
551
|
-
filter:
|
|
552
|
-
current.filter || signal.filter
|
|
553
|
-
? {
|
|
554
|
-
filter: { ...current.filter?.filter, ...signal.filter?.filter },
|
|
555
|
-
sortKeys: [...new Set([...(current.filter?.sortKeys ?? []), ...(signal.filter?.sortKeys ?? [])])],
|
|
556
|
-
}
|
|
557
|
-
: undefined,
|
|
558
|
-
getGuards: signal.getGuards ?? current.getGuards,
|
|
559
|
-
cruGuards: signal.cruGuards ?? current.cruGuards,
|
|
560
|
-
}
|
|
561
|
-
: signal;
|
|
562
|
-
};
|
|
563
653
|
signals.forEach((signal) => {
|
|
564
654
|
if ("endpoint" in signal) {
|
|
565
655
|
const refName = (signal as DatabaseSignal | ServiceSignal).endpoint.baseName;
|
|
566
|
-
|
|
656
|
+
FetchClient.#mergeSerializedSignalInto(
|
|
657
|
+
serializedSignal,
|
|
658
|
+
refName,
|
|
659
|
+
(signal as DatabaseSignal | ServiceSignal).serializedSignal,
|
|
660
|
+
);
|
|
567
661
|
} else {
|
|
568
662
|
Object.assign(handler, (signal as FetchClient).handler);
|
|
569
663
|
Object.entries((signal as FetchClient).serializedSignal).forEach(([refName, signal]) => {
|
|
570
|
-
|
|
664
|
+
FetchClient.#mergeSerializedSignalInto(serializedSignal, refName, signal);
|
|
571
665
|
});
|
|
572
666
|
}
|
|
573
667
|
});
|
|
@@ -613,8 +707,11 @@ export class FetchClient {
|
|
|
613
707
|
get(target, prop) {
|
|
614
708
|
if (prop in target) return (target as any)[prop];
|
|
615
709
|
else if (prop === "instance") return instance;
|
|
616
|
-
else if (prop
|
|
617
|
-
|
|
710
|
+
else if (typeof prop === "string") {
|
|
711
|
+
const handler = instance.#getOrCreateHandler(prop);
|
|
712
|
+
if (handler) return handler;
|
|
713
|
+
}
|
|
714
|
+
return (instance as unknown as Record<PropertyKey, unknown>)[prop];
|
|
618
715
|
},
|
|
619
716
|
}) as FetchProxy<FetchType, SliceMetaObj>;
|
|
620
717
|
}
|
package/package.json
CHANGED
|
@@ -34,9 +34,12 @@ export declare class FetchClient {
|
|
|
34
34
|
constructor(origin: string, handler?: Record<string, FetchHandler>, serializedSignal?: {
|
|
35
35
|
[key: string]: SerializedSignal;
|
|
36
36
|
}, ErrorCls?: ErrorConstructor | undefined);
|
|
37
|
+
static resetSharedRegistry(): void;
|
|
37
38
|
setErrorConstructor(ErrorCls?: ErrorConstructor): void;
|
|
38
39
|
applySignal(serializedSignal: {
|
|
39
40
|
[key: string]: SerializedSignal;
|
|
41
|
+
}, { share }?: {
|
|
42
|
+
share?: boolean;
|
|
40
43
|
}): this;
|
|
41
44
|
connect(): void;
|
|
42
45
|
disconnect(): void;
|