@softure-ai/billing 0.1.5 → 0.1.7
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/CHANGELOG.md +15 -0
- package/README.md +1 -5
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/mailing/reminder-mail.d.ts +8 -2
- package/dist/mailing/reminder-mail.d.ts.map +1 -1
- package/dist/mailing/reminder-mail.js +6 -1
- package/dist/mailing/reminder-mail.js.map +1 -1
- package/dist/next/context.js +2 -2
- package/dist/next/context.js.map +1 -1
- package/module.json +2 -2
- package/package.json +6 -2
- package/src/index.ts +2 -2
- package/src/mailing/reminder-mail.ts +14 -3
- package/src/next/context.ts +2 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Newest first. Each version lists what changed for an app that uses `@softure-ai/billing`. When an app has run a version in
|
|
4
|
+
production, the version gets a line `verified in: <app>@<commit>` ([docs/05](../../docs/05-adoption-playbook.md),
|
|
5
|
+
"Definition of done"). Versions before the first one below are described in their GitHub Releases (`billing@x.y.z`).
|
|
6
|
+
|
|
7
|
+
## 0.1.7
|
|
8
|
+
|
|
9
|
+
- `sendAccessReminders` stops at a refused mail account or a spent quota (mailing's `halted`, counted in
|
|
10
|
+
`retryLater`) and skips a reminder whose send was interrupted long ago (mailing's `uncertain`, counted in `skipped`).
|
|
11
|
+
|
|
12
|
+
## 0.1.6
|
|
13
|
+
|
|
14
|
+
- `module.json` names `mailing` and `ops` as optional dependencies.
|
|
15
|
+
- Adapters and commands use the configured database handle; `@softure-ai/ui` is a peer dependency.
|
package/README.md
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
# @softure-ai/billing
|
|
2
2
|
|
|
3
|
-
**
|
|
4
|
-
and the manual adapter (MO-2); Stripe Checkout with verified webhooks and refunds (MO-3); stored
|
|
5
|
-
invoice requests, recorded manual grants with revoke and an account history (FU-9); reminder mail
|
|
6
|
-
before and after access ends (FU-6) · depends on: core, db, ui, security, auth (mailing optional, for
|
|
7
|
-
`@softure-ai/billing/mailing`)
|
|
3
|
+
**Depends on:** core, db, ui, security, auth (mailing optional, for `@softure-ai/billing/mailing`).
|
|
8
4
|
|
|
9
5
|
Decides whether an account may still write: a trial every account starts with, paid access (dated
|
|
10
6
|
or lifetime) and a read-only state once both end. It replaces FIRE_TRACKER's access logic
|
package/dist/index.js
CHANGED
|
@@ -25,8 +25,8 @@ export const BILLING_RATE_LIMIT_BUCKETS = {
|
|
|
25
25
|
export const billing = defineModule({
|
|
26
26
|
manifest: {
|
|
27
27
|
id: MODULE_ID,
|
|
28
|
-
version: "0.1.
|
|
29
|
-
dependsOn: { security: "^0.1.0", auth: "^0.1.0" },
|
|
28
|
+
version: "0.1.7",
|
|
29
|
+
dependsOn: { security: "^0.1.0", auth: "^0.1.0", mailing: "^0.1.0?", ops: "^0.1.0?" },
|
|
30
30
|
dbSchema: "billing",
|
|
31
31
|
tables: ["entitlements", "payments", "payment_requests", "manual_grants"],
|
|
32
32
|
env: [
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sGAAsG;AACtG,iGAAiG;AACjG,yFAAyF;AACzF,wFAAwF;AACxF,oGAAoG;AACpG,2FAA2F;AAC3F,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAEhE,MAAM,CAAC,MAAM,SAAS,GAAG,SAAS,CAAC;AAEnC;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,iBAAiB,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE;CAC1C,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,YAAY,CAAC;IAClC,QAAQ,EAAE;QACR,EAAE,EAAE,SAAS;QACb,OAAO,EAAE,OAAO;QAChB,SAAS,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -28,11 +28,17 @@ export interface AccessReminderSummary {
|
|
|
28
28
|
readonly due: number;
|
|
29
29
|
/** Mails sent now. */
|
|
30
30
|
readonly sent: number;
|
|
31
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Mails an earlier run sent (or refused), another run is sending right now, or whose send was interrupted
|
|
33
|
+
* so long ago that it may have gone out (mailing's `uncertain`).
|
|
34
|
+
*/
|
|
32
35
|
readonly skipped: number;
|
|
33
36
|
/** Mails refused for good (an invalid address, the provider rejected it). */
|
|
34
37
|
readonly rejected: number;
|
|
35
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Mails the provider could not take now; the next run sends them. A refused account or a spent quota stops the
|
|
40
|
+
* run there: the mails after it are not counted and go out on the next run.
|
|
41
|
+
*/
|
|
36
42
|
readonly retryLater: number;
|
|
37
43
|
}
|
|
38
44
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reminder-mail.d.ts","sourceRoot":"","sources":["../../src/mailing/reminder-mail.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAA0B,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEjF,OAAO,EAAuB,KAAK,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAG9F,gGAAgG;AAChG,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,uCAAuC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,wEAAwE;AACxE,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,MAAM,CAAC,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpG,MAAM,WAAW,0BAA2B,SAAQ,0BAA0B;IAC5E,kGAAkG;IAClG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,qBAAqB;IACpC,wCAAwC;IACxC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,sBAAsB;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB
|
|
1
|
+
{"version":3,"file":"reminder-mail.d.ts","sourceRoot":"","sources":["../../src/mailing/reminder-mail.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAA0B,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEjF,OAAO,EAAuB,KAAK,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAG9F,gGAAgG;AAChG,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,uCAAuC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,wEAAwE;AACxE,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,MAAM,CAAC,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpG,MAAM,WAAW,0BAA2B,SAAQ,0BAA0B;IAC5E,kGAAkG;IAClG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,qBAAqB;IACpC,wCAAwC;IACxC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,sBAAsB;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAeD;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,uBAAuB,GAAG,kBAAkB,CAStH;AAMD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,aAAa,CAAC,SAAS,CAAC,EAAE,OAAO,GAAE,0BAA+B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA0BjJ"}
|
|
@@ -53,10 +53,15 @@ export async function sendAccessReminders(ctx, options = {}) {
|
|
|
53
53
|
const lastDay = formatLastDay(reminder.endsAt, ctx.config.locale, ctx.config.timezone);
|
|
54
54
|
const mail = renderAccessReminderMail(messages, { kind: reminder.kind, lastDay, link });
|
|
55
55
|
const outcome = await deliverOnce(ctx, { scope: getAccessReminderScope(reminder.kind, reminder.userId, reminder.endsAt), mail: { to: reminder.email, ...mail } });
|
|
56
|
-
if (outcome.status === "done" || outcome.status === "in-flight") {
|
|
56
|
+
if (outcome.status === "done" || outcome.status === "in-flight" || outcome.status === "uncertain") {
|
|
57
57
|
counts.skipped += 1;
|
|
58
58
|
continue;
|
|
59
59
|
}
|
|
60
|
+
if (outcome.status === "halted") {
|
|
61
|
+
// The account cannot send (refused key, spent quota): every other mail would fail the same way.
|
|
62
|
+
counts.retryLater += 1;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
60
65
|
if (outcome.status === "sent")
|
|
61
66
|
counts.sent += 1;
|
|
62
67
|
else if (outcome.status === "rejected")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reminder-mail.js","sourceRoot":"","sources":["../../src/mailing/reminder-mail.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,oGAAoG;AACpG,oGAAoG;AACpG,yFAAyF;AACzF,OAAO,EAAE,aAAa,EAAsB,MAAM,kBAAkB,CAAC;AAGrE,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,sBAAsB,EAA2B,MAAM,gBAAgB,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAmC,MAAM,wBAAwB,CAAC;AAC9F,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,gGAAgG;AAChG,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"reminder-mail.js","sourceRoot":"","sources":["../../src/mailing/reminder-mail.ts"],"names":[],"mappings":"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"}
|
package/dist/next/context.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// undeclared `adminRole`) fails on the first one.
|
|
4
4
|
import { systemClock } from "@softure-ai/core";
|
|
5
5
|
import { getSoftureConfig } from "@softure-ai/core/next";
|
|
6
|
-
import {
|
|
6
|
+
import { getConfiguredDatabase } from "@softure-ai/db";
|
|
7
7
|
import { assertAdminRoleDeclared } from "../server/setup.js";
|
|
8
8
|
export async function getBillingContext(config = getSoftureConfig()) {
|
|
9
9
|
assertAdminRoleDeclared(config);
|
|
@@ -11,7 +11,7 @@ export async function getBillingContext(config = getSoftureConfig()) {
|
|
|
11
11
|
// Unreachable for a validated config: the module has a database schema.
|
|
12
12
|
throw new Error("@softure-ai/billing: softure.config.ts has no database; billing needs one");
|
|
13
13
|
}
|
|
14
|
-
const { db } = await
|
|
14
|
+
const { db } = await getConfiguredDatabase(config.database);
|
|
15
15
|
return { db, clock: systemClock, config };
|
|
16
16
|
}
|
|
17
17
|
//# sourceMappingURL=context.js.map
|
package/dist/next/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/next/context.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,6FAA6F;AAC7F,kDAAkD;AAClD,OAAO,EAAE,WAAW,EAAsB,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/next/context.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,6FAA6F;AAC7F,kDAAkD;AAClD,OAAO,EAAE,WAAW,EAAsB,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,SAAwB,gBAAgB,EAAE;IAChF,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC7B,wEAAwE;QACxE,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;IAC/F,CAAC;IACD,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,qBAAqB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC5D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;AAC5C,CAAC"}
|
package/module.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "billing",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"dependsOn": { "security": "^0.1.0", "auth": "^0.1.0" },
|
|
3
|
+
"version": "0.1.7",
|
|
4
|
+
"dependsOn": { "security": "^0.1.0", "auth": "^0.1.0", "mailing": "^0.1.0?", "ops": "^0.1.0?" },
|
|
5
5
|
"dbSchema": "billing",
|
|
6
6
|
"tables": ["entitlements", "payments", "payment_requests", "manual_grants"],
|
|
7
7
|
"env": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softure-ai/billing",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "SOFTURE AI billing module: trial, paid and read-only entitlements from a pure state machine, billing.entitlements apart from auth.users, a write guard for server actions, access badge and notice components, a privacy contributor, reminder mail before access ends over @softure-ai/mailing, manual invoice requests handed to the owner once and expired after a configurable age, grant-plan and revoke-grant ops scripts, and import-entitlements and pin-trials ops scripts for accounts that exist before billing is turned on.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"dist",
|
|
18
|
+
"CHANGELOG.md",
|
|
18
19
|
"src",
|
|
19
20
|
"!src/**/*.test.ts",
|
|
20
21
|
"!src/**/*.test.tsx",
|
|
@@ -66,15 +67,18 @@
|
|
|
66
67
|
"@softure-ai/db": "^0.1.0",
|
|
67
68
|
"@softure-ai/ops": "^0.1.0",
|
|
68
69
|
"@softure-ai/security": "^0.1.0",
|
|
69
|
-
"@softure-ai/ui": "^0.1.0",
|
|
70
70
|
"zod": "^4.6.5"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
73
|
"@softure-ai/mailing": "^0.1.0",
|
|
74
|
+
"@softure-ai/ui": "^0.1.0",
|
|
74
75
|
"drizzle-orm": "^0.45.2",
|
|
75
76
|
"next": "^16.0.0",
|
|
76
77
|
"react": "^19.0.0"
|
|
77
78
|
},
|
|
79
|
+
"devDependencies": {
|
|
80
|
+
"@softure-ai/ui": "^0.1.7"
|
|
81
|
+
},
|
|
78
82
|
"peerDependenciesMeta": {
|
|
79
83
|
"@softure-ai/mailing": {
|
|
80
84
|
"optional": true
|
package/src/index.ts
CHANGED
|
@@ -28,8 +28,8 @@ export const BILLING_RATE_LIMIT_BUCKETS = {
|
|
|
28
28
|
export const billing = defineModule({
|
|
29
29
|
manifest: {
|
|
30
30
|
id: MODULE_ID,
|
|
31
|
-
version: "0.1.
|
|
32
|
-
dependsOn: { security: "^0.1.0", auth: "^0.1.0" },
|
|
31
|
+
version: "0.1.7",
|
|
32
|
+
dependsOn: { security: "^0.1.0", auth: "^0.1.0", mailing: "^0.1.0?", ops: "^0.1.0?" },
|
|
33
33
|
dbSchema: "billing",
|
|
34
34
|
tables: ["entitlements", "payments", "payment_requests", "manual_grants"],
|
|
35
35
|
env: [
|
|
@@ -38,11 +38,17 @@ export interface AccessReminderSummary {
|
|
|
38
38
|
readonly due: number;
|
|
39
39
|
/** Mails sent now. */
|
|
40
40
|
readonly sent: number;
|
|
41
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Mails an earlier run sent (or refused), another run is sending right now, or whose send was interrupted
|
|
43
|
+
* so long ago that it may have gone out (mailing's `uncertain`).
|
|
44
|
+
*/
|
|
42
45
|
readonly skipped: number;
|
|
43
46
|
/** Mails refused for good (an invalid address, the provider rejected it). */
|
|
44
47
|
readonly rejected: number;
|
|
45
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Mails the provider could not take now; the next run sends them. A refused account or a spent quota stops the
|
|
50
|
+
* run there: the mails after it are not counted and go out on the next run.
|
|
51
|
+
*/
|
|
46
52
|
readonly retryLater: number;
|
|
47
53
|
}
|
|
48
54
|
|
|
@@ -95,10 +101,15 @@ export async function sendAccessReminders(ctx: ModuleContext<Queryable>, options
|
|
|
95
101
|
const lastDay = formatLastDay(reminder.endsAt, ctx.config.locale, ctx.config.timezone);
|
|
96
102
|
const mail = renderAccessReminderMail(messages, { kind: reminder.kind, lastDay, link });
|
|
97
103
|
const outcome = await deliverOnce(ctx, { scope: getAccessReminderScope(reminder.kind, reminder.userId, reminder.endsAt), mail: { to: reminder.email, ...mail } });
|
|
98
|
-
if (outcome.status === "done" || outcome.status === "in-flight") {
|
|
104
|
+
if (outcome.status === "done" || outcome.status === "in-flight" || outcome.status === "uncertain") {
|
|
99
105
|
counts.skipped += 1;
|
|
100
106
|
continue;
|
|
101
107
|
}
|
|
108
|
+
if (outcome.status === "halted") {
|
|
109
|
+
// The account cannot send (refused key, spent quota): every other mail would fail the same way.
|
|
110
|
+
counts.retryLater += 1;
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
102
113
|
if (outcome.status === "sent") counts.sent += 1;
|
|
103
114
|
else if (outcome.status === "rejected") counts.rejected += 1;
|
|
104
115
|
else counts.retryLater += 1;
|
package/src/next/context.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// undeclared `adminRole`) fails on the first one.
|
|
4
4
|
import { systemClock, type SoftureConfig } from "@softure-ai/core";
|
|
5
5
|
import { getSoftureConfig } from "@softure-ai/core/next";
|
|
6
|
-
import {
|
|
6
|
+
import { getConfiguredDatabase } from "@softure-ai/db";
|
|
7
7
|
import type { BillingContext } from "../server/entitlements.js";
|
|
8
8
|
import { assertAdminRoleDeclared } from "../server/setup.js";
|
|
9
9
|
|
|
@@ -13,6 +13,6 @@ export async function getBillingContext(config: SoftureConfig = getSoftureConfig
|
|
|
13
13
|
// Unreachable for a validated config: the module has a database schema.
|
|
14
14
|
throw new Error("@softure-ai/billing: softure.config.ts has no database; billing needs one");
|
|
15
15
|
}
|
|
16
|
-
const { db } = await
|
|
16
|
+
const { db } = await getConfiguredDatabase(config.database);
|
|
17
17
|
return { db, clock: systemClock, config };
|
|
18
18
|
}
|