@socketsecurity/lib 5.20.1 → 5.21.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/CHANGELOG.md +96 -95
- package/dist/archives.js +13 -0
- package/dist/cacache.js +6 -8
- package/dist/cache-with-ttl.js +1 -1
- package/dist/constants/socket.js +1 -1
- package/dist/dlx/detect.js +25 -8
- package/dist/dlx/package.js +14 -1
- package/dist/fs.js +8 -2
- package/dist/globs.js +5 -1
- package/dist/ipc.js +2 -2
- package/dist/json/parse.d.ts +47 -2
- package/dist/json/parse.js +40 -2
- package/dist/json/types.d.ts +49 -0
- package/dist/memoization.d.ts +4 -23
- package/dist/memoization.js +14 -54
- package/dist/packages/specs.js +9 -2
- package/dist/process-lock.js +1 -6
- package/dist/promise-queue.d.ts +9 -4
- package/dist/promise-queue.js +9 -7
- package/dist/promises.d.ts +41 -0
- package/dist/promises.js +19 -2
- package/dist/regexps.d.ts +4 -13
- package/dist/regexps.js +60 -3
- package/dist/schema/parse.d.ts +26 -0
- package/dist/schema/parse.js +38 -0
- package/dist/schema/types.d.ts +121 -0
- package/dist/schema/validate.d.ts +35 -0
- package/dist/{validation/validate-schema.js → schema/validate.js} +4 -14
- package/dist/suppress-warnings.js +0 -2
- package/dist/url.js +5 -1
- package/dist/versions.js +2 -2
- package/dist/words.js +4 -7
- package/package.json +14 -14
- package/dist/validation/json-parser.d.ts +0 -58
- package/dist/validation/json-parser.js +0 -63
- package/dist/validation/types.d.ts +0 -118
- package/dist/validation/validate-schema.d.ts +0 -124
- /package/dist/{validation → schema}/types.js +0 -0
package/dist/json/parse.js
CHANGED
|
@@ -21,9 +21,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var parse_exports = {};
|
|
22
22
|
__export(parse_exports, {
|
|
23
23
|
isJsonPrimitive: () => isJsonPrimitive,
|
|
24
|
-
jsonParse: () => jsonParse
|
|
24
|
+
jsonParse: () => jsonParse,
|
|
25
|
+
safeJsonParse: () => safeJsonParse
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(parse_exports);
|
|
28
|
+
var import_validate = require("../schema/validate");
|
|
27
29
|
var import_strings = require("../strings");
|
|
28
30
|
const JSONParse = JSON.parse;
|
|
29
31
|
// @__NO_SIDE_EFFECTS__
|
|
@@ -69,8 +71,44 @@ function jsonParse(content, options) {
|
|
|
69
71
|
}
|
|
70
72
|
return void 0;
|
|
71
73
|
}
|
|
74
|
+
const DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
75
|
+
function prototypePollutionReviver(key, value) {
|
|
76
|
+
if (DANGEROUS_KEYS.has(key)) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
"JSON contains potentially malicious prototype pollution keys"
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
const DEFAULT_MAX_SIZE = 10 * 1024 * 1024;
|
|
84
|
+
// @__NO_SIDE_EFFECTS__
|
|
85
|
+
function safeJsonParse(jsonString, schema, options = {}) {
|
|
86
|
+
const { allowPrototype = false, maxSize = DEFAULT_MAX_SIZE } = options;
|
|
87
|
+
const byteLength = Buffer.byteLength(jsonString, "utf8");
|
|
88
|
+
if (byteLength > maxSize) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`JSON string exceeds maximum size limit${maxSize !== DEFAULT_MAX_SIZE ? ` of ${maxSize} bytes` : ""}`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
let parsed;
|
|
94
|
+
try {
|
|
95
|
+
parsed = allowPrototype ? JSONParse(jsonString) : JSONParse(jsonString, prototypePollutionReviver);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
throw new Error(`Failed to parse JSON: ${error}`);
|
|
98
|
+
}
|
|
99
|
+
if (schema) {
|
|
100
|
+
const result = (0, import_validate.validateSchema)(schema, parsed);
|
|
101
|
+
if (!result.ok) {
|
|
102
|
+
const summary = result.errors.map((e) => `${e.path.join(".") || "(root)"}: ${e.message}`).join(", ");
|
|
103
|
+
throw new Error(`Validation failed: ${summary}`);
|
|
104
|
+
}
|
|
105
|
+
return result.value;
|
|
106
|
+
}
|
|
107
|
+
return parsed;
|
|
108
|
+
}
|
|
72
109
|
// Annotate the CommonJS export names for ESM import in node:
|
|
73
110
|
0 && (module.exports = {
|
|
74
111
|
isJsonPrimitive,
|
|
75
|
-
jsonParse
|
|
112
|
+
jsonParse,
|
|
113
|
+
safeJsonParse
|
|
76
114
|
});
|
package/dist/json/types.d.ts
CHANGED
|
@@ -72,6 +72,55 @@ export type JsonValue = JsonPrimitive | JsonObject | JsonArray;
|
|
|
72
72
|
* ```
|
|
73
73
|
*/
|
|
74
74
|
export type JsonReviver = (key: string, value: unknown) => unknown;
|
|
75
|
+
/**
|
|
76
|
+
* Options for `safeJsonParse`: security controls for untrusted JSON.
|
|
77
|
+
*
|
|
78
|
+
* Distinct from `JsonParseOptions` (which is scoped to reviver /
|
|
79
|
+
* error-handling for trusted-source fs reads). Use this type when
|
|
80
|
+
* parsing user input, network payloads, or anything beyond a trust
|
|
81
|
+
* boundary.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```ts
|
|
85
|
+
* const options: SafeJsonParseOptions = {
|
|
86
|
+
* maxSize: 1024 * 1024, // 1MB limit
|
|
87
|
+
* allowPrototype: false // Block prototype pollution
|
|
88
|
+
* }
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export interface SafeJsonParseOptions {
|
|
92
|
+
/**
|
|
93
|
+
* Allow dangerous prototype pollution keys (`__proto__`, `constructor`, `prototype`).
|
|
94
|
+
* Set to `true` only if you trust the JSON source completely.
|
|
95
|
+
*
|
|
96
|
+
* @default false
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* // Will throw error by default
|
|
101
|
+
* safeJsonParse('{"__proto__": {"polluted": true}}')
|
|
102
|
+
*
|
|
103
|
+
* // Allows the parse (dangerous!)
|
|
104
|
+
* safeJsonParse('{"__proto__": {"polluted": true}}', undefined, {
|
|
105
|
+
* allowPrototype: true
|
|
106
|
+
* })
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
allowPrototype?: boolean | undefined;
|
|
110
|
+
/**
|
|
111
|
+
* Maximum allowed size of JSON string in bytes.
|
|
112
|
+
* Prevents memory exhaustion from extremely large payloads.
|
|
113
|
+
*
|
|
114
|
+
* @default 10_485_760 (10 MB)
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* // Limit to 1KB
|
|
119
|
+
* safeJsonParse(jsonString, undefined, { maxSize: 1024 })
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
maxSize?: number | undefined;
|
|
123
|
+
}
|
|
75
124
|
/**
|
|
76
125
|
* Options for JSON parsing operations.
|
|
77
126
|
*/
|
package/dist/memoization.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* Options for memoization behavior.
|
|
7
7
|
*/
|
|
8
|
-
export type MemoizeOptions<Args extends unknown[]
|
|
8
|
+
export type MemoizeOptions<Args extends unknown[]> = {
|
|
9
9
|
/** Custom cache key generator (defaults to JSON.stringify) */
|
|
10
10
|
keyGen?: (...args: Args) => string;
|
|
11
11
|
/** Maximum cache size (LRU eviction when exceeded) */
|
|
@@ -49,7 +49,7 @@ export declare function clearAllMemoizationCaches(): void;
|
|
|
49
49
|
* }
|
|
50
50
|
* }
|
|
51
51
|
*/
|
|
52
|
-
export declare function Memoize(options?: MemoizeOptions<unknown[]
|
|
52
|
+
export declare function Memoize(options?: MemoizeOptions<unknown[]>): (_target: unknown, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
53
53
|
/**
|
|
54
54
|
* Memoize a function with configurable caching behavior.
|
|
55
55
|
* Caches function results to avoid repeated computation.
|
|
@@ -69,7 +69,7 @@ export declare function Memoize(options?: MemoizeOptions<unknown[], unknown>): (
|
|
|
69
69
|
* expensiveOperation(1000) // Computed
|
|
70
70
|
* expensiveOperation(1000) // Cached
|
|
71
71
|
*/
|
|
72
|
-
export declare function memoize<Args extends unknown[], Result>(fn: (...args: Args) => Result, options?: MemoizeOptions<Args
|
|
72
|
+
export declare function memoize<Args extends unknown[], Result>(fn: (...args: Args) => Result, options?: MemoizeOptions<Args>): (...args: Args) => Result;
|
|
73
73
|
/**
|
|
74
74
|
* Memoize an async function.
|
|
75
75
|
* Similar to memoize() but handles promises properly.
|
|
@@ -89,26 +89,7 @@ export declare function memoize<Args extends unknown[], Result>(fn: (...args: Ar
|
|
|
89
89
|
* await fetchUser('123') // Fetches from API
|
|
90
90
|
* await fetchUser('123') // Returns cached result
|
|
91
91
|
*/
|
|
92
|
-
export declare function memoizeAsync<Args extends unknown[], Result>(fn: (...args: Args) => Promise<Result>, options?: MemoizeOptions<Args
|
|
93
|
-
/**
|
|
94
|
-
* Create a debounced memoized function.
|
|
95
|
-
* Combines memoization with debouncing for expensive operations.
|
|
96
|
-
*
|
|
97
|
-
* @param fn - Function to memoize and debounce
|
|
98
|
-
* @param wait - Debounce wait time in milliseconds
|
|
99
|
-
* @param options - Memoization options
|
|
100
|
-
* @returns Debounced memoized function
|
|
101
|
-
*
|
|
102
|
-
* @example
|
|
103
|
-
* import { memoizeDebounced } from '@socketsecurity/lib/memoization'
|
|
104
|
-
*
|
|
105
|
-
* const search = memoizeDebounced(
|
|
106
|
-
* (query: string) => performSearch(query),
|
|
107
|
-
* 300,
|
|
108
|
-
* { name: 'search' }
|
|
109
|
-
* )
|
|
110
|
-
*/
|
|
111
|
-
export declare function memoizeDebounced<Args extends unknown[], Result>(fn: (...args: Args) => Result, wait: number, options?: MemoizeOptions<Args, Result>): (...args: Args) => Result;
|
|
92
|
+
export declare function memoizeAsync<Args extends unknown[], Result>(fn: (...args: Args) => Promise<Result>, options?: MemoizeOptions<Args>): (...args: Args) => Promise<Result>;
|
|
112
93
|
/**
|
|
113
94
|
* Memoize with WeakMap for object keys.
|
|
114
95
|
* Allows garbage collection when objects are no longer referenced.
|
package/dist/memoization.js
CHANGED
|
@@ -24,7 +24,6 @@ __export(memoization_exports, {
|
|
|
24
24
|
clearAllMemoizationCaches: () => clearAllMemoizationCaches,
|
|
25
25
|
memoize: () => memoize,
|
|
26
26
|
memoizeAsync: () => memoizeAsync,
|
|
27
|
-
memoizeDebounced: () => memoizeDebounced,
|
|
28
27
|
memoizeWeak: () => memoizeWeak,
|
|
29
28
|
once: () => once
|
|
30
29
|
});
|
|
@@ -78,15 +77,13 @@ function memoize(fn, options = {}) {
|
|
|
78
77
|
throw new TypeError("TTL must be non-negative");
|
|
79
78
|
}
|
|
80
79
|
const cache = /* @__PURE__ */ new Map();
|
|
81
|
-
const accessOrder = [];
|
|
82
80
|
cacheRegistry.push(() => {
|
|
83
81
|
cache.clear();
|
|
84
|
-
accessOrder.length = 0;
|
|
85
82
|
});
|
|
86
83
|
function evictLRU() {
|
|
87
|
-
if (cache.size >= maxSize
|
|
88
|
-
const oldest =
|
|
89
|
-
if (oldest) {
|
|
84
|
+
if (cache.size >= maxSize) {
|
|
85
|
+
const oldest = cache.keys().next().value;
|
|
86
|
+
if (oldest !== void 0) {
|
|
90
87
|
cache.delete(oldest);
|
|
91
88
|
(0, import_debug.debugLog)(`[memoize:${name}] clear`, {
|
|
92
89
|
key: oldest,
|
|
@@ -107,19 +104,12 @@ function memoize(fn, options = {}) {
|
|
|
107
104
|
if (cached) {
|
|
108
105
|
if (!isExpired(cached)) {
|
|
109
106
|
cached.hits++;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
accessOrder.splice(index2, 1);
|
|
113
|
-
}
|
|
114
|
-
accessOrder.push(key);
|
|
107
|
+
cache.delete(key);
|
|
108
|
+
cache.set(key, cached);
|
|
115
109
|
(0, import_debug.debugLog)(`[memoize:${name}] hit`, { key, hits: cached.hits });
|
|
116
110
|
return cached.value;
|
|
117
111
|
}
|
|
118
112
|
cache.delete(key);
|
|
119
|
-
const index = accessOrder.indexOf(key);
|
|
120
|
-
if (index !== -1) {
|
|
121
|
-
accessOrder.splice(index, 1);
|
|
122
|
-
}
|
|
123
113
|
}
|
|
124
114
|
(0, import_debug.debugLog)(`[memoize:${name}] miss`, { key });
|
|
125
115
|
const value = fn(...args);
|
|
@@ -129,7 +119,6 @@ function memoize(fn, options = {}) {
|
|
|
129
119
|
timestamp: Date.now(),
|
|
130
120
|
hits: 0
|
|
131
121
|
});
|
|
132
|
-
accessOrder.push(key);
|
|
133
122
|
(0, import_debug.debugLog)(`[memoize:${name}] set`, { key, cacheSize: cache.size });
|
|
134
123
|
return value;
|
|
135
124
|
};
|
|
@@ -142,15 +131,13 @@ function memoizeAsync(fn, options = {}) {
|
|
|
142
131
|
ttl = Number.POSITIVE_INFINITY
|
|
143
132
|
} = options;
|
|
144
133
|
const cache = /* @__PURE__ */ new Map();
|
|
145
|
-
const accessOrder = [];
|
|
146
134
|
cacheRegistry.push(() => {
|
|
147
135
|
cache.clear();
|
|
148
|
-
accessOrder.length = 0;
|
|
149
136
|
});
|
|
150
137
|
function evictLRU() {
|
|
151
|
-
if (cache.size >= maxSize
|
|
152
|
-
const oldest =
|
|
153
|
-
if (oldest) {
|
|
138
|
+
if (cache.size >= maxSize) {
|
|
139
|
+
const oldest = cache.keys().next().value;
|
|
140
|
+
if (oldest !== void 0) {
|
|
154
141
|
cache.delete(oldest);
|
|
155
142
|
(0, import_debug.debugLog)(`[memoizeAsync:${name}] clear`, {
|
|
156
143
|
key: oldest,
|
|
@@ -165,6 +152,10 @@ function memoizeAsync(fn, options = {}) {
|
|
|
165
152
|
}
|
|
166
153
|
return Date.now() - entry.timestamp > ttl;
|
|
167
154
|
}
|
|
155
|
+
function bumpRecency(key, entry) {
|
|
156
|
+
cache.delete(key);
|
|
157
|
+
cache.set(key, entry);
|
|
158
|
+
}
|
|
168
159
|
const refreshing = /* @__PURE__ */ new Map();
|
|
169
160
|
return async function memoized(...args) {
|
|
170
161
|
const key = keyGen(...args);
|
|
@@ -172,29 +163,17 @@ function memoizeAsync(fn, options = {}) {
|
|
|
172
163
|
if (cached) {
|
|
173
164
|
if (!isExpired(cached)) {
|
|
174
165
|
cached.hits++;
|
|
175
|
-
|
|
176
|
-
if (index2 !== -1) {
|
|
177
|
-
accessOrder.splice(index2, 1);
|
|
178
|
-
}
|
|
179
|
-
accessOrder.push(key);
|
|
166
|
+
bumpRecency(key, cached);
|
|
180
167
|
(0, import_debug.debugLog)(`[memoizeAsync:${name}] hit`, { key, hits: cached.hits });
|
|
181
168
|
return await cached.value;
|
|
182
169
|
}
|
|
183
170
|
const inflight = refreshing.get(key);
|
|
184
171
|
if (inflight) {
|
|
185
172
|
(0, import_debug.debugLog)(`[memoizeAsync:${name}] stale-dedup`, { key });
|
|
186
|
-
|
|
187
|
-
if (inflightIndex !== -1) {
|
|
188
|
-
accessOrder.splice(inflightIndex, 1);
|
|
189
|
-
}
|
|
190
|
-
accessOrder.push(key);
|
|
173
|
+
bumpRecency(key, cached);
|
|
191
174
|
return await inflight;
|
|
192
175
|
}
|
|
193
176
|
cache.delete(key);
|
|
194
|
-
const index = accessOrder.indexOf(key);
|
|
195
|
-
if (index !== -1) {
|
|
196
|
-
accessOrder.splice(index, 1);
|
|
197
|
-
}
|
|
198
177
|
}
|
|
199
178
|
(0, import_debug.debugLog)(`[memoizeAsync:${name}] miss`, { key });
|
|
200
179
|
const promise = fn(...args).then(
|
|
@@ -210,10 +189,6 @@ function memoizeAsync(fn, options = {}) {
|
|
|
210
189
|
(error) => {
|
|
211
190
|
refreshing.delete(key);
|
|
212
191
|
cache.delete(key);
|
|
213
|
-
const index = accessOrder.indexOf(key);
|
|
214
|
-
if (index !== -1) {
|
|
215
|
-
accessOrder.splice(index, 1);
|
|
216
|
-
}
|
|
217
192
|
(0, import_debug.debugLog)(`[memoizeAsync:${name}] error`, { key, error });
|
|
218
193
|
throw error;
|
|
219
194
|
}
|
|
@@ -225,24 +200,10 @@ function memoizeAsync(fn, options = {}) {
|
|
|
225
200
|
timestamp: Date.now(),
|
|
226
201
|
hits: 0
|
|
227
202
|
});
|
|
228
|
-
accessOrder.push(key);
|
|
229
203
|
(0, import_debug.debugLog)(`[memoizeAsync:${name}] set`, { key, cacheSize: cache.size });
|
|
230
204
|
return await promise;
|
|
231
205
|
};
|
|
232
206
|
}
|
|
233
|
-
function memoizeDebounced(fn, wait, options = {}) {
|
|
234
|
-
const memoized = memoize(fn, options);
|
|
235
|
-
let timeoutId;
|
|
236
|
-
return function debounced(...args) {
|
|
237
|
-
if (timeoutId) {
|
|
238
|
-
clearTimeout(timeoutId);
|
|
239
|
-
}
|
|
240
|
-
timeoutId = setTimeout(() => {
|
|
241
|
-
memoized(...args);
|
|
242
|
-
}, wait);
|
|
243
|
-
return memoized(...args);
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
207
|
function memoizeWeak(fn) {
|
|
247
208
|
const cache = /* @__PURE__ */ new WeakMap();
|
|
248
209
|
return function memoized(key) {
|
|
@@ -276,7 +237,6 @@ function once(fn) {
|
|
|
276
237
|
clearAllMemoizationCaches,
|
|
277
238
|
memoize,
|
|
278
239
|
memoizeAsync,
|
|
279
|
-
memoizeDebounced,
|
|
280
240
|
memoizeWeak,
|
|
281
241
|
once
|
|
282
242
|
});
|
package/dist/packages/specs.js
CHANGED
|
@@ -42,9 +42,16 @@ var import_objects = require("../objects");
|
|
|
42
42
|
var import_strings = require("../strings");
|
|
43
43
|
// @__NO_SIDE_EFFECTS__
|
|
44
44
|
function getRepoUrlDetails(repoUrl = "") {
|
|
45
|
-
const
|
|
45
|
+
const match = /^(?:[a-z][a-z+]*:\/\/)(?:[^/@]+@)?github\.com\/([^?#]+)(?:[?#]|$)/i.exec(
|
|
46
|
+
repoUrl
|
|
47
|
+
);
|
|
48
|
+
if (!match || !match[1]) {
|
|
49
|
+
return { user: "", project: "" };
|
|
50
|
+
}
|
|
51
|
+
const userAndRepo = match[1].split("/");
|
|
46
52
|
const user = userAndRepo[0] || "";
|
|
47
|
-
const
|
|
53
|
+
const rawProject = userAndRepo[1] ?? "";
|
|
54
|
+
const project = rawProject.endsWith(".git") ? rawProject.slice(0, -4) : rawProject;
|
|
48
55
|
return { user, project };
|
|
49
56
|
}
|
|
50
57
|
// @__NO_SIDE_EFFECTS__
|
package/dist/process-lock.js
CHANGED
|
@@ -136,9 +136,7 @@ class ProcessLockManager {
|
|
|
136
136
|
if (!stats) {
|
|
137
137
|
return false;
|
|
138
138
|
}
|
|
139
|
-
|
|
140
|
-
const staleSeconds = Math.floor(staleMs / 1e3);
|
|
141
|
-
return ageSeconds > staleSeconds;
|
|
139
|
+
return Date.now() - stats.mtime.getTime() > staleMs;
|
|
142
140
|
} catch {
|
|
143
141
|
return false;
|
|
144
142
|
}
|
|
@@ -186,9 +184,6 @@ class ProcessLockManager {
|
|
|
186
184
|
}
|
|
187
185
|
}
|
|
188
186
|
const fs = /* @__PURE__ */ getFs();
|
|
189
|
-
if (fs.existsSync(lockPath)) {
|
|
190
|
-
throw new Error(`Lock already exists: ${lockPath}`);
|
|
191
|
-
}
|
|
192
187
|
const parent = (/* @__PURE__ */ getPath()).dirname(lockPath);
|
|
193
188
|
if (parent && parent !== "." && parent !== lockPath) {
|
|
194
189
|
fs.mkdirSync(parent, { recursive: true });
|
package/dist/promise-queue.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Bounded concurrency promise queue.
|
|
3
3
|
* Exports the `PromiseQueue` class, which limits how many async tasks run
|
|
4
|
-
* simultaneously, supports an optional max queue length (
|
|
5
|
-
*
|
|
4
|
+
* simultaneously, supports an optional max queue length (new tasks beyond
|
|
5
|
+
* the cap are rejected with "Task dropped: queue length exceeded"), and
|
|
6
|
+
* exposes an idle-wait helper.
|
|
6
7
|
*/
|
|
7
8
|
export declare class PromiseQueue {
|
|
8
9
|
private queue;
|
|
@@ -13,13 +14,17 @@ export declare class PromiseQueue {
|
|
|
13
14
|
/**
|
|
14
15
|
* Creates a new PromiseQueue
|
|
15
16
|
* @param maxConcurrency - Maximum number of promises that can run concurrently
|
|
16
|
-
* @param maxQueueLength - Maximum queue size
|
|
17
|
+
* @param maxQueueLength - Maximum queue size; submissions past the cap
|
|
18
|
+
* reject with "Task dropped: queue length exceeded" instead of evicting
|
|
19
|
+
* a caller that has been waiting patiently. Callers must handle this
|
|
20
|
+
* rejection or they'll see an unhandled rejection.
|
|
17
21
|
*/
|
|
18
22
|
constructor(maxConcurrency: number, maxQueueLength?: number | undefined);
|
|
19
23
|
/**
|
|
20
24
|
* Add a task to the queue
|
|
21
25
|
* @param fn - Async function to execute
|
|
22
|
-
* @returns Promise that resolves with the function's result
|
|
26
|
+
* @returns Promise that resolves with the function's result, or rejects
|
|
27
|
+
* with "Task dropped: queue length exceeded" if the queue is full.
|
|
23
28
|
*/
|
|
24
29
|
add<T>(fn: () => Promise<T>): Promise<T>;
|
|
25
30
|
private runNext;
|
package/dist/promise-queue.js
CHANGED
|
@@ -32,7 +32,10 @@ class PromiseQueue {
|
|
|
32
32
|
/**
|
|
33
33
|
* Creates a new PromiseQueue
|
|
34
34
|
* @param maxConcurrency - Maximum number of promises that can run concurrently
|
|
35
|
-
* @param maxQueueLength - Maximum queue size
|
|
35
|
+
* @param maxQueueLength - Maximum queue size; submissions past the cap
|
|
36
|
+
* reject with "Task dropped: queue length exceeded" instead of evicting
|
|
37
|
+
* a caller that has been waiting patiently. Callers must handle this
|
|
38
|
+
* rejection or they'll see an unhandled rejection.
|
|
36
39
|
*/
|
|
37
40
|
constructor(maxConcurrency, maxQueueLength) {
|
|
38
41
|
this.maxConcurrency = maxConcurrency;
|
|
@@ -44,17 +47,16 @@ class PromiseQueue {
|
|
|
44
47
|
/**
|
|
45
48
|
* Add a task to the queue
|
|
46
49
|
* @param fn - Async function to execute
|
|
47
|
-
* @returns Promise that resolves with the function's result
|
|
50
|
+
* @returns Promise that resolves with the function's result, or rejects
|
|
51
|
+
* with "Task dropped: queue length exceeded" if the queue is full.
|
|
48
52
|
*/
|
|
49
53
|
async add(fn) {
|
|
50
54
|
return await new Promise((resolve, reject) => {
|
|
51
|
-
const task = { fn, resolve, reject };
|
|
52
55
|
if (this.maxQueueLength !== void 0 && this.queue.length >= this.maxQueueLength) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
droppedTask.reject(new Error("Task dropped: queue length exceeded"));
|
|
56
|
-
}
|
|
56
|
+
reject(new Error("Task dropped: queue length exceeded"));
|
|
57
|
+
return;
|
|
57
58
|
}
|
|
59
|
+
const task = { fn, resolve, reject };
|
|
58
60
|
this.queue.push(task);
|
|
59
61
|
this.runNext();
|
|
60
62
|
});
|
package/dist/promises.d.ts
CHANGED
|
@@ -440,3 +440,44 @@ export declare function pRetry<T>(callbackFn: (...args: unknown[]) => Promise<T>
|
|
|
440
440
|
* // => { retries: 5, minTimeout: 200, maxTimeout: 5000, factor: 2 }
|
|
441
441
|
*/
|
|
442
442
|
export declare function resolveRetryOptions(options?: number | RetryOptions | undefined): RetryOptions;
|
|
443
|
+
/**
|
|
444
|
+
* Shape returned by {@link withResolvers}: a fresh pending promise plus
|
|
445
|
+
* the `resolve` / `reject` handles that settle it.
|
|
446
|
+
*
|
|
447
|
+
* Matches the spec return-shape exactly
|
|
448
|
+
* ([ECMA-262 §27.2.4.9](https://tc39.es/ecma262/#sec-promise.withResolvers)).
|
|
449
|
+
*/
|
|
450
|
+
export interface PromiseWithResolvers<T> {
|
|
451
|
+
/** The pending promise. */
|
|
452
|
+
promise: Promise<T>;
|
|
453
|
+
/** Resolves {@link promise} with the given value (or thenable). */
|
|
454
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
455
|
+
/** Rejects {@link promise} with the given reason. */
|
|
456
|
+
reject: (reason?: unknown) => void;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Create a pending promise together with its `resolve` and `reject`
|
|
460
|
+
* handles as first-class values, per
|
|
461
|
+
* [ECMA-262 §27.2.4.9](https://tc39.es/ecma262/#sec-promise.withResolvers).
|
|
462
|
+
*
|
|
463
|
+
* Bound to native `Promise.withResolvers` when available (Node 20.12+ /
|
|
464
|
+
* 21+ / 22+; V8 ≥ 12.0); otherwise falls back to a spec-equivalent
|
|
465
|
+
* `new Promise(executor)` implementation that captures the handles via
|
|
466
|
+
* closure. The returned object always has own data properties `promise`,
|
|
467
|
+
* `resolve`, `reject` on `Object.prototype` — writable, enumerable, and
|
|
468
|
+
* configurable — matching the spec's `CreateDataPropertyOrThrow` steps.
|
|
469
|
+
*
|
|
470
|
+
* Use this instead of the manual
|
|
471
|
+
* `let resolve; const p = new Promise(r => { resolve = r })` dance for
|
|
472
|
+
* deferred-resolution patterns (event-driven bridges, adapter layers,
|
|
473
|
+
* handshake signaling) where the settle path lives outside the executor.
|
|
474
|
+
*
|
|
475
|
+
* @example
|
|
476
|
+
* ```typescript
|
|
477
|
+
* const { promise, resolve, reject } = withResolvers<string>()
|
|
478
|
+
* emitter.once('ready', () => resolve('ok'))
|
|
479
|
+
* emitter.once('error', err => reject(err))
|
|
480
|
+
* const result = await promise
|
|
481
|
+
* ```
|
|
482
|
+
*/
|
|
483
|
+
export declare const withResolvers: <T>() => PromiseWithResolvers<T>;
|
package/dist/promises.js
CHANGED
|
@@ -27,7 +27,8 @@ __export(promises_exports, {
|
|
|
27
27
|
pFilter: () => pFilter,
|
|
28
28
|
pFilterChunk: () => pFilterChunk,
|
|
29
29
|
pRetry: () => pRetry,
|
|
30
|
-
resolveRetryOptions: () => resolveRetryOptions
|
|
30
|
+
resolveRetryOptions: () => resolveRetryOptions,
|
|
31
|
+
withResolvers: () => withResolvers
|
|
31
32
|
});
|
|
32
33
|
module.exports = __toCommonJS(promises_exports);
|
|
33
34
|
var import_arrays = require("./arrays");
|
|
@@ -258,6 +259,21 @@ function resolveRetryOptions(options) {
|
|
|
258
259
|
}
|
|
259
260
|
return options ? { ...defaults, ...options } : defaults;
|
|
260
261
|
}
|
|
262
|
+
const maybeNativeWithResolvers = Promise.withResolvers;
|
|
263
|
+
const withResolvers = typeof maybeNativeWithResolvers === "function" ? (
|
|
264
|
+
// Bind so callers who destructure the export don't lose `this`.
|
|
265
|
+
maybeNativeWithResolvers.bind(
|
|
266
|
+
Promise
|
|
267
|
+
)
|
|
268
|
+
) : () => {
|
|
269
|
+
let resolve;
|
|
270
|
+
let reject;
|
|
271
|
+
const promise = new Promise((res, rej) => {
|
|
272
|
+
resolve = res;
|
|
273
|
+
reject = rej;
|
|
274
|
+
});
|
|
275
|
+
return { promise, resolve, reject };
|
|
276
|
+
};
|
|
261
277
|
// Annotate the CommonJS export names for ESM import in node:
|
|
262
278
|
0 && (module.exports = {
|
|
263
279
|
normalizeIterationOptions,
|
|
@@ -267,5 +283,6 @@ function resolveRetryOptions(options) {
|
|
|
267
283
|
pFilter,
|
|
268
284
|
pFilterChunk,
|
|
269
285
|
pRetry,
|
|
270
|
-
resolveRetryOptions
|
|
286
|
+
resolveRetryOptions,
|
|
287
|
+
withResolvers
|
|
271
288
|
});
|
package/dist/regexps.d.ts
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Regular expression utilities including
|
|
3
|
-
* Provides regex escaping and pattern matching
|
|
2
|
+
* @fileoverview Regular expression utilities including a spec-compliant
|
|
3
|
+
* `RegExp.escape` fallback. Provides regex escaping and pattern matching
|
|
4
|
+
* helpers.
|
|
4
5
|
*/
|
|
5
|
-
|
|
6
|
-
* Escape special characters in a string for use in a regular expression.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```typescript
|
|
10
|
-
* escapeRegExp('foo.bar') // 'foo\\.bar'
|
|
11
|
-
* escapeRegExp('a+b*c?') // 'a\\+b\\*c\\?'
|
|
12
|
-
* new RegExp(escapeRegExp('[test]')) // /\[test\]/
|
|
13
|
-
* ```
|
|
14
|
-
*/
|
|
15
|
-
export declare function escapeRegExp(str: string): string;
|
|
6
|
+
export declare const escapeRegExp: (str: string) => string;
|
package/dist/regexps.js
CHANGED
|
@@ -23,10 +23,67 @@ __export(regexps_exports, {
|
|
|
23
23
|
escapeRegExp: () => escapeRegExp
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(regexps_exports);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
const SYNTAX_CHARACTERS = new Set("^$\\.*+?()[]{}|/");
|
|
27
|
+
const CONTROL_ESCAPES = /* @__PURE__ */ new Map([
|
|
28
|
+
[9, "\\t"],
|
|
29
|
+
[10, "\\n"],
|
|
30
|
+
[11, "\\v"],
|
|
31
|
+
[12, "\\f"],
|
|
32
|
+
[13, "\\r"]
|
|
33
|
+
]);
|
|
34
|
+
const OTHER_PUNCTUATORS = new Set(",-=<>#&!%:;@~'`\"");
|
|
35
|
+
function isSpecHexEscapeCp(cp) {
|
|
36
|
+
if (OTHER_PUNCTUATORS.has(String.fromCodePoint(cp))) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
if (cp === 10 || cp === 13 || cp === 8232 || cp === 8233) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
if (cp === 9 || cp === 11 || cp === 12 || cp === 32 || cp === 160 || cp === 65279) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
if (cp >= 55296 && cp <= 57343) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
function hex2(n) {
|
|
51
|
+
return n.toString(16).padStart(2, "0");
|
|
52
|
+
}
|
|
53
|
+
function hex4(n) {
|
|
54
|
+
return n.toString(16).padStart(4, "0");
|
|
55
|
+
}
|
|
56
|
+
function escapeRegExpFallback(str) {
|
|
57
|
+
let out = "";
|
|
58
|
+
let isFirst = true;
|
|
59
|
+
for (const char of str) {
|
|
60
|
+
const cp = char.codePointAt(0);
|
|
61
|
+
if (isFirst && (cp >= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122)) {
|
|
62
|
+
out += "\\x" + hex2(cp);
|
|
63
|
+
} else if (SYNTAX_CHARACTERS.has(char)) {
|
|
64
|
+
out += "\\" + char;
|
|
65
|
+
} else {
|
|
66
|
+
const ctrl = CONTROL_ESCAPES.get(cp);
|
|
67
|
+
if (ctrl !== void 0) {
|
|
68
|
+
out += ctrl;
|
|
69
|
+
} else if (isSpecHexEscapeCp(cp)) {
|
|
70
|
+
if (cp <= 255) {
|
|
71
|
+
out += "\\x" + hex2(cp);
|
|
72
|
+
} else {
|
|
73
|
+
for (let i = 0; i < char.length; i++) {
|
|
74
|
+
out += "\\u" + hex4(char.charCodeAt(i));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
out += char;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
isFirst = false;
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
29
84
|
}
|
|
85
|
+
const maybeNativeEscape = RegExp.escape;
|
|
86
|
+
const escapeRegExp = typeof maybeNativeEscape === "function" ? maybeNativeEscape : escapeRegExpFallback;
|
|
30
87
|
// Annotate the CommonJS export names for ESM import in node:
|
|
31
88
|
0 && (module.exports = {
|
|
32
89
|
escapeRegExp
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Throwing twin of `validateSchema`.
|
|
3
|
+
*
|
|
4
|
+
* Use `parseSchema(schema, data)` for fail-fast trust boundaries (app
|
|
5
|
+
* startup, config files, internal assertions). Use the non-throwing
|
|
6
|
+
* `validateSchema` for recoverable input (form fields, API request bodies,
|
|
7
|
+
* anywhere errors need to surface to a user).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { z } from 'zod'
|
|
12
|
+
* import { parseSchema } from '@socketsecurity/lib/schema/parse'
|
|
13
|
+
*
|
|
14
|
+
* const Config = z.object({ host: z.string(), port: z.number() })
|
|
15
|
+
* const config = parseSchema(Config, json) // throws on invalid
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import type { Infer } from './types';
|
|
19
|
+
/**
|
|
20
|
+
* Parse `data` against `schema` and return the validated value.
|
|
21
|
+
*
|
|
22
|
+
* @throws {Error} When validation fails. The message lists all issues as
|
|
23
|
+
* `path: message, path: message, ...`. Use `validateSchema` if you need
|
|
24
|
+
* structured access to the error list.
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseSchema<S>(schema: S, data: unknown): Infer<S>;
|