SerializerMiddleware.js 8.1 KB

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