@drawbridge/drawbridge-utils 0.0.61 → 0.0.62
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/index.cjs +28 -0
- package/dist/index.d.cts +58 -1
- package/dist/index.d.ts +58 -1
- package/dist/index.js +26 -0
- package/package.json +1 -4
package/dist/index.cjs
CHANGED
|
@@ -43,10 +43,12 @@ __export(index_exports, {
|
|
|
43
43
|
gigabyte: () => gigabyte,
|
|
44
44
|
incrementUsageTotals: () => incrementUsageTotals,
|
|
45
45
|
infinite: () => infinite,
|
|
46
|
+
isDuplicateKey: () => isDuplicateKey,
|
|
46
47
|
isInfinite: () => isInfinite,
|
|
47
48
|
megabyte: () => megabyte,
|
|
48
49
|
nanoid: () => nanoid,
|
|
49
50
|
percentage: () => percentage,
|
|
51
|
+
recordAction: () => recordAction,
|
|
50
52
|
reducers: () => reducers,
|
|
51
53
|
regex: () => regex,
|
|
52
54
|
shareUrls: () => shareUrls,
|
|
@@ -390,6 +392,30 @@ var incrementUsageTotals = async ({ controller, usageId, $inc, session }) => {
|
|
|
390
392
|
query: { id: usageId }
|
|
391
393
|
});
|
|
392
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 }) => {
|
|
397
|
+
if (!controller || !organization || !usageId || !type || !key) return;
|
|
398
|
+
const amount = Number(units);
|
|
399
|
+
if (!Number.isFinite(amount) || amount === 0) return;
|
|
400
|
+
try {
|
|
401
|
+
await controller.create({
|
|
402
|
+
collection: "action",
|
|
403
|
+
data: {
|
|
404
|
+
organization,
|
|
405
|
+
usage: usageId,
|
|
406
|
+
type,
|
|
407
|
+
units: amount,
|
|
408
|
+
key,
|
|
409
|
+
meta,
|
|
410
|
+
...traceId && { traceId }
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
} catch (error) {
|
|
414
|
+
if (isDuplicateKey(error)) return;
|
|
415
|
+
throw error;
|
|
416
|
+
}
|
|
417
|
+
await incrementUsageTotals({ controller, usageId, $inc: { "totals.actions": amount } });
|
|
418
|
+
};
|
|
393
419
|
var percentage = (value1, value2, decimals = 1) => ((value1 || 0) / (value2 || 0) * 100 || 0).toFixed(decimals);
|
|
394
420
|
var reducers = {
|
|
395
421
|
fields: (data2 = [], callback = () => ({})) => data2.reduce(
|
|
@@ -483,10 +509,12 @@ var currency = (val) => (0, import_currency_codes.code)(val);
|
|
|
483
509
|
gigabyte,
|
|
484
510
|
incrementUsageTotals,
|
|
485
511
|
infinite,
|
|
512
|
+
isDuplicateKey,
|
|
486
513
|
isInfinite,
|
|
487
514
|
megabyte,
|
|
488
515
|
nanoid,
|
|
489
516
|
percentage,
|
|
517
|
+
recordAction,
|
|
490
518
|
reducers,
|
|
491
519
|
regex,
|
|
492
520
|
shareUrls,
|
package/dist/index.d.cts
CHANGED
|
@@ -379,6 +379,63 @@ const incrementUsageTotals = async ({ controller, usageId, $inc, session }) => {
|
|
|
379
379
|
|
|
380
380
|
};
|
|
381
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). The unique
|
|
390
|
+
// `key` makes recording idempotent: a duplicate (the segment realtime path AND the
|
|
391
|
+
// sweep firing for the same membership, or a job retry) hits the unique index and
|
|
392
|
+
// is skipped, so it can never double-count. On a genuinely new row we bump the
|
|
393
|
+
// display cache `usage.totals.actions` by the same units; the billed number always
|
|
394
|
+
// comes from summing the ledger, so a missed cache $inc is only cosmetic (it
|
|
395
|
+
// self-heals at changeover). No transaction needed — the unique index is the
|
|
396
|
+
// atomic dedup.
|
|
397
|
+
// key deterministic per source event, e.g. 'segment.join:<contact>:<segment>'
|
|
398
|
+
// units billable units (default 1; N for batch, e.g. accepted entries)
|
|
399
|
+
// meta attribution ids { campaign, page, submission, segment, workflow, step, ... }
|
|
400
|
+
const recordAction = async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId }) => {
|
|
401
|
+
|
|
402
|
+
if( ! controller || ! organization || ! usageId || ! type || ! key ) return;
|
|
403
|
+
|
|
404
|
+
const amount = Number( units );
|
|
405
|
+
|
|
406
|
+
if( ! Number.isFinite( amount ) || amount === 0 ) return;
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
|
|
410
|
+
await controller.create({
|
|
411
|
+
collection : 'action',
|
|
412
|
+
data : {
|
|
413
|
+
organization,
|
|
414
|
+
usage : usageId,
|
|
415
|
+
type,
|
|
416
|
+
units : amount,
|
|
417
|
+
key,
|
|
418
|
+
meta,
|
|
419
|
+
...( traceId && { traceId })
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
} catch ( error ) {
|
|
424
|
+
|
|
425
|
+
// Duplicate key = this exact action was already recorded — skip silently.
|
|
426
|
+
// This is the dedup that makes billing drift-proof. Re-throw anything else.
|
|
427
|
+
if( isDuplicateKey( error ) ) return;
|
|
428
|
+
|
|
429
|
+
throw error;
|
|
430
|
+
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// New row only — keep the display cache in step. Billing sums the ledger, so
|
|
434
|
+
// this is cosmetic and self-heals at changeover if it ever fails.
|
|
435
|
+
await incrementUsageTotals({ controller, usageId, $inc : { 'totals.actions' : amount } });
|
|
436
|
+
|
|
437
|
+
};
|
|
438
|
+
|
|
382
439
|
const percentage = ( value1, value2, decimals = 1 ) => ( ( ( ( value1 || 0 ) / ( value2 || 0 ) ) * 100 ) || 0 ).toFixed( decimals );
|
|
383
440
|
|
|
384
441
|
const reducers = {
|
|
@@ -477,4 +534,4 @@ const currencies = data.map( ( item ) => ({
|
|
|
477
534
|
|
|
478
535
|
const currency = ( val ) => code( val );
|
|
479
536
|
|
|
480
|
-
export { bytesToGB, bytesToMB, capitalize, constants, currencies, currency, expiredPaymentMethod, formatCurrency, formatDateString, formatNumber, getPlanFeature, gigabyte, incrementUsageTotals, infinite, isInfinite, megabyte, nanoid, percentage, reducers, regex, shareUrls, urlRoot };
|
|
537
|
+
export { bytesToGB, bytesToMB, capitalize, constants, currencies, currency, expiredPaymentMethod, formatCurrency, formatDateString, formatNumber, getPlanFeature, gigabyte, incrementUsageTotals, infinite, isDuplicateKey, isInfinite, megabyte, nanoid, percentage, recordAction, reducers, regex, shareUrls, urlRoot };
|
package/dist/index.d.ts
CHANGED
|
@@ -379,6 +379,63 @@ const incrementUsageTotals = async ({ controller, usageId, $inc, session }) => {
|
|
|
379
379
|
|
|
380
380
|
};
|
|
381
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). The unique
|
|
390
|
+
// `key` makes recording idempotent: a duplicate (the segment realtime path AND the
|
|
391
|
+
// sweep firing for the same membership, or a job retry) hits the unique index and
|
|
392
|
+
// is skipped, so it can never double-count. On a genuinely new row we bump the
|
|
393
|
+
// display cache `usage.totals.actions` by the same units; the billed number always
|
|
394
|
+
// comes from summing the ledger, so a missed cache $inc is only cosmetic (it
|
|
395
|
+
// self-heals at changeover). No transaction needed — the unique index is the
|
|
396
|
+
// atomic dedup.
|
|
397
|
+
// key deterministic per source event, e.g. 'segment.join:<contact>:<segment>'
|
|
398
|
+
// units billable units (default 1; N for batch, e.g. accepted entries)
|
|
399
|
+
// meta attribution ids { campaign, page, submission, segment, workflow, step, ... }
|
|
400
|
+
const recordAction = async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId }) => {
|
|
401
|
+
|
|
402
|
+
if( ! controller || ! organization || ! usageId || ! type || ! key ) return;
|
|
403
|
+
|
|
404
|
+
const amount = Number( units );
|
|
405
|
+
|
|
406
|
+
if( ! Number.isFinite( amount ) || amount === 0 ) return;
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
|
|
410
|
+
await controller.create({
|
|
411
|
+
collection : 'action',
|
|
412
|
+
data : {
|
|
413
|
+
organization,
|
|
414
|
+
usage : usageId,
|
|
415
|
+
type,
|
|
416
|
+
units : amount,
|
|
417
|
+
key,
|
|
418
|
+
meta,
|
|
419
|
+
...( traceId && { traceId })
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
} catch ( error ) {
|
|
424
|
+
|
|
425
|
+
// Duplicate key = this exact action was already recorded — skip silently.
|
|
426
|
+
// This is the dedup that makes billing drift-proof. Re-throw anything else.
|
|
427
|
+
if( isDuplicateKey( error ) ) return;
|
|
428
|
+
|
|
429
|
+
throw error;
|
|
430
|
+
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// New row only — keep the display cache in step. Billing sums the ledger, so
|
|
434
|
+
// this is cosmetic and self-heals at changeover if it ever fails.
|
|
435
|
+
await incrementUsageTotals({ controller, usageId, $inc : { 'totals.actions' : amount } });
|
|
436
|
+
|
|
437
|
+
};
|
|
438
|
+
|
|
382
439
|
const percentage = ( value1, value2, decimals = 1 ) => ( ( ( ( value1 || 0 ) / ( value2 || 0 ) ) * 100 ) || 0 ).toFixed( decimals );
|
|
383
440
|
|
|
384
441
|
const reducers = {
|
|
@@ -477,4 +534,4 @@ const currencies = data.map( ( item ) => ({
|
|
|
477
534
|
|
|
478
535
|
const currency = ( val ) => code( val );
|
|
479
536
|
|
|
480
|
-
export { bytesToGB, bytesToMB, capitalize, constants, currencies, currency, expiredPaymentMethod, formatCurrency, formatDateString, formatNumber, getPlanFeature, gigabyte, incrementUsageTotals, infinite, isInfinite, megabyte, nanoid, percentage, reducers, regex, shareUrls, urlRoot };
|
|
537
|
+
export { bytesToGB, bytesToMB, capitalize, constants, currencies, currency, expiredPaymentMethod, formatCurrency, formatDateString, formatNumber, getPlanFeature, gigabyte, incrementUsageTotals, infinite, isDuplicateKey, isInfinite, megabyte, nanoid, percentage, recordAction, reducers, regex, shareUrls, urlRoot };
|
package/dist/index.js
CHANGED
|
@@ -336,6 +336,30 @@ var incrementUsageTotals = async ({ controller, usageId, $inc, session }) => {
|
|
|
336
336
|
query: { id: usageId }
|
|
337
337
|
});
|
|
338
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 }) => {
|
|
341
|
+
if (!controller || !organization || !usageId || !type || !key) return;
|
|
342
|
+
const amount = Number(units);
|
|
343
|
+
if (!Number.isFinite(amount) || amount === 0) return;
|
|
344
|
+
try {
|
|
345
|
+
await controller.create({
|
|
346
|
+
collection: "action",
|
|
347
|
+
data: {
|
|
348
|
+
organization,
|
|
349
|
+
usage: usageId,
|
|
350
|
+
type,
|
|
351
|
+
units: amount,
|
|
352
|
+
key,
|
|
353
|
+
meta,
|
|
354
|
+
...traceId && { traceId }
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
} catch (error) {
|
|
358
|
+
if (isDuplicateKey(error)) return;
|
|
359
|
+
throw error;
|
|
360
|
+
}
|
|
361
|
+
await incrementUsageTotals({ controller, usageId, $inc: { "totals.actions": amount } });
|
|
362
|
+
};
|
|
339
363
|
var percentage = (value1, value2, decimals = 1) => ((value1 || 0) / (value2 || 0) * 100 || 0).toFixed(decimals);
|
|
340
364
|
var reducers = {
|
|
341
365
|
fields: (data2 = [], callback = () => ({})) => data2.reduce(
|
|
@@ -428,10 +452,12 @@ export {
|
|
|
428
452
|
gigabyte,
|
|
429
453
|
incrementUsageTotals,
|
|
430
454
|
infinite,
|
|
455
|
+
isDuplicateKey,
|
|
431
456
|
isInfinite,
|
|
432
457
|
megabyte,
|
|
433
458
|
nanoid,
|
|
434
459
|
percentage,
|
|
460
|
+
recordAction,
|
|
435
461
|
reducers,
|
|
436
462
|
regex,
|
|
437
463
|
shareUrls,
|
package/package.json
CHANGED
|
@@ -17,9 +17,6 @@
|
|
|
17
17
|
"tsup": "8.5.1",
|
|
18
18
|
"typescript": "5.9.3"
|
|
19
19
|
},
|
|
20
|
-
"overrides": {
|
|
21
|
-
"esbuild": "0.28.1"
|
|
22
|
-
},
|
|
23
20
|
"peerDependencies": {
|
|
24
21
|
"@node-oauth/oauth2-server": "^5.3.0"
|
|
25
22
|
},
|
|
@@ -160,5 +157,5 @@
|
|
|
160
157
|
"build": "tsup && npm publish"
|
|
161
158
|
},
|
|
162
159
|
"types": "dist/index.d.ts",
|
|
163
|
-
"version": "0.0.
|
|
160
|
+
"version": "0.0.62"
|
|
164
161
|
}
|