@shaferllc/keel 0.83.0 → 0.83.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.
Files changed (51) hide show
  1. package/README.md +2 -2
  2. package/dist/billing/billable.d.ts +2 -3
  3. package/dist/billing/billable.js +2 -3
  4. package/dist/billing/billing.config.stub +1 -1
  5. package/dist/billing/builder.d.ts +2 -2
  6. package/dist/billing/builder.js +2 -2
  7. package/dist/billing/provider.d.ts +1 -1
  8. package/dist/billing/provider.js +2 -2
  9. package/dist/billing/subscription-item.d.ts +0 -1
  10. package/dist/billing/subscription-item.js +0 -1
  11. package/dist/core/decorators.d.ts +5 -7
  12. package/dist/core/decorators.js +5 -7
  13. package/dist/core/exceptions.d.ts +2 -3
  14. package/dist/core/exceptions.js +2 -3
  15. package/dist/core/model-query.d.ts +1 -1
  16. package/dist/core/model-query.js +1 -1
  17. package/dist/core/request-logger.d.ts +2 -2
  18. package/dist/core/request-logger.js +2 -2
  19. package/dist/core/request.js +1 -1
  20. package/dist/core/testing.d.ts +3 -3
  21. package/dist/core/testing.js +3 -3
  22. package/docs/ai-manifest.json +14 -14
  23. package/docs/billing.md +45 -6
  24. package/docs/changelog.md +124 -21
  25. package/docs/cors.md +28 -0
  26. package/docs/decorators.md +2 -6
  27. package/docs/errors.md +1 -1
  28. package/docs/examples/billing.ts +128 -0
  29. package/docs/examples/cors.ts +29 -0
  30. package/docs/examples/hono.ts +29 -0
  31. package/docs/examples/openapi.ts +35 -0
  32. package/docs/examples/orm.ts +30 -0
  33. package/docs/examples/packages.ts +26 -0
  34. package/docs/examples/query-builder.ts +118 -0
  35. package/docs/examples/security.ts +31 -0
  36. package/docs/examples/social-auth.ts +76 -0
  37. package/docs/examples/starter-kits.ts +14 -0
  38. package/docs/examples/watch.ts +10 -0
  39. package/docs/hooks.md +1 -3
  40. package/docs/logger.md +2 -2
  41. package/docs/orm.md +39 -0
  42. package/docs/packages.md +3 -3
  43. package/docs/providers.md +6 -6
  44. package/docs/request-response.md +2 -2
  45. package/docs/starter-kits.md +14 -0
  46. package/docs/testing.md +1 -2
  47. package/docs/validation.md +1 -2
  48. package/docs/watch.md +1 -1
  49. package/llms-full.txt +145 -34
  50. package/llms.txt +13 -2
  51. package/package.json +1 -1
package/README.md CHANGED
@@ -160,7 +160,7 @@ src/core/ The framework
160
160
  src/db/ Database adapters (D1, Postgres, libSQL)
161
161
  src/api/ CRUD REST resources from a model
162
162
  src/openapi/ Generates an OpenAPI spec from the routes
163
- src/billing/ Subscription billing — Stripe + Paddle (Cashier port)
163
+ src/billing/ Subscription billing — Stripe + Paddle
164
164
  src/watch/ The debug dashboard
165
165
  src/mcp/ The MCP server (docs + API for AI agents)
166
166
  src/vite/ The Vite plugin
@@ -228,7 +228,7 @@ See [docs/architecture.md](./docs/architecture.md) for the full picture.
228
228
  | [Internationalization](./docs/i18n.md) | ICU messages, `Intl` formatters, locale detection |
229
229
  | [Pages](./docs/pages.md) | Page-based routing — a file is a route |
230
230
  | [Packages](./docs/packages.md) | Redistributable slices of an app: routes, migrations, commands |
231
- | [Billing](./docs/billing.md) | Subscriptions, charges & webhooks — Stripe + Paddle (Cashier port) |
231
+ | [Billing](./docs/billing.md) | Subscriptions, charges & webhooks — Stripe + Paddle |
232
232
  | [Watch](./docs/watch.md) | Debug dashboard — requests, queries, jobs, logs at `/watch` |
233
233
  | [Views](./docs/views.md) | Hono JSX components, layouts, the View service |
234
234
  | [Templates](./docs/templates.md) | `{{ }}` + `@`-tag templating engine, edge-safe |
@@ -1,7 +1,6 @@
1
1
  /**
2
- * The `Billable` mixin — Cashier's Billable trait, as a TypeScript mixin. Apply
3
- * it to a model to give that model a gateway customer, subscriptions, charges,
4
- * and checkout:
2
+ * The `Billable` mixin — apply it to a model to give that model a gateway
3
+ * customer, subscriptions, charges, and checkout:
5
4
  *
6
5
  * class User extends Billable(Model) {
7
6
  * static table = "users";
@@ -1,7 +1,6 @@
1
1
  /**
2
- * The `Billable` mixin — Cashier's Billable trait, as a TypeScript mixin. Apply
3
- * it to a model to give that model a gateway customer, subscriptions, charges,
4
- * and checkout:
2
+ * The `Billable` mixin — apply it to a model to give that model a gateway
3
+ * customer, subscriptions, charges, and checkout:
5
4
  *
6
5
  * class User extends Billable(Model) {
7
6
  * static table = "users";
@@ -1,7 +1,7 @@
1
1
  import { env } from "@shaferllc/keel/core";
2
2
 
3
3
  /**
4
- * Billing configuration — a Cashier-style port covering Stripe and Paddle.
4
+ * Billing configuration — subscriptions and charges, covering Stripe and Paddle.
5
5
  * Published with `keel vendor:publish --tag billing-config`.
6
6
  */
7
7
  export default {
@@ -1,6 +1,6 @@
1
1
  /**
2
- * The fluent subscription builder — Cashier's `newSubscription(...)->create()`.
3
- * The `Billable` mixin hands it a small `BillableTarget` (so this file needn't
2
+ * The fluent subscription builder — `newSubscription(...).create()`. The
3
+ * `Billable` mixin hands it a small `BillableTarget` (so this file needn't
4
4
  * import the mixin, avoiding a cycle); the builder shapes the request, calls the
5
5
  * gateway, and persists a local `Subscription` synced from the result.
6
6
  *
@@ -1,6 +1,6 @@
1
1
  /**
2
- * The fluent subscription builder — Cashier's `newSubscription(...)->create()`.
3
- * The `Billable` mixin hands it a small `BillableTarget` (so this file needn't
2
+ * The fluent subscription builder — `newSubscription(...).create()`. The
3
+ * `Billable` mixin hands it a small `BillableTarget` (so this file needn't
4
4
  * import the mixin, avoiding a cycle); the builder shapes the request, calls the
5
5
  * gateway, and persists a local `Subscription` synced from the result.
6
6
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Keel Billing — a Cashier-style port covering Stripe and Paddle, shipped as a
2
+ * Keel Billing — subscription billing covering Stripe and Paddle, shipped as a
3
3
  * Keel package. One line in `bootstrap/providers.ts` turns it on:
4
4
  *
5
5
  * app.register(BillingServiceProvider)
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Keel Billing — a Cashier-style port covering Stripe and Paddle, shipped as a
2
+ * Keel Billing — subscription billing covering Stripe and Paddle, shipped as a
3
3
  * Keel package. One line in `bootstrap/providers.ts` turns it on:
4
4
  *
5
5
  * app.register(BillingServiceProvider)
@@ -29,7 +29,7 @@ export class BillingServiceProvider extends PackageProvider {
29
29
  registerDefaultGateways(this.manager);
30
30
  setBilling(this.manager);
31
31
  this.app.instance(BillingManager, this.manager);
32
- // Cashier targets the standard `users` billable table.
32
+ // Default billable table is `users`.
33
33
  this.migrations([billingMigration("users")]);
34
34
  this.publishes({ [join(here, "billing.config.stub")]: "config/billing.ts" }, "billing-config");
35
35
  }
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * One line of a subscription — a single price and its quantity. A single-price
3
3
  * subscription has exactly one item; a multi-product subscription has several.
4
- * Mirrors Cashier's `subscription_items` table.
5
4
  */
6
5
  import { Model } from "../core/model.js";
7
6
  import type { Casts } from "../core/casts.js";
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * One line of a subscription — a single price and its quantity. A single-price
3
3
  * subscription has exactly one item; a multi-product subscription has several.
4
- * Mirrors Cashier's `subscription_items` table.
5
4
  */
6
5
  import { Model } from "../core/model.js";
7
6
  export class SubscriptionItem extends Model {
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * Request decorators — attach named, computed values to the current request,
3
- * resolved lazily and memoized for the life of that request. Inspired by
4
- * Fastify's `decorateRequest`, but without its footguns: you register a resolver
5
- * once, and Keel computes it on first access and caches it per request (keyed off
6
- * the Hono context via a WeakMap, so there's no shared-state leak between
7
- * requests and no V8 shape deopt to design around).
3
+ * resolved lazily and memoized for the life of that request. You register a
4
+ * resolver once, and Keel computes it on first access and caches it per request
5
+ * (keyed off the Hono context via a WeakMap, so there's no shared-state leak
6
+ * between requests and no V8 shape deopt to design around).
8
7
  *
9
8
  * decorateRequest("user", async (c) => findUser(c.req.header("authorization")));
10
9
  *
@@ -18,8 +17,7 @@ import type { Context } from "hono";
18
17
  /** Resolves a decorator's value from the current request context. */
19
18
  export type RequestResolver<T = unknown> = (c: Context) => T | Promise<T>;
20
19
  /**
21
- * Register a request decorator. Throws if `name` is already registered — a
22
- * collision guard, like Fastify's.
20
+ * Register a request decorator. Throws if `name` is already registered.
23
21
  */
24
22
  export declare function decorateRequest<T>(name: string, resolver: RequestResolver<T>): void;
25
23
  /** Whether a request decorator has been registered. */
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * Request decorators — attach named, computed values to the current request,
3
- * resolved lazily and memoized for the life of that request. Inspired by
4
- * Fastify's `decorateRequest`, but without its footguns: you register a resolver
5
- * once, and Keel computes it on first access and caches it per request (keyed off
6
- * the Hono context via a WeakMap, so there's no shared-state leak between
7
- * requests and no V8 shape deopt to design around).
3
+ * resolved lazily and memoized for the life of that request. You register a
4
+ * resolver once, and Keel computes it on first access and caches it per request
5
+ * (keyed off the Hono context via a WeakMap, so there's no shared-state leak
6
+ * between requests and no V8 shape deopt to design around).
8
7
  *
9
8
  * decorateRequest("user", async (c) => findUser(c.req.header("authorization")));
10
9
  *
@@ -27,8 +26,7 @@ function bag(c) {
27
26
  return b;
28
27
  }
29
28
  /**
30
- * Register a request decorator. Throws if `name` is already registered — a
31
- * collision guard, like Fastify's.
29
+ * Register a request decorator. Throws if `name` is already registered.
32
30
  */
33
31
  export function decorateRequest(name, resolver) {
34
32
  if (resolvers.has(name)) {
@@ -92,9 +92,8 @@ export declare class ServiceUnavailableException extends HttpException {
92
92
  }
93
93
  /**
94
94
  * Mint a reusable, coded `HttpException` subclass — the ergonomic way to define
95
- * app-specific errors with a stable, machine-readable `code`. Inspired by
96
- * `@fastify/error`. The `message` may carry `%s` placeholders, filled in order
97
- * from the constructor arguments.
95
+ * app-specific errors with a stable, machine-readable `code`. The `message` may
96
+ * carry `%s` placeholders, filled in order from the constructor arguments.
98
97
  *
99
98
  * const InsufficientFunds = createError("E_FUNDS", "Balance too low: need %s", 402);
100
99
  * throw new InsufficientFunds("$40");
@@ -196,9 +196,8 @@ export class ServiceUnavailableException extends HttpException {
196
196
  }
197
197
  /**
198
198
  * Mint a reusable, coded `HttpException` subclass — the ergonomic way to define
199
- * app-specific errors with a stable, machine-readable `code`. Inspired by
200
- * `@fastify/error`. The `message` may carry `%s` placeholders, filled in order
201
- * from the constructor arguments.
199
+ * app-specific errors with a stable, machine-readable `code`. The `message` may
200
+ * carry `%s` placeholders, filled in order from the constructor arguments.
202
201
  *
203
202
  * const InsufficientFunds = createError("E_FUNDS", "Balance too low: need %s", 402);
204
203
  * throw new InsufficientFunds("$40");
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * A model-aware query builder — what `Model.query()` sugar and eager loading are
3
3
  * built on. It wraps the plain `QueryBuilder`, hydrates rows into models (firing
4
- * `retrieved`), and adds the relationship-aware operations Eloquent has and a raw
4
+ * `retrieved`), and adds the relationship-aware operations a query builder lacks and a raw
5
5
  * builder can't: `with()` (nested eager loading), `withCount()`, and existence
6
6
  * filters `has()` / `whereHas()` / `doesntHave()`.
7
7
  *
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * A model-aware query builder — what `Model.query()` sugar and eager loading are
3
3
  * built on. It wraps the plain `QueryBuilder`, hydrates rows into models (firing
4
- * `retrieved`), and adds the relationship-aware operations Eloquent has and a raw
4
+ * `retrieved`), and adds the relationship-aware operations a query builder lacks and a raw
5
5
  * builder can't: `with()` (nested eager loading), `withCount()`, and existence
6
6
  * filters `has()` / `whereHas()` / `doesntHave()`.
7
7
  *
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Per-request logging. `requestLogger()` middleware binds a child logger to each
3
3
  * request with a generated `reqId`, so every log line within that request
4
- * correlates — Fastify's `request.log`, built on Keel's `Logger.child()`. It can
5
- * also log the request start and completion (method, path, status, duration).
4
+ * correlates — built on Keel's `Logger.child()`. It can also log the request
5
+ * start and completion (method, path, status, duration).
6
6
  *
7
7
  * kernel.use(requestLogger()); // in app/Http/Kernel.ts
8
8
  * requestLog().info("charging card"); // anywhere in the request → carries reqId
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Per-request logging. `requestLogger()` middleware binds a child logger to each
3
3
  * request with a generated `reqId`, so every log line within that request
4
- * correlates — Fastify's `request.log`, built on Keel's `Logger.child()`. It can
5
- * also log the request start and completion (method, path, status, duration).
4
+ * correlates — built on Keel's `Logger.child()`. It can also log the request
5
+ * start and completion (method, path, status, duration).
6
6
  *
7
7
  * kernel.use(requestLogger()); // in app/Http/Kernel.ts
8
8
  * requestLog().info("charging card"); // anywhere in the request → carries reqId
@@ -112,7 +112,7 @@ export function html(body, status) {
112
112
  }
113
113
  export function redirect(location, status) {
114
114
  const c = maybeCtx();
115
- // Koa-style `redirect("back")`: bounce to the Referer, or "/" if there isn't one.
115
+ // `redirect("back")`: bounce to the Referer, or "/" if there isn't one.
116
116
  if (location === "back")
117
117
  location = c?.req.header("referer") ?? "/";
118
118
  return c
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * A test client for Keel apps — inject requests without a live server and assert
3
- * on the response, the way Fastify's `inject()` does. Wraps the app's Hono
4
- * instance (which already does fetch-style request injection), adding verb
5
- * helpers with JSON bodies and fluent response assertions.
3
+ * on the response. Wraps the app's Hono instance (which already does fetch-style
4
+ * request injection), adding verb helpers with JSON bodies and fluent response
5
+ * assertions.
6
6
  *
7
7
  * const client = await testClient(app);
8
8
  * const res = await client.post("/users", { email: "a@b.com" });
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * A test client for Keel apps — inject requests without a live server and assert
3
- * on the response, the way Fastify's `inject()` does. Wraps the app's Hono
4
- * instance (which already does fetch-style request injection), adding verb
5
- * helpers with JSON bodies and fluent response assertions.
3
+ * on the response. Wraps the app's Hono instance (which already does fetch-style
4
+ * request injection), adding verb helpers with JSON bodies and fluent response
5
+ * assertions.
6
6
  *
7
7
  * const client = await testClient(app);
8
8
  * const res = await client.post("/users", { email: "a@b.com" });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shaferllc/keel",
3
- "version": "0.83.0",
3
+ "version": "0.83.2",
4
4
  "description": "The house framework for Node.js — a service container, providers, routing, JSX views, and a code-generating console.",
5
5
  "repo": "https://github.com/shaferllc/keel",
6
6
  "generated": "run `npm run build:ai` to regenerate",
@@ -50,9 +50,9 @@
50
50
  {
51
51
  "slug": "billing",
52
52
  "title": "Billing",
53
- "summary": "Keel Billing is a subscription-billing layer — a port of Laravel Cashier — for charging customers, managing subscriptions, and reconciling gateway state through webhooks.",
53
+ "summary": "Keel Billing is a subscription-billing layer for charging customers, managing subscriptions, and reconciling gateway state through webhooks.",
54
54
  "path": "docs/billing.md",
55
- "example": null
55
+ "example": "docs/examples/billing.ts"
56
56
  },
57
57
  {
58
58
  "slug": "broadcasting",
@@ -115,7 +115,7 @@
115
115
  "title": "CORS",
116
116
  "summary": "Cross-Origin Resource Sharing lets browsers on other origins call your API.",
117
117
  "path": "docs/cors.md",
118
- "example": null
118
+ "example": "docs/examples/cors.ts"
119
119
  },
120
120
  {
121
121
  "slug": "database",
@@ -192,7 +192,7 @@
192
192
  "title": "Built on Hono",
193
193
  "summary": "Keel's HTTP layer is Hono — an ultrafast, web-standard router that runs on Node, Cloudflare Workers, Deno, Bun, and more.",
194
194
  "path": "docs/hono.md",
195
- "example": null
195
+ "example": "docs/examples/hono.ts"
196
196
  },
197
197
  {
198
198
  "slug": "hooks",
@@ -269,21 +269,21 @@
269
269
  "title": "OpenAPI",
270
270
  "summary": "Keel OpenAPI generates an OpenAPI 3 spec from your routes and serves Swagger UI to explore it.",
271
271
  "path": "docs/openapi.md",
272
- "example": null
272
+ "example": "docs/examples/openapi.ts"
273
273
  },
274
274
  {
275
275
  "slug": "orm",
276
276
  "title": "ORM",
277
277
  "summary": "Keel's ORM is a compact active record over the query builder: a model is a class pointed at a table, and its rows come back as typed objects with methods.",
278
278
  "path": "docs/orm.md",
279
- "example": null
279
+ "example": "docs/examples/orm.ts"
280
280
  },
281
281
  {
282
282
  "slug": "packages",
283
283
  "title": "Packages",
284
284
  "summary": "A package is a redistributable slice of a Keel app — routes, a UI, config, migrations, console commands — that installs with a single app.register(...).",
285
285
  "path": "docs/packages.md",
286
- "example": null
286
+ "example": "docs/examples/packages.ts"
287
287
  },
288
288
  {
289
289
  "slug": "pages",
@@ -304,7 +304,7 @@
304
304
  "title": "Query Builder",
305
305
  "summary": "Keel's driver-agnostic query builder — build and run SQL by chaining methods off db(table).",
306
306
  "path": "docs/query-builder.md",
307
- "example": null
307
+ "example": "docs/examples/query-builder.ts"
308
308
  },
309
309
  {
310
310
  "slug": "queues",
@@ -353,7 +353,7 @@
353
353
  "title": "Securing SSR apps",
354
354
  "summary": "Two middlewares harden server-rendered apps: securityHeaders() sets the defensive HTTP headers browsers act on, and csrf() blocks cross-site form submissions.",
355
355
  "path": "docs/security.md",
356
- "example": null
356
+ "example": "docs/examples/security.ts"
357
357
  },
358
358
  {
359
359
  "slug": "sessions",
@@ -367,14 +367,14 @@
367
367
  "title": "Social authentication",
368
368
  "summary": "\"Sign in with GitHub / Google / Discord\" — OAuth 2.0, without an SDK.",
369
369
  "path": "docs/social-auth.md",
370
- "example": null
370
+ "example": "docs/examples/social-auth.ts"
371
371
  },
372
372
  {
373
373
  "slug": "starter-kits",
374
374
  "title": "Starter kits",
375
375
  "summary": "",
376
376
  "path": "docs/starter-kits.md",
377
- "example": null
377
+ "example": "docs/examples/starter-kits.ts"
378
378
  },
379
379
  {
380
380
  "slug": "static-files",
@@ -456,9 +456,9 @@
456
456
  {
457
457
  "slug": "watch",
458
458
  "title": "Watch",
459
- "summary": "Keel Watch is a debug dashboard — a Telescope for Keel.",
459
+ "summary": "Keel Watch is a debug dashboard for Keel apps.",
460
460
  "path": "docs/watch.md",
461
- "example": null
461
+ "example": "docs/examples/watch.ts"
462
462
  }
463
463
  ],
464
464
  "api": [
package/docs/billing.md CHANGED
@@ -1,9 +1,8 @@
1
1
  # Billing
2
2
 
3
- Keel Billing is a subscription-billing layer a port of [Laravel
4
- Cashier](https://laravel.com/docs/13.x/billing) for charging customers,
5
- managing subscriptions, and reconciling gateway state through webhooks. It ships
6
- as a Keel [package](./packages.md) and supports two gateways behind one API:
3
+ Keel Billing is a subscription-billing layer for charging customers, managing
4
+ subscriptions, and reconciling gateway state through webhooks. It ships as a
5
+ Keel [package](./packages.md) and supports two gateways behind one API:
7
6
  **Stripe** and **Paddle**.
8
7
 
9
8
  It attaches to a model with a mixin. Your `User` becomes billable, gains a
@@ -206,6 +205,46 @@ resolveBillableUsing(async (customerId) => {
206
205
  });
207
206
  ```
208
207
 
208
+ ## A complete flow
209
+
210
+ From "user signs up" to "they're subscribed", with the fake gateway for tests:
211
+
212
+ ```ts
213
+ import { Model } from "@shaferllc/keel/core";
214
+ import {
215
+ Billable,
216
+ BillingManager,
217
+ FakeGateway,
218
+ setBilling,
219
+ } from "@shaferllc/keel/billing";
220
+
221
+ class User extends Billable(Model) {
222
+ static table = "users";
223
+ declare email: string;
224
+ }
225
+
226
+ // In a test bootstrap:
227
+ const fake = new FakeGateway();
228
+ const manager = new BillingManager({
229
+ default: "fake",
230
+ currency: "usd",
231
+ billableModel: "User",
232
+ webhook: { path: "billing/webhook" },
233
+ gateways: { stripe: { key: "", webhookSecret: "" }, paddle: { key: "", webhookSecret: "" }, fake: {} },
234
+ });
235
+ manager.register("fake", () => fake);
236
+ setBilling(manager);
237
+
238
+ const user = await User.create({ email: "ada@example.com" });
239
+ await user.newSubscription("default", "price_pro").trialDays(14).create();
240
+
241
+ await user.subscribed(); // true
242
+ fake.calls.some((c) => c.method === "createSubscription"); // true
243
+ ```
244
+
245
+ In production you skip the fake manager — `BillingServiceProvider` wires the
246
+ real gateway from `config/billing.ts` and `.env`.
247
+
209
248
  ## Gateway differences
210
249
 
211
250
  | Concern | Stripe | Paddle |
@@ -221,8 +260,8 @@ resolveBillableUsing(async (customerId) => {
221
260
  The migration is gateway-neutral: `subscriptions` (with `gateway`,
222
261
  `provider_id`, `provider_status`, `provider_price`, trial/grace timestamps),
223
262
  `subscription_items`, and columns on `users` (`billing_gateway`,
224
- `billing_customer_id`, `pm_type`, `pm_last_four`, `trial_ends_at`). Cashier
225
- targets the standard `users` billable table.
263
+ `billing_customer_id`, `pm_type`, `pm_last_four`, `trial_ends_at`). The default
264
+ migration targets the standard `users` billable table.
226
265
 
227
266
  ## Testing
228
267