@snail-js/api 0.1.3 → 0.1.4

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
@@ -4,11 +4,13 @@
4
4
  </p>
5
5
 
6
6
  ## 项目介绍
7
+
7
8
  - 基于 Axios 二次封装
8
9
  - 使用`reflect-metadata`创建和处理元数据
9
10
  - 提供装饰器方式定义请求,基本实例`Snail`,请求实例`Api`
10
11
 
11
12
  ## 安装
13
+
12
14
  `npm install @snail-js/api`
13
15
 
14
16
  ## 使用
@@ -61,7 +63,6 @@ import { Service } from "./service";
61
63
 
62
64
  @Api("user")
63
65
  class UserApi {
64
-
65
66
  @Get()
66
67
  get(@Params("id") id: string) {}
67
68
 
@@ -95,11 +96,11 @@ if (error !== null) {
95
96
  - CacheManage:缓存管理器
96
97
  - type:缓存管理器类型,CacheType,`enum:localStorage,IndexDB,Memory`
97
98
  - ttl: 缓存过期时间
98
- - enableLog: 是否打印日志
99
+ - enableLog: 是否打印日志
99
100
 
100
101
  ## Api 配置
101
102
 
102
- - url?: api 请求端点,与 Server 中的`baseUrl`拼接请求地址,,不要使用`/`开头
103
+ - url?: api 请求端点,与 Server 中的`baseUrl`拼接请求地址,不要使用`/`开头
103
104
  - timeout?: 请求超时时间;会覆盖`Server.timeout`
104
105
  - version?: 请求版本,会覆盖`Server.Versioning.defaultVersion`;
105
106
 
@@ -119,7 +120,6 @@ if (error !== null) {
119
120
  ```typescript
120
121
  @Api("user")
121
122
  class UserApi {
122
-
123
123
  @Get()
124
124
  get(@Params("id") id: string, @Params("sign") sign: string) {}
125
125
  }
@@ -137,7 +137,6 @@ class QueryParams {
137
137
 
138
138
  @Api("user")
139
139
  class UserApi {
140
-
141
140
  @Get()
142
141
  get(@Params() params: QueryParams) {}
143
142
  }
@@ -155,7 +154,6 @@ class QueryParams {
155
154
 
156
155
  @Api("user")
157
156
  class UserApi {
158
-
159
157
  @Get()
160
158
  get(@Params() params: QueryParams, @Params("a") a: number) {}
161
159
  }
@@ -291,11 +289,12 @@ export type VersioningOption =
291
289
  ```
292
290
 
293
291
  ### 临时版本修改器`@Version`
292
+
294
293
  - 临时改变方法请求的版本
294
+
295
295
  ```typescript
296
296
  @Api("test")
297
297
  class Test {
298
-
299
298
  @Get("HelloWorld")
300
299
  @Version("0.2.0")
301
300
  test() {}
@@ -305,15 +304,14 @@ class Test {
305
304
  > 临时改变 api 版本,便于测试
306
305
 
307
306
  ### 缓存装饰器`@Cache`
307
+
308
308
  - `@Cache(string | null)`
309
- - 当设置为null时,此方法不应用缓存
310
- - 当设置为string时,应为此Api类下的方法名称,当设置的此名称方法被调用且正常响应时,被装饰的方法缓存失效
309
+ - 当设置为 null 时,此方法不应用缓存
310
+ - 当设置为 string 时,应为此 Api 类下的方法名称,当设置的此名称方法被调用且正常响应时,被装饰的方法缓存失效
311
311
 
312
312
  ```typescript
313
-
314
313
  @Api("test")
315
314
  class Test {
316
-
317
315
  @Get("HelloWorld")
318
316
  @Cache("test2")
319
317
  test1() {}
@@ -328,17 +326,84 @@ class Test {
328
326
  ```
329
327
 
330
328
  > 当请求`[Post]test`成功时,`[Get]test/HelloWorld`的缓存失效
331
- > 注意:`test2`方法请求成功的前提是需要设置`@Cache(null)`,否则仅第一次请求会发送,后续请求需等待缓存管理设置的ttl时间到期才会发送请求
332
- > 因此,若未设置`test2`方法的`@Cache(null)`,仅第一次请求会使`[Get]test/HelloWorld`的缓存失效,后续需等待ttl时间到期,才会继续失效
329
+ > 注意:`test2`方法请求成功的前提是需要设置`@Cache(null)`,否则仅第一次请求会发送,后续请求需等待缓存管理设置的 ttl 时间到期才会发送请求
330
+ > 因此,若未设置`test2`方法的`@Cache(null)`,仅第一次请求会使`[Get]test/HelloWorld`的缓存失效,后续需等待 ttl 时间到期,才会继续失效
333
331
 
334
332
  > `test3`方法请求成功时,不缓存
335
333
 
336
334
  > 注意:要使用缓存,请配置`@Server({CacheManage})`缓存管理器
337
335
 
336
+ ### 上传进度装饰器`@UploadProgress`
337
+
338
+ - `@UploadProgress((progress: AxiosProgressEvent) => void)`
339
+
340
+ ## Server Send Event 服务端推送
341
+
342
+ ### 创建 sse 端点
343
+
344
+ ```typescript
345
+ @Api("sse")
346
+ class ServerSend {
347
+ @Sse()
348
+ create() {}
349
+
350
+ @OnSseOpen()
351
+ handleOpen(event: Event) {
352
+ console.log("sse-open:", event);
353
+ }
354
+
355
+ @OnSseError()
356
+ handleError(event: Event) {
357
+ console.log("sse-error:", event);
358
+ }
359
+
360
+ // 处理默认message事件
361
+ @SseEvent()
362
+ handleEvent(event: MessageEvent) {
363
+ console.log("sse-event[message]:", event.data);
364
+ }
365
+
366
+ // 处理自定义名称事件
367
+ @SseEvent("chunk")
368
+ handleEvent(event: Event) {
369
+ console.log("sse-event[chunk]:", event);
370
+ }
371
+ }
372
+
373
+ export const Sse = Service.createSse(ServerSend);
374
+ ```
375
+
376
+ ### 服务端推送装饰器`@Sse`
377
+ - `@Sse(path:string,options:{withCredentials: boolean})`
378
+ - 创建一个服务端推送连接,当被装饰的方法调用时打开连接
379
+ - 被装饰的方法调用后会返回`{eventSource:EventSource,close:function}`
380
+ - eventSource: sse连接实例
381
+ - close: 关闭此sse连接的方法
382
+
383
+ ### 注册`onopen`装饰器`@OnSseOpen`
384
+ - 当`@Sse`装饰的方法被调用时,将`@OnSseOpen`装饰的方法注册为`@Sse`装饰的方法返回的`EventSource`实例`onopen`处理函数
385
+
386
+ ### 注册`onerror`装饰器`@OnSseError`
387
+ - 当`@Sse`装饰的方法被调用时,将`@OnSseError`装饰的方法注册为`@Sse`装饰的方法返回的`EventSource`实例`onerror`处理函数
388
+
389
+ ### 事件处理装饰器`@SseEvent`
390
+ - `@SseEvent(eventName?:string)`
391
+ - 未传入`eventName`,默认注册为`message`事件处理器
392
+ - 传入`eventName`,注册为对应名称的事件处理器
393
+
394
+
338
395
  ### 代码仓库
339
396
 
340
- - ![Static Badge](https://img.shields.io/badge/snail-js?style=flat&label=gitee&labelColor=F56C6C&link=https%3A%2F%2Fgitee.com%2Flimich%2Fsnail)
341
- - ![Static Badge](https://img.shields.io/badge/snail-js?style=flat&label=github&labelColor=F56C6C&link=https%3A%2F%2Fgihub.com%2Flimingchang%2Fsnail)
397
+ <p>
398
+ <a src="https://gitee.com/limich/snail">
399
+ <img src="https://img.shields.io/badge/snail-js?style=flat&label=gitee&labelColor=F56C6C&link=https%3A%2F%2Fgitee.com%2Flimich%2Fsnail"></img>
400
+ </a>
401
+ </p>
402
+ <p>
403
+ <a src="https://github.com/limingchang/snail">
404
+ <img src="https://img.shields.io/badge/snail-js?style=flat&label=github&labelColor=F56C6C&link=https%3A%2F%2Fgihub.com%2Flimingchang%2Fsnail"></img>
405
+ </a>
406
+ </p>
342
407
 
343
408
  ### 作者
344
409
 
@@ -1,5 +1,5 @@
1
1
  import "reflect-metadata";
2
- import { Strategy, ApiProxy, ResponseData } from "../typings";
2
+ import { Strategy, ApiProxy, SseProxy, ResponseData } from "../typings";
3
3
  export declare class Snail<R extends {
4
4
  data: any;
5
5
  } = ResponseData> {
@@ -8,6 +8,7 @@ export declare class Snail<R extends {
8
8
  private cacheStorage?;
9
9
  private version?;
10
10
  private sourceMap;
11
+ private eventSource;
11
12
  registerStrategy(strategy: Strategy): void;
12
13
  createApi<T extends object>(constructor: new () => T): ApiProxy<T, R>;
13
14
  private buildRequestArgs;
@@ -23,6 +24,11 @@ export declare class Snail<R extends {
23
24
  private getServerConfig;
24
25
  private getApiConfig;
25
26
  private initAxios;
27
+ private generateSseUrl;
26
28
  private handleResponse;
27
29
  private handleError;
30
+ createSse<T extends object>(constructor: new () => T): SseProxy<T>;
31
+ private initSse;
32
+ private setSse;
33
+ private registerSseEvent;
28
34
  }
@@ -3,10 +3,38 @@ import { ApiConfig } from "../typings";
3
3
  export declare const METHOD_KEY: unique symbol;
4
4
  export declare const API_CONFIG_KEY: unique symbol;
5
5
  export declare const Api: (url?: string, config?: ApiConfig) => ClassDecorator;
6
+ /**
7
+ * 定义装饰的方法为Get请求
8
+ * @param path 字符型,请求端点,默认为空
9
+ */
6
10
  export declare const Get: (path?: string) => (target: any, propertyKey: string | symbol) => void;
11
+ /**
12
+ * 定义装饰的方法为Post请求
13
+ * @param path 字符型,请求端点,默认为空
14
+ */
7
15
  export declare const Post: (path?: string) => (target: any, propertyKey: string | symbol) => void;
16
+ /**
17
+ * 定义装饰的方法为Put请求
18
+ * @param path 字符型,请求端点,默认为空
19
+ */
8
20
  export declare const Put: (path?: string) => (target: any, propertyKey: string | symbol) => void;
21
+ /**
22
+ * 定义装饰的方法为Delete请求
23
+ * @param path 字符型,请求端点,默认为空
24
+ */
9
25
  export declare const Delete: (path?: string) => (target: any, propertyKey: string | symbol) => void;
26
+ /**
27
+ * 定义装饰的方法为Patch请求
28
+ * @param path 字符型,请求端点,默认为空
29
+ */
10
30
  export declare const Patch: (path?: string) => (target: any, propertyKey: string | symbol) => void;
31
+ /**
32
+ * 定义装饰的方法为Options请求
33
+ * @param path 字符型,请求端点,默认为空
34
+ */
11
35
  export declare const Options: (path?: string) => (target: any, propertyKey: string | symbol) => void;
36
+ /**
37
+ * 定义装饰的方法为Head请求
38
+ * @param path 字符型,请求端点,默认为空
39
+ */
12
40
  export declare const Head: (path?: string) => (target: any, propertyKey: string | symbol) => void;
@@ -1,3 +1,10 @@
1
1
  import "reflect-metadata";
2
2
  export declare const CACHE_OPTIONS_KEY: unique symbol;
3
+ /**
4
+ * 为请求方法设置失效源,或设置不启用缓存
5
+ * @param hitSource 失效源,字符型|null;
6
+ * - 设置为null时,该请求方法不启用缓存;
7
+ * - 设置为字符时,对应该Api下请求方法名称,当设置的名称方法请求成功时,被装饰的请求方法缓存失效
8
+ * @returns
9
+ */
3
10
  export declare const Cache: (hitSource: string | null) => (target: any, propertyKey?: string) => void;
@@ -1,4 +1,14 @@
1
1
  import "reflect-metadata";
2
2
  export declare const REQUEST_ARGS_KEY: unique symbol;
3
+ /**
4
+ * 将被装饰的参数定义为查询参数
5
+ * @param key 可选,查询参数的key,未给定key,则被装饰的参数应为对象
6
+ * @returns
7
+ */
3
8
  export declare const Params: (key?: string) => (target: any, propertyKey: string | symbol, parameterIndex: number) => void;
4
- export declare const Data: () => (target: any, propertyKey: string | symbol, parameterIndex: number) => void;
9
+ /**
10
+ * 将被装饰的参数定义为请求数据
11
+ * @param key 可选,请求data的key,未给定key,则被装饰的参数应为对象
12
+ * @returns
13
+ */
14
+ export declare const Data: (key?: string) => (target: any, propertyKey: string | symbol, parameterIndex: number) => void;
@@ -0,0 +1,4 @@
1
+ import "reflect-metadata";
2
+ import { AxiosProgressEvent } from "axios";
3
+ export declare const UPLOAD_PROGRESS_KEY: unique symbol;
4
+ export declare const UploadProgress: (handler: (progress: AxiosProgressEvent) => void) => (target: any, propertyKey: string) => void;
@@ -1,4 +1,9 @@
1
1
  import "reflect-metadata";
2
2
  import { SnailOption } from "../typings";
3
3
  export declare const SERVER_CONFIG_KEY: unique symbol;
4
+ /**
5
+ * 装饰一个继承Snail 的 class,创建后端交互基本实例
6
+ * @param config @type SnailOption
7
+ * @returns
8
+ */
4
9
  export declare const Server: (config: SnailOption) => (target: any) => void;
@@ -0,0 +1,31 @@
1
+ import "reflect-metadata";
2
+ export declare const EVENT_SOURCE_OPTION_KEY: unique symbol;
3
+ /**
4
+ * 将装饰的方法创建为SSE连接
5
+ * @param path 路径,要连接到服务端sse端点路径
6
+ * @param options 配置选项
7
+ * @returns 调用被装饰的方法可获得返回{close,eventSource}
8
+ */
9
+ export declare const Sse: (path?: string, options?: {
10
+ withCredentials: boolean;
11
+ }) => (target: any, propertyKey: string) => void;
12
+ export declare const EVENT_SOURCE_EVENTS_KEY: unique symbol;
13
+ /**
14
+ * 将装饰的方法注册为eventSource的对应事件
15
+ * @param eventName 事件名称,可选,默认处理message事件
16
+ * @param options boolean | AddEventListenerOptions 可选,注册事件选项
17
+ * @returns
18
+ */
19
+ export declare const SseEvent: (eventName?: string, options?: boolean | AddEventListenerOptions) => (target: any, propertyKey: string) => void;
20
+ export declare const EVENT_SOURCE_OPEN_KEY: unique symbol;
21
+ /**
22
+ * 将装饰的方法注册为eventSource.onOpen
23
+ * @returns
24
+ */
25
+ export declare const OnSseOpen: () => (target: any, propertyKey: string) => void;
26
+ export declare const EVENT_SOURCE_ERROR_KEY: unique symbol;
27
+ /**
28
+ * 将装饰的方法注册eventSource.onError
29
+ * @returns
30
+ */
31
+ export declare const OnSseError: () => (target: any, propertyKey: string) => void;
@@ -1,4 +1,10 @@
1
1
  import "reflect-metadata";
2
2
  import { Strategy } from "../typings";
3
3
  export declare const STRATEGY_KEY: unique symbol;
4
+ /**
5
+ * 策略装饰器,为请求添加请求策略或响应策略
6
+ * @param strategies @type Strategy[]
7
+ * - 传入的策略请使用new 实例化
8
+ * @returns
9
+ */
4
10
  export declare const UseStrategy: (...strategies: Strategy[]) => (target: any, propertyKey?: string) => void;
@@ -2,5 +2,15 @@ import "reflect-metadata";
2
2
  import { VersioningOption } from "../typings";
3
3
  export declare const VERSIONING_KEY: unique symbol;
4
4
  export declare const VERSION_KEY: unique symbol;
5
+ /**
6
+ * 版本管理装饰器,为请求设置全局版本管理器
7
+ * @param options 版本管理选项
8
+ * @returns
9
+ */
5
10
  export declare const Versioning: (options: VersioningOption) => (target: any) => void;
11
+ /**
12
+ * 为方法设置版本
13
+ * @param version 字符型,临时设置被装饰的请求版本
14
+ * @returns
15
+ */
6
16
  export declare const Version: (version: string) => (target: any, propertyKey: string) => void;
package/dist/index.d.ts CHANGED
@@ -5,5 +5,6 @@ export { Cache } from "./decorators/cache";
5
5
  export { Params, Data } from "./decorators/param";
6
6
  export { UseStrategy } from "./decorators/strategy";
7
7
  export { Versioning, Version } from "./decorators/versioning";
8
+ export { Sse, SseEvent, OnSseError, OnSseOpen } from "./decorators/sse";
8
9
  export * from "./typings";
9
10
  export * from "./utils";
package/dist/snail-api.js CHANGED
@@ -3682,6 +3682,13 @@ var CacheType = /* @__PURE__ */ ((CacheType2) => {
3682
3682
  })(CacheType || {});
3683
3683
  class Strategy {
3684
3684
  }
3685
+ class RegisterSseEvent {
3686
+ constructor() {
3687
+ __publicField(this, "eventName");
3688
+ __publicField(this, "emit");
3689
+ __publicField(this, "options");
3690
+ }
3691
+ }
3685
3692
  function createCache(type, ttl) {
3686
3693
  if (type === CacheType.Memory) {
3687
3694
  return new MemoryCache(ttl);
@@ -3781,7 +3788,7 @@ const UseStrategy = (...strategies) => {
3781
3788
  };
3782
3789
  const REQUEST_ARGS_KEY = Symbol("SNAIL_REQUEST_ARGS_KEY");
3783
3790
  const Params = (key) => createArgsDecorator("params", key);
3784
- const Data = () => createArgsDecorator("data");
3791
+ const Data = (key) => createArgsDecorator("data", key);
3785
3792
  const createArgsDecorator = (type, key) => {
3786
3793
  return (target, propertyKey, parameterIndex) => {
3787
3794
  const args = Reflect.getMetadata(REQUEST_ARGS_KEY, target, propertyKey) || [];
@@ -3808,6 +3815,45 @@ const Cache = (hitSource) => {
3808
3815
  Reflect.defineMetadata(key, hitSource, target);
3809
3816
  };
3810
3817
  };
3818
+ const UPLOAD_PROGRESS_KEY = Symbol("SNAIL_UPLOAD_PROGRESS_KEY");
3819
+ const EVENT_SOURCE_OPTION_KEY = Symbol("SNAIL_EVENT_SOURCE_OPTION_KEY");
3820
+ const Sse = (path, options) => {
3821
+ return (target, propertyKey) => {
3822
+ Reflect.defineMetadata(
3823
+ EVENT_SOURCE_OPTION_KEY,
3824
+ {
3825
+ path,
3826
+ withCredentials: options == null ? void 0 : options.withCredentials
3827
+ },
3828
+ target,
3829
+ propertyKey
3830
+ );
3831
+ };
3832
+ };
3833
+ const EVENT_SOURCE_EVENTS_KEY = Symbol("EVENT_SOURCE_EVENTS_KEY");
3834
+ const SseEvent = (eventName, options) => {
3835
+ return (target, propertyKey) => {
3836
+ const events = Reflect.getMetadata(EVENT_SOURCE_EVENTS_KEY, target) || [];
3837
+ events.push({
3838
+ eventName: eventName ? eventName : "message",
3839
+ emit: target[propertyKey],
3840
+ options
3841
+ });
3842
+ Reflect.defineMetadata(EVENT_SOURCE_EVENTS_KEY, events, target);
3843
+ };
3844
+ };
3845
+ const EVENT_SOURCE_OPEN_KEY = Symbol("EVENT_SOURCE_OPEN_KEY");
3846
+ const OnSseOpen = () => {
3847
+ return (target, propertyKey) => {
3848
+ Reflect.defineMetadata(EVENT_SOURCE_OPEN_KEY, target[propertyKey], target);
3849
+ };
3850
+ };
3851
+ const EVENT_SOURCE_ERROR_KEY = Symbol("EVENT_SOURCE_ERROR_KEY");
3852
+ const OnSseError = () => {
3853
+ return (target, propertyKey) => {
3854
+ Reflect.defineMetadata(EVENT_SOURCE_ERROR_KEY, target[propertyKey], target);
3855
+ };
3856
+ };
3811
3857
  class Snail {
3812
3858
  constructor() {
3813
3859
  __publicField(this, "axiosInstance");
@@ -3816,6 +3862,7 @@ class Snail {
3816
3862
  __publicField(this, "cacheStorage");
3817
3863
  __publicField(this, "version");
3818
3864
  __publicField(this, "sourceMap", /* @__PURE__ */ new Map());
3865
+ __publicField(this, "eventSource");
3819
3866
  }
3820
3867
  registerStrategy(strategy) {
3821
3868
  this.strategies.push(strategy);
@@ -3879,9 +3926,17 @@ class Snail {
3879
3926
  return this.handleResponse(cachedResponse, true);
3880
3927
  }
3881
3928
  }
3929
+ const onUploadProgress = Reflect.getMetadata(
3930
+ UPLOAD_PROGRESS_KEY,
3931
+ target,
3932
+ propertyKey
3933
+ );
3882
3934
  try {
3883
3935
  enableLog && console.log("send request:", request);
3884
- const response = await this.axiosInstance(request);
3936
+ const response = await this.axiosInstance({
3937
+ ...request,
3938
+ onUploadProgress
3939
+ });
3885
3940
  hitSource !== null && this.setCache(request, response);
3886
3941
  this.expireCache(propertyKey);
3887
3942
  return this.handleResponse(response, false);
@@ -4033,6 +4088,15 @@ class Snail {
4033
4088
  });
4034
4089
  }
4035
4090
  }
4091
+ generateSseUrl(baseURL, url) {
4092
+ if (!baseURL && !url) {
4093
+ return "/";
4094
+ }
4095
+ if (!baseURL && url) {
4096
+ return `/${url}`;
4097
+ }
4098
+ return `${baseURL}/${url}`;
4099
+ }
4036
4100
  handleResponse(response, hitCache) {
4037
4101
  return {
4038
4102
  data: response.data,
@@ -4046,6 +4110,75 @@ class Snail {
4046
4110
  error
4047
4111
  };
4048
4112
  }
4113
+ createSse(constructor) {
4114
+ const instance = new constructor();
4115
+ const serverConfig = this.getServerConfig();
4116
+ const { baseURL, enableLog } = serverConfig;
4117
+ const apiConfig = this.getApiConfig(constructor);
4118
+ enableLog && console.log("ApiConfig:", apiConfig);
4119
+ this.version = apiConfig.version;
4120
+ return new Proxy(instance, {
4121
+ get: (target, propertyKey) => {
4122
+ enableLog && console.log("proxy:", target, "|", propertyKey);
4123
+ if (typeof propertyKey !== "string") return;
4124
+ const sseOption = Reflect.getMetadata(
4125
+ EVENT_SOURCE_OPTION_KEY,
4126
+ target,
4127
+ propertyKey
4128
+ );
4129
+ if (sseOption) {
4130
+ const url = sseOption.path == "" ? apiConfig.url : apiConfig.url + `/${sseOption.path}`;
4131
+ const { url: versionUrl } = this.applyVersion(
4132
+ { url },
4133
+ target,
4134
+ propertyKey
4135
+ );
4136
+ const sseUrl = this.generateSseUrl(baseURL, versionUrl || url);
4137
+ enableLog && console.log("sse-url:", sseUrl);
4138
+ const { eventSource, close } = this.initSse(sseUrl, sseOption);
4139
+ this.eventSource = eventSource;
4140
+ this.setSse(target);
4141
+ this.registerSseEvent(target);
4142
+ return () => {
4143
+ return { eventSource, close };
4144
+ };
4145
+ }
4146
+ }
4147
+ });
4148
+ }
4149
+ initSse(url, options) {
4150
+ const eventSource = new EventSource(url, {
4151
+ withCredentials: options.withCredentials ? options.withCredentials : false
4152
+ });
4153
+ console.log("EventSource:", eventSource);
4154
+ return {
4155
+ eventSource,
4156
+ close: eventSource.close
4157
+ };
4158
+ }
4159
+ setSse(target) {
4160
+ const onOpenFunc = Reflect.getMetadata(EVENT_SOURCE_OPEN_KEY, target);
4161
+ console.log("sse-proxy[open]:", onOpenFunc);
4162
+ if (typeof onOpenFunc == "function") {
4163
+ this.eventSource && (this.eventSource.onopen = onOpenFunc);
4164
+ }
4165
+ const onErrorFunc = Reflect.getMetadata(EVENT_SOURCE_ERROR_KEY, target);
4166
+ console.log("sse-proxy[error]:", onErrorFunc);
4167
+ if (typeof onErrorFunc == "function") {
4168
+ this.eventSource && (this.eventSource.onerror = onErrorFunc);
4169
+ }
4170
+ }
4171
+ registerSseEvent(target) {
4172
+ const eventSource = this.eventSource;
4173
+ if (!eventSource) return;
4174
+ const events = Reflect.getMetadata(
4175
+ EVENT_SOURCE_EVENTS_KEY,
4176
+ target
4177
+ );
4178
+ events.map((event) => {
4179
+ eventSource.addEventListener(event.eventName, event.emit, event.options);
4180
+ });
4181
+ }
4049
4182
  }
4050
4183
  export {
4051
4184
  Api,
@@ -4055,14 +4188,19 @@ export {
4055
4188
  Delete,
4056
4189
  Get,
4057
4190
  Head,
4191
+ OnSseError,
4192
+ OnSseOpen,
4058
4193
  Options,
4059
4194
  Params,
4060
4195
  Patch,
4061
4196
  Post,
4062
4197
  Put,
4198
+ RegisterSseEvent,
4063
4199
  RequestMethod,
4064
4200
  Server,
4065
4201
  Snail,
4202
+ Sse,
4203
+ SseEvent,
4066
4204
  Strategy,
4067
4205
  UseStrategy,
4068
4206
  Version,
@@ -3686,6 +3686,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3686
3686
  })(CacheType || {});
3687
3687
  class Strategy {
3688
3688
  }
3689
+ class RegisterSseEvent {
3690
+ constructor() {
3691
+ __publicField(this, "eventName");
3692
+ __publicField(this, "emit");
3693
+ __publicField(this, "options");
3694
+ }
3695
+ }
3689
3696
  function createCache(type, ttl) {
3690
3697
  if (type === CacheType.Memory) {
3691
3698
  return new MemoryCache(ttl);
@@ -3785,7 +3792,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3785
3792
  };
3786
3793
  const REQUEST_ARGS_KEY = Symbol("SNAIL_REQUEST_ARGS_KEY");
3787
3794
  const Params = (key) => createArgsDecorator("params", key);
3788
- const Data = () => createArgsDecorator("data");
3795
+ const Data = (key) => createArgsDecorator("data", key);
3789
3796
  const createArgsDecorator = (type, key) => {
3790
3797
  return (target, propertyKey, parameterIndex) => {
3791
3798
  const args = Reflect.getMetadata(REQUEST_ARGS_KEY, target, propertyKey) || [];
@@ -3812,6 +3819,45 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3812
3819
  Reflect.defineMetadata(key, hitSource, target);
3813
3820
  };
3814
3821
  };
3822
+ const UPLOAD_PROGRESS_KEY = Symbol("SNAIL_UPLOAD_PROGRESS_KEY");
3823
+ const EVENT_SOURCE_OPTION_KEY = Symbol("SNAIL_EVENT_SOURCE_OPTION_KEY");
3824
+ const Sse = (path, options) => {
3825
+ return (target, propertyKey) => {
3826
+ Reflect.defineMetadata(
3827
+ EVENT_SOURCE_OPTION_KEY,
3828
+ {
3829
+ path,
3830
+ withCredentials: options == null ? void 0 : options.withCredentials
3831
+ },
3832
+ target,
3833
+ propertyKey
3834
+ );
3835
+ };
3836
+ };
3837
+ const EVENT_SOURCE_EVENTS_KEY = Symbol("EVENT_SOURCE_EVENTS_KEY");
3838
+ const SseEvent = (eventName, options) => {
3839
+ return (target, propertyKey) => {
3840
+ const events = Reflect.getMetadata(EVENT_SOURCE_EVENTS_KEY, target) || [];
3841
+ events.push({
3842
+ eventName: eventName ? eventName : "message",
3843
+ emit: target[propertyKey],
3844
+ options
3845
+ });
3846
+ Reflect.defineMetadata(EVENT_SOURCE_EVENTS_KEY, events, target);
3847
+ };
3848
+ };
3849
+ const EVENT_SOURCE_OPEN_KEY = Symbol("EVENT_SOURCE_OPEN_KEY");
3850
+ const OnSseOpen = () => {
3851
+ return (target, propertyKey) => {
3852
+ Reflect.defineMetadata(EVENT_SOURCE_OPEN_KEY, target[propertyKey], target);
3853
+ };
3854
+ };
3855
+ const EVENT_SOURCE_ERROR_KEY = Symbol("EVENT_SOURCE_ERROR_KEY");
3856
+ const OnSseError = () => {
3857
+ return (target, propertyKey) => {
3858
+ Reflect.defineMetadata(EVENT_SOURCE_ERROR_KEY, target[propertyKey], target);
3859
+ };
3860
+ };
3815
3861
  class Snail {
3816
3862
  constructor() {
3817
3863
  __publicField(this, "axiosInstance");
@@ -3820,6 +3866,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3820
3866
  __publicField(this, "cacheStorage");
3821
3867
  __publicField(this, "version");
3822
3868
  __publicField(this, "sourceMap", /* @__PURE__ */ new Map());
3869
+ __publicField(this, "eventSource");
3823
3870
  }
3824
3871
  registerStrategy(strategy) {
3825
3872
  this.strategies.push(strategy);
@@ -3883,9 +3930,17 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3883
3930
  return this.handleResponse(cachedResponse, true);
3884
3931
  }
3885
3932
  }
3933
+ const onUploadProgress = Reflect.getMetadata(
3934
+ UPLOAD_PROGRESS_KEY,
3935
+ target,
3936
+ propertyKey
3937
+ );
3886
3938
  try {
3887
3939
  enableLog && console.log("send request:", request);
3888
- const response = await this.axiosInstance(request);
3940
+ const response = await this.axiosInstance({
3941
+ ...request,
3942
+ onUploadProgress
3943
+ });
3889
3944
  hitSource !== null && this.setCache(request, response);
3890
3945
  this.expireCache(propertyKey);
3891
3946
  return this.handleResponse(response, false);
@@ -4037,6 +4092,15 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4037
4092
  });
4038
4093
  }
4039
4094
  }
4095
+ generateSseUrl(baseURL, url) {
4096
+ if (!baseURL && !url) {
4097
+ return "/";
4098
+ }
4099
+ if (!baseURL && url) {
4100
+ return `/${url}`;
4101
+ }
4102
+ return `${baseURL}/${url}`;
4103
+ }
4040
4104
  handleResponse(response, hitCache) {
4041
4105
  return {
4042
4106
  data: response.data,
@@ -4050,6 +4114,75 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4050
4114
  error
4051
4115
  };
4052
4116
  }
4117
+ createSse(constructor) {
4118
+ const instance = new constructor();
4119
+ const serverConfig = this.getServerConfig();
4120
+ const { baseURL, enableLog } = serverConfig;
4121
+ const apiConfig = this.getApiConfig(constructor);
4122
+ enableLog && console.log("ApiConfig:", apiConfig);
4123
+ this.version = apiConfig.version;
4124
+ return new Proxy(instance, {
4125
+ get: (target, propertyKey) => {
4126
+ enableLog && console.log("proxy:", target, "|", propertyKey);
4127
+ if (typeof propertyKey !== "string") return;
4128
+ const sseOption = Reflect.getMetadata(
4129
+ EVENT_SOURCE_OPTION_KEY,
4130
+ target,
4131
+ propertyKey
4132
+ );
4133
+ if (sseOption) {
4134
+ const url = sseOption.path == "" ? apiConfig.url : apiConfig.url + `/${sseOption.path}`;
4135
+ const { url: versionUrl } = this.applyVersion(
4136
+ { url },
4137
+ target,
4138
+ propertyKey
4139
+ );
4140
+ const sseUrl = this.generateSseUrl(baseURL, versionUrl || url);
4141
+ enableLog && console.log("sse-url:", sseUrl);
4142
+ const { eventSource, close } = this.initSse(sseUrl, sseOption);
4143
+ this.eventSource = eventSource;
4144
+ this.setSse(target);
4145
+ this.registerSseEvent(target);
4146
+ return () => {
4147
+ return { eventSource, close };
4148
+ };
4149
+ }
4150
+ }
4151
+ });
4152
+ }
4153
+ initSse(url, options) {
4154
+ const eventSource = new EventSource(url, {
4155
+ withCredentials: options.withCredentials ? options.withCredentials : false
4156
+ });
4157
+ console.log("EventSource:", eventSource);
4158
+ return {
4159
+ eventSource,
4160
+ close: eventSource.close
4161
+ };
4162
+ }
4163
+ setSse(target) {
4164
+ const onOpenFunc = Reflect.getMetadata(EVENT_SOURCE_OPEN_KEY, target);
4165
+ console.log("sse-proxy[open]:", onOpenFunc);
4166
+ if (typeof onOpenFunc == "function") {
4167
+ this.eventSource && (this.eventSource.onopen = onOpenFunc);
4168
+ }
4169
+ const onErrorFunc = Reflect.getMetadata(EVENT_SOURCE_ERROR_KEY, target);
4170
+ console.log("sse-proxy[error]:", onErrorFunc);
4171
+ if (typeof onErrorFunc == "function") {
4172
+ this.eventSource && (this.eventSource.onerror = onErrorFunc);
4173
+ }
4174
+ }
4175
+ registerSseEvent(target) {
4176
+ const eventSource = this.eventSource;
4177
+ if (!eventSource) return;
4178
+ const events = Reflect.getMetadata(
4179
+ EVENT_SOURCE_EVENTS_KEY,
4180
+ target
4181
+ );
4182
+ events.map((event) => {
4183
+ eventSource.addEventListener(event.eventName, event.emit, event.options);
4184
+ });
4185
+ }
4053
4186
  }
4054
4187
  exports2.Api = Api;
4055
4188
  exports2.Cache = Cache;
@@ -4058,14 +4191,19 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4058
4191
  exports2.Delete = Delete;
4059
4192
  exports2.Get = Get;
4060
4193
  exports2.Head = Head;
4194
+ exports2.OnSseError = OnSseError;
4195
+ exports2.OnSseOpen = OnSseOpen;
4061
4196
  exports2.Options = Options;
4062
4197
  exports2.Params = Params;
4063
4198
  exports2.Patch = Patch;
4064
4199
  exports2.Post = Post;
4065
4200
  exports2.Put = Put;
4201
+ exports2.RegisterSseEvent = RegisterSseEvent;
4066
4202
  exports2.RequestMethod = RequestMethod;
4067
4203
  exports2.Server = Server;
4068
4204
  exports2.Snail = Snail;
4205
+ exports2.Sse = Sse;
4206
+ exports2.SseEvent = SseEvent;
4069
4207
  exports2.Strategy = Strategy;
4070
4208
  exports2.UseStrategy = UseStrategy;
4071
4209
  exports2.Version = Version;
@@ -5,7 +5,7 @@ export type ApiResponse<T> = Promise<{
5
5
  export type ApiProxy<T, R extends {
6
6
  data: any;
7
7
  }> = {
8
- [K in keyof T]: T[K] extends (...args: infer A) => any ? <D = any, E = any>(...args: A) => ApiResponse<R & {
8
+ [K in keyof T]: T[K] extends (...args: infer A) => any ? <D = any>(...args: A) => ApiResponse<R & {
9
9
  data: D;
10
10
  }> : T[K];
11
11
  };
@@ -8,3 +8,4 @@ export * from "./cache.type";
8
8
  export * from "./response.data";
9
9
  export * from "./strategy";
10
10
  export * from "./apiProxy";
11
+ export * from "./sse";
@@ -0,0 +1,11 @@
1
+ export type SseProxy<T extends object> = {
2
+ [K in keyof T]: T[K] extends (...args: infer A) => any ? () => {
3
+ sourceEvent: EventSource;
4
+ close: Function;
5
+ } : T[K];
6
+ };
7
+ export declare class RegisterSseEvent {
8
+ eventName: string;
9
+ emit: (event: any) => any;
10
+ options: boolean | AddEventListenerOptions;
11
+ }
@@ -1,8 +1,7 @@
1
1
  import { VersioningOption } from "../typings";
2
- interface VersioningResult {
2
+ export interface VersioningResult {
3
3
  url?: string;
4
4
  headers?: Record<string, any>;
5
5
  params?: Record<string, any>;
6
6
  }
7
7
  export declare function applyVersioning(version: string, versioning: VersioningOption): VersioningResult;
8
- export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snail-js/api",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Http Request with Decorators Api, build on axios",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",