@sudobility/consumables_service 0.0.2
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/CLAUDE.md +92 -0
- package/dist/helpers/ConsumablesHelper.d.ts +27 -0
- package/dist/helpers/ConsumablesHelper.d.ts.map +1 -0
- package/dist/helpers/ConsumablesHelper.js +142 -0
- package/dist/helpers/ConsumablesHelper.js.map +1 -0
- package/dist/helpers/WebhookHelper.d.ts +18 -0
- package/dist/helpers/WebhookHelper.d.ts.map +1 -0
- package/dist/helpers/WebhookHelper.js +34 -0
- package/dist/helpers/WebhookHelper.js.map +1 -0
- package/dist/helpers/index.d.ts +3 -0
- package/dist/helpers/index.d.ts.map +1 -0
- package/dist/helpers/index.js +3 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/schema/index.d.ts +14 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +36 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/types/index.d.ts +44 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +61 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Consumables Service
|
|
2
|
+
|
|
3
|
+
Shared backend library for consumable credits management with Drizzle ORM.
|
|
4
|
+
|
|
5
|
+
**npm**: `@sudobility/consumables_service` (public)
|
|
6
|
+
|
|
7
|
+
## Tech Stack
|
|
8
|
+
|
|
9
|
+
- **Language**: TypeScript (strict mode)
|
|
10
|
+
- **Runtime**: Bun
|
|
11
|
+
- **Package Manager**: Bun (do not use npm/yarn/pnpm for installing dependencies)
|
|
12
|
+
- **Build**: TypeScript compiler (ESM)
|
|
13
|
+
- **Test**: vitest
|
|
14
|
+
|
|
15
|
+
## Project Structure
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
src/
|
|
19
|
+
├── index.ts # Main exports
|
|
20
|
+
├── types/
|
|
21
|
+
│ └── index.ts # All type definitions
|
|
22
|
+
├── schema/
|
|
23
|
+
│ └── index.ts # Drizzle schema creator
|
|
24
|
+
└── helpers/
|
|
25
|
+
├── index.ts # Helper re-exports
|
|
26
|
+
├── ConsumablesHelper.ts # Core business logic
|
|
27
|
+
└── WebhookHelper.ts # RevenueCat webhook validation
|
|
28
|
+
tests/
|
|
29
|
+
├── ConsumablesHelper.test.ts
|
|
30
|
+
└── WebhookHelper.test.ts
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Commands
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bun run build # Build ESM
|
|
37
|
+
bun run clean # Remove dist/
|
|
38
|
+
bun run dev # Watch mode
|
|
39
|
+
bun test # Run tests
|
|
40
|
+
bun run lint # Run ESLint
|
|
41
|
+
bun run typecheck # TypeScript check
|
|
42
|
+
bun run verify # All checks + build (use before commit)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Key Concepts
|
|
46
|
+
|
|
47
|
+
### Schema Creator
|
|
48
|
+
|
|
49
|
+
`createConsumablesSchema(pgSchema)` creates three tables within a given Drizzle PgSchema:
|
|
50
|
+
- `consumable_balances` — user balance with stored credit count
|
|
51
|
+
- `consumable_purchases` — purchase audit trail (source, transaction ref, price)
|
|
52
|
+
- `consumable_usages` — usage audit trail (filename, timestamp)
|
|
53
|
+
|
|
54
|
+
The consuming API passes its own schema so migrations stay in one place.
|
|
55
|
+
|
|
56
|
+
### ConsumablesHelper
|
|
57
|
+
|
|
58
|
+
Core business logic class. Constructed with `(db, tables, config)`:
|
|
59
|
+
- `getBalance(userId)` — get-or-create; auto-grants free credits on first access
|
|
60
|
+
- `recordPurchase(userId, request)` — insert purchase + atomic balance increment
|
|
61
|
+
- `recordUsage(userId, filename?)` — atomic decrement with balance > 0 guard
|
|
62
|
+
- `getPurchaseHistory/getUsageHistory` — paginated audit trail queries
|
|
63
|
+
- `recordPurchaseFromWebhook(...)` — idempotent webhook processing
|
|
64
|
+
|
|
65
|
+
### WebhookHelper
|
|
66
|
+
|
|
67
|
+
- `validateWebhookSignature(rawBody, signature, secret)` — HMAC-SHA256 validation
|
|
68
|
+
- `parseConsumablePurchaseEvent(event)` — extract purchase data from RevenueCat webhook
|
|
69
|
+
|
|
70
|
+
## Usage
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import {
|
|
74
|
+
createConsumablesSchema,
|
|
75
|
+
ConsumablesHelper,
|
|
76
|
+
} from "@sudobility/consumables_service";
|
|
77
|
+
|
|
78
|
+
// In your API's schema file:
|
|
79
|
+
const tables = createConsumablesSchema(mySchema);
|
|
80
|
+
|
|
81
|
+
// In your API's service file:
|
|
82
|
+
const helper = new ConsumablesHelper(db, tables, { initialFreeCredits: 3 });
|
|
83
|
+
|
|
84
|
+
const balance = await helper.getBalance(userId);
|
|
85
|
+
const updated = await helper.recordPurchase(userId, { credits: 25, source: "web", ... });
|
|
86
|
+
const result = await helper.recordUsage(userId, "logo.svg");
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Consuming APIs
|
|
90
|
+
|
|
91
|
+
APIs using this library:
|
|
92
|
+
- svgr_api
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ConsumablesSchemaResult } from "../schema";
|
|
2
|
+
import type { BalanceResponse, PurchaseRequest, UseResponse, ConsumablePurchase, ConsumableUsage, ConsumablesConfig, ConsumableSource } from "../types";
|
|
3
|
+
export declare class ConsumablesHelper {
|
|
4
|
+
private db;
|
|
5
|
+
private tables;
|
|
6
|
+
private config;
|
|
7
|
+
constructor(db: any, tables: ConsumablesSchemaResult, config: ConsumablesConfig);
|
|
8
|
+
/** Get or create balance record. Auto-grants free credits on first access. */
|
|
9
|
+
getBalance(userId: string): Promise<BalanceResponse>;
|
|
10
|
+
/** Record a purchase. Atomically adds credits to balance. */
|
|
11
|
+
recordPurchase(userId: string, request: PurchaseRequest): Promise<BalanceResponse>;
|
|
12
|
+
/** Record a usage. Atomically deducts 1 credit. Returns error if insufficient. */
|
|
13
|
+
recordUsage(userId: string, filename?: string): Promise<UseResponse>;
|
|
14
|
+
/** Get purchase history (most recent first). */
|
|
15
|
+
getPurchaseHistory(userId: string, limit?: number, offset?: number): Promise<ConsumablePurchase[]>;
|
|
16
|
+
/** Get usage history (most recent first). */
|
|
17
|
+
getUsageHistory(userId: string, limit?: number, offset?: number): Promise<ConsumableUsage[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Idempotent purchase recording from webhook.
|
|
20
|
+
* Checks if transaction_ref_id already exists to prevent duplicates.
|
|
21
|
+
*/
|
|
22
|
+
recordPurchaseFromWebhook(userId: string, transactionId: string, credits: number, source: ConsumableSource, productId: string, priceCents: number, currency: string): Promise<{
|
|
23
|
+
alreadyProcessed: boolean;
|
|
24
|
+
balance: number;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=ConsumablesHelper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConsumablesHelper.d.ts","sourceRoot":"","sources":["../../src/helpers/ConsumablesHelper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,UAAU,CAAC;AAElB,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,EAAE,CAAM;IAChB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,MAAM,CAAoB;gBAGhC,EAAE,EAAE,GAAG,EACP,MAAM,EAAE,uBAAuB,EAC/B,MAAM,EAAE,iBAAiB;IAO3B,8EAA8E;IACxE,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAkC1D,6DAA6D;IACvD,cAAc,CAClB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,eAAe,CAAC;IAsC3B,kFAAkF;IAC5E,WAAW,CACf,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC;IA8BvB,gDAAgD;IAC1C,kBAAkB,CACtB,MAAM,EAAE,MAAM,EACd,KAAK,SAAK,EACV,MAAM,SAAI,GACT,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAWhC,6CAA6C;IACvC,eAAe,CACnB,MAAM,EAAE,MAAM,EACd,KAAK,SAAK,EACV,MAAM,SAAI,GACT,OAAO,CAAC,eAAe,EAAE,CAAC;IAW7B;;;OAGG;IACG,yBAAyB,CAC7B,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,gBAAgB,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAyB3D"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { eq, desc, sql } from "drizzle-orm";
|
|
2
|
+
export class ConsumablesHelper {
|
|
3
|
+
constructor(db, tables, config) {
|
|
4
|
+
this.db = db;
|
|
5
|
+
this.tables = tables;
|
|
6
|
+
this.config = config;
|
|
7
|
+
}
|
|
8
|
+
/** Get or create balance record. Auto-grants free credits on first access. */
|
|
9
|
+
async getBalance(userId) {
|
|
10
|
+
const { consumableBalances } = this.tables;
|
|
11
|
+
const existing = await this.db
|
|
12
|
+
.select()
|
|
13
|
+
.from(consumableBalances)
|
|
14
|
+
.where(eq(consumableBalances.user_id, userId));
|
|
15
|
+
if (existing.length > 0) {
|
|
16
|
+
return {
|
|
17
|
+
balance: existing[0].balance,
|
|
18
|
+
initial_credits: existing[0].initial_credits,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
// First access — create with free credits
|
|
22
|
+
const freeCredits = this.config.initialFreeCredits;
|
|
23
|
+
await this.db.insert(consumableBalances).values({
|
|
24
|
+
user_id: userId,
|
|
25
|
+
balance: freeCredits,
|
|
26
|
+
initial_credits: freeCredits,
|
|
27
|
+
});
|
|
28
|
+
// Record the free credits as a purchase audit entry
|
|
29
|
+
if (freeCredits > 0) {
|
|
30
|
+
await this.db.insert(this.tables.consumablePurchases).values({
|
|
31
|
+
user_id: userId,
|
|
32
|
+
credits: freeCredits,
|
|
33
|
+
source: "free",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return { balance: freeCredits, initial_credits: freeCredits };
|
|
37
|
+
}
|
|
38
|
+
/** Record a purchase. Atomically adds credits to balance. */
|
|
39
|
+
async recordPurchase(userId, request) {
|
|
40
|
+
const { consumableBalances, consumablePurchases } = this.tables;
|
|
41
|
+
// Ensure balance record exists (idempotent)
|
|
42
|
+
await this.getBalance(userId);
|
|
43
|
+
// Insert purchase record
|
|
44
|
+
await this.db.insert(consumablePurchases).values({
|
|
45
|
+
user_id: userId,
|
|
46
|
+
credits: request.credits,
|
|
47
|
+
source: request.source,
|
|
48
|
+
transaction_ref_id: request.transaction_ref_id ?? null,
|
|
49
|
+
product_id: request.product_id ?? null,
|
|
50
|
+
price_cents: request.price_cents ?? null,
|
|
51
|
+
currency: request.currency ?? null,
|
|
52
|
+
});
|
|
53
|
+
// Atomically increment balance
|
|
54
|
+
await this.db
|
|
55
|
+
.update(consumableBalances)
|
|
56
|
+
.set({
|
|
57
|
+
balance: sql `${consumableBalances.balance} + ${request.credits}`,
|
|
58
|
+
updated_at: new Date(),
|
|
59
|
+
})
|
|
60
|
+
.where(eq(consumableBalances.user_id, userId));
|
|
61
|
+
// Return updated balance
|
|
62
|
+
const updated = await this.db
|
|
63
|
+
.select()
|
|
64
|
+
.from(consumableBalances)
|
|
65
|
+
.where(eq(consumableBalances.user_id, userId));
|
|
66
|
+
return {
|
|
67
|
+
balance: updated[0].balance,
|
|
68
|
+
initial_credits: updated[0].initial_credits,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** Record a usage. Atomically deducts 1 credit. Returns error if insufficient. */
|
|
72
|
+
async recordUsage(userId, filename) {
|
|
73
|
+
const { consumableBalances, consumableUsages } = this.tables;
|
|
74
|
+
// Atomically decrement (with guard against going negative)
|
|
75
|
+
const result = await this.db
|
|
76
|
+
.update(consumableBalances)
|
|
77
|
+
.set({
|
|
78
|
+
balance: sql `${consumableBalances.balance} - 1`,
|
|
79
|
+
updated_at: new Date(),
|
|
80
|
+
})
|
|
81
|
+
.where(sql `${consumableBalances.user_id} = ${userId} AND ${consumableBalances.balance} > 0`)
|
|
82
|
+
.returning({ balance: consumableBalances.balance });
|
|
83
|
+
if (result.length === 0) {
|
|
84
|
+
// Either user doesn't exist or balance is 0
|
|
85
|
+
const current = await this.getBalance(userId);
|
|
86
|
+
return { balance: current.balance, success: false };
|
|
87
|
+
}
|
|
88
|
+
// Record usage
|
|
89
|
+
await this.db.insert(consumableUsages).values({
|
|
90
|
+
user_id: userId,
|
|
91
|
+
filename: filename ?? null,
|
|
92
|
+
});
|
|
93
|
+
return { balance: result[0].balance, success: true };
|
|
94
|
+
}
|
|
95
|
+
/** Get purchase history (most recent first). */
|
|
96
|
+
async getPurchaseHistory(userId, limit = 50, offset = 0) {
|
|
97
|
+
const { consumablePurchases } = this.tables;
|
|
98
|
+
return this.db
|
|
99
|
+
.select()
|
|
100
|
+
.from(consumablePurchases)
|
|
101
|
+
.where(eq(consumablePurchases.user_id, userId))
|
|
102
|
+
.orderBy(desc(consumablePurchases.created_at))
|
|
103
|
+
.limit(limit)
|
|
104
|
+
.offset(offset);
|
|
105
|
+
}
|
|
106
|
+
/** Get usage history (most recent first). */
|
|
107
|
+
async getUsageHistory(userId, limit = 50, offset = 0) {
|
|
108
|
+
const { consumableUsages } = this.tables;
|
|
109
|
+
return this.db
|
|
110
|
+
.select()
|
|
111
|
+
.from(consumableUsages)
|
|
112
|
+
.where(eq(consumableUsages.user_id, userId))
|
|
113
|
+
.orderBy(desc(consumableUsages.created_at))
|
|
114
|
+
.limit(limit)
|
|
115
|
+
.offset(offset);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Idempotent purchase recording from webhook.
|
|
119
|
+
* Checks if transaction_ref_id already exists to prevent duplicates.
|
|
120
|
+
*/
|
|
121
|
+
async recordPurchaseFromWebhook(userId, transactionId, credits, source, productId, priceCents, currency) {
|
|
122
|
+
// Check for duplicate
|
|
123
|
+
const existing = await this.db
|
|
124
|
+
.select()
|
|
125
|
+
.from(this.tables.consumablePurchases)
|
|
126
|
+
.where(eq(this.tables.consumablePurchases.transaction_ref_id, transactionId));
|
|
127
|
+
if (existing.length > 0) {
|
|
128
|
+
const bal = await this.getBalance(userId);
|
|
129
|
+
return { alreadyProcessed: true, balance: bal.balance };
|
|
130
|
+
}
|
|
131
|
+
const result = await this.recordPurchase(userId, {
|
|
132
|
+
credits,
|
|
133
|
+
source,
|
|
134
|
+
transaction_ref_id: transactionId,
|
|
135
|
+
product_id: productId,
|
|
136
|
+
price_cents: priceCents,
|
|
137
|
+
currency,
|
|
138
|
+
});
|
|
139
|
+
return { alreadyProcessed: false, balance: result.balance };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=ConsumablesHelper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConsumablesHelper.js","sourceRoot":"","sources":["../../src/helpers/ConsumablesHelper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RevenueCatWebhookEvent } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Validates RevenueCat webhook HMAC signature.
|
|
4
|
+
*/
|
|
5
|
+
export declare function validateWebhookSignature(rawBody: string, signature: string, secret: string): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Parse the webhook event and extract purchase data.
|
|
8
|
+
* Returns null if the event type is not a consumable purchase.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseConsumablePurchaseEvent(event: RevenueCatWebhookEvent): {
|
|
11
|
+
userId: string;
|
|
12
|
+
transactionId: string;
|
|
13
|
+
productId: string;
|
|
14
|
+
priceCents: number;
|
|
15
|
+
currency: string;
|
|
16
|
+
store: string;
|
|
17
|
+
} | null;
|
|
18
|
+
//# sourceMappingURL=WebhookHelper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebhookHelper.d.ts","sourceRoot":"","sources":["../../src/helpers/WebhookHelper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAEvD;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAKT;AAQD;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,sBAAsB,GAC5B;IACD,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,IAAI,CAeP"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createHmac } from "crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Validates RevenueCat webhook HMAC signature.
|
|
4
|
+
*/
|
|
5
|
+
export function validateWebhookSignature(rawBody, signature, secret) {
|
|
6
|
+
const hmac = createHmac("sha256", secret);
|
|
7
|
+
hmac.update(rawBody);
|
|
8
|
+
const expected = hmac.digest("hex");
|
|
9
|
+
return signature === expected;
|
|
10
|
+
}
|
|
11
|
+
const STORE_TO_SOURCE = {
|
|
12
|
+
STRIPE: "web",
|
|
13
|
+
APP_STORE: "apple",
|
|
14
|
+
PLAY_STORE: "google",
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Parse the webhook event and extract purchase data.
|
|
18
|
+
* Returns null if the event type is not a consumable purchase.
|
|
19
|
+
*/
|
|
20
|
+
export function parseConsumablePurchaseEvent(event) {
|
|
21
|
+
const validTypes = ["NON_RENEWING_PURCHASE", "INITIAL_PURCHASE"];
|
|
22
|
+
if (!validTypes.includes(event.event.type)) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
userId: event.event.app_user_id,
|
|
27
|
+
transactionId: event.event.transaction_id,
|
|
28
|
+
productId: event.event.product_id,
|
|
29
|
+
priceCents: Math.round(event.event.price_in_purchased_currency * 100),
|
|
30
|
+
currency: event.event.currency,
|
|
31
|
+
store: STORE_TO_SOURCE[event.event.store] ?? event.event.store,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=WebhookHelper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebhookHelper.js","sourceRoot":"","sources":["../../src/helpers/WebhookHelper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGpC;;GAEG;AACH,MAAM,UAAU,wBAAwB,CACtC,OAAe,EACf,SAAiB,EACjB,MAAc;IAEd,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACpC,OAAO,SAAS,KAAK,QAAQ,CAAC;AAChC,CAAC;AAED,MAAM,eAAe,GAA2B;IAC9C,MAAM,EAAE,KAAK;IACb,SAAS,EAAE,OAAO;IAClB,UAAU,EAAE,QAAQ;CACrB,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,4BAA4B,CAC1C,KAA6B;IAS7B,MAAM,UAAU,GAAG,CAAC,uBAAuB,EAAE,kBAAkB,CAAC,CAAC;IAEjE,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW;QAC/B,aAAa,EAAE,KAAK,CAAC,KAAK,CAAC,cAAc;QACzC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU;QACjC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,2BAA2B,GAAG,GAAG,CAAC;QACrE,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,QAAQ;QAC9B,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK;KAC/D,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,iBAAiB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { ConsumableSource, ConsumableBalanceResponse, ConsumablePurchaseRequest, ConsumableUseRequest, ConsumableUseResponse, ConsumablePurchaseRecord, ConsumableUsageRecord, BalanceResponse, PurchaseRequest, UseRequest, UseResponse, } from "./types";
|
|
2
|
+
export type { ConsumableBalance, ConsumablePurchase, ConsumableUsage, RevenueCatWebhookEvent, ConsumablesConfig, } from "./types";
|
|
3
|
+
export { createConsumablesSchema, type ConsumablesSchemaResult, } from "./schema";
|
|
4
|
+
export { ConsumablesHelper, validateWebhookSignature, parseConsumablePurchaseEvent, } from "./helpers";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,gBAAgB,EAChB,yBAAyB,EACzB,yBAAyB,EACzB,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EAErB,eAAe,EACf,eAAe,EACf,UAAU,EACV,WAAW,GACZ,MAAM,SAAS,CAAC;AAGjB,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,uBAAuB,EACvB,KAAK,uBAAuB,GAC7B,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAyBA,SAAS;AACT,OAAO,EACL,uBAAuB,GAExB,MAAM,UAAU,CAAC;AAElB,UAAU;AACV,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create consumable tables within a given Drizzle PgSchema.
|
|
3
|
+
* The consuming API passes its own schema so migrations stay in one place.
|
|
4
|
+
*
|
|
5
|
+
* Uses `any` for schema param to avoid drizzle-orm version coupling
|
|
6
|
+
* between this library and the consuming API.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createConsumablesSchema(schema: any): {
|
|
9
|
+
consumableBalances: any;
|
|
10
|
+
consumablePurchases: any;
|
|
11
|
+
consumableUsages: any;
|
|
12
|
+
};
|
|
13
|
+
export type ConsumablesSchemaResult = ReturnType<typeof createConsumablesSchema>;
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,GAAG;;;;EA6BlD;AAED,MAAM,MAAM,uBAAuB,GAAG,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { varchar, timestamp, serial, integer, } from "drizzle-orm/pg-core";
|
|
2
|
+
/**
|
|
3
|
+
* Create consumable tables within a given Drizzle PgSchema.
|
|
4
|
+
* The consuming API passes its own schema so migrations stay in one place.
|
|
5
|
+
*
|
|
6
|
+
* Uses `any` for schema param to avoid drizzle-orm version coupling
|
|
7
|
+
* between this library and the consuming API.
|
|
8
|
+
*/
|
|
9
|
+
export function createConsumablesSchema(schema) {
|
|
10
|
+
const consumableBalances = schema.table("consumable_balances", {
|
|
11
|
+
user_id: varchar("user_id", { length: 128 }).primaryKey(),
|
|
12
|
+
balance: integer("balance").notNull().default(0),
|
|
13
|
+
initial_credits: integer("initial_credits").notNull().default(0),
|
|
14
|
+
created_at: timestamp("created_at").defaultNow().notNull(),
|
|
15
|
+
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
|
16
|
+
});
|
|
17
|
+
const consumablePurchases = schema.table("consumable_purchases", {
|
|
18
|
+
id: serial("id").primaryKey(),
|
|
19
|
+
user_id: varchar("user_id", { length: 128 }).notNull(),
|
|
20
|
+
credits: integer("credits").notNull(),
|
|
21
|
+
source: varchar("source", { length: 20 }).notNull(),
|
|
22
|
+
transaction_ref_id: varchar("transaction_ref_id", { length: 255 }),
|
|
23
|
+
product_id: varchar("product_id", { length: 255 }),
|
|
24
|
+
price_cents: integer("price_cents"),
|
|
25
|
+
currency: varchar("currency", { length: 10 }),
|
|
26
|
+
created_at: timestamp("created_at").defaultNow().notNull(),
|
|
27
|
+
});
|
|
28
|
+
const consumableUsages = schema.table("consumable_usages", {
|
|
29
|
+
id: serial("id").primaryKey(),
|
|
30
|
+
user_id: varchar("user_id", { length: 128 }).notNull(),
|
|
31
|
+
filename: varchar("filename", { length: 500 }),
|
|
32
|
+
created_at: timestamp("created_at").defaultNow().notNull(),
|
|
33
|
+
});
|
|
34
|
+
return { consumableBalances, consumablePurchases, consumableUsages };
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,SAAS,EACT,MAAM,EACN,OAAO,GACR,MAAM,qBAAqB,CAAC;AAE7B;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAW;IACjD,MAAM,kBAAkB,GAAG,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE;QAC7D,OAAO,EAAE,OAAO,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,UAAU,EAAE;QACzD,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QAChD,eAAe,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QAChE,UAAU,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE;QAC1D,UAAU,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE;KAC3D,CAAC,CAAC;IAEH,MAAM,mBAAmB,GAAG,MAAM,CAAC,KAAK,CAAC,sBAAsB,EAAE;QAC/D,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE;QAC7B,OAAO,EAAE,OAAO,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,OAAO,EAAE;QACtD,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;QACrC,MAAM,EAAE,OAAO,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE;QACnD,kBAAkB,EAAE,OAAO,CAAC,oBAAoB,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;QAClE,UAAU,EAAE,OAAO,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;QAClD,WAAW,EAAE,OAAO,CAAC,aAAa,CAAC;QACnC,QAAQ,EAAE,OAAO,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QAC7C,UAAU,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE;KAC3D,CAAC,CAAC;IAEH,MAAM,gBAAgB,GAAG,MAAM,CAAC,KAAK,CAAC,mBAAmB,EAAE;QACzD,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE;QAC7B,OAAO,EAAE,OAAO,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,OAAO,EAAE;QACtD,QAAQ,EAAE,OAAO,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;QAC9C,UAAU,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE;KAC3D,CAAC,CAAC;IAEH,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,CAAC;AACvE,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export type { ConsumableSource, ConsumableBalanceResponse, ConsumablePurchaseRequest, ConsumableUseRequest, ConsumableUseResponse, ConsumablePurchaseRecord, ConsumableUsageRecord, } from "@sudobility/types";
|
|
2
|
+
export type { ConsumableBalanceResponse as BalanceResponse, ConsumablePurchaseRequest as PurchaseRequest, ConsumableUseRequest as UseRequest, ConsumableUseResponse as UseResponse, } from "@sudobility/types";
|
|
3
|
+
export interface ConsumableBalance {
|
|
4
|
+
user_id: string;
|
|
5
|
+
balance: number;
|
|
6
|
+
initial_credits: number;
|
|
7
|
+
created_at: Date;
|
|
8
|
+
updated_at: Date;
|
|
9
|
+
}
|
|
10
|
+
export interface ConsumablePurchase {
|
|
11
|
+
id: number;
|
|
12
|
+
user_id: string;
|
|
13
|
+
credits: number;
|
|
14
|
+
source: string;
|
|
15
|
+
transaction_ref_id: string | null;
|
|
16
|
+
product_id: string | null;
|
|
17
|
+
price_cents: number | null;
|
|
18
|
+
currency: string | null;
|
|
19
|
+
created_at: Date;
|
|
20
|
+
}
|
|
21
|
+
export interface ConsumableUsage {
|
|
22
|
+
id: number;
|
|
23
|
+
user_id: string;
|
|
24
|
+
filename: string | null;
|
|
25
|
+
created_at: Date;
|
|
26
|
+
}
|
|
27
|
+
export interface RevenueCatWebhookEvent {
|
|
28
|
+
api_version: string;
|
|
29
|
+
event: {
|
|
30
|
+
type: string;
|
|
31
|
+
app_user_id: string;
|
|
32
|
+
product_id: string;
|
|
33
|
+
price_in_purchased_currency: number;
|
|
34
|
+
currency: string;
|
|
35
|
+
store: string;
|
|
36
|
+
transaction_id: string;
|
|
37
|
+
purchased_at_ms: number;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export interface ConsumablesConfig {
|
|
41
|
+
initialFreeCredits: number;
|
|
42
|
+
revenueCatWebhookSecret?: string;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,gBAAgB,EAChB,yBAAyB,EACzB,yBAAyB,EACzB,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAG3B,YAAY,EACV,yBAAyB,IAAI,eAAe,EAC5C,yBAAyB,IAAI,eAAe,EAC5C,oBAAoB,IAAI,UAAU,EAClC,qBAAqB,IAAI,WAAW,GACrC,MAAM,mBAAmB,CAAC;AAI3B,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,IAAI,CAAC;IACjB,UAAU,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,CAAC;CAClB;AAID,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,2BAA2B,EAAE,MAAM,CAAC;QACpC,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AAID,MAAM,WAAW,iBAAiB;IAChC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sudobility/consumables_service",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Shared backend library for consumable credits management with Drizzle ORM",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc -p tsconfig.esm.json",
|
|
16
|
+
"clean": "rm -rf dist",
|
|
17
|
+
"dev": "tsc --watch",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:watch": "vitest",
|
|
20
|
+
"lint": "eslint src/",
|
|
21
|
+
"lint:fix": "eslint src/ --fix",
|
|
22
|
+
"typecheck": "tsc --noEmit",
|
|
23
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
24
|
+
"format:check": "prettier --check \"src/**/*.ts\"",
|
|
25
|
+
"verify": "bun run typecheck && bun run lint && bun run test && bun run build",
|
|
26
|
+
"prepublishOnly": "bun run clean && bun run verify"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist/**/*",
|
|
30
|
+
"CLAUDE.md"
|
|
31
|
+
],
|
|
32
|
+
"keywords": [
|
|
33
|
+
"consumables",
|
|
34
|
+
"credits",
|
|
35
|
+
"revenuecat",
|
|
36
|
+
"in-app-purchase",
|
|
37
|
+
"drizzle"
|
|
38
|
+
],
|
|
39
|
+
"author": "Sudobility",
|
|
40
|
+
"license": "BUSL-1.1",
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@sudobility/types": "^1.9.51",
|
|
43
|
+
"drizzle-orm": ">=0.44.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@sudobility/types": "^1.9.51",
|
|
47
|
+
"drizzle-orm": "^0.45.1",
|
|
48
|
+
"vitest": "^4.0.4",
|
|
49
|
+
"@types/bun": "latest",
|
|
50
|
+
"@types/node": "^24.0.0",
|
|
51
|
+
"@typescript-eslint/eslint-plugin": "^8.50.0",
|
|
52
|
+
"@typescript-eslint/parser": "^8.50.0",
|
|
53
|
+
"eslint": "^9.39.0",
|
|
54
|
+
"eslint-plugin-import": "^2.32.0",
|
|
55
|
+
"prettier": "^3.7.0",
|
|
56
|
+
"typescript": "^5.9.0"
|
|
57
|
+
},
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public"
|
|
60
|
+
}
|
|
61
|
+
}
|