@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.
Files changed (2) hide show
  1. package/README.md +135 -113
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,145 +1,167 @@
1
- # Limitr: Open-Source Monetization Policy
2
- **Limitr is an embedded, open-source policy engine for enforcing plans, limits, and usage in your application.**
3
-
4
- It is designed for AI products, developer tools, and open-source software where monetization logic must be:
5
- - inspectable
6
- - portable
7
- - deterministic
8
- - and not hard-coded into application logic
9
-
10
- > Limitr answers the question:
11
- > **"What is this customer allowed to consume, right now, and what happens if it exceeds the limit?"**
12
-
13
- - πŸ‘‰ Jump to the [Quick Example](#example-seat-based-plan-enforcement-typescript)
14
- - Additional [info and examples](https://docs.stof.dev/applications/limitr)
15
- - [Limitr Cloud](https://limitr.dev)
16
-
17
- ## What Limitr Gives You
18
- - Define plans, entitlements, and limits in a policy document β€” not application code
19
- - Enforce limits locally and offline, embedded directly in your app
20
- - Change limits without redeploying
21
- - Stripe-agnostic and billing-system-agnostic
22
- - Inspectable and auditable by developers and customers
23
- - Event-driven enforcement (usage changes, overages, denials)
24
- - Policy evolves independently from product code
25
- - Built on [Stof](https://docs.stof.dev), an open-source data + logic runtime
26
-
27
- ## What Limitr Is Not
28
- - Not a billing system
29
- - Not a payment processor
30
- - Not a hosted SaaS requirement
31
- - Not a feature-flag system (outside of entitlements)
32
-
33
- Limitr enforces **truth** about usage and limits.
34
- Billing and payments can subscribe to Limitr’s events.
35
-
36
- Limitr is designed to integrate cleanly with existing systems, not replace them.
37
-
38
- ## Why
39
- Most applications implement monetization logic in files like `limits.ts`:
40
- - seat limits
41
- - usage caps
42
- - plan checks
43
- - special cases and overrides
44
-
45
- Over time, this logic:
46
- - becomes tightly coupled to the app
47
- - requires redeploys to change pricing
48
- - is hard to audit or explain
49
- - breaks down with usage-based or AI pricing
50
-
51
- Payments systems (like Stripe) handle money, but not **enforcement**.
52
-
53
- Limitr separates **monetization policy** from application logic, so limits are:
54
- - explicit
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 'jsr:@formata/limitr';
56
+ import { Limitr } from '@formata/limitr';
70
57
 
71
- // Load a Limitr policy from a DB, string, file, API, etc.
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: 'A single seat credit that can be tracked per customer.'
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: 'seat'
69
+ credit: seat
85
70
  value: 1
86
71
  increment: 1
87
- paid:
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: 'seat'
93
- value: 3
76
+ credit: seat
77
+ value: 10
94
78
  increment: 1
95
79
  `, 'yaml');
96
80
 
97
- // Entitlements define features + limits (gated behaviors) on plans.
98
- // Meters are state stored per customer per entitlement.
81
+ // Create/load customers
82
+ await policy.createCustomer('user_123', 'free');
83
+ await policy.createCustomer('user_456', 'pro');
99
84
 
100
- // Create/save/load customers (database, Stripe, etc.)
101
- await policy.addCustomer('cus_free_customer', 'free');
102
- await policy.addCustomer('cus_paid_customer', 'paid');
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
- // Perform entitlement checks, meter usage, etc.
105
- await policy.increment('cus_free_customer', 'seats'); // adds one seat
106
- await policy.increment('cus_paid_customer', 'seats'); // adds one seat
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
- // Add callbacks to the document directly or as a library function
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
- // Will return false and emit an meter-limit event in the doc (if App.meter_limit exists, it will also be called)
117
- if (await policy.increment('cus_free_customer', 'seats')) throw Error("will not get here");
118
- if (await policy.allow('cus_free_customer', 'seats', 2)) throw Error("cannot request 2 additional seats..");
119
- if (await policy.increment('cus_paid_customer', 'seats')) {
120
- // paid customer can add a second seat
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
- throw Error("will not get here");
108
+ // denied: limit exceeded
123
109
  }
124
110
  ```
125
- ```bash
126
- > deno run --allow-all typescript/examples/seats.ts
127
- FREE PLAN SEAT LIMIT HIT, current: 1 requested: 2
128
- FREE PLAN SEAT LIMIT HIT, current: 1 requested: 3
111
+
112
+ **Feature gates:**
113
+ ```typescript
114
+ const hasAdvancedFeatures = await policy.allow('user_789', 'advanced_analytics');
129
115
  ```
130
116
 
131
- ### What's happening here?
132
- - Plans define which entitlements a customer has
133
- - Entitlements define how a meter is allowed to change with limits
134
- - Meters are stored on each customer as state
135
- - Limitr enforces limits and emits events when limits are hit
136
- - The application decides how to respond (deny, warn, bill, notify)
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. See LICENSE for details.
162
+ Apache 2.0 - See [LICENSE](LICENSE)
140
163
 
141
164
  ## Contributing
142
- - Open issues or discussions on [GitHub](https://github.com/dev-formata-io/limitr)
143
- - Chat with us on [Discord](https://discord.gg/Up5kxdeXZt)
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.14",
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.7"
30
+ "@formata/stof": "^0.9.9"
31
31
  },
32
32
  "devDependencies": {
33
33
  "cpy-cli": "^7.0.0",