@eecho/api-client 0.0.3 → 0.2.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.
package/README.md CHANGED
@@ -1,22 +1,42 @@
1
1
  # @eecho/api-client
2
2
 
3
- Type-safe API client library for the EEcho framework.
3
+ Validated HTTP execution for API contracts created by `@eecho/definition`.
4
4
 
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm install @eecho/api-client @eecho/definition
8
+ npm install @eecho/api-client @eecho/definition zod
9
9
  ```
10
10
 
11
- ## Features
11
+ ## Usage
12
12
 
13
- - Generate type-safe API clients
14
- - Automatic API specification generation
15
- - Built-in request/response handling
16
- - TypeScript support with full type inference
17
- - Customizable HTTP client configuration
13
+ ```ts
14
+ import { createAPI, createHttpClient, createRetryHandler } from '@eecho/api-client';
15
+ import { PetAPI } from './pet-api.js';
18
16
 
19
- ## Usage
17
+ const httpClient = createHttpClient();
18
+ httpClient.setHost('https://api.example.com');
19
+ httpClient.addExceptionHandler(createRetryHandler({
20
+ retryCount: 2,
21
+ timeout: 250,
22
+ }));
23
+
24
+ const petAPI = createAPI({
25
+ apiSpec: PetAPI.ReadAPISpecification,
26
+ httpClient,
27
+ });
20
28
 
21
- ```typescript
29
+ const response = await petAPI.getItems({
30
+ queryParams: {
31
+ filter: { species: 'Dog' },
32
+ sort: { name: 'asc' },
33
+ },
34
+ });
22
35
  ```
36
+
37
+ Requests are validated before they are sent. JSON responses are validated
38
+ before they are returned to application code. HTTP failures can be handled by
39
+ ordered exception handlers; retry behavior is opt-in.
40
+
41
+ `genAPIDefinition` is re-exported for compatibility. New code should import the
42
+ contract generator from `@eecho/definition`, which owns the shared contract.
package/dist/client.d.ts CHANGED
@@ -1,38 +1,39 @@
1
- type SimpleInit = Omit<RequestInit, "headers"> & {
2
- headers?: {
3
- [key: string]: string;
4
- };
1
+ type RequestOptions = Omit<RequestInit, 'headers'> & {
2
+ headers?: Record<string, string>;
5
3
  };
6
4
  export interface FetchExceptionContext {
7
- errorType: "FetchException";
5
+ errorType: 'FetchException';
8
6
  requestUrl: string;
9
7
  response?: Response;
10
8
  options: RequestInit;
11
9
  timestamp: Date;
10
+ cause?: unknown;
12
11
  }
13
- export type APIExceptionHandler = (ctx: FetchExceptionContext) => Promise<{
12
+ export type APIExceptionHandler = (context: FetchExceptionContext) => Promise<{
14
13
  result?: Response;
15
14
  isNext: boolean;
16
15
  }>;
17
- export type RequestHandler = (p: {
16
+ export type RequestHandler = (request: {
18
17
  url: string;
19
- options: SimpleInit;
18
+ options: RequestOptions;
20
19
  }) => Promise<{
21
20
  url: string;
22
- options: SimpleInit;
21
+ options: RequestOptions;
23
22
  }>;
24
23
  export declare function createHttpClient(): {
25
24
  setHost(url: string): void;
26
- setHeader(header: Record<string, string>): void;
25
+ setHeader(nextHeaders: Record<string, string>): void;
27
26
  addRequestHandler(handler: RequestHandler): void;
28
27
  addExceptionHandler(handler: APIExceptionHandler): void;
29
28
  readonly host: string;
30
- readonly headers: Record<string, string>;
29
+ readonly headers: {
30
+ [x: string]: string;
31
+ };
31
32
  readonly apiExceptionHandlers: APIExceptionHandler[];
32
- fetch(p: {
33
+ fetch(input: {
33
34
  url: string;
34
- options?: SimpleInit;
35
- }): Promise<Response | undefined>;
35
+ options?: RequestOptions;
36
+ }): Promise<Response>;
36
37
  };
37
38
  export {};
38
39
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,KAAK,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,GAAG;IAC/C,OAAO,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CACrC,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,gBAAgB,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,MAAM,mBAAmB,GAC7B,CAAC,GAAG,EAAE,qBAAqB,KAAK,OAAO,CAAC;IAAE,MAAM,CAAC,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAEhF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,EAAE;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,UAAU,CAAA;CAAC,KAAK,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AAkBxH,wBAAgB,gBAAgB;iBAgBf,MAAM;sBACD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;+BACb,cAAc;iCAGZ,mBAAmB;;;;aAQjC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,UAAU,CAAA;KAAE;EAuDvD"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,KAAK,cAAc,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,GAAG;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,gBAAgB,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,MAAM,mBAAmB,GAAG,CAChC,OAAO,EAAE,qBAAqB,KAC3B,OAAO,CAAC;IAAE,MAAM,CAAC,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAErD,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,cAAc,CAAC;CACzB,KAAK,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,CAAC,CAAC;AA4DxD,wBAAgB,gBAAgB;iBAOf,MAAM;2BAGI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;+BAGlB,cAAc;iCAGZ,mBAAmB;;;;;;iBAc7B;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,cAAc,CAAA;KAAE;EAoC/D"}
package/dist/client.js CHANGED
@@ -1,87 +1,93 @@
1
1
  class FetchException extends Error {
2
- constructor(ctx) {
3
- super(`Fetch error: ${ctx.errorType} at ${ctx.timestamp.toISOString()}`);
4
- this.ctx = ctx;
5
- this.errorType = "FetchException";
6
- this.requestUrl = ctx.requestUrl;
7
- this.response = ctx.response;
8
- this.options = ctx.options;
9
- this.timestamp = ctx.timestamp;
2
+ constructor(failure) {
3
+ const status = failure.response ? ` (${failure.response.status})` : '';
4
+ super(`Fetch failed for ${failure.requestUrl}${status}`);
5
+ this.name = 'FetchException';
6
+ this.errorType = 'FetchException';
7
+ this.timestamp = new Date();
8
+ this.requestUrl = failure.requestUrl;
9
+ this.options = failure.options;
10
+ this.response = failure.response;
11
+ this.cause = failure.cause;
10
12
  }
11
13
  }
12
- export function createHttpClient() {
13
- let __host = "";
14
- let __headers = {};
15
- let __requestHandlers = [];
16
- let __apiExceptionHandlers = [];
17
- async function runHandlers(ctx) {
18
- if (__apiExceptionHandlers.length === 0)
19
- throw ctx;
20
- for (const handler of __apiExceptionHandlers) {
21
- const { result, isNext } = await handler(ctx);
22
- if (!isNext)
14
+ function resolveRequestUrl(host, url) {
15
+ if (/^https?:\/\//i.test(url)) {
16
+ return url;
17
+ }
18
+ if (!host) {
19
+ throw new Error(`Cannot resolve relative request URL without a host: ${url}`);
20
+ }
21
+ const normalizedHost = host.replace(/\/$/, '');
22
+ const normalizedPath = url.startsWith('/') ? url : `/${url}`;
23
+ return `${normalizedHost}${normalizedPath}`;
24
+ }
25
+ async function runExceptionHandlers(exception, handlers) {
26
+ for (const handler of handlers) {
27
+ const { result, isNext } = await handler(exception);
28
+ if (!isNext) {
29
+ if (result) {
23
30
  return result;
31
+ }
32
+ break;
24
33
  }
25
- throw ctx;
26
34
  }
35
+ throw exception;
36
+ }
37
+ export function createHttpClient() {
38
+ let host = '';
39
+ let headers = {};
40
+ const requestHandlers = [];
41
+ const exceptionHandlers = [];
27
42
  return {
28
- setHost(url) { __host = url; },
29
- setHeader(header) { __headers = header; },
43
+ setHost(url) {
44
+ host = url;
45
+ },
46
+ setHeader(nextHeaders) {
47
+ headers = { ...nextHeaders };
48
+ },
30
49
  addRequestHandler(handler) {
31
- __requestHandlers.push(handler);
50
+ requestHandlers.push(handler);
32
51
  },
33
52
  addExceptionHandler(handler) {
34
- __apiExceptionHandlers.push(handler);
53
+ exceptionHandlers.push(handler);
54
+ },
55
+ get host() {
56
+ return host;
35
57
  },
36
- get host() { return __host; },
37
- get headers() { return __headers; },
38
- get apiExceptionHandlers() { return __apiExceptionHandlers; },
39
- async fetch(p) {
40
- let { url, options = {} } = p;
41
- const combinedHeaders = {
42
- ...__headers,
43
- ...(options.headers ?? {}),
58
+ get headers() {
59
+ return { ...headers };
60
+ },
61
+ get apiExceptionHandlers() {
62
+ return [...exceptionHandlers];
63
+ },
64
+ async fetch(input) {
65
+ let request = {
66
+ url: input.url,
67
+ options: {
68
+ ...input.options,
69
+ headers: {
70
+ ...headers,
71
+ ...input.options?.headers,
72
+ },
73
+ },
44
74
  };
45
- options = { ...options, headers: combinedHeaders };
46
- for (const handler of __requestHandlers) {
47
- const next = await handler({ url, options });
48
- url = next.url;
49
- options = next.options;
75
+ for (const handler of requestHandlers) {
76
+ request = await handler(request);
50
77
  }
51
- const fullUrl = (() => {
52
- if (url.startsWith('http://') || url.startsWith('https://')) {
53
- return url;
54
- }
55
- if (url.startsWith('/')) {
56
- const hostUrl = new URL(__host);
57
- return `${hostUrl.origin}${hostUrl.pathname.replace(/\/$/, '')}${url}`;
58
- }
59
- const normalizedHost = __host.endsWith('/') ? __host.slice(0, -1) : __host;
60
- const normalizedUrl = url.startsWith('/') ? url : `/${url}`;
61
- return `${normalizedHost}${normalizedUrl}`;
62
- })();
78
+ const requestUrl = resolveRequestUrl(host, request.url);
79
+ let response;
80
+ let cause;
63
81
  try {
64
- const res = await fetch(fullUrl, options);
65
- if (res.ok)
66
- return res;
67
- const ctx = new FetchException({
68
- errorType: "FetchException",
69
- requestUrl: fullUrl,
70
- response: res,
71
- options,
72
- timestamp: new Date(),
73
- });
74
- return await runHandlers(ctx);
82
+ response = await fetch(requestUrl, request.options);
83
+ }
84
+ catch (error) {
85
+ cause = error;
75
86
  }
76
- catch (err) {
77
- const netCtx = new FetchException({
78
- errorType: "FetchException",
79
- requestUrl: fullUrl,
80
- options,
81
- timestamp: new Date(),
82
- });
83
- return await runHandlers(netCtx);
87
+ if (response?.ok) {
88
+ return response;
84
89
  }
90
+ return runExceptionHandlers(new FetchException({ requestUrl, options: request.options, response, cause }), exceptionHandlers);
85
91
  },
86
92
  };
87
93
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { APIExceptionHandler } from "./client";
2
- export declare function createRetryHandler(p: {
1
+ import type { APIExceptionHandler } from './client.js';
2
+ export declare function createRetryHandler(options: {
3
3
  timeout?: number;
4
4
  retryCount: number;
5
5
  }): APIExceptionHandler;
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAyB,MAAM,UAAU,CAAC;AAEtE,wBAAgB,kBAAkB,CAAC,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,mBAAmB,CAuCnG"}
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAyB,MAAM,aAAa,CAAC;AAY9E,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,mBAAmB,CAwCtB"}
package/dist/handlers.js CHANGED
@@ -1,32 +1,41 @@
1
- export function createRetryHandler(p) {
2
- return async (ctx) => {
3
- const status = ctx.response?.status;
4
- const shouldRetry = ctx.response == null ||
5
- status === 429 ||
6
- (status != null && status >= 500 && status <= 599);
7
- if (!shouldRetry) {
8
- return { isNext: true };
9
- }
10
- const retryUrl = ctx.requestUrl;
11
- if (!retryUrl) {
1
+ const wait = (delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs));
2
+ function isRetryable(response) {
3
+ if (!response) {
4
+ return true;
5
+ }
6
+ return response.status === 429 || response.status >= 500;
7
+ }
8
+ export function createRetryHandler(options) {
9
+ const { retryCount, timeout: delayMs = 1000 } = options;
10
+ if (!Number.isInteger(retryCount) || retryCount < 0) {
11
+ throw new RangeError('retryCount must be a non-negative integer.');
12
+ }
13
+ if (delayMs < 0) {
14
+ throw new RangeError('timeout must be non-negative.');
15
+ }
16
+ return async (context) => {
17
+ if (!isRetryable(context.response) || context.options.signal?.aborted) {
12
18
  return { isNext: true };
13
19
  }
14
- const delayMs = p.timeout ?? 1000;
15
- for (let attempt = 1; attempt <= p.retryCount; attempt++) {
16
- await new Promise((r) => setTimeout(r, delayMs));
17
- const signal = ctx.options.signal?.aborted
18
- ? undefined
19
- : ctx.options.signal;
20
+ let latestResponse;
21
+ for (let attempt = 0; attempt < retryCount; attempt += 1) {
22
+ await wait(delayMs);
23
+ if (context.options.signal?.aborted) {
24
+ return { isNext: true };
25
+ }
20
26
  try {
21
- const retryRes = await fetch(retryUrl, { ...ctx.options, signal });
22
- if (retryRes.ok) {
23
- return { result: retryRes, isNext: false };
27
+ latestResponse = await fetch(context.requestUrl, context.options);
28
+ if (latestResponse.ok || !isRetryable(latestResponse)) {
29
+ return { result: latestResponse, isNext: false };
24
30
  }
25
31
  }
26
- catch (err) {
32
+ catch {
33
+ latestResponse = undefined;
27
34
  }
28
35
  }
29
- return { isNext: false };
36
+ return latestResponse
37
+ ? { result: latestResponse, isNext: false }
38
+ : { isNext: true };
30
39
  };
31
40
  }
32
41
  //# sourceMappingURL=handlers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.js","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,kBAAkB,CAAC,CAA2C;IAC5E,OAAO,KAAK,EAAE,GAA0B,EAAE,EAAE;QAC1C,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC;QACpC,MAAM,WAAW,GACf,GAAG,CAAC,QAAQ,IAAI,IAAI;YACpB,MAAM,KAAK,GAAG;YACd,CAAC,MAAM,IAAI,IAAI,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC,CAAC;QAErD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QAC1B,CAAC;QAED,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC;QAChC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QAC1B,CAAC;QAED,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,IAAI,IAAI,CAAC;QAElC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,CAAC,CAAC,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;YACzD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;YAEjD,MAAM,MAAM,GACT,GAAG,CAAC,OAAO,CAAC,MAAkC,EAAE,OAAO;gBACtD,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;YAEzB,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;gBAEnE,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;oBAChB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;gBAC7C,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;YACf,CAAC;QACH,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC3B,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"handlers.js","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"AAEA,MAAM,IAAI,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAEzF,SAAS,WAAW,CAAC,QAAmB;IACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAGlC;IACC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,IAAK,EAAE,GAAG,OAAO,CAAC;IAEzD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,UAAU,CAAC,4CAA4C,CAAC,CAAC;IACrE,CAAC;IAED,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,MAAM,IAAI,UAAU,CAAC,+BAA+B,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,KAAK,EAAE,OAA8B,EAAE,EAAE;QAC9C,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YACtE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QAC1B,CAAC;QAED,IAAI,cAAoC,CAAC;QAEzC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,UAAU,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;YACzD,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;YAEpB,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;gBACpC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;YAC1B,CAAC;YAED,IAAI,CAAC;gBACH,cAAc,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;gBAElE,IAAI,cAAc,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,EAAE,CAAC;oBACtD,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;gBACnD,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,cAAc,GAAG,SAAS,CAAC;YAC7B,CAAC;QACH,CAAC;QAED,OAAO,cAAc;YACnB,CAAC,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE;YAC3C,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACvB,CAAC,CAAC;AACJ,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- export { createHttpClient } from './client';
2
- export { createRetryHandler } from './handlers';
3
- export { createAPI } from './spec';
4
- export { genAPIDefinition } from './spec.generator';
1
+ export { createHttpClient } from './client.js';
2
+ export type { APIExceptionHandler, FetchExceptionContext, RequestHandler } from './client.js';
3
+ export { createRetryHandler } from './handlers.js';
4
+ export { createAPI } from './spec.js';
5
+ export type { APIMapOf } from './spec.js';
6
+ export { genAPIDefinition } from '@eecho/definition';
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,YAAY,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
- export { createHttpClient } from './client';
2
- export { createRetryHandler } from './handlers';
3
- export { createAPI } from './spec';
4
- export { genAPIDefinition } from './spec.generator';
1
+ export { createHttpClient } from './client.js';
2
+ export { createRetryHandler } from './handlers.js';
3
+ export { createAPI } from './spec.js';
4
+ // Compatibility re-export: API contracts are owned by @eecho/definition.
5
+ export { genAPIDefinition } from '@eecho/definition';
5
6
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,yEAAyE;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/spec.d.ts CHANGED
@@ -1,18 +1,19 @@
1
- import { z } from "zod";
2
- import { createHttpClient } from "./client";
3
- import { ClientAPISpecification } from '@eecho/definition';
4
- export type APIMapOf<S extends ClientAPISpecification> = {
5
- [K in S["operationId"]]: (input?: {
6
- queryParams?: InferIfSchema<S["Request"]["queryParams"]>;
7
- body?: InferIfSchema<S["Request"]["body"]>;
8
- }) => Promise<z.infer<S["Response"]["body"]>>;
1
+ import { z } from 'zod';
2
+ import type { ClientAPISpecification } from '@eecho/definition';
3
+ import { createHttpClient } from './client.js';
4
+ type InferInputIfSchema<T> = T extends z.ZodTypeAny ? z.input<T> : never;
5
+ type InferOutputIfSchema<T> = T extends z.ZodTypeAny ? z.output<T> : never;
6
+ export type APIMapOf<TSpec extends ClientAPISpecification> = {
7
+ [K in TSpec['operationId']]: (input?: {
8
+ queryParams?: InferInputIfSchema<TSpec['Request']['queryParams']>;
9
+ body?: InferInputIfSchema<TSpec['Request']['body']>;
10
+ }) => Promise<InferOutputIfSchema<TSpec['Response']['body']>>;
9
11
  };
10
- type InferIfSchema<T> = T extends z.ZodTypeAny ? z.infer<T> : never;
11
- export declare function createAPI<S extends ClientAPISpecification>(p: {
12
- apiSpec: S;
12
+ export declare function createAPI<TSpec extends ClientAPISpecification>(options: {
13
+ apiSpec: TSpec;
13
14
  httpClient: ReturnType<typeof createHttpClient>;
14
15
  contentType?: 'application/json' | 'multipart/form-data';
15
16
  responseType?: 'json' | 'blob';
16
- }): APIMapOf<S>;
17
+ }): APIMapOf<TSpec>;
17
18
  export {};
18
19
  //# sourceMappingURL=spec.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,sBAAsB,IAAI;KACtD,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;QAChC,WAAW,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;KAC5C,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;CAC9C,CAAC;AAEF,KAAK,aAAa,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;AAIpE,wBAAgB,SAAS,CAAC,CAAC,SAAS,sBAAsB,EAAE,CAAC,EAAE;IAC7D,OAAO,EAAE,CAAC,CAAC;IACX,UAAU,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;IAChD,WAAW,CAAC,EAAE,kBAAkB,GAAG,qBAAqB,CAAC;IACvD,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAElC,GAiGM,QAAQ,CAAC,CAAC,CAAC,CACjB"}
1
+ {"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAEhE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,KAAK,kBAAkB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;AACzE,KAAK,mBAAmB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;AAE3E,MAAM,MAAM,QAAQ,CAAC,KAAK,SAAS,sBAAsB,IAAI;KAC1D,CAAC,IAAI,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;QACpC,WAAW,CAAC,EAAE,kBAAkB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,EAAE,kBAAkB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;KACrD,KAAK,OAAO,CAAC,mBAAmB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;CAC9D,CAAC;AA0DF,wBAAgB,SAAS,CAAC,KAAK,SAAS,sBAAsB,EAAE,OAAO,EAAE;IACvE,OAAO,EAAE,KAAK,CAAC;IACf,UAAU,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;IAChD,WAAW,CAAC,EAAE,kBAAkB,GAAG,qBAAqB,CAAC;IACzD,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAChC,GAsDM,QAAQ,CAAC,KAAK,CAAC,CACrB"}
package/dist/spec.js CHANGED
@@ -1,86 +1,71 @@
1
1
  import { stringify } from 'qs';
2
- // Create API function
3
- // this return a function that can be called with queryParams and body the types are inferred from the APISpecification
4
- export function createAPI(p) {
5
- const { apiSpec, httpClient, contentType = "application/json", responseType = "json" } = p;
2
+ function createValidationError(label, error) {
3
+ const validationError = new Error(`${label}: ${error.message}`);
4
+ Object.assign(validationError, { cause: error });
5
+ return validationError;
6
+ }
7
+ function parseRequestPart(schema, value, label) {
8
+ if (!schema) {
9
+ return undefined;
10
+ }
11
+ const result = schema.safeParse(value);
12
+ if (!result.success) {
13
+ throw createValidationError(label, result.error);
14
+ }
15
+ return result.data;
16
+ }
17
+ function appendFormValue(formData, key, value) {
18
+ if (value === undefined || value === null) {
19
+ return;
20
+ }
21
+ if (Array.isArray(value)) {
22
+ value.forEach((item) => appendFormValue(formData, key, item));
23
+ return;
24
+ }
25
+ formData.append(key, value instanceof Blob ? value : String(value));
26
+ }
27
+ function serializeBody(value, contentType) {
28
+ if (value === undefined) {
29
+ return {};
30
+ }
31
+ if (contentType === 'application/json') {
32
+ return {
33
+ body: JSON.stringify(value),
34
+ headers: { 'Content-Type': 'application/json' },
35
+ };
36
+ }
37
+ const formData = new FormData();
38
+ if (typeof value === 'object' && value !== null) {
39
+ Object.entries(value).forEach(([key, item]) => appendFormValue(formData, key, item));
40
+ }
41
+ return { body: formData };
42
+ }
43
+ export function createAPI(options) {
44
+ const { apiSpec, httpClient, contentType = 'application/json', responseType = 'json', } = options;
45
+ const operation = `${apiSpec.operationId} ${apiSpec.Method} ${apiSpec.APIEndpoint}`;
6
46
  const handler = async (input) => {
7
- if (apiSpec.Request.queryParams && input?.queryParams) {
8
- const queryParamsParsing = apiSpec.Request.queryParams.safeParse(input.queryParams);
9
- if (!queryParamsParsing.success) {
10
- console.error(`API Query Params Validation Error: ${apiSpec.operationId} ${apiSpec.Method} ${apiSpec.APIEndpoint}`, queryParamsParsing.error);
11
- console.error(`Input Query Params:`, input?.queryParams);
12
- throw new Error(`API Query Params Validation Error: ${queryParamsParsing.error.message}`);
13
- }
14
- }
15
- if (apiSpec.Request.body && input?.body) {
16
- const bodyParamsParsing = apiSpec.Request.body.safeParse(input.body);
17
- if (!bodyParamsParsing.success) {
18
- console.error(`API Body Params Validation Error: ${apiSpec.operationId} ${apiSpec.Method} ${apiSpec.APIEndpoint}`, bodyParamsParsing.error);
19
- console.error(`Input Body Params:`, input?.body);
20
- throw new Error(`API Body Params Validation Error: ${bodyParamsParsing.error.message}`);
21
- }
22
- }
23
- let url = httpClient.host + apiSpec.APIEndpoint;
24
- let queryString = "";
25
- if (input?.queryParams) {
26
- queryString = stringify(input.queryParams, { addQueryPrefix: true, arrayFormat: 'brackets' });
27
- }
28
- url += queryString;
29
- // Content-Type에 따른 body 처리
30
- let body;
31
- let headers = {};
32
- if (input?.body) {
33
- if (contentType === 'application/json') {
34
- body = JSON.stringify(input.body);
35
- headers = { 'Content-Type': 'application/json' };
36
- }
37
- else if (contentType === 'multipart/form-data') {
38
- // FormData로 변환
39
- const formData = new FormData();
40
- if (typeof input.body === 'object' && input.body !== null) {
41
- Object.entries(input.body).forEach(([key, value]) => {
42
- if (value instanceof File) {
43
- formData.append(key, value);
44
- }
45
- else if (value !== undefined && value !== null) {
46
- formData.append(key, String(value));
47
- }
48
- });
49
- }
50
- body = formData;
51
- // multipart/form-data는 브라우저가 자동으로 Content-Type을 설정하므로 헤더를 설정하지 않음
52
- }
53
- }
54
- // 3) fetch
55
- const res = await httpClient.fetch({
56
- url: url.toString(),
47
+ const queryParams = parseRequestPart(apiSpec.Request.queryParams, input?.queryParams ?? {}, `Invalid query parameters for ${operation}`);
48
+ const bodyParams = parseRequestPart(apiSpec.Request.body, input?.body, `Invalid request body for ${operation}`);
49
+ const queryString = queryParams
50
+ ? stringify(queryParams, { addQueryPrefix: true, arrayFormat: 'brackets' })
51
+ : '';
52
+ const requestBody = serializeBody(bodyParams, contentType);
53
+ const response = await httpClient.fetch({
54
+ url: `${apiSpec.APIEndpoint}${queryString}`,
57
55
  options: {
58
56
  method: apiSpec.Method,
59
- body,
60
- headers: Object.keys(headers).length > 0 ? headers : undefined,
57
+ ...requestBody,
61
58
  },
62
59
  });
63
- // 4) 응답 검증
64
- let data;
65
- if (responseType === 'json') {
66
- data = await res?.json();
67
- }
68
- else if (responseType === 'blob') {
69
- data = await res?.blob();
60
+ if (!response.ok) {
61
+ throw new Error(`API request failed for ${operation}: ${response.status} ${response.statusText}`);
70
62
  }
63
+ const data = responseType === 'json'
64
+ ? await response.json()
65
+ : await response.blob();
71
66
  const parsedResult = apiSpec.Response.body.safeParse(data);
72
- if (res && !res.ok) {
73
- console.error(`API Error: ${apiSpec.operationId} ${apiSpec.Method} ${apiSpec.APIEndpoint}`, {
74
- status: res.status,
75
- statusText: res.statusText,
76
- data: parsedResult.error,
77
- });
78
- throw new Error(`API Error: ${res.status} ${res.statusText}`);
79
- }
80
67
  if (!parsedResult.success) {
81
- console.error(`API Response Validation Error: ${apiSpec.operationId} ${apiSpec.Method} ${apiSpec.APIEndpoint}`, parsedResult.error);
82
- console.error(`Response Data:`, data);
83
- throw new Error(`API Response Validation Error: ${parsedResult.error.message}`);
68
+ throw createValidationError(`Invalid API response for ${operation}`, parsedResult.error);
84
69
  }
85
70
  return parsedResult.data;
86
71
  };
package/dist/spec.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"spec.js","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"spec.js","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,9 +1,15 @@
1
1
  {
2
2
  "name": "@eecho/api-client",
3
- "version": "0.0.3",
4
- "description": "EEcho API client for declarative server applications",
3
+ "version": "0.2.0",
4
+ "description": "Validated HTTP client for shared EEcho API contracts",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
7
13
  "type": "module",
8
14
  "files": [
9
15
  "dist/**/*"
@@ -17,15 +23,17 @@
17
23
  "license": "MIT",
18
24
  "repository": {
19
25
  "type": "git",
20
- "url": "https://github.com/eecho/eechojs.git"
26
+ "url": "git+https://github.com/eecho/eechojs.git"
21
27
  },
22
28
  "dependencies": {
23
- "@eecho/definition": "^0.0.2",
24
- "@types/qs": "^6.14.0",
29
+ "@eecho/definition": "^0.2.0",
25
30
  "qs": "^6.14.0"
26
31
  },
32
+ "peerDependencies": {
33
+ "zod": "^4.1.12"
34
+ },
27
35
  "devDependencies": {
28
- "ts-node": "^10.9.2",
36
+ "@types/qs": "^6.14.0",
29
37
  "typescript": "^5.0.0"
30
38
  },
31
39
  "publishConfig": {
@@ -1,90 +0,0 @@
1
- import { z } from 'zod';
2
- import { Definition } from '@eecho/definition';
3
- export declare function genAPIDefinition<TDefinition extends Definition, const TPrefix extends string>(params: {
4
- definition: TDefinition;
5
- endpointPrefix: TPrefix;
6
- }): {
7
- CreateAPISpecification: {
8
- readonly APIEndpoint: `${TPrefix}/createItems`;
9
- readonly Method: "POST";
10
- readonly operationId: "createItems";
11
- readonly Request: {
12
- readonly body: z.ZodObject<{
13
- data: z.ZodObject<({ [K_1 in keyof TDefinition]: TDefinition[K_1] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<any, any, infer TApi extends import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? TApi["create"] extends readonly unknown[] ? Extract<"Optional", TApi["create"][number]> | Extract<"System", TApi["create"][number]> extends never ? K_1 : never : K_1 : never; }[keyof TDefinition] extends infer T_1 extends keyof TDefinition ? { [K in T_1 as TDefinition[K] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, readonly import("@eecho/definition/dist/model/model.define.type").PrimitiveIndex[], import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? K : never]: TDefinition[K] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<infer TSchema extends z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, readonly import("@eecho/definition/dist/model/model.define.type").PrimitiveIndex[], import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? TSchema : never; } : never) extends infer T ? { -readonly [P in keyof T]: T[P]; } : never, z.core.$strip>;
14
- }, z.core.$strip>;
15
- };
16
- readonly Response: {
17
- readonly body: z.ZodObject<{
18
- success: z.ZodLiteral<true>;
19
- data: z.ZodObject<{}, z.core.$strip>;
20
- }, z.core.$strip>;
21
- };
22
- };
23
- ReadAPISpecification: {
24
- readonly APIEndpoint: `${TPrefix}/getItems`;
25
- readonly Method: "GET";
26
- readonly operationId: "getItems";
27
- readonly Request: {
28
- readonly queryParams: z.ZodObject<{
29
- page: z.ZodOptional<z.ZodDefault<z.ZodCoercedNumber<unknown>>>;
30
- limit: z.ZodOptional<z.ZodDefault<z.ZodCoercedNumber<unknown>>>;
31
- filter: z.ZodUnion<readonly [z.ZodOptional<z.ZodObject<(({ [K_4 in keyof TDefinition]: TDefinition[K_4] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<any, any, infer TApi extends import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? TApi["read"] extends readonly unknown[] ? "Searchable" extends infer T_5 ? T_5 extends "Searchable" ? T_5 extends TApi["read"][number] ? K_4 : never : never : never : never : never; }[keyof TDefinition] extends infer T_4 extends keyof TDefinition ? { [K_3 in T_4 as TDefinition[K_3] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, readonly import("@eecho/definition/dist/model/model.define.type").PrimitiveIndex[], import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? K_3 : never]: TDefinition[K_3] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<infer TSchema extends z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, readonly import("@eecho/definition/dist/model/model.define.type").PrimitiveIndex[], import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? TSchema : never; } : never) extends infer T_3 extends Record<string, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>> ? { [K_2 in keyof T_3]: z.ZodOptional<T_3[K_2]>; } : never) extends infer T_2 ? { -readonly [P_1 in keyof T_2]: T_2[P_1]; } : never, z.core.$strip>>, z.ZodOptional<z.ZodObject<(({ [K_7 in keyof TDefinition]: TDefinition[K_7] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<any, any, infer TApi extends import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? TApi["read"] extends readonly unknown[] ? "SearchableArray" extends infer T_8 ? T_8 extends "SearchableArray" ? T_8 extends TApi["read"][number] ? K_7 : never : never : never : never : never; }[keyof TDefinition] extends infer T_7 extends keyof TDefinition ? { [K_6 in T_7 as TDefinition[K_6] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, readonly import("@eecho/definition/dist/model/model.define.type").PrimitiveIndex[], import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? K_6 : never]: TDefinition[K_6] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<infer TSchema extends z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, readonly import("@eecho/definition/dist/model/model.define.type").PrimitiveIndex[], import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? TSchema : never; } : never) extends infer T_6 extends Record<string, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>> ? { [K_5 in keyof T_6]: z.ZodOptional<z.ZodArray<z.ZodOptional<T_6[K_5]>>>; } : never) extends infer T_5 ? { -readonly [P_2 in keyof T_5]: T_5[P_2]; } : never, z.core.$strip>>]>;
32
- sort: z.ZodOptional<z.ZodObject<(({ [K_10 in keyof TDefinition]: TDefinition[K_10] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<any, any, infer TApi extends import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? TApi["read"] extends readonly unknown[] ? "Sortable" extends infer T_11 ? T_11 extends "Sortable" ? T_11 extends TApi["read"][number] ? K_10 : never : never : never : never : never; }[keyof TDefinition] extends infer T_10 extends keyof TDefinition ? { [K_9 in T_10 as TDefinition[K_9] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, readonly import("@eecho/definition/dist/model/model.define.type").PrimitiveIndex[], import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? K_9 : never]: TDefinition[K_9] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<infer TSchema extends z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, readonly import("@eecho/definition/dist/model/model.define.type").PrimitiveIndex[], import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? TSchema : never; } : never) extends infer T_9 extends Record<string, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>> ? { [K_8 in keyof T_9]: z.ZodOptional<T_9[K_8]>; } : never) extends infer T_8 ? { -readonly [P_3 in keyof T_8]: T_8[P_3]; } : never, z.core.$strip>>;
33
- }, z.core.$strip>;
34
- };
35
- readonly Response: {
36
- readonly body: z.ZodObject<{
37
- success: z.ZodLiteral<true>;
38
- data: z.ZodArray<z.ZodObject<({ [K_12 in keyof TDefinition]: TDefinition[K_12] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<any, any, infer TApi extends import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? TApi["read"] extends readonly unknown[] ? Extract<"Hidden", TApi["read"][number]> | Extract<"Detail", TApi["read"][number]> extends never ? K_12 : never : K_12 : never; }[keyof TDefinition] extends infer T_12 extends keyof TDefinition ? { [K_11 in T_12 as TDefinition[K_11] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, readonly import("@eecho/definition/dist/model/model.define.type").PrimitiveIndex[], import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? K_11 : never]: TDefinition[K_11] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<infer TSchema extends z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, readonly import("@eecho/definition/dist/model/model.define.type").PrimitiveIndex[], import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? TSchema : never; } : never) extends infer T_11 ? { -readonly [P_4 in keyof T_11]: T_11[P_4]; } : never, z.core.$strip>>;
39
- }, z.core.$strip>;
40
- };
41
- };
42
- UpdateAPISpecification: {
43
- readonly APIEndpoint: `${TPrefix}/updateItem`;
44
- readonly Method: "POST";
45
- readonly operationId: "updateItem";
46
- readonly Request: {
47
- readonly body: z.ZodObject<{
48
- id: z.ZodAny;
49
- data: z.ZodObject<(({ [K_15 in keyof TDefinition]: TDefinition[K_15] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<any, any, infer TApi extends import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? TApi["update"] extends readonly unknown[] ? "Updatable" extends infer T_16 ? T_16 extends "Updatable" ? T_16 extends TApi["update"][number] ? K_15 : never : never : never : never : never; }[keyof TDefinition] extends infer T_15 extends keyof TDefinition ? { [K_14 in T_15 as TDefinition[K_14] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, readonly import("@eecho/definition/dist/model/model.define.type").PrimitiveIndex[], import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? K_14 : never]: TDefinition[K_14] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<infer TSchema extends z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, readonly import("@eecho/definition/dist/model/model.define.type").PrimitiveIndex[], import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? TSchema : never; } : never) extends infer T_14 extends Record<string, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>> ? { [K_13 in keyof T_14]: z.ZodOptional<T_14[K_13]>; } : never) extends infer T_13 ? { -readonly [P_5 in keyof T_13]: T_13[P_5]; } : never, z.core.$strip>;
50
- }, z.core.$strip>;
51
- };
52
- readonly Response: {
53
- readonly body: z.ZodObject<{
54
- success: z.ZodLiteral<true>;
55
- }, z.core.$strip>;
56
- };
57
- };
58
- DeleteAPISpecification: {
59
- readonly APIEndpoint: `${TPrefix}/deleteItems`;
60
- readonly Method: "POST";
61
- readonly operationId: "deleteItems";
62
- readonly Request: {
63
- readonly body: z.ZodObject<{
64
- ids: z.ZodArray<z.ZodObject<{}, z.core.$strip>>;
65
- }, z.core.$strip>;
66
- };
67
- readonly Response: {
68
- readonly body: z.ZodObject<{
69
- success: z.ZodLiteral<true>;
70
- }, z.core.$strip>;
71
- };
72
- };
73
- PutAPISpecification: {
74
- readonly APIEndpoint: `${TPrefix}/putItem`;
75
- readonly Method: "PUT";
76
- readonly operationId: "putItem";
77
- readonly Request: {
78
- readonly body: z.ZodObject<{
79
- data: z.ZodObject<({ [K_1 in keyof TDefinition]: TDefinition[K_1] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<any, any, infer TApi extends import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? TApi["create"] extends readonly unknown[] ? Extract<"Optional", TApi["create"][number]> | Extract<"System", TApi["create"][number]> extends never ? K_1 : never : K_1 : never; }[keyof TDefinition] extends infer T_17 extends keyof TDefinition ? { [K in T_17 as TDefinition[K] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, readonly import("@eecho/definition/dist/model/model.define.type").PrimitiveIndex[], import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? K : never]: TDefinition[K] extends import("@eecho/definition/dist/model/model.define.type").PrimitiveDefinition<infer TSchema extends z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, readonly import("@eecho/definition/dist/model/model.define.type").PrimitiveIndex[], import("@eecho/definition/dist/model/model.define.type").APIIndex, string> ? TSchema : never; } : never) extends infer T_16 ? { -readonly [P in keyof T_16]: T_16[P]; } : never, z.core.$strip>;
80
- }, z.core.$strip>;
81
- };
82
- readonly Response: {
83
- readonly body: z.ZodObject<{
84
- success: z.ZodLiteral<true>;
85
- data: z.ZodObject<{}, z.core.$strip>;
86
- }, z.core.$strip>;
87
- };
88
- };
89
- };
90
- //# sourceMappingURL=spec.generator.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"spec.generator.d.ts","sourceRoot":"","sources":["../src/spec.generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAOL,UAAU,EACX,MAAM,mBAAmB,CAAC;AAE3B,wBAAgB,gBAAgB,CAAC,WAAW,SAAS,UAAU,EAAE,KAAK,CAAC,OAAO,SAAS,MAAM,EAAE,MAAM,EAAE;IACrG,UAAU,EAAE,WAAW,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;CACzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgGA"}
@@ -1,92 +0,0 @@
1
- import { z } from 'zod';
2
- import { extractCreateRequiredField, extractReadbleField, extractSearchArrayOption, extractSearchOption, extractSortableOption, extractUpdateOption } from '@eecho/definition';
3
- export function genAPIDefinition(params) {
4
- const { definition, endpointPrefix } = params;
5
- const createRequiredField = extractCreateRequiredField({ definition });
6
- const readableField = extractReadbleField({ definition });
7
- const searchableOption = extractSearchOption({ definition });
8
- const searchableArrayOption = extractSearchArrayOption({ definition });
9
- const sortableOption = extractSortableOption({ definition });
10
- const updatableOption = extractUpdateOption({ definition });
11
- const CreateAPISpecification = {
12
- APIEndpoint: `${endpointPrefix}/createItems`,
13
- Method: 'POST',
14
- operationId: 'createItems',
15
- Request: {
16
- body: z.object({ data: z.object(createRequiredField) }),
17
- },
18
- Response: {
19
- body: z.object({
20
- success: z.literal(true),
21
- data: z.object({}),
22
- }),
23
- },
24
- };
25
- const ReadAPISpecification = {
26
- APIEndpoint: `${endpointPrefix}/getItems`,
27
- Method: 'GET',
28
- operationId: 'getItems',
29
- Request: {
30
- queryParams: z.object({
31
- page: z.coerce.number().default(1).optional(),
32
- limit: z.coerce.number().default(15).optional(),
33
- filter: z.union([
34
- z.object(searchableOption).optional(),
35
- z.object(searchableArrayOption).optional()
36
- ]),
37
- sort: z.object(sortableOption).optional(),
38
- }),
39
- },
40
- Response: {
41
- body: z.object({
42
- success: z.literal(true),
43
- data: z.array(z.object(readableField)),
44
- })
45
- },
46
- };
47
- const UpdateAPISpecification = {
48
- APIEndpoint: `${endpointPrefix}/updateItem`,
49
- Method: 'POST',
50
- operationId: 'updateItem',
51
- Request: {
52
- body: z.object({
53
- id: z.any(),
54
- data: z.object(updatableOption),
55
- })
56
- },
57
- Response: {
58
- body: z.object({
59
- success: z.literal(true),
60
- }),
61
- },
62
- };
63
- const DeleteAPISpecification = {
64
- APIEndpoint: `${endpointPrefix}/deleteItems`,
65
- Method: 'POST',
66
- operationId: 'deleteItems',
67
- Request: {
68
- body: z.object({
69
- ids: z.array(z.object({})),
70
- }),
71
- },
72
- Response: {
73
- body: z.object({
74
- success: z.literal(true),
75
- }),
76
- },
77
- };
78
- const PutAPISpecification = {
79
- ...CreateAPISpecification,
80
- APIEndpoint: `${endpointPrefix}/putItem`,
81
- Method: 'PUT',
82
- operationId: 'putItem'
83
- };
84
- return {
85
- CreateAPISpecification,
86
- ReadAPISpecification,
87
- UpdateAPISpecification,
88
- DeleteAPISpecification,
89
- PutAPISpecification
90
- };
91
- }
92
- //# sourceMappingURL=spec.generator.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"spec.generator.js","sourceRoot":"","sources":["../src/spec.generator.ts"],"names":[],"mappings":"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"}