serializable.cjs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.Serializable = exports.get_lc_unique_name = void 0;
  4. const map_keys_js_1 = require("./map_keys.cjs");
  5. function shallowCopy(obj) {
  6. return Array.isArray(obj) ? [...obj] : { ...obj };
  7. }
  8. function replaceSecrets(root, secretsMap) {
  9. const result = shallowCopy(root);
  10. for (const [path, secretId] of Object.entries(secretsMap)) {
  11. const [last, ...partsReverse] = path.split(".").reverse();
  12. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  13. let current = result;
  14. for (const part of partsReverse.reverse()) {
  15. if (current[part] === undefined) {
  16. break;
  17. }
  18. current[part] = shallowCopy(current[part]);
  19. current = current[part];
  20. }
  21. if (current[last] !== undefined) {
  22. current[last] = {
  23. lc: 1,
  24. type: "secret",
  25. id: [secretId],
  26. };
  27. }
  28. }
  29. return result;
  30. }
  31. /**
  32. * Get a unique name for the module, rather than parent class implementations.
  33. * Should not be subclassed, subclass lc_name above instead.
  34. */
  35. function get_lc_unique_name(
  36. // eslint-disable-next-line @typescript-eslint/no-use-before-define
  37. serializableClass) {
  38. // "super" here would refer to the parent class of Serializable,
  39. // when we want the parent class of the module actually calling this method.
  40. const parentClass = Object.getPrototypeOf(serializableClass);
  41. const lcNameIsSubclassed = typeof serializableClass.lc_name === "function" &&
  42. (typeof parentClass.lc_name !== "function" ||
  43. serializableClass.lc_name() !== parentClass.lc_name());
  44. if (lcNameIsSubclassed) {
  45. return serializableClass.lc_name();
  46. }
  47. else {
  48. return serializableClass.name;
  49. }
  50. }
  51. exports.get_lc_unique_name = get_lc_unique_name;
  52. class Serializable {
  53. /**
  54. * The name of the serializable. Override to provide an alias or
  55. * to preserve the serialized module name in minified environments.
  56. *
  57. * Implemented as a static method to support loading logic.
  58. */
  59. static lc_name() {
  60. return this.name;
  61. }
  62. /**
  63. * The final serialized identifier for the module.
  64. */
  65. get lc_id() {
  66. return [
  67. ...this.lc_namespace,
  68. get_lc_unique_name(this.constructor),
  69. ];
  70. }
  71. /**
  72. * A map of secrets, which will be omitted from serialization.
  73. * Keys are paths to the secret in constructor args, e.g. "foo.bar.baz".
  74. * Values are the secret ids, which will be used when deserializing.
  75. */
  76. get lc_secrets() {
  77. return undefined;
  78. }
  79. /**
  80. * A map of additional attributes to merge with constructor args.
  81. * Keys are the attribute names, e.g. "foo".
  82. * Values are the attribute values, which will be serialized.
  83. * These attributes need to be accepted by the constructor as arguments.
  84. */
  85. get lc_attributes() {
  86. return undefined;
  87. }
  88. /**
  89. * A map of aliases for constructor args.
  90. * Keys are the attribute names, e.g. "foo".
  91. * Values are the alias that will replace the key in serialization.
  92. * This is used to eg. make argument names match Python.
  93. */
  94. get lc_aliases() {
  95. return undefined;
  96. }
  97. /**
  98. * A manual list of keys that should be serialized.
  99. * If not overridden, all fields passed into the constructor will be serialized.
  100. */
  101. get lc_serializable_keys() {
  102. return undefined;
  103. }
  104. constructor(kwargs, ..._args) {
  105. Object.defineProperty(this, "lc_serializable", {
  106. enumerable: true,
  107. configurable: true,
  108. writable: true,
  109. value: false
  110. });
  111. Object.defineProperty(this, "lc_kwargs", {
  112. enumerable: true,
  113. configurable: true,
  114. writable: true,
  115. value: void 0
  116. });
  117. if (this.lc_serializable_keys !== undefined) {
  118. this.lc_kwargs = Object.fromEntries(Object.entries(kwargs || {}).filter(([key]) => this.lc_serializable_keys?.includes(key)));
  119. }
  120. else {
  121. this.lc_kwargs = kwargs ?? {};
  122. }
  123. }
  124. toJSON() {
  125. if (!this.lc_serializable) {
  126. return this.toJSONNotImplemented();
  127. }
  128. if (
  129. // eslint-disable-next-line no-instanceof/no-instanceof
  130. this.lc_kwargs instanceof Serializable ||
  131. typeof this.lc_kwargs !== "object" ||
  132. Array.isArray(this.lc_kwargs)) {
  133. // We do not support serialization of classes with arg not a POJO
  134. // I'm aware the check above isn't as strict as it could be
  135. return this.toJSONNotImplemented();
  136. }
  137. const aliases = {};
  138. const secrets = {};
  139. const kwargs = Object.keys(this.lc_kwargs).reduce((acc, key) => {
  140. acc[key] = key in this ? this[key] : this.lc_kwargs[key];
  141. return acc;
  142. }, {});
  143. // get secrets, attributes and aliases from all superclasses
  144. for (
  145. // eslint-disable-next-line @typescript-eslint/no-this-alias
  146. let current = Object.getPrototypeOf(this); current; current = Object.getPrototypeOf(current)) {
  147. Object.assign(aliases, Reflect.get(current, "lc_aliases", this));
  148. Object.assign(secrets, Reflect.get(current, "lc_secrets", this));
  149. Object.assign(kwargs, Reflect.get(current, "lc_attributes", this));
  150. }
  151. // include all secrets used, even if not in kwargs,
  152. // will be replaced with sentinel value in replaceSecrets
  153. Object.keys(secrets).forEach((keyPath) => {
  154. // eslint-disable-next-line @typescript-eslint/no-this-alias, @typescript-eslint/no-explicit-any
  155. let read = this;
  156. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  157. let write = kwargs;
  158. const [last, ...partsReverse] = keyPath.split(".").reverse();
  159. for (const key of partsReverse.reverse()) {
  160. if (!(key in read) || read[key] === undefined)
  161. return;
  162. if (!(key in write) || write[key] === undefined) {
  163. if (typeof read[key] === "object" && read[key] != null) {
  164. write[key] = {};
  165. }
  166. else if (Array.isArray(read[key])) {
  167. write[key] = [];
  168. }
  169. }
  170. read = read[key];
  171. write = write[key];
  172. }
  173. if (last in read && read[last] !== undefined) {
  174. write[last] = write[last] || read[last];
  175. }
  176. });
  177. return {
  178. lc: 1,
  179. type: "constructor",
  180. id: this.lc_id,
  181. kwargs: (0, map_keys_js_1.mapKeys)(Object.keys(secrets).length ? replaceSecrets(kwargs, secrets) : kwargs, map_keys_js_1.keyToJson, aliases),
  182. };
  183. }
  184. toJSONNotImplemented() {
  185. return {
  186. lc: 1,
  187. type: "not_implemented",
  188. id: this.lc_id,
  189. };
  190. }
  191. }
  192. exports.Serializable = Serializable;