euredact 0.6.0 → 0.7.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/NOTICE +11 -0
- package/README.md +403 -39
- package/dist/cjs/cache.js +2 -8
- package/dist/cjs/cloud/account.js +180 -0
- package/dist/cjs/cloud/batches.js +469 -0
- package/dist/cjs/cloud/client.js +124 -14
- package/dist/cjs/cloud/config.js +28 -2
- package/dist/cjs/cloud/errors.js +39 -2
- package/dist/cjs/cloud/index.js +16 -1
- package/dist/cjs/index.js +9 -1
- package/dist/cjs/platform.browser.js +98 -0
- package/dist/cjs/platform.js +140 -0
- package/dist/cjs/rules/bicGleif.js +57 -0
- package/dist/cjs/rules/bicRegistry.js +37 -11
- package/dist/cjs/rules/countries/index.js +63 -10
- package/dist/cjs/rules/cues.js +29 -9
- package/dist/cjs/rules/engine.js +14 -6
- package/dist/cjs/rules/suppressors.js +292 -22
- package/dist/cjs/rules/validators.js +50 -0
- package/dist/cjs/sdk.js +18 -7
- package/dist/cjs/types.js +4 -1
- package/dist/esm/cache.js +2 -8
- package/dist/esm/cloud/account.d.ts +155 -0
- package/dist/esm/cloud/account.js +175 -0
- package/dist/esm/cloud/batches.d.ts +166 -0
- package/dist/esm/cloud/batches.js +464 -0
- package/dist/esm/cloud/client.d.ts +83 -1
- package/dist/esm/cloud/client.js +119 -22
- package/dist/esm/cloud/config.d.ts +6 -0
- package/dist/esm/cloud/config.js +27 -2
- package/dist/esm/cloud/errors.d.ts +26 -1
- package/dist/esm/cloud/errors.js +35 -1
- package/dist/esm/cloud/index.d.ts +4 -2
- package/dist/esm/cloud/index.js +4 -2
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/platform.browser.d.ts +27 -0
- package/dist/esm/platform.browser.js +92 -0
- package/dist/esm/platform.d.ts +20 -0
- package/dist/esm/platform.js +101 -0
- package/dist/esm/rules/bicGleif.d.ts +4 -0
- package/dist/esm/rules/bicGleif.js +53 -0
- package/dist/esm/rules/bicRegistry.d.ts +25 -11
- package/dist/esm/rules/bicRegistry.js +36 -11
- package/dist/esm/rules/countries/index.js +63 -10
- package/dist/esm/rules/cues.d.ts +1 -1
- package/dist/esm/rules/cues.js +29 -9
- package/dist/esm/rules/engine.js +14 -6
- package/dist/esm/rules/suppressors.d.ts +1 -0
- package/dist/esm/rules/suppressors.js +291 -21
- package/dist/esm/rules/validators.d.ts +14 -0
- package/dist/esm/rules/validators.js +47 -0
- package/dist/esm/sdk.d.ts +51 -4
- package/dist/esm/sdk.js +18 -9
- package/dist/esm/types.d.ts +58 -2
- package/dist/esm/types.js +4 -1
- package/package.json +12 -4
package/dist/cjs/cloud/client.js
CHANGED
|
@@ -26,12 +26,27 @@
|
|
|
26
26
|
* own `fetch`.
|
|
27
27
|
*/
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.CloudClient = void 0;
|
|
29
|
+
exports.CloudClient = exports.RETRY_STATUS = void 0;
|
|
30
|
+
exports.requireFetch = requireFetch;
|
|
31
|
+
exports.sleep = sleep;
|
|
32
|
+
exports.retryAfterMs = retryAfterMs;
|
|
33
|
+
exports.backoffMs = backoffMs;
|
|
34
|
+
exports.uuid = uuid;
|
|
35
|
+
exports.codePointOffsets = codePointOffsets;
|
|
36
|
+
exports.isInteger = isInteger;
|
|
37
|
+
exports.toUsage = toUsage;
|
|
38
|
+
exports.toCloudInfo = toCloudInfo;
|
|
39
|
+
exports.isQuota = isQuota;
|
|
40
|
+
exports.cloudErrorFor = cloudErrorFor;
|
|
41
|
+
exports.toResult = toResult;
|
|
42
|
+
exports.readJson = readJson;
|
|
30
43
|
const config_js_1 = require("./config.js");
|
|
31
44
|
const errors_js_1 = require("./errors.js");
|
|
32
45
|
const types_js_1 = require("../types.js");
|
|
33
|
-
|
|
46
|
+
/** @internal Shared with cloud/account.ts. */
|
|
47
|
+
exports.RETRY_STATUS = new Set([429, 500, 502, 503, 504]);
|
|
34
48
|
const MAX_BACKOFF_MS = 30_000;
|
|
49
|
+
/** @internal */
|
|
35
50
|
function requireFetch(supplied) {
|
|
36
51
|
const impl = supplied ?? globalThis.fetch;
|
|
37
52
|
if (typeof impl !== "function") {
|
|
@@ -40,9 +55,11 @@ function requireFetch(supplied) {
|
|
|
40
55
|
}
|
|
41
56
|
return impl;
|
|
42
57
|
}
|
|
58
|
+
/** @internal */
|
|
43
59
|
function sleep(ms) {
|
|
44
60
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
45
61
|
}
|
|
62
|
+
/** @internal */
|
|
46
63
|
function retryAfterMs(headers) {
|
|
47
64
|
const raw = headers?.get("Retry-After");
|
|
48
65
|
if (!raw)
|
|
@@ -55,11 +72,13 @@ function retryAfterMs(headers) {
|
|
|
55
72
|
* Full jitter. A synchronised retry storm from many clients is how a
|
|
56
73
|
* recovering service is knocked back over.
|
|
57
74
|
*/
|
|
75
|
+
/** @internal */
|
|
58
76
|
function backoffMs(attempt, retryAfter) {
|
|
59
77
|
if (retryAfter !== null)
|
|
60
78
|
return Math.min(retryAfter, MAX_BACKOFF_MS);
|
|
61
79
|
return Math.min(MAX_BACKOFF_MS, Math.random() * 2 ** attempt * 1000);
|
|
62
80
|
}
|
|
81
|
+
/** @internal */
|
|
63
82
|
function uuid() {
|
|
64
83
|
const c = globalThis.crypto;
|
|
65
84
|
if (c && typeof c.randomUUID === "function")
|
|
@@ -87,6 +106,7 @@ function toEntityType(raw) {
|
|
|
87
106
|
* Returns the identity when the text has none, which is the common case and
|
|
88
107
|
* costs one regex test.
|
|
89
108
|
*/
|
|
109
|
+
/** @internal Shared with cloud/batches.ts. */
|
|
90
110
|
function codePointOffsets(text) {
|
|
91
111
|
if (!/[\uD800-\uDBFF]/.test(text))
|
|
92
112
|
return cp => cp;
|
|
@@ -99,6 +119,97 @@ function codePointOffsets(text) {
|
|
|
99
119
|
units.push(i);
|
|
100
120
|
return cp => units[Math.min(cp, units.length - 1)];
|
|
101
121
|
}
|
|
122
|
+
/** @internal */
|
|
123
|
+
function isInteger(value) {
|
|
124
|
+
return typeof value === "number" && Number.isInteger(value);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The `usage` block, or undefined when absent or malformed. Shared rules with
|
|
128
|
+
* the Python SDK (`conformance/cloud_usage.json`): `tokens` and `credits` are
|
|
129
|
+
* integers, `billing_rate` a number, `factors` a list whose entries carry a
|
|
130
|
+
* string `code`. Anything else there drops the whole block rather than
|
|
131
|
+
* reporting a cost nobody can trust; unknown keys are ignored.
|
|
132
|
+
* @internal Exported for the conformance test.
|
|
133
|
+
*/
|
|
134
|
+
function toUsage(raw) {
|
|
135
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
136
|
+
return undefined;
|
|
137
|
+
const u = raw;
|
|
138
|
+
if (!isInteger(u.tokens) || !isInteger(u.credits) || typeof u.billing_rate !== "number"
|
|
139
|
+
|| !Array.isArray(u.factors))
|
|
140
|
+
return undefined;
|
|
141
|
+
const factors = [];
|
|
142
|
+
for (const f of u.factors) {
|
|
143
|
+
if (f === null || typeof f !== "object" || Array.isArray(f))
|
|
144
|
+
return undefined;
|
|
145
|
+
const r = f;
|
|
146
|
+
if (typeof r.code !== "string")
|
|
147
|
+
return undefined;
|
|
148
|
+
const factor = { code: r.code, detail: typeof r.detail === "string" ? r.detail : "" };
|
|
149
|
+
if (Array.isArray(r.types))
|
|
150
|
+
factor.types = r.types.filter((t) => typeof t === "string");
|
|
151
|
+
factors.push(factor);
|
|
152
|
+
}
|
|
153
|
+
return { tokens: u.tokens, billingRate: u.billing_rate, credits: u.credits, factors };
|
|
154
|
+
}
|
|
155
|
+
function str(value) {
|
|
156
|
+
return typeof value === "string" ? value : null;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The service's account of a request beside its spans. Shared rules with the
|
|
160
|
+
* Python SDK (`conformance/cloud_result.json`): a field of the wrong type reads
|
|
161
|
+
* as absent, and an unlocated entry without string `text` is skipped.
|
|
162
|
+
* @internal
|
|
163
|
+
*/
|
|
164
|
+
function toCloudInfo(payload) {
|
|
165
|
+
const unlocated = [];
|
|
166
|
+
for (const item of Array.isArray(payload.unlocated) ? payload.unlocated : []) {
|
|
167
|
+
if (item && typeof item === "object" && typeof item.text === "string") {
|
|
168
|
+
const t = item.type;
|
|
169
|
+
unlocated.push({
|
|
170
|
+
text: item.text,
|
|
171
|
+
entityType: toEntityType(typeof t === "string" ? t : "OTHER"),
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const usage = toUsage(payload.usage);
|
|
176
|
+
return {
|
|
177
|
+
jobId: str(payload.job_id),
|
|
178
|
+
modelVersion: str(payload.model_version),
|
|
179
|
+
...(usage ? { usage } : {}),
|
|
180
|
+
unlocated,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* A 429 from the gateway's daily quota rather than the edge's rate limit: the
|
|
185
|
+
* gateway answers in JSON with `detail.used` and `detail.limit`; nginx in HTML.
|
|
186
|
+
* @internal
|
|
187
|
+
*/
|
|
188
|
+
function isQuota(payload) {
|
|
189
|
+
const detail = payload.detail;
|
|
190
|
+
return !!detail && typeof detail === "object" && "limit" in detail && "used" in detail;
|
|
191
|
+
}
|
|
192
|
+
/** The error for a non-success answer. @internal Shared with batches and account. */
|
|
193
|
+
function cloudErrorFor(status, payload) {
|
|
194
|
+
const message = typeof payload.error === "string" ? payload.error : `HTTP ${status}`;
|
|
195
|
+
const detail = (payload.detail && typeof payload.detail === "object"
|
|
196
|
+
? payload.detail : {});
|
|
197
|
+
if (status === 401)
|
|
198
|
+
return new errors_js_1.CloudError(`authentication failed: ${message}`, status);
|
|
199
|
+
if (status === 404)
|
|
200
|
+
return new errors_js_1.NotFoundError(message, status, detail);
|
|
201
|
+
if (status === 410)
|
|
202
|
+
return new errors_js_1.ResultExpiredError(message, status, detail);
|
|
203
|
+
if (status === 413)
|
|
204
|
+
return new errors_js_1.TooLargeError(message, status, detail);
|
|
205
|
+
if (status === 429) {
|
|
206
|
+
return isQuota(payload)
|
|
207
|
+
? new errors_js_1.QuotaExceededError(message, status, detail)
|
|
208
|
+
: new errors_js_1.RateLimitedError("rate limited by the service (HTTP 429); try again shortly", status, detail);
|
|
209
|
+
}
|
|
210
|
+
return new errors_js_1.CloudError(message, status, detail);
|
|
211
|
+
}
|
|
212
|
+
/** @internal Shared with cloud/batches.ts. */
|
|
102
213
|
function toResult(payload, text) {
|
|
103
214
|
const offset = codePointOffsets(text);
|
|
104
215
|
const detections = (payload.entities ?? []).map(span => ({
|
|
@@ -108,7 +219,9 @@ function toResult(payload, text) {
|
|
|
108
219
|
text: span.text,
|
|
109
220
|
source: span.source === "model" ? types_js_1.DetectionSource.CLOUD : types_js_1.DetectionSource.RULES,
|
|
110
221
|
country: null,
|
|
111
|
-
|
|
222
|
+
// A model span carries no evidence of its own: the service's confidence
|
|
223
|
+
// when it sends one, never an invented "high" (rules-engine#108).
|
|
224
|
+
confidence: span.source === "model" ? (span.confidence ?? null) : (span.confidence ?? "high"),
|
|
112
225
|
}));
|
|
113
226
|
detections.sort((a, b) => a.start - b.start || b.end - a.end);
|
|
114
227
|
return {
|
|
@@ -121,6 +234,7 @@ function toResult(payload, text) {
|
|
|
121
234
|
detectionMode: "declared",
|
|
122
235
|
tokens: {},
|
|
123
236
|
exempted: [],
|
|
237
|
+
cloud: toCloudInfo(payload),
|
|
124
238
|
};
|
|
125
239
|
}
|
|
126
240
|
class CloudClient {
|
|
@@ -129,6 +243,8 @@ class CloudClient {
|
|
|
129
243
|
const resolved = config ?? (0, config_js_1.getConfig)();
|
|
130
244
|
if (resolved === null)
|
|
131
245
|
throw new errors_js_1.NotConfiguredError();
|
|
246
|
+
// A config built by hand bypasses configure(), so check it here too.
|
|
247
|
+
(0, config_js_1.requireSecureBaseUrl)(resolved.baseUrl);
|
|
132
248
|
this.config = resolved;
|
|
133
249
|
}
|
|
134
250
|
headers(idempotencyKey) {
|
|
@@ -142,15 +258,7 @@ class CloudClient {
|
|
|
142
258
|
};
|
|
143
259
|
}
|
|
144
260
|
raiseFor(status, payload) {
|
|
145
|
-
|
|
146
|
-
const detail = payload.detail ?? {};
|
|
147
|
-
if (status === 401)
|
|
148
|
-
throw new errors_js_1.CloudError(`authentication failed: ${message}`, status);
|
|
149
|
-
if (status === 413)
|
|
150
|
-
throw new errors_js_1.TooLargeError(message, status, detail);
|
|
151
|
-
if (status === 429)
|
|
152
|
-
throw new errors_js_1.QuotaExceededError(message, status, detail);
|
|
153
|
-
throw new errors_js_1.CloudError(message, status, detail);
|
|
261
|
+
throw cloudErrorFor(status, payload);
|
|
154
262
|
}
|
|
155
263
|
async redact(text, options) {
|
|
156
264
|
const doFetch = requireFetch(options.fetchImpl);
|
|
@@ -189,7 +297,8 @@ class CloudClient {
|
|
|
189
297
|
}
|
|
190
298
|
if (response.status === 200)
|
|
191
299
|
return toResult(payload, text);
|
|
192
|
-
if (!RETRY_STATUS.has(response.status) || attempt >= this.config.maxRetries
|
|
300
|
+
if (!exports.RETRY_STATUS.has(response.status) || attempt >= this.config.maxRetries
|
|
301
|
+
|| (response.status === 429 && isQuota(payload))) {
|
|
193
302
|
this.raiseFor(response.status, payload);
|
|
194
303
|
}
|
|
195
304
|
}
|
|
@@ -212,7 +321,7 @@ class CloudClient {
|
|
|
212
321
|
if (response.status === 200 && payload.redacted_text !== undefined) {
|
|
213
322
|
return payload;
|
|
214
323
|
}
|
|
215
|
-
if (response.status !== 200 && !RETRY_STATUS.has(response.status)) {
|
|
324
|
+
if (response.status !== 200 && !exports.RETRY_STATUS.has(response.status)) {
|
|
216
325
|
this.raiseFor(response.status, payload);
|
|
217
326
|
}
|
|
218
327
|
if (Date.now() >= deadline) {
|
|
@@ -235,6 +344,7 @@ class CloudClient {
|
|
|
235
344
|
}
|
|
236
345
|
}
|
|
237
346
|
exports.CloudClient = CloudClient;
|
|
347
|
+
/** @internal */
|
|
238
348
|
async function readJson(response) {
|
|
239
349
|
try {
|
|
240
350
|
const data = await response.json();
|
package/dist/cjs/cloud/config.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/** [CLOUD EXTENSION] Cloud tier configuration. */
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.DEFAULT_MAX_RETRIES = exports.DEFAULT_POLL_TIMEOUT_MS = exports.DEFAULT_TIMEOUT_MS = exports.DEFAULT_BASE_URL = void 0;
|
|
5
|
+
exports.requireSecureBaseUrl = requireSecureBaseUrl;
|
|
5
6
|
exports.configure = configure;
|
|
6
7
|
exports.getConfig = getConfig;
|
|
7
8
|
exports.reset = reset;
|
|
@@ -19,6 +20,29 @@ exports.DEFAULT_TIMEOUT_MS = 90_000;
|
|
|
19
20
|
*/
|
|
20
21
|
exports.DEFAULT_POLL_TIMEOUT_MS = 900_000;
|
|
21
22
|
exports.DEFAULT_MAX_RETRIES = 3;
|
|
23
|
+
/** Hosts a plain-HTTP base URL may name: a gateway or TLS proxy on this machine. */
|
|
24
|
+
const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
|
|
25
|
+
/**
|
|
26
|
+
* Refuse a cloud address that would not use TLS. The API key and the text sent
|
|
27
|
+
* must travel encrypted; plain `http` is allowed only to a loopback host, for a
|
|
28
|
+
* local gateway or a TLS-terminating proxy on the same machine (rules-engine#86).
|
|
29
|
+
*/
|
|
30
|
+
function requireSecureBaseUrl(baseUrl) {
|
|
31
|
+
// Parsed by hand rather than with `URL`: a runtime without that global would
|
|
32
|
+
// otherwise refuse every address, https included, with a misleading message.
|
|
33
|
+
const m = /^([a-z][a-z0-9+.-]*):\/\/(?:[^@/?#]*@)?(\[[^\]]*\]|[^:/?#]*)/i.exec(baseUrl);
|
|
34
|
+
if (m !== null) {
|
|
35
|
+
const scheme = m[1].toLowerCase();
|
|
36
|
+
const host = m[2].toLowerCase();
|
|
37
|
+
if (scheme === "https" && host)
|
|
38
|
+
return;
|
|
39
|
+
if (scheme === "http" && LOOPBACK_HOSTS.has(host))
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
throw new Error(`refusing cloud base URL ${JSON.stringify(baseUrl)}: it must start with https:// ` +
|
|
43
|
+
"(plain http:// is allowed only to localhost, 127.0.0.1 or ::1), because the " +
|
|
44
|
+
"API key and document text would otherwise travel unencrypted");
|
|
45
|
+
}
|
|
22
46
|
let config = null;
|
|
23
47
|
/** Read an env var where there is an environment to read. Browsers have none. */
|
|
24
48
|
function env(name) {
|
|
@@ -36,10 +60,12 @@ function configure(options = {}) {
|
|
|
36
60
|
if (!apiKey) {
|
|
37
61
|
throw new Error("no API key: pass configure({ apiKey }) or set EUREDACT_API_KEY");
|
|
38
62
|
}
|
|
63
|
+
const baseUrl = (options.baseUrl ?? env("EUREDACT_BASE_URL") ?? exports.DEFAULT_BASE_URL)
|
|
64
|
+
.replace(/\/+$/, "");
|
|
65
|
+
requireSecureBaseUrl(baseUrl);
|
|
39
66
|
config = {
|
|
40
67
|
apiKey,
|
|
41
|
-
baseUrl
|
|
42
|
-
.replace(/\/+$/, ""),
|
|
68
|
+
baseUrl,
|
|
43
69
|
timeoutMs: options.timeoutMs ?? exports.DEFAULT_TIMEOUT_MS,
|
|
44
70
|
pollTimeoutMs: options.pollTimeoutMs ?? exports.DEFAULT_POLL_TIMEOUT_MS,
|
|
45
71
|
maxRetries: options.maxRetries ?? exports.DEFAULT_MAX_RETRIES,
|
package/dist/cjs/cloud/errors.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/** [CLOUD EXTENSION] Errors the cloud tier can raise. */
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.TooLargeError = exports.QuotaExceededError = exports.NotConfiguredError = exports.CloudError = void 0;
|
|
4
|
+
exports.TooLargeError = exports.ResultExpiredError = exports.NotFoundError = exports.RateLimitedError = exports.QuotaExceededError = exports.NotConfiguredError = exports.CloudError = void 0;
|
|
5
5
|
class CloudError extends Error {
|
|
6
6
|
status;
|
|
7
7
|
detail;
|
|
@@ -31,7 +31,11 @@ class NotConfiguredError extends CloudError {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
exports.NotConfiguredError = NotConfiguredError;
|
|
34
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* 429: the account's daily document quota is used up. Thrown at once, not
|
|
36
|
+
* after retries: the gateway's quota answer (JSON with `detail.used` and
|
|
37
|
+
* `detail.limit`) will not change before the day does (rules-engine#89).
|
|
38
|
+
*/
|
|
35
39
|
class QuotaExceededError extends CloudError {
|
|
36
40
|
constructor(message, status, detail) {
|
|
37
41
|
super(message, status, detail);
|
|
@@ -39,6 +43,39 @@ class QuotaExceededError extends CloudError {
|
|
|
39
43
|
}
|
|
40
44
|
}
|
|
41
45
|
exports.QuotaExceededError = QuotaExceededError;
|
|
46
|
+
/**
|
|
47
|
+
* 429 from the edge's rate limit, still there after every retry. It clears
|
|
48
|
+
* within seconds, so it is retried with backoff first. A subclass of
|
|
49
|
+
* `QuotaExceededError`, which is what every 429 threw before the two were told apart.
|
|
50
|
+
*/
|
|
51
|
+
class RateLimitedError extends QuotaExceededError {
|
|
52
|
+
constructor(message, status, detail) {
|
|
53
|
+
super(message, status, detail);
|
|
54
|
+
this.name = "RateLimitedError";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.RateLimitedError = RateLimitedError;
|
|
58
|
+
/** 404: no such job, batch or key -- or another account's, which the service
|
|
59
|
+
* deliberately does not distinguish. */
|
|
60
|
+
class NotFoundError extends CloudError {
|
|
61
|
+
constructor(message, status, detail) {
|
|
62
|
+
super(message, status, detail);
|
|
63
|
+
this.name = "NotFoundError";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.NotFoundError = NotFoundError;
|
|
67
|
+
/**
|
|
68
|
+
* 410: the job succeeded, but its result is no longer retained -- swept by the
|
|
69
|
+
* account's retention period, or discarded on delivery under no-retention. An
|
|
70
|
+
* empty result here would read like a document with no personal data in it.
|
|
71
|
+
*/
|
|
72
|
+
class ResultExpiredError extends CloudError {
|
|
73
|
+
constructor(message, status, detail) {
|
|
74
|
+
super(message, status, detail);
|
|
75
|
+
this.name = "ResultExpiredError";
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.ResultExpiredError = ResultExpiredError;
|
|
42
79
|
/**
|
|
43
80
|
* 413. The document is over the service's input cap.
|
|
44
81
|
*
|
package/dist/cjs/cloud/index.js
CHANGED
|
@@ -16,16 +16,31 @@
|
|
|
16
16
|
* ```
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.TooLargeError = exports.QuotaExceededError = exports.NotConfiguredError = exports.CloudError = exports.DEFAULT_BASE_URL = exports.reset = exports.getConfig = exports.configure = exports.CloudClient = void 0;
|
|
19
|
+
exports.TooLargeError = exports.ResultExpiredError = exports.RateLimitedError = exports.QuotaExceededError = exports.NotFoundError = exports.NotConfiguredError = exports.CloudError = exports.DEFAULT_BASE_URL = exports.requireSecureBaseUrl = exports.reset = exports.getConfig = exports.configure = exports.MAX_DOCUMENT_TOKENS = exports.MAX_BODY_BYTES = exports.CUSTOM_ID = exports.MAX_CUSTOM_ID = exports.MAX_DOCUMENTS = exports.BatchError = exports.Batches = exports.Jobs = exports.Account = exports.CloudClient = void 0;
|
|
20
20
|
var client_js_1 = require("./client.js");
|
|
21
21
|
Object.defineProperty(exports, "CloudClient", { enumerable: true, get: function () { return client_js_1.CloudClient; } });
|
|
22
|
+
var account_js_1 = require("./account.js");
|
|
23
|
+
Object.defineProperty(exports, "Account", { enumerable: true, get: function () { return account_js_1.Account; } });
|
|
24
|
+
Object.defineProperty(exports, "Jobs", { enumerable: true, get: function () { return account_js_1.Jobs; } });
|
|
25
|
+
var batches_js_1 = require("./batches.js");
|
|
26
|
+
Object.defineProperty(exports, "Batches", { enumerable: true, get: function () { return batches_js_1.Batches; } });
|
|
27
|
+
Object.defineProperty(exports, "BatchError", { enumerable: true, get: function () { return batches_js_1.BatchError; } });
|
|
28
|
+
Object.defineProperty(exports, "MAX_DOCUMENTS", { enumerable: true, get: function () { return batches_js_1.MAX_DOCUMENTS; } });
|
|
29
|
+
Object.defineProperty(exports, "MAX_CUSTOM_ID", { enumerable: true, get: function () { return batches_js_1.MAX_CUSTOM_ID; } });
|
|
30
|
+
Object.defineProperty(exports, "CUSTOM_ID", { enumerable: true, get: function () { return batches_js_1.CUSTOM_ID; } });
|
|
31
|
+
Object.defineProperty(exports, "MAX_BODY_BYTES", { enumerable: true, get: function () { return batches_js_1.MAX_BODY_BYTES; } });
|
|
32
|
+
Object.defineProperty(exports, "MAX_DOCUMENT_TOKENS", { enumerable: true, get: function () { return batches_js_1.MAX_DOCUMENT_TOKENS; } });
|
|
22
33
|
var config_js_1 = require("./config.js");
|
|
23
34
|
Object.defineProperty(exports, "configure", { enumerable: true, get: function () { return config_js_1.configure; } });
|
|
24
35
|
Object.defineProperty(exports, "getConfig", { enumerable: true, get: function () { return config_js_1.getConfig; } });
|
|
25
36
|
Object.defineProperty(exports, "reset", { enumerable: true, get: function () { return config_js_1.reset; } });
|
|
37
|
+
Object.defineProperty(exports, "requireSecureBaseUrl", { enumerable: true, get: function () { return config_js_1.requireSecureBaseUrl; } });
|
|
26
38
|
Object.defineProperty(exports, "DEFAULT_BASE_URL", { enumerable: true, get: function () { return config_js_1.DEFAULT_BASE_URL; } });
|
|
27
39
|
var errors_js_1 = require("./errors.js");
|
|
28
40
|
Object.defineProperty(exports, "CloudError", { enumerable: true, get: function () { return errors_js_1.CloudError; } });
|
|
29
41
|
Object.defineProperty(exports, "NotConfiguredError", { enumerable: true, get: function () { return errors_js_1.NotConfiguredError; } });
|
|
42
|
+
Object.defineProperty(exports, "NotFoundError", { enumerable: true, get: function () { return errors_js_1.NotFoundError; } });
|
|
30
43
|
Object.defineProperty(exports, "QuotaExceededError", { enumerable: true, get: function () { return errors_js_1.QuotaExceededError; } });
|
|
44
|
+
Object.defineProperty(exports, "RateLimitedError", { enumerable: true, get: function () { return errors_js_1.RateLimitedError; } });
|
|
45
|
+
Object.defineProperty(exports, "ResultExpiredError", { enumerable: true, get: function () { return errors_js_1.ResultExpiredError; } });
|
|
31
46
|
Object.defineProperty(exports, "TooLargeError", { enumerable: true, get: function () { return errors_js_1.TooLargeError; } });
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TooLargeError = exports.QuotaExceededError = exports.NotConfiguredError = exports.CloudError = exports.CloudClient = exports.getConfig = exports.configure = exports.SEED_BIC6_PREFIXES = exports.getBicRegistry = exports.setBicRegistry = exports.COUNTRY_CONFIGS = exports.restore = exports.EuRedact = exports.DocumentContext = exports.canonicalType = exports.LEGACY_TYPE_ALIASES = exports.DetectionSource = exports.EntityType = void 0;
|
|
3
|
+
exports.TooLargeError = exports.ResultExpiredError = exports.RateLimitedError = exports.QuotaExceededError = exports.NotFoundError = exports.NotConfiguredError = exports.Jobs = exports.CUSTOM_ID = exports.BatchError = exports.Batches = exports.CloudError = exports.CloudClient = exports.Account = exports.getConfig = exports.configure = exports.SEED_BIC6_PREFIXES = exports.getBicRegistry = exports.setBicRegistry = exports.COUNTRY_CONFIGS = exports.restore = exports.EuRedact = exports.DocumentContext = exports.canonicalType = exports.LEGACY_TYPE_ALIASES = exports.DetectionSource = exports.EntityType = void 0;
|
|
4
4
|
exports.availableCountries = availableCountries;
|
|
5
5
|
exports.redact = redact;
|
|
6
6
|
exports.redactAsync = redactAsync;
|
|
@@ -26,10 +26,18 @@ Object.defineProperty(exports, "SEED_BIC6_PREFIXES", { enumerable: true, get: fu
|
|
|
26
26
|
var index_js_2 = require("./cloud/index.js");
|
|
27
27
|
Object.defineProperty(exports, "configure", { enumerable: true, get: function () { return index_js_2.configure; } });
|
|
28
28
|
Object.defineProperty(exports, "getConfig", { enumerable: true, get: function () { return index_js_2.getConfig; } });
|
|
29
|
+
Object.defineProperty(exports, "Account", { enumerable: true, get: function () { return index_js_2.Account; } });
|
|
29
30
|
Object.defineProperty(exports, "CloudClient", { enumerable: true, get: function () { return index_js_2.CloudClient; } });
|
|
30
31
|
Object.defineProperty(exports, "CloudError", { enumerable: true, get: function () { return index_js_2.CloudError; } });
|
|
32
|
+
Object.defineProperty(exports, "Batches", { enumerable: true, get: function () { return index_js_2.Batches; } });
|
|
33
|
+
Object.defineProperty(exports, "BatchError", { enumerable: true, get: function () { return index_js_2.BatchError; } });
|
|
34
|
+
Object.defineProperty(exports, "CUSTOM_ID", { enumerable: true, get: function () { return index_js_2.CUSTOM_ID; } });
|
|
35
|
+
Object.defineProperty(exports, "Jobs", { enumerable: true, get: function () { return index_js_2.Jobs; } });
|
|
31
36
|
Object.defineProperty(exports, "NotConfiguredError", { enumerable: true, get: function () { return index_js_2.NotConfiguredError; } });
|
|
37
|
+
Object.defineProperty(exports, "NotFoundError", { enumerable: true, get: function () { return index_js_2.NotFoundError; } });
|
|
32
38
|
Object.defineProperty(exports, "QuotaExceededError", { enumerable: true, get: function () { return index_js_2.QuotaExceededError; } });
|
|
39
|
+
Object.defineProperty(exports, "RateLimitedError", { enumerable: true, get: function () { return index_js_2.RateLimitedError; } });
|
|
40
|
+
Object.defineProperty(exports, "ResultExpiredError", { enumerable: true, get: function () { return index_js_2.ResultExpiredError; } });
|
|
33
41
|
Object.defineProperty(exports, "TooLargeError", { enumerable: true, get: function () { return index_js_2.TooLargeError; } });
|
|
34
42
|
const sdk_js_2 = require("./sdk.js");
|
|
35
43
|
const index_js_3 = require("./rules/countries/index.js");
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `platform.ts` for browsers: no `node:` imports, so the package bundles for a
|
|
4
|
+
* page. Selected by the `browser` field in package.json (rules-engine#56).
|
|
5
|
+
*
|
|
6
|
+
* The cache key is computed synchronously and must stay collision-resistant
|
|
7
|
+
* (see `ResultCache.key`), and Web Crypto's `subtle.digest` is asynchronous, so
|
|
8
|
+
* SHA-256 is implemented here. A test checks it against `node:crypto` byte for
|
|
9
|
+
* byte, astral characters and lone surrogates included.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.sha256Hex = sha256Hex;
|
|
13
|
+
exports.randomIndex = randomIndex;
|
|
14
|
+
exports.defaultBatchStore = defaultBatchStore;
|
|
15
|
+
exports.gzip = gzip;
|
|
16
|
+
const K = new Uint32Array([
|
|
17
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
18
|
+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
19
|
+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
20
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
21
|
+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
22
|
+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
23
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
24
|
+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
|
25
|
+
]);
|
|
26
|
+
/** Hex SHA-256 of the UTF-8 encoding of `parts`, joined with nothing between. */
|
|
27
|
+
function sha256Hex(parts) {
|
|
28
|
+
// TextEncoder replaces a lone surrogate with U+FFFD, as Node's hash.update does.
|
|
29
|
+
const bytes = new TextEncoder().encode(parts.join(""));
|
|
30
|
+
const bitLength = bytes.length * 8;
|
|
31
|
+
const padded = new Uint8Array(((bytes.length + 9 + 63) >> 6) << 6);
|
|
32
|
+
padded.set(bytes);
|
|
33
|
+
padded[bytes.length] = 0x80;
|
|
34
|
+
const view = new DataView(padded.buffer);
|
|
35
|
+
view.setUint32(padded.length - 8, Math.floor(bitLength / 0x100000000));
|
|
36
|
+
view.setUint32(padded.length - 4, bitLength >>> 0);
|
|
37
|
+
const h = new Uint32Array([
|
|
38
|
+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
|
39
|
+
]);
|
|
40
|
+
const w = new Uint32Array(64);
|
|
41
|
+
for (let offset = 0; offset < padded.length; offset += 64) {
|
|
42
|
+
for (let i = 0; i < 16; i++)
|
|
43
|
+
w[i] = view.getUint32(offset + i * 4);
|
|
44
|
+
for (let i = 16; i < 64; i++) {
|
|
45
|
+
const a = w[i - 15], b = w[i - 2];
|
|
46
|
+
const s0 = ((a >>> 7) | (a << 25)) ^ ((a >>> 18) | (a << 14)) ^ (a >>> 3);
|
|
47
|
+
const s1 = ((b >>> 17) | (b << 15)) ^ ((b >>> 19) | (b << 13)) ^ (b >>> 10);
|
|
48
|
+
w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;
|
|
49
|
+
}
|
|
50
|
+
let [a, b, c, d, e, f, g, hh] = h;
|
|
51
|
+
for (let i = 0; i < 64; i++) {
|
|
52
|
+
const S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));
|
|
53
|
+
const ch = (e & f) ^ (~e & g);
|
|
54
|
+
const t1 = (hh + S1 + ch + K[i] + w[i]) >>> 0;
|
|
55
|
+
const S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));
|
|
56
|
+
const maj = (a & b) ^ (a & c) ^ (b & c);
|
|
57
|
+
const t2 = (S0 + maj) >>> 0;
|
|
58
|
+
hh = g;
|
|
59
|
+
g = f;
|
|
60
|
+
f = e;
|
|
61
|
+
e = (d + t1) >>> 0;
|
|
62
|
+
d = c;
|
|
63
|
+
c = b;
|
|
64
|
+
b = a;
|
|
65
|
+
a = (t1 + t2) >>> 0;
|
|
66
|
+
}
|
|
67
|
+
h[0] = (h[0] + a) >>> 0;
|
|
68
|
+
h[1] = (h[1] + b) >>> 0;
|
|
69
|
+
h[2] = (h[2] + c) >>> 0;
|
|
70
|
+
h[3] = (h[3] + d) >>> 0;
|
|
71
|
+
h[4] = (h[4] + e) >>> 0;
|
|
72
|
+
h[5] = (h[5] + f) >>> 0;
|
|
73
|
+
h[6] = (h[6] + g) >>> 0;
|
|
74
|
+
h[7] = (h[7] + hh) >>> 0;
|
|
75
|
+
}
|
|
76
|
+
return Array.from(h, x => x.toString(16).padStart(8, "0")).join("");
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* A uniformly random integer in `[0, n)`, from `crypto.getRandomValues`.
|
|
80
|
+
* Rejection sampling: a plain modulo would favour the low residues.
|
|
81
|
+
*/
|
|
82
|
+
function randomIndex(n) {
|
|
83
|
+
const limit = Math.floor(0x100000000 / n) * n;
|
|
84
|
+
const buf = new Uint32Array(1);
|
|
85
|
+
for (;;) {
|
|
86
|
+
globalThis.crypto.getRandomValues(buf);
|
|
87
|
+
if (buf[0] < limit)
|
|
88
|
+
return buf[0] % n;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** A page has no filesystem: the caller passes `store` to `Batches`. */
|
|
92
|
+
function defaultBatchStore(_directory) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
/** No synchronous gzip in a page; the batch is uploaded uncompressed. */
|
|
96
|
+
function gzip(_data) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.sha256Hex = sha256Hex;
|
|
37
|
+
exports.randomIndex = randomIndex;
|
|
38
|
+
exports.defaultBatchStore = defaultBatchStore;
|
|
39
|
+
exports.gzip = gzip;
|
|
40
|
+
/**
|
|
41
|
+
* The two things the SDK needs from a crypto library: a SHA-256 digest for the
|
|
42
|
+
* result-cache key and a uniform random index for token suffixes.
|
|
43
|
+
*
|
|
44
|
+
* This is the Node implementation, on `node:crypto`. A browser bundle gets
|
|
45
|
+
* `platform.browser.ts` instead, through the `browser` field in package.json,
|
|
46
|
+
* because `node:crypto` does not exist there and importing it from `cache.ts`
|
|
47
|
+
* and `sdk.ts` made the package unbundleable for a page (rules-engine#56).
|
|
48
|
+
* Every other module imports these two functions from here and nothing from
|
|
49
|
+
* `node:`; a test holds the source tree to that.
|
|
50
|
+
*/
|
|
51
|
+
const node_crypto_1 = require("node:crypto");
|
|
52
|
+
const fs = __importStar(require("node:fs"));
|
|
53
|
+
const os = __importStar(require("node:os"));
|
|
54
|
+
const path = __importStar(require("node:path"));
|
|
55
|
+
const node_zlib_1 = require("node:zlib");
|
|
56
|
+
/** Hex SHA-256 of the UTF-8 encoding of `parts`, joined with nothing between. */
|
|
57
|
+
function sha256Hex(parts) {
|
|
58
|
+
const hash = (0, node_crypto_1.createHash)("sha256");
|
|
59
|
+
for (const part of parts)
|
|
60
|
+
hash.update(part);
|
|
61
|
+
return hash.digest("hex");
|
|
62
|
+
}
|
|
63
|
+
/** A uniformly random integer in `[0, n)`. */
|
|
64
|
+
function randomIndex(n) {
|
|
65
|
+
return (0, node_crypto_1.randomInt)(n);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* `<dir>/<batchId>.json`, directory `0700`, files `0600`, the default local
|
|
69
|
+
* batch store. Writes go to a temporary file in the same directory, created
|
|
70
|
+
* `0600` before any byte is written, then renamed over the old one.
|
|
71
|
+
*/
|
|
72
|
+
function defaultBatchStore(directory) {
|
|
73
|
+
const dir = directory ?? path.join(os.homedir(), ".euredact", "batches");
|
|
74
|
+
const fileFor = (batchId) => {
|
|
75
|
+
if (!batchId || /[/\\]/.test(batchId) || batchId === "." || batchId === "..") {
|
|
76
|
+
throw new Error(`not a batch id: ${JSON.stringify(batchId)}`);
|
|
77
|
+
}
|
|
78
|
+
return path.join(dir, `${batchId}.json`);
|
|
79
|
+
};
|
|
80
|
+
return {
|
|
81
|
+
async read(batchId) {
|
|
82
|
+
try {
|
|
83
|
+
return new Uint8Array(fs.readFileSync(fileFor(batchId)));
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
if (err.code === "ENOENT")
|
|
87
|
+
return null;
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
async write(batchId, data) {
|
|
92
|
+
const target = fileFor(batchId);
|
|
93
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
94
|
+
try {
|
|
95
|
+
fs.chmodSync(dir, 0o700);
|
|
96
|
+
}
|
|
97
|
+
catch { /* not ours to change */ }
|
|
98
|
+
const tmp = path.join(dir, `.${batchId}.${process.pid}.${Date.now()}.tmp`);
|
|
99
|
+
const fd = fs.openSync(tmp, "wx", 0o600);
|
|
100
|
+
try {
|
|
101
|
+
fs.writeSync(fd, data);
|
|
102
|
+
fs.fsyncSync(fd);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
fs.closeSync(fd);
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
fs.renameSync(tmp, target);
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
try {
|
|
112
|
+
fs.unlinkSync(tmp);
|
|
113
|
+
}
|
|
114
|
+
catch { /* already gone */ }
|
|
115
|
+
throw err;
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
async delete(batchId) {
|
|
119
|
+
try {
|
|
120
|
+
fs.unlinkSync(fileFor(batchId));
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
if (err.code !== "ENOENT")
|
|
124
|
+
throw err;
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
async list() {
|
|
128
|
+
if (!fs.existsSync(dir))
|
|
129
|
+
return [];
|
|
130
|
+
return fs.readdirSync(dir)
|
|
131
|
+
.filter(name => name.endsWith(".json") && !name.startsWith("."))
|
|
132
|
+
.map(name => name.slice(0, -".json".length))
|
|
133
|
+
.sort();
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/** Gzip, for the batch upload. */
|
|
138
|
+
function gzip(data) {
|
|
139
|
+
return new Uint8Array((0, node_zlib_1.gzipSync)(data));
|
|
140
|
+
}
|