scrapely 1.2.20 → 1.2.22

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/dist/actor.d.ts CHANGED
@@ -19,6 +19,11 @@ import { Configuration, type ConfigurationOptions } from './configuration.js';
19
19
  import type { StorageClient } from '@crawlee/types';
20
20
  import { type ApiStorageConfig } from './storage/api-storage-client.js';
21
21
  import { ChargingManager, type ChargeOptions as ChargingChargeOptions, type ChargeResult as ChargingChargeResult } from './charging.js';
22
+ /** Options for a multi-event charged push (`Actor.pushData(row, { charges })`). */
23
+ export interface PushDataChargeOptions {
24
+ /** Events per row, e.g. `{ result: 1, page: 3 }`. */
25
+ charges: Record<string, number>;
26
+ }
22
27
  import { createProxyConfiguration, type CreateProxyConfigurationOptions } from './proxy-configuration.js';
23
28
  import { type BrowserConfiguration, type BrowserConfigurationOptions } from './browser-configuration.js';
24
29
  import { type Clearance, type SolveOptions, type SolverVendorStatus } from './solver.js';
@@ -405,7 +410,14 @@ export declare class Actor {
405
410
  * @returns ChargeResult indicating if the user's spending limit was reached
406
411
  */
407
412
  static pushData<Data extends Record<string, any>>(item: Data | Data[], eventName: string): Promise<ChargeResult>;
408
- pushData<Data extends Record<string, any>>(item: Data | Data[], eventName?: string): Promise<void | ChargeResult>;
413
+ /**
414
+ * Stores rows that each cost several pay-per-event events, charged atomically:
415
+ * `pushData(row, { charges: { result: 1, page: 3 } })` charges 1 `result` and 3 `page`
416
+ * events per row. A row is stored only when all of its events were charged; rows the
417
+ * spending limit cannot pay for in full are not stored.
418
+ */
419
+ static pushData<Data extends Record<string, any>>(item: Data | Data[], options: PushDataChargeOptions): Promise<ChargeResult>;
420
+ pushData<Data extends Record<string, any>>(item: Data | Data[], eventNameOrOptions?: string | PushDataChargeOptions): Promise<void | ChargeResult>;
409
421
  private usesPushDataInterception;
410
422
  private pushDataViaInterceptedClient;
411
423
  private pushDataWithExplicitCharging;
package/dist/actor.js CHANGED
@@ -29,6 +29,7 @@ const events_client_js_1 = require("./events/events-client.js");
29
29
  const scrapely_event_manager_js_1 = require("./events/scrapely-event-manager.js");
30
30
  const secrets_js_1 = require("./crypto/secrets.js");
31
31
  const charging_js_1 = require("./charging.js");
32
+ const charging_js_2 = require("./charging.js");
32
33
  const charging_client_js_1 = require("./charging_client.js");
33
34
  const proxy_configuration_js_1 = require("./proxy-configuration.js");
34
35
  const browser_configuration_js_1 = require("./browser-configuration.js");
@@ -513,11 +514,23 @@ class Actor {
513
514
  }
514
515
  return cachedInput ?? undefined;
515
516
  }
516
- static async pushData(item, eventName) {
517
- return Actor.getDefaultInstance().pushData(item, eventName);
517
+ static async pushData(item, eventNameOrOptions) {
518
+ return Actor.getDefaultInstance().pushData(item, eventNameOrOptions);
518
519
  }
519
- async pushData(item, eventName) {
520
+ async pushData(item, eventNameOrOptions) {
520
521
  Actor._ensureActorInit('pushData');
522
+ if (eventNameOrOptions !== undefined && typeof eventNameOrOptions === 'object') {
523
+ const { Dataset } = await import('crawlee');
524
+ const dataset = await Dataset.open();
525
+ return (0, charging_js_2.pushDataWithCharges)({
526
+ chargingManager: this.chargingManager,
527
+ items: item,
528
+ charges: eventNameOrOptions.charges,
529
+ isDefaultDataset: dataset.id === this.config.get('defaultDatasetId'),
530
+ pushFn: async (rows) => { await dataset.pushData(rows); },
531
+ });
532
+ }
533
+ const eventName = eventNameOrOptions;
521
534
  // Exact synthetic names only — prefix would block real priced events like apify-foo.
522
535
  if (eventName === 'apify-default-dataset-item' ||
523
536
  eventName === 'scrapely-default-dataset-item') {
@@ -77,6 +77,15 @@ export declare class ChargingManager {
77
77
  private chargeLimitHit;
78
78
  /** Default-dataset rows refused after the limit (one warning, then a count). */
79
79
  private refusedDefaultDatasetItems;
80
+ /**
81
+ * Prepaid-row balance: priced events charged through charge() minus rows
82
+ * pushed to the default dataset without a per-row charge. An Actor that
83
+ * charges first and then pushes plainly (charge('product'); pushData(row))
84
+ * has a positive balance after the charge, so the row it already paid for is
85
+ * still saved when that charge used up the spending limit. Push-then-charge
86
+ * Actors never go positive, so they get nothing extra for free.
87
+ */
88
+ private prepaidRowBalance;
80
89
  /** @internal Called by Actor shutdown before the final flush. */
81
90
  close(): void;
82
91
  /**
@@ -98,6 +107,27 @@ export declare class ChargingManager {
98
107
  /** @internal Whether every row of a push with these events is charged a positive price. */
99
108
  _isPaidPerItem(eventName: string | undefined, isDefaultDataset: boolean): boolean;
100
109
  /** @internal */
110
+ _notePrepaidCharge(eventName: string, chargedCount: number): void;
111
+ /** @internal */
112
+ _isPayPerEvent(): boolean;
113
+ /**
114
+ * @internal The priced part of a multi-event row (`charges`), plus the
115
+ * default-dataset-item event on the default dataset when it is priced, and
116
+ * how many rows the remaining budget pays for in full.
117
+ */
118
+ _rowCharges(charges: Record<string, number>, isDefaultDataset: boolean, rowCount: number): {
119
+ perRow: Record<string, number>;
120
+ rows: number;
121
+ };
122
+ /** @internal Current prepaid-row balance (tests). */
123
+ _prepaidRowBalance(): number;
124
+ /**
125
+ * @internal Account for rows pushed without a per-row charge. Returns how
126
+ * many may be stored: all while the limit is not reached, afterwards only
127
+ * the rows earlier charges already paid for (the positive balance).
128
+ */
129
+ _takeUnpaidRows(itemCount: number): number;
130
+ /** @internal */
101
131
  _refuseDefaultDatasetPush(itemCount: number): boolean;
102
132
  /** @internal Whether shutdown has closed this manager to new charges. */
103
133
  isClosed(): boolean;
@@ -231,6 +261,23 @@ export declare function pushDataAndCharge<T>({ chargingManager, items, eventName
231
261
  isDefaultDataset: boolean;
232
262
  pushFn: (limitedItems: T | T[]) => Promise<void>;
233
263
  }): Promise<ChargeResult>;
264
+ /** Validate `{ eventName: eventsPerRow }` for a multi-event push. */
265
+ export declare function validateRowCharges(charges: unknown): Record<string, number>;
266
+ /**
267
+ * Push rows that each cost several events, atomically per row
268
+ * (`charges = { result: 1, page: 3 }`). A row is stored only when every one of
269
+ * its events was charged: rows the budget cannot pay for in full are not
270
+ * stored, and an operation that came back short is voided and re-charged for
271
+ * the rows that are kept, so no row is stored part-paid and no event is billed
272
+ * for a row that was not stored. Unpriced events are ignored.
273
+ */
274
+ export declare function pushDataWithCharges<T>({ chargingManager, items, charges, isDefaultDataset, pushFn, }: {
275
+ chargingManager: ChargingManager;
276
+ items: T | T[];
277
+ charges: Record<string, number>;
278
+ isDefaultDataset: boolean;
279
+ pushFn: (rows: T | T[]) => Promise<void>;
280
+ }): Promise<ChargeResult>;
234
281
  /**
235
282
  * @internal True when a dataset push certainly stored nothing: the storage API
236
283
  * answered with an error status, or the request never reached it. Timeouts and
package/dist/charging.js CHANGED
@@ -9,6 +9,8 @@ exports.resolveDefaultDatasetItemEventName = resolveDefaultDatasetItemEventName;
9
9
  exports.hasDefaultDatasetItemEventInPricing = hasDefaultDatasetItemEventInPricing;
10
10
  exports.mergeChargeResults = mergeChargeResults;
11
11
  exports.pushDataAndCharge = pushDataAndCharge;
12
+ exports.validateRowCharges = validateRowCharges;
13
+ exports.pushDataWithCharges = pushDataWithCharges;
12
14
  exports.pushCertainlyFailed = pushCertainlyFailed;
13
15
  // SPDX-License-Identifier: Apache-2.0
14
16
  // Derived from apify-sdk-js (src/charging.ts),
@@ -154,6 +156,15 @@ class ChargingManager {
154
156
  chargeLimitHit = false;
155
157
  /** Default-dataset rows refused after the limit (one warning, then a count). */
156
158
  refusedDefaultDatasetItems = 0;
159
+ /**
160
+ * Prepaid-row balance: priced events charged through charge() minus rows
161
+ * pushed to the default dataset without a per-row charge. An Actor that
162
+ * charges first and then pushes plainly (charge('product'); pushData(row))
163
+ * has a positive balance after the charge, so the row it already paid for is
164
+ * still saved when that charge used up the spending limit. Push-then-charge
165
+ * Actors never go positive, so they get nothing extra for free.
166
+ */
167
+ prepaidRowBalance = 0;
157
168
  /** @internal Called by Actor shutdown before the final flush. */
158
169
  close() {
159
170
  this._closed = true;
@@ -216,9 +227,73 @@ class ChargingManager {
216
227
  return names.some((name) => (this.calculateEventPrice(name) ?? 0) > 0);
217
228
  }
218
229
  /** @internal */
230
+ _notePrepaidCharge(eventName, chargedCount) {
231
+ if (chargedCount > 0 && !isSyntheticEvent(eventName) && (this.pricingInfo[eventName]?.price ?? 0) > 0) {
232
+ this.prepaidRowBalance += chargedCount;
233
+ }
234
+ }
235
+ /** @internal */
236
+ _isPayPerEvent() {
237
+ return this.chargingState !== undefined && this.isPayPerEvent;
238
+ }
239
+ /**
240
+ * @internal The priced part of a multi-event row (`charges`), plus the
241
+ * default-dataset-item event on the default dataset when it is priced, and
242
+ * how many rows the remaining budget pays for in full.
243
+ */
244
+ _rowCharges(charges, isDefaultDataset, rowCount) {
245
+ const perRow = {};
246
+ for (const [name, n] of Object.entries(charges)) {
247
+ if ((this.calculateEventPrice(name) ?? 0) > 0)
248
+ perRow[name] = n;
249
+ }
250
+ if (isDefaultDataset) {
251
+ const def = resolveDefaultDatasetItemEventName(this.pricingInfo);
252
+ if (!(def in perRow) && (this.calculateEventPrice(def) ?? 0) > 0)
253
+ perRow[def] = 1;
254
+ }
255
+ const rowCost = Object.entries(perRow).reduce((a, [name, n]) => a + (this.calculateEventPrice(name) ?? 0) * n, 0);
256
+ let rows = rowCount;
257
+ if (rowCost > 0 && Number.isFinite(this.maxTotalChargeUsd)) {
258
+ const fit = Math.floor(Number(((this.maxTotalChargeUsd - this.calculateTotalChargedAmount()) / rowCost).toFixed(4)));
259
+ rows = Math.min(rows, Math.max(0, fit));
260
+ }
261
+ return { perRow, rows };
262
+ }
263
+ /** @internal Current prepaid-row balance (tests). */
264
+ _prepaidRowBalance() {
265
+ return this.prepaidRowBalance;
266
+ }
267
+ /**
268
+ * @internal Account for rows pushed without a per-row charge. Returns how
269
+ * many may be stored: all while the limit is not reached, afterwards only
270
+ * the rows earlier charges already paid for (the positive balance).
271
+ */
272
+ _takeUnpaidRows(itemCount) {
273
+ if (itemCount <= 0)
274
+ return 0;
275
+ if (!this.isChargeLimitReached()) {
276
+ this.prepaidRowBalance -= itemCount;
277
+ return itemCount;
278
+ }
279
+ const allowed = Math.max(0, Math.min(itemCount, this.prepaidRowBalance));
280
+ this.prepaidRowBalance -= allowed;
281
+ return allowed;
282
+ }
283
+ /** @internal */
219
284
  _refuseDefaultDatasetPush(itemCount) {
220
- if (itemCount <= 0 || !this.isChargeLimitReached())
285
+ if (itemCount <= 0 || !this.isPayPerEvent)
286
+ return false;
287
+ if (!this.isChargeLimitReached()) {
288
+ this._takeUnpaidRows(itemCount);
289
+ return false;
290
+ }
291
+ // All-or-nothing here (storage clients cannot store a slice): store the
292
+ // batch only when earlier charges paid for every row of it.
293
+ if (this.prepaidRowBalance >= itemCount) {
294
+ this.prepaidRowBalance -= itemCount;
221
295
  return false;
296
+ }
222
297
  this._noteRefusedDefaultDatasetItems(itemCount);
223
298
  return true;
224
299
  }
@@ -771,7 +846,11 @@ class ChargingManager {
771
846
  }
772
847
  }
773
848
  async charge({ eventName, count = 1 }) {
774
- return this._withOperationLock(() => this._chargeExclusive({ eventName, count }));
849
+ return this._withOperationLock(async () => {
850
+ const result = await this._chargeExclusive({ eventName, count });
851
+ this._notePrepaidCharge(eventName, result.chargedCount);
852
+ return result;
853
+ });
775
854
  }
776
855
  /** @internal Caller must hold the operation lock. */
777
856
  async _chargeExclusive({ eventName, count = 1 }) {
@@ -983,16 +1062,28 @@ async function pushDataAndCharge({ chargingManager, items, eventName, isDefaultD
983
1062
  // default-dataset push that does not pay per row (no event, or only free
984
1063
  // events) stores nothing. Paid pushes go on below, where the budget cap
985
1064
  // stores only the rows that are actually charged.
1065
+ // Rows the Actor already paid for with an earlier charge() (charge first,
1066
+ // then a plain push) are still stored: see ChargingManager._takeUnpaidRows.
1067
+ // The result still reports the limit, so the Actor stops after them.
1068
+ let unpaidPastLimit = false;
986
1069
  if (isDefaultDataset &&
987
1070
  requested > 0 &&
988
- chargingManager.isChargeLimitReached?.() &&
989
- !chargingManager._isPaidPerItem(eventName, isDefaultDataset)) {
990
- chargingManager._noteRefusedDefaultDatasetItems(requested);
991
- return {
992
- eventChargeLimitReached: true,
993
- chargedCount: 0,
994
- chargeableWithinLimit: chargingManager._chargeableWithinLimit?.() ?? {},
995
- };
1071
+ // Optional calls: partial managers (tests, older shims) skip the net, as before.
1072
+ chargingManager._isPayPerEvent?.() === true &&
1073
+ !chargingManager._isPaidPerItem?.(eventName, isDefaultDataset)) {
1074
+ unpaidPastLimit = chargingManager.isChargeLimitReached?.() ?? false;
1075
+ const allowed = chargingManager._takeUnpaidRows(requested);
1076
+ if (allowed < requested) {
1077
+ chargingManager._noteRefusedDefaultDatasetItems(requested - allowed);
1078
+ if (allowed === 0) {
1079
+ return {
1080
+ eventChargeLimitReached: true,
1081
+ chargedCount: 0,
1082
+ chargeableWithinLimit: chargingManager._chargeableWithinLimit?.() ?? {},
1083
+ };
1084
+ }
1085
+ items = (Array.isArray(items) ? items.slice(0, allowed) : items);
1086
+ }
996
1087
  }
997
1088
  const { limitedItems, eventsToCharge } = chargingManager.calculatePushDataLimits({
998
1089
  items,
@@ -1161,17 +1252,148 @@ async function pushDataAndCharge({ chargingManager, items, eventName, isDefaultD
1161
1252
  }
1162
1253
  throw pushErr;
1163
1254
  }
1164
- return chargeResult;
1255
+ return unpaidPastLimit ? { ...chargeResult, eventChargeLimitReached: true } : chargeResult;
1165
1256
  }
1166
1257
  // No billable events — still persist the budget-limited items.
1167
1258
  await chargeGatedPush.run(true, () => pushFn(Array.isArray(items) ? limitedItems : limitedItems[0]));
1168
1259
  return {
1169
- eventChargeLimitReached: false,
1260
+ eventChargeLimitReached: unpaidPastLimit,
1170
1261
  chargedCount: 0,
1171
1262
  chargeableWithinLimit: {},
1172
1263
  };
1173
1264
  });
1174
1265
  }
1266
+ /** Validate `{ eventName: eventsPerRow }` for a multi-event push. */
1267
+ function validateRowCharges(charges) {
1268
+ if (!charges || typeof charges !== 'object' || Array.isArray(charges) || Object.keys(charges).length === 0) {
1269
+ throw new Error('charges must be a non-empty object of { eventName: countPerRow }');
1270
+ }
1271
+ const out = {};
1272
+ for (const [name, count] of Object.entries(charges)) {
1273
+ if (!name)
1274
+ throw new Error('charges keys must be event names');
1275
+ if (isSyntheticEvent(name))
1276
+ throw new Error(`Cannot charge for synthetic event '${name}' manually`);
1277
+ if (typeof count !== 'number' || !Number.isInteger(count) || count < 1) {
1278
+ throw new Error(`charges['${name}'] must be a positive integer (events per row)`);
1279
+ }
1280
+ out[name] = count;
1281
+ }
1282
+ return out;
1283
+ }
1284
+ /**
1285
+ * Push rows that each cost several events, atomically per row
1286
+ * (`charges = { result: 1, page: 3 }`). A row is stored only when every one of
1287
+ * its events was charged: rows the budget cannot pay for in full are not
1288
+ * stored, and an operation that came back short is voided and re-charged for
1289
+ * the rows that are kept, so no row is stored part-paid and no event is billed
1290
+ * for a row that was not stored. Unpriced events are ignored.
1291
+ */
1292
+ async function pushDataWithCharges({ chargingManager, items, charges, isDefaultDataset, pushFn, }) {
1293
+ const perRowIn = validateRowCharges(charges);
1294
+ const rowsIn = Array.isArray(items) ? items : [items];
1295
+ const single = !Array.isArray(items);
1296
+ const store = (rows) => chargeGatedPush.run(true, () => pushFn(single ? rows[0] : rows));
1297
+ if (rowsIn.length === 0)
1298
+ return { eventChargeLimitReached: false, chargedCount: 0, chargeableWithinLimit: {} };
1299
+ return chargingManager._withOperationLock(async () => {
1300
+ if (chargingManager.isClosed()) {
1301
+ throw new Error('ChargingManager is closed (actor is shutting down); charge rejected');
1302
+ }
1303
+ const voidsConfirmed = (await chargingManager._retryPendingVoids?.()) !== false;
1304
+ if (!chargingManager._isPayPerEvent()) {
1305
+ await store(rowsIn);
1306
+ return { eventChargeLimitReached: false, chargedCount: 0, chargeableWithinLimit: {} };
1307
+ }
1308
+ let { perRow, rows } = chargingManager._rowCharges(perRowIn, isDefaultDataset, rowsIn.length);
1309
+ if (Object.keys(perRow).length === 0) {
1310
+ // Only free events: an unpaid push (prepaid-row rules apply).
1311
+ const pastLimit = chargingManager.isChargeLimitReached();
1312
+ const allowed = isDefaultDataset ? chargingManager._takeUnpaidRows(rowsIn.length) : rowsIn.length;
1313
+ if (allowed < rowsIn.length)
1314
+ chargingManager._noteRefusedDefaultDatasetItems(rowsIn.length - allowed);
1315
+ if (allowed > 0)
1316
+ await store(rowsIn.slice(0, allowed));
1317
+ return { eventChargeLimitReached: pastLimit, chargedCount: 0, chargeableWithinLimit: chargingManager._chargeableWithinLimit() };
1318
+ }
1319
+ if (!voidsConfirmed && Object.keys(perRow).some((n) => !isSyntheticEvent(n))) {
1320
+ throw new Error('[Scrapely] An earlier charge for a failed dataset push could not be voided yet; retry the push');
1321
+ }
1322
+ const undo = async (ops) => {
1323
+ for (const op of ops) {
1324
+ if (op.charged <= 0)
1325
+ continue;
1326
+ if (!op.operationId)
1327
+ chargingManager.applyAcceptedCount(op.name, op.charged, 0);
1328
+ else if (op.uncertain)
1329
+ await chargingManager._cancelUncertainCharge(op.name, op.operationId, op.charged);
1330
+ else
1331
+ await chargingManager._voidCharge(op.name, op.operationId, op.charged);
1332
+ }
1333
+ };
1334
+ const order = Object.entries(perRow).sort(([a], [b]) => Number(isSyntheticEvent(a)) - Number(isSyntheticEvent(b)) || a.localeCompare(b));
1335
+ let ops = [];
1336
+ for (let attempt = 0; attempt < 4 && rows > 0; attempt++) {
1337
+ ops = [];
1338
+ let shortTo = null;
1339
+ for (const [name, n] of order) {
1340
+ const want = n * rows;
1341
+ chargingManager._takeLastAcceptedOperationId?.();
1342
+ let got;
1343
+ try {
1344
+ const r = await chargingManager._chargeExclusive({ eventName: name, count: want });
1345
+ got = r.chargedCount;
1346
+ ops.push({ name, charged: got, operationId: chargingManager._takeLastAcceptedOperationId?.(), uncertain: false });
1347
+ }
1348
+ catch (err) {
1349
+ if (!(err instanceof ChargeUncertainError)) {
1350
+ await undo(ops);
1351
+ throw err;
1352
+ }
1353
+ // Not confirmed: stays pending and is re-posted at exit, so it counts as accepted.
1354
+ got = err.count;
1355
+ ops.push({ name, charged: got, operationId: err.operationId, uncertain: true });
1356
+ }
1357
+ if (got < want) {
1358
+ shortTo = Math.floor(got / n);
1359
+ break;
1360
+ }
1361
+ }
1362
+ if (shortTo === null)
1363
+ break;
1364
+ // An event came back short: void this attempt and retry with the rows it pays for.
1365
+ await undo(ops);
1366
+ ops = [];
1367
+ rows = shortTo;
1368
+ }
1369
+ if (rows <= 0 || ops.length === 0) {
1370
+ await undo(ops);
1371
+ chargingManager._markChargeLimitHit();
1372
+ if (isDefaultDataset)
1373
+ chargingManager._noteRefusedDefaultDatasetItems(rowsIn.length);
1374
+ return { eventChargeLimitReached: true, chargedCount: 0, chargeableWithinLimit: chargingManager._chargeableWithinLimit() };
1375
+ }
1376
+ if (rows < rowsIn.length) {
1377
+ chargingManager._markChargeLimitHit();
1378
+ if (isDefaultDataset)
1379
+ chargingManager._noteRefusedDefaultDatasetItems(rowsIn.length - rows);
1380
+ }
1381
+ try {
1382
+ await store(rowsIn.slice(0, rows));
1383
+ }
1384
+ catch (pushErr) {
1385
+ console.error(`[Scrapely] Dataset push failed after charges accepted (rows=${rows}): ${pushErr.message}; voiding the charges`);
1386
+ if (pushCertainlyFailed(pushErr))
1387
+ await undo(ops);
1388
+ throw pushErr;
1389
+ }
1390
+ return {
1391
+ eventChargeLimitReached: rows < rowsIn.length || chargingManager.isChargeLimitReached(),
1392
+ chargedCount: rows,
1393
+ chargeableWithinLimit: chargingManager._chargeableWithinLimit(),
1394
+ };
1395
+ });
1396
+ }
1175
1397
  /** HTTP status of an API/client error, if the server answered. */
1176
1398
  function httpStatusOf(err) {
1177
1399
  const e = err;
package/dist/index.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import './log-env.js';
2
2
  export { setCdpUrl } from './patch-browser-pool.js';
3
- export { Actor, ACTOR_PERMISSION_LEVEL, type ActorInput, type InitOptions, type MainOptions, type ExitOptions, type RebootOptions, type AbortOptions, type MetamorphOptions, type RecordOptions, type OpenStorageOptions, type CallOptions, type CallTaskOptions, type StartOptions, type SetStatusMessageOptions, type WebhookOptions, type ApifyEnv, type UserFunc, type Token, type Timeout, EXIT_CODES, } from './actor';
3
+ export { Actor, ACTOR_PERMISSION_LEVEL, type ActorInput, type InitOptions, type MainOptions, type ExitOptions, type RebootOptions, type AbortOptions, type PushDataChargeOptions, type MetamorphOptions, type RecordOptions, type OpenStorageOptions, type CallOptions, type CallTaskOptions, type StartOptions, type SetStatusMessageOptions, type WebhookOptions, type ApifyEnv, type UserFunc, type Token, type Timeout, EXIT_CODES, } from './actor';
4
4
  export type { StorageAlias, StorageId, StorageName, StorageIdentifier, StorageIdentifierWithoutAlias, } from './storage-identifiers.js';
5
5
  export { ApifyClient, type ApifyClientOptions, type ActorRun } from 'scrapely-client';
6
6
  export { Configuration, type ConfigurationOptions } from './configuration.js';
7
7
  export { DEGRADED_STATUS_MESSAGE, IpBanned, SolveAttemptError, SolverAuthError, SolverRateLimited, VendorUnavailable, degradedStatusMessage, detectVendor, isBlocked, reportSolveOutcome, solve, solverConfigured, solverStatus, stepAllowed, type Clearance, type SolveCookie, type SolveOptions, type SolverStepRequest, type SolverVendorStatus, } from './solver.js';
8
8
  export { ScrapelyEventManager as PlatformEventManager } from './events/scrapely-event-manager.js';
9
- export { ChargingManager, ChargeUncertainError, ChargeRejectedError, DEFAULT_DATASET_ITEM_EVENT, APIFY_DEFAULT_DATASET_ITEM_EVENT, SCRAPELY_DEFAULT_DATASET_ITEM_EVENT, hasDefaultDatasetItemEventInPricing, isDefaultDatasetItemEvent, resolveDefaultDatasetItemEventName, mergeChargeResults, pushDataAndCharge, type ChargeOptions, type ChargeResult, type ActorPricingInfo, } from './charging.js';
9
+ export { ChargingManager, ChargeUncertainError, ChargeRejectedError, DEFAULT_DATASET_ITEM_EVENT, APIFY_DEFAULT_DATASET_ITEM_EVENT, SCRAPELY_DEFAULT_DATASET_ITEM_EVENT, hasDefaultDatasetItemEventInPricing, isDefaultDatasetItemEvent, resolveDefaultDatasetItemEventName, mergeChargeResults, pushDataAndCharge, pushDataWithCharges, validateRowCharges, type ChargeOptions, type ChargeResult, type ActorPricingInfo, } from './charging.js';
10
10
  export { createChargingClient, pushDataChargingContext, USES_PUSH_DATA_INTERCEPTION, type PpeAwarePushDataContext, } from './charging_client.js';
11
11
  export { BillingTracker, type BillingClient } from './storage/billing-tracker.js';
12
12
  export { ApiStorageClientFactory, type ApiStorageConfig } from './storage/api-storage-client.js';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LogLevel = exports.Log = exports.log = exports.RequestQueue = exports.KeyValueStore = exports.Dataset = exports.createBrowserConfiguration = exports.BrowserConfiguration = exports.getPlatformProxyEnv = exports.createProxyConfiguration = exports.validateProxySessionId = exports.isPlatformProxySentinel = exports.ProxyConfiguration = exports.ApiStorageClientFactory = exports.BillingTracker = exports.USES_PUSH_DATA_INTERCEPTION = exports.pushDataChargingContext = exports.createChargingClient = exports.pushDataAndCharge = exports.mergeChargeResults = exports.resolveDefaultDatasetItemEventName = exports.isDefaultDatasetItemEvent = exports.hasDefaultDatasetItemEventInPricing = exports.SCRAPELY_DEFAULT_DATASET_ITEM_EVENT = exports.APIFY_DEFAULT_DATASET_ITEM_EVENT = exports.DEFAULT_DATASET_ITEM_EVENT = exports.ChargeRejectedError = exports.ChargeUncertainError = exports.ChargingManager = exports.PlatformEventManager = exports.stepAllowed = exports.solverStatus = exports.solverConfigured = exports.solve = exports.reportSolveOutcome = exports.isBlocked = exports.detectVendor = exports.degradedStatusMessage = exports.VendorUnavailable = exports.SolverRateLimited = exports.SolverAuthError = exports.SolveAttemptError = exports.IpBanned = exports.DEGRADED_STATUS_MESSAGE = exports.Configuration = exports.ApifyClient = exports.EXIT_CODES = exports.ACTOR_PERMISSION_LEVEL = exports.Actor = exports.setCdpUrl = void 0;
4
- exports.puppeteerUtils = exports.playwrightUtils = exports.LinkeDOMCrawler = exports.JSDOMCrawler = exports.BrowserCrawler = exports.BasicCrawler = exports.HttpCrawler = exports.CheerioCrawler = exports.PuppeteerCrawler = exports.PlaywrightCrawler = exports.ScrapelyRequestList = exports.enqueueLinks = exports.utils = exports.LoggerText = exports.LoggerJson = exports.Logger = void 0;
3
+ exports.log = exports.RequestQueue = exports.KeyValueStore = exports.Dataset = exports.createBrowserConfiguration = exports.BrowserConfiguration = exports.getPlatformProxyEnv = exports.createProxyConfiguration = exports.validateProxySessionId = exports.isPlatformProxySentinel = exports.ProxyConfiguration = exports.ApiStorageClientFactory = exports.BillingTracker = exports.USES_PUSH_DATA_INTERCEPTION = exports.pushDataChargingContext = exports.createChargingClient = exports.validateRowCharges = exports.pushDataWithCharges = exports.pushDataAndCharge = exports.mergeChargeResults = exports.resolveDefaultDatasetItemEventName = exports.isDefaultDatasetItemEvent = exports.hasDefaultDatasetItemEventInPricing = exports.SCRAPELY_DEFAULT_DATASET_ITEM_EVENT = exports.APIFY_DEFAULT_DATASET_ITEM_EVENT = exports.DEFAULT_DATASET_ITEM_EVENT = exports.ChargeRejectedError = exports.ChargeUncertainError = exports.ChargingManager = exports.PlatformEventManager = exports.stepAllowed = exports.solverStatus = exports.solverConfigured = exports.solve = exports.reportSolveOutcome = exports.isBlocked = exports.detectVendor = exports.degradedStatusMessage = exports.VendorUnavailable = exports.SolverRateLimited = exports.SolverAuthError = exports.SolveAttemptError = exports.IpBanned = exports.DEGRADED_STATUS_MESSAGE = exports.Configuration = exports.ApifyClient = exports.EXIT_CODES = exports.ACTOR_PERMISSION_LEVEL = exports.Actor = exports.setCdpUrl = void 0;
4
+ exports.puppeteerUtils = exports.playwrightUtils = exports.LinkeDOMCrawler = exports.JSDOMCrawler = exports.BrowserCrawler = exports.BasicCrawler = exports.HttpCrawler = exports.CheerioCrawler = exports.PuppeteerCrawler = exports.PlaywrightCrawler = exports.ScrapelyRequestList = exports.enqueueLinks = exports.utils = exports.LoggerText = exports.LoggerJson = exports.Logger = exports.LogLevel = exports.Log = void 0;
5
5
  require("./log-env.js");
6
6
  var patch_browser_pool_js_1 = require("./patch-browser-pool.js");
7
7
  Object.defineProperty(exports, "setCdpUrl", { enumerable: true, get: function () { return patch_browser_pool_js_1.setCdpUrl; } });
@@ -43,6 +43,8 @@ Object.defineProperty(exports, "isDefaultDatasetItemEvent", { enumerable: true,
43
43
  Object.defineProperty(exports, "resolveDefaultDatasetItemEventName", { enumerable: true, get: function () { return charging_js_1.resolveDefaultDatasetItemEventName; } });
44
44
  Object.defineProperty(exports, "mergeChargeResults", { enumerable: true, get: function () { return charging_js_1.mergeChargeResults; } });
45
45
  Object.defineProperty(exports, "pushDataAndCharge", { enumerable: true, get: function () { return charging_js_1.pushDataAndCharge; } });
46
+ Object.defineProperty(exports, "pushDataWithCharges", { enumerable: true, get: function () { return charging_js_1.pushDataWithCharges; } });
47
+ Object.defineProperty(exports, "validateRowCharges", { enumerable: true, get: function () { return charging_js_1.validateRowCharges; } });
46
48
  var charging_client_js_1 = require("./charging_client.js");
47
49
  Object.defineProperty(exports, "createChargingClient", { enumerable: true, get: function () { return charging_client_js_1.createChargingClient; } });
48
50
  Object.defineProperty(exports, "pushDataChargingContext", { enumerable: true, get: function () { return charging_client_js_1.pushDataChargingContext; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scrapely",
3
- "version": "1.2.20",
3
+ "version": "1.2.22",
4
4
  "description": "Scrapely SDK for building JavaScript and TypeScript Actors",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -23,6 +23,7 @@
23
23
  "test": "npx tsx tests/test-mvp.ts",
24
24
  "test:charging": "npx tsx --test tests/charging-pending.test.ts",
25
25
  "test:charge-limit-guard": "npx tsx --test tests/charge-limit-guard.test.ts",
26
+ "test:charge-multi-event": "npx tsx --test tests/charge-multi-event.test.ts",
26
27
  "test:storage-retries": "npx tsx --test tests/storage-retries.test.ts",
27
28
  "test:push-idempotency": "npx tsx --test tests/push-idempotency.test.ts",
28
29
  "test:stdio-drain": "npx tsx --test tests/stdio-drain.test.ts",