@drawbridge/drawbridge-utils 0.0.67 → 0.0.69

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
@@ -157,45 +157,6 @@ var incrementUsageTotals = async ({ controller, usageId, $inc, session }) => {
157
157
  });
158
158
  };
159
159
  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) || "");
160
- var recordAction = async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId, session }) => {
161
- if (!controller || !organization || !usageId || !type || !key) return;
162
- const amount = Number(units);
163
- if (!Number.isFinite(amount) || amount === 0) return;
164
- let inserted = false;
165
- try {
166
- const { lastErrorObject } = await controller.update({
167
- collection: "action",
168
- data: {
169
- $setOnInsert: {
170
- organization,
171
- usage: usageId,
172
- type,
173
- units: amount,
174
- key,
175
- meta,
176
- ...traceId && { traceId }
177
- }
178
- },
179
- options: {
180
- bypassDocumentValidation: true,
181
- includeResultMetadata: true,
182
- upsert: true,
183
- ...session && { session }
184
- },
185
- query: {
186
- key,
187
- usage: usageId
188
- }
189
- });
190
- inserted = Boolean(lastErrorObject == null ? void 0 : lastErrorObject.upserted);
191
- } catch (error) {
192
- if (isDuplicateKey(error) && !session) return;
193
- throw error;
194
- }
195
- if (inserted) {
196
- await incrementUsageTotals({ controller, usageId, $inc: { "totals.actions": amount }, session });
197
- }
198
- };
199
160
 
200
161
  // lib/billing.js
201
162
  var import_drawbridge_telemetry2 = require("@drawbridge/drawbridge-telemetry");
@@ -346,9 +307,52 @@ var grant = {
346
307
  admin: ({ db, user, amount }) => credit({ db, user, amount, type: "ai", category: "promotional", source: "admin" })
347
308
  };
348
309
  var action = {
349
- // Append a billable action (idempotent on { usage, key }) + bump the
350
- // usage.totals.actions display cache. The proven recordAction primitive.
351
- record: recordAction,
310
+ // Append a billable action to the ledger (idempotent UPSERT on { usage, key }:
311
+ // a duplicate event the segment realtime path AND the sweep, or a retry —
312
+ // matches the existing row and no-ops, so it's safe inside a caller's
313
+ // transaction), then bump the usage.totals.actions display cache ONLY on a
314
+ // genuinely new row. Pass `session` to join the caller's transaction.
315
+ // key deterministic per source event, e.g. 'segment.join:<contact>:<segment>'
316
+ // units billable units (default 1; N for batch, e.g. accepted entries)
317
+ record: async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId, session }) => {
318
+ if (!controller || !organization || !usageId || !type || !key) return;
319
+ const amount = Number(units);
320
+ if (!Number.isFinite(amount) || amount === 0) return;
321
+ let inserted = false;
322
+ try {
323
+ const { lastErrorObject } = await controller.update({
324
+ collection: "action",
325
+ data: {
326
+ $setOnInsert: {
327
+ organization,
328
+ usage: usageId,
329
+ type,
330
+ units: amount,
331
+ key,
332
+ meta,
333
+ ...traceId && { traceId }
334
+ }
335
+ },
336
+ options: {
337
+ bypassDocumentValidation: true,
338
+ includeResultMetadata: true,
339
+ upsert: true,
340
+ ...session && { session }
341
+ },
342
+ query: {
343
+ key,
344
+ usage: usageId
345
+ }
346
+ });
347
+ inserted = Boolean(lastErrorObject == null ? void 0 : lastErrorObject.upserted);
348
+ } catch (error) {
349
+ if (isDuplicateKey(error) && !session) return;
350
+ throw error;
351
+ }
352
+ if (inserted) {
353
+ await incrementUsageTotals({ controller, usageId, $inc: { "totals.actions": amount }, session });
354
+ }
355
+ },
352
356
  // Authoritative per-period total to bill — sum(units) over the period's ledger
353
357
  // rows. The Stripe meter emit (in the Stripe/sync layer) reports this number.
354
358
  sum: async ({ controller, usageId }) => {
@@ -1,5 +1,5 @@
1
1
  import { credit, debit } from './transactions.cjs';
2
- import { recordAction } from './usage.cjs';
2
+ import { isDuplicateKey, incrementUsageTotals } from './usage.cjs';
3
3
  import { createLogger } from '@drawbridge/drawbridge-telemetry';
4
4
 
5
5
  // Billing — the single source of truth for what Drawbridge charges, and the
@@ -273,9 +273,70 @@ const grant = {
273
273
  // to the Stripe meter. See the action-ledger design.
274
274
  const action = {
275
275
 
276
- // Append a billable action (idempotent on { usage, key }) + bump the
277
- // usage.totals.actions display cache. The proven recordAction primitive.
278
- record : recordAction,
276
+ // Append a billable action to the ledger (idempotent UPSERT on { usage, key }:
277
+ // a duplicate event the segment realtime path AND the sweep, or a retry —
278
+ // matches the existing row and no-ops, so it's safe inside a caller's
279
+ // transaction), then bump the usage.totals.actions display cache ONLY on a
280
+ // genuinely new row. Pass `session` to join the caller's transaction.
281
+ // key deterministic per source event, e.g. 'segment.join:<contact>:<segment>'
282
+ // units billable units (default 1; N for batch, e.g. accepted entries)
283
+ record : async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId, session }) => {
284
+
285
+ if( ! controller || ! organization || ! usageId || ! type || ! key ) return;
286
+
287
+ const amount = Number( units );
288
+
289
+ if( ! Number.isFinite( amount ) || amount === 0 ) return;
290
+
291
+ let inserted = false;
292
+
293
+ try {
294
+
295
+ const { lastErrorObject } = await controller.update({
296
+ collection : 'action',
297
+ data : {
298
+ $setOnInsert : {
299
+ organization,
300
+ usage : usageId,
301
+ type,
302
+ units : amount,
303
+ key,
304
+ meta,
305
+ ...( traceId && { traceId })
306
+ }
307
+ },
308
+ options : {
309
+ bypassDocumentValidation : true,
310
+ includeResultMetadata : true,
311
+ upsert : true,
312
+ ...( session && { session })
313
+ },
314
+ query : {
315
+ key,
316
+ usage : usageId
317
+ }
318
+ });
319
+
320
+ inserted = Boolean( lastErrorObject?.upserted );
321
+
322
+ } catch ( error ) {
323
+
324
+ // A concurrent upsert of the same { usage, key } can still race to E11000.
325
+ // Standalone: the other writer recorded it — skip. Inside a transaction:
326
+ // the txn is already aborted, so re-throw to let the caller retry the unit.
327
+ if( isDuplicateKey( error ) && ! session ) return;
328
+
329
+ throw error;
330
+
331
+ }
332
+
333
+ if( inserted ){
334
+
335
+ await incrementUsageTotals({ controller, usageId, $inc : { 'totals.actions' : amount }, session });
336
+
337
+ }
338
+
339
+ },
279
340
 
280
341
  // Authoritative per-period total to bill — sum(units) over the period's ledger
281
342
  // rows. The Stripe meter emit (in the Stripe/sync layer) reports this number.
package/dist/billing.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { credit, debit } from './transactions.js';
2
- import { recordAction } from './usage.js';
2
+ import { isDuplicateKey, incrementUsageTotals } from './usage.js';
3
3
  import { createLogger } from '@drawbridge/drawbridge-telemetry';
4
4
 
5
5
  // Billing — the single source of truth for what Drawbridge charges, and the
@@ -273,9 +273,70 @@ const grant = {
273
273
  // to the Stripe meter. See the action-ledger design.
274
274
  const action = {
275
275
 
276
- // Append a billable action (idempotent on { usage, key }) + bump the
277
- // usage.totals.actions display cache. The proven recordAction primitive.
278
- record : recordAction,
276
+ // Append a billable action to the ledger (idempotent UPSERT on { usage, key }:
277
+ // a duplicate event the segment realtime path AND the sweep, or a retry —
278
+ // matches the existing row and no-ops, so it's safe inside a caller's
279
+ // transaction), then bump the usage.totals.actions display cache ONLY on a
280
+ // genuinely new row. Pass `session` to join the caller's transaction.
281
+ // key deterministic per source event, e.g. 'segment.join:<contact>:<segment>'
282
+ // units billable units (default 1; N for batch, e.g. accepted entries)
283
+ record : async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId, session }) => {
284
+
285
+ if( ! controller || ! organization || ! usageId || ! type || ! key ) return;
286
+
287
+ const amount = Number( units );
288
+
289
+ if( ! Number.isFinite( amount ) || amount === 0 ) return;
290
+
291
+ let inserted = false;
292
+
293
+ try {
294
+
295
+ const { lastErrorObject } = await controller.update({
296
+ collection : 'action',
297
+ data : {
298
+ $setOnInsert : {
299
+ organization,
300
+ usage : usageId,
301
+ type,
302
+ units : amount,
303
+ key,
304
+ meta,
305
+ ...( traceId && { traceId })
306
+ }
307
+ },
308
+ options : {
309
+ bypassDocumentValidation : true,
310
+ includeResultMetadata : true,
311
+ upsert : true,
312
+ ...( session && { session })
313
+ },
314
+ query : {
315
+ key,
316
+ usage : usageId
317
+ }
318
+ });
319
+
320
+ inserted = Boolean( lastErrorObject?.upserted );
321
+
322
+ } catch ( error ) {
323
+
324
+ // A concurrent upsert of the same { usage, key } can still race to E11000.
325
+ // Standalone: the other writer recorded it — skip. Inside a transaction:
326
+ // the txn is already aborted, so re-throw to let the caller retry the unit.
327
+ if( isDuplicateKey( error ) && ! session ) return;
328
+
329
+ throw error;
330
+
331
+ }
332
+
333
+ if( inserted ){
334
+
335
+ await incrementUsageTotals({ controller, usageId, $inc : { 'totals.actions' : amount }, session });
336
+
337
+ }
338
+
339
+ },
279
340
 
280
341
  // Authoritative per-period total to bill — sum(units) over the period's ledger
281
342
  // rows. The Stripe meter emit (in the Stripe/sync layer) reports this number.
package/dist/billing.js CHANGED
@@ -128,45 +128,6 @@ var incrementUsageTotals = async ({ controller, usageId, $inc, session }) => {
128
128
  });
129
129
  };
130
130
  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) || "");
131
- var recordAction = async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId, session }) => {
132
- if (!controller || !organization || !usageId || !type || !key) return;
133
- const amount = Number(units);
134
- if (!Number.isFinite(amount) || amount === 0) return;
135
- let inserted = false;
136
- try {
137
- const { lastErrorObject } = await controller.update({
138
- collection: "action",
139
- data: {
140
- $setOnInsert: {
141
- organization,
142
- usage: usageId,
143
- type,
144
- units: amount,
145
- key,
146
- meta,
147
- ...traceId && { traceId }
148
- }
149
- },
150
- options: {
151
- bypassDocumentValidation: true,
152
- includeResultMetadata: true,
153
- upsert: true,
154
- ...session && { session }
155
- },
156
- query: {
157
- key,
158
- usage: usageId
159
- }
160
- });
161
- inserted = Boolean(lastErrorObject == null ? void 0 : lastErrorObject.upserted);
162
- } catch (error) {
163
- if (isDuplicateKey(error) && !session) return;
164
- throw error;
165
- }
166
- if (inserted) {
167
- await incrementUsageTotals({ controller, usageId, $inc: { "totals.actions": amount }, session });
168
- }
169
- };
170
131
 
171
132
  // lib/billing.js
172
133
  import { createLogger } from "@drawbridge/drawbridge-telemetry";
@@ -317,9 +278,52 @@ var grant = {
317
278
  admin: ({ db, user, amount }) => credit({ db, user, amount, type: "ai", category: "promotional", source: "admin" })
318
279
  };
319
280
  var action = {
320
- // Append a billable action (idempotent on { usage, key }) + bump the
321
- // usage.totals.actions display cache. The proven recordAction primitive.
322
- record: recordAction,
281
+ // Append a billable action to the ledger (idempotent UPSERT on { usage, key }:
282
+ // a duplicate event the segment realtime path AND the sweep, or a retry —
283
+ // matches the existing row and no-ops, so it's safe inside a caller's
284
+ // transaction), then bump the usage.totals.actions display cache ONLY on a
285
+ // genuinely new row. Pass `session` to join the caller's transaction.
286
+ // key deterministic per source event, e.g. 'segment.join:<contact>:<segment>'
287
+ // units billable units (default 1; N for batch, e.g. accepted entries)
288
+ record: async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId, session }) => {
289
+ if (!controller || !organization || !usageId || !type || !key) return;
290
+ const amount = Number(units);
291
+ if (!Number.isFinite(amount) || amount === 0) return;
292
+ let inserted = false;
293
+ try {
294
+ const { lastErrorObject } = await controller.update({
295
+ collection: "action",
296
+ data: {
297
+ $setOnInsert: {
298
+ organization,
299
+ usage: usageId,
300
+ type,
301
+ units: amount,
302
+ key,
303
+ meta,
304
+ ...traceId && { traceId }
305
+ }
306
+ },
307
+ options: {
308
+ bypassDocumentValidation: true,
309
+ includeResultMetadata: true,
310
+ upsert: true,
311
+ ...session && { session }
312
+ },
313
+ query: {
314
+ key,
315
+ usage: usageId
316
+ }
317
+ });
318
+ inserted = Boolean(lastErrorObject == null ? void 0 : lastErrorObject.upserted);
319
+ } catch (error) {
320
+ if (isDuplicateKey(error) && !session) return;
321
+ throw error;
322
+ }
323
+ if (inserted) {
324
+ await incrementUsageTotals({ controller, usageId, $inc: { "totals.actions": amount }, session });
325
+ }
326
+ },
323
327
  // Authoritative per-period total to bill — sum(units) over the period's ledger
324
328
  // rows. The Stripe meter emit (in the Stripe/sync layer) reports this number.
325
329
  sum: async ({ controller, usageId }) => {
package/dist/usage.cjs CHANGED
@@ -20,8 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  var usage_exports = {};
21
21
  __export(usage_exports, {
22
22
  incrementUsageTotals: () => incrementUsageTotals,
23
- isDuplicateKey: () => isDuplicateKey,
24
- recordAction: () => recordAction
23
+ isDuplicateKey: () => isDuplicateKey
25
24
  });
26
25
  module.exports = __toCommonJS(usage_exports);
27
26
  var incrementUsageTotals = async ({ controller, usageId, $inc, session }) => {
@@ -44,48 +43,8 @@ var incrementUsageTotals = async ({ controller, usageId, $inc, session }) => {
44
43
  });
45
44
  };
46
45
  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) || "");
47
- var recordAction = async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId, session }) => {
48
- if (!controller || !organization || !usageId || !type || !key) return;
49
- const amount = Number(units);
50
- if (!Number.isFinite(amount) || amount === 0) return;
51
- let inserted = false;
52
- try {
53
- const { lastErrorObject } = await controller.update({
54
- collection: "action",
55
- data: {
56
- $setOnInsert: {
57
- organization,
58
- usage: usageId,
59
- type,
60
- units: amount,
61
- key,
62
- meta,
63
- ...traceId && { traceId }
64
- }
65
- },
66
- options: {
67
- bypassDocumentValidation: true,
68
- includeResultMetadata: true,
69
- upsert: true,
70
- ...session && { session }
71
- },
72
- query: {
73
- key,
74
- usage: usageId
75
- }
76
- });
77
- inserted = Boolean(lastErrorObject == null ? void 0 : lastErrorObject.upserted);
78
- } catch (error) {
79
- if (isDuplicateKey(error) && !session) return;
80
- throw error;
81
- }
82
- if (inserted) {
83
- await incrementUsageTotals({ controller, usageId, $inc: { "totals.actions": amount }, session });
84
- }
85
- };
86
46
  // Annotate the CommonJS export names for ESM import in node:
87
47
  0 && (module.exports = {
88
48
  incrementUsageTotals,
89
- isDuplicateKey,
90
- recordAction
49
+ isDuplicateKey
91
50
  });
package/dist/usage.d.cts CHANGED
@@ -1,9 +1,8 @@
1
- // Usage helpers — the per-org `usage` doc totals and the append-only `action`
2
- // ledger. These are the low-level usage primitives; billing.action.* (lib/
3
- // billing.js) is the public surface. Kept separate from billing so the generic
4
- // usage-totals $inc (used for non-billing totals like revenue/submissions/
5
- // entries too) doesn't live under the billing namespace. Re-exported from
6
- // index.js for backward compatibility with existing import sites.
1
+ // Usage helpers — the per-org `usage` doc totals $inc and a duplicate-key guard.
2
+ // Generic, non-billing primitives (incrementUsageTotals is used for revenue /
3
+ // submissions / entries totals too), so they live here rather than under the
4
+ // billing namespace; billing.action.record (lib/billing.js) uses both to write
5
+ // the action ledger. Re-exported from index.js for existing import sites.
7
6
 
8
7
  // Centralized $inc helper for the per-org usage doc. Replaces inline
9
8
  // $inc-with-bypass-validation patterns in drawbridge-api + drawbridge-sync.
@@ -49,81 +48,4 @@ const isDuplicateKey = ( error ) =>
49
48
  || error?.code === '11000'
50
49
  || /E11000|duplicate key/i.test( error?.message || '' );
51
50
 
52
- // Record a billable action as a row in the append-only `action` ledger — the
53
- // source of truth for usage billing (summed per period at changeover).
54
- //
55
- // Idempotent via an UPSERT on the unique { usage, key }: a duplicate event (the
56
- // segment realtime path AND the sweep firing for the same membership, or a retry)
57
- // matches the existing row and no-ops — no duplicate-key error — so this is safe
58
- // to call INSIDE a caller's transaction (a thrown E11000 would abort the whole
59
- // transaction). The display cache `usage.totals.actions` is bumped ONLY when the
60
- // upsert actually inserts a new row, so a duplicate never double-counts.
61
- //
62
- // Pass `session` to make the ledger row + cache bump atomic with the caller's own
63
- // writes (e.g. the membership writes in a segment-sweep chunk, or the submission
64
- // docs) — all-or-nothing. Without a session it runs standalone.
65
- // key deterministic per source event, e.g. 'segment.join:<contact>:<segment>'
66
- // units billable units (default 1; N for batch, e.g. accepted entries)
67
- // meta attribution ids { campaign, page, submission, segment, workflow, step, ... }
68
- const recordAction = async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId, session }) => {
69
-
70
- if( ! controller || ! organization || ! usageId || ! type || ! key ) return;
71
-
72
- const amount = Number( units );
73
-
74
- if( ! Number.isFinite( amount ) || amount === 0 ) return;
75
-
76
- let inserted = false;
77
-
78
- try {
79
-
80
- const { lastErrorObject } = await controller.update({
81
- collection : 'action',
82
- data : {
83
- $setOnInsert : {
84
- organization,
85
- usage : usageId,
86
- type,
87
- units : amount,
88
- key,
89
- meta,
90
- ...( traceId && { traceId })
91
- }
92
- },
93
- options : {
94
- bypassDocumentValidation : true,
95
- includeResultMetadata : true,
96
- upsert : true,
97
- ...( session && { session })
98
- },
99
- query : {
100
- key,
101
- usage : usageId
102
- }
103
- });
104
-
105
- inserted = Boolean( lastErrorObject?.upserted );
106
-
107
- } catch ( error ) {
108
-
109
- // A concurrent upsert of the same { usage, key } can still race to E11000.
110
- // Standalone: the other writer recorded it — skip. Inside a transaction:
111
- // the txn is already aborted, so re-throw to let the caller retry the whole
112
- // unit (which then sees the row and no-ops).
113
- if( isDuplicateKey( error ) && ! session ) return;
114
-
115
- throw error;
116
-
117
- }
118
-
119
- // Only a genuinely new row bumps the display cache (a matched duplicate must
120
- // not double-count). Joined to the caller's transaction when a session is given.
121
- if( inserted ){
122
-
123
- await incrementUsageTotals({ controller, usageId, $inc : { 'totals.actions' : amount }, session });
124
-
125
- }
126
-
127
- };
128
-
129
- export { incrementUsageTotals, isDuplicateKey, recordAction };
51
+ export { incrementUsageTotals, isDuplicateKey };
package/dist/usage.d.ts CHANGED
@@ -1,9 +1,8 @@
1
- // Usage helpers — the per-org `usage` doc totals and the append-only `action`
2
- // ledger. These are the low-level usage primitives; billing.action.* (lib/
3
- // billing.js) is the public surface. Kept separate from billing so the generic
4
- // usage-totals $inc (used for non-billing totals like revenue/submissions/
5
- // entries too) doesn't live under the billing namespace. Re-exported from
6
- // index.js for backward compatibility with existing import sites.
1
+ // Usage helpers — the per-org `usage` doc totals $inc and a duplicate-key guard.
2
+ // Generic, non-billing primitives (incrementUsageTotals is used for revenue /
3
+ // submissions / entries totals too), so they live here rather than under the
4
+ // billing namespace; billing.action.record (lib/billing.js) uses both to write
5
+ // the action ledger. Re-exported from index.js for existing import sites.
7
6
 
8
7
  // Centralized $inc helper for the per-org usage doc. Replaces inline
9
8
  // $inc-with-bypass-validation patterns in drawbridge-api + drawbridge-sync.
@@ -49,81 +48,4 @@ const isDuplicateKey = ( error ) =>
49
48
  || error?.code === '11000'
50
49
  || /E11000|duplicate key/i.test( error?.message || '' );
51
50
 
52
- // Record a billable action as a row in the append-only `action` ledger — the
53
- // source of truth for usage billing (summed per period at changeover).
54
- //
55
- // Idempotent via an UPSERT on the unique { usage, key }: a duplicate event (the
56
- // segment realtime path AND the sweep firing for the same membership, or a retry)
57
- // matches the existing row and no-ops — no duplicate-key error — so this is safe
58
- // to call INSIDE a caller's transaction (a thrown E11000 would abort the whole
59
- // transaction). The display cache `usage.totals.actions` is bumped ONLY when the
60
- // upsert actually inserts a new row, so a duplicate never double-counts.
61
- //
62
- // Pass `session` to make the ledger row + cache bump atomic with the caller's own
63
- // writes (e.g. the membership writes in a segment-sweep chunk, or the submission
64
- // docs) — all-or-nothing. Without a session it runs standalone.
65
- // key deterministic per source event, e.g. 'segment.join:<contact>:<segment>'
66
- // units billable units (default 1; N for batch, e.g. accepted entries)
67
- // meta attribution ids { campaign, page, submission, segment, workflow, step, ... }
68
- const recordAction = async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId, session }) => {
69
-
70
- if( ! controller || ! organization || ! usageId || ! type || ! key ) return;
71
-
72
- const amount = Number( units );
73
-
74
- if( ! Number.isFinite( amount ) || amount === 0 ) return;
75
-
76
- let inserted = false;
77
-
78
- try {
79
-
80
- const { lastErrorObject } = await controller.update({
81
- collection : 'action',
82
- data : {
83
- $setOnInsert : {
84
- organization,
85
- usage : usageId,
86
- type,
87
- units : amount,
88
- key,
89
- meta,
90
- ...( traceId && { traceId })
91
- }
92
- },
93
- options : {
94
- bypassDocumentValidation : true,
95
- includeResultMetadata : true,
96
- upsert : true,
97
- ...( session && { session })
98
- },
99
- query : {
100
- key,
101
- usage : usageId
102
- }
103
- });
104
-
105
- inserted = Boolean( lastErrorObject?.upserted );
106
-
107
- } catch ( error ) {
108
-
109
- // A concurrent upsert of the same { usage, key } can still race to E11000.
110
- // Standalone: the other writer recorded it — skip. Inside a transaction:
111
- // the txn is already aborted, so re-throw to let the caller retry the whole
112
- // unit (which then sees the row and no-ops).
113
- if( isDuplicateKey( error ) && ! session ) return;
114
-
115
- throw error;
116
-
117
- }
118
-
119
- // Only a genuinely new row bumps the display cache (a matched duplicate must
120
- // not double-count). Joined to the caller's transaction when a session is given.
121
- if( inserted ){
122
-
123
- await incrementUsageTotals({ controller, usageId, $inc : { 'totals.actions' : amount }, session });
124
-
125
- }
126
-
127
- };
128
-
129
- export { incrementUsageTotals, isDuplicateKey, recordAction };
51
+ export { incrementUsageTotals, isDuplicateKey };
package/dist/usage.js CHANGED
@@ -19,47 +19,7 @@ var incrementUsageTotals = async ({ controller, usageId, $inc, session }) => {
19
19
  });
20
20
  };
21
21
  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) || "");
22
- var recordAction = async ({ controller, organization, usageId, type, units = 1, key, meta = {}, traceId, session }) => {
23
- if (!controller || !organization || !usageId || !type || !key) return;
24
- const amount = Number(units);
25
- if (!Number.isFinite(amount) || amount === 0) return;
26
- let inserted = false;
27
- try {
28
- const { lastErrorObject } = await controller.update({
29
- collection: "action",
30
- data: {
31
- $setOnInsert: {
32
- organization,
33
- usage: usageId,
34
- type,
35
- units: amount,
36
- key,
37
- meta,
38
- ...traceId && { traceId }
39
- }
40
- },
41
- options: {
42
- bypassDocumentValidation: true,
43
- includeResultMetadata: true,
44
- upsert: true,
45
- ...session && { session }
46
- },
47
- query: {
48
- key,
49
- usage: usageId
50
- }
51
- });
52
- inserted = Boolean(lastErrorObject == null ? void 0 : lastErrorObject.upserted);
53
- } catch (error) {
54
- if (isDuplicateKey(error) && !session) return;
55
- throw error;
56
- }
57
- if (inserted) {
58
- await incrementUsageTotals({ controller, usageId, $inc: { "totals.actions": amount }, session });
59
- }
60
- };
61
22
  export {
62
23
  incrementUsageTotals,
63
- isDuplicateKey,
64
- recordAction
24
+ isDuplicateKey
65
25
  };