@drawbridge/drawbridge-utils 0.0.64 → 0.0.66
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/ai.cjs +45 -49
- package/dist/ai.d.cts +7 -161
- package/dist/ai.d.ts +7 -161
- package/dist/ai.js +44 -42
- package/dist/billing.cjs +373 -0
- package/dist/billing.d.cts +300 -0
- package/dist/billing.d.ts +300 -0
- package/dist/billing.js +343 -0
- package/dist/index.cjs +61 -59
- package/dist/index.d.cts +2 -122
- package/dist/index.d.ts +2 -122
- package/dist/index.js +61 -59
- package/dist/phone.cjs +48 -0
- package/dist/phone.d.cts +45 -0
- package/dist/phone.d.ts +45 -0
- package/dist/phone.js +23 -0
- package/dist/plans.d.cts +1 -0
- package/dist/plans.d.ts +1 -0
- package/dist/usage.cjs +91 -0
- package/dist/usage.d.cts +129 -0
- package/dist/usage.d.ts +129 -0
- package/dist/usage.js +65 -0
- package/package.json +12 -1
package/dist/index.cjs
CHANGED
|
@@ -285,6 +285,67 @@ var constants_default = {
|
|
|
285
285
|
}
|
|
286
286
|
};
|
|
287
287
|
|
|
288
|
+
// lib/usage.js
|
|
289
|
+
var incrementUsageTotals = async ({ controller, usageId, $inc, session }) => {
|
|
290
|
+
if (!usageId || !$inc) return;
|
|
291
|
+
const safeInc = Object.fromEntries(
|
|
292
|
+
Object.entries($inc).filter(([, value]) => {
|
|
293
|
+
const number = Number(value);
|
|
294
|
+
return Number.isFinite(number) && number !== 0;
|
|
295
|
+
})
|
|
296
|
+
);
|
|
297
|
+
if (Object.keys(safeInc).length === 0) return;
|
|
298
|
+
return controller.update({
|
|
299
|
+
collection: "usage",
|
|
300
|
+
data: { $inc: safeInc },
|
|
301
|
+
options: {
|
|
302
|
+
bypassDocumentValidation: true,
|
|
303
|
+
...session && { session }
|
|
304
|
+
},
|
|
305
|
+
query: { id: usageId }
|
|
306
|
+
});
|
|
307
|
+
};
|
|
308
|
+
var isDuplicateKey = (error) => (error == null ? void 0 : error.code) === 11e3 || (error == null ? void 0 : error.code) === "11000" || /E11000|duplicate key/i.test((error == null ? void 0 : error.message) || "");
|
|
309
|
+
var recordAction = async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId, session }) => {
|
|
310
|
+
if (!controller || !organization || !usageId || !type || !key) return;
|
|
311
|
+
const amount = Number(units);
|
|
312
|
+
if (!Number.isFinite(amount) || amount === 0) return;
|
|
313
|
+
let inserted = false;
|
|
314
|
+
try {
|
|
315
|
+
const { lastErrorObject } = await controller.update({
|
|
316
|
+
collection: "action",
|
|
317
|
+
data: {
|
|
318
|
+
$setOnInsert: {
|
|
319
|
+
organization,
|
|
320
|
+
usage: usageId,
|
|
321
|
+
type,
|
|
322
|
+
units: amount,
|
|
323
|
+
key,
|
|
324
|
+
meta,
|
|
325
|
+
...traceId && { traceId }
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
options: {
|
|
329
|
+
bypassDocumentValidation: true,
|
|
330
|
+
includeResultMetadata: true,
|
|
331
|
+
upsert: true,
|
|
332
|
+
...session && { session }
|
|
333
|
+
},
|
|
334
|
+
query: {
|
|
335
|
+
key,
|
|
336
|
+
usage: usageId
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
inserted = Boolean(lastErrorObject == null ? void 0 : lastErrorObject.upserted);
|
|
340
|
+
} catch (error) {
|
|
341
|
+
if (isDuplicateKey(error) && !session) return;
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
if (inserted) {
|
|
345
|
+
await incrementUsageTotals({ controller, usageId, $inc: { "totals.actions": amount }, session });
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
|
|
288
349
|
// index.js
|
|
289
350
|
var nanoid = (0, import_nanoid.customAlphabet)("0123456789abcdefghijklmnopqrstuvwxyz", 8);
|
|
290
351
|
var constants = constants_default;
|
|
@@ -373,65 +434,6 @@ var getPlanFeature = (plan, key) => {
|
|
|
373
434
|
message: granted ? feature : error
|
|
374
435
|
};
|
|
375
436
|
};
|
|
376
|
-
var incrementUsageTotals = async ({ controller, usageId, $inc, session }) => {
|
|
377
|
-
if (!usageId || !$inc) return;
|
|
378
|
-
const safeInc = Object.fromEntries(
|
|
379
|
-
Object.entries($inc).filter(([, value]) => {
|
|
380
|
-
const number = Number(value);
|
|
381
|
-
return Number.isFinite(number) && number !== 0;
|
|
382
|
-
})
|
|
383
|
-
);
|
|
384
|
-
if (Object.keys(safeInc).length === 0) return;
|
|
385
|
-
return controller.update({
|
|
386
|
-
collection: "usage",
|
|
387
|
-
data: { $inc: safeInc },
|
|
388
|
-
options: {
|
|
389
|
-
bypassDocumentValidation: true,
|
|
390
|
-
...session && { session }
|
|
391
|
-
},
|
|
392
|
-
query: { id: usageId }
|
|
393
|
-
});
|
|
394
|
-
};
|
|
395
|
-
var isDuplicateKey = (error) => (error == null ? void 0 : error.code) === 11e3 || (error == null ? void 0 : error.code) === "11000" || /E11000|duplicate key/i.test((error == null ? void 0 : error.message) || "");
|
|
396
|
-
var recordAction = async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId, session }) => {
|
|
397
|
-
if (!controller || !organization || !usageId || !type || !key) return;
|
|
398
|
-
const amount = Number(units);
|
|
399
|
-
if (!Number.isFinite(amount) || amount === 0) return;
|
|
400
|
-
let inserted = false;
|
|
401
|
-
try {
|
|
402
|
-
const { lastErrorObject } = await controller.update({
|
|
403
|
-
collection: "action",
|
|
404
|
-
data: {
|
|
405
|
-
$setOnInsert: {
|
|
406
|
-
organization,
|
|
407
|
-
usage: usageId,
|
|
408
|
-
type,
|
|
409
|
-
units: amount,
|
|
410
|
-
key,
|
|
411
|
-
meta,
|
|
412
|
-
...traceId && { traceId }
|
|
413
|
-
}
|
|
414
|
-
},
|
|
415
|
-
options: {
|
|
416
|
-
bypassDocumentValidation: true,
|
|
417
|
-
includeResultMetadata: true,
|
|
418
|
-
upsert: true,
|
|
419
|
-
...session && { session }
|
|
420
|
-
},
|
|
421
|
-
query: {
|
|
422
|
-
key,
|
|
423
|
-
usage: usageId
|
|
424
|
-
}
|
|
425
|
-
});
|
|
426
|
-
inserted = Boolean(lastErrorObject == null ? void 0 : lastErrorObject.upserted);
|
|
427
|
-
} catch (error) {
|
|
428
|
-
if (isDuplicateKey(error) && !session) return;
|
|
429
|
-
throw error;
|
|
430
|
-
}
|
|
431
|
-
if (inserted) {
|
|
432
|
-
await incrementUsageTotals({ controller, usageId, $inc: { "totals.actions": amount }, session });
|
|
433
|
-
}
|
|
434
|
-
};
|
|
435
437
|
var percentage = (value1, value2, decimals = 1) => ((value1 || 0) / (value2 || 0) * 100 || 0).toFixed(decimals);
|
|
436
438
|
var reducers = {
|
|
437
439
|
fields: (data2 = [], callback = () => ({})) => data2.reduce(
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { data, code } from 'currency-codes';
|
|
2
2
|
import { customAlphabet } from 'nanoid';
|
|
3
3
|
import { colorFormatted, colorAccessible } from './color.cjs';
|
|
4
|
+
export { incrementUsageTotals, isDuplicateKey, recordAction } from './usage.cjs';
|
|
4
5
|
import 'tinycolor2';
|
|
5
6
|
|
|
6
7
|
const font = {
|
|
@@ -341,127 +342,6 @@ const getPlanFeature = ( plan, key ) => {
|
|
|
341
342
|
|
|
342
343
|
};
|
|
343
344
|
|
|
344
|
-
// Centralized $inc helper for the per-org usage doc. Replaces inline
|
|
345
|
-
// $inc-with-bypass-validation patterns in drawbridge-api + drawbridge-sync.
|
|
346
|
-
//
|
|
347
|
-
// MongoDB's $inc with 0 / NaN / undefined / null writes the value into the
|
|
348
|
-
// field (zeroing or NaN-ing it) instead of no-op'ing — so we filter the
|
|
349
|
-
// $inc object to keep only finite, non-zero values and callers don't have
|
|
350
|
-
// to remember the guard.
|
|
351
|
-
//
|
|
352
|
-
// `session` is forwarded when present so callers inside a transaction get
|
|
353
|
-
// atomic writes; standalone callers omit it.
|
|
354
|
-
const incrementUsageTotals = async ({ controller, usageId, $inc, session }) => {
|
|
355
|
-
|
|
356
|
-
if( ! usageId || ! $inc ) return;
|
|
357
|
-
|
|
358
|
-
const safeInc = Object.fromEntries(
|
|
359
|
-
Object.entries( $inc ).filter( ([ , value ]) => {
|
|
360
|
-
|
|
361
|
-
const number = Number( value );
|
|
362
|
-
|
|
363
|
-
return Number.isFinite( number ) && number !== 0;
|
|
364
|
-
|
|
365
|
-
})
|
|
366
|
-
);
|
|
367
|
-
|
|
368
|
-
if( Object.keys( safeInc ).length === 0 ) return;
|
|
369
|
-
|
|
370
|
-
return controller.update({
|
|
371
|
-
collection : 'usage',
|
|
372
|
-
data : { $inc : safeInc },
|
|
373
|
-
options : {
|
|
374
|
-
bypassDocumentValidation : true,
|
|
375
|
-
...( session && { session })
|
|
376
|
-
},
|
|
377
|
-
query : { id : usageId }
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
};
|
|
381
|
-
|
|
382
|
-
// True for a MongoDB duplicate-key error (unique-index violation).
|
|
383
|
-
const isDuplicateKey = ( error ) =>
|
|
384
|
-
error?.code === 11000
|
|
385
|
-
|| error?.code === '11000'
|
|
386
|
-
|| /E11000|duplicate key/i.test( error?.message || '' );
|
|
387
|
-
|
|
388
|
-
// Record a billable action as a row in the append-only `action` ledger — the
|
|
389
|
-
// source of truth for usage billing (summed per period at changeover).
|
|
390
|
-
//
|
|
391
|
-
// Idempotent via an UPSERT on the unique { usage, key }: a duplicate event (the
|
|
392
|
-
// segment realtime path AND the sweep firing for the same membership, or a retry)
|
|
393
|
-
// matches the existing row and no-ops — no duplicate-key error — so this is safe
|
|
394
|
-
// to call INSIDE a caller's transaction (a thrown E11000 would abort the whole
|
|
395
|
-
// transaction). The display cache `usage.totals.actions` is bumped ONLY when the
|
|
396
|
-
// upsert actually inserts a new row, so a duplicate never double-counts.
|
|
397
|
-
//
|
|
398
|
-
// Pass `session` to make the ledger row + cache bump atomic with the caller's own
|
|
399
|
-
// writes (e.g. the membership writes in a segment-sweep chunk, or the submission
|
|
400
|
-
// docs) — all-or-nothing. Without a session it runs standalone.
|
|
401
|
-
// key deterministic per source event, e.g. 'segment.join:<contact>:<segment>'
|
|
402
|
-
// units billable units (default 1; N for batch, e.g. accepted entries)
|
|
403
|
-
// meta attribution ids { campaign, page, submission, segment, workflow, step, ... }
|
|
404
|
-
const recordAction = async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId, session }) => {
|
|
405
|
-
|
|
406
|
-
if( ! controller || ! organization || ! usageId || ! type || ! key ) return;
|
|
407
|
-
|
|
408
|
-
const amount = Number( units );
|
|
409
|
-
|
|
410
|
-
if( ! Number.isFinite( amount ) || amount === 0 ) return;
|
|
411
|
-
|
|
412
|
-
let inserted = false;
|
|
413
|
-
|
|
414
|
-
try {
|
|
415
|
-
|
|
416
|
-
const { lastErrorObject } = await controller.update({
|
|
417
|
-
collection : 'action',
|
|
418
|
-
data : {
|
|
419
|
-
$setOnInsert : {
|
|
420
|
-
organization,
|
|
421
|
-
usage : usageId,
|
|
422
|
-
type,
|
|
423
|
-
units : amount,
|
|
424
|
-
key,
|
|
425
|
-
meta,
|
|
426
|
-
...( traceId && { traceId })
|
|
427
|
-
}
|
|
428
|
-
},
|
|
429
|
-
options : {
|
|
430
|
-
bypassDocumentValidation : true,
|
|
431
|
-
includeResultMetadata : true,
|
|
432
|
-
upsert : true,
|
|
433
|
-
...( session && { session })
|
|
434
|
-
},
|
|
435
|
-
query : {
|
|
436
|
-
key,
|
|
437
|
-
usage : usageId
|
|
438
|
-
}
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
inserted = Boolean( lastErrorObject?.upserted );
|
|
442
|
-
|
|
443
|
-
} catch ( error ) {
|
|
444
|
-
|
|
445
|
-
// A concurrent upsert of the same { usage, key } can still race to E11000.
|
|
446
|
-
// Standalone: the other writer recorded it — skip. Inside a transaction:
|
|
447
|
-
// the txn is already aborted, so re-throw to let the caller retry the whole
|
|
448
|
-
// unit (which then sees the row and no-ops).
|
|
449
|
-
if( isDuplicateKey( error ) && ! session ) return;
|
|
450
|
-
|
|
451
|
-
throw error;
|
|
452
|
-
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
// Only a genuinely new row bumps the display cache (a matched duplicate must
|
|
456
|
-
// not double-count). Joined to the caller's transaction when a session is given.
|
|
457
|
-
if( inserted ){
|
|
458
|
-
|
|
459
|
-
await incrementUsageTotals({ controller, usageId, $inc : { 'totals.actions' : amount }, session });
|
|
460
|
-
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
};
|
|
464
|
-
|
|
465
345
|
const percentage = ( value1, value2, decimals = 1 ) => ( ( ( ( value1 || 0 ) / ( value2 || 0 ) ) * 100 ) || 0 ).toFixed( decimals );
|
|
466
346
|
|
|
467
347
|
const reducers = {
|
|
@@ -560,4 +440,4 @@ const currencies = data.map( ( item ) => ({
|
|
|
560
440
|
|
|
561
441
|
const currency = ( val ) => code( val );
|
|
562
442
|
|
|
563
|
-
export { bytesToGB, bytesToMB, capitalize, constants, currencies, currency, expiredPaymentMethod, formatCurrency, formatDateString, formatNumber, getPlanFeature, gigabyte,
|
|
443
|
+
export { bytesToGB, bytesToMB, capitalize, constants, currencies, currency, expiredPaymentMethod, formatCurrency, formatDateString, formatNumber, getPlanFeature, gigabyte, infinite, isInfinite, megabyte, nanoid, percentage, reducers, regex, shareUrls, urlRoot };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { data, code } from 'currency-codes';
|
|
2
2
|
import { customAlphabet } from 'nanoid';
|
|
3
3
|
import { colorFormatted, colorAccessible } from './color.js';
|
|
4
|
+
export { incrementUsageTotals, isDuplicateKey, recordAction } from './usage.js';
|
|
4
5
|
import 'tinycolor2';
|
|
5
6
|
|
|
6
7
|
const font = {
|
|
@@ -341,127 +342,6 @@ const getPlanFeature = ( plan, key ) => {
|
|
|
341
342
|
|
|
342
343
|
};
|
|
343
344
|
|
|
344
|
-
// Centralized $inc helper for the per-org usage doc. Replaces inline
|
|
345
|
-
// $inc-with-bypass-validation patterns in drawbridge-api + drawbridge-sync.
|
|
346
|
-
//
|
|
347
|
-
// MongoDB's $inc with 0 / NaN / undefined / null writes the value into the
|
|
348
|
-
// field (zeroing or NaN-ing it) instead of no-op'ing — so we filter the
|
|
349
|
-
// $inc object to keep only finite, non-zero values and callers don't have
|
|
350
|
-
// to remember the guard.
|
|
351
|
-
//
|
|
352
|
-
// `session` is forwarded when present so callers inside a transaction get
|
|
353
|
-
// atomic writes; standalone callers omit it.
|
|
354
|
-
const incrementUsageTotals = async ({ controller, usageId, $inc, session }) => {
|
|
355
|
-
|
|
356
|
-
if( ! usageId || ! $inc ) return;
|
|
357
|
-
|
|
358
|
-
const safeInc = Object.fromEntries(
|
|
359
|
-
Object.entries( $inc ).filter( ([ , value ]) => {
|
|
360
|
-
|
|
361
|
-
const number = Number( value );
|
|
362
|
-
|
|
363
|
-
return Number.isFinite( number ) && number !== 0;
|
|
364
|
-
|
|
365
|
-
})
|
|
366
|
-
);
|
|
367
|
-
|
|
368
|
-
if( Object.keys( safeInc ).length === 0 ) return;
|
|
369
|
-
|
|
370
|
-
return controller.update({
|
|
371
|
-
collection : 'usage',
|
|
372
|
-
data : { $inc : safeInc },
|
|
373
|
-
options : {
|
|
374
|
-
bypassDocumentValidation : true,
|
|
375
|
-
...( session && { session })
|
|
376
|
-
},
|
|
377
|
-
query : { id : usageId }
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
};
|
|
381
|
-
|
|
382
|
-
// True for a MongoDB duplicate-key error (unique-index violation).
|
|
383
|
-
const isDuplicateKey = ( error ) =>
|
|
384
|
-
error?.code === 11000
|
|
385
|
-
|| error?.code === '11000'
|
|
386
|
-
|| /E11000|duplicate key/i.test( error?.message || '' );
|
|
387
|
-
|
|
388
|
-
// Record a billable action as a row in the append-only `action` ledger — the
|
|
389
|
-
// source of truth for usage billing (summed per period at changeover).
|
|
390
|
-
//
|
|
391
|
-
// Idempotent via an UPSERT on the unique { usage, key }: a duplicate event (the
|
|
392
|
-
// segment realtime path AND the sweep firing for the same membership, or a retry)
|
|
393
|
-
// matches the existing row and no-ops — no duplicate-key error — so this is safe
|
|
394
|
-
// to call INSIDE a caller's transaction (a thrown E11000 would abort the whole
|
|
395
|
-
// transaction). The display cache `usage.totals.actions` is bumped ONLY when the
|
|
396
|
-
// upsert actually inserts a new row, so a duplicate never double-counts.
|
|
397
|
-
//
|
|
398
|
-
// Pass `session` to make the ledger row + cache bump atomic with the caller's own
|
|
399
|
-
// writes (e.g. the membership writes in a segment-sweep chunk, or the submission
|
|
400
|
-
// docs) — all-or-nothing. Without a session it runs standalone.
|
|
401
|
-
// key deterministic per source event, e.g. 'segment.join:<contact>:<segment>'
|
|
402
|
-
// units billable units (default 1; N for batch, e.g. accepted entries)
|
|
403
|
-
// meta attribution ids { campaign, page, submission, segment, workflow, step, ... }
|
|
404
|
-
const recordAction = async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId, session }) => {
|
|
405
|
-
|
|
406
|
-
if( ! controller || ! organization || ! usageId || ! type || ! key ) return;
|
|
407
|
-
|
|
408
|
-
const amount = Number( units );
|
|
409
|
-
|
|
410
|
-
if( ! Number.isFinite( amount ) || amount === 0 ) return;
|
|
411
|
-
|
|
412
|
-
let inserted = false;
|
|
413
|
-
|
|
414
|
-
try {
|
|
415
|
-
|
|
416
|
-
const { lastErrorObject } = await controller.update({
|
|
417
|
-
collection : 'action',
|
|
418
|
-
data : {
|
|
419
|
-
$setOnInsert : {
|
|
420
|
-
organization,
|
|
421
|
-
usage : usageId,
|
|
422
|
-
type,
|
|
423
|
-
units : amount,
|
|
424
|
-
key,
|
|
425
|
-
meta,
|
|
426
|
-
...( traceId && { traceId })
|
|
427
|
-
}
|
|
428
|
-
},
|
|
429
|
-
options : {
|
|
430
|
-
bypassDocumentValidation : true,
|
|
431
|
-
includeResultMetadata : true,
|
|
432
|
-
upsert : true,
|
|
433
|
-
...( session && { session })
|
|
434
|
-
},
|
|
435
|
-
query : {
|
|
436
|
-
key,
|
|
437
|
-
usage : usageId
|
|
438
|
-
}
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
inserted = Boolean( lastErrorObject?.upserted );
|
|
442
|
-
|
|
443
|
-
} catch ( error ) {
|
|
444
|
-
|
|
445
|
-
// A concurrent upsert of the same { usage, key } can still race to E11000.
|
|
446
|
-
// Standalone: the other writer recorded it — skip. Inside a transaction:
|
|
447
|
-
// the txn is already aborted, so re-throw to let the caller retry the whole
|
|
448
|
-
// unit (which then sees the row and no-ops).
|
|
449
|
-
if( isDuplicateKey( error ) && ! session ) return;
|
|
450
|
-
|
|
451
|
-
throw error;
|
|
452
|
-
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
// Only a genuinely new row bumps the display cache (a matched duplicate must
|
|
456
|
-
// not double-count). Joined to the caller's transaction when a session is given.
|
|
457
|
-
if( inserted ){
|
|
458
|
-
|
|
459
|
-
await incrementUsageTotals({ controller, usageId, $inc : { 'totals.actions' : amount }, session });
|
|
460
|
-
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
};
|
|
464
|
-
|
|
465
345
|
const percentage = ( value1, value2, decimals = 1 ) => ( ( ( ( value1 || 0 ) / ( value2 || 0 ) ) * 100 ) || 0 ).toFixed( decimals );
|
|
466
346
|
|
|
467
347
|
const reducers = {
|
|
@@ -560,4 +440,4 @@ const currencies = data.map( ( item ) => ({
|
|
|
560
440
|
|
|
561
441
|
const currency = ( val ) => code( val );
|
|
562
442
|
|
|
563
|
-
export { bytesToGB, bytesToMB, capitalize, constants, currencies, currency, expiredPaymentMethod, formatCurrency, formatDateString, formatNumber, getPlanFeature, gigabyte,
|
|
443
|
+
export { bytesToGB, bytesToMB, capitalize, constants, currencies, currency, expiredPaymentMethod, formatCurrency, formatDateString, formatNumber, getPlanFeature, gigabyte, infinite, isInfinite, megabyte, nanoid, percentage, reducers, regex, shareUrls, urlRoot };
|
package/dist/index.js
CHANGED
|
@@ -229,6 +229,67 @@ var constants_default = {
|
|
|
229
229
|
}
|
|
230
230
|
};
|
|
231
231
|
|
|
232
|
+
// lib/usage.js
|
|
233
|
+
var incrementUsageTotals = async ({ controller, usageId, $inc, session }) => {
|
|
234
|
+
if (!usageId || !$inc) return;
|
|
235
|
+
const safeInc = Object.fromEntries(
|
|
236
|
+
Object.entries($inc).filter(([, value]) => {
|
|
237
|
+
const number = Number(value);
|
|
238
|
+
return Number.isFinite(number) && number !== 0;
|
|
239
|
+
})
|
|
240
|
+
);
|
|
241
|
+
if (Object.keys(safeInc).length === 0) return;
|
|
242
|
+
return controller.update({
|
|
243
|
+
collection: "usage",
|
|
244
|
+
data: { $inc: safeInc },
|
|
245
|
+
options: {
|
|
246
|
+
bypassDocumentValidation: true,
|
|
247
|
+
...session && { session }
|
|
248
|
+
},
|
|
249
|
+
query: { id: usageId }
|
|
250
|
+
});
|
|
251
|
+
};
|
|
252
|
+
var isDuplicateKey = (error) => (error == null ? void 0 : error.code) === 11e3 || (error == null ? void 0 : error.code) === "11000" || /E11000|duplicate key/i.test((error == null ? void 0 : error.message) || "");
|
|
253
|
+
var recordAction = async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId, session }) => {
|
|
254
|
+
if (!controller || !organization || !usageId || !type || !key) return;
|
|
255
|
+
const amount = Number(units);
|
|
256
|
+
if (!Number.isFinite(amount) || amount === 0) return;
|
|
257
|
+
let inserted = false;
|
|
258
|
+
try {
|
|
259
|
+
const { lastErrorObject } = await controller.update({
|
|
260
|
+
collection: "action",
|
|
261
|
+
data: {
|
|
262
|
+
$setOnInsert: {
|
|
263
|
+
organization,
|
|
264
|
+
usage: usageId,
|
|
265
|
+
type,
|
|
266
|
+
units: amount,
|
|
267
|
+
key,
|
|
268
|
+
meta,
|
|
269
|
+
...traceId && { traceId }
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
options: {
|
|
273
|
+
bypassDocumentValidation: true,
|
|
274
|
+
includeResultMetadata: true,
|
|
275
|
+
upsert: true,
|
|
276
|
+
...session && { session }
|
|
277
|
+
},
|
|
278
|
+
query: {
|
|
279
|
+
key,
|
|
280
|
+
usage: usageId
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
inserted = Boolean(lastErrorObject == null ? void 0 : lastErrorObject.upserted);
|
|
284
|
+
} catch (error) {
|
|
285
|
+
if (isDuplicateKey(error) && !session) return;
|
|
286
|
+
throw error;
|
|
287
|
+
}
|
|
288
|
+
if (inserted) {
|
|
289
|
+
await incrementUsageTotals({ controller, usageId, $inc: { "totals.actions": amount }, session });
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
|
|
232
293
|
// index.js
|
|
233
294
|
var nanoid = customAlphabet("0123456789abcdefghijklmnopqrstuvwxyz", 8);
|
|
234
295
|
var constants = constants_default;
|
|
@@ -317,65 +378,6 @@ var getPlanFeature = (plan, key) => {
|
|
|
317
378
|
message: granted ? feature : error
|
|
318
379
|
};
|
|
319
380
|
};
|
|
320
|
-
var incrementUsageTotals = async ({ controller, usageId, $inc, session }) => {
|
|
321
|
-
if (!usageId || !$inc) return;
|
|
322
|
-
const safeInc = Object.fromEntries(
|
|
323
|
-
Object.entries($inc).filter(([, value]) => {
|
|
324
|
-
const number = Number(value);
|
|
325
|
-
return Number.isFinite(number) && number !== 0;
|
|
326
|
-
})
|
|
327
|
-
);
|
|
328
|
-
if (Object.keys(safeInc).length === 0) return;
|
|
329
|
-
return controller.update({
|
|
330
|
-
collection: "usage",
|
|
331
|
-
data: { $inc: safeInc },
|
|
332
|
-
options: {
|
|
333
|
-
bypassDocumentValidation: true,
|
|
334
|
-
...session && { session }
|
|
335
|
-
},
|
|
336
|
-
query: { id: usageId }
|
|
337
|
-
});
|
|
338
|
-
};
|
|
339
|
-
var isDuplicateKey = (error) => (error == null ? void 0 : error.code) === 11e3 || (error == null ? void 0 : error.code) === "11000" || /E11000|duplicate key/i.test((error == null ? void 0 : error.message) || "");
|
|
340
|
-
var recordAction = async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId, session }) => {
|
|
341
|
-
if (!controller || !organization || !usageId || !type || !key) return;
|
|
342
|
-
const amount = Number(units);
|
|
343
|
-
if (!Number.isFinite(amount) || amount === 0) return;
|
|
344
|
-
let inserted = false;
|
|
345
|
-
try {
|
|
346
|
-
const { lastErrorObject } = await controller.update({
|
|
347
|
-
collection: "action",
|
|
348
|
-
data: {
|
|
349
|
-
$setOnInsert: {
|
|
350
|
-
organization,
|
|
351
|
-
usage: usageId,
|
|
352
|
-
type,
|
|
353
|
-
units: amount,
|
|
354
|
-
key,
|
|
355
|
-
meta,
|
|
356
|
-
...traceId && { traceId }
|
|
357
|
-
}
|
|
358
|
-
},
|
|
359
|
-
options: {
|
|
360
|
-
bypassDocumentValidation: true,
|
|
361
|
-
includeResultMetadata: true,
|
|
362
|
-
upsert: true,
|
|
363
|
-
...session && { session }
|
|
364
|
-
},
|
|
365
|
-
query: {
|
|
366
|
-
key,
|
|
367
|
-
usage: usageId
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
|
-
inserted = Boolean(lastErrorObject == null ? void 0 : lastErrorObject.upserted);
|
|
371
|
-
} catch (error) {
|
|
372
|
-
if (isDuplicateKey(error) && !session) return;
|
|
373
|
-
throw error;
|
|
374
|
-
}
|
|
375
|
-
if (inserted) {
|
|
376
|
-
await incrementUsageTotals({ controller, usageId, $inc: { "totals.actions": amount }, session });
|
|
377
|
-
}
|
|
378
|
-
};
|
|
379
381
|
var percentage = (value1, value2, decimals = 1) => ((value1 || 0) / (value2 || 0) * 100 || 0).toFixed(decimals);
|
|
380
382
|
var reducers = {
|
|
381
383
|
fields: (data2 = [], callback = () => ({})) => data2.reduce(
|
package/dist/phone.cjs
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// lib/phone.js
|
|
20
|
+
var phone_exports = {};
|
|
21
|
+
__export(phone_exports, {
|
|
22
|
+
isValid: () => isValid,
|
|
23
|
+
toE164: () => toE164
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(phone_exports);
|
|
26
|
+
var import_libphonenumber_js = require("libphonenumber-js");
|
|
27
|
+
var toE164 = (value, country) => {
|
|
28
|
+
if (!value) return null;
|
|
29
|
+
try {
|
|
30
|
+
const parsed = (0, import_libphonenumber_js.parsePhoneNumberFromString)(String(value), country);
|
|
31
|
+
return parsed ? parsed.number : null;
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var isValid = (value, country) => {
|
|
37
|
+
if (!value) return false;
|
|
38
|
+
try {
|
|
39
|
+
return (0, import_libphonenumber_js.isValidPhoneNumber)(String(value), country);
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
45
|
+
0 && (module.exports = {
|
|
46
|
+
isValid,
|
|
47
|
+
toE164
|
|
48
|
+
});
|
package/dist/phone.d.cts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { isValidPhoneNumber, parsePhoneNumberFromString } from 'libphonenumber-js';
|
|
2
|
+
|
|
3
|
+
// Canonicalize a phone value to E.164 (e.g. '+15551234567'), or null if it cannot
|
|
4
|
+
// be parsed. `country` (ISO 3166-1 alpha-2, e.g. 'US') is only needed for
|
|
5
|
+
// national-format input; E.164 input (Shopify webhooks, react-phone-number-input)
|
|
6
|
+
// parses without it. Lenient by design — returns the canonical form for any
|
|
7
|
+
// parseable number so order/lead phone matching does not drop valid-but-unusual
|
|
8
|
+
// numbers; clearly garbage input yields null.
|
|
9
|
+
const toE164 = ( value, country ) => {
|
|
10
|
+
|
|
11
|
+
if( ! value ) return null;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
|
|
15
|
+
const parsed = parsePhoneNumberFromString( String( value ), country );
|
|
16
|
+
|
|
17
|
+
return parsed ? parsed.number : null;
|
|
18
|
+
|
|
19
|
+
} catch {
|
|
20
|
+
|
|
21
|
+
return null;
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Strict validity check (wraps libphonenumber-js). Mirrors the validation
|
|
28
|
+
// drawbridge-api's submission route already performs on lead phone input.
|
|
29
|
+
const isValid = ( value, country ) => {
|
|
30
|
+
|
|
31
|
+
if( ! value ) return false;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
|
|
35
|
+
return isValidPhoneNumber( String( value ), country );
|
|
36
|
+
|
|
37
|
+
} catch {
|
|
38
|
+
|
|
39
|
+
return false;
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export { isValid, toE164 };
|
package/dist/phone.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { isValidPhoneNumber, parsePhoneNumberFromString } from 'libphonenumber-js';
|
|
2
|
+
|
|
3
|
+
// Canonicalize a phone value to E.164 (e.g. '+15551234567'), or null if it cannot
|
|
4
|
+
// be parsed. `country` (ISO 3166-1 alpha-2, e.g. 'US') is only needed for
|
|
5
|
+
// national-format input; E.164 input (Shopify webhooks, react-phone-number-input)
|
|
6
|
+
// parses without it. Lenient by design — returns the canonical form for any
|
|
7
|
+
// parseable number so order/lead phone matching does not drop valid-but-unusual
|
|
8
|
+
// numbers; clearly garbage input yields null.
|
|
9
|
+
const toE164 = ( value, country ) => {
|
|
10
|
+
|
|
11
|
+
if( ! value ) return null;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
|
|
15
|
+
const parsed = parsePhoneNumberFromString( String( value ), country );
|
|
16
|
+
|
|
17
|
+
return parsed ? parsed.number : null;
|
|
18
|
+
|
|
19
|
+
} catch {
|
|
20
|
+
|
|
21
|
+
return null;
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Strict validity check (wraps libphonenumber-js). Mirrors the validation
|
|
28
|
+
// drawbridge-api's submission route already performs on lead phone input.
|
|
29
|
+
const isValid = ( value, country ) => {
|
|
30
|
+
|
|
31
|
+
if( ! value ) return false;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
|
|
35
|
+
return isValidPhoneNumber( String( value ), country );
|
|
36
|
+
|
|
37
|
+
} catch {
|
|
38
|
+
|
|
39
|
+
return false;
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export { isValid, toE164 };
|