@formata/limitr 0.5.14 β 0.5.16
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 +135 -113
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,145 +1,167 @@
|
|
|
1
|
-
# Limitr
|
|
2
|
-
**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
- portable
|
|
56
|
-
- testable
|
|
57
|
-
- and easy to evolve
|
|
58
|
-
|
|
59
|
-
## Who Limitr Is For
|
|
60
|
-
Limitr is a good fit if you are:
|
|
61
|
-
- Building an AI or usage-based product
|
|
62
|
-
- Implementing seat-based or credit-based pricing
|
|
63
|
-
- Shipping developer tools or infrastructure
|
|
64
|
-
- Supporting self-hosted or open-source deployments
|
|
65
|
-
- Tired of hardcoding pricing logic in application code
|
|
66
|
-
|
|
67
|
-
## Example: Seat-Based Plan Enforcement (TypeScript [JSR](https://jsr.io/@formata/limitr))
|
|
1
|
+
# Limitr
|
|
2
|
+
**Open-source policy engine for plans, limits, and usage enforcement.**
|
|
3
|
+
|
|
4
|
+
Limitr embeds monetization logic directly in your app. No hard-coded pricing, no redeploys to change limits.
|
|
5
|
+
|
|
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
|
+
```typescript
|
|
19
|
+
if (user.plan === 'free' && user.seats >= 1) {
|
|
20
|
+
throw new Error('Upgrade to add more seats');
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This breaks down with usage-based pricing, AI products, and self-hosted deployments.
|
|
25
|
+
|
|
26
|
+
**Limitr separates policy from code** so limits are explicit, testable, and easy to evolve.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
```bash
|
|
30
|
+
npm install @formata/limitr
|
|
31
|
+
```
|
|
32
|
+
|
|
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.
|
|
35
|
+
|
|
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).
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// Vite
|
|
40
|
+
import { initStof } from '@formata/stof';
|
|
41
|
+
import stofWasm from '@formata/stof/wasm?url';
|
|
42
|
+
await initStof(stofWasm);
|
|
43
|
+
|
|
44
|
+
// Browser with bundler - Pass WASM explicitly (e.g. @rollup/plugin-wasm)
|
|
45
|
+
import { initStof } from '@formata/stof';
|
|
46
|
+
import stofWasm from '@formata/stof/wasm';
|
|
47
|
+
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
|
+
```
|
|
53
|
+
|
|
54
|
+
## Quick Start (Local)
|
|
68
55
|
```typescript
|
|
69
|
-
import { Limitr } from '
|
|
56
|
+
import { Limitr } from '@formata/limitr';
|
|
70
57
|
|
|
71
|
-
//
|
|
72
|
-
// Stof is the default format, but can also be yaml, json, etc.
|
|
58
|
+
// Define policy (YAML, JSON, TOML, STOF) (load from DB, API, file, etc.)
|
|
73
59
|
const policy = await Limitr.new(`
|
|
74
60
|
policy:
|
|
75
61
|
credits:
|
|
76
62
|
seat:
|
|
77
|
-
description:
|
|
63
|
+
description: A single seat in our app.
|
|
78
64
|
plans:
|
|
79
65
|
free:
|
|
80
66
|
entitlements:
|
|
81
67
|
seats:
|
|
82
|
-
description: 'Each customer (user, org, etc.) with this plan can have up to this many seats.'
|
|
83
68
|
limit:
|
|
84
|
-
credit:
|
|
69
|
+
credit: seat
|
|
85
70
|
value: 1
|
|
86
71
|
increment: 1
|
|
87
|
-
|
|
72
|
+
pro:
|
|
88
73
|
entitlements:
|
|
89
74
|
seats:
|
|
90
|
-
description: 'Each customer (user, org, etc.) with this plan can have up to this many seats.'
|
|
91
75
|
limit:
|
|
92
|
-
credit:
|
|
93
|
-
value:
|
|
76
|
+
credit: seat
|
|
77
|
+
value: 10
|
|
94
78
|
increment: 1
|
|
95
79
|
`, 'yaml');
|
|
96
80
|
|
|
97
|
-
//
|
|
98
|
-
|
|
81
|
+
// Create/load customers
|
|
82
|
+
await policy.createCustomer('user_123', 'free');
|
|
83
|
+
await policy.createCustomer('user_456', 'pro');
|
|
99
84
|
|
|
100
|
-
//
|
|
101
|
-
await policy.
|
|
102
|
-
await policy.
|
|
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)
|
|
103
88
|
|
|
104
|
-
//
|
|
105
|
-
await policy.
|
|
106
|
-
|
|
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)
|
|
91
|
+
```
|
|
107
92
|
|
|
108
|
-
|
|
109
|
-
policy.doc.lib('App', 'meter_limit', (json: string) => {
|
|
110
|
-
const record = JSON.parse(json);
|
|
111
|
-
if (record.customer.plan === 'free' && record.entitlement === 'seats') {
|
|
112
|
-
console.log('FREE PLAN SEAT LIMIT HIT, current: ', record.customer.meters.seats.value, ' requested: ', record.invalid_value);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
93
|
+
## Common Use Cases
|
|
115
94
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (await policy.
|
|
119
|
-
|
|
120
|
-
|
|
95
|
+
**Seat-based plans:**
|
|
96
|
+
```typescript
|
|
97
|
+
if (await policy.increment('org_123', 'seats')) {
|
|
98
|
+
// Add user to org
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Usage-based limits:**
|
|
103
|
+
```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)
|
|
121
107
|
} else {
|
|
122
|
-
|
|
108
|
+
// denied: limit exceeded
|
|
123
109
|
}
|
|
124
110
|
```
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
111
|
+
|
|
112
|
+
**Feature gates:**
|
|
113
|
+
```typescript
|
|
114
|
+
const hasAdvancedFeatures = await policy.allow('user_789', 'advanced_analytics');
|
|
129
115
|
```
|
|
130
116
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
117
|
+
## Local vs Cloud
|
|
118
|
+
|
|
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
|
|
126
|
+
|
|
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
|
|
139
|
+
|
|
140
|
+
[Learn more about Limitr Cloud β](https://limitr.dev)
|
|
141
|
+
|
|
142
|
+
## Documentation
|
|
143
|
+
- π [Full Documentation](https://formata.gitbook.io/limitr)
|
|
144
|
+
- π [Local Quick Start](https://formata.gitbook.io/limitr/local/quick-start)
|
|
145
|
+
- βοΈ [Cloud Quick Start](https://formata.gitbook.io/limitr/cloud/quick-start)
|
|
146
|
+
- π¬ [Discord Community](https://discord.gg/Up5kxdeXZt)
|
|
147
|
+
|
|
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
|
|
154
|
+
|
|
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
|
|
137
160
|
|
|
138
161
|
## License
|
|
139
|
-
Apache 2.0
|
|
162
|
+
Apache 2.0 - See [LICENSE](LICENSE)
|
|
140
163
|
|
|
141
164
|
## Contributing
|
|
142
|
-
-
|
|
143
|
-
-
|
|
144
|
-
|
|
145
|
-
> Reach out to info@stof.dev to contact us directly
|
|
165
|
+
- Issues & PRs: [GitHub](https://github.com/dev-formata-io/limitr)
|
|
166
|
+
- Questions: [Discord](https://discord.gg/Up5kxdeXZt)
|
|
167
|
+
- Contact: info@limitr.dev
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formata/limitr",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/main.js",
|
|
6
6
|
"types": "./dist/main.d.ts",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"homepage": "https://limitr.dev",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@formata/stof": "^0.9.
|
|
30
|
+
"@formata/stof": "^0.9.9"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"cpy-cli": "^7.0.0",
|