SerializerMiddleware.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. */
  4. "use strict";
  5. const memoize = require("../util/memoize");
  6. const LAZY_TARGET = Symbol("lazy serialization target");
  7. const LAZY_SERIALIZED_VALUE = Symbol("lazy serialization data");
  8. /** @typedef {SerializerMiddleware<EXPECTED_ANY, EXPECTED_ANY, Record<string, EXPECTED_ANY>>} LazyTarget */
  9. /** @typedef {Record<string, EXPECTED_ANY>} LazyOptions */
  10. /**
  11. * @template InputValue
  12. * @template OutputValue
  13. * @template {LazyTarget} InternalLazyTarget
  14. * @template {LazyOptions | undefined} InternalLazyOptions
  15. * @typedef {(() => InputValue | Promise<InputValue>) & Partial<{ [LAZY_TARGET]: InternalLazyTarget, options: InternalLazyOptions, [LAZY_SERIALIZED_VALUE]?: OutputValue | LazyFunction<OutputValue, InputValue, InternalLazyTarget, InternalLazyOptions> | undefined }>} LazyFunction
  16. */
  17. /**
  18. * @template DeserializedType
  19. * @template SerializedType
  20. * @template Context
  21. */
  22. class SerializerMiddleware {
  23. /* istanbul ignore next */
  24. /**
  25. * @abstract
  26. * @param {DeserializedType} data data
  27. * @param {Context} context context object
  28. * @returns {SerializedType | Promise<SerializedType> | null} serialized data
  29. */
  30. serialize(data, context) {
  31. const AbstractMethodError = require("../AbstractMethodError");
  32. throw new AbstractMethodError();
  33. }
  34. /* istanbul ignore next */
  35. /**
  36. * @abstract
  37. * @param {SerializedType} data data
  38. * @param {Context} context context object
  39. * @returns {DeserializedType | Promise<DeserializedType>} deserialized data
  40. */
  41. deserialize(data, context) {
  42. const AbstractMethodError = require("../AbstractMethodError");
  43. throw new AbstractMethodError();
  44. }
  45. /**
  46. * @template TLazyInputValue
  47. * @template TLazyOutputValue
  48. * @template {LazyTarget} TLazyTarget
  49. * @template {LazyOptions | undefined} TLazyOptions
  50. * @param {TLazyInputValue | (() => TLazyInputValue)} value contained value or function to value
  51. * @param {TLazyTarget} target target middleware
  52. * @param {TLazyOptions=} options lazy options
  53. * @param {TLazyOutputValue=} serializedValue serialized value
  54. * @returns {LazyFunction<TLazyInputValue, TLazyOutputValue, TLazyTarget, TLazyOptions>} lazy function
  55. */
  56. static createLazy(
  57. value,
  58. target,
  59. options = /** @type {TLazyOptions} */ ({}),
  60. serializedValue = undefined
  61. ) {
  62. if (SerializerMiddleware.isLazy(value, target)) return value;
  63. const fn =
  64. /** @type {LazyFunction<TLazyInputValue, TLazyOutputValue, TLazyTarget, TLazyOptions>} */
  65. (typeof value === "function" ? value : () => value);
  66. fn[LAZY_TARGET] = target;
  67. fn.options = options;
  68. fn[LAZY_SERIALIZED_VALUE] = serializedValue;
  69. return fn;
  70. }
  71. /**
  72. * @template {LazyTarget} TLazyTarget
  73. * @param {EXPECTED_ANY} fn lazy function
  74. * @param {TLazyTarget=} target target middleware
  75. * @returns {fn is LazyFunction<EXPECTED_ANY, EXPECTED_ANY, TLazyTarget, EXPECTED_ANY>} true, when fn is a lazy function (optionally of that target)
  76. */
  77. static isLazy(fn, target) {
  78. if (typeof fn !== "function") return false;
  79. const t = fn[LAZY_TARGET];
  80. return target ? t === target : Boolean(t);
  81. }
  82. /**
  83. * @template TLazyInputValue
  84. * @template TLazyOutputValue
  85. * @template {LazyTarget} TLazyTarget
  86. * @template {Record<string, EXPECTED_ANY>} TLazyOptions
  87. * @template TLazySerializedValue
  88. * @param {LazyFunction<TLazyInputValue, TLazyOutputValue, TLazyTarget, TLazyOptions>} fn lazy function
  89. * @returns {LazyOptions | undefined} options
  90. */
  91. static getLazyOptions(fn) {
  92. if (typeof fn !== "function") return;
  93. return fn.options;
  94. }
  95. /**
  96. * @template TLazyInputValue
  97. * @template TLazyOutputValue
  98. * @template {LazyTarget} TLazyTarget
  99. * @template {LazyOptions} TLazyOptions
  100. * @param {LazyFunction<TLazyInputValue, TLazyOutputValue, TLazyTarget, TLazyOptions> | EXPECTED_ANY} fn lazy function
  101. * @returns {TLazyOutputValue | undefined} serialized value
  102. */
  103. static getLazySerializedValue(fn) {
  104. if (typeof fn !== "function") return;
  105. return fn[LAZY_SERIALIZED_VALUE];
  106. }
  107. /**
  108. * @template TLazyInputValue
  109. * @template TLazyOutputValue
  110. * @template {LazyTarget} TLazyTarget
  111. * @template {LazyOptions} TLazyOptions
  112. * @param {LazyFunction<TLazyInputValue, TLazyOutputValue, LazyTarget, LazyOptions>} fn lazy function
  113. * @param {TLazyOutputValue} value serialized value
  114. * @returns {void}
  115. */
  116. static setLazySerializedValue(fn, value) {
  117. fn[LAZY_SERIALIZED_VALUE] = value;
  118. }
  119. /**
  120. * @template TLazyInputValue DeserializedValue
  121. * @template TLazyOutputValue SerializedValue
  122. * @template {LazyTarget} TLazyTarget
  123. * @template {LazyOptions | undefined} TLazyOptions
  124. * @param {LazyFunction<TLazyInputValue, TLazyOutputValue, TLazyTarget, TLazyOptions>} lazy lazy function
  125. * @param {(value: TLazyInputValue) => TLazyOutputValue} serialize serialize function
  126. * @returns {LazyFunction<TLazyOutputValue, TLazyInputValue, TLazyTarget, TLazyOptions>} new lazy
  127. */
  128. static serializeLazy(lazy, serialize) {
  129. const fn =
  130. /** @type {LazyFunction<TLazyOutputValue, TLazyInputValue, TLazyTarget, TLazyOptions>} */
  131. (
  132. memoize(() => {
  133. const r = lazy();
  134. if (
  135. r &&
  136. typeof (/** @type {Promise<TLazyInputValue>} */ (r).then) ===
  137. "function"
  138. ) {
  139. return (
  140. /** @type {Promise<TLazyInputValue>} */
  141. (r).then(data => data && serialize(data))
  142. );
  143. }
  144. return serialize(/** @type {TLazyInputValue} */ (r));
  145. })
  146. );
  147. fn[LAZY_TARGET] = lazy[LAZY_TARGET];
  148. fn.options = lazy.options;
  149. lazy[LAZY_SERIALIZED_VALUE] = fn;
  150. return fn;
  151. }
  152. /**
  153. * @template TLazyInputValue SerializedValue
  154. * @template TLazyOutputValue DeserializedValue
  155. * @template SerializedValue
  156. * @template {LazyTarget} TLazyTarget
  157. * @template {LazyOptions | undefined} TLazyOptions
  158. * @param {LazyFunction<TLazyInputValue, TLazyOutputValue, TLazyTarget, TLazyOptions>} lazy lazy function
  159. * @param {(data: TLazyInputValue) => TLazyOutputValue} deserialize deserialize function
  160. * @returns {LazyFunction<TLazyOutputValue, TLazyInputValue, TLazyTarget, TLazyOptions>} new lazy
  161. */
  162. static deserializeLazy(lazy, deserialize) {
  163. const fn =
  164. /** @type {LazyFunction<TLazyOutputValue, TLazyInputValue, TLazyTarget, TLazyOptions>} */ (
  165. memoize(() => {
  166. const r = lazy();
  167. if (
  168. r &&
  169. typeof (/** @type {Promise<TLazyInputValue>} */ (r).then) ===
  170. "function"
  171. ) {
  172. return (
  173. /** @type {Promise<TLazyInputValue>} */
  174. (r).then(data => deserialize(data))
  175. );
  176. }
  177. return deserialize(/** @type {TLazyInputValue} */ (r));
  178. })
  179. );
  180. fn[LAZY_TARGET] = lazy[LAZY_TARGET];
  181. fn.options = lazy.options;
  182. fn[LAZY_SERIALIZED_VALUE] = lazy;
  183. return fn;
  184. }
  185. /**
  186. * @template TLazyInputValue
  187. * @template TLazyOutputValue
  188. * @template {LazyTarget} TLazyTarget
  189. * @template {LazyOptions} TLazyOptions
  190. * @param {LazyFunction<TLazyInputValue | TLazyOutputValue, TLazyInputValue | TLazyOutputValue, TLazyTarget, TLazyOptions> | undefined} lazy lazy function
  191. * @returns {LazyFunction<TLazyInputValue | TLazyOutputValue, TLazyInputValue | TLazyOutputValue, TLazyTarget, TLazyOptions> | undefined} new lazy
  192. */
  193. static unMemoizeLazy(lazy) {
  194. if (!SerializerMiddleware.isLazy(lazy)) return lazy;
  195. /** @type {LazyFunction<TLazyInputValue | TLazyOutputValue, TLazyInputValue | TLazyOutputValue, TLazyTarget, TLazyOptions>} */
  196. const fn = () => {
  197. throw new Error(
  198. "A lazy value that has been unmemorized can't be called again"
  199. );
  200. };
  201. fn[LAZY_SERIALIZED_VALUE] = SerializerMiddleware.unMemoizeLazy(
  202. /** @type {LazyFunction<TLazyInputValue | TLazyOutputValue, TLazyInputValue | TLazyOutputValue, TLazyTarget, TLazyOptions>} */
  203. (lazy[LAZY_SERIALIZED_VALUE])
  204. );
  205. fn[LAZY_TARGET] = lazy[LAZY_TARGET];
  206. fn.options = lazy.options;
  207. return fn;
  208. }
  209. }
  210. module.exports = SerializerMiddleware;