@proteinjs/service 1.1.0 → 1.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/CHANGELOG.md CHANGED
@@ -3,6 +3,33 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [1.2.0](https://github.com/proteinjs/service/compare/@proteinjs/service@1.1.1...@proteinjs/service@1.2.0) (2025-02-06)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * fix typing for the debounce config ([62bb2d6](https://github.com/proteinjs/service/commit/62bb2d646e2c71f2bf5300364cd92bbd95911f21))
12
+
13
+
14
+ ### Features
15
+
16
+ * method specific debouncers. allows one service to use multiple debouncers for each method in the service. ([00fa7b2](https://github.com/proteinjs/service/commit/00fa7b23c4960ad9a006d4791e9f5a12f9ab6c89))
17
+
18
+
19
+
20
+
21
+
22
+ ## [1.1.1](https://github.com/proteinjs/service/compare/@proteinjs/service@1.1.0...@proteinjs/service@1.1.1) (2024-09-10)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * move retry logic into ServiceClient. send 400s in ServiceRouter if there is an error in the routing. ([742aa15](https://github.com/proteinjs/service/commit/742aa15ce505f115e94093fc96e6cac811aaf83e))
28
+
29
+
30
+
31
+
32
+
6
33
  # [1.1.0](https://github.com/proteinjs/service/compare/@proteinjs/service@1.0.32...@proteinjs/service@1.1.0) (2024-09-09)
7
34
 
8
35
 
@@ -10,6 +10,12 @@ type KeysWithoutService<T extends Service> = Diff<KeysWithoutIndexSignature<T>,
10
10
  type RetryConfig<T extends Service> = {
11
11
  [K in KeysWithoutService<T>]?: number;
12
12
  };
13
+ interface DebounceConfig {
14
+ waitTime: number;
15
+ }
16
+ type MethodDebounceConfig<T extends Service> = {
17
+ [K in KeysWithoutService<T>]?: DebounceConfig;
18
+ };
13
19
  export interface Service extends Loadable {
14
20
  serviceMetadata?: {
15
21
  auth?: {
@@ -36,9 +42,9 @@ export interface Service extends Loadable {
36
42
  * Create a factory that creates an instance of the Service. The Service instance is a
37
43
  * ServiceClient wrapped in the interface's api.
38
44
  * @param serviceInterfaceQualifiedName the package-qualified name of the service interface (ie. service-package-name/MyService)
39
- * @param debouncer optionally pass in an instance of debouncer if you want to limit service client calls
45
+ * @param debouncer pass in either a single debouncer instance or method-specific debounce configurations
40
46
  * @returns a function that creates a Service
41
47
  */
42
- export declare const serviceFactory: <T extends Service>(serviceInterfaceQualifiedName: string, debouncer?: Debouncer, retryConfig?: RetryConfig<T> | undefined) => () => T;
48
+ export declare const serviceFactory: <T extends Service>(serviceInterfaceQualifiedName: string, debouncer?: Debouncer | MethodDebounceConfig<T> | undefined, retryConfig?: RetryConfig<T> | undefined) => () => T;
43
49
  export {};
44
50
  //# sourceMappingURL=Service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Service.d.ts","sourceRoot":"","sources":["../../src/Service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAoB,MAAM,uBAAuB,CAAC;AAEnE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,KAAK,WAAW,CAAC,CAAC,IAAI;KACnB,CAAC,IAAI,MAAM,CAAC,IAAI,MAAM,SAAS,CAAC,GAAG,KAAK,GAAG,MAAM,SAAS,CAAC,GAAG,KAAK,GAAG,MAAM,SAAS,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAC3G,CAAC;AACF,KAAK,yBAAyB,CAAC,CAAC,IAAI,MAAM,WAAW,CAAC,CAAC,CAAC,CAAC;AAGzD,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;AAG1C,KAAK,kBAAkB,CAAC,CAAC,SAAS,OAAO,IAAI,IAAI,CAAC,yBAAyB,CAAC,CAAC,CAAC,EAAE,yBAAyB,CAAC,OAAO,CAAC,CAAC,CAAC;AAEpH,KAAK,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI;KACnC,CAAC,IAAI,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM;CACtC,CAAC;AAEF,MAAM,WAAW,OAAQ,SAAQ,QAAQ;IACvC,eAAe,CAAC,EAAE;QAChB,IAAI,CAAC,EAAE;YACL,2HAA2H;YAC3H,MAAM,CAAC,EAAE,OAAO,CAAC;YACjB,kIAAkI;YAClI,QAAQ,CAAC,EAAE,OAAO,CAAC;YACnB,8HAA8H;YAC9H,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;YACjB;;;;;eAKG;YACH,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC;SAC1D,CAAC;QACF,uFAAuF;QACvF,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,CAAC;IACF,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;CACzD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,qDACM,MAAM,cACzB,SAAS,sDAmCtB,CAAC"}
1
+ {"version":3,"file":"Service.d.ts","sourceRoot":"","sources":["../../src/Service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAoB,MAAM,uBAAuB,CAAC;AAEnE,OAAO,EAAE,SAAS,EAAgB,MAAM,iBAAiB,CAAC;AAE1D,KAAK,WAAW,CAAC,CAAC,IAAI;KACnB,CAAC,IAAI,MAAM,CAAC,IAAI,MAAM,SAAS,CAAC,GAAG,KAAK,GAAG,MAAM,SAAS,CAAC,GAAG,KAAK,GAAG,MAAM,SAAS,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAC3G,CAAC;AACF,KAAK,yBAAyB,CAAC,CAAC,IAAI,MAAM,WAAW,CAAC,CAAC,CAAC,CAAC;AAGzD,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;AAG1C,KAAK,kBAAkB,CAAC,CAAC,SAAS,OAAO,IAAI,IAAI,CAAC,yBAAyB,CAAC,CAAC,CAAC,EAAE,yBAAyB,CAAC,OAAO,CAAC,CAAC,CAAC;AAEpH,KAAK,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI;KACnC,CAAC,IAAI,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM;CACtC,CAAC;AAEF,UAAU,cAAc;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,oBAAoB,CAAC,CAAC,SAAS,OAAO,IAAI;KAC5C,CAAC,IAAI,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,cAAc;CAC9C,CAAC;AAEF,MAAM,WAAW,OAAQ,SAAQ,QAAQ;IACvC,eAAe,CAAC,EAAE;QAChB,IAAI,CAAC,EAAE;YACL,2HAA2H;YAC3H,MAAM,CAAC,EAAE,OAAO,CAAC;YACjB,kIAAkI;YAClI,QAAQ,CAAC,EAAE,OAAO,CAAC;YACnB,8HAA8H;YAC9H,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;YACjB;;;;;eAKG;YACH,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC;SAC1D,CAAC;QACF,uFAAuF;QACvF,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,CAAC;IACF,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;CACzD;AAaD;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,qDACM,MAAM,mHAkCtC,CAAC"}
@@ -1,108 +1,52 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __generator = (this && this.__generator) || function (thisArg, body) {
12
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
- function verb(n) { return function (v) { return step([n, v]); }; }
15
- function step(op) {
16
- if (f) throw new TypeError("Generator is already executing.");
17
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
- if (y = 0, t) op = [op[0] & 2, t.value];
20
- switch (op[0]) {
21
- case 0: case 1: t = op; break;
22
- case 4: _.label++; return { value: op[1], done: false };
23
- case 5: _.label++; y = op[1]; op = [0]; continue;
24
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
- default:
26
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
- if (t[2]) _.ops.pop();
31
- _.trys.pop(); continue;
32
- }
33
- op = body.call(thisArg, _);
34
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
- }
37
- };
38
2
  Object.defineProperty(exports, "__esModule", { value: true });
39
3
  exports.serviceFactory = void 0;
40
4
  var reflection_1 = require("@proteinjs/reflection");
41
5
  var ServiceClient_1 = require("./ServiceClient");
6
+ var util_1 = require("@proteinjs/util");
7
+ var debouncerMap = {};
8
+ /** Retrieve an existing debouncer from the map or make a new one.
9
+ * This allows the debouncer instance per method to remain the same across multiple service calls.
10
+ */
11
+ function getOrCreateDebouncer(methodName, waitTime) {
12
+ if (!debouncerMap[methodName]) {
13
+ debouncerMap[methodName] = new util_1.Debouncer(waitTime);
14
+ }
15
+ return debouncerMap[methodName];
16
+ }
42
17
  /**
43
18
  * Create a factory that creates an instance of the Service. The Service instance is a
44
19
  * ServiceClient wrapped in the interface's api.
45
20
  * @param serviceInterfaceQualifiedName the package-qualified name of the service interface (ie. service-package-name/MyService)
46
- * @param debouncer optionally pass in an instance of debouncer if you want to limit service client calls
21
+ * @param debouncer pass in either a single debouncer instance or method-specific debounce configurations
47
22
  * @returns a function that creates a Service
48
23
  */
49
24
  var serviceFactory = function (serviceInterfaceQualifiedName, debouncer, retryConfig) {
50
25
  return function () {
51
26
  var service = {};
52
27
  var serviceInterface = reflection_1.SourceRepository.get().interface(serviceInterfaceQualifiedName);
53
- var _loop_1 = function (method) {
28
+ for (var _i = 0, _a = serviceInterface.methods; _i < _a.length; _i++) {
29
+ var method = _a[_i];
54
30
  var servicePath = "/service/".concat(serviceInterface.qualifiedName, "/").concat(method.name);
55
- var serviceClient = new ServiceClient_1.ServiceClient(servicePath, method, debouncer);
56
- if (retryConfig && method.name in retryConfig) {
57
- var methodName = method.name;
58
- var maxRetries_1 = retryConfig[methodName];
59
- service[method.name] = function () {
60
- var args = [];
61
- for (var _i = 0; _i < arguments.length; _i++) {
62
- args[_i] = arguments[_i];
63
- }
64
- return __awaiter(void 0, void 0, void 0, function () {
65
- var lastError, attempt, error_1;
66
- return __generator(this, function (_a) {
67
- switch (_a.label) {
68
- case 0:
69
- attempt = 0;
70
- _a.label = 1;
71
- case 1:
72
- if (!(attempt < maxRetries_1)) return [3 /*break*/, 7];
73
- _a.label = 2;
74
- case 2:
75
- _a.trys.push([2, 3, , 6]);
76
- return [2 /*return*/, serviceClient.send.bind(serviceClient).apply(void 0, args)];
77
- case 3:
78
- error_1 = _a.sent();
79
- lastError = error_1;
80
- if (!(attempt < maxRetries_1 - 1)) return [3 /*break*/, 5];
81
- return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 1000); })];
82
- case 4:
83
- _a.sent(); // short wait before retrying
84
- _a.label = 5;
85
- case 5: return [3 /*break*/, 6];
86
- case 6:
87
- attempt++;
88
- return [3 /*break*/, 1];
89
- case 7:
90
- if (lastError) {
91
- throw lastError;
92
- }
93
- return [2 /*return*/];
94
- }
95
- });
96
- });
97
- };
31
+ var methodName = method.name;
32
+ var retryCount = 0;
33
+ if (retryConfig && methodName in retryConfig) {
34
+ retryCount = retryConfig[methodName];
98
35
  }
99
- else {
100
- service[method.name] = serviceClient.send.bind(serviceClient);
36
+ var methodDebouncer = void 0;
37
+ if (debouncer) {
38
+ if ((0, util_1.isInstanceOf)(debouncer, util_1.Debouncer)) {
39
+ methodDebouncer = debouncer;
40
+ }
41
+ else if (methodName in debouncer) {
42
+ var methodConfig = debouncer[methodName];
43
+ if (methodConfig) {
44
+ methodDebouncer = getOrCreateDebouncer(methodName, methodConfig.waitTime);
45
+ }
46
+ }
101
47
  }
102
- };
103
- for (var _i = 0, _a = serviceInterface.methods; _i < _a.length; _i++) {
104
- var method = _a[_i];
105
- _loop_1(method);
48
+ var serviceClient = new ServiceClient_1.ServiceClient(servicePath, method, methodDebouncer, retryCount);
49
+ service[methodName] = serviceClient.send.bind(serviceClient);
106
50
  }
107
51
  return service;
108
52
  };
@@ -1 +1 @@
1
- {"version":3,"file":"Service.js","sourceRoot":"","sources":["../../src/Service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,oDAAmE;AACnE,iDAAgD;AAyChD;;;;;;GAMG;AACI,IAAM,cAAc,GAAG,UAC5B,6BAAqC,EACrC,SAAqB,EACrB,WAA4B;IAE5B,OAAO;QACL,IAAM,OAAO,GAAQ,EAAE,CAAC;QACxB,IAAM,gBAAgB,GAAG,6BAAgB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC;gCAC9E,MAAM;YACf,IAAM,WAAW,GAAG,mBAAY,gBAAgB,CAAC,aAAa,cAAI,MAAM,CAAC,IAAI,CAAE,CAAC;YAChF,IAAM,aAAa,GAAG,IAAI,6BAAa,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;YACxE,IAAI,WAAW,IAAI,MAAM,CAAC,IAAI,IAAI,WAAW,EAAE;gBAC7C,IAAM,UAAU,GAAG,MAAM,CAAC,IAA6B,CAAC;gBACxD,IAAM,YAAU,GAAG,WAAW,CAAC,UAAU,CAAE,CAAC;gBAC5C,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG;oBAAO,cAAc;yBAAd,UAAc,EAAd,qBAAc,EAAd,IAAc;wBAAd,yBAAc;;;;;;;oCAEjC,OAAO,GAAG,CAAC;;;yCAAE,CAAA,OAAO,GAAG,YAAU,CAAA;;;;oCAEtC,sBAAO,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,eAAI,IAAI,GAAE;;;oCAEvD,SAAS,GAAG,OAAK,CAAC;yCACd,CAAA,OAAO,GAAG,YAAU,GAAG,CAAC,CAAA,EAAxB,wBAAwB;oCAC1B,qBAAM,IAAI,OAAO,CAAC,UAAC,OAAO,IAAK,OAAA,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,EAAzB,CAAyB,CAAC,EAAA;;oCAAzD,SAAyD,CAAC,CAAC,6BAA6B;;;;oCANlD,OAAO,EAAE,CAAA;;;oCAUrD,IAAI,SAAS,EAAE;wCACb,MAAM,SAAS,CAAC;qCACjB;;;;;iBACF,CAAC;aACH;iBAAM;gBACL,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;aAC/D;;QAxBH,KAAqB,UAAwB,EAAxB,KAAA,gBAAgB,CAAC,OAAO,EAAxB,cAAwB,EAAxB,IAAwB;YAAxC,IAAM,MAAM,SAAA;oBAAN,MAAM;SAyBhB;QAED,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;AACJ,CAAC,CAAC;AArCW,QAAA,cAAc,kBAqCzB"}
1
+ {"version":3,"file":"Service.js","sourceRoot":"","sources":["../../src/Service.ts"],"names":[],"mappings":";;;AACA,oDAAmE;AACnE,iDAAgD;AAChD,wCAA0D;AAgD1D,IAAM,YAAY,GAAiC,EAAE,CAAC;AACtD;;GAEG;AACH,SAAS,oBAAoB,CAAC,UAAkB,EAAE,QAAgB;IAChE,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE;QAC7B,YAAY,CAAC,UAAU,CAAC,GAAG,IAAI,gBAAS,CAAC,QAAQ,CAAC,CAAC;KACpD;IACD,OAAO,YAAY,CAAC,UAAU,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;GAMG;AACI,IAAM,cAAc,GAAG,UAC5B,6BAAqC,EACrC,SAA+C,EAC/C,WAA4B;IAE5B,OAAO;QACL,IAAM,OAAO,GAAQ,EAAE,CAAC;QACxB,IAAM,gBAAgB,GAAG,6BAAgB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC;QACzF,KAAqB,UAAwB,EAAxB,KAAA,gBAAgB,CAAC,OAAO,EAAxB,cAAwB,EAAxB,IAAwB,EAAE;YAA1C,IAAM,MAAM,SAAA;YACf,IAAM,WAAW,GAAG,mBAAY,gBAAgB,CAAC,aAAa,cAAI,MAAM,CAAC,IAAI,CAAE,CAAC;YAChF,IAAM,UAAU,GAAG,MAAM,CAAC,IAA6B,CAAC;YAExD,IAAI,UAAU,GAAG,CAAC,CAAC;YACnB,IAAI,WAAW,IAAI,UAAU,IAAI,WAAW,EAAE;gBAC5C,UAAU,GAAG,WAAW,CAAC,UAAU,CAAE,CAAC;aACvC;YAED,IAAI,eAAe,SAAuB,CAAC;YAC3C,IAAI,SAAS,EAAE;gBACb,IAAI,IAAA,mBAAY,EAAC,SAAS,EAAE,gBAAS,CAAC,EAAE;oBACtC,eAAe,GAAG,SAAsB,CAAC;iBAC1C;qBAAM,IAAI,UAAU,IAAI,SAAS,EAAE;oBAClC,IAAM,YAAY,GAAI,SAAqC,CAAC,UAAU,CAAC,CAAC;oBACxE,IAAI,YAAY,EAAE;wBAChB,eAAe,GAAG,oBAAoB,CAAC,UAAoB,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;qBACrF;iBACF;aACF;YAED,IAAM,aAAa,GAAG,IAAI,6BAAa,CAAC,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE,UAAU,CAAC,CAAC;YAC1F,OAAO,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;SAC9D;QAED,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;AACJ,CAAC,CAAC;AAnCW,QAAA,cAAc,kBAmCzB"}
@@ -4,8 +4,9 @@ export declare class ServiceClient {
4
4
  private servicePath;
5
5
  private serviceMethod;
6
6
  private debouncer?;
7
+ private retryCount;
7
8
  private static requestCounter;
8
- constructor(servicePath: string, serviceMethod: Method, debouncer?: Debouncer | undefined);
9
+ constructor(servicePath: string, serviceMethod: Method, debouncer?: Debouncer | undefined, retryCount?: number);
9
10
  send(...args: any[]): Promise<any>;
10
11
  private _send;
11
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ServiceClient.d.ts","sourceRoot":"","sources":["../../src/ServiceClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,qBAAa,aAAa;IAItB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,SAAS,CAAC;IALpB,OAAO,CAAC,MAAM,CAAC,cAAc,CAAK;gBAGxB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,SAAS,CAAC,uBAAW;IAGzB,IAAI,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;YA0B1B,KAAK;CAsBpB"}
1
+ {"version":3,"file":"ServiceClient.d.ts","sourceRoot":"","sources":["../../src/ServiceClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,qBAAa,aAAa;IAItB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,SAAS,CAAC;IAClB,OAAO,CAAC,UAAU;IANpB,OAAO,CAAC,MAAM,CAAC,cAAc,CAAK;gBAGxB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,SAAS,CAAC,uBAAW,EACrB,UAAU,GAAE,MAAU;IAG1B,IAAI,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;YA0C1B,KAAK;CAsBpB"}
@@ -40,10 +40,12 @@ exports.ServiceClient = void 0;
40
40
  var serializer_1 = require("@proteinjs/serializer");
41
41
  var isVoidReturnType_1 = require("./isVoidReturnType");
42
42
  var ServiceClient = /** @class */ (function () {
43
- function ServiceClient(servicePath, serviceMethod, debouncer) {
43
+ function ServiceClient(servicePath, serviceMethod, debouncer, retryCount) {
44
+ if (retryCount === void 0) { retryCount = 0; }
44
45
  this.servicePath = servicePath;
45
46
  this.serviceMethod = serviceMethod;
46
47
  this.debouncer = debouncer;
48
+ this.retryCount = retryCount;
47
49
  }
48
50
  ServiceClient.prototype.send = function () {
49
51
  var args = [];
@@ -51,7 +53,7 @@ var ServiceClient = /** @class */ (function () {
51
53
  args[_i] = arguments[_i];
52
54
  }
53
55
  return __awaiter(this, void 0, void 0, function () {
54
- var sendRequest;
56
+ var sendRequest, executeWithRetry, executeRequest;
55
57
  var _this = this;
56
58
  return __generator(this, function (_a) {
57
59
  sendRequest = function () { return __awaiter(_this, void 0, void 0, function () {
@@ -76,11 +78,43 @@ var ServiceClient = /** @class */ (function () {
76
78
  }
77
79
  });
78
80
  }); };
81
+ executeWithRetry = function (fn) { return __awaiter(_this, void 0, void 0, function () {
82
+ var maxAttempts, attempt, error_1;
83
+ return __generator(this, function (_a) {
84
+ switch (_a.label) {
85
+ case 0:
86
+ maxAttempts = 1 + this.retryCount;
87
+ attempt = 0;
88
+ _a.label = 1;
89
+ case 1:
90
+ if (!(attempt < maxAttempts)) return [3 /*break*/, 7];
91
+ _a.label = 2;
92
+ case 2:
93
+ _a.trys.push([2, 4, , 6]);
94
+ return [4 /*yield*/, fn()];
95
+ case 3: return [2 /*return*/, _a.sent()];
96
+ case 4:
97
+ error_1 = _a.sent();
98
+ if (attempt === maxAttempts - 1) {
99
+ throw error_1;
100
+ }
101
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 1000); })];
102
+ case 5:
103
+ _a.sent(); // 1 second delay before retry
104
+ return [3 /*break*/, 6];
105
+ case 6:
106
+ attempt++;
107
+ return [3 /*break*/, 1];
108
+ case 7: return [2 /*return*/];
109
+ }
110
+ });
111
+ }); };
112
+ executeRequest = this.retryCount > 0 ? executeWithRetry : function (fn) { return fn(); };
79
113
  if (this.debouncer) {
80
- this.debouncer.debounce(sendRequest);
114
+ return [2 /*return*/, this.debouncer.debounce(function () { return executeRequest(sendRequest); })];
81
115
  }
82
116
  else {
83
- return [2 /*return*/, sendRequest()];
117
+ return [2 /*return*/, executeRequest(sendRequest)];
84
118
  }
85
119
  return [2 /*return*/];
86
120
  });
@@ -1 +1 @@
1
- {"version":3,"file":"ServiceClient.js","sourceRoot":"","sources":["../../src/ServiceClient.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,oDAAmD;AAEnD,uDAAsD;AAEtD;IAGE,uBACU,WAAmB,EACnB,aAAqB,EACrB,SAAqB;QAFrB,gBAAW,GAAX,WAAW,CAAQ;QACnB,kBAAa,GAAb,aAAa,CAAQ;QACrB,cAAS,GAAT,SAAS,CAAY;IAC5B,CAAC;IAEE,4BAAI,GAAV;QAAW,cAAc;aAAd,UAAc,EAAd,qBAAc,EAAd,IAAc;YAAd,yBAAc;;;;;;gBACjB,WAAW,GAAG;;;;;gCACZ,cAAc,GAAG,uBAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;gCAC5C,aAAa,GAAG,aAAa,CAAC,cAAc,CAAC;gCACnD,aAAa,CAAC,cAAc,EAAE,CAAC;gCAC/B,OAAO,CAAC,cAAc,CAAC,YAAK,aAAa,wCAA8B,IAAI,CAAC,WAAW,YAAS,CAAC,CAAC;gCAClG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gCAClB,OAAO,CAAC,QAAQ,EAAE,CAAC;gCACM,qBAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,EAAA;;gCAArE,gBAAgB,GAAG,SAAkD;gCACrE,kBAAkB,GAAG,uBAAU,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAC;gCACpE,OAAO,CAAC,cAAc,CACpB,YAAK,aAAa,0CAAgC,IAAI,CAAC,WAAW,sBAAY,IAAA,mCAAgB,EAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAE,CACtI,CAAC;gCACF,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;gCAChC,OAAO,CAAC,QAAQ,EAAE,CAAC;gCAEnB,sBAAO,kBAAkB,EAAC;;;qBAC3B,CAAC;gBAEF,IAAI,IAAI,CAAC,SAAS,EAAE;oBAClB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;iBACtC;qBAAM;oBACL,sBAAO,WAAW,EAAE,EAAC;iBACtB;;;;KACF;IAEa,6BAAK,GAAnB,UAAoB,WAAmB,EAAE,cAAsB;;;;;;wBACvD,OAAO,GAAG,IAAI,OAAO,CAAC,WAAW,EAAE;4BACvC,MAAM,EAAE,MAAM;4BACd,IAAI,EAAE,cAAc;4BACpB,QAAQ,EAAE,QAAQ;4BAClB,WAAW,EAAE,aAAa;4BAC1B,OAAO,EAAE;gCACP,cAAc,EAAE,kBAAkB;6BACnC;yBACF,CAAC,CAAC;wBACc,qBAAM,KAAK,CAAC,OAAO,CAAC,EAAA;;wBAA/B,QAAQ,GAAG,SAAoB;wBACrC,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE;4BAC1B,MAAM,IAAI,KAAK,CAAC,6CAAsC,WAAW,sBAAY,QAAQ,CAAC,UAAU,CAAE,CAAC,CAAC;yBACrG;wBAEY,qBAAM,QAAQ,CAAC,IAAI,EAAE,EAAA;;wBAA5B,IAAI,GAAG,SAAqB;wBAClC,IAAI,IAAI,CAAC,KAAK,EAAE;4BACd,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;yBAC7B;wBAED,sBAAO,IAAI,CAAC,gBAAgB,EAAC;;;;KAC9B;IAvDc,4BAAc,GAAG,CAAC,CAAC;IAwDpC,oBAAC;CAAA,AAzDD,IAyDC;AAzDY,sCAAa"}
1
+ {"version":3,"file":"ServiceClient.js","sourceRoot":"","sources":["../../src/ServiceClient.ts"],"names":[],"mappings":"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"}
@@ -80,7 +80,7 @@ var ServiceRouter = /** @class */ (function () {
80
80
  if (!serviceExecutor) {
81
81
  error = "Unable to find service matching path: ".concat(request.path);
82
82
  this.logger.error({ message: error });
83
- response.send({ error: error });
83
+ response.status(404).send({ error: error });
84
84
  return [2 /*return*/];
85
85
  }
86
86
  _a.label = 1;
@@ -98,7 +98,7 @@ var ServiceRouter = /** @class */ (function () {
98
98
  this.logger.error({ error: error_1 });
99
99
  errorMessage = 'Internal server error';
100
100
  }
101
- response.send({ error: errorMessage });
101
+ response.status(400).send({ error: errorMessage });
102
102
  return [3 /*break*/, 4];
103
103
  case 4: return [2 /*return*/];
104
104
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ServiceRouter.js","sourceRoot":"","sources":["../../src/ServiceRouter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ServiceRouter.js","sourceRoot":"","sources":["../../src/ServiceRouter.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proteinjs/service",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Service api",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -43,5 +43,5 @@
43
43
  },
44
44
  "main": "./dist/generated/index.js",
45
45
  "types": "./dist/generated/index.d.ts",
46
- "gitHead": "22d0bff91a32a3e34b35d6985a0ce5863270757d"
46
+ "gitHead": "3de027b69d80594ca40d10c2069b6ec1ef119f89"
47
47
  }
package/src/Service.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { SerializableFunction, NotFunction } from '@proteinjs/serializer';
2
2
  import { Loadable, SourceRepository } from '@proteinjs/reflection';
3
3
  import { ServiceClient } from './ServiceClient';
4
- import { Debouncer } from '@proteinjs/util';
4
+ import { Debouncer, isInstanceOf } from '@proteinjs/util';
5
5
 
6
6
  type RemoveIndex<T> = {
7
7
  [K in keyof T as string extends K ? never : number extends K ? never : symbol extends K ? never : K]: T[K];
@@ -18,6 +18,14 @@ type RetryConfig<T extends Service> = {
18
18
  [K in KeysWithoutService<T>]?: number;
19
19
  };
20
20
 
21
+ interface DebounceConfig {
22
+ waitTime: number;
23
+ }
24
+
25
+ type MethodDebounceConfig<T extends Service> = {
26
+ [K in KeysWithoutService<T>]?: DebounceConfig;
27
+ };
28
+
21
29
  export interface Service extends Loadable {
22
30
  serviceMetadata?: {
23
31
  auth?: {
@@ -41,16 +49,27 @@ export interface Service extends Loadable {
41
49
  [prop: string]: SerializableFunction | NotFunction<any>;
42
50
  }
43
51
 
52
+ const debouncerMap: { [key: string]: Debouncer } = {};
53
+ /** Retrieve an existing debouncer from the map or make a new one.
54
+ * This allows the debouncer instance per method to remain the same across multiple service calls.
55
+ */
56
+ function getOrCreateDebouncer(methodName: string, waitTime: number): Debouncer {
57
+ if (!debouncerMap[methodName]) {
58
+ debouncerMap[methodName] = new Debouncer(waitTime);
59
+ }
60
+ return debouncerMap[methodName];
61
+ }
62
+
44
63
  /**
45
64
  * Create a factory that creates an instance of the Service. The Service instance is a
46
65
  * ServiceClient wrapped in the interface's api.
47
66
  * @param serviceInterfaceQualifiedName the package-qualified name of the service interface (ie. service-package-name/MyService)
48
- * @param debouncer optionally pass in an instance of debouncer if you want to limit service client calls
67
+ * @param debouncer pass in either a single debouncer instance or method-specific debounce configurations
49
68
  * @returns a function that creates a Service
50
69
  */
51
70
  export const serviceFactory = <T extends Service>(
52
71
  serviceInterfaceQualifiedName: string,
53
- debouncer?: Debouncer,
72
+ debouncer?: MethodDebounceConfig<T> | Debouncer,
54
73
  retryConfig?: RetryConfig<T>
55
74
  ): (() => T) => {
56
75
  return () => {
@@ -58,29 +77,27 @@ export const serviceFactory = <T extends Service>(
58
77
  const serviceInterface = SourceRepository.get().interface(serviceInterfaceQualifiedName);
59
78
  for (const method of serviceInterface.methods) {
60
79
  const servicePath = `/service/${serviceInterface.qualifiedName}/${method.name}`;
61
- const serviceClient = new ServiceClient(servicePath, method, debouncer);
62
- if (retryConfig && method.name in retryConfig) {
63
- const methodName = method.name as KeysWithoutService<T>;
64
- const maxRetries = retryConfig[methodName]!;
65
- service[method.name] = async (...args: any[]) => {
66
- let lastError;
67
- for (let attempt = 0; attempt < maxRetries; attempt++) {
68
- try {
69
- return serviceClient.send.bind(serviceClient)(...args);
70
- } catch (error) {
71
- lastError = error;
72
- if (attempt < maxRetries - 1) {
73
- await new Promise((resolve) => setTimeout(resolve, 1000)); // short wait before retrying
74
- }
75
- }
76
- }
77
- if (lastError) {
78
- throw lastError;
80
+ const methodName = method.name as KeysWithoutService<T>;
81
+
82
+ let retryCount = 0;
83
+ if (retryConfig && methodName in retryConfig) {
84
+ retryCount = retryConfig[methodName]!;
85
+ }
86
+
87
+ let methodDebouncer: Debouncer | undefined;
88
+ if (debouncer) {
89
+ if (isInstanceOf(debouncer, Debouncer)) {
90
+ methodDebouncer = debouncer as Debouncer;
91
+ } else if (methodName in debouncer) {
92
+ const methodConfig = (debouncer as MethodDebounceConfig<T>)[methodName];
93
+ if (methodConfig) {
94
+ methodDebouncer = getOrCreateDebouncer(methodName as string, methodConfig.waitTime);
79
95
  }
80
- };
81
- } else {
82
- service[method.name] = serviceClient.send.bind(serviceClient);
96
+ }
83
97
  }
98
+
99
+ const serviceClient = new ServiceClient(servicePath, method, methodDebouncer, retryCount);
100
+ service[methodName] = serviceClient.send.bind(serviceClient);
84
101
  }
85
102
 
86
103
  return service;
@@ -9,7 +9,8 @@ export class ServiceClient {
9
9
  constructor(
10
10
  private servicePath: string,
11
11
  private serviceMethod: Method,
12
- private debouncer?: Debouncer
12
+ private debouncer?: Debouncer,
13
+ private retryCount: number = 0
13
14
  ) {}
14
15
 
15
16
  async send(...args: any[]): Promise<any> {
@@ -31,10 +32,26 @@ export class ServiceClient {
31
32
  return deserializedReturn;
32
33
  };
33
34
 
35
+ const executeWithRetry = async (fn: () => Promise<any>) => {
36
+ const maxAttempts = 1 + this.retryCount;
37
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
38
+ try {
39
+ return await fn();
40
+ } catch (error) {
41
+ if (attempt === maxAttempts - 1) {
42
+ throw error;
43
+ }
44
+ await new Promise((resolve) => setTimeout(resolve, 1000)); // 1 second delay before retry
45
+ }
46
+ }
47
+ };
48
+
49
+ const executeRequest = this.retryCount > 0 ? executeWithRetry : (fn: () => Promise<any>) => fn();
50
+
34
51
  if (this.debouncer) {
35
- this.debouncer.debounce(sendRequest);
52
+ return this.debouncer.debounce(() => executeRequest(sendRequest));
36
53
  } else {
37
- return sendRequest();
54
+ return executeRequest(sendRequest);
38
55
  }
39
56
  }
40
57
 
@@ -43,7 +43,7 @@ export class ServiceRouter implements Route {
43
43
  if (!serviceExecutor) {
44
44
  const error = `Unable to find service matching path: ${request.path}`;
45
45
  this.logger.error({ message: error });
46
- response.send({ error });
46
+ response.status(404).send({ error });
47
47
  return;
48
48
  }
49
49
 
@@ -56,7 +56,7 @@ export class ServiceRouter implements Route {
56
56
  this.logger.error({ error });
57
57
  errorMessage = 'Internal server error';
58
58
  }
59
- response.send({ error: errorMessage });
59
+ response.status(400).send({ error: errorMessage });
60
60
  }
61
61
  }
62
62
  }