@formata/limitr 0.5.23 → 0.5.24

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/README.md CHANGED
@@ -1,39 +1,82 @@
1
1
  # Limitr
2
- **Open-source policy engine for plans, limits, and usage enforcement.**
3
2
 
4
- Limitr embeds monetization logic directly in your app. No hard-coded pricing, no redeploys to change limits.
3
+ **Stop hardcoding your pricing. Treat it like config.**
5
4
 
6
- ## What It Does
7
- Define pricing in a policy document, not application code:
8
- - **Plans & entitlements** - seat limits, usage caps, feature gates
9
- - **Offline enforcement** - runs locally, no API calls required
10
- - **Event-driven** - react to limit hits, overages, denials
11
- - **Portable** - works anywhere JavaScript runs
12
- - **Inspectable** - customers and auditors can read your limits
13
-
14
- ## Why Limitr?
15
- Billing systems (Stripe, etc.) handle payments. **Limitr handles enforcement.**
16
-
17
- Most apps hardcode limits in `pricing.ts`:
18
5
  ```typescript
6
+ // Before: Pricing logic scattered everywhere
19
7
  if (user.plan === 'free' && user.seats >= 1) {
20
8
  throw new Error('Upgrade to add more seats');
21
9
  }
10
+
11
+ // After: One source of truth
12
+ if (await policy.increment('user_123', 'seats')) {
13
+ // Add the seat
14
+ }
15
+ ```
16
+
17
+ Limitr is an open-source pricing engine that moves your limits, quotas, and feature gates out of application code and into a declarative policy document.
18
+
19
+ Powered by WebAssembly for deterministic, portable enforcement across Node.js, browsers, Deno, and Bun.
20
+
21
+ ## The Problem
22
+
23
+ Your pricing logic is everywhere:
24
+ - Hardcoded in route handlers
25
+ - Duplicated across services
26
+ - Impossible to change without deploying
27
+ - Breaks when product changes the free tier
28
+
29
+ **Changing "free tier gets 1 seat → 3 seats" shouldn't require a code change.**
30
+
31
+ ## How It Works
32
+
33
+ **1. Define your pricing once**
34
+ ```yaml
35
+ policy:
36
+ credits:
37
+ seat:
38
+ label: Seat
39
+ plans:
40
+ free:
41
+ entitlements:
42
+ seats:
43
+ limit:
44
+ credit: seat
45
+ value: 1
46
+ pro:
47
+ entitlements:
48
+ seats:
49
+ limit:
50
+ credit: seat
51
+ value: 10
22
52
  ```
23
53
 
24
- This breaks down with usage-based pricing, AI products, and self-hosted deployments.
54
+ **2. Enforce everywhere**
55
+ ```typescript
56
+ const policy = await Limitr.new(policyDoc);
25
57
 
26
- **Limitr separates policy from code** so limits are explicit, testable, and easy to evolve.
58
+ // Seat limits
59
+ await policy.allow('user_123', 'seats', 1);
60
+
61
+ // Usage limits
62
+ await policy.allow('user_456', 'ai_tokens', 4200);
63
+
64
+ // Feature gates
65
+ await policy.allow('user_789', 'advanced_analytics');
66
+ ```
67
+
68
+ **3. Change pricing without redeploying**
69
+ Update the policy document. That's it.
27
70
 
28
71
  ## Install
72
+
29
73
  ```bash
30
74
  npm install @formata/limitr
31
75
  ```
32
76
 
33
- ### Initialization
34
- Limitr uses [Stof](https://docs.stof.dev) for policy enforcement (@formata/stof). This is sandboxed WebAssembly, and needs to be initialized once before use.
77
+ ### Initialization (Browser Only)
35
78
 
36
- > This step is for font-end (browser) apps only. For Node.js, Deno, & Bun, this step is handled automatically by Limitr (you can skip this).
79
+ Limitr uses [Stof](https://docs.stof.dev) for policy enforcement, which compiles to WebAssembly. Browser apps need to initialize WASM once before use. **Node.js, Deno, and Bun handle this automaticallyskip this step.**
37
80
 
38
81
  ```typescript
39
82
  // Vite
@@ -41,26 +84,22 @@ import { initStof } from '@formata/stof';
41
84
  import stofWasm from '@formata/stof/wasm?url';
42
85
  await initStof(stofWasm);
43
86
 
44
- // Browser with bundler - Pass WASM explicitly (e.g. @rollup/plugin-wasm)
87
+ // Other bundlers (with WASM plugin)
45
88
  import { initStof } from '@formata/stof';
46
89
  import stofWasm from '@formata/stof/wasm';
47
90
  await initStof(await stofWasm());
48
-
49
- // Node.js, Deno, & Bun - Auto-detects and loads WASM (you can skip this though, Limitr does it)
50
- import { initStof } from '@formata/stof';
51
- await initStof();
52
91
  ```
53
92
 
54
- ## Quick Start (Local)
93
+ ## Quick Start
94
+
55
95
  ```typescript
56
96
  import { Limitr } from '@formata/limitr';
57
97
 
58
- // Define policy (YAML, JSON, TOML, STOF) (load from DB, API, file, etc.)
59
98
  const policy = await Limitr.new(`
60
99
  policy:
61
100
  credits:
62
101
  seat:
63
- description: A single seat in our app.
102
+ label: Seat
64
103
  plans:
65
104
  free:
66
105
  entitlements:
@@ -78,90 +117,124 @@ policy:
78
117
  increment: 1
79
118
  `, 'yaml');
80
119
 
81
- // Create/load customers
82
120
  await policy.createCustomer('user_123', 'free');
83
- await policy.createCustomer('user_456', 'pro');
84
121
 
85
- // Enforce limits
86
- await policy.increment('user_123', 'seats'); // true - succeeds (1/1 used)
87
- await policy.increment('user_123', 'seats'); // false - fails (limit hit)
122
+ await policy.increment('user_123', 'seats'); // true (increment -> shorthand for allow(..) with limit's "increment" value)
123
+ await policy.increment('user_123', 'seats'); // false (limit hit)
124
+ ```
88
125
 
89
- await policy.increment('user_456', 'seats'); // true - succeeds (1/10 used)
90
- await policy.allow('user_456', 'seats', 5); // true - succeeds (6/10 used)
126
+ ## When To Use This
127
+
128
+ - ✅ You have seat-based, usage-based, or hybrid pricing
129
+ - ✅ Your pricing changes more than once a quarter
130
+ - ✅ You support self-hosted deployments
131
+ - ✅ You're building an AI product with token limits
132
+ - ✅ You're tired of pricing logic in 47 different files
133
+ - ✅ Easy to adopt incrementally, wrap existing checks with `policy.allow()` one at a time
134
+
135
+ ## Local vs Cloud
136
+
137
+ ### Local (Open Source)
138
+ Runs entirely in your app. No external calls. Perfect for self-hosted.
139
+
140
+ ```typescript
141
+ const policy = await Limitr.new(policyDoc);
91
142
  ```
92
143
 
93
- ## Common Use Cases
144
+ ### Cloud (Managed + Stripe)
145
+ Hosted version with Stripe integration, customer management, dashboard, and analytics.
94
146
 
95
- **Seat-based plans:**
147
+ ```typescript
148
+ const policy = await Limitr.cloud({
149
+ token: 'limitr_...'
150
+ });
151
+ ```
152
+
153
+ - Change pricing in minutes without redeploys
154
+ - Built-in UI for pricing tables, plan selection, invoices
155
+ - Automatic sync with Stripe
156
+ - Analytics dashboards
157
+
158
+ [Learn more about Limitr Cloud →](https://limitr.dev)
159
+
160
+ ## How It's Different
161
+
162
+ > **Stripe knows:** "User paid for Pro plan"<br/>
163
+ > **Limitr enforces:** "User can create 10 seats, use 1M tokens, and export PDFs"
164
+
165
+ Most apps:
166
+ 1. Check limits in code → `if (user.plan === 'free')`
167
+ 2. Take action
168
+ 3. Send usage to billing system
169
+
170
+ With Limitr:
171
+ 1. Check policy → `await policy.allow('user', 'action')`
172
+ 2. Take action
173
+ 3. Usage tracked automatically (Cloud) or synced on your schedule (Local)
174
+
175
+ ## Real-World Examples
176
+
177
+ **Seat-based SaaS:**
96
178
  ```typescript
97
179
  if (await policy.increment('org_123', 'seats')) {
98
- // Add user to org
180
+ await db.addUserToOrg(userId, orgId);
99
181
  }
100
182
  ```
101
183
 
102
- **Usage-based limits:**
184
+ **AI product with token limits:**
103
185
  ```typescript
104
- if (await policy.allow('user_456', 'chat_ai_tokens', 4200)) {
105
- // allowed: process LLM request
106
- // usage recorded and synced in the background (*Limitr Cloud)
107
- } else {
108
- // denied: limit exceeded
186
+ if (await policy.allow('user_456', 'tokens', estimatedTokens)) {
187
+ const response = await callLLM(prompt);
188
+ // Usage recorded, synced in background
109
189
  }
110
190
  ```
111
191
 
112
- **Feature gates:**
192
+ **Feature gating:**
113
193
  ```typescript
114
- const hasAdvancedFeatures = await policy.allow('user_789', 'advanced_analytics');
194
+ const canExport = await policy.allow('user_789', 'pdf_export');
195
+ if (!canExport) {
196
+ return { error: 'Upgrade to export PDFs' };
197
+ }
115
198
  ```
116
199
 
117
- ## Local vs Cloud
200
+ ## Why Policies Are Better Than Code
118
201
 
119
- ### Local (Open Source)
120
- ```typescript
121
- const policy = await Limitr.new(policyDocument);
122
- ```
123
- - Runs entirely in your app
124
- - No external dependencies
125
- - Perfect for self-hosted deployments
202
+ Update your free tier limit at 3pm on Friday. All users see the new limit and it's enforced instantly. No deploy, no invalidation, no coordination.
126
203
 
127
- ### Cloud (Fully Managed w/Stripe + UI)
128
- ```typescript
129
- const policy = await Limitr.cloud({
130
- token: 'limitr_...'
131
- });
132
- ```
133
- - Syncs policy and customer data automatically
134
- - Stripe integration built-in (no Stripe dependencies in your app)
135
- - UI included (pricing tables, plan selection, invoices, cancel/resume, etc)
136
- - Dashboard for managing plans and customers
137
- - Analytics & events (payments, revenue, margins)
138
- - Create/change pricing in a couple of minutes without redeploys
204
+ **Code:**
205
+ - Scattered across files
206
+ - Requires deploys to change
207
+ - Hard to audit
208
+ - Breaks with typos
139
209
 
140
- [Learn more about Limitr Cloud →](https://limitr.dev)
210
+ **Policy:**
211
+ - Single source of truth
212
+ - Change without deploying
213
+ - Human-readable by customers and auditors
214
+ - Type-safe (compiled to WebAssembly)
215
+
216
+ ## Tech Details
217
+
218
+ - Written in [Stof](https://docs.stof.dev) (compiles to WebAssembly)
219
+ - Microsecond enforcement (no network calls)
220
+ - Runs in Node.js, browsers, Deno, Bun
221
+ - Deterministic (same input = same output)
222
+ - No external dependencies for local mode
223
+ - Offline-first (no API calls required)
141
224
 
142
225
  ## Documentation
143
- - 📖 [Full Documentation](https://formata.gitbook.io/limitr)
226
+
227
+ - 📖 [Full Docs](https://formata.gitbook.io/limitr)
144
228
  - 🚀 [Local Quick Start](https://formata.gitbook.io/limitr/local/quick-start)
145
229
  - ☁️ [Cloud Quick Start](https://formata.gitbook.io/limitr/cloud/quick-start)
146
- - 💬 [Discord Community](https://discord.gg/Up5kxdeXZt)
230
+ - 💬 [Discord](https://discord.gg/Up5kxdeXZt)
147
231
 
148
- ## Who It's For
149
- - AI products with usage-based pricing
150
- - Developer tools with seat or API limits
151
- - SaaS apps that need flexible pricing
152
- - Open-source projects offering paid tiers
153
- - Anyone tired of hardcoding pricing logic
232
+ ## Contributing
154
233
 
155
- ## Built With [Stof](https://docs.stof.dev)
156
- Limitr policies are written in Stof, a data + logic language that compiles to WebAssembly. This makes policies:
157
- - **Deterministic** - same input always produces same output
158
- - **Portable** - runs in Node.js, browsers, Deno, Bun
159
- - **Auditable** - human-readable policy documents
234
+ Issues & PRs: [GitHub](https://github.com/dev-formata-io/limitr)
235
+ Questions: [Discord](https://discord.gg/Up5kxdeXZt)
236
+ Contact: info@limitr.dev
160
237
 
161
238
  ## License
162
- Apache 2.0 - See [LICENSE](LICENSE)
163
239
 
164
- ## Contributing
165
- - Issues & PRs: [GitHub](https://github.com/dev-formata-io/limitr)
166
- - Questions: [Discord](https://discord.gg/Up5kxdeXZt)
167
- - Contact: info@limitr.dev
240
+ Apache 2.0
@@ -1 +1 @@
1
- {"version":3,"file":"limitr.d.ts","sourceRoot":"","sources":["../limitr.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,yBAA6xtoB,CAAC"}
1
+ {"version":3,"file":"limitr.d.ts","sourceRoot":"","sources":["../limitr.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,yBAA+/5qB,CAAC"}