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.

287 lines
10 KiB

  1. # jsprim: utilities for primitive JavaScript types
  2. This module provides miscellaneous facilities for working with strings,
  3. numbers, dates, and objects and arrays of these basic types.
  4. ### deepCopy(obj)
  5. Creates a deep copy of a primitive type, object, or array of primitive types.
  6. ### deepEqual(obj1, obj2)
  7. Returns whether two objects are equal.
  8. ### isEmpty(obj)
  9. Returns true if the given object has no properties and false otherwise. This
  10. is O(1) (unlike `Object.keys(obj).length === 0`, which is O(N)).
  11. ### hasKey(obj, key)
  12. Returns true if the given object has an enumerable, non-inherited property
  13. called `key`. [For information on enumerability and ownership of properties, see
  14. the MDN
  15. documentation.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties)
  16. ### forEachKey(obj, callback)
  17. Like Array.forEach, but iterates enumerable, owned properties of an object
  18. rather than elements of an array. Equivalent to:
  19. for (var key in obj) {
  20. if (Object.prototype.hasOwnProperty.call(obj, key)) {
  21. callback(key, obj[key]);
  22. }
  23. }
  24. ### flattenObject(obj, depth)
  25. Flattens an object up to a given level of nesting, returning an array of arrays
  26. of length "depth + 1", where the first "depth" elements correspond to flattened
  27. columns and the last element contains the remaining object . For example:
  28. flattenObject({
  29. 'I': {
  30. 'A': {
  31. 'i': {
  32. 'datum1': [ 1, 2 ],
  33. 'datum2': [ 3, 4 ]
  34. },
  35. 'ii': {
  36. 'datum1': [ 3, 4 ]
  37. }
  38. },
  39. 'B': {
  40. 'i': {
  41. 'datum1': [ 5, 6 ]
  42. },
  43. 'ii': {
  44. 'datum1': [ 7, 8 ],
  45. 'datum2': [ 3, 4 ],
  46. },
  47. 'iii': {
  48. }
  49. }
  50. },
  51. 'II': {
  52. 'A': {
  53. 'i': {
  54. 'datum1': [ 1, 2 ],
  55. 'datum2': [ 3, 4 ]
  56. }
  57. }
  58. }
  59. }, 3)
  60. becomes:
  61. [
  62. [ 'I', 'A', 'i', { 'datum1': [ 1, 2 ], 'datum2': [ 3, 4 ] } ],
  63. [ 'I', 'A', 'ii', { 'datum1': [ 3, 4 ] } ],
  64. [ 'I', 'B', 'i', { 'datum1': [ 5, 6 ] } ],
  65. [ 'I', 'B', 'ii', { 'datum1': [ 7, 8 ], 'datum2': [ 3, 4 ] } ],
  66. [ 'I', 'B', 'iii', {} ],
  67. [ 'II', 'A', 'i', { 'datum1': [ 1, 2 ], 'datum2': [ 3, 4 ] } ]
  68. ]
  69. This function is strict: "depth" must be a non-negative integer and "obj" must
  70. be a non-null object with at least "depth" levels of nesting under all keys.
  71. ### flattenIter(obj, depth, func)
  72. This is similar to `flattenObject` except that instead of returning an array,
  73. this function invokes `func(entry)` for each `entry` in the array that
  74. `flattenObject` would return. `flattenIter(obj, depth, func)` is logically
  75. equivalent to `flattenObject(obj, depth).forEach(func)`. Importantly, this
  76. version never constructs the full array. Its memory usage is O(depth) rather
  77. than O(n) (where `n` is the number of flattened elements).
  78. There's another difference between `flattenObject` and `flattenIter` that's
  79. related to the special case where `depth === 0`. In this case, `flattenObject`
  80. omits the array wrapping `obj` (which is regrettable).
  81. ### pluck(obj, key)
  82. Fetch nested property "key" from object "obj", traversing objects as needed.
  83. For example, `pluck(obj, "foo.bar.baz")` is roughly equivalent to
  84. `obj.foo.bar.baz`, except that:
  85. 1. If traversal fails, the resulting value is undefined, and no error is
  86. thrown. For example, `pluck({}, "foo.bar")` is just undefined.
  87. 2. If "obj" has property "key" directly (without traversing), the
  88. corresponding property is returned. For example,
  89. `pluck({ 'foo.bar': 1 }, 'foo.bar')` is 1, not undefined. This is also
  90. true recursively, so `pluck({ 'a': { 'foo.bar': 1 } }, 'a.foo.bar')` is
  91. also 1, not undefined.
  92. ### randElt(array)
  93. Returns an element from "array" selected uniformly at random. If "array" is
  94. empty, throws an Error.
  95. ### startsWith(str, prefix)
  96. Returns true if the given string starts with the given prefix and false
  97. otherwise.
  98. ### endsWith(str, suffix)
  99. Returns true if the given string ends with the given suffix and false
  100. otherwise.
  101. ### parseInteger(str, options)
  102. Parses the contents of `str` (a string) as an integer. On success, the integer
  103. value is returned (as a number). On failure, an error is **returned** describing
  104. why parsing failed.
  105. By default, leading and trailing whitespace characters are not allowed, nor are
  106. trailing characters that are not part of the numeric representation. This
  107. behaviour can be toggled by using the options below. The empty string (`''`) is
  108. not considered valid input. If the return value cannot be precisely represented
  109. as a number (i.e., is smaller than `Number.MIN_SAFE_INTEGER` or larger than
  110. `Number.MAX_SAFE_INTEGER`), an error is returned. Additionally, the string
  111. `'-0'` will be parsed as the integer `0`, instead of as the IEEE floating point
  112. value `-0`.
  113. This function accepts both upper and lowercase characters for digits, similar to
  114. `parseInt()`, `Number()`, and [strtol(3C)](https://illumos.org/man/3C/strtol).
  115. The following may be specified in `options`:
  116. Option | Type | Default | Meaning
  117. ------------------ | ------- | ------- | ---------------------------
  118. base | number | 10 | numeric base (radix) to use, in the range 2 to 36
  119. allowSign | boolean | true | whether to interpret any leading `+` (positive) and `-` (negative) characters
  120. allowImprecise | boolean | false | whether to accept values that may have lost precision (past `MAX_SAFE_INTEGER` or below `MIN_SAFE_INTEGER`)
  121. allowPrefix | boolean | false | whether to interpret the prefixes `0b` (base 2), `0o` (base 8), `0t` (base 10), or `0x` (base 16)
  122. allowTrailing | boolean | false | whether to ignore trailing characters
  123. trimWhitespace | boolean | false | whether to trim any leading or trailing whitespace/line terminators
  124. leadingZeroIsOctal | boolean | false | whether a leading zero indicates octal
  125. Note that if `base` is unspecified, and `allowPrefix` or `leadingZeroIsOctal`
  126. are, then the leading characters can change the default base from 10. If `base`
  127. is explicitly specified and `allowPrefix` is true, then the prefix will only be
  128. accepted if it matches the specified base. `base` and `leadingZeroIsOctal`
  129. cannot be used together.
  130. **Context:** It's tricky to parse integers with JavaScript's built-in facilities
  131. for several reasons:
  132. - `parseInt()` and `Number()` by default allow the base to be specified in the
  133. input string by a prefix (e.g., `0x` for hex).
  134. - `parseInt()` allows trailing nonnumeric characters.
  135. - `Number(str)` returns 0 when `str` is the empty string (`''`).
  136. - Both functions return incorrect values when the input string represents a
  137. valid integer outside the range of integers that can be represented precisely.
  138. Specifically, `parseInt('9007199254740993')` returns 9007199254740992.
  139. - Both functions always accept `-` and `+` signs before the digit.
  140. - Some older JavaScript engines always interpret a leading 0 as indicating
  141. octal, which can be surprising when parsing input from users who expect a
  142. leading zero to be insignificant.
  143. While each of these may be desirable in some contexts, there are also times when
  144. none of them are wanted. `parseInteger()` grants greater control over what
  145. input's permissible.
  146. ### iso8601(date)
  147. Converts a Date object to an ISO8601 date string of the form
  148. "YYYY-MM-DDTHH:MM:SS.sssZ". This format is not customizable.
  149. ### parseDateTime(str)
  150. Parses a date expressed as a string, as either a number of milliseconds since
  151. the epoch or any string format that Date accepts, giving preference to the
  152. former where these two sets overlap (e.g., strings containing small numbers).
  153. ### hrtimeDiff(timeA, timeB)
  154. Given two hrtime readings (as from Node's `process.hrtime()`), where timeA is
  155. later than timeB, compute the difference and return that as an hrtime. It is
  156. illegal to invoke this for a pair of times where timeB is newer than timeA.
  157. ### hrtimeAdd(timeA, timeB)
  158. Add two hrtime intervals (as from Node's `process.hrtime()`), returning a new
  159. hrtime interval array. This function does not modify either input argument.
  160. ### hrtimeAccum(timeA, timeB)
  161. Add two hrtime intervals (as from Node's `process.hrtime()`), storing the
  162. result in `timeA`. This function overwrites (and returns) the first argument
  163. passed in.
  164. ### hrtimeNanosec(timeA), hrtimeMicrosec(timeA), hrtimeMillisec(timeA)
  165. This suite of functions converts a hrtime interval (as from Node's
  166. `process.hrtime()`) into a scalar number of nanoseconds, microseconds or
  167. milliseconds. Results are truncated, as with `Math.floor()`.
  168. ### validateJsonObject(schema, object)
  169. Uses JSON validation (via JSV) to validate the given object against the given
  170. schema. On success, returns null. On failure, *returns* (does not throw) a
  171. useful Error object.
  172. ### extraProperties(object, allowed)
  173. Check an object for unexpected properties. Accepts the object to check, and an
  174. array of allowed property name strings. If extra properties are detected, an
  175. array of extra property names is returned. If no properties other than those
  176. in the allowed list are present on the object, the returned array will be of
  177. zero length.
  178. ### mergeObjects(provided, overrides, defaults)
  179. Merge properties from objects "provided", "overrides", and "defaults". The
  180. intended use case is for functions that accept named arguments in an "args"
  181. object, but want to provide some default values and override other values. In
  182. that case, "provided" is what the caller specified, "overrides" are what the
  183. function wants to override, and "defaults" contains default values.
  184. The function starts with the values in "defaults", overrides them with the
  185. values in "provided", and then overrides those with the values in "overrides".
  186. For convenience, any of these objects may be falsey, in which case they will be
  187. ignored. The input objects are never modified, but properties in the returned
  188. object are not deep-copied.
  189. For example:
  190. mergeObjects(undefined, { 'objectMode': true }, { 'highWaterMark': 0 })
  191. returns:
  192. { 'objectMode': true, 'highWaterMark': 0 }
  193. For another example:
  194. mergeObjects(
  195. { 'highWaterMark': 16, 'objectMode': 7 }, /* from caller */
  196. { 'objectMode': true }, /* overrides */
  197. { 'highWaterMark': 0 }); /* default */
  198. returns:
  199. { 'objectMode': true, 'highWaterMark': 16 }
  200. # Contributing
  201. See separate [contribution guidelines](CONTRIBUTING.md).