punctilio 3.13.2 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/utils.ts CHANGED
@@ -4,16 +4,18 @@ const ERROR_STRING_THRESHOLD = 2000
4
4
 
5
5
  /**
6
6
  * Formats a string for error messages, truncating above the threshold.
7
- * Writes full content to stderr in Node.js so build logs capture it.
7
+ * When the PUNCTILIO_DEBUG environment variable is set to a non-empty
8
+ * value, also writes the full content to stderr in Node.js so build logs
9
+ * capture it.
8
10
  */
9
11
  export function formatErrorString(content: string, label: string): string {
10
12
  if (content.length <= ERROR_STRING_THRESHOLD) {
11
13
  return JSON.stringify(content)
12
14
  }
13
15
 
14
- // In Node.js, write full content to stderr for debugging
16
+ // In Node.js, write full content to stderr for debugging when opted in
15
17
  try {
16
- if (typeof globalThis.process?.stderr?.write === "function") {
18
+ if (globalThis.process?.env?.PUNCTILIO_DEBUG && typeof globalThis.process?.stderr?.write === "function") {
17
19
  globalThis.process.stderr.write(
18
20
  `\n[punctilio ${label} full content (${content.length} chars)]:\n${content}\n\n`
19
21
  )
@@ -106,6 +108,30 @@ export function assertSeparatorCountPreserved(
106
108
  }
107
109
  }
108
110
 
111
+ /** Throws if `options` contains a key not present in `validKeys`. @internal */
112
+ export function assertKnownOptionKeys(
113
+ options: object,
114
+ validKeys: readonly string[],
115
+ context: string,
116
+ ): void {
117
+ for (const key of Object.keys(options)) {
118
+ if (!validKeys.includes(key)) {
119
+ throw new Error(
120
+ `Unknown option "${key}" for ${context}. Valid options: ${[...validKeys].sort().join(", ")}.`
121
+ )
122
+ }
123
+ }
124
+ }
125
+
126
+ /** Returns a copy of `obj` without the listed keys. @internal */
127
+ export function omitKeys<T extends object>(obj: T, keys: readonly string[]): Partial<T> {
128
+ const result: Record<string, unknown> = {}
129
+ for (const [k, v] of Object.entries(obj)) {
130
+ if (!keys.includes(k)) result[k] = v
131
+ }
132
+ return result as Partial<T>
133
+ }
134
+
109
135
  /** Returns a copy of `obj` with `undefined`-valued keys removed. @internal */
110
136
  export function filterUndefined<T extends object>(obj: T): Partial<T> {
111
137
  const result: Record<string, unknown> = {}
@@ -128,3 +154,22 @@ export function stableStringify(obj: object): string {
128
154
  export function namedGroups<G>(args: unknown[]): G {
129
155
  return args[args.length - 1] as G
130
156
  }
157
+
158
+ /**
159
+ * Extracts the match offset and input string from a `.replace()` callback's
160
+ * arguments, independent of capture-group count. The trailing arguments are
161
+ * `..., offset, input[, namedGroups]`: the input is the last string (the
162
+ * named-groups object, when present, follows it) and the offset is the
163
+ * number immediately before the input. @internal
164
+ */
165
+ export function replaceCallbackContext(args: unknown[]): { offset: number; input: string } {
166
+ let inputIndex = args.length - 1
167
+ while (inputIndex >= 0 && typeof args[inputIndex] !== "string") {
168
+ inputIndex--
169
+ }
170
+ const offset = args[inputIndex - 1]
171
+ if (inputIndex < 1 || typeof offset !== "number") {
172
+ throw new Error("Could not locate offset and input in replace-callback arguments.")
173
+ }
174
+ return { offset, input: args[inputIndex] as string }
175
+ }