@cloudbitmaps/core 0.1.0-rc.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/LICENSE +202 -0
- package/NOTICE +12 -0
- package/README.md +22 -0
- package/dist/azure/index.cjs +368 -0
- package/dist/azure/index.cjs.map +1 -0
- package/dist/azure/index.d.cts +48 -0
- package/dist/azure/index.d.ts +48 -0
- package/dist/azure/index.js +304 -0
- package/dist/azure/index.js.map +1 -0
- package/dist/cassandra/index.cjs +265 -0
- package/dist/cassandra/index.cjs.map +1 -0
- package/dist/cassandra/index.d.cts +52 -0
- package/dist/cassandra/index.d.ts +52 -0
- package/dist/cassandra/index.js +204 -0
- package/dist/cassandra/index.js.map +1 -0
- package/dist/chunk-2YDULGXS.js +203 -0
- package/dist/chunk-2YDULGXS.js.map +1 -0
- package/dist/chunk-7LMLYSVJ.js +43 -0
- package/dist/chunk-7LMLYSVJ.js.map +1 -0
- package/dist/chunk-AS6ODRLT.js +6 -0
- package/dist/chunk-AS6ODRLT.js.map +1 -0
- package/dist/chunk-NUIDEEFZ.js +91 -0
- package/dist/chunk-NUIDEEFZ.js.map +1 -0
- package/dist/chunk-SNJVZ227.js +35 -0
- package/dist/chunk-SNJVZ227.js.map +1 -0
- package/dist/dynamodb/index.cjs +731 -0
- package/dist/dynamodb/index.cjs.map +1 -0
- package/dist/dynamodb/index.d.cts +106 -0
- package/dist/dynamodb/index.d.ts +106 -0
- package/dist/dynamodb/index.js +460 -0
- package/dist/dynamodb/index.js.map +1 -0
- package/dist/gcs/index.cjs +343 -0
- package/dist/gcs/index.cjs.map +1 -0
- package/dist/gcs/index.d.cts +46 -0
- package/dist/gcs/index.d.ts +46 -0
- package/dist/gcs/index.js +279 -0
- package/dist/gcs/index.js.map +1 -0
- package/dist/index.cjs +4031 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1945 -0
- package/dist/index.d.ts +1945 -0
- package/dist/index.js +3642 -0
- package/dist/index.js.map +1 -0
- package/dist/mongodb/index.cjs +260 -0
- package/dist/mongodb/index.cjs.map +1 -0
- package/dist/mongodb/index.d.cts +45 -0
- package/dist/mongodb/index.d.ts +45 -0
- package/dist/mongodb/index.js +199 -0
- package/dist/mongodb/index.js.map +1 -0
- package/dist/mysql/index.cjs +281 -0
- package/dist/mysql/index.cjs.map +1 -0
- package/dist/mysql/index.d.cts +56 -0
- package/dist/mysql/index.d.ts +56 -0
- package/dist/mysql/index.js +211 -0
- package/dist/mysql/index.js.map +1 -0
- package/dist/ports-D3BrJ6ax.d.cts +357 -0
- package/dist/ports-D3BrJ6ax.d.ts +357 -0
- package/dist/postgres/index.cjs +273 -0
- package/dist/postgres/index.cjs.map +1 -0
- package/dist/postgres/index.d.cts +54 -0
- package/dist/postgres/index.d.ts +54 -0
- package/dist/postgres/index.js +203 -0
- package/dist/postgres/index.js.map +1 -0
- package/dist/redis/index.cjs +257 -0
- package/dist/redis/index.cjs.map +1 -0
- package/dist/redis/index.d.cts +42 -0
- package/dist/redis/index.d.ts +42 -0
- package/dist/redis/index.js +197 -0
- package/dist/redis/index.js.map +1 -0
- package/dist/s3/index.cjs +891 -0
- package/dist/s3/index.cjs.map +1 -0
- package/dist/s3/index.d.cts +112 -0
- package/dist/s3/index.d.ts +112 -0
- package/dist/s3/index.js +555 -0
- package/dist/s3/index.js.map +1 -0
- package/package.json +172 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// src/core/errors.ts
|
|
2
|
+
var ERROR_BRAND = /* @__PURE__ */ Symbol.for("cloud-roaring.error");
|
|
3
|
+
var TRANSIENT_BRAND = /* @__PURE__ */ Symbol.for("cloud-roaring.error.transient");
|
|
4
|
+
var CloudRoaringError = class extends Error {
|
|
5
|
+
/** Cross-bundle brand — see the predicates ({@link isCloudRoaringError}, …). Non-enumerable-ish (symbol key ⇒ not in JSON). */
|
|
6
|
+
[ERROR_BRAND] = true;
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = new.target.name;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var ValidationError = class extends CloudRoaringError {
|
|
13
|
+
};
|
|
14
|
+
var WriteConflictError = class extends CloudRoaringError {
|
|
15
|
+
};
|
|
16
|
+
var IntegrityError = class extends CloudRoaringError {
|
|
17
|
+
};
|
|
18
|
+
var NotFoundError = class extends CloudRoaringError {
|
|
19
|
+
};
|
|
20
|
+
var UnsupportedError = class extends CloudRoaringError {
|
|
21
|
+
};
|
|
22
|
+
var CapabilityError = class extends CloudRoaringError {
|
|
23
|
+
};
|
|
24
|
+
var BudgetExceededError = class extends CloudRoaringError {
|
|
25
|
+
};
|
|
26
|
+
var KeyUnavailableError = class extends CloudRoaringError {
|
|
27
|
+
};
|
|
28
|
+
var TransientError = class extends CloudRoaringError {
|
|
29
|
+
/** A second brand so the whole transient subtree (incl. {@link TimeoutError}) is classifiable cross-bundle. */
|
|
30
|
+
[TRANSIENT_BRAND] = true;
|
|
31
|
+
constructor(message, options) {
|
|
32
|
+
super(message);
|
|
33
|
+
if (options && "cause" in options) this.cause = options.cause;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var TimeoutError = class extends TransientError {
|
|
37
|
+
};
|
|
38
|
+
function hasBrand(err, brand) {
|
|
39
|
+
return typeof err === "object" && err !== null && err[brand] === true;
|
|
40
|
+
}
|
|
41
|
+
function isCloudRoaringError(err) {
|
|
42
|
+
return hasBrand(err, ERROR_BRAND);
|
|
43
|
+
}
|
|
44
|
+
function isWriteConflictError(err) {
|
|
45
|
+
return isCloudRoaringError(err) && err.name === "WriteConflictError";
|
|
46
|
+
}
|
|
47
|
+
function isTransientError(err) {
|
|
48
|
+
return hasBrand(err, TRANSIENT_BRAND);
|
|
49
|
+
}
|
|
50
|
+
function isNotFoundError(err) {
|
|
51
|
+
return isCloudRoaringError(err) && err.name === "NotFoundError";
|
|
52
|
+
}
|
|
53
|
+
function isIntegrityError(err) {
|
|
54
|
+
return isCloudRoaringError(err) && err.name === "IntegrityError";
|
|
55
|
+
}
|
|
56
|
+
function isValidationError(err) {
|
|
57
|
+
return isCloudRoaringError(err) && err.name === "ValidationError";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/core/validate.ts
|
|
61
|
+
var CHUNK_KEY_MAX = 65535;
|
|
62
|
+
var NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,255}$/;
|
|
63
|
+
function validatePart(value, field) {
|
|
64
|
+
if (typeof value !== "string" || !NAME.test(value) || value.includes("..")) {
|
|
65
|
+
throw new ValidationError(
|
|
66
|
+
`${field} must match ${String(NAME)} and contain no "..": got ${JSON.stringify(value)}`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function validateSegmentRef(ref) {
|
|
71
|
+
validatePart(ref.segment, "segment");
|
|
72
|
+
if (ref.namespace !== void 0) validatePart(ref.namespace, "namespace");
|
|
73
|
+
}
|
|
74
|
+
function validateChunkRef(ref) {
|
|
75
|
+
validateSegmentRef(ref);
|
|
76
|
+
if (!Number.isInteger(ref.chunkKey) || ref.chunkKey < 0 || ref.chunkKey > CHUNK_KEY_MAX) {
|
|
77
|
+
throw new ValidationError(
|
|
78
|
+
`chunkKey must be an integer in [0, ${CHUNK_KEY_MAX}]; got ${ref.chunkKey}`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/drivers/_shared/keys.ts
|
|
84
|
+
var DEFAULT_NAMESPACE = "_default";
|
|
85
|
+
function namespacePart(namespace) {
|
|
86
|
+
return namespace ?? DEFAULT_NAMESPACE;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { BudgetExceededError, CapabilityError, CloudRoaringError, DEFAULT_NAMESPACE, IntegrityError, KeyUnavailableError, NotFoundError, TimeoutError, TransientError, UnsupportedError, ValidationError, WriteConflictError, isCloudRoaringError, isIntegrityError, isNotFoundError, isTransientError, isValidationError, isWriteConflictError, namespacePart, validateChunkRef, validateSegmentRef };
|
|
90
|
+
//# sourceMappingURL=chunk-NUIDEEFZ.js.map
|
|
91
|
+
//# sourceMappingURL=chunk-NUIDEEFZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/errors.ts","../src/core/validate.ts","../src/drivers/_shared/keys.ts"],"names":[],"mappings":"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","file":"chunk-NUIDEEFZ.js","sourcesContent":["/**\n * Typed errors — callers learn *why* something failed, never by parsing strings. Retry is the driver\n * decorators' job, not the engine's: a driver classifies its backend's failures into this vocabulary, and\n * `withRetry` decides what is transient.\n */\n\n/**\n * Registry-symbol brands. The package ships as multiple bundles — the core entry and the `./s3` / `./dynamodb`\n * subpaths — and the builder inlines `core/errors` into each. A driver in a subpath bundle therefore throws a\n * *different* class object than the one the core engine/retry code would `instanceof`-check, so `instanceof`\n * silently returns false across that boundary in the published CJS package (defeating OCC/transient retry and\n * compaction race-handling). These `Symbol.for` brands are identity-stable across bundles/realms; classify\n * errors with the exported predicates below (never `instanceof`) anywhere an error may cross the boundary.\n */\nconst ERROR_BRAND: unique symbol = Symbol.for('cloud-roaring.error');\nconst TRANSIENT_BRAND: unique symbol = Symbol.for('cloud-roaring.error.transient');\n\n/** Base class for every error CloudRoaring throws. */\nexport class CloudRoaringError extends Error {\n /** Cross-bundle brand — see the predicates ({@link isCloudRoaringError}, …). Non-enumerable-ish (symbol key ⇒ not in JSON). */\n readonly [ERROR_BRAND] = true as const;\n constructor(message: string) {\n super(message);\n // Subclass name (works under transpilation since we set it explicitly). Also the discriminator the\n // predicates match on — a runtime string, so it survives bundling where the class identity does not.\n this.name = new.target.name;\n }\n}\n\n/** Invalid caller input (bad id, segment name, options). Raised before any storage call. */\nexport class ValidationError extends CloudRoaringError {}\n\n/** An OCC conditional write/delete lost the race — the row changed since it was read. */\nexport class WriteConflictError extends CloudRoaringError {}\n\n/** Bytes from a tier are corrupt, oversized, or fail a checksum/format check. */\nexport class IntegrityError extends CloudRoaringError {}\n\n/**\n * A requested object/row does not exist. Part of the driver error vocabulary; thrown by\n * persistent drivers from Phase 2 — the Phase-1 engine + in-memory drivers return `null` instead.\n */\nexport class NotFoundError extends CloudRoaringError {}\n\n/**\n * This build/configuration cannot perform the requested operation, though nothing is malformed. Two uses:\n * (1) **format** — the bytes are well-formed but unreadable here (an unknown `.crbm` major version, an\n * encrypted file before the crypto path exists) — distinct from `IntegrityError` (corruption); and (2)\n * **store configuration** — an operation this store's wiring doesn't support (e.g. a lifecycle helper like\n * `compact`/`eraseSubject` called on a store built without a raw cold driver + registry). Raised at\n * operation time, before any mutation.\n */\nexport class UnsupportedError extends CloudRoaringError {}\n\n/**\n * A driver cannot meet a capability the chosen topology requires (e.g. a Cold driver without range\n * reads). Raised fail-fast at wiring time, never mid-operation.\n */\nexport class CapabilityError extends CloudRoaringError {}\n\n/**\n * An operation would exceed its per-op **denial-of-wallet budget** — too many backend requests for a single\n * `count`/`iterate`/`intersect`/`subjectReport`/`eraseSubject` call — so it is refused **before** fanning out\n * (Decision #3 / invariant T3). Default-on but generous (normal ops never hit it); tune it\n * per store (`budget`) or per op, or disable with `budget: false`. Deterministic (never retried): the op is too\n * big by policy, not by luck. Each request's bytes are separately capped (the safe-deserialize ceiling), so\n * bounding the request count transitively bounds bytes. Carries the projected count + the limit, never data.\n */\nexport class BudgetExceededError extends CloudRoaringError {}\n\n/**\n * An encrypted segment's data key (DEK) cannot be unwrapped because the keystore holds none of the\n * key-encryption-keys (KEKs) its wrappings reference — the KEK was never configured, rotated away without\n * keeping the old key, or lost. Deterministic (never retried): without a KEK the ciphertext is unreadable by\n * design. The flip side of crypto-shred — when this is *intended* (a destroyed segment) the registry row is\n * already a `destroyed` tombstone; when it's *not*, restore the missing KEK (or its recovery KEK). Carries no\n * key material.\n */\nexport class KeyUnavailableError extends CloudRoaringError {}\n\n/**\n * A **transient** infrastructure fault that is safe to retry — throttling, a 5xx, a dropped connection,\n * a client-side request timeout. Drivers classify their backend's retryable faults and raise this (the\n * SDK-specific knowledge stays in the SDK-specific driver); the retry layer (`core/retry`) retries **only**\n * this class, never a deterministic error like {@link ValidationError}, {@link IntegrityError},\n * {@link NotFoundError}, or {@link WriteConflictError} (retrying those is pointless or wrong). The original\n * error is preserved in `cause` so callers can still inspect it.\n *\n * Note for logging hygiene (threat-model S12): `cause` is the **raw SDK error**, which may carry operational\n * metadata (endpoint host, request IDs, `$metadata`). The library's own `message` is identifier-only and safe\n * to log; if you serialize the whole error *chain*, be aware you're including that metadata.\n */\nexport class TransientError extends CloudRoaringError {\n /** A second brand so the whole transient subtree (incl. {@link TimeoutError}) is classifiable cross-bundle. */\n readonly [TRANSIENT_BRAND] = true as const;\n constructor(message: string, options?: { cause?: unknown }) {\n super(message);\n if (options && 'cause' in options) this.cause = options.cause;\n }\n}\n\n/**\n * A single attempt exceeded its time budget. Subclass of {@link TransientError} so the retry layer treats a\n * timeout as retryable by default — a stalled request often succeeds on a fresh connection. Raised by a\n * driver whose injected client reports a request timeout — setting one on your injected client is the\n * recommended way to bound a hang.\n */\nexport class TimeoutError extends TransientError {}\n\n/**\n * Bundle-safe error predicates — use these, not `instanceof`, wherever an error may cross the core↔driver\n * (`./s3` / `./dynamodb`) boundary (and prefer them in consumer `catch` blocks too, for the same reason). They\n * match the {@link ERROR_BRAND} registry brand + the runtime `name`, both of which survive separate bundling.\n */\nfunction hasBrand(err: unknown, brand: symbol): boolean {\n return (\n typeof err === 'object' && err !== null && (err as Record<symbol, unknown>)[brand] === true\n );\n}\n\n/** Any error thrown by CloudRoaring (any tier, any bundle). */\nexport function isCloudRoaringError(err: unknown): err is CloudRoaringError {\n return hasBrand(err, ERROR_BRAND);\n}\n\n/** An OCC conditional write/delete lost the race — retry the read-modify-write, don't fail. */\nexport function isWriteConflictError(err: unknown): err is WriteConflictError {\n return isCloudRoaringError(err) && err.name === 'WriteConflictError';\n}\n\n/** A retryable transient infrastructure fault (incl. {@link TimeoutError}). The retry layer keys on this. */\nexport function isTransientError(err: unknown): err is TransientError {\n return hasBrand(err, TRANSIENT_BRAND);\n}\n\n/** A requested object/row does not exist. */\nexport function isNotFoundError(err: unknown): err is NotFoundError {\n return isCloudRoaringError(err) && err.name === 'NotFoundError';\n}\n\n/** Corrupt/oversized/failed-checksum bytes from a tier. */\nexport function isIntegrityError(err: unknown): err is IntegrityError {\n return isCloudRoaringError(err) && err.name === 'IntegrityError';\n}\n\n/** Invalid caller input. */\nexport function isValidationError(err: unknown): err is ValidationError {\n return isCloudRoaringError(err) && err.name === 'ValidationError';\n}\n","/**\n * Boundary validation for segment / namespace names (finding S2).\n * Names become object keys, partition keys, and file paths — so they're validated before they\n * ever reach storage: strict charset, bounded length, no path traversal.\n */\nimport { ValidationError } from './errors';\nimport type { ChunkRef, SegmentRef } from './ports';\n\nconst CHUNK_KEY_MAX = 0xffff;\n\n// The locked name grammar: 1 leading alphanumeric + up to 255 more (max 256 chars).\nconst NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,255}$/;\n\nfunction validatePart(value: string, field: string): void {\n if (typeof value !== 'string' || !NAME.test(value) || value.includes('..')) {\n throw new ValidationError(\n `${field} must match ${String(NAME)} and contain no \"..\": got ${JSON.stringify(value)}`,\n );\n }\n}\n\nexport function validateSegmentRef(ref: SegmentRef): void {\n validatePart(ref.segment, 'segment');\n if (ref.namespace !== undefined) validatePart(ref.namespace, 'namespace');\n}\n\n/** Validate a chunk ref: the segment/namespace grammar plus `chunkKey` ∈ `[0, 65535]` (a u16). */\nexport function validateChunkRef(ref: ChunkRef): void {\n validateSegmentRef(ref);\n if (!Number.isInteger(ref.chunkKey) || ref.chunkKey < 0 || ref.chunkKey > CHUNK_KEY_MAX) {\n throw new ValidationError(\n `chunkKey must be an integer in [0, ${CHUNK_KEY_MAX}]; got ${ref.chunkKey}`,\n );\n }\n}\n","/**\n * Shared key-grammar fragments used by every driver's logical-ref → physical-key mapping (Phase 4c).\n *\n * Extracted once the registry drivers became the 4th/5th consumer of the same `_default` namespace sentinel\n * (it lived copy-pasted in `s3/keys`, `dynamodb/keys`, `localfs/paths`). Pure string logic, no SDK, no I/O —\n * lives in the SDK-free `_shared` bundle so any driver may import it.\n */\n\n/**\n * The physical stand-in for an **absent** namespace. The name grammar forbids a leading underscore\n * so `_default` can never collide with a real\n * namespace — `segment(\"s\")` and `segment(\"s\", { namespace: \"_default\" })` would be a grammar error, not an\n * aliasing hazard.\n */\nexport const DEFAULT_NAMESPACE = '_default';\n\n/** Map an optional namespace to its physical part: the namespace itself, or {@link DEFAULT_NAMESPACE}. */\nexport function namespacePart(namespace: string | undefined): string {\n return namespace ?? DEFAULT_NAMESPACE;\n}\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ValidationError } from './chunk-NUIDEEFZ.js';
|
|
2
|
+
|
|
3
|
+
// src/core/concurrency.ts
|
|
4
|
+
async function mapWithConcurrency(items, limit, fn) {
|
|
5
|
+
if (!Number.isInteger(limit) || limit < 1) {
|
|
6
|
+
throw new ValidationError(`concurrency limit must be a positive integer; got ${limit}`);
|
|
7
|
+
}
|
|
8
|
+
const results = new Array(items.length);
|
|
9
|
+
let next = 0;
|
|
10
|
+
let failed = false;
|
|
11
|
+
let firstError;
|
|
12
|
+
const worker = async () => {
|
|
13
|
+
while (!failed) {
|
|
14
|
+
const i = next++;
|
|
15
|
+
if (i >= items.length) return;
|
|
16
|
+
try {
|
|
17
|
+
results[i] = await fn(items[i], i);
|
|
18
|
+
} catch (err) {
|
|
19
|
+
if (!failed) {
|
|
20
|
+
failed = true;
|
|
21
|
+
firstError = err;
|
|
22
|
+
}
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const workers = Array.from({ length: Math.min(limit, items.length) }, () => worker());
|
|
28
|
+
await Promise.all(workers);
|
|
29
|
+
if (failed) throw firstError;
|
|
30
|
+
return results;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { mapWithConcurrency };
|
|
34
|
+
//# sourceMappingURL=chunk-SNJVZ227.js.map
|
|
35
|
+
//# sourceMappingURL=chunk-SNJVZ227.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/concurrency.ts"],"names":[],"mappings":";;;AAiBA,eAAsB,kBAAA,CACpB,KAAA,EACA,KAAA,EACA,EAAA,EACc;AACd,EAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,KAAK,CAAA,IAAK,QAAQ,CAAA,EAAG;AACzC,IAAA,MAAM,IAAI,eAAA,CAAgB,CAAA,kDAAA,EAAqD,KAAK,CAAA,CAAE,CAAA;AAAA,EACxF;AACA,EAAA,MAAM,OAAA,GAAU,IAAI,KAAA,CAAS,KAAA,CAAM,MAAM,CAAA;AACzC,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,IAAI,MAAA,GAAS,KAAA;AACb,EAAA,IAAI,UAAA;AACJ,EAAA,MAAM,SAAS,YAA2B;AACxC,IAAA,OAAO,CAAC,MAAA,EAAQ;AACd,MAAA,MAAM,CAAA,GAAI,IAAA,EAAA;AACV,MAAA,IAAI,CAAA,IAAK,MAAM,MAAA,EAAQ;AACvB,MAAA,IAAI;AACF,QAAA,OAAA,CAAQ,CAAC,CAAA,GAAI,MAAM,GAAG,KAAA,CAAM,CAAC,GAAI,CAAC,CAAA;AAAA,MACpC,SAAS,GAAA,EAAK;AACZ,QAAA,IAAI,CAAC,MAAA,EAAQ;AACX,UAAA,MAAA,GAAS,IAAA;AACT,UAAA,UAAA,GAAa,GAAA;AAAA,QACf;AACA,QAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAA;AACA,EAAA,MAAM,OAAA,GAAU,KAAA,CAAM,IAAA,CAAK,EAAE,QAAQ,IAAA,CAAK,GAAA,CAAI,KAAA,EAAO,KAAA,CAAM,MAAM,CAAA,EAAE,EAAG,MAAM,QAAQ,CAAA;AACpF,EAAA,MAAM,OAAA,CAAQ,IAAI,OAAO,CAAA;AACzB,EAAA,IAAI,QAAQ,MAAM,UAAA;AAClB,EAAA,OAAO,OAAA;AACT","file":"chunk-SNJVZ227.js","sourcesContent":["/**\n * Bounded-concurrency fan-out — the missing primitive for running async work over many items without either\n * a thundering herd (unbounded `Promise.all` → N simultaneous S3/DynamoDB calls) or full serial latency\n * (`for await`). Pure + I/O-free, so it lives in `core/`; callers inject the async work.\n */\n\nimport { ValidationError } from './errors';\n\n/**\n * Run `fn` over `items` with at most `limit` promises in flight at once, returning results in **input order**\n * (regardless of completion order). If any `fn` rejects, the pool stops scheduling new work, lets the in-flight\n * tasks settle, and rejects with the **first** error — callers that must not abort on one failure (e.g. an\n * erasure ledger that records per-item faults) should catch inside `fn` so it never rejects.\n *\n * `items` is a materialized array on purpose: every caller here already enumerates its source (registry list,\n * S3 page, chunk map), so an array keeps the pool trivial and race-free (no shared-iterator hazard).\n */\nexport async function mapWithConcurrency<T, R>(\n items: readonly T[],\n limit: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n if (!Number.isInteger(limit) || limit < 1) {\n throw new ValidationError(`concurrency limit must be a positive integer; got ${limit}`);\n }\n const results = new Array<R>(items.length);\n let next = 0;\n let failed = false;\n let firstError: unknown;\n const worker = async (): Promise<void> => {\n while (!failed) {\n const i = next++;\n if (i >= items.length) return;\n try {\n results[i] = await fn(items[i]!, i);\n } catch (err) {\n if (!failed) {\n failed = true; // first failure wins; stop scheduling new items (in-flight ones still settle)\n firstError = err;\n }\n return;\n }\n }\n };\n const workers = Array.from({ length: Math.min(limit, items.length) }, () => worker());\n await Promise.all(workers);\n if (failed) throw firstError;\n return results;\n}\n"]}
|