scrapely 1.2.35 → 1.2.36

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 CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.2.36] - 2026-10-22
6
+
7
+ - Debug captures (`Actor.debugCapture`, `Actor.debugCaptureSync`) keep payloads over 256 KiB whole. They are stored gzip-compressed and base64-encoded as `{ key, payload, encoding: 'gzip-base64', length, truncated }` instead of being cut at 262,144 characters; up to 8 Mi characters are kept. A payload that does not compress enough keeps its first 256 KiB, now marked `truncated: true` with its full `length`. `Uint8Array` / `Buffer` payloads are stored as UTF-8 text. Captures are only active on the platform's own diagnostic runs (`SCRAPELY_DEBUG_CAPTURE=1`).
8
+
5
9
  ## [1.2.35] - 2026-10-20
6
10
 
7
11
  - The storage client percent-encodes request ids and key-value store record keys as one URL path segment and refuses `''`, `.` and `..`. A request id such as `..` read from a queue used to send the call to another resource.
package/dist/actor.d.ts CHANGED
@@ -459,7 +459,15 @@ export declare class Actor {
459
459
  */
460
460
  static setValue<T>(key: string, value: T | null, options?: RecordOptions): Promise<void>;
461
461
  private static _debugCaptureCount;
462
+ /**
463
+ * A payload up to DEBUG_CAPTURE_MAX_BYTES characters is stored as it is; a larger one gzip-compressed
464
+ * (base64, at most DEBUG_CAPTURE_MAX_BYTES characters again), keeping up to DEBUG_CAPTURE_MAX_CHARS
465
+ * characters / DEBUG_CAPTURE_MAX_ENCODED bytes of it. A plain cut at 256 KiB lost the part a check
466
+ * needed when a large API document starts with site navigation.
467
+ */
462
468
  static readonly DEBUG_CAPTURE_MAX_BYTES: number;
469
+ static readonly DEBUG_CAPTURE_MAX_CHARS: number;
470
+ static readonly DEBUG_CAPTURE_MAX_ENCODED: number;
463
471
  static readonly DEBUG_CAPTURE_MAX_RECORDS = 500;
464
472
  /** The key-value store record name for a capture key (item URL or id). */
465
473
  static debugCaptureRecordKey(key: string): Promise<string>;
@@ -471,6 +479,8 @@ export declare class Actor {
471
479
  /** debugCapture for synchronous code: fire and forget. Does nothing unless capture is enabled for the run. */
472
480
  static debugCaptureSync(key: string, payload: unknown): void;
473
481
  static debugCapture(key: string, payload: unknown): Promise<void>;
482
+ /** The record for one capture: plain when small, else gzip-base64 (plain head as the last resort). */
483
+ static debugCaptureRecord(key: string, text: string): Promise<Record<string, unknown>>;
474
484
  /**
475
485
  * Helper to resolve storage identifier to a string ID or name.
476
486
  * Supports: string, { id: '...' }, { name: '...' }, { alias: '...' }
package/dist/actor.js CHANGED
@@ -660,7 +660,15 @@ class Actor {
660
660
  await store.setValue(key, value, options);
661
661
  }
662
662
  static _debugCaptureCount = 0;
663
+ /**
664
+ * A payload up to DEBUG_CAPTURE_MAX_BYTES characters is stored as it is; a larger one gzip-compressed
665
+ * (base64, at most DEBUG_CAPTURE_MAX_BYTES characters again), keeping up to DEBUG_CAPTURE_MAX_CHARS
666
+ * characters / DEBUG_CAPTURE_MAX_ENCODED bytes of it. A plain cut at 256 KiB lost the part a check
667
+ * needed when a large API document starts with site navigation.
668
+ */
663
669
  static DEBUG_CAPTURE_MAX_BYTES = 256 * 1024;
670
+ static DEBUG_CAPTURE_MAX_CHARS = 8 * 1024 * 1024;
671
+ static DEBUG_CAPTURE_MAX_ENCODED = 8 * 1024 * 1024;
664
672
  static DEBUG_CAPTURE_MAX_RECORDS = 500;
665
673
  /** The key-value store record name for a capture key (item URL or id). */
666
674
  static async debugCaptureRecordKey(key) {
@@ -684,14 +692,32 @@ class Actor {
684
692
  if (Actor._debugCaptureCount >= Actor.DEBUG_CAPTURE_MAX_RECORDS)
685
693
  return;
686
694
  try {
687
- const text = (typeof payload === 'string' ? payload : JSON.stringify(payload) ?? '').slice(0, Actor.DEBUG_CAPTURE_MAX_BYTES);
695
+ const text = typeof payload === 'string' ? payload
696
+ : payload instanceof Uint8Array ? Buffer.from(payload.buffer, payload.byteOffset, payload.byteLength).toString('utf8')
697
+ : JSON.stringify(payload) ?? '';
698
+ const record = await Actor.debugCaptureRecord(String(key), text);
688
699
  Actor._debugCaptureCount++;
689
- await Actor.setValue(await Actor.debugCaptureRecordKey(key), { key: String(key), payload: text });
700
+ await Actor.setValue(await Actor.debugCaptureRecordKey(key), record);
690
701
  }
691
702
  catch {
692
703
  // never break the run
693
704
  }
694
705
  }
706
+ /** The record for one capture: plain when small, else gzip-base64 (plain head as the last resort). */
707
+ static async debugCaptureRecord(key, text) {
708
+ const limit = Actor.DEBUG_CAPTURE_MAX_BYTES;
709
+ if (text.length <= limit)
710
+ return { key, payload: text };
711
+ const { gzipSync } = await import('node:zlib');
712
+ const raw = Buffer.from(text.slice(0, Actor.DEBUG_CAPTURE_MAX_CHARS), 'utf8');
713
+ const kept = raw.subarray(0, Actor.DEBUG_CAPTURE_MAX_ENCODED);
714
+ const packed = gzipSync(kept, { level: 6 }).toString('base64');
715
+ if (packed.length <= limit) {
716
+ const truncated = text.length > Actor.DEBUG_CAPTURE_MAX_CHARS || kept.length < raw.length;
717
+ return { key, payload: packed, encoding: 'gzip-base64', length: text.length, truncated };
718
+ }
719
+ return { key, payload: text.slice(0, limit), length: text.length, truncated: true };
720
+ }
695
721
  /**
696
722
  * Helper to resolve storage identifier to a string ID or name.
697
723
  * Supports: string, { id: '...' }, { name: '...' }, { alias: '...' }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scrapely",
3
- "version": "1.2.35",
3
+ "version": "1.2.36",
4
4
  "description": "Scrapely SDK for building JavaScript and TypeScript Actors",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -48,6 +48,7 @@
48
48
  "test:charge-platform-budget": "npx tsx --test tests/charge-platform-budget.test.ts",
49
49
  "test:storage-retries": "npx tsx --test tests/storage-retries.test.ts",
50
50
  "test:storage-path-segments": "npx tsx --test tests/storage-path-segments.test.ts",
51
+ "test:storage-record-keys": "npx tsx --test tests/storage-record-keys.test.ts",
51
52
  "test:push-idempotency": "npx tsx --test tests/push-idempotency.test.ts",
52
53
  "test:stdio-drain": "npx tsx --test tests/stdio-drain.test.ts",
53
54
  "test:charging-client": "npx tsx --test tests/charging-client.test.ts",