@drawbridge/drawbridge-utils 0.0.69 → 0.0.71

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/billing.cjs CHANGED
@@ -253,26 +253,59 @@ var ai = {
253
253
  };
254
254
  var STANDARD_RATE_PER_GB = 8;
255
255
  var PREMIUM_RATE_PER_GB = 11;
256
+ var STANDARD_CPM_CENTS = 0.15;
257
+ var PREMIUM_CPM_CENTS = 0.25;
256
258
  var PREMIUM_HOSTS = /* @__PURE__ */ new Set();
257
259
  var LOCAL_FLAT_CENTS = 1;
258
260
  var REUSE_FLAT_CENTS = 1;
259
- var ratePerGB = (url) => {
261
+ var isPremium = (url) => {
260
262
  try {
261
- return PREMIUM_HOSTS.has(new URL(url).hostname) ? PREMIUM_RATE_PER_GB : STANDARD_RATE_PER_GB;
263
+ return PREMIUM_HOSTS.has(new URL(url).hostname);
262
264
  } catch {
263
- return STANDARD_RATE_PER_GB;
265
+ return false;
264
266
  }
265
267
  };
266
- var scrapeFee = ({ provider, bytes, url, cold }) => {
268
+ var ratePerGB = (url) => isPremium(url) ? PREMIUM_RATE_PER_GB : STANDARD_RATE_PER_GB;
269
+ var cpmCents = (url) => isPremium(url) ? PREMIUM_CPM_CENTS : STANDARD_CPM_CENTS;
270
+ var scrapeFee = ({ provider, bytes, requests, url, cold }) => {
267
271
  if (!cold) return REUSE_FLAT_CENTS;
268
272
  if (provider === "brightdata") {
269
273
  return Math.ceil((bytes || 0) / 1e9 * ratePerGB(url) * 100 * MARKUP);
270
274
  }
275
+ if (provider === "brightdata-unlocker") {
276
+ return Math.ceil((requests || 0) * cpmCents(url) * MARKUP);
277
+ }
271
278
  return LOCAL_FLAT_CENTS;
272
279
  };
280
+ var scrapeBreakdown = ({ provider, bytes, requests, url }) => {
281
+ if (provider === "brightdata") {
282
+ return {
283
+ provider,
284
+ basis: "bytes",
285
+ units: bytes || 0,
286
+ rate: ratePerGB(url),
287
+ rateUnit: "GB",
288
+ cents: scrapeFee({ provider, bytes, url, cold: true })
289
+ };
290
+ }
291
+ if (provider === "brightdata-unlocker") {
292
+ return {
293
+ provider,
294
+ basis: "requests",
295
+ units: requests || 0,
296
+ // cpmCents is cents-per-request; ×10 expresses it as dollars per 1,000.
297
+ rate: cpmCents(url) * 10,
298
+ rateUnit: "1K requests",
299
+ cents: scrapeFee({ provider, requests, url, cold: true })
300
+ };
301
+ }
302
+ return { provider: provider || "local", basis: "local", units: null, rate: null, rateUnit: null, cents: LOCAL_FLAT_CENTS };
303
+ };
273
304
  var scrape = {
274
305
  // Cents for a scrape given its provider/bytes/cold basis.
275
306
  fee: scrapeFee,
307
+ // Itemized cold-fee breakdown (basis/units/rate/cents) for display + the scrape doc.
308
+ breakdown: scrapeBreakdown,
276
309
  // Deferred bill(scrapeId) the scrape store's resolve() hands the generation
277
310
  // flow. Atomically claims the cold (first-time) fee via the `charged` flag so
278
311
  // a BrightData scrape's metered cost is recovered ONCE; later reuses pay the
@@ -290,6 +323,7 @@ var scrape = {
290
323
  const cents = scrapeFee({
291
324
  provider: (_a = page == null ? void 0 : page.provider) == null ? void 0 : _a.name,
292
325
  bytes: page == null ? void 0 : page.bytes,
326
+ requests: page == null ? void 0 : page.requests,
293
327
  url: (page == null ? void 0 : page.url) || url,
294
328
  cold: Boolean(claim == null ? void 0 : claim.value)
295
329
  });
@@ -174,27 +174,39 @@ const ai = {
174
174
 
175
175
  // ─── Scrape (credit rail) ──────────────────────────────────────────────────────
176
176
 
177
- // BrightData (the Scraping Browser fallback) bills by GB, so its fee is METERED
178
- // to the traffic measured at scrape time; the 30% margin matches MARKUP above. A
179
- // cold local render and a cache reuse have no external cost, so they're small
180
- // flat fees (set to 0 to make either free). Tune against real BrightData invoices.
177
+ // BrightData has two unblocking tiers, billed on different bases:
178
+ // - Scraping Browser (the CDP fallback): bills by GB of traffic, so its fee is
179
+ // METERED to the bytes measured at scrape time.
180
+ // - Web Unlocker (the last-resort HTML tier): bills per successful request (CPM
181
+ // = per 1,000), so its fee is METERED to the request COUNT the tier made.
182
+ // Both carry the same 30% margin (MARKUP). A cold local render and a cache reuse
183
+ // have no external cost, so they're small flat fees (set to 0 to make either
184
+ // free). Tune the rates against real BrightData invoices.
181
185
  const STANDARD_RATE_PER_GB = 8;
182
186
  const PREMIUM_RATE_PER_GB = 11;
187
+ // Web Unlocker CPM in cents-per-request: $1.50/1,000 = 0.15¢, $2.50/1,000 = 0.25¢.
188
+ const STANDARD_CPM_CENTS = 0.15;
189
+ const PREMIUM_CPM_CENTS = 0.25;
190
+ // Premium-rate domains — shared by both tiers (empty = everything standard).
183
191
  const PREMIUM_HOSTS = new Set();
184
192
  const LOCAL_FLAT_CENTS = 1;
185
193
  const REUSE_FLAT_CENTS = 1;
186
194
 
187
- const ratePerGB = ( url ) => {
195
+ const isPremium = ( url ) => {
188
196
 
189
- try { return PREMIUM_HOSTS.has( new URL( url ).hostname ) ? PREMIUM_RATE_PER_GB : STANDARD_RATE_PER_GB; }
190
- catch { return STANDARD_RATE_PER_GB; }
197
+ try { return PREMIUM_HOSTS.has( new URL( url ).hostname ); }
198
+ catch { return false; }
191
199
 
192
200
  };
193
201
 
202
+ const ratePerGB = ( url ) => isPremium( url ) ? PREMIUM_RATE_PER_GB : STANDARD_RATE_PER_GB;
203
+ const cpmCents = ( url ) => isPremium( url ) ? PREMIUM_CPM_CENTS : STANDARD_CPM_CENTS;
204
+
194
205
  // Cents to charge. `cold` = the first generation off this scrape (charge the real
195
- // cost: metered for BrightData, a flat for a local render); otherwise it's a reuse
196
- // (flat). Rounded up so we never under-bill a fractional cent.
197
- const scrapeFee = ({ provider, bytes, url, cold }) => {
206
+ // cost: metered for either BrightData tier, a flat for a local render); otherwise
207
+ // it's a reuse (flat). Rounded up so we never under-bill a fractional cent — so a
208
+ // typical 1–2 request unlock rounds up to the floor.
209
+ const scrapeFee = ({ provider, bytes, requests, url, cold }) => {
198
210
 
199
211
  if( ! cold ) return REUSE_FLAT_CENTS;
200
212
 
@@ -204,15 +216,63 @@ const scrapeFee = ({ provider, bytes, url, cold }) => {
204
216
 
205
217
  }
206
218
 
219
+ if( provider === 'brightdata-unlocker' ){
220
+
221
+ return Math.ceil( ( requests || 0 ) * cpmCents( url ) * MARKUP );
222
+
223
+ }
224
+
207
225
  return LOCAL_FLAT_CENTS;
208
226
 
209
227
  };
210
228
 
229
+ // Itemized cold-fee breakdown for the scrape doc / admin dashboard: the same cents
230
+ // scrapeFee charges on a COLD scrape, plus the basis, the metered units, and the
231
+ // wholesale provider rate behind it. Reuse hits aren't represented here — the doc
232
+ // records its cold acquisition cost. `rate` is the wholesale rate in dollars per
233
+ // `rateUnit` (pre-markup, i.e. the BrightData list price); `cents` is the retail fee
234
+ // billed (incl MARKUP). Local renders have no external cost — flat, no rate.
235
+ const scrapeBreakdown = ({ provider, bytes, requests, url }) => {
236
+
237
+ if( provider === 'brightdata' ){
238
+
239
+ return {
240
+ provider,
241
+ basis : 'bytes',
242
+ units : bytes || 0,
243
+ rate : ratePerGB( url ),
244
+ rateUnit : 'GB',
245
+ cents : scrapeFee({ provider, bytes, url, cold : true })
246
+ };
247
+
248
+ }
249
+
250
+ if( provider === 'brightdata-unlocker' ){
251
+
252
+ return {
253
+ provider,
254
+ basis : 'requests',
255
+ units : requests || 0,
256
+ // cpmCents is cents-per-request; ×10 expresses it as dollars per 1,000.
257
+ rate : cpmCents( url ) * 10,
258
+ rateUnit : '1K requests',
259
+ cents : scrapeFee({ provider, requests, url, cold : true })
260
+ };
261
+
262
+ }
263
+
264
+ return { provider : provider || 'local', basis : 'local', units : null, rate : null, rateUnit : null, cents : LOCAL_FLAT_CENTS };
265
+
266
+ };
267
+
211
268
  const scrape = {
212
269
 
213
270
  // Cents for a scrape given its provider/bytes/cold basis.
214
271
  fee : scrapeFee,
215
272
 
273
+ // Itemized cold-fee breakdown (basis/units/rate/cents) for display + the scrape doc.
274
+ breakdown : scrapeBreakdown,
275
+
216
276
  // Deferred bill(scrapeId) the scrape store's resolve() hands the generation
217
277
  // flow. Atomically claims the cold (first-time) fee via the `charged` flag so
218
278
  // a BrightData scrape's metered cost is recovered ONCE; later reuses pay the
@@ -233,6 +293,7 @@ const scrape = {
233
293
  const cents = scrapeFee({
234
294
  provider : page?.provider?.name,
235
295
  bytes : page?.bytes,
296
+ requests : page?.requests,
236
297
  url : page?.url || url,
237
298
  cold : Boolean( claim?.value )
238
299
  });
package/dist/billing.d.ts CHANGED
@@ -174,27 +174,39 @@ const ai = {
174
174
 
175
175
  // ─── Scrape (credit rail) ──────────────────────────────────────────────────────
176
176
 
177
- // BrightData (the Scraping Browser fallback) bills by GB, so its fee is METERED
178
- // to the traffic measured at scrape time; the 30% margin matches MARKUP above. A
179
- // cold local render and a cache reuse have no external cost, so they're small
180
- // flat fees (set to 0 to make either free). Tune against real BrightData invoices.
177
+ // BrightData has two unblocking tiers, billed on different bases:
178
+ // - Scraping Browser (the CDP fallback): bills by GB of traffic, so its fee is
179
+ // METERED to the bytes measured at scrape time.
180
+ // - Web Unlocker (the last-resort HTML tier): bills per successful request (CPM
181
+ // = per 1,000), so its fee is METERED to the request COUNT the tier made.
182
+ // Both carry the same 30% margin (MARKUP). A cold local render and a cache reuse
183
+ // have no external cost, so they're small flat fees (set to 0 to make either
184
+ // free). Tune the rates against real BrightData invoices.
181
185
  const STANDARD_RATE_PER_GB = 8;
182
186
  const PREMIUM_RATE_PER_GB = 11;
187
+ // Web Unlocker CPM in cents-per-request: $1.50/1,000 = 0.15¢, $2.50/1,000 = 0.25¢.
188
+ const STANDARD_CPM_CENTS = 0.15;
189
+ const PREMIUM_CPM_CENTS = 0.25;
190
+ // Premium-rate domains — shared by both tiers (empty = everything standard).
183
191
  const PREMIUM_HOSTS = new Set();
184
192
  const LOCAL_FLAT_CENTS = 1;
185
193
  const REUSE_FLAT_CENTS = 1;
186
194
 
187
- const ratePerGB = ( url ) => {
195
+ const isPremium = ( url ) => {
188
196
 
189
- try { return PREMIUM_HOSTS.has( new URL( url ).hostname ) ? PREMIUM_RATE_PER_GB : STANDARD_RATE_PER_GB; }
190
- catch { return STANDARD_RATE_PER_GB; }
197
+ try { return PREMIUM_HOSTS.has( new URL( url ).hostname ); }
198
+ catch { return false; }
191
199
 
192
200
  };
193
201
 
202
+ const ratePerGB = ( url ) => isPremium( url ) ? PREMIUM_RATE_PER_GB : STANDARD_RATE_PER_GB;
203
+ const cpmCents = ( url ) => isPremium( url ) ? PREMIUM_CPM_CENTS : STANDARD_CPM_CENTS;
204
+
194
205
  // Cents to charge. `cold` = the first generation off this scrape (charge the real
195
- // cost: metered for BrightData, a flat for a local render); otherwise it's a reuse
196
- // (flat). Rounded up so we never under-bill a fractional cent.
197
- const scrapeFee = ({ provider, bytes, url, cold }) => {
206
+ // cost: metered for either BrightData tier, a flat for a local render); otherwise
207
+ // it's a reuse (flat). Rounded up so we never under-bill a fractional cent — so a
208
+ // typical 1–2 request unlock rounds up to the floor.
209
+ const scrapeFee = ({ provider, bytes, requests, url, cold }) => {
198
210
 
199
211
  if( ! cold ) return REUSE_FLAT_CENTS;
200
212
 
@@ -204,15 +216,63 @@ const scrapeFee = ({ provider, bytes, url, cold }) => {
204
216
 
205
217
  }
206
218
 
219
+ if( provider === 'brightdata-unlocker' ){
220
+
221
+ return Math.ceil( ( requests || 0 ) * cpmCents( url ) * MARKUP );
222
+
223
+ }
224
+
207
225
  return LOCAL_FLAT_CENTS;
208
226
 
209
227
  };
210
228
 
229
+ // Itemized cold-fee breakdown for the scrape doc / admin dashboard: the same cents
230
+ // scrapeFee charges on a COLD scrape, plus the basis, the metered units, and the
231
+ // wholesale provider rate behind it. Reuse hits aren't represented here — the doc
232
+ // records its cold acquisition cost. `rate` is the wholesale rate in dollars per
233
+ // `rateUnit` (pre-markup, i.e. the BrightData list price); `cents` is the retail fee
234
+ // billed (incl MARKUP). Local renders have no external cost — flat, no rate.
235
+ const scrapeBreakdown = ({ provider, bytes, requests, url }) => {
236
+
237
+ if( provider === 'brightdata' ){
238
+
239
+ return {
240
+ provider,
241
+ basis : 'bytes',
242
+ units : bytes || 0,
243
+ rate : ratePerGB( url ),
244
+ rateUnit : 'GB',
245
+ cents : scrapeFee({ provider, bytes, url, cold : true })
246
+ };
247
+
248
+ }
249
+
250
+ if( provider === 'brightdata-unlocker' ){
251
+
252
+ return {
253
+ provider,
254
+ basis : 'requests',
255
+ units : requests || 0,
256
+ // cpmCents is cents-per-request; ×10 expresses it as dollars per 1,000.
257
+ rate : cpmCents( url ) * 10,
258
+ rateUnit : '1K requests',
259
+ cents : scrapeFee({ provider, requests, url, cold : true })
260
+ };
261
+
262
+ }
263
+
264
+ return { provider : provider || 'local', basis : 'local', units : null, rate : null, rateUnit : null, cents : LOCAL_FLAT_CENTS };
265
+
266
+ };
267
+
211
268
  const scrape = {
212
269
 
213
270
  // Cents for a scrape given its provider/bytes/cold basis.
214
271
  fee : scrapeFee,
215
272
 
273
+ // Itemized cold-fee breakdown (basis/units/rate/cents) for display + the scrape doc.
274
+ breakdown : scrapeBreakdown,
275
+
216
276
  // Deferred bill(scrapeId) the scrape store's resolve() hands the generation
217
277
  // flow. Atomically claims the cold (first-time) fee via the `charged` flag so
218
278
  // a BrightData scrape's metered cost is recovered ONCE; later reuses pay the
@@ -233,6 +293,7 @@ const scrape = {
233
293
  const cents = scrapeFee({
234
294
  provider : page?.provider?.name,
235
295
  bytes : page?.bytes,
296
+ requests : page?.requests,
236
297
  url : page?.url || url,
237
298
  cold : Boolean( claim?.value )
238
299
  });
package/dist/billing.js CHANGED
@@ -224,26 +224,59 @@ var ai = {
224
224
  };
225
225
  var STANDARD_RATE_PER_GB = 8;
226
226
  var PREMIUM_RATE_PER_GB = 11;
227
+ var STANDARD_CPM_CENTS = 0.15;
228
+ var PREMIUM_CPM_CENTS = 0.25;
227
229
  var PREMIUM_HOSTS = /* @__PURE__ */ new Set();
228
230
  var LOCAL_FLAT_CENTS = 1;
229
231
  var REUSE_FLAT_CENTS = 1;
230
- var ratePerGB = (url) => {
232
+ var isPremium = (url) => {
231
233
  try {
232
- return PREMIUM_HOSTS.has(new URL(url).hostname) ? PREMIUM_RATE_PER_GB : STANDARD_RATE_PER_GB;
234
+ return PREMIUM_HOSTS.has(new URL(url).hostname);
233
235
  } catch {
234
- return STANDARD_RATE_PER_GB;
236
+ return false;
235
237
  }
236
238
  };
237
- var scrapeFee = ({ provider, bytes, url, cold }) => {
239
+ var ratePerGB = (url) => isPremium(url) ? PREMIUM_RATE_PER_GB : STANDARD_RATE_PER_GB;
240
+ var cpmCents = (url) => isPremium(url) ? PREMIUM_CPM_CENTS : STANDARD_CPM_CENTS;
241
+ var scrapeFee = ({ provider, bytes, requests, url, cold }) => {
238
242
  if (!cold) return REUSE_FLAT_CENTS;
239
243
  if (provider === "brightdata") {
240
244
  return Math.ceil((bytes || 0) / 1e9 * ratePerGB(url) * 100 * MARKUP);
241
245
  }
246
+ if (provider === "brightdata-unlocker") {
247
+ return Math.ceil((requests || 0) * cpmCents(url) * MARKUP);
248
+ }
242
249
  return LOCAL_FLAT_CENTS;
243
250
  };
251
+ var scrapeBreakdown = ({ provider, bytes, requests, url }) => {
252
+ if (provider === "brightdata") {
253
+ return {
254
+ provider,
255
+ basis: "bytes",
256
+ units: bytes || 0,
257
+ rate: ratePerGB(url),
258
+ rateUnit: "GB",
259
+ cents: scrapeFee({ provider, bytes, url, cold: true })
260
+ };
261
+ }
262
+ if (provider === "brightdata-unlocker") {
263
+ return {
264
+ provider,
265
+ basis: "requests",
266
+ units: requests || 0,
267
+ // cpmCents is cents-per-request; ×10 expresses it as dollars per 1,000.
268
+ rate: cpmCents(url) * 10,
269
+ rateUnit: "1K requests",
270
+ cents: scrapeFee({ provider, requests, url, cold: true })
271
+ };
272
+ }
273
+ return { provider: provider || "local", basis: "local", units: null, rate: null, rateUnit: null, cents: LOCAL_FLAT_CENTS };
274
+ };
244
275
  var scrape = {
245
276
  // Cents for a scrape given its provider/bytes/cold basis.
246
277
  fee: scrapeFee,
278
+ // Itemized cold-fee breakdown (basis/units/rate/cents) for display + the scrape doc.
279
+ breakdown: scrapeBreakdown,
247
280
  // Deferred bill(scrapeId) the scrape store's resolve() hands the generation
248
281
  // flow. Atomically claims the cold (first-time) fee via the `charged` flag so
249
282
  // a BrightData scrape's metered cost is recovered ONCE; later reuses pay the
@@ -261,6 +294,7 @@ var scrape = {
261
294
  const cents = scrapeFee({
262
295
  provider: (_a = page == null ? void 0 : page.provider) == null ? void 0 : _a.name,
263
296
  bytes: page == null ? void 0 : page.bytes,
297
+ requests: page == null ? void 0 : page.requests,
264
298
  url: (page == null ? void 0 : page.url) || url,
265
299
  cold: Boolean(claim == null ? void 0 : claim.value)
266
300
  });
package/package.json CHANGED
@@ -158,5 +158,5 @@
158
158
  "build": "tsup && npm publish"
159
159
  },
160
160
  "types": "dist/index.d.ts",
161
- "version": "0.0.69"
161
+ "version": "0.0.71"
162
162
  }