@duplojs/http 0.10.0 → 0.11.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.
Files changed (82) hide show
  1. package/dist/client/clientCache.cjs +84 -0
  2. package/dist/client/clientCache.d.ts +4 -0
  3. package/dist/client/clientCache.mjs +80 -0
  4. package/dist/client/getBody.cjs +0 -3
  5. package/dist/client/getBody.mjs +0 -3
  6. package/dist/client/hooks.cjs +36 -0
  7. package/dist/client/hooks.d.ts +5 -1
  8. package/dist/client/hooks.mjs +33 -1
  9. package/dist/client/httpClient.cjs +26 -1
  10. package/dist/client/httpClient.d.ts +8 -1
  11. package/dist/client/httpClient.mjs +26 -1
  12. package/dist/client/index.cjs +12 -0
  13. package/dist/client/index.d.ts +2 -0
  14. package/dist/client/index.mjs +4 -2
  15. package/dist/client/promiseRequest.cjs +33 -8
  16. package/dist/client/promiseRequest.d.ts +2 -1
  17. package/dist/client/promiseRequest.mjs +34 -9
  18. package/dist/client/serverSentEvents.cjs +18 -12
  19. package/dist/client/serverSentEvents.d.ts +2 -1
  20. package/dist/client/serverSentEvents.mjs +18 -13
  21. package/dist/client/stream.cjs +117 -0
  22. package/dist/client/stream.d.ts +3 -0
  23. package/dist/client/stream.mjs +95 -0
  24. package/dist/client/types/clientCache.cjs +2 -0
  25. package/dist/client/types/clientCache.d.ts +27 -0
  26. package/dist/client/types/clientCache.mjs +1 -0
  27. package/dist/client/types/clientRequestParams.d.ts +8 -1
  28. package/dist/client/types/clientResponse.d.ts +24 -6
  29. package/dist/client/types/hooks.d.ts +10 -1
  30. package/dist/client/types/index.cjs +1 -0
  31. package/dist/client/types/index.d.ts +1 -0
  32. package/dist/client/types/index.mjs +1 -0
  33. package/dist/client/types/promiseRequestParams.d.ts +3 -0
  34. package/dist/client/types/serverRoute.d.ts +2 -0
  35. package/dist/core/builders/route/handler.d.ts +5 -5
  36. package/dist/core/defaultHooks/index.cjs +14 -0
  37. package/dist/core/defaultHooks/index.mjs +14 -0
  38. package/dist/core/functionsBuilders/steps/defaults/extractStep.mjs +1 -1
  39. package/dist/core/functionsBuilders/steps/defaults/handlerStep.cjs +33 -0
  40. package/dist/core/functionsBuilders/steps/defaults/handlerStep.mjs +33 -0
  41. package/dist/core/hub/defaultMalformedUrlHandler.d.ts +1 -1
  42. package/dist/core/hub/defaultNotfoundHandler.d.ts +1 -1
  43. package/dist/core/index.cjs +143 -134
  44. package/dist/core/index.d.ts +1 -0
  45. package/dist/core/index.mjs +42 -39
  46. package/dist/core/response/contract.cjs +21 -0
  47. package/dist/core/response/contract.d.ts +33 -5
  48. package/dist/core/response/contract.mjs +21 -0
  49. package/dist/core/response/index.cjs +4 -0
  50. package/dist/core/response/index.d.ts +7 -0
  51. package/dist/core/response/index.mjs +2 -0
  52. package/dist/core/response/streamPredicted.cjs +23 -0
  53. package/dist/core/response/streamPredicted.d.ts +14 -0
  54. package/dist/core/response/streamPredicted.mjs +21 -0
  55. package/dist/core/response/streamTextPredicted.cjs +23 -0
  56. package/dist/core/response/streamTextPredicted.d.ts +14 -0
  57. package/dist/core/response/streamTextPredicted.mjs +21 -0
  58. package/dist/core/serverSentEvents.cjs +25 -77
  59. package/dist/core/serverSentEvents.d.ts +4 -16
  60. package/dist/core/serverSentEvents.mjs +25 -77
  61. package/dist/core/steps/handler.d.ts +15 -5
  62. package/dist/core/stream.cjs +73 -0
  63. package/dist/core/stream.d.ts +19 -0
  64. package/dist/core/stream.mjs +73 -0
  65. package/dist/interfaces/node/bodyReaders/formData/index.cjs +1 -1
  66. package/dist/interfaces/node/bodyReaders/formData/index.mjs +1 -1
  67. package/dist/interfaces/node/hooks/index.cjs +21 -1
  68. package/dist/interfaces/node/hooks/index.mjs +21 -1
  69. package/dist/interfaces/node/index.cjs +6 -6
  70. package/dist/interfaces/node/index.mjs +2 -2
  71. package/dist/plugins/codeGenerator/aggregateStepContract.cjs +12 -0
  72. package/dist/plugins/codeGenerator/aggregateStepContract.mjs +13 -1
  73. package/dist/plugins/cookie/index.cjs +2 -2
  74. package/dist/plugins/cookie/index.mjs +1 -1
  75. package/dist/plugins/openApiGenerator/aggregateStepContract.d.ts +1 -1
  76. package/dist/plugins/openApiGenerator/makeOpenApiRoute.d.ts +1 -1
  77. package/dist/plugins/openApiGenerator/routeToOpenApi.cjs +55 -2
  78. package/dist/plugins/openApiGenerator/routeToOpenApi.mjs +55 -2
  79. package/dist/plugins/openApiGenerator/types/endpointResponse.d.ts +4 -1
  80. package/dist/plugins/static/makeRouteFile.d.ts +1 -1
  81. package/dist/plugins/static/makeRouteFolder.d.ts +1 -1
  82. package/package.json +3 -3
@@ -1,7 +1,7 @@
1
1
  import { type NeverCoalescing, type MaybePromise, type O } from "@duplojs/utils";
2
2
  import * as EE from "@duplojs/utils/either";
3
3
  import { type RequestErrorContent } from "./unexpectedResponseError";
4
- import { type PromiseRequestParams, type Hooks, type NotPredictedResponseHook, type ErrorHook, type ClientEventsResponse, type AllClientResponse, type AllNotPredictedClientResponse, type ClientEventsResponseHandler, type ServerEvent } from "./types";
4
+ import { type PromiseRequestParams, type Hooks, type NotPredictedResponseHook, type ErrorHook, type ClientEventsResponse, type AllClientResponse, type AllNotPredictedClientResponse, type ClientEventsResponseHandler, type ServerEvent, type ClientStreamResponseHandler, type ClientStreamResponse } from "./types";
5
5
  type MaybeResponse<GenericClientResponse extends AllClientResponse = AllClientResponse> = (EE.Right<"response", GenericClientResponse> | EE.Left<"request-error", RequestErrorContent>);
6
6
  type MaybeWantedResponse<GenericWantedClientResponse extends AllClientResponse = AllClientResponse, GenericUnexpectClientResponse extends AllClientResponse = AllClientResponse> = (EE.Right<"response", GenericWantedClientResponse> | EE.Left<"unexpect-response", GenericUnexpectClientResponse> | EE.Left<"request-error", RequestErrorContent>);
7
7
  export declare class PromiseRequest<GenericHookParams extends Record<string, unknown> = Record<string, unknown>, GenericClientResponse extends AllClientResponse<GenericHookParams> = AllClientResponse<GenericHookParams>> extends Promise<MaybeResponse<GenericClientResponse | AllNotPredictedClientResponse<GenericHookParams>>> {
@@ -39,6 +39,7 @@ export declare class PromiseRequest<GenericHookParams extends Record<string, unk
39
39
  whenReceiveServerEvent<GenericEvent extends (GenericClientResponse extends ClientEventsResponseHandler<infer InferredEvent> ? InferredEvent : never), GenericEventName extends GenericEvent["event"]>(eventName: GenericEventName, callback: (event: NoInfer<NeverCoalescing<Extract<GenericEvent, {
40
40
  event: GenericEventName;
41
41
  }>, ServerEvent>>, response: NeverCoalescing<Extract<GenericClientResponse, ClientEventsResponseHandler<GenericEvent>>, ClientEventsResponse>) => MaybePromise<void>): this;
42
+ whenReceiveDataStream<GenericFlux extends (GenericClientResponse extends ClientStreamResponseHandler<infer InferredFlux> ? InferredFlux : never)>(callback: (data: GenericFlux, response: NeverCoalescing<Extract<GenericClientResponse, ClientStreamResponseHandler>, ClientStreamResponse>) => MaybePromise<void>): this;
42
43
  iWantInformation<GenericInformation extends Extract<GenericClientResponse["information"], string>, GenericResponse extends NeverCoalescing<Extract<GenericClientResponse, GenericInformation extends any ? {
43
44
  information: GenericInformation;
44
45
  } : never>, AllClientResponse<GenericHookParams>>>(information: GenericInformation | GenericInformation[]): Promise<MaybeWantedResponse<GenericResponse, NeverCoalescing<Exclude<GenericClientResponse, GenericResponse>, AllClientResponse<GenericHookParams>> | AllNotPredictedClientResponse<GenericHookParams>>>;
@@ -7,7 +7,9 @@ import * as EE from '@duplojs/utils/either';
7
7
  import * as SS from '@duplojs/utils/string';
8
8
  import * as AA from '@duplojs/utils/array';
9
9
  import { UnexpectedInformationResponseError, UnexpectedCodeResponseError, UnexpectedResponseTypeError, UnexpectedResponseError } from './unexpectedResponseError.mjs';
10
- import { makeClientEventsResponse } from './serverSentEvents.mjs';
10
+ import { isClientEventsResponse, makeClientEventsResponse } from './serverSentEvents.mjs';
11
+ import { findResponseFromCacheStore, saveResponseInCacheStore } from './clientCache.mjs';
12
+ import { isClientStreamResponse, makeClientStreamResponse } from './stream.mjs';
11
13
 
12
14
  class PromiseRequest extends Promise {
13
15
  params;
@@ -128,12 +130,22 @@ class PromiseRequest extends Promise {
128
130
  void this.then(EE.whenIsRight((response) => {
129
131
  if ((response.predicted === true
130
132
  || response.requestParams.disabledPredicateMode === true)
131
- && Symbol.asyncIterator in response) {
133
+ && isClientEventsResponse(response)) {
132
134
  response.onReceiveEvent(eventName, callback);
133
135
  }
134
136
  }));
135
137
  return this;
136
138
  }
139
+ whenReceiveDataStream(callback) {
140
+ void this.then(EE.whenIsRight((response) => {
141
+ if ((response.predicted === true
142
+ || response.requestParams.disabledPredicateMode === true)
143
+ && isClientStreamResponse(response)) {
144
+ response.onStream("receiveData", callback);
145
+ }
146
+ }));
147
+ return this;
148
+ }
137
149
  iWantInformation(information) {
138
150
  const formattedInformation = AA.coalescing(information);
139
151
  return this.then(EE.whenIsRight((response) => {
@@ -295,7 +307,7 @@ class PromiseRequest extends Promise {
295
307
  if (EE.isRight(maybeResponse)) {
296
308
  return unwrap(maybeResponse);
297
309
  }
298
- throw new UnexpectedResponseTypeError("informational", unwrap(maybeResponse));
310
+ throw new UnexpectedResponseTypeError("serverError", unwrap(maybeResponse));
299
311
  });
300
312
  }
301
313
  iWantExpectedResponseOrThrow() {
@@ -322,6 +334,10 @@ class PromiseRequest extends Promise {
322
334
  return Promise;
323
335
  }
324
336
  static fetch(requestParams) {
337
+ const cachedResponse = findResponseFromCacheStore(requestParams);
338
+ if (cachedResponse) {
339
+ return Promise.resolve(EE.right("response", cachedResponse));
340
+ }
325
341
  const path = insertParamsInPath(requestParams.path, requestParams.params);
326
342
  const query = queryToString(requestParams.query);
327
343
  const url = query
@@ -336,7 +352,7 @@ class PromiseRequest extends Promise {
336
352
  body = body.toString();
337
353
  }
338
354
  else if (body instanceof TheFormData) {
339
- headers["content-type-options"] = "advanced";
355
+ headers["x-duplojs-body-options"] = "advanced";
340
356
  }
341
357
  else if ((body
342
358
  && typeof body === "object"
@@ -360,10 +376,9 @@ class PromiseRequest extends Promise {
360
376
  signal: requestParams.abortController.signal,
361
377
  };
362
378
  return fetch(fetchUrl, fetchInitParams)
363
- .then((response) => getBody(response)
364
- .then((body) => {
379
+ .then((response) => {
365
380
  const clientResponse = {
366
- body,
381
+ body: undefined,
367
382
  information: response.headers.get(requestParams.informationHeaderKey) ?? undefined,
368
383
  code: response.status.toString(),
369
384
  ok: (response.status < 500)
@@ -380,8 +395,18 @@ class PromiseRequest extends Promise {
380
395
  if (response.headers.get("content-type")?.includes("text/event-stream")) {
381
396
  return EE.right("response", makeClientEventsResponse(clientResponse, fetchUrl, fetchInitParams));
382
397
  }
383
- return EE.right("response", clientResponse);
384
- }))
398
+ if (response.headers.get("x-duplojs-body-options")?.includes("stream")) {
399
+ return EE.right("response", makeClientStreamResponse(clientResponse));
400
+ }
401
+ return getBody(response)
402
+ .then((body) => {
403
+ clientResponse.body = body;
404
+ if (clientResponse.code.startsWith("2")) {
405
+ saveResponseInCacheStore(requestParams, clientResponse);
406
+ }
407
+ return EE.right("response", clientResponse);
408
+ });
409
+ })
385
410
  .catch((error) => EE.left("request-error", {
386
411
  error,
387
412
  requestParams,
@@ -43,14 +43,7 @@ function makeClientEventsResponse(response, fetchUrl, fetchInitParams) {
43
43
  let receiveEventServerEvent = undefined;
44
44
  const eventResponse = {
45
45
  ...response,
46
- closeEventStream: () => void abortController.abort(closeReason),
47
- onReceiveEvent: (eventName, callback) => {
48
- receiveEventServerEvent ??= [];
49
- receiveEventServerEvent.push((receiveEvent) => receiveEvent.event === eventName
50
- ? callback(receiveEvent, eventResponse)
51
- : undefined);
52
- return eventResponse;
53
- },
46
+ handlerType: "events",
54
47
  onStreamEvent: (event, callback) => {
55
48
  if (event === "receiveServerEvents") {
56
49
  receiveEventServerEvent ??= [];
@@ -74,7 +67,18 @@ function makeClientEventsResponse(response, fetchUrl, fetchInitParams) {
74
67
  }
75
68
  return eventResponse;
76
69
  },
77
- async consumeEventStream() {
70
+ onReceiveEvent: (eventName, callback) => {
71
+ receiveEventServerEvent ??= [];
72
+ receiveEventServerEvent.push((receiveEvent) => receiveEvent.event === eventName
73
+ ? callback(receiveEvent, eventResponse)
74
+ : undefined);
75
+ return eventResponse;
76
+ },
77
+ closeEventStream: () => {
78
+ abortController.abort(closeReason);
79
+ void reader?.cancel(closeReason);
80
+ },
81
+ consumeEventStream: async () => {
78
82
  for await (const __ of eventResponse) { }
79
83
  },
80
84
  [Symbol.asyncIterator]: async function* () {
@@ -94,9 +98,7 @@ function makeClientEventsResponse(response, fetchUrl, fetchInitParams) {
94
98
  };
95
99
  return eventResponse;
96
100
  };
97
- if (!reader
98
- || response.code === "204"
99
- || !response.headers.get("content-type")?.includes("text/event-stream")) {
101
+ if (!reader || response.code === "204") {
100
102
  return createEventResponse(async function* () { });
101
103
  }
102
104
  return createEventResponse((emitError, emitBeforeRetry) => {
@@ -227,5 +229,9 @@ function makeClientEventsResponse(response, fetchUrl, fetchInitParams) {
227
229
  });
228
230
  });
229
231
  }
232
+ function isClientEventsResponse(response) {
233
+ return Symbol.asyncIterator in response && response.handlerType === "events";
234
+ }
230
235
 
236
+ exports.isClientEventsResponse = isClientEventsResponse;
231
237
  exports.makeClientEventsResponse = makeClientEventsResponse;
@@ -1,2 +1,3 @@
1
- import { type ClientEventsResponse, type ClientResponse } from "./types";
1
+ import { type ClientEventsResponse, type ClientResponse, type AllClientResponse } from "./types";
2
2
  export declare function makeClientEventsResponse(response: ClientResponse, fetchUrl: string, fetchInitParams: RequestInit): ClientEventsResponse;
3
+ export declare function isClientEventsResponse<GenericResponse extends AllClientResponse>(response: GenericResponse): response is Extract<GenericResponse, ClientEventsResponse>;
@@ -20,14 +20,7 @@ function makeClientEventsResponse(response, fetchUrl, fetchInitParams) {
20
20
  let receiveEventServerEvent = undefined;
21
21
  const eventResponse = {
22
22
  ...response,
23
- closeEventStream: () => void abortController.abort(closeReason),
24
- onReceiveEvent: (eventName, callback) => {
25
- receiveEventServerEvent ??= [];
26
- receiveEventServerEvent.push((receiveEvent) => receiveEvent.event === eventName
27
- ? callback(receiveEvent, eventResponse)
28
- : undefined);
29
- return eventResponse;
30
- },
23
+ handlerType: "events",
31
24
  onStreamEvent: (event, callback) => {
32
25
  if (event === "receiveServerEvents") {
33
26
  receiveEventServerEvent ??= [];
@@ -51,7 +44,18 @@ function makeClientEventsResponse(response, fetchUrl, fetchInitParams) {
51
44
  }
52
45
  return eventResponse;
53
46
  },
54
- async consumeEventStream() {
47
+ onReceiveEvent: (eventName, callback) => {
48
+ receiveEventServerEvent ??= [];
49
+ receiveEventServerEvent.push((receiveEvent) => receiveEvent.event === eventName
50
+ ? callback(receiveEvent, eventResponse)
51
+ : undefined);
52
+ return eventResponse;
53
+ },
54
+ closeEventStream: () => {
55
+ abortController.abort(closeReason);
56
+ void reader?.cancel(closeReason);
57
+ },
58
+ consumeEventStream: async () => {
55
59
  for await (const __ of eventResponse) { }
56
60
  },
57
61
  [Symbol.asyncIterator]: async function* () {
@@ -71,9 +75,7 @@ function makeClientEventsResponse(response, fetchUrl, fetchInitParams) {
71
75
  };
72
76
  return eventResponse;
73
77
  };
74
- if (!reader
75
- || response.code === "204"
76
- || !response.headers.get("content-type")?.includes("text/event-stream")) {
78
+ if (!reader || response.code === "204") {
77
79
  return createEventResponse(async function* () { });
78
80
  }
79
81
  return createEventResponse((emitError, emitBeforeRetry) => {
@@ -204,5 +206,8 @@ function makeClientEventsResponse(response, fetchUrl, fetchInitParams) {
204
206
  });
205
207
  });
206
208
  }
209
+ function isClientEventsResponse(response) {
210
+ return Symbol.asyncIterator in response && response.handlerType === "events";
211
+ }
207
212
 
208
- export { makeClientEventsResponse };
213
+ export { isClientEventsResponse, makeClientEventsResponse };
@@ -0,0 +1,117 @@
1
+ 'use strict';
2
+
3
+ var GG = require('@duplojs/utils/generator');
4
+ var hooks = require('./hooks.cjs');
5
+
6
+ function _interopNamespaceDefault(e) {
7
+ var n = Object.create(null);
8
+ if (e) {
9
+ Object.keys(e).forEach(function (k) {
10
+ if (k !== 'default') {
11
+ var d = Object.getOwnPropertyDescriptor(e, k);
12
+ Object.defineProperty(n, k, d.get ? d : {
13
+ enumerable: true,
14
+ get: function () { return e[k]; }
15
+ });
16
+ }
17
+ });
18
+ }
19
+ n.default = e;
20
+ return Object.freeze(n);
21
+ }
22
+
23
+ var GG__namespace = /*#__PURE__*/_interopNamespaceDefault(GG);
24
+
25
+ const closeReason = Symbol("CloseReason");
26
+ function makeClientStreamResponse(response) {
27
+ const reader = response.raw.body?.getReader();
28
+ const abortController = response.requestParams.abortController;
29
+ const createStreamResponse = (streamReaderGenerator) => {
30
+ let closeStream = undefined;
31
+ let receiveDataStream = undefined;
32
+ let errorStream = undefined;
33
+ let startStream = undefined;
34
+ const streamResponse = {
35
+ ...response,
36
+ handlerType: "stream",
37
+ onStream: (event, callback) => {
38
+ if (event === "close") {
39
+ closeStream ??= [];
40
+ closeStream.push(callback);
41
+ }
42
+ else if (event === "receiveData") {
43
+ receiveDataStream ??= [];
44
+ receiveDataStream.push(callback);
45
+ }
46
+ else if (event === "error") {
47
+ errorStream ??= [];
48
+ errorStream.push(callback);
49
+ }
50
+ else if (event === "start") {
51
+ startStream ??= [];
52
+ startStream.push(callback);
53
+ }
54
+ return streamResponse;
55
+ },
56
+ closeStream: () => {
57
+ abortController.abort(closeReason);
58
+ void reader?.cancel(closeReason);
59
+ },
60
+ consumeStream: async () => {
61
+ for await (const __ of streamResponse) { }
62
+ },
63
+ [Symbol.asyncIterator]: async function* () {
64
+ await hooks.launchStartStreamHook(response.requestParams.hooks.startStream, startStream ?? [], streamResponse).catch(console.error);
65
+ const onError = (error) => hooks.launchErrorStreamHook(response.requestParams.hooks.errorStream, errorStream ?? [], error, streamResponse).catch(console.error);
66
+ const generator = streamReaderGenerator(onError);
67
+ try {
68
+ for await (const event of generator) {
69
+ await hooks.launchReceiveDataStreamHook(response.requestParams.hooks.receiveDataStream, receiveDataStream ?? [], event, streamResponse).catch(console.error);
70
+ yield event;
71
+ }
72
+ }
73
+ finally {
74
+ await hooks.launchCloseStreamHook(response.requestParams.hooks.closeStream, closeStream ?? [], streamResponse).catch(console.error);
75
+ }
76
+ },
77
+ };
78
+ return streamResponse;
79
+ };
80
+ if (!reader || response.code === "204") {
81
+ return createStreamResponse(async function* () { });
82
+ }
83
+ const textDecoder = response.headers.get("content-type")?.includes("text") && new TextDecoder("utf-8");
84
+ return createStreamResponse((emitError) => GG__namespace.asyncLoop(async ({ next, exit }) => {
85
+ try {
86
+ if (abortController.signal.aborted) {
87
+ return exit();
88
+ }
89
+ const result = await reader.read();
90
+ if (textDecoder) {
91
+ const chunk = textDecoder.decode(result.value, { stream: true }) || undefined;
92
+ if (result.done) {
93
+ return exit(`${chunk ?? ""}${textDecoder.decode()}` || undefined);
94
+ }
95
+ return next(chunk);
96
+ }
97
+ else if (result.done) {
98
+ return exit(result.value);
99
+ }
100
+ else {
101
+ return next(result.value);
102
+ }
103
+ }
104
+ catch (error) {
105
+ if (error !== closeReason) {
106
+ await emitError(error);
107
+ }
108
+ return exit();
109
+ }
110
+ }));
111
+ }
112
+ function isClientStreamResponse(response) {
113
+ return Symbol.asyncIterator in response && response.handlerType === "stream";
114
+ }
115
+
116
+ exports.isClientStreamResponse = isClientStreamResponse;
117
+ exports.makeClientStreamResponse = makeClientStreamResponse;
@@ -0,0 +1,3 @@
1
+ import { type ClientResponse, type ClientStreamResponse, type AllClientResponse } from "./types";
2
+ export declare function makeClientStreamResponse(response: ClientResponse): ClientStreamResponse;
3
+ export declare function isClientStreamResponse<GenericResponse extends AllClientResponse>(response: GenericResponse): response is Extract<GenericResponse, ClientStreamResponse>;
@@ -0,0 +1,95 @@
1
+ import * as GG from '@duplojs/utils/generator';
2
+ import { launchStartStreamHook, launchErrorStreamHook, launchReceiveDataStreamHook, launchCloseStreamHook } from './hooks.mjs';
3
+
4
+ const closeReason = Symbol("CloseReason");
5
+ function makeClientStreamResponse(response) {
6
+ const reader = response.raw.body?.getReader();
7
+ const abortController = response.requestParams.abortController;
8
+ const createStreamResponse = (streamReaderGenerator) => {
9
+ let closeStream = undefined;
10
+ let receiveDataStream = undefined;
11
+ let errorStream = undefined;
12
+ let startStream = undefined;
13
+ const streamResponse = {
14
+ ...response,
15
+ handlerType: "stream",
16
+ onStream: (event, callback) => {
17
+ if (event === "close") {
18
+ closeStream ??= [];
19
+ closeStream.push(callback);
20
+ }
21
+ else if (event === "receiveData") {
22
+ receiveDataStream ??= [];
23
+ receiveDataStream.push(callback);
24
+ }
25
+ else if (event === "error") {
26
+ errorStream ??= [];
27
+ errorStream.push(callback);
28
+ }
29
+ else if (event === "start") {
30
+ startStream ??= [];
31
+ startStream.push(callback);
32
+ }
33
+ return streamResponse;
34
+ },
35
+ closeStream: () => {
36
+ abortController.abort(closeReason);
37
+ void reader?.cancel(closeReason);
38
+ },
39
+ consumeStream: async () => {
40
+ for await (const __ of streamResponse) { }
41
+ },
42
+ [Symbol.asyncIterator]: async function* () {
43
+ await launchStartStreamHook(response.requestParams.hooks.startStream, startStream ?? [], streamResponse).catch(console.error);
44
+ const onError = (error) => launchErrorStreamHook(response.requestParams.hooks.errorStream, errorStream ?? [], error, streamResponse).catch(console.error);
45
+ const generator = streamReaderGenerator(onError);
46
+ try {
47
+ for await (const event of generator) {
48
+ await launchReceiveDataStreamHook(response.requestParams.hooks.receiveDataStream, receiveDataStream ?? [], event, streamResponse).catch(console.error);
49
+ yield event;
50
+ }
51
+ }
52
+ finally {
53
+ await launchCloseStreamHook(response.requestParams.hooks.closeStream, closeStream ?? [], streamResponse).catch(console.error);
54
+ }
55
+ },
56
+ };
57
+ return streamResponse;
58
+ };
59
+ if (!reader || response.code === "204") {
60
+ return createStreamResponse(async function* () { });
61
+ }
62
+ const textDecoder = response.headers.get("content-type")?.includes("text") && new TextDecoder("utf-8");
63
+ return createStreamResponse((emitError) => GG.asyncLoop(async ({ next, exit }) => {
64
+ try {
65
+ if (abortController.signal.aborted) {
66
+ return exit();
67
+ }
68
+ const result = await reader.read();
69
+ if (textDecoder) {
70
+ const chunk = textDecoder.decode(result.value, { stream: true }) || undefined;
71
+ if (result.done) {
72
+ return exit(`${chunk ?? ""}${textDecoder.decode()}` || undefined);
73
+ }
74
+ return next(chunk);
75
+ }
76
+ else if (result.done) {
77
+ return exit(result.value);
78
+ }
79
+ else {
80
+ return next(result.value);
81
+ }
82
+ }
83
+ catch (error) {
84
+ if (error !== closeReason) {
85
+ await emitError(error);
86
+ }
87
+ return exit();
88
+ }
89
+ }));
90
+ }
91
+ function isClientStreamResponse(response) {
92
+ return Symbol.asyncIterator in response && response.handlerType === "stream";
93
+ }
94
+
95
+ export { isClientStreamResponse, makeClientStreamResponse };
@@ -0,0 +1,2 @@
1
+ 'use strict';
2
+
@@ -0,0 +1,27 @@
1
+ import type * as SS from "@duplojs/utils/string";
2
+ import { type ClientRequestParamsBody, type ClientRequestParamsHeaders, type ClientRequestParamsParams, type ClientRequestParamsQuery } from "./clientRequestParams";
3
+ import { type ClientResponseBody } from "./clientResponse";
4
+ import { type BivariantFunction } from "@duplojs/utils";
5
+ export interface ClientCacheValue {
6
+ information?: string;
7
+ body: ClientResponseBody;
8
+ headers: Record<string, string>;
9
+ ok: boolean | null;
10
+ type: ResponseType;
11
+ code: SS.Number;
12
+ url: string;
13
+ redirected: boolean;
14
+ predicted: boolean;
15
+ }
16
+ export type ClientCacheInitialValues = Record<string, ClientCacheValue>;
17
+ export type ClientCacheStore = Map<string, ClientCacheValue>;
18
+ export interface CreateClientCacheKeyParams<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> {
19
+ method: string;
20
+ path: string;
21
+ headers: ClientRequestParamsHeaders | undefined;
22
+ params: ClientRequestParamsParams | undefined;
23
+ query: ClientRequestParamsQuery | undefined;
24
+ body: ClientRequestParamsBody;
25
+ hookParams: GenericHookParams | undefined;
26
+ }
27
+ export type CreateClientCacheKey<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> = BivariantFunction<(params: CreateClientCacheKeyParams<GenericHookParams>) => string | null>;
@@ -0,0 +1 @@
1
+
@@ -2,6 +2,7 @@ import { type SimplifyTopLevel, type IsEqual, type MaybeArray, type AnyTuple } f
2
2
  import { type ServerRouteHeaders, type ServerRouteParams, type ServerRouteQuery, type ServerRoute, type ServerPrimitiveData } from "./serverRoute";
3
3
  import { type ObjectCanBeEmpty } from "./ObjectCanBeEmpty";
4
4
  import type * as OO from "@duplojs/utils/object";
5
+ import { type CreateClientCacheKey } from "./clientCache";
5
6
  export interface ClientRequestInitParams extends Pick<RequestInit, "cache" | "credentials" | "integrity" | "keepalive" | "mode" | "redirect" | "referrer" | "referrerPolicy" | "signal"> {
6
7
  }
7
8
  export type ClientRequestParamsHeaders = Record<string, string | undefined | {
@@ -24,6 +25,9 @@ export interface ClientRequestParams<GenericHookParams extends Record<string, un
24
25
  abortController?: AbortController;
25
26
  initParams?: ClientRequestInitParams;
26
27
  hookParams?: GenericHookParams;
28
+ clientCache?: "auto" | CreateClientCacheKey<GenericHookParams>;
29
+ bypassClientCache?: boolean;
30
+ refreshClientCache?: boolean;
27
31
  }
28
32
  type StringifyTuple<GenericTuple extends AnyTuple<ServerPrimitiveData>> = GenericTuple extends [
29
33
  infer InferredFirst extends ServerPrimitiveData,
@@ -47,6 +51,9 @@ export type ServerRouteToClientRequestParams<GenericServerRoute extends ServerRo
47
51
  abortController?: AbortController;
48
52
  initParams?: ClientRequestInitParams;
49
53
  hookParams?: GenericHookParams;
54
+ bypassClientCache?: boolean;
55
+ refreshClientCache?: boolean;
56
+ clientCache?: "auto" | CreateClientCacheKey<GenericHookParams>;
50
57
  } & MaybeParams<(IsEqual<GenericServerRoute["headers"], unknown> extends true ? {} : {
51
58
  headers: ServerRouteToClientRequestParamsHeaders<GenericServerRoute["headers"]>;
52
59
  }) & (IsEqual<GenericServerRoute["params"], unknown> extends true ? {} : {
@@ -55,5 +62,5 @@ export type ServerRouteToClientRequestParams<GenericServerRoute extends ServerRo
55
62
  query: ServerRouteToClientRequestParamsQuery<GenericServerRoute["query"]>;
56
63
  }) & (IsEqual<GenericServerRoute["body"], unknown> extends true ? {} : {
57
64
  body: GenericServerRoute["body"];
58
- })>)> extends infer InferredResult extends ClientRequestParams ? InferredResult : never : never;
65
+ })>)> extends infer InferredResult extends ClientRequestParams<GenericHookParams> ? InferredResult : never : never;
59
66
  export {};
@@ -1,7 +1,8 @@
1
1
  import type * as SS from "@duplojs/utils/string";
2
- import { type ServerRouteResponse, type ServerRoute } from "./serverRoute";
2
+ import { type ServerRouteResponse, type ServerRoute, type ServerRouteResponseFlux } from "./serverRoute";
3
3
  import { type MaybePromise, type IsEqual, type SimplifyTopLevel, type NeverCoalescing } from "@duplojs/utils";
4
4
  import { type PromiseRequestParams } from "./promiseRequestParams";
5
+ import { type ServerRouteToClientRequestParams } from "./clientRequestParams";
5
6
  export type ClientResponseBody = unknown;
6
7
  export interface ClientResponse<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> {
7
8
  code: SS.Number;
@@ -15,6 +16,20 @@ export interface ClientResponse<GenericHookParams extends Record<string, unknown
15
16
  raw: globalThis.Response;
16
17
  requestParams: PromiseRequestParams<GenericHookParams>;
17
18
  predicted: boolean;
19
+ fromCache?: boolean;
20
+ }
21
+ export interface ClientResponseHandler<GenericType extends string> {
22
+ handlerType: GenericType;
23
+ }
24
+ export interface ClientStreamResponseHandler<GenericFlux extends ServerRouteResponseFlux = ServerRouteResponseFlux> extends AsyncIterable<GenericFlux, void>, ClientResponseHandler<"stream"> {
25
+ closeStream(): void;
26
+ onStream(event: "close", callback: (response: this) => MaybePromise<void>): this;
27
+ onStream(event: "error", callback: (error: unknown, response: this) => MaybePromise<void>): this;
28
+ onStream(event: "start", callback: (response: this) => MaybePromise<void>): this;
29
+ onStream(event: "receiveData", callback: (data: GenericFlux, response: this) => MaybePromise<void>): this;
30
+ consumeStream(): Promise<void>;
31
+ }
32
+ export interface ClientStreamResponse<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> extends ClientResponse<GenericHookParams>, ClientStreamResponseHandler {
18
33
  }
19
34
  export interface ServerEvent {
20
35
  data: unknown;
@@ -22,7 +37,7 @@ export interface ServerEvent {
22
37
  id?: string;
23
38
  retry?: number;
24
39
  }
25
- export interface ClientEventsResponseHandler<GenericServerEvent extends ServerEvent = ServerEvent> extends AsyncIterable<GenericServerEvent, void> {
40
+ export interface ClientEventsResponseHandler<GenericServerEvent extends ServerEvent = ServerEvent> extends AsyncIterable<GenericServerEvent, void>, ClientResponseHandler<"events"> {
26
41
  closeEventStream(): void;
27
42
  onReceiveEvent<GenericEventName extends GenericServerEvent["event"]>(name: GenericEventName, callback: (event: NeverCoalescing<Extract<GenericServerEvent, {
28
43
  event: GenericEventName;
@@ -36,13 +51,15 @@ export interface ClientEventsResponseHandler<GenericServerEvent extends ServerEv
36
51
  }
37
52
  export interface ClientEventsResponse<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> extends ClientResponse<GenericHookParams>, ClientEventsResponseHandler {
38
53
  }
39
- export type AllClientResponse<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> = (ClientResponse<GenericHookParams> | ClientEventsResponse<GenericHookParams>);
54
+ export type AllClientResponse<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> = (ClientResponse<GenericHookParams> | ClientStreamResponse<GenericHookParams> | ClientEventsResponse<GenericHookParams>);
40
55
  export interface NotPredictedClientResponse<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> extends ClientResponse<GenericHookParams> {
41
56
  predicted: false;
42
57
  }
58
+ export interface NotPredictedClientStreamResponse<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> extends NotPredictedClientResponse<GenericHookParams>, ClientStreamResponseHandler {
59
+ }
43
60
  export interface NotPredictedClientEventsResponse<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> extends NotPredictedClientResponse<GenericHookParams>, ClientEventsResponseHandler {
44
61
  }
45
- export type AllNotPredictedClientResponse<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> = (NotPredictedClientResponse<GenericHookParams> | NotPredictedClientEventsResponse<GenericHookParams>);
62
+ export type AllNotPredictedClientResponse<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> = (NotPredictedClientResponse<GenericHookParams> | NotPredictedClientStreamResponse<GenericHookParams> | NotPredictedClientEventsResponse<GenericHookParams>);
46
63
  export type ServerRouteToClientResponse<GenericServerRoute extends ServerRoute = ServerRoute, GenericHookParams extends Record<string, unknown> = Record<string, unknown>> = GenericServerRoute extends any ? GenericServerRoute["responses"] extends infer InferredResponse ? InferredResponse extends ServerRouteResponse ? (SimplifyTopLevel<{
47
64
  code: InferredResponse["code"];
48
65
  information: InferredResponse["information"];
@@ -53,8 +70,9 @@ export type ServerRouteToClientResponse<GenericServerRoute extends ServerRoute =
53
70
  url: string;
54
71
  redirected: boolean;
55
72
  raw: globalThis.Response;
56
- requestParams: PromiseRequestParams<GenericHookParams>;
73
+ requestParams: SimplifyTopLevel<ServerRouteToClientRequestParams<GenericServerRoute, GenericHookParams> & PromiseRequestParams<GenericHookParams>>;
57
74
  predicted: boolean;
75
+ fromCache?: boolean;
58
76
  }> & (IsEqual<InferredResponse["events"], unknown> extends true ? unknown : InferredResponse["events"] extends object ? ClientEventsResponseHandler<{
59
77
  [EventName in keyof InferredResponse["events"]]: EventName extends string ? {
60
78
  event: EventName;
@@ -62,4 +80,4 @@ export type ServerRouteToClientResponse<GenericServerRoute extends ServerRoute =
62
80
  id?: string;
63
81
  retry?: number;
64
82
  } : never;
65
- }[keyof InferredResponse["events"]]> : unknown)) extends infer InferredResult extends ClientResponse ? InferredResult : never : never : never : never;
83
+ }[keyof InferredResponse["events"]]> : unknown) & (IsEqual<InferredResponse["flux"], unknown> extends true ? unknown : InferredResponse["flux"] extends ServerRouteResponseFlux ? ClientStreamResponseHandler<InferredResponse["flux"]> : never)) extends infer InferredResult extends ClientResponse ? InferredResult : never : never : never : never;
@@ -1,6 +1,7 @@
1
1
  import { type MaybePromise } from "@duplojs/utils";
2
- import { type ClientEventsResponse, type ServerEvent, type AllClientResponse, type AllNotPredictedClientResponse } from "./clientResponse";
2
+ import { type ClientEventsResponse, type ServerEvent, type AllClientResponse, type AllNotPredictedClientResponse, type ClientStreamResponse } from "./clientResponse";
3
3
  import { type PromiseRequestParams } from "./promiseRequestParams";
4
+ import { type ServerRouteResponseFlux } from "./serverRoute";
4
5
  export type RequestHook<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> = (requestParams: PromiseRequestParams<GenericHookParams>) => MaybePromise<PromiseRequestParams<GenericHookParams>>;
5
6
  export type ResponseHook<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> = (response: AllClientResponse<GenericHookParams>) => MaybePromise<AllClientResponse<GenericHookParams>>;
6
7
  export type InformationHook<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> = (response: AllClientResponse<GenericHookParams>) => MaybePromise<void>;
@@ -14,6 +15,10 @@ export type BeforeRetryServerEventHook<GenericHookParams extends Record<string,
14
15
  export type ErrorServerEventHook<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> = (error: unknown, response: ClientEventsResponse<GenericHookParams>) => MaybePromise<void>;
15
16
  export type StartServerEventHook<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> = (response: ClientEventsResponse<GenericHookParams>) => MaybePromise<void>;
16
17
  export type ReceiveEventServerEventHook<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> = (event: ServerEvent, response: ClientEventsResponse<GenericHookParams>) => MaybePromise<void>;
18
+ export type CloseStreamHook<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> = (response: ClientStreamResponse<GenericHookParams>) => MaybePromise<void>;
19
+ export type ReceiveDataStreamHook<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> = (data: ServerRouteResponseFlux, response: ClientStreamResponse<GenericHookParams>) => MaybePromise<void>;
20
+ export type ErrorStreamHook<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> = (error: unknown, response: ClientStreamResponse<GenericHookParams>) => MaybePromise<void>;
21
+ export type StartStreamHook<GenericHookParams extends Record<string, unknown> = Record<string, unknown>> = (response: ClientStreamResponse<GenericHookParams>) => MaybePromise<void>;
17
22
  export interface Hooks {
18
23
  request: RequestHook[];
19
24
  response: ResponseHook[];
@@ -32,4 +37,8 @@ export interface Hooks {
32
37
  errorServerEvent: ErrorServerEventHook[];
33
38
  startServerEvent: StartServerEventHook[];
34
39
  receiveEventServerEvent: ReceiveEventServerEventHook[];
40
+ closeStream: CloseStreamHook[];
41
+ receiveDataStream: ReceiveDataStreamHook[];
42
+ errorStream: ErrorStreamHook[];
43
+ startStream: StartStreamHook[];
35
44
  }
@@ -6,4 +6,5 @@ require('./serverRoute.cjs');
6
6
  require('./ObjectCanBeEmpty.cjs');
7
7
  require('./promiseRequestParams.cjs');
8
8
  require('./hooks.cjs');
9
+ require('./clientCache.cjs');
9
10
 
@@ -4,3 +4,4 @@ export * from "./serverRoute";
4
4
  export * from "./ObjectCanBeEmpty";
5
5
  export * from "./promiseRequestParams";
6
6
  export * from "./hooks";
7
+ export * from "./clientCache";
@@ -4,3 +4,4 @@ import './serverRoute.mjs';
4
4
  import './ObjectCanBeEmpty.mjs';
5
5
  import './promiseRequestParams.mjs';
6
6
  import './hooks.mjs';
7
+ import './clientCache.mjs';