117 lines
4.0 KiB
Markdown
117 lines
4.0 KiB
Markdown
# Banking plugin factory
|
|
|
|
## Purpose
|
|
|
|
`createBankingPlugin(options?)` is the package's primary entry point —
|
|
it Zod-validates the supplied options, builds the `BankingService`
|
|
inside the descriptor's `onInit`, bootstraps the default bank inside
|
|
`onReady`, mounts the OpenAPIHono route tree on the host `app` under
|
|
the configured `routePrefix`, and drops every cached reference inside
|
|
`onClose`. The descriptor is shaped against the canonical
|
|
`@suaveplan/api-plugin` `PluginDescriptor` so any host that consumes
|
|
that contract can register the banking plugin without bespoke wiring.
|
|
|
|
## Features
|
|
|
|
- Zod boundary at `createBankingPlugin(options)` — invalid input
|
|
rejects with `BankingError(BANKING.INVALID_OPTIONS)` carrying the
|
|
`ZodError` as `cause`.
|
|
- Declared dependency on the `database` plugin so the topological
|
|
loader runs the database plugin first and the `db` slot is
|
|
populated before `onInit` resolves it.
|
|
- Optional dependencies on `auth`, `audit`, `rate-limit` so a host
|
|
that registers those plugins gets them loaded before banking
|
|
(matching the enterprise audit's future-work additions); when
|
|
absent, the plugin still works (the optional dep contract).
|
|
- Idempotent `initializeDefaultBank` invoked from `onReady` — safe on
|
|
every host start.
|
|
- `decommissionBankingTenant(service, tenantId)` helper exported for
|
|
hosts that orchestrate tenant lifecycle externally (since the
|
|
current `PluginDescriptor` does not yet declare tenant hooks).
|
|
|
|
## Usage: Basic
|
|
|
|
```ts
|
|
import { createBankingPlugin } from "@suaveplan/api-plugin-banking";
|
|
|
|
const plugin = createBankingPlugin();
|
|
registry.register(plugin);
|
|
```
|
|
|
|
## Usage: Advanced
|
|
|
|
Customise the configuration:
|
|
|
|
```ts
|
|
import { createBankingPlugin } from "@suaveplan/api-plugin-banking";
|
|
|
|
const plugin = createBankingPlugin({
|
|
routePrefix: "/api/v1/banking",
|
|
maxAccountsPerUser: 25,
|
|
enableOverdraft: true,
|
|
enableTransfers: true,
|
|
defaultBankName: "Acme Trust",
|
|
defaultSortCode: 234567,
|
|
});
|
|
|
|
await registry.register(plugin);
|
|
await registry.initAll();
|
|
await registry.readyAll();
|
|
```
|
|
|
|
Tenant decommission:
|
|
|
|
```ts
|
|
import {
|
|
decommissionBankingTenant,
|
|
type BankingService,
|
|
} from "@suaveplan/api-plugin-banking";
|
|
|
|
const service = ctx.getService("bankingService") as BankingService;
|
|
const { banks, accounts } = await decommissionBankingTenant(
|
|
service,
|
|
tenantId
|
|
);
|
|
```
|
|
|
|
## API Reference
|
|
|
|
### `createBankingPlugin(options?: BankingPluginOptionsInput): PluginDescriptor<BankingPluginConfig>`
|
|
|
|
Returns a frozen descriptor. The descriptor exposes:
|
|
|
|
- `name: "banking"`
|
|
- `version: "0.1.0"`
|
|
- `dependencies: ["database"]`
|
|
- `optionalDependencies: ["auth", "audit", "rate-limit"]`
|
|
- `tags: ["banking", "accounts", "finance"]`
|
|
- `defaults`: the parsed `BankingPluginConfig`
|
|
- `onInit`, `onReady`, `onClose`
|
|
|
|
### `decommissionBankingTenant(service, tenantId): Promise<{ banks; accounts }>`
|
|
|
|
Convenience wrapper that delegates to
|
|
`BankingService.deleteTenantData`. Returns the count of soft-deleted
|
|
banks and hard-deleted accounts. The deletes are not atomic — if the
|
|
underlying SurQL throws between the two, `BANKING.TENANT_CLEANUP_FAILED`
|
|
surfaces with the partial state visible to the next call.
|
|
|
|
## Implementation Notes
|
|
|
|
The plugin uses `dependencies: ["database"]`, not
|
|
`optionalDependencies` — banking is meaningless without a database,
|
|
and surfacing a typed `BANKING.SERVICE_NOT_AVAILABLE` for the missing-slot
|
|
case at `onInit` time is the canonical genesis pattern (instead of
|
|
silently no-oping).
|
|
|
|
The `emit` helper carries a typed-payload signature constrained by
|
|
`BankingEventMap`. It runtime-casts to the loose `EventBusLike.emit`
|
|
shape because `@suaveplan/types`' `EventBusLike` is intentionally
|
|
untyped — strong typing lives on the per-plugin event map, not the
|
|
bus. The cast is at one place and narrow.
|
|
|
|
`onTenantCreate` is intentionally absent because the upstream
|
|
package's version was a no-op stub. Hosts that need per-tenant
|
|
default-bank provisioning call `service.createBank({ ... site_id })`
|
|
from their own tenant lifecycle hook.
|