@sudobility/consumables_service 0.0.3 → 0.0.4

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.
Files changed (2) hide show
  1. package/CLAUDE.md +38 -0
  2. package/package.json +3 -3
package/CLAUDE.md CHANGED
@@ -90,3 +90,41 @@ const result = await helper.recordUsage(userId, "logo.svg");
90
90
 
91
91
  APIs using this library:
92
92
  - svgr_api
93
+
94
+ ## Related Projects
95
+
96
+ - **svgr_api** — The primary consuming API. It uses `createConsumablesSchema` and `ConsumablesHelper` for its credits system. Changes here directly affect svgr_api.
97
+ - **consumables_client** (`@sudobility/consumables_client`) — Frontend counterpart that calls this service's API endpoints over HTTP. The client and service must agree on API contracts (response shapes, error codes).
98
+
99
+ Dependency direction: `svgr_api` --> `consumables_service` (library dep); `consumables_client` --> `consumables_service` (HTTP at runtime)
100
+
101
+ ## Coding Patterns
102
+
103
+ - **Schema factory pattern**: `createConsumablesSchema(pgSchema)` accepts any Drizzle `PgSchema` so the consumer owns all migrations. Never create migrations in this package -- the consuming API handles that.
104
+ - **Atomic balance operations**: Balance increments (purchases) and decrements (usage) use atomic SQL operations (e.g., `SET credits = credits + N`). Never read-then-write; always use atomic updates to avoid race conditions.
105
+ - **Idempotent webhook processing**: `recordPurchaseFromWebhook()` uses the transaction reference as a deduplication key. Replaying the same webhook is safe and produces no duplicate records.
106
+ - **Get-or-create pattern for balances**: `getBalance(userId)` creates a balance row with `initialFreeCredits` if none exists. This avoids separate "create user" flows.
107
+
108
+ ## Gotchas
109
+
110
+ - **`createConsumablesSchema` takes any `PgSchema` to avoid drizzle-orm version coupling**: The consuming API passes its own schema instance. This means this library does not pin a specific drizzle-orm version for schema creation. Be careful not to use drizzle APIs that only exist in specific versions.
111
+ - **`recordPurchaseFromWebhook` is idempotent (safe to replay)**: It checks for existing transactions by reference ID before inserting. Do not remove this deduplication check.
112
+ - **Balance decrements guard against going negative**: `recordUsage()` uses a `WHERE credits > 0` guard in the atomic update. If the balance is zero, the operation fails gracefully rather than going negative. Do not remove this guard.
113
+ - **Consumer owns migrations**: Even though this package defines the schema, the consuming API (e.g., svgr_api) generates and runs migrations. Never add migration files to this package.
114
+ - **Free credits are granted once**: `getBalance()` only grants `initialFreeCredits` on first access (row creation). Subsequent calls return the existing balance. Do not change this to grant free credits on every call.
115
+
116
+ ## Testing
117
+
118
+ - Run tests: `bun test` (uses vitest)
119
+ - Tests use a **mock database** -- they do not connect to a real PostgreSQL instance.
120
+ - `ConsumablesHelper.test.ts` tests core business logic: balance get-or-create, purchase recording, usage recording, pagination, and idempotent webhook processing.
121
+ - `WebhookHelper.test.ts` tests HMAC signature validation and event parsing.
122
+ - When adding new helper methods, add corresponding tests with both success and failure cases (e.g., insufficient balance, duplicate webhook).
123
+
124
+ ## Publishing
125
+
126
+ - Package: `@sudobility/consumables_service` (public on npm)
127
+ - Build before publish: `bun run build` produces ESM output in `dist/`
128
+ - Run `bun run verify` before publishing -- this runs lint, typecheck, tests, and build in sequence.
129
+ - Bump version in `package.json`, then `npm publish --access public`
130
+ - After publishing, update the dependency version in consuming APIs (e.g., svgr_api) and verify they still build and pass tests
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sudobility/consumables_service",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Shared backend library for consumable credits management with Drizzle ORM",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -39,11 +39,11 @@
39
39
  "author": "Sudobility",
40
40
  "license": "BUSL-1.1",
41
41
  "peerDependencies": {
42
- "@sudobility/types": "^1.9.51",
42
+ "@sudobility/types": "^1.9.52",
43
43
  "drizzle-orm": ">=0.44.0"
44
44
  },
45
45
  "devDependencies": {
46
- "@sudobility/types": "^1.9.51",
46
+ "@sudobility/types": "^1.9.52",
47
47
  "drizzle-orm": "^0.45.1",
48
48
  "vitest": "^4.0.4",
49
49
  "@types/bun": "latest",