-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
Copy pathmethods.js
executable file
·126 lines (85 loc) · 3.5 KB
/
methods.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
'use strict';
const Boom = require('@hapi/boom');
const Hoek = require('@hapi/hoek');
const Config = require('./config');
const internals = {
methodNameRx: /^[_$a-zA-Z][$\w]*(?:\.[_$a-zA-Z][$\w]*)*$/
};
exports = module.exports = internals.Methods = class {
methods = {};
#core = null;
constructor(core) {
this.#core = core;
}
add(name, method, options, realm) {
if (typeof name !== 'object') {
return this._add(name, method, options, realm);
}
// {} or [{}, {}]
const items = [].concat(name);
for (let item of items) {
item = Config.apply('methodObject', item);
this._add(item.name, item.method, item.options ?? {}, realm);
}
}
_add(name, method, options, realm) {
Hoek.assert(typeof method === 'function', 'method must be a function');
Hoek.assert(typeof name === 'string', 'name must be a string');
Hoek.assert(name.match(internals.methodNameRx), 'Invalid name:', name);
Hoek.assert(!Hoek.reach(this.methods, name, { functions: false }), 'Server method function name already exists:', name);
options = Config.apply('method', options, name);
const settings = Hoek.clone(options, { shallow: ['bind'] });
settings.generateKey = settings.generateKey ?? internals.generateKey;
const bind = settings.bind ?? realm.settings.bind ?? null;
const bound = !bind ? method : (...args) => method.apply(bind, args);
// Not cached
if (!settings.cache) {
return this._assign(name, bound);
}
// Cached
Hoek.assert(!settings.cache.generateFunc, 'Cannot set generateFunc with method caching:', name);
Hoek.assert(settings.cache.generateTimeout !== undefined, 'Method caching requires a timeout value in generateTimeout:', name);
settings.cache.generateFunc = (id, flags) => bound(...id.args, flags);
const cache = this.#core._cachePolicy(settings.cache, '#' + name);
const func = function (...args) {
const key = settings.generateKey.apply(bind, args);
if (typeof key !== 'string') {
return Promise.reject(Boom.badImplementation('Invalid method key when invoking: ' + name, { name, args }));
}
return cache.get({ id: key, args });
};
func.cache = {
drop: function (...args) {
const key = settings.generateKey.apply(bind, args);
if (typeof key !== 'string') {
return Promise.reject(Boom.badImplementation('Invalid method key when invoking: ' + name, { name, args }));
}
return cache.drop(key);
},
stats: cache.stats
};
this._assign(name, func, func);
}
_assign(name, method) {
const path = name.split('.');
let ref = this.methods;
for (let i = 0; i < path.length; ++i) {
if (!ref[path[i]]) {
ref[path[i]] = (i + 1 === path.length ? method : {});
}
ref = ref[path[i]];
}
}
};
internals.supportedArgs = ['string', 'number', 'boolean'];
internals.generateKey = function (...args) {
let key = '';
for (let i = 0; i < args.length; ++i) {
const arg = args[i];
if (!internals.supportedArgs.includes(typeof arg)) {
return null;
}
key = key + (i ? ':' : '') + encodeURIComponent(arg.toString());
}
return key;
};