@wp-typia/rest 0.3.5 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -37,6 +37,11 @@ const endpoint = createEndpoint<MyRequest, MyResponse>({
37
37
  const result = await callEndpoint(endpoint, { title: "Hello" });
38
38
  ```
39
39
 
40
+ `callEndpoint(...)` returns `EndpointValidationResult<Req, Res>`. If request
41
+ validation fails before transport execution, the result keeps
42
+ `validationTarget: "request"`. Response validation runs after transport and uses
43
+ `validationTarget: "response"`.
44
+
40
45
  If you need a canonical REST URL for a route path, use:
41
46
 
42
47
  ```ts
package/dist/client.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import type { APIFetchOptions, ApiFetch } from "@wordpress/api-fetch";
2
+ import type { EndpointValidationResult } from "@wp-typia/api-client";
2
3
  import { type ValidationLike, type ValidationResult } from "./internal/runtime-primitives.js";
3
4
  export type { ValidationError, ValidationLike, ValidationResult } from "./internal/runtime-primitives.js";
4
5
  export { isValidationResult, normalizeValidationError, toValidationResult } from "./internal/runtime-primitives.js";
6
+ export type { EndpointRequestValidationResult, EndpointResponseValidationResult, EndpointValidationResult, EndpointValidationTarget, } from "@wp-typia/api-client";
5
7
  export interface ValidatedFetch<T> {
6
8
  assertFetch(options: APIFetchOptions): Promise<T>;
7
9
  fetch(options: APIFetchOptions): Promise<ValidationResult<T>>;
@@ -25,4 +27,4 @@ export interface EndpointCallOptions {
25
27
  export declare function resolveRestRouteUrl(routePath: string, root?: string): string;
26
28
  export declare function createValidatedFetch<T>(validator: (input: unknown) => ValidationLike<T>, fetchFn?: ApiFetch): ValidatedFetch<T>;
27
29
  export declare function createEndpoint<Req, Res>(config: ApiEndpoint<Req, Res>): ApiEndpoint<Req, Res>;
28
- export declare function callEndpoint<Req, Res>(endpoint: ApiEndpoint<Req, Res>, request: Req, { fetchFn, requestOptions }?: EndpointCallOptions): Promise<ValidationResult<Res>>;
30
+ export declare function callEndpoint<Req, Res>(endpoint: ApiEndpoint<Req, Res>, request: Req, { fetchFn, requestOptions }?: EndpointCallOptions): Promise<EndpointValidationResult<Req, Res>>;
package/dist/client.js CHANGED
@@ -169,11 +169,26 @@ export function createValidatedFetch(validator, fetchFn = defaultFetch) {
169
169
  export function createEndpoint(config) {
170
170
  return config;
171
171
  }
172
+ function isInvalidValidationResult(validation) {
173
+ return validation.isValid === false;
174
+ }
175
+ function toEndpointRequestValidationResult(validation) {
176
+ return {
177
+ ...validation,
178
+ validationTarget: "request",
179
+ };
180
+ }
181
+ function toEndpointResponseValidationResult(validation) {
182
+ return {
183
+ ...validation,
184
+ validationTarget: "response",
185
+ };
186
+ }
172
187
  export async function callEndpoint(endpoint, request, { fetchFn = defaultFetch, requestOptions } = {}) {
173
188
  const requestValidation = endpoint.validateRequest(request);
174
- if (!requestValidation.isValid) {
175
- return requestValidation;
189
+ if (isInvalidValidationResult(requestValidation)) {
190
+ return toEndpointRequestValidationResult(requestValidation);
176
191
  }
177
192
  const payload = await fetchFn(mergeFetchOptions(buildEndpointFetchOptions(endpoint, request), requestOptions));
178
- return endpoint.validateResponse(payload);
193
+ return toEndpointResponseValidationResult(endpoint.validateResponse(payload));
179
194
  }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { createEndpoint, createValidatedFetch, callEndpoint, isValidationResult, normalizeValidationError, resolveRestRouteUrl, toValidationResult, type ApiEndpoint, type EndpointCallOptions, type ValidatedFetch, type ValidationResult, type ValidationError, type ValidationLike, } from "./client.js";
1
+ export { createEndpoint, createValidatedFetch, callEndpoint, isValidationResult, normalizeValidationError, resolveRestRouteUrl, toValidationResult, type ApiEndpoint, type EndpointCallOptions, type EndpointRequestValidationResult, type EndpointResponseValidationResult, type EndpointValidationResult, type EndpointValidationTarget, type ValidatedFetch, type ValidationResult, type ValidationError, type ValidationLike, } from "./client.js";
2
2
  export { createHeadersDecoder, createParameterDecoder, createQueryDecoder, } from "./http.js";
package/dist/react.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createElement } from "@wordpress/element";
2
2
  import type { ApiFetch } from "@wordpress/api-fetch";
3
- import { type ApiEndpoint, type EndpointCallOptions, type ValidationResult } from "./client.js";
3
+ import { type ApiEndpoint, type EndpointCallOptions, type EndpointResponseValidationResult, type EndpointValidationResult } from "./client.js";
4
4
  type EndpointDataUpdater<T> = T | ((current: T | undefined) => T | undefined);
5
5
  export interface EndpointDataClient {
6
6
  invalidate<Req, Res>(endpoint: ApiEndpoint<Req, Res>, request?: Req): void;
@@ -19,31 +19,31 @@ export interface UseEndpointQueryOptions<_Req, Res, Selected = Res> {
19
19
  fetchFn?: ApiFetch;
20
20
  initialData?: Res;
21
21
  onError?: (error: unknown) => void | Promise<void>;
22
- onSuccess?: (data: Selected, validation: ValidationResult<Res>) => void | Promise<void>;
22
+ onSuccess?: (data: Selected, validation: EndpointResponseValidationResult<Res>) => void | Promise<void>;
23
23
  resolveCallOptions?: () => EndpointCallOptions | undefined;
24
24
  select?: (data: Res) => Selected;
25
25
  staleTime?: number;
26
26
  }
27
- export interface UseEndpointQueryResult<Res, Selected = Res> {
27
+ export interface UseEndpointQueryResult<Res, Selected = Res, Req = unknown> {
28
28
  data: Selected | undefined;
29
29
  error: unknown;
30
30
  isFetching: boolean;
31
31
  isLoading: boolean;
32
- refetch: () => Promise<ValidationResult<Res>>;
33
- validation: ValidationResult<Res> | null;
32
+ refetch: () => Promise<EndpointValidationResult<Req, Res>>;
33
+ validation: EndpointValidationResult<Req, Res> | null;
34
34
  }
35
35
  export interface UseEndpointMutationOptions<Req, Res, Context = unknown> {
36
36
  client?: EndpointDataClient;
37
37
  fetchFn?: ApiFetch;
38
- invalidate?: EndpointInvalidateTargets | ((data: Res | undefined, variables: Req, validation: ValidationResult<Res>) => EndpointInvalidateTargets);
38
+ invalidate?: EndpointInvalidateTargets | ((data: Res | undefined, variables: Req, validation: EndpointValidationResult<Req, Res>) => EndpointInvalidateTargets);
39
39
  onError?: (error: unknown, variables: Req, client: EndpointDataClient, context: Context | undefined) => void | Promise<void>;
40
40
  onMutate?: (variables: Req, client: EndpointDataClient) => Context | Promise<Context>;
41
41
  onSettled?: (result: {
42
42
  data: Res | undefined;
43
43
  error: unknown;
44
- validation: ValidationResult<Res> | null;
44
+ validation: EndpointValidationResult<Req, Res> | null;
45
45
  }, variables: Req, client: EndpointDataClient, context: Context | undefined) => void | Promise<void>;
46
- onSuccess?: (data: Res | undefined, variables: Req, validation: ValidationResult<Res>, client: EndpointDataClient, context: Context | undefined) => void | Promise<void>;
46
+ onSuccess?: (data: Res | undefined, variables: Req, validation: EndpointResponseValidationResult<Res>, client: EndpointDataClient, context: Context | undefined) => void | Promise<void>;
47
47
  resolveCallOptions?: (variables: Req) => EndpointCallOptions | undefined;
48
48
  }
49
49
  export interface UseEndpointMutationResult<Req, Res> {
@@ -51,9 +51,9 @@ export interface UseEndpointMutationResult<Req, Res> {
51
51
  error: unknown;
52
52
  isPending: boolean;
53
53
  mutate: (variables: Req) => void;
54
- mutateAsync: (variables: Req) => Promise<ValidationResult<Res>>;
54
+ mutateAsync: (variables: Req) => Promise<EndpointValidationResult<Req, Res>>;
55
55
  reset: () => void;
56
- validation: ValidationResult<Res> | null;
56
+ validation: EndpointValidationResult<Req, Res> | null;
57
57
  }
58
58
  export interface EndpointDataProviderProps {
59
59
  children?: unknown;
@@ -62,6 +62,6 @@ export interface EndpointDataProviderProps {
62
62
  export declare function createEndpointDataClient(): EndpointDataClient;
63
63
  export declare function EndpointDataProvider({ children, client, }: EndpointDataProviderProps): ReturnType<typeof createElement>;
64
64
  export declare function useEndpointDataClient(): EndpointDataClient;
65
- export declare function useEndpointQuery<Req, Res, Selected = Res>(endpoint: ApiEndpoint<Req, Res>, request: Req, options?: UseEndpointQueryOptions<Req, Res, Selected>): UseEndpointQueryResult<Res, Selected>;
65
+ export declare function useEndpointQuery<Req, Res, Selected = Res>(endpoint: ApiEndpoint<Req, Res>, request: Req, options?: UseEndpointQueryOptions<Req, Res, Selected>): UseEndpointQueryResult<Res, Selected, Req>;
66
66
  export declare function useEndpointMutation<Req, Res, Context = unknown>(endpoint: ApiEndpoint<Req, Res>, options?: UseEndpointMutationOptions<Req, Res, Context>): UseEndpointMutationResult<Req, Res>;
67
67
  export {};
package/dist/react.js CHANGED
@@ -10,6 +10,18 @@ const EMPTY_SNAPSHOT = {
10
10
  validation: null,
11
11
  };
12
12
  const EndpointDataClientContext = createContext(null);
13
+ const endpointIdentityIds = new WeakMap();
14
+ let nextEndpointIdentityId = 0;
15
+ function getStableEndpointIdentity(value) {
16
+ const existing = endpointIdentityIds.get(value);
17
+ if (existing !== undefined) {
18
+ return existing;
19
+ }
20
+ const created = nextEndpointIdentityId;
21
+ nextEndpointIdentityId += 1;
22
+ endpointIdentityIds.set(value, created);
23
+ return created;
24
+ }
13
25
  function normalizeCacheValue(value) {
14
26
  if (value === undefined) {
15
27
  return undefined;
@@ -45,7 +57,18 @@ function normalizeCacheValue(value) {
45
57
  return String(value);
46
58
  }
47
59
  function createEndpointPrefix(endpoint) {
48
- return `${endpoint.method} ${endpoint.path}`;
60
+ const requestValidatorId = getStableEndpointIdentity(endpoint.validateRequest);
61
+ const responseValidatorId = getStableEndpointIdentity(endpoint.validateResponse);
62
+ const requestBuilderId = endpoint.buildRequestOptions !== undefined
63
+ ? getStableEndpointIdentity(endpoint.buildRequestOptions)
64
+ : -1;
65
+ return [
66
+ endpoint.method,
67
+ endpoint.path,
68
+ `request:${requestValidatorId}`,
69
+ `response:${responseValidatorId}`,
70
+ `builder:${requestBuilderId}`,
71
+ ].join(" ");
49
72
  }
50
73
  function createCacheKey(endpoint, request) {
51
74
  const requestValidation = endpoint.validateRequest(request);
@@ -103,6 +126,21 @@ function isEntryStale(entry, staleTime) {
103
126
  function asInternalClient(client) {
104
127
  return client;
105
128
  }
129
+ function isInvalidValidationResult(validation) {
130
+ return validation.isValid === false;
131
+ }
132
+ function toEndpointRequestValidationResult(validation) {
133
+ return {
134
+ ...validation,
135
+ validationTarget: "request",
136
+ };
137
+ }
138
+ function toEndpointResponseValidationResult(validation) {
139
+ return {
140
+ ...validation,
141
+ validationTarget: "response",
142
+ };
143
+ }
106
144
  export function createEndpointDataClient() {
107
145
  const entries = new Map();
108
146
  function notify(cacheKey) {
@@ -171,11 +209,11 @@ export function createEndpointDataClient() {
171
209
  entry.data = resolvedNext;
172
210
  entry.error = null;
173
211
  entry.updatedAt = Date.now();
174
- entry.validation = {
212
+ entry.validation = toEndpointResponseValidationResult({
175
213
  data: resolvedNext,
176
214
  errors: [],
177
215
  isValid: true,
178
- };
216
+ });
179
217
  syncSnapshot(entry);
180
218
  notify(cacheKey);
181
219
  },
@@ -194,6 +232,9 @@ export function createEndpointDataClient() {
194
232
  if (validation.isValid) {
195
233
  entry.data = validation.data;
196
234
  }
235
+ else if (validation.validationTarget === "request") {
236
+ entry.data = undefined;
237
+ }
197
238
  syncSnapshot(entry);
198
239
  notify(cacheKey);
199
240
  },
@@ -257,11 +298,11 @@ export function createEndpointDataClient() {
257
298
  entry.data = data;
258
299
  entry.error = null;
259
300
  entry.updatedAt = Date.now();
260
- entry.validation = {
301
+ entry.validation = toEndpointResponseValidationResult({
261
302
  data,
262
303
  errors: [],
263
304
  isValid: true,
264
- };
305
+ });
265
306
  syncSnapshot(entry);
266
307
  notify(cacheKey);
267
308
  },
@@ -328,8 +369,8 @@ export function useEndpointQuery(endpoint, request, options = {}) {
328
369
  if (!executeQueryRef.current) {
329
370
  executeQueryRef.current = async (force) => {
330
371
  const latest = latestRef.current;
331
- if (!latest.requestValidation.isValid) {
332
- const invalidValidation = latest.requestValidation;
372
+ if (isInvalidValidationResult(latest.requestValidation)) {
373
+ const invalidValidation = toEndpointRequestValidationResult(latest.requestValidation);
333
374
  latest.client.__publishValidation(latest.cacheKey, invalidValidation);
334
375
  return invalidValidation;
335
376
  }
@@ -371,11 +412,11 @@ export function useEndpointQuery(endpoint, request, options = {}) {
371
412
  if (!enabled) {
372
413
  return;
373
414
  }
374
- if (!prepared.requestValidation.isValid) {
415
+ if (isInvalidValidationResult(prepared.requestValidation)) {
375
416
  if (snapshot.validation?.isValid === false) {
376
417
  return;
377
418
  }
378
- client.__publishValidation(prepared.cacheKey, prepared.requestValidation);
419
+ client.__publishValidation(prepared.cacheKey, toEndpointRequestValidationResult(prepared.requestValidation));
379
420
  return;
380
421
  }
381
422
  if (snapshot.isFetching) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wp-typia/rest",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "Typed WordPress REST helpers powered by Typia validation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -62,7 +62,7 @@
62
62
  "bun": ">=1.3.11"
63
63
  },
64
64
  "dependencies": {
65
- "@wp-typia/api-client": "^0.4.2",
65
+ "@wp-typia/api-client": "^0.4.3",
66
66
  "@typia/interface": "^12.0.1",
67
67
  "@wordpress/api-fetch": "^7.42.0"
68
68
  },