Extending Porulle
When Porulle’s bundled features don’t match your case, you extend the framework rather than fork it. Every extension point is a config-time contribution: hooks intercept operations, custom tables share the schema, plugins compose multiple extensions into one config-transform. No subclassing, no monkey-patching.
Hooks Intercept any operation with before/after handlers. Before runs in the transaction; after runs outside it.
Custom Tables Add app-level Drizzle tables that share the org-scoped schema. No plugin wrapper needed.
Payment Adapter Implement the PaymentAdapter contract for any rail — Stripe is the reference implementation.
Testing Plugin test apps, PGlite-backed integration tests, the actor injection pattern.
TypeScript Patterns Result discipline, narrowing CommerceError, plugin context types.
Adopter contracts
Section titled “Adopter contracts”These three contracts are normative — every extension must follow them:
- Plugin Contract — what plugin authors MUST do (actor as required parameter,
Result<T>, org scoping, no body-supplied identity) - Payment Adapter Contract — accurate
amountCaptured, idempotency, timing-safe webhook verification - Security Model — the framework’s threat model and adopter-side responsibilities
Where to next
Section titled “Where to next”- Reference → Plugins —
defineCommercePluginmanifest reference - Reference → Hooks — every available hook key with type signatures
- Concepts → Plugin Architecture — why plugins are config transforms
- Tutorials → Build a Loyalty Plugin — a full plugin walkthrough