@resistdesign/voltra 3.0.0-alpha.51 → 3.0.0-alpha.52

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/app/index.js CHANGED
@@ -98,6 +98,7 @@ var ApplicationStateProvider = ({
98
98
  };
99
99
 
100
100
  // src/app/utils/Service.ts
101
+ var activeRequestControllers = /* @__PURE__ */ new Map();
101
102
  var getFullUrl = (protocol, domain, basePath = "", path = "", port) => {
102
103
  const normalizedProtocol = protocol.endsWith(":") ? protocol.slice(0, -1) : protocol;
103
104
  const normalizedDomain = domain.replace(/\/+$/, "");
@@ -113,39 +114,56 @@ var getFullUrl = (protocol, domain, basePath = "", path = "", port) => {
113
114
  const normalizedPath = fullPath ? fullPath.startsWith(PATH_DELIMITER) ? fullPath : `${PATH_DELIMITER}${fullPath}` : PATH_DELIMITER;
114
115
  return `${normalizedProtocol}://${normalizedDomain}${portString}${normalizedPath}`;
115
116
  };
116
- var sendServiceRequest = async (config, path = "", args = []) => {
117
+ var sendServiceRequest = async (config, path = "", args = [], requestConfig = {}) => {
117
118
  const { protocol, domain, port, basePath = "", authorization = "" } = config;
119
+ const { cancelPendingOnNewRequest = false } = requestConfig;
118
120
  const fullUrl = getFullUrl(protocol, domain, basePath, path, port);
121
+ const abortController = new AbortController();
122
+ const previousRequestController = activeRequestControllers.get(fullUrl);
123
+ if (cancelPendingOnNewRequest) {
124
+ previousRequestController?.abort();
125
+ activeRequestControllers.set(fullUrl, abortController);
126
+ }
119
127
  const requestHeaders = {
120
128
  "Content-Type": "application/json",
121
129
  ...!!authorization ? {
122
130
  Authorization: `Bearer ${authorization}`
123
131
  } : {}
124
132
  };
125
- const response = await fetch(fullUrl, {
126
- headers: requestHeaders,
127
- credentials: "same-origin",
128
- method: "POST",
129
- body: JSON.stringify(args)
130
- });
131
- const { ok: responseIsOk } = response;
132
- const textData = await response.text();
133
- let data = textData;
134
133
  try {
135
- data = JSON.parse(textData);
136
- } catch (error) {
137
- }
138
- if (typeof data === "object") {
139
- if (responseIsOk) {
140
- return data;
141
- } else {
142
- throw data;
134
+ const response = await fetch(fullUrl, {
135
+ headers: requestHeaders,
136
+ credentials: "same-origin",
137
+ method: "POST",
138
+ body: JSON.stringify(args),
139
+ signal: abortController.signal
140
+ });
141
+ const { ok: responseIsOk } = response;
142
+ const textData = await response.text();
143
+ let data = textData;
144
+ try {
145
+ data = JSON.parse(textData);
146
+ } catch (error) {
143
147
  }
144
- } else {
145
- if (responseIsOk) {
146
- return { data };
148
+ if (typeof data === "object") {
149
+ if (responseIsOk) {
150
+ return data;
151
+ } else {
152
+ throw data;
153
+ }
147
154
  } else {
148
- throw { message: data };
155
+ if (responseIsOk) {
156
+ return { data };
157
+ } else {
158
+ throw { message: data };
159
+ }
160
+ }
161
+ } finally {
162
+ if (cancelPendingOnNewRequest) {
163
+ const activeRequestController = activeRequestControllers.get(fullUrl);
164
+ if (activeRequestController === abortController) {
165
+ activeRequestControllers.delete(fullUrl);
166
+ }
149
167
  }
150
168
  }
151
169
  };
@@ -155,10 +173,12 @@ var useApplicationStateLoader = (config) => {
155
173
  remoteProcedureCall,
156
174
  resetOnError = false,
157
175
  onLoadComplete,
158
- manual = false
176
+ manual = false,
177
+ cancelPendingOnNewRequest = false
159
178
  } = config;
160
179
  const { args = [] } = remoteProcedureCall;
161
180
  const argsRef = useRef(args);
181
+ const requestSequenceRef = useRef(0);
162
182
  argsRef.current = args;
163
183
  const [cacheValidity, setCacheValidity] = useState({});
164
184
  const [loading, setLoading] = useState(false);
@@ -170,6 +190,7 @@ var useApplicationStateLoader = (config) => {
170
190
  }, []);
171
191
  const makeRemoteProcedureCall = useCallback(
172
192
  async (...directArgs) => {
193
+ const requestSequence = ++requestSequenceRef.current;
173
194
  let success = false;
174
195
  setLoading(true);
175
196
  setLatestError(void 0);
@@ -178,23 +199,43 @@ var useApplicationStateLoader = (config) => {
178
199
  const result = await sendServiceRequest(
179
200
  serviceConfig,
180
201
  path,
181
- directArgs
202
+ directArgs,
203
+ {
204
+ cancelPendingOnNewRequest
205
+ }
182
206
  );
207
+ if (requestSequence !== requestSequenceRef.current) {
208
+ return;
209
+ }
183
210
  success = true;
184
211
  onChange(result);
185
212
  setModified(false);
186
213
  } catch (error) {
214
+ if (requestSequence !== requestSequenceRef.current) {
215
+ return;
216
+ }
187
217
  success = false;
188
218
  setLatestError(error);
189
219
  if (resetOnError) {
190
220
  onChange(void 0);
191
221
  setModified(false);
192
222
  }
223
+ } finally {
224
+ if (requestSequence !== requestSequenceRef.current) {
225
+ return;
226
+ }
227
+ setLoading(false);
228
+ onLoadComplete?.(success);
193
229
  }
194
- setLoading(false);
195
- onLoadComplete?.(success);
196
230
  },
197
- [remoteProcedureCall, onChange, setModified, resetOnError, onLoadComplete]
231
+ [
232
+ remoteProcedureCall,
233
+ onChange,
234
+ setModified,
235
+ resetOnError,
236
+ onLoadComplete,
237
+ cancelPendingOnNewRequest
238
+ ]
198
239
  );
199
240
  const appStateLoader = useMemo(
200
241
  () => ({
@@ -5,7 +5,7 @@
5
5
  * tracks loading/error state, and populates ApplicationState via identifiers.
6
6
  */
7
7
  import { ApplicationStateIdentifier, type ApplicationStateValue, type ApplicationStateValueController } from "./ApplicationState";
8
- import { ServiceConfig } from "./Service";
8
+ import { type ServiceConfig, type ServiceRequestConfig } from "./Service";
9
9
  /**
10
10
  * Access and track the loading of an application state value.
11
11
  * */
@@ -49,7 +49,7 @@ export type RemoteProcedureCall<ArgsType extends any[] = any[]> = {
49
49
  /**
50
50
  * The configuration for an application state loader.
51
51
  * */
52
- export type ApplicationStateLoaderConfig<ValueType = ApplicationStateValue, ArgsType extends any[] = any[]> = {
52
+ export type ApplicationStateLoaderConfig<ValueType = ApplicationStateValue, ArgsType extends any[] = any[]> = ServiceRequestConfig & {
53
53
  /**
54
54
  * Identifier for the value to update in application state.
55
55
  * */
@@ -23,6 +23,17 @@ export type ServiceConfig = {
23
23
  * */
24
24
  authorization?: string;
25
25
  };
26
+ /**
27
+ * Additional request behavior for a service call.
28
+ * */
29
+ export type ServiceRequestConfig = {
30
+ /**
31
+ * Abort the prior in-flight request for the same service URL before starting a new one.
32
+ *
33
+ * @default false
34
+ * */
35
+ cancelPendingOnNewRequest?: boolean;
36
+ };
26
37
  /**
27
38
  * Build the full URL for a service call from config pieces.
28
39
  *
@@ -40,6 +51,7 @@ export declare const getFullUrl: (protocol: string, domain: string, basePath?: s
40
51
  * @param config - Service configuration for the request.
41
52
  * @param path - Endpoint path to call.
42
53
  * @param args - JSON-serializable arguments to send.
54
+ * @param requestConfig - Additional request behavior configuration.
43
55
  * @returns Parsed JSON response.
44
56
  */
45
- export declare const sendServiceRequest: (config: ServiceConfig, path?: string, args?: any[]) => Promise<any>;
57
+ export declare const sendServiceRequest: (config: ServiceConfig, path?: string, args?: any[], requestConfig?: ServiceRequestConfig) => Promise<any>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@resistdesign/voltra",
3
- "version": "3.0.0-alpha.51",
3
+ "version": "3.0.0-alpha.52",
4
4
  "description": "With our powers combined!",
5
5
  "homepage": "https://voltra.app",
6
6
  "repository": "git@github.com:resistdesign/voltra.git",