@snail-js/api 0.1.3 → 0.1.5

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,88 @@ 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((progressEvent: AxiosProgressEvent) => void)`
339
+
340
+ ### 下载进度装饰器`@DownloadProgress`
341
+
342
+ - `@DownloadProgress((progressEvent: AxiosProgressEvent) => void)`
343
+
344
+ ## Server Send Event 服务端推送
345
+
346
+ ### 创建 sse 端点
347
+
348
+ ```typescript
349
+ @Api("sse")
350
+ class ServerSend {
351
+ @Sse()
352
+ create() {}
353
+
354
+ @OnSseOpen()
355
+ handleOpen(event: Event) {
356
+ console.log("sse-open:", event);
357
+ }
358
+
359
+ @OnSseError()
360
+ handleError(event: Event) {
361
+ console.log("sse-error:", event);
362
+ }
363
+
364
+ // 处理默认message事件
365
+ @SseEvent()
366
+ handleEvent(event: MessageEvent) {
367
+ console.log("sse-event[message]:", event.data);
368
+ }
369
+
370
+ // 处理自定义名称事件
371
+ @SseEvent("chunk")
372
+ handleEvent(event: Event) {
373
+ console.log("sse-event[chunk]:", event);
374
+ }
375
+ }
376
+
377
+ export const Sse = Service.createSse(ServerSend);
378
+ ```
379
+
380
+ ### 服务端推送装饰器`@Sse`
381
+ - `@Sse(path:string,options:{withCredentials: boolean})`
382
+ - 创建一个服务端推送连接,当被装饰的方法调用时打开连接
383
+ - 被装饰的方法调用后会返回`{eventSource:EventSource,close:function}`
384
+ - eventSource: sse连接实例
385
+ - close: 关闭此sse连接的方法
386
+
387
+ ### 注册`onopen`装饰器`@OnSseOpen`
388
+ - 当`@Sse`装饰的方法被调用时,将`@OnSseOpen`装饰的方法注册为`@Sse`装饰的方法返回的`EventSource`实例`onopen`处理函数
389
+
390
+ ### 注册`onerror`装饰器`@OnSseError`
391
+ - 当`@Sse`装饰的方法被调用时,将`@OnSseError`装饰的方法注册为`@Sse`装饰的方法返回的`EventSource`实例`onerror`处理函数
392
+
393
+ ### 事件处理装饰器`@SseEvent`
394
+ - `@SseEvent(eventName?:string)`
395
+ - 未传入`eventName`,默认注册为`message`事件处理器
396
+ - 传入`eventName`,注册为对应名称的事件处理器
397
+
398
+
338
399
  ### 代码仓库
339
400
 
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)
401
+ <p>
402
+ <a href="https://gitee.com/limich/snail">
403
+ <img src="https://img.shields.io/badge/snail-js?style=flat&label=gitee&labelColor=F56C6C&link=https%3A%2F%2Fgitee.com%2Flimich%2Fsnail"></img>
404
+ </a>
405
+ </p>
406
+ <p>
407
+ <a href="https://github.com/limingchang/snail">
408
+ <img src="https://img.shields.io/badge/snail-js?style=flat&label=github&labelColor=F56C6C&link=https%3A%2F%2Fgihub.com%2Flimingchang%2Fsnail"></img>
409
+ </a>
410
+ </p>
342
411
 
343
412
  ### 作者
344
413
 
@@ -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,6 @@
1
+ import "reflect-metadata";
2
+ import { AxiosProgressEvent } from "axios";
3
+ export declare const UPLOAD_PROGRESS_KEY: unique symbol;
4
+ export declare const UploadProgress: (handler: (progressEvent: AxiosProgressEvent) => void) => (target: any, propertyKey: string) => void;
5
+ export declare const DOWNLOAD_PROGRESS_KEY: unique symbol;
6
+ export declare const DownloadProgress: (handler: (progressEvent: 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,46 @@ 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 DOWNLOAD_PROGRESS_KEY = Symbol("SNAIL_DOWNLOAD_PROGRESS_KEY");
3820
+ const EVENT_SOURCE_OPTION_KEY = Symbol("SNAIL_EVENT_SOURCE_OPTION_KEY");
3821
+ const Sse = (path, options) => {
3822
+ return (target, propertyKey) => {
3823
+ Reflect.defineMetadata(
3824
+ EVENT_SOURCE_OPTION_KEY,
3825
+ {
3826
+ path,
3827
+ withCredentials: options == null ? void 0 : options.withCredentials
3828
+ },
3829
+ target,
3830
+ propertyKey
3831
+ );
3832
+ };
3833
+ };
3834
+ const EVENT_SOURCE_EVENTS_KEY = Symbol("EVENT_SOURCE_EVENTS_KEY");
3835
+ const SseEvent = (eventName, options) => {
3836
+ return (target, propertyKey) => {
3837
+ const events = Reflect.getMetadata(EVENT_SOURCE_EVENTS_KEY, target) || [];
3838
+ events.push({
3839
+ eventName: eventName ? eventName : "message",
3840
+ emit: target[propertyKey],
3841
+ options
3842
+ });
3843
+ Reflect.defineMetadata(EVENT_SOURCE_EVENTS_KEY, events, target);
3844
+ };
3845
+ };
3846
+ const EVENT_SOURCE_OPEN_KEY = Symbol("EVENT_SOURCE_OPEN_KEY");
3847
+ const OnSseOpen = () => {
3848
+ return (target, propertyKey) => {
3849
+ Reflect.defineMetadata(EVENT_SOURCE_OPEN_KEY, target[propertyKey], target);
3850
+ };
3851
+ };
3852
+ const EVENT_SOURCE_ERROR_KEY = Symbol("EVENT_SOURCE_ERROR_KEY");
3853
+ const OnSseError = () => {
3854
+ return (target, propertyKey) => {
3855
+ Reflect.defineMetadata(EVENT_SOURCE_ERROR_KEY, target[propertyKey], target);
3856
+ };
3857
+ };
3811
3858
  class Snail {
3812
3859
  constructor() {
3813
3860
  __publicField(this, "axiosInstance");
@@ -3816,6 +3863,7 @@ class Snail {
3816
3863
  __publicField(this, "cacheStorage");
3817
3864
  __publicField(this, "version");
3818
3865
  __publicField(this, "sourceMap", /* @__PURE__ */ new Map());
3866
+ __publicField(this, "eventSource");
3819
3867
  }
3820
3868
  registerStrategy(strategy) {
3821
3869
  this.strategies.push(strategy);
@@ -3879,9 +3927,23 @@ class Snail {
3879
3927
  return this.handleResponse(cachedResponse, true);
3880
3928
  }
3881
3929
  }
3930
+ const onUploadProgress = Reflect.getMetadata(
3931
+ UPLOAD_PROGRESS_KEY,
3932
+ target,
3933
+ propertyKey
3934
+ );
3935
+ const onDownloadProgress = Reflect.getMetadata(
3936
+ DOWNLOAD_PROGRESS_KEY,
3937
+ target,
3938
+ propertyKey
3939
+ );
3882
3940
  try {
3883
3941
  enableLog && console.log("send request:", request);
3884
- const response = await this.axiosInstance(request);
3942
+ const response = await this.axiosInstance({
3943
+ ...request,
3944
+ onUploadProgress,
3945
+ onDownloadProgress
3946
+ });
3885
3947
  hitSource !== null && this.setCache(request, response);
3886
3948
  this.expireCache(propertyKey);
3887
3949
  return this.handleResponse(response, false);
@@ -4033,6 +4095,15 @@ class Snail {
4033
4095
  });
4034
4096
  }
4035
4097
  }
4098
+ generateSseUrl(baseURL, url) {
4099
+ if (!baseURL && !url) {
4100
+ return "/";
4101
+ }
4102
+ if (!baseURL && url) {
4103
+ return `/${url}`;
4104
+ }
4105
+ return `${baseURL}/${url}`;
4106
+ }
4036
4107
  handleResponse(response, hitCache) {
4037
4108
  return {
4038
4109
  data: response.data,
@@ -4046,6 +4117,75 @@ class Snail {
4046
4117
  error
4047
4118
  };
4048
4119
  }
4120
+ createSse(constructor) {
4121
+ const instance = new constructor();
4122
+ const serverConfig = this.getServerConfig();
4123
+ const { baseURL, enableLog } = serverConfig;
4124
+ const apiConfig = this.getApiConfig(constructor);
4125
+ enableLog && console.log("ApiConfig:", apiConfig);
4126
+ this.version = apiConfig.version;
4127
+ return new Proxy(instance, {
4128
+ get: (target, propertyKey) => {
4129
+ enableLog && console.log("proxy:", target, "|", propertyKey);
4130
+ if (typeof propertyKey !== "string") return;
4131
+ const sseOption = Reflect.getMetadata(
4132
+ EVENT_SOURCE_OPTION_KEY,
4133
+ target,
4134
+ propertyKey
4135
+ );
4136
+ if (sseOption) {
4137
+ const url = sseOption.path == "" ? apiConfig.url : apiConfig.url + `/${sseOption.path}`;
4138
+ const { url: versionUrl } = this.applyVersion(
4139
+ { url },
4140
+ target,
4141
+ propertyKey
4142
+ );
4143
+ const sseUrl = this.generateSseUrl(baseURL, versionUrl || url);
4144
+ enableLog && console.log("sse-url:", sseUrl);
4145
+ const { eventSource, close } = this.initSse(sseUrl, sseOption);
4146
+ this.eventSource = eventSource;
4147
+ this.setSse(target);
4148
+ this.registerSseEvent(target);
4149
+ return () => {
4150
+ return { eventSource, close };
4151
+ };
4152
+ }
4153
+ }
4154
+ });
4155
+ }
4156
+ initSse(url, options) {
4157
+ const eventSource = new EventSource(url, {
4158
+ withCredentials: options.withCredentials ? options.withCredentials : false
4159
+ });
4160
+ console.log("EventSource:", eventSource);
4161
+ return {
4162
+ eventSource,
4163
+ close: eventSource.close
4164
+ };
4165
+ }
4166
+ setSse(target) {
4167
+ const onOpenFunc = Reflect.getMetadata(EVENT_SOURCE_OPEN_KEY, target);
4168
+ console.log("sse-proxy[open]:", onOpenFunc);
4169
+ if (typeof onOpenFunc == "function") {
4170
+ this.eventSource && (this.eventSource.onopen = onOpenFunc);
4171
+ }
4172
+ const onErrorFunc = Reflect.getMetadata(EVENT_SOURCE_ERROR_KEY, target);
4173
+ console.log("sse-proxy[error]:", onErrorFunc);
4174
+ if (typeof onErrorFunc == "function") {
4175
+ this.eventSource && (this.eventSource.onerror = onErrorFunc);
4176
+ }
4177
+ }
4178
+ registerSseEvent(target) {
4179
+ const eventSource = this.eventSource;
4180
+ if (!eventSource) return;
4181
+ const events = Reflect.getMetadata(
4182
+ EVENT_SOURCE_EVENTS_KEY,
4183
+ target
4184
+ );
4185
+ events.map((event) => {
4186
+ eventSource.addEventListener(event.eventName, event.emit, event.options);
4187
+ });
4188
+ }
4049
4189
  }
4050
4190
  export {
4051
4191
  Api,
@@ -4055,14 +4195,19 @@ export {
4055
4195
  Delete,
4056
4196
  Get,
4057
4197
  Head,
4198
+ OnSseError,
4199
+ OnSseOpen,
4058
4200
  Options,
4059
4201
  Params,
4060
4202
  Patch,
4061
4203
  Post,
4062
4204
  Put,
4205
+ RegisterSseEvent,
4063
4206
  RequestMethod,
4064
4207
  Server,
4065
4208
  Snail,
4209
+ Sse,
4210
+ SseEvent,
4066
4211
  Strategy,
4067
4212
  UseStrategy,
4068
4213
  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,46 @@ 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 DOWNLOAD_PROGRESS_KEY = Symbol("SNAIL_DOWNLOAD_PROGRESS_KEY");
3824
+ const EVENT_SOURCE_OPTION_KEY = Symbol("SNAIL_EVENT_SOURCE_OPTION_KEY");
3825
+ const Sse = (path, options) => {
3826
+ return (target, propertyKey) => {
3827
+ Reflect.defineMetadata(
3828
+ EVENT_SOURCE_OPTION_KEY,
3829
+ {
3830
+ path,
3831
+ withCredentials: options == null ? void 0 : options.withCredentials
3832
+ },
3833
+ target,
3834
+ propertyKey
3835
+ );
3836
+ };
3837
+ };
3838
+ const EVENT_SOURCE_EVENTS_KEY = Symbol("EVENT_SOURCE_EVENTS_KEY");
3839
+ const SseEvent = (eventName, options) => {
3840
+ return (target, propertyKey) => {
3841
+ const events = Reflect.getMetadata(EVENT_SOURCE_EVENTS_KEY, target) || [];
3842
+ events.push({
3843
+ eventName: eventName ? eventName : "message",
3844
+ emit: target[propertyKey],
3845
+ options
3846
+ });
3847
+ Reflect.defineMetadata(EVENT_SOURCE_EVENTS_KEY, events, target);
3848
+ };
3849
+ };
3850
+ const EVENT_SOURCE_OPEN_KEY = Symbol("EVENT_SOURCE_OPEN_KEY");
3851
+ const OnSseOpen = () => {
3852
+ return (target, propertyKey) => {
3853
+ Reflect.defineMetadata(EVENT_SOURCE_OPEN_KEY, target[propertyKey], target);
3854
+ };
3855
+ };
3856
+ const EVENT_SOURCE_ERROR_KEY = Symbol("EVENT_SOURCE_ERROR_KEY");
3857
+ const OnSseError = () => {
3858
+ return (target, propertyKey) => {
3859
+ Reflect.defineMetadata(EVENT_SOURCE_ERROR_KEY, target[propertyKey], target);
3860
+ };
3861
+ };
3815
3862
  class Snail {
3816
3863
  constructor() {
3817
3864
  __publicField(this, "axiosInstance");
@@ -3820,6 +3867,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3820
3867
  __publicField(this, "cacheStorage");
3821
3868
  __publicField(this, "version");
3822
3869
  __publicField(this, "sourceMap", /* @__PURE__ */ new Map());
3870
+ __publicField(this, "eventSource");
3823
3871
  }
3824
3872
  registerStrategy(strategy) {
3825
3873
  this.strategies.push(strategy);
@@ -3883,9 +3931,23 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3883
3931
  return this.handleResponse(cachedResponse, true);
3884
3932
  }
3885
3933
  }
3934
+ const onUploadProgress = Reflect.getMetadata(
3935
+ UPLOAD_PROGRESS_KEY,
3936
+ target,
3937
+ propertyKey
3938
+ );
3939
+ const onDownloadProgress = Reflect.getMetadata(
3940
+ DOWNLOAD_PROGRESS_KEY,
3941
+ target,
3942
+ propertyKey
3943
+ );
3886
3944
  try {
3887
3945
  enableLog && console.log("send request:", request);
3888
- const response = await this.axiosInstance(request);
3946
+ const response = await this.axiosInstance({
3947
+ ...request,
3948
+ onUploadProgress,
3949
+ onDownloadProgress
3950
+ });
3889
3951
  hitSource !== null && this.setCache(request, response);
3890
3952
  this.expireCache(propertyKey);
3891
3953
  return this.handleResponse(response, false);
@@ -4037,6 +4099,15 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4037
4099
  });
4038
4100
  }
4039
4101
  }
4102
+ generateSseUrl(baseURL, url) {
4103
+ if (!baseURL && !url) {
4104
+ return "/";
4105
+ }
4106
+ if (!baseURL && url) {
4107
+ return `/${url}`;
4108
+ }
4109
+ return `${baseURL}/${url}`;
4110
+ }
4040
4111
  handleResponse(response, hitCache) {
4041
4112
  return {
4042
4113
  data: response.data,
@@ -4050,6 +4121,75 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4050
4121
  error
4051
4122
  };
4052
4123
  }
4124
+ createSse(constructor) {
4125
+ const instance = new constructor();
4126
+ const serverConfig = this.getServerConfig();
4127
+ const { baseURL, enableLog } = serverConfig;
4128
+ const apiConfig = this.getApiConfig(constructor);
4129
+ enableLog && console.log("ApiConfig:", apiConfig);
4130
+ this.version = apiConfig.version;
4131
+ return new Proxy(instance, {
4132
+ get: (target, propertyKey) => {
4133
+ enableLog && console.log("proxy:", target, "|", propertyKey);
4134
+ if (typeof propertyKey !== "string") return;
4135
+ const sseOption = Reflect.getMetadata(
4136
+ EVENT_SOURCE_OPTION_KEY,
4137
+ target,
4138
+ propertyKey
4139
+ );
4140
+ if (sseOption) {
4141
+ const url = sseOption.path == "" ? apiConfig.url : apiConfig.url + `/${sseOption.path}`;
4142
+ const { url: versionUrl } = this.applyVersion(
4143
+ { url },
4144
+ target,
4145
+ propertyKey
4146
+ );
4147
+ const sseUrl = this.generateSseUrl(baseURL, versionUrl || url);
4148
+ enableLog && console.log("sse-url:", sseUrl);
4149
+ const { eventSource, close } = this.initSse(sseUrl, sseOption);
4150
+ this.eventSource = eventSource;
4151
+ this.setSse(target);
4152
+ this.registerSseEvent(target);
4153
+ return () => {
4154
+ return { eventSource, close };
4155
+ };
4156
+ }
4157
+ }
4158
+ });
4159
+ }
4160
+ initSse(url, options) {
4161
+ const eventSource = new EventSource(url, {
4162
+ withCredentials: options.withCredentials ? options.withCredentials : false
4163
+ });
4164
+ console.log("EventSource:", eventSource);
4165
+ return {
4166
+ eventSource,
4167
+ close: eventSource.close
4168
+ };
4169
+ }
4170
+ setSse(target) {
4171
+ const onOpenFunc = Reflect.getMetadata(EVENT_SOURCE_OPEN_KEY, target);
4172
+ console.log("sse-proxy[open]:", onOpenFunc);
4173
+ if (typeof onOpenFunc == "function") {
4174
+ this.eventSource && (this.eventSource.onopen = onOpenFunc);
4175
+ }
4176
+ const onErrorFunc = Reflect.getMetadata(EVENT_SOURCE_ERROR_KEY, target);
4177
+ console.log("sse-proxy[error]:", onErrorFunc);
4178
+ if (typeof onErrorFunc == "function") {
4179
+ this.eventSource && (this.eventSource.onerror = onErrorFunc);
4180
+ }
4181
+ }
4182
+ registerSseEvent(target) {
4183
+ const eventSource = this.eventSource;
4184
+ if (!eventSource) return;
4185
+ const events = Reflect.getMetadata(
4186
+ EVENT_SOURCE_EVENTS_KEY,
4187
+ target
4188
+ );
4189
+ events.map((event) => {
4190
+ eventSource.addEventListener(event.eventName, event.emit, event.options);
4191
+ });
4192
+ }
4053
4193
  }
4054
4194
  exports2.Api = Api;
4055
4195
  exports2.Cache = Cache;
@@ -4058,14 +4198,19 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4058
4198
  exports2.Delete = Delete;
4059
4199
  exports2.Get = Get;
4060
4200
  exports2.Head = Head;
4201
+ exports2.OnSseError = OnSseError;
4202
+ exports2.OnSseOpen = OnSseOpen;
4061
4203
  exports2.Options = Options;
4062
4204
  exports2.Params = Params;
4063
4205
  exports2.Patch = Patch;
4064
4206
  exports2.Post = Post;
4065
4207
  exports2.Put = Put;
4208
+ exports2.RegisterSseEvent = RegisterSseEvent;
4066
4209
  exports2.RequestMethod = RequestMethod;
4067
4210
  exports2.Server = Server;
4068
4211
  exports2.Snail = Snail;
4212
+ exports2.Sse = Sse;
4213
+ exports2.SseEvent = SseEvent;
4069
4214
  exports2.Strategy = Strategy;
4070
4215
  exports2.UseStrategy = UseStrategy;
4071
4216
  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.5",
4
4
  "description": "Http Request with Decorators Api, build on axios",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",