@indigoai-us/hq-cloud 5.46.0 → 5.47.1

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.
Files changed (52) hide show
  1. package/dist/bin/sync-runner.d.ts +12 -0
  2. package/dist/bin/sync-runner.d.ts.map +1 -1
  3. package/dist/bin/sync-runner.js +39 -0
  4. package/dist/bin/sync-runner.js.map +1 -1
  5. package/dist/bin/sync-runner.test.js +27 -1
  6. package/dist/bin/sync-runner.test.js.map +1 -1
  7. package/dist/cli/share.d.ts.map +1 -1
  8. package/dist/cli/share.js +17 -2
  9. package/dist/cli/share.js.map +1 -1
  10. package/dist/cli/share.test.js +2 -0
  11. package/dist/cli/share.test.js.map +1 -1
  12. package/dist/cli/sync-scope.test.js +1 -0
  13. package/dist/cli/sync-scope.test.js.map +1 -1
  14. package/dist/cli/sync.d.ts.map +1 -1
  15. package/dist/cli/sync.js +11 -1
  16. package/dist/cli/sync.js.map +1 -1
  17. package/dist/cli/sync.test.js +1 -0
  18. package/dist/cli/sync.test.js.map +1 -1
  19. package/dist/object-io.d.ts +218 -0
  20. package/dist/object-io.d.ts.map +1 -0
  21. package/dist/object-io.js +588 -0
  22. package/dist/object-io.js.map +1 -0
  23. package/dist/object-io.test.d.ts +11 -0
  24. package/dist/object-io.test.d.ts.map +1 -0
  25. package/dist/object-io.test.js +568 -0
  26. package/dist/object-io.test.js.map +1 -0
  27. package/dist/s3.d.ts +37 -0
  28. package/dist/s3.d.ts.map +1 -1
  29. package/dist/s3.js +225 -201
  30. package/dist/s3.js.map +1 -1
  31. package/dist/s3.test.js +21 -0
  32. package/dist/s3.test.js.map +1 -1
  33. package/dist/vault-client.d.ts +68 -0
  34. package/dist/vault-client.d.ts.map +1 -1
  35. package/dist/vault-client.js +35 -0
  36. package/dist/vault-client.js.map +1 -1
  37. package/package.json +1 -1
  38. package/scripts/presign-transport-e2e.mjs +203 -0
  39. package/scripts/vault-rebaseline.sh +275 -0
  40. package/scripts/vault-rescue.sh +8 -0
  41. package/src/bin/sync-runner.test.ts +41 -0
  42. package/src/bin/sync-runner.ts +52 -0
  43. package/src/cli/share.test.ts +2 -0
  44. package/src/cli/share.ts +29 -2
  45. package/src/cli/sync-scope.test.ts +1 -0
  46. package/src/cli/sync.test.ts +1 -0
  47. package/src/cli/sync.ts +22 -1
  48. package/src/object-io.test.ts +663 -0
  49. package/src/object-io.ts +782 -0
  50. package/src/s3.test.ts +24 -0
  51. package/src/s3.ts +277 -237
  52. package/src/vault-client.ts +101 -0
@@ -0,0 +1,588 @@
1
+ /**
2
+ * ObjectIO — transport seam for vault object byte/metadata movement.
3
+ *
4
+ * s3.ts holds the *semantics* of sync (symlink-record encoding, mode/mtime
5
+ * stamping, created-at preservation, directory-marker filtering). Those never
6
+ * change. What CAN change is the *wire transport* underneath them:
7
+ *
8
+ * - `S3SdkObjectIO` — the historical path. STS-vended credentials + the AWS
9
+ * S3 SDK talking directly to the per-company bucket. No policy-size
10
+ * ceiling concern for the BYTES, but the STS session policy that grants
11
+ * access has the 2048-char IAM limit that motivated the presigned model.
12
+ *
13
+ * - `PresignObjectIO` — the presigned-URL path. The vault-service decides
14
+ * access as a runtime DDB check (no IAM policy ceiling) and hands back
15
+ * short-lived presigned GET/PUT/DELETE URLs + (for PUT) the exact headers
16
+ * to replay. The client never holds AWS credentials — it just `fetch`es
17
+ * the signed URLs.
18
+ *
19
+ * The seam is a per-EntityContext factory resolved INSIDE s3.ts, so every
20
+ * existing call site (`uploadFile(ctx, …)`, `downloadFile(ctx, …)`, …) keeps
21
+ * its signature. `runRunner` selects the transport once per session via
22
+ * {@link setObjectIOFactory}; absent any selection the default is the S3 SDK,
23
+ * preserving today's behavior for every non-gated caller.
24
+ */
25
+ import { S3Client, PutObjectCommand, GetObjectCommand, ListObjectsV2Command, DeleteObjectCommand, HeadObjectCommand, } from "@aws-sdk/client-s3";
26
+ import { VaultClientError } from "./vault-client.js";
27
+ // ---------------------------------------------------------------------------
28
+ // S3 SDK transport (default)
29
+ // ---------------------------------------------------------------------------
30
+ function stripQuotes(etag) {
31
+ return etag ? etag.replace(/^"|"$/g, "") : "";
32
+ }
33
+ async function drainToBuffer(body) {
34
+ if (!body)
35
+ return Buffer.alloc(0);
36
+ const chunks = [];
37
+ for await (const chunk of body) {
38
+ chunks.push(Buffer.from(chunk));
39
+ }
40
+ return Buffer.concat(chunks);
41
+ }
42
+ /**
43
+ * Direct-to-S3 transport over STS-vended credentials. A fresh client per
44
+ * instance so the latest credentials from the EntityContext are always used
45
+ * (caching/refresh is the caller's concern, at the EntityContext level — see
46
+ * the original buildClient note in s3.ts).
47
+ */
48
+ export class S3SdkObjectIO {
49
+ client;
50
+ bucket;
51
+ constructor(ctx) {
52
+ this.bucket = ctx.bucketName;
53
+ this.client = new S3Client({
54
+ region: ctx.region,
55
+ credentials: {
56
+ accessKeyId: ctx.credentials.accessKeyId,
57
+ secretAccessKey: ctx.credentials.secretAccessKey,
58
+ sessionToken: ctx.credentials.sessionToken,
59
+ },
60
+ });
61
+ }
62
+ async putObject(input) {
63
+ const res = await this.client.send(new PutObjectCommand({
64
+ Bucket: this.bucket,
65
+ Key: input.key,
66
+ Body: input.body,
67
+ ContentType: input.contentType,
68
+ ...(input.metadata && Object.keys(input.metadata).length > 0
69
+ ? { Metadata: input.metadata }
70
+ : {}),
71
+ }));
72
+ return { etag: res.ETag || "" };
73
+ }
74
+ async getObject(key) {
75
+ const res = await this.client.send(new GetObjectCommand({ Bucket: this.bucket, Key: key }));
76
+ if (!res.Body) {
77
+ throw new Error(`Empty response for ${key}`);
78
+ }
79
+ const body = await drainToBuffer(res.Body);
80
+ return { body, metadata: res.Metadata };
81
+ }
82
+ async listObjects(input) {
83
+ const res = await this.client.send(new ListObjectsV2Command({
84
+ Bucket: this.bucket,
85
+ Prefix: input.prefix,
86
+ ContinuationToken: input.continuationToken,
87
+ }));
88
+ const objects = [];
89
+ for (const obj of res.Contents || []) {
90
+ if (!obj.Key)
91
+ continue;
92
+ objects.push({
93
+ key: obj.Key,
94
+ size: obj.Size ?? 0,
95
+ lastModified: obj.LastModified || new Date(),
96
+ etag: obj.ETag || "",
97
+ });
98
+ }
99
+ return {
100
+ objects,
101
+ nextContinuationToken: res.NextContinuationToken,
102
+ };
103
+ }
104
+ async deleteObject(key) {
105
+ await this.client.send(new DeleteObjectCommand({ Bucket: this.bucket, Key: key }));
106
+ }
107
+ async headObject(key) {
108
+ try {
109
+ const res = await this.client.send(new HeadObjectCommand({ Bucket: this.bucket, Key: key }));
110
+ return {
111
+ lastModified: res.LastModified || new Date(),
112
+ etag: res.ETag || "",
113
+ size: res.ContentLength || 0,
114
+ metadata: res.Metadata,
115
+ };
116
+ }
117
+ catch (err) {
118
+ if (err &&
119
+ typeof err === "object" &&
120
+ "name" in err &&
121
+ err.name === "NotFound") {
122
+ return null;
123
+ }
124
+ throw err;
125
+ }
126
+ }
127
+ }
128
+ // ---------------------------------------------------------------------------
129
+ // Presigned-URL transport
130
+ // ---------------------------------------------------------------------------
131
+ /**
132
+ * Pull every `x-amz-meta-*` response header into a plain metadata record with
133
+ * the prefix stripped. S3 surfaces user metadata this way on GET/HEAD; fetch
134
+ * lowercases header names, matching how the SDK lowercases `Metadata` keys, so
135
+ * the read path in s3.ts is identical across both transports.
136
+ */
137
+ function metaFromHeaders(headers) {
138
+ const meta = {};
139
+ headers.forEach((value, name) => {
140
+ if (name.startsWith("x-amz-meta-")) {
141
+ meta[name.slice("x-amz-meta-".length)] = value;
142
+ }
143
+ });
144
+ return meta;
145
+ }
146
+ function firstRowOrThrow(results, key, op) {
147
+ const row = results[0];
148
+ if (!row) {
149
+ throw new Error(`presign ${op} returned no row for ${key}`);
150
+ }
151
+ if (row.error || !row.url) {
152
+ throw new Error(`presign ${op} denied for ${key}: ${row.error ?? "no url"}${row.code ? ` (${row.code})` : ""}`);
153
+ }
154
+ return row;
155
+ }
156
+ /**
157
+ * An error shaped like the AWS SDK's NoSuchKey/NotFound so existing catch
158
+ * sites in s3.ts (which test `err.name === "NotFound"`) treat a presigned
159
+ * 404 the same as an SDK 404.
160
+ */
161
+ function notFoundError(key) {
162
+ return Object.assign(new Error(`Not found: ${key}`), { name: "NotFound" });
163
+ }
164
+ /**
165
+ * Max keys per presign request when priming — the server's hard batch cap
166
+ * (hq-pro files-presign MAX_BATCH_KEYS = 1000). One presign call costs ONE
167
+ * audit row toward the 100/hr limit regardless of how many keys it carries, so
168
+ * filling the batch is strictly better: a 5.5k-file pull is 6 presign calls at
169
+ * 1000/chunk vs 55 at 100. (The sibling list page size is fixed at 1000 by
170
+ * AWS's ListObjectsV2 MaxKeys cap — that one we can't raise.)
171
+ */
172
+ const PRIME_CHUNK = 1000;
173
+ /**
174
+ * Lifetime requested for primed URLs. Generous (30 min) so a whole sync batch
175
+ * completes within one prime, but well inside the presign Lambda role's
176
+ * credential lifetime (a presigned URL cannot outlive the creds that signed
177
+ * it). A batch that somehow outruns this falls back to per-key single presign.
178
+ */
179
+ const PRIME_EXPIRES_IN_SECONDS = 1800;
180
+ /** Concurrent prime chunks in flight (each is one presign HTTP call). */
181
+ const PRIME_CONCURRENCY = 4;
182
+ /** Treat a cached URL within this window of expiry as a miss (re-presign). */
183
+ const CACHE_SAFETY_MS = 60_000;
184
+ /**
185
+ * Thrown when a presign mint is skipped because the per-user 100/hr vault rate
186
+ * budget is exhausted. Distinct name so callers can tell "deferred, retry next
187
+ * sync" apart from a real transfer failure. The key was NOT synced.
188
+ */
189
+ export class RateLimitedError extends Error {
190
+ key;
191
+ op;
192
+ constructor(key, op, options) {
193
+ super(`rate limited (100/hr) — ${op} ${key} deferred to next sync`);
194
+ this.key = key;
195
+ this.op = op;
196
+ this.name = "RateLimited";
197
+ if (options?.cause !== undefined) {
198
+ this.cause = options.cause;
199
+ }
200
+ }
201
+ }
202
+ /**
203
+ * One-way circuit breaker shared across a run's per-company transports. The
204
+ * first 429 (vault rate budget exhausted) trips it; thereafter every UNCACHED
205
+ * presign fails fast with {@link RateLimitedError} instead of hitting the wire.
206
+ *
207
+ * Without this, an exhausted budget spirals: prime chunks 429 → keys uncached
208
+ * → per-file presign → each 429s (after VaultClient's own 3 retries +
209
+ * backoff) → an 86-minute storm of ~10k doomed calls (observed live). Tripping
210
+ * once and short-circuiting turns that into a clean fast finish: primed URLs
211
+ * still work, un-primed keys are deferred, and the run reports them so the next
212
+ * sync (after the rolling hour recovers) picks them up.
213
+ */
214
+ export class RateLimitBreaker {
215
+ tripped = false;
216
+ isTripped() {
217
+ return this.tripped;
218
+ }
219
+ trip() {
220
+ this.tripped = true;
221
+ }
222
+ }
223
+ /** A VaultClient 429 (rate budget exhausted) after its own retries. */
224
+ function isRateLimit(err) {
225
+ return err instanceof VaultClientError && err.statusCode === 429;
226
+ }
227
+ /**
228
+ * Transport that moves bytes over short-lived presigned URLs minted by the
229
+ * vault-service. Holds no AWS credentials. `companyUid` is the EntityContext's
230
+ * `uid` — the server resolves the per-company bucket from it, so cross-company
231
+ * reach is structurally impossible (same authority model as the list/presign
232
+ * handlers).
233
+ *
234
+ * URL cache: {@link prime} batch-mints URLs into `urlCache` (keyed by op+key)
235
+ * so the per-file get/head calls during a sync reuse them instead of issuing a
236
+ * presign request each. A single instance is shared across all s3.ts calls for
237
+ * one company within a run (see {@link presignObjectIOFactory} memoization), so
238
+ * a prime before the transfer loop warms the cache the loop then drains.
239
+ */
240
+ export class PresignObjectIO {
241
+ vault;
242
+ companyUid;
243
+ breaker;
244
+ urlCache = new Map();
245
+ constructor(vault, companyUid,
246
+ // Shared across a run's per-company instances by the factory; a directly
247
+ // constructed instance gets its own (fine for tests / one-offs).
248
+ breaker = new RateLimitBreaker()) {
249
+ this.vault = vault;
250
+ this.companyUid = companyUid;
251
+ this.breaker = breaker;
252
+ }
253
+ cacheKey(op, key) {
254
+ return `${op}${key}`;
255
+ }
256
+ hasPrimedPut(key) {
257
+ return this.cached("put", key) !== undefined;
258
+ }
259
+ /** A live (non-expiring) cached URL for op+key, or undefined. */
260
+ cached(op, key) {
261
+ const hit = this.urlCache.get(this.cacheKey(op, key));
262
+ if (!hit)
263
+ return undefined;
264
+ if (Date.now() >= hit.expiresAtMs - CACHE_SAFETY_MS) {
265
+ this.urlCache.delete(this.cacheKey(op, key));
266
+ return undefined;
267
+ }
268
+ return hit;
269
+ }
270
+ /**
271
+ * Resolve a presigned URL (+ replay headers) for op+key: cache hit if primed,
272
+ * else a single presign. Throws on per-key denial (matches the SDK path's
273
+ * access error). `extra` carries PUT contentType/metadata on the miss path.
274
+ */
275
+ async resolveUrl(op, key, extra) {
276
+ const hit = this.cached(op, key);
277
+ if (hit)
278
+ return { url: hit.url, headers: hit.headers };
279
+ // Primed URLs still serve once the breaker trips (no wire needed); only an
280
+ // uncached key on an exhausted budget fails fast.
281
+ if (this.breaker.isTripped())
282
+ throw new RateLimitedError(key, op);
283
+ let results;
284
+ try {
285
+ ({ results } = await this.vault.presign({
286
+ companyUid: this.companyUid,
287
+ op,
288
+ keys: [{ key, op, ...extra }],
289
+ }));
290
+ }
291
+ catch (err) {
292
+ if (isRateLimit(err)) {
293
+ this.breaker.trip();
294
+ throw new RateLimitedError(key, op, { cause: err });
295
+ }
296
+ throw err;
297
+ }
298
+ const row = firstRowOrThrow(results, key, op);
299
+ return { url: row.url, headers: row.headers };
300
+ }
301
+ async prime(op, keys) {
302
+ if (keys.length === 0)
303
+ return;
304
+ const chunks = [];
305
+ for (let i = 0; i < keys.length; i += PRIME_CHUNK) {
306
+ chunks.push(keys.slice(i, i + PRIME_CHUNK));
307
+ }
308
+ let next = 0;
309
+ const worker = async () => {
310
+ while (next < chunks.length) {
311
+ // Budget already exhausted — stop priming; remaining keys defer.
312
+ if (this.breaker.isTripped())
313
+ return;
314
+ const chunk = chunks[next++];
315
+ let resp;
316
+ try {
317
+ resp = await this.vault.presign({
318
+ companyUid: this.companyUid,
319
+ op,
320
+ expiresIn: PRIME_EXPIRES_IN_SECONDS,
321
+ keys: chunk.map((k) => ({ ...k, op })),
322
+ });
323
+ }
324
+ catch (err) {
325
+ if (isRateLimit(err)) {
326
+ // Budget exhausted mid-prime: trip the breaker and stop. Priming
327
+ // on means every remaining chunk + per-file fallback would 429 —
328
+ // the spiral. Tripping makes the transfer loop fail fast instead.
329
+ this.breaker.trip();
330
+ return;
331
+ }
332
+ // A non-429 chunk failure just means those keys aren't cached — the
333
+ // per-key call will single-presign. Never let priming fail the sync.
334
+ continue;
335
+ }
336
+ const now = Date.now();
337
+ for (const row of resp.results) {
338
+ if (row.error || !row.url)
339
+ continue;
340
+ this.urlCache.set(this.cacheKey(op, row.key), {
341
+ url: row.url,
342
+ headers: row.headers,
343
+ expiresAtMs: now + (row.expiresIn ?? PRIME_EXPIRES_IN_SECONDS) * 1000,
344
+ });
345
+ }
346
+ }
347
+ };
348
+ await Promise.all(Array.from({ length: Math.min(PRIME_CONCURRENCY, chunks.length) }, worker));
349
+ }
350
+ async putObject(input) {
351
+ const row = await this.resolveUrl("put", input.key, {
352
+ contentType: input.contentType,
353
+ ...(input.metadata && Object.keys(input.metadata).length > 0
354
+ ? { metadata: input.metadata }
355
+ : {}),
356
+ });
357
+ // The server signs Content-Type, SSE-KMS, and every x-amz-meta-* into the
358
+ // signature and returns them in `headers`; they MUST be replayed verbatim
359
+ // or SigV4 rejects the PUT.
360
+ const res = await fetchWithRetry(row.url, { method: "PUT", body: input.body, headers: row.headers ?? {} }, `presigned PUT ${input.key}`);
361
+ if (!res.ok) {
362
+ const detail = await safeText(res);
363
+ throw new Error(`presigned PUT failed for ${input.key}: ${res.status} ${detail}`);
364
+ }
365
+ return { etag: stripQuotes(res.headers.get("etag") ?? undefined) };
366
+ }
367
+ async getObject(key) {
368
+ const row = await this.resolveUrl("get", key);
369
+ const res = await fetchWithRetry(row.url, { method: "GET" }, `presigned GET ${key}`);
370
+ if (res.status === 404) {
371
+ await cancelBody(res);
372
+ throw notFoundError(key);
373
+ }
374
+ if (!res.ok) {
375
+ const detail = await safeText(res);
376
+ throw new Error(`presigned GET failed for ${key}: ${res.status} ${detail}`);
377
+ }
378
+ const body = Buffer.from(await res.arrayBuffer());
379
+ return { body, metadata: metaFromHeaders(res.headers) };
380
+ }
381
+ async listObjects(input) {
382
+ const { objects, cursor } = await this.vault.listFiles(this.companyUid, input.prefix, input.continuationToken);
383
+ return {
384
+ objects: objects.map((o) => ({
385
+ key: o.key,
386
+ size: o.size,
387
+ lastModified: o.lastModified ? new Date(o.lastModified) : new Date(),
388
+ etag: o.etag ?? "",
389
+ })),
390
+ nextContinuationToken: cursor ?? undefined,
391
+ };
392
+ }
393
+ async deleteObject(key) {
394
+ const row = await this.resolveUrl("delete", key);
395
+ const res = await fetchWithRetry(row.url, { method: "DELETE" }, `presigned DELETE ${key}`);
396
+ // S3 DELETE is idempotent — a 204 (deleted) and a 404 (already gone) are
397
+ // both success for the sync engine's purposes.
398
+ if (!res.ok && res.status !== 404) {
399
+ const detail = await safeText(res);
400
+ throw new Error(`presigned DELETE failed for ${key}: ${res.status} ${detail}`);
401
+ }
402
+ }
403
+ async headObject(key) {
404
+ // The presign endpoint has no HEAD op (get/put/delete only). A presigned
405
+ // GET signs the GET method, so we issue a real GET and read only the
406
+ // response headers, cancelling the body stream before it downloads — the
407
+ // headers (etag, content-length, last-modified, x-amz-meta-*) are all we
408
+ // need and arrive before the body. Cheap for the created-at-preservation
409
+ // and conflict-detection call sites that use headObject. Reuses the GET
410
+ // cache: a prime("get", …) before a pull warms these HEADs for free.
411
+ let url;
412
+ const hit = this.cached("get", key);
413
+ if (hit) {
414
+ url = hit.url;
415
+ }
416
+ else {
417
+ if (this.breaker.isTripped())
418
+ throw new RateLimitedError(key, "get");
419
+ let results;
420
+ try {
421
+ ({ results } = await this.vault.presign({
422
+ companyUid: this.companyUid,
423
+ op: "get",
424
+ keys: [{ key, op: "get" }],
425
+ }));
426
+ }
427
+ catch (err) {
428
+ if (isRateLimit(err)) {
429
+ this.breaker.trip();
430
+ throw new RateLimitedError(key, "get", { cause: err });
431
+ }
432
+ throw err;
433
+ }
434
+ const row = results[0];
435
+ if (!row || row.error || !row.url) {
436
+ // A per-key denial here means the caller can't read the key — treat as
437
+ // absent for HEAD semantics (the SDK path would 403, which callers map
438
+ // to "no usable head"); they all tolerate null.
439
+ return null;
440
+ }
441
+ url = row.url;
442
+ }
443
+ const res = await fetchWithRetry(url, { method: "GET" }, `presigned HEAD ${key}`);
444
+ if (res.status === 404 || res.status === 403) {
445
+ await cancelBody(res);
446
+ return null;
447
+ }
448
+ if (!res.ok) {
449
+ await cancelBody(res);
450
+ const detail = await safeText(res);
451
+ throw new Error(`presigned HEAD failed for ${key}: ${res.status} ${detail}`);
452
+ }
453
+ const result = {
454
+ lastModified: parseLastModified(res.headers.get("last-modified")),
455
+ etag: stripQuotes(res.headers.get("etag") ?? undefined),
456
+ size: Number(res.headers.get("content-length") ?? "0"),
457
+ metadata: metaFromHeaders(res.headers),
458
+ };
459
+ await cancelBody(res);
460
+ return result;
461
+ }
462
+ }
463
+ async function safeText(res) {
464
+ try {
465
+ return (await res.text()).slice(0, 200);
466
+ }
467
+ catch {
468
+ return "";
469
+ }
470
+ }
471
+ async function cancelBody(res) {
472
+ try {
473
+ await res.body?.cancel();
474
+ }
475
+ catch {
476
+ // Best-effort — the socket is released either way once GC'd.
477
+ }
478
+ }
479
+ function parseLastModified(value) {
480
+ if (!value)
481
+ return new Date();
482
+ const d = new Date(value);
483
+ return Number.isNaN(d.getTime()) ? new Date() : d;
484
+ }
485
+ // ---------------------------------------------------------------------------
486
+ // Transient-failure retry for the presigned-URL fetches
487
+ // ---------------------------------------------------------------------------
488
+ //
489
+ // The AWS S3 SDK retries transient errors (5xx, throttling, dropped sockets)
490
+ // automatically with backoff. Moving the byte transfer to `fetch` over a
491
+ // presigned URL dropped that resilience — and at sync scale (thousands of
492
+ // objects per pull) transient S3 5xx (notably 503 SlowDown) are routine, so
493
+ // without retry a large sync sporadically loses files. This restores
494
+ // SDK-parity: retry network errors and transient 5xx with exponential backoff
495
+ // + jitter; 4xx (404/403) are definitive and pass straight through.
496
+ const FETCH_MAX_RETRIES = 3;
497
+ const FETCH_BASE_DELAY_MS = 400;
498
+ function isTransientStatus(status) {
499
+ return status === 500 || status === 502 || status === 503 || status === 504;
500
+ }
501
+ function sleep(ms) {
502
+ return new Promise((resolve) => setTimeout(resolve, ms));
503
+ }
504
+ /**
505
+ * `fetch` with bounded retry on network errors + transient 5xx. The presigned
506
+ * URL is reusable until expiry and the bodies are in-memory Buffers, so a
507
+ * retry simply re-issues the same request. Jitter avoids a thundering-herd
508
+ * re-retry when the whole transfer pool hits a 503 SlowDown at once. After
509
+ * exhausting retries on a 5xx it returns the final (failing) Response so the
510
+ * caller's normal status handling reports it; network errors throw.
511
+ */
512
+ async function fetchWithRetry(url, init, what) {
513
+ let lastError;
514
+ for (let attempt = 0; attempt <= FETCH_MAX_RETRIES; attempt++) {
515
+ if (attempt > 0) {
516
+ const backoff = FETCH_BASE_DELAY_MS * 2 ** (attempt - 1);
517
+ const jitter = Math.floor(Math.random() * FETCH_BASE_DELAY_MS);
518
+ await sleep(backoff + jitter);
519
+ }
520
+ let res;
521
+ try {
522
+ res = await fetch(url, init);
523
+ }
524
+ catch (err) {
525
+ lastError = err; // socket reset / DNS / TLS — retry
526
+ continue;
527
+ }
528
+ if (isTransientStatus(res.status) && attempt < FETCH_MAX_RETRIES) {
529
+ await cancelBody(res); // free the socket before backoff
530
+ lastError = new Error(`${what}: transient ${res.status}`);
531
+ continue;
532
+ }
533
+ return res; // success, a non-transient status, or the last 5xx attempt
534
+ }
535
+ throw lastError instanceof Error
536
+ ? lastError
537
+ : new Error(`${what}: failed after ${FETCH_MAX_RETRIES} retries`);
538
+ }
539
+ const DEFAULT_FACTORY = (ctx) => new S3SdkObjectIO(ctx);
540
+ let activeFactory = DEFAULT_FACTORY;
541
+ /**
542
+ * Install the transport factory for the current process. Passing `null`
543
+ * resets to the default S3 SDK transport. Called once by `runRunner` after it
544
+ * resolves the caller's identity + feature-flag gate; every subsequent s3.ts
545
+ * call resolves its transport through this.
546
+ */
547
+ export function setObjectIOFactory(factory) {
548
+ activeFactory = factory ?? DEFAULT_FACTORY;
549
+ }
550
+ /** Resolve the transport for an EntityContext using the active factory. */
551
+ export function resolveObjectIO(ctx) {
552
+ return activeFactory(ctx);
553
+ }
554
+ /**
555
+ * Build a factory that routes every EntityContext through the presigned-URL
556
+ * transport, reusing the one already-authenticated VaultClient and deriving
557
+ * the per-company authority from `ctx.uid`.
558
+ */
559
+ export function presignObjectIOFactory(vault) {
560
+ // Memoize one PresignObjectIO per company for the run, so a prime() and the
561
+ // transfer loop that drains its URL cache share the SAME instance. (Safe to
562
+ // memoize: PresignObjectIO holds only the vault client — whose token is a
563
+ // live getter — and the stable companyUid; it captures no rotating
564
+ // credentials, unlike S3SdkObjectIO, which is why the default factory is
565
+ // intentionally NOT memoized.)
566
+ // One breaker per run, shared across companies: the 100/hr budget is
567
+ // per-user, so a 429 in one company means the whole run should stop minting.
568
+ const breaker = new RateLimitBreaker();
569
+ const byCompany = new Map();
570
+ return (ctx) => {
571
+ // Personal vaults are a PERSON entity (prs_*) accessed via the membership-
572
+ // less vend-self model; the list/presign endpoints are membership-gated and
573
+ // 403 ("no active membership in company prs_…") for them. Personal vaults
574
+ // also have no ACL-scale problem (single owner), so they don't need
575
+ // presign — keep them on the S3 SDK (STS) transport. Presign is for company
576
+ // vaults (cmp_*), which is where the unbounded-grants problem lives.
577
+ if (!ctx.uid.startsWith("cmp_")) {
578
+ return new S3SdkObjectIO(ctx);
579
+ }
580
+ let io = byCompany.get(ctx.uid);
581
+ if (!io) {
582
+ io = new PresignObjectIO(vault, ctx.uid, breaker);
583
+ byCompany.set(ctx.uid, io);
584
+ }
585
+ return io;
586
+ };
587
+ }
588
+ //# sourceMappingURL=object-io.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-io.js","sourceRoot":"","sources":["../src/object-io.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Unit tests for the ObjectIO transport seam.
3
+ *
4
+ * Focus is the PresignObjectIO path (the new transport): it talks to a
5
+ * VaultClient-shaped stub for URL minting and a mocked global `fetch` for the
6
+ * byte movement. The S3SdkObjectIO path is already covered transitively by
7
+ * s3.test.ts (which exercises s3.ts through the default factory), plus a couple
8
+ * of factory-selection tests here.
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=object-io.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-io.test.d.ts","sourceRoot":"","sources":["../src/object-io.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}