用工小程序前端代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

259 lines
6.4 KiB

7 months ago
  1. 'use strict';
  2. const EventEmitter = require('events');
  3. const JSONB = require('json-buffer');
  4. const loadStore = options => {
  5. const adapters = {
  6. redis: '@keyv/redis',
  7. rediss: '@keyv/redis',
  8. mongodb: '@keyv/mongo',
  9. mongo: '@keyv/mongo',
  10. sqlite: '@keyv/sqlite',
  11. postgresql: '@keyv/postgres',
  12. postgres: '@keyv/postgres',
  13. mysql: '@keyv/mysql',
  14. etcd: '@keyv/etcd',
  15. offline: '@keyv/offline',
  16. tiered: '@keyv/tiered',
  17. };
  18. if (options.adapter || options.uri) {
  19. const adapter = options.adapter || /^[^:+]*/.exec(options.uri)[0];
  20. return new (require(adapters[adapter]))(options);
  21. }
  22. return new Map();
  23. };
  24. const iterableAdapters = [
  25. 'sqlite',
  26. 'postgres',
  27. 'mysql',
  28. 'mongo',
  29. 'redis',
  30. 'tiered',
  31. ];
  32. class Keyv extends EventEmitter {
  33. constructor(uri, {emitErrors = true, ...options} = {}) {
  34. super();
  35. this.opts = {
  36. namespace: 'keyv',
  37. serialize: JSONB.stringify,
  38. deserialize: JSONB.parse,
  39. ...((typeof uri === 'string') ? {uri} : uri),
  40. ...options,
  41. };
  42. if (!this.opts.store) {
  43. const adapterOptions = {...this.opts};
  44. this.opts.store = loadStore(adapterOptions);
  45. }
  46. if (this.opts.compression) {
  47. const compression = this.opts.compression;
  48. this.opts.serialize = compression.serialize.bind(compression);
  49. this.opts.deserialize = compression.deserialize.bind(compression);
  50. }
  51. if (typeof this.opts.store.on === 'function' && emitErrors) {
  52. this.opts.store.on('error', error => this.emit('error', error));
  53. }
  54. this.opts.store.namespace = this.opts.namespace;
  55. const generateIterator = iterator => async function * () {
  56. for await (const [key, raw] of typeof iterator === 'function'
  57. ? iterator(this.opts.store.namespace)
  58. : iterator) {
  59. const data = await this.opts.deserialize(raw);
  60. if (this.opts.store.namespace && !key.includes(this.opts.store.namespace)) {
  61. continue;
  62. }
  63. if (typeof data.expires === 'number' && Date.now() > data.expires) {
  64. this.delete(key);
  65. continue;
  66. }
  67. yield [this._getKeyUnprefix(key), data.value];
  68. }
  69. };
  70. // Attach iterators
  71. if (typeof this.opts.store[Symbol.iterator] === 'function' && this.opts.store instanceof Map) {
  72. this.iterator = generateIterator(this.opts.store);
  73. } else if (typeof this.opts.store.iterator === 'function' && this.opts.store.opts
  74. && this._checkIterableAdaptar()) {
  75. this.iterator = generateIterator(this.opts.store.iterator.bind(this.opts.store));
  76. }
  77. }
  78. _checkIterableAdaptar() {
  79. return iterableAdapters.includes(this.opts.store.opts.dialect)
  80. || iterableAdapters.findIndex(element => this.opts.store.opts.url.includes(element)) >= 0;
  81. }
  82. _getKeyPrefix(key) {
  83. return `${this.opts.namespace}:${key}`;
  84. }
  85. _getKeyPrefixArray(keys) {
  86. return keys.map(key => `${this.opts.namespace}:${key}`);
  87. }
  88. _getKeyUnprefix(key) {
  89. return key
  90. .split(':')
  91. .splice(1)
  92. .join(':');
  93. }
  94. get(key, options) {
  95. const {store} = this.opts;
  96. const isArray = Array.isArray(key);
  97. const keyPrefixed = isArray ? this._getKeyPrefixArray(key) : this._getKeyPrefix(key);
  98. if (isArray && store.getMany === undefined) {
  99. const promises = [];
  100. for (const key of keyPrefixed) {
  101. promises.push(Promise.resolve()
  102. .then(() => store.get(key))
  103. .then(data => (typeof data === 'string') ? this.opts.deserialize(data) : (this.opts.compression ? this.opts.deserialize(data) : data))
  104. .then(data => {
  105. if (data === undefined || data === null) {
  106. return undefined;
  107. }
  108. if (typeof data.expires === 'number' && Date.now() > data.expires) {
  109. return this.delete(key).then(() => undefined);
  110. }
  111. return (options && options.raw) ? data : data.value;
  112. }),
  113. );
  114. }
  115. return Promise.allSettled(promises)
  116. .then(values => {
  117. const data = [];
  118. for (const value of values) {
  119. data.push(value.value);
  120. }
  121. return data;
  122. });
  123. }
  124. return Promise.resolve()
  125. .then(() => isArray ? store.getMany(keyPrefixed) : store.get(keyPrefixed))
  126. .then(data => (typeof data === 'string') ? this.opts.deserialize(data) : (this.opts.compression ? this.opts.deserialize(data) : data))
  127. .then(data => {
  128. if (data === undefined || data === null) {
  129. return undefined;
  130. }
  131. if (isArray) {
  132. return data.map((row, index) => {
  133. if ((typeof row === 'string')) {
  134. row = this.opts.deserialize(row);
  135. }
  136. if (row === undefined || row === null) {
  137. return undefined;
  138. }
  139. if (typeof row.expires === 'number' && Date.now() > row.expires) {
  140. this.delete(key[index]).then(() => undefined);
  141. return undefined;
  142. }
  143. return (options && options.raw) ? row : row.value;
  144. });
  145. }
  146. if (typeof data.expires === 'number' && Date.now() > data.expires) {
  147. return this.delete(key).then(() => undefined);
  148. }
  149. return (options && options.raw) ? data : data.value;
  150. });
  151. }
  152. set(key, value, ttl) {
  153. const keyPrefixed = this._getKeyPrefix(key);
  154. if (typeof ttl === 'undefined') {
  155. ttl = this.opts.ttl;
  156. }
  157. if (ttl === 0) {
  158. ttl = undefined;
  159. }
  160. const {store} = this.opts;
  161. return Promise.resolve()
  162. .then(() => {
  163. const expires = (typeof ttl === 'number') ? (Date.now() + ttl) : null;
  164. if (typeof value === 'symbol') {
  165. this.emit('error', 'symbol cannot be serialized');
  166. }
  167. value = {value, expires};
  168. return this.opts.serialize(value);
  169. })
  170. .then(value => store.set(keyPrefixed, value, ttl))
  171. .then(() => true);
  172. }
  173. delete(key) {
  174. const {store} = this.opts;
  175. if (Array.isArray(key)) {
  176. const keyPrefixed = this._getKeyPrefixArray(key);
  177. if (store.deleteMany === undefined) {
  178. const promises = [];
  179. for (const key of keyPrefixed) {
  180. promises.push(store.delete(key));
  181. }
  182. return Promise.allSettled(promises)
  183. .then(values => values.every(x => x.value === true));
  184. }
  185. return Promise.resolve()
  186. .then(() => store.deleteMany(keyPrefixed));
  187. }
  188. const keyPrefixed = this._getKeyPrefix(key);
  189. return Promise.resolve()
  190. .then(() => store.delete(keyPrefixed));
  191. }
  192. clear() {
  193. const {store} = this.opts;
  194. return Promise.resolve()
  195. .then(() => store.clear());
  196. }
  197. has(key) {
  198. const keyPrefixed = this._getKeyPrefix(key);
  199. const {store} = this.opts;
  200. return Promise.resolve()
  201. .then(async () => {
  202. if (typeof store.has === 'function') {
  203. return store.has(keyPrefixed);
  204. }
  205. const value = await store.get(keyPrefixed);
  206. return value !== undefined;
  207. });
  208. }
  209. disconnect() {
  210. const {store} = this.opts;
  211. if (typeof store.disconnect === 'function') {
  212. return store.disconnect();
  213. }
  214. }
  215. }
  216. module.exports = Keyv;