@mariachi/core 0.0.1

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.
@@ -0,0 +1,222 @@
1
+ # Mariachi AI Guide
2
+
3
+ Concise reference for AI assistants generating code on the Mariachi framework. For quick lookups, see [architecture.md](./architecture.md), [patterns.md](./patterns.md), [conventions.md](./conventions.md), and [packages.md](./packages.md). For step-by-step instructions, see the [recipes](./recipes/).
4
+
5
+ ---
6
+
7
+ ## Architecture
8
+
9
+ Three-layer request flow:
10
+
11
+ ```
12
+ HTTP → Facade (FastifyAdapter, auth, rate limit)
13
+ → Controller (Zod validation, communication.call())
14
+ → Service (business logic, DB, cache, events)
15
+ ```
16
+
17
+ Apps map to layers:
18
+
19
+ | App | Layer | Purpose |
20
+ |-----|-------|---------|
21
+ | API app | Facade + Controller | HTTP servers, controllers, auth |
22
+ | Services app | Service | Domain logic, handler registration |
23
+ | Worker app | Background | BullMQ job workers, schedules |
24
+
25
+ ---
26
+
27
+ ## Decision Tree: Which Component to Use
28
+
29
+ **"I need to handle an HTTP request"**
30
+ → Add a controller extending `BaseController`. Register on a server.
31
+
32
+ **"I need to run business logic"**
33
+ → Create a service in your services app. Register a handler via `communication.register()`. Call it from a controller via `communication.call('<domain>.<action>', ctx, input)`.
34
+
35
+ **"I need to run something in the background"**
36
+ → Define a job with a Zod schema and retry config. Enqueue via `jobQueue.enqueue(jobName, data)` from a service.
37
+ → See [recipes/add-background-job.md](./recipes/add-background-job.md).
38
+
39
+ **"I need to react to something that happened"**
40
+ → Use the event bus: `eventBus.publish('user.created', payload)` and `eventBus.subscribe('user.created', handler)`. Adapter: Redis pub/sub or NATS.
41
+
42
+ **"I need to run something on a schedule"**
43
+ → Add a schedule entry: `{ name, cron, jobName, data }`.
44
+
45
+ **"I need to cache data"**
46
+ → Use `cache.getOrSet(key, ttl, fetchFn)` from `@mariachi/cache`. Redis-backed.
47
+
48
+ **"I need to store files"**
49
+ → Use `@mariachi/storage` with S3 adapter.
50
+
51
+ **"I need real-time updates"**
52
+ → Use `@mariachi/realtime` with `WSAdapter`. Supports channels, broadcast, and per-user messaging.
53
+
54
+ **"I need to accept webhooks from a third party"**
55
+ → Create a `WebhookController`. Choose `mode: 'direct'` (sync via communication) or `mode: 'queue'` (async via jobs).
56
+ → See [recipes/add-webhook-endpoint.md](./recipes/add-webhook-endpoint.md).
57
+
58
+ **"I need to integrate with an external service"**
59
+ → Use `defineIntegrationFn()` from `@mariachi/integrations`. See [recipes/add-integration.md](./recipes/add-integration.md).
60
+
61
+ **"I need to wire up and bootstrap an app from scratch"**
62
+ → See [recipes/wiring-and-bootstrap.md](./recipes/wiring-and-bootstrap.md). Shows the full initialization sequence from config to running servers.
63
+
64
+ **"I need to add a new domain entity end-to-end"**
65
+ → See [recipes/add-domain-entity.md](./recipes/add-domain-entity.md).
66
+
67
+ ---
68
+
69
+ ## Package Cheat Sheet
70
+
71
+ ### bootstrap (lifecycle)
72
+
73
+ ```ts
74
+ import { bootstrap } from '@mariachi/lifecycle';
75
+ const { config, logger, startup, shutdown, health } = bootstrap();
76
+ startup.register({ name: 'my-service', priority: 10, fn: async () => { ... } });
77
+ await startup.runAll(logger);
78
+ ```
79
+
80
+ ### communication
81
+
82
+ ```ts
83
+ import { createCommunication } from '@mariachi/communication';
84
+ const communication = createCommunication();
85
+
86
+ // Register a handler (in services app)
87
+ communication.register('users.create', {
88
+ schema: { input: CreateUserInput, output: UserOutput },
89
+ handler: (ctx, input) => UsersService.create(ctx, input),
90
+ });
91
+
92
+ // Call a handler (in API controller)
93
+ const result = await communication.call('users.create', ctx, input);
94
+ ```
95
+
96
+ ### controller (api-facade)
97
+
98
+ ```ts
99
+ import { BaseController, type HttpContext } from '@mariachi/api-facade';
100
+
101
+ export class OrdersController extends BaseController {
102
+ readonly prefix = 'orders';
103
+
104
+ init() {
105
+ this.post(this.buildPath(), this.create);
106
+ this.get(this.buildPath(':id'), this.getById);
107
+ }
108
+
109
+ create = async (ctx: HttpContext, body: unknown) => {
110
+ const input = CreateOrderInput.parse(body);
111
+ return communication.call('orders.create', ctx, input);
112
+ };
113
+ }
114
+ ```
115
+
116
+ ### server (api-facade)
117
+
118
+ ```ts
119
+ import { FastifyAdapter } from '@mariachi/api-facade';
120
+
121
+ const server = new FastifyAdapter({ name: 'public' })
122
+ .withAuth(['session', 'api-key'])
123
+ .withRateLimit({ perUser: 1000, perApiKey: 5000, window: '1h' });
124
+
125
+ server.registerController(new OrdersController());
126
+ await server.listen(3000);
127
+ ```
128
+
129
+ ### database schema
130
+
131
+ ```ts
132
+ import { defineTable } from '@mariachi/database';
133
+ import { column } from '@mariachi/database';
134
+
135
+ export const ordersTable = defineTable('orders', {
136
+ id: column.uuid().primaryKey().defaultRandom(),
137
+ tenantId: column.text().notNull(),
138
+ userId: column.text().notNull(),
139
+ total: column.numeric().notNull(),
140
+ status: column.text().notNull(),
141
+ createdAt: column.timestamp().notNull().defaultNow(),
142
+ updatedAt: column.timestamp().notNull().defaultNow(),
143
+ deletedAt: column.timestamp(),
144
+ });
145
+ ```
146
+
147
+ ### repository (database-postgres)
148
+
149
+ ```ts
150
+ import { DrizzleRepository } from '@mariachi/database-postgres';
151
+ import { orders } from '../compiled-schemas';
152
+
153
+ export class DrizzleOrdersRepository extends DrizzleRepository<Order> {
154
+ constructor(db: DrizzleDb) {
155
+ super(orders, db, { tenantColumn: 'tenantId' });
156
+ }
157
+ }
158
+ ```
159
+
160
+ Inherited methods: `findById`, `findMany`, `create`, `update`, `softDelete`, `hardDelete`, `paginate`, `count`.
161
+
162
+ ### jobs
163
+
164
+ ```ts
165
+ import { z } from 'zod';
166
+
167
+ export const ProcessOrderJob = {
168
+ name: 'orders.process',
169
+ schema: z.object({ orderId: z.string() }),
170
+ retry: { attempts: 3, backoff: 'exponential' as const },
171
+ handler: async (data, ctx) => { ... },
172
+ };
173
+ ```
174
+
175
+ ### events
176
+
177
+ ```ts
178
+ const eventBus = createEventBus({ adapter: 'redis', url: process.env.REDIS_URL });
179
+ await eventBus.publish('order.created', { orderId: '123' });
180
+ await eventBus.subscribe('order.created', async (event) => { ... });
181
+ ```
182
+
183
+ ### cache
184
+
185
+ ```ts
186
+ const cache = createCache({ adapter: 'redis', url: process.env.REDIS_URL });
187
+ const user = await cache.getOrSet(
188
+ cache.key('users', userId),
189
+ 3600,
190
+ () => repo.findById(ctx, userId),
191
+ );
192
+ ```
193
+
194
+ ### testing
195
+
196
+ ```ts
197
+ import { createTestHarness, createTestContext, TestRepository } from '@mariachi/testing';
198
+
199
+ const harness = createTestHarness();
200
+ const ctx = createTestContext();
201
+ const repo = new TestRepository<User>();
202
+ ```
203
+
204
+ ---
205
+
206
+ ## Common Gotchas
207
+
208
+ 1. **Communication handlers must be registered before `call()`**. Call your handler registration (e.g. `registerServiceHandlers(communication)`) before starting the API server.
209
+
210
+ 2. **No in-memory job adapter exists**. The worker uses BullMQ. Use `@mariachi/testing`'s `TestJobQueue` in tests.
211
+
212
+ 3. **`createCommunication()` returns an `InProcessAdapter`**. The communication layer is in-process only.
213
+
214
+ 4. **Soft deletes are the default**. `DrizzleRepository.softDelete()` sets `deletedAt`. All queries automatically filter out soft-deleted rows. Use `hardDelete()` only when explicitly needed.
215
+
216
+ 5. **Tenant isolation is automatic in `DrizzleRepository`**. When `tenantColumn` is set and `ctx.tenantId` is present, all queries are scoped to that tenant.
217
+
218
+ 6. **`bootstrap()` registers Config and Logger in the DI container**. Other services pull them via `getContainer().resolve(KEYS.Logger)`.
219
+
220
+ 7. **Controller route handlers receive `(ctx, body, params, query)`**. Use the controller's handler signature, not raw Fastify request.
221
+
222
+ 8. **Some planned adapters are not implemented**. This guide reflects actual code only (Postgres, Redis, BullMQ, etc.).
@@ -0,0 +1,110 @@
1
+ # Mariachi Framework Architecture
2
+
3
+ Mariachi is an LLM-optimized TypeScript backend framework with adapter-based abstractions. It provides a modular structure where external dependencies (databases, caches, message queues, third-party APIs) are hidden behind config-driven adapters, enabling vendor independence and testability.
4
+
5
+ ## Three-Layer Architecture
6
+
7
+ Requests flow through three layers: **Facade** (HTTP/entry), **Controller** (routing and validation), and **Service** (business logic). Controllers delegate to services via the communication layer.
8
+
9
+ ```mermaid
10
+ flowchart TB
11
+ subgraph Facade
12
+ HTTP[HTTP Request]
13
+ Fastify[FastifyAdapter]
14
+ Auth[Auth Strategies]
15
+ RateLimit[Rate Limiting]
16
+ end
17
+
18
+ subgraph Controller
19
+ Ctrl[Controller]
20
+ Validation[Input Validation]
21
+ end
22
+
23
+ subgraph Communication
24
+ Comm[Communication Layer]
25
+ Middleware[Middleware Pipeline]
26
+ end
27
+
28
+ subgraph Service
29
+ Svc[Service]
30
+ BusinessLogic[Business Logic]
31
+ end
32
+
33
+ HTTP --> Fastify
34
+ Fastify --> Auth
35
+ Auth --> RateLimit
36
+ RateLimit --> Ctrl
37
+ Ctrl --> Validation
38
+ Validation --> Comm
39
+ Comm --> Middleware
40
+ Middleware --> Svc
41
+ Svc --> BusinessLogic
42
+ ```
43
+
44
+ | Layer | Responsibility |
45
+ |-------|----------------|
46
+ | **Facade** | HTTP server (Fastify), auth strategies, rate limiting, route registration |
47
+ | **Controller** | Parse/validate input, call communication layer, return response |
48
+ | **Service** | Business logic, database access, external integrations |
49
+
50
+ ## Package Overview
51
+
52
+ | Package | Purpose |
53
+ |---------|---------|
54
+ | `@mariachi/core` | Shared types, errors, context, container |
55
+ | `@mariachi/config` | Typed config, secrets, feature flags |
56
+ | `@mariachi/observability` | Logging (Pino), tracing (OTEL), metrics, error tracking |
57
+ | `@mariachi/lifecycle` | Startup/shutdown, health checks, bootstrap |
58
+ | `@mariachi/communication` | Inter-module communication, middleware pipeline |
59
+ | `@mariachi/database` | Drizzle ORM, PostgreSQL, repositories |
60
+ | `@mariachi/cache` | Redis, in-memory, distributed locks |
61
+ | `@mariachi/events` | Event bus, Redis Pub/Sub |
62
+ | `@mariachi/jobs` | BullMQ job queue, workers, scheduler |
63
+ | `@mariachi/auth` | JWT, API keys, RBAC |
64
+ | `@mariachi/tenancy` | Multi-tenant isolation |
65
+ | `@mariachi/rate-limit` | Redis sliding window rate limiting |
66
+ | `@mariachi/audit` | Append-only audit logging |
67
+ | `@mariachi/api-facade` | Fastify server adapter, auth strategies |
68
+ | `@mariachi/storage` | S3, local file storage |
69
+ | `@mariachi/notifications` | Email (Resend), in-app notifications |
70
+ | `@mariachi/billing` | Stripe billing, webhooks |
71
+ | `@mariachi/search` | Typesense, full-text search |
72
+ | `@mariachi/ai` | AI SDK, sessions, tools, prompts, agent loops |
73
+ | `@mariachi/integrations` | Third-party integration pattern |
74
+ | `@mariachi/testing` | In-memory test doubles, factories |
75
+ | `@mariachi/create` | Scaffolding, validation |
76
+ | `@mariachi/cli` | CLI binary |
77
+
78
+ ## Monolith vs Microservice
79
+
80
+ Mariachi is designed as a **modular monolith**. All packages can live in one codebase and share the same process. The communication layer (`@mariachi/communication`) uses an in-process adapter by default, routing procedure calls directly to registered handlers.
81
+
82
+ For future scaling, the communication layer can be swapped for a transport adapter (e.g., message queue, gRPC) without changing controllers or services. The framework does not prescribe microservices; teams can extract services later if needed.
83
+
84
+ ## Adapter Pattern
85
+
86
+ External dependencies are abstracted behind adapters. Each package exposes a factory (e.g., `createCache`, `createSearch`) that selects the implementation based on config.
87
+
88
+ ```mermaid
89
+ flowchart LR
90
+ subgraph Config
91
+ C[Config]
92
+ end
93
+
94
+ subgraph Factory
95
+ F[createX]
96
+ end
97
+
98
+ subgraph Adapters
99
+ A1[RedisAdapter]
100
+ A2[MemoryAdapter]
101
+ end
102
+
103
+ C --> F
104
+ F -->|adapter: redis| A1
105
+ F -->|adapter: memory| A2
106
+ ```
107
+
108
+ **Example:** `createSearch(config)` returns `TypesenseSearchAdapter` when `config.adapter === 'typesense'`, or `MemorySearchAdapter` when `config.adapter === 'memory'`. Tests use in-memory adapters; production uses Redis, PostgreSQL, Stripe, etc.
109
+
110
+ Adapters implement a common interface. The factory is the single place that maps config to implementation, keeping application code vendor-agnostic.
@@ -0,0 +1,109 @@
1
+ # Mariachi Conventions
2
+
3
+ ## TypeScript and ESM
4
+
5
+ - **All packages use ESM** (`"type": "module"` in every `package.json`)
6
+ - **Build tool:** tsup (ESM + CJS dual output, declaration files, sourcemaps)
7
+ - **TypeScript:** strict mode, ES2022 target, `"moduleResolution": "bundler"`
8
+ - **Relative imports are extensionless:** `import { foo } from './bar'` (not `'./bar.js'`). The `"moduleResolution": "bundler"` tsconfig setting and tsup handle resolution.
9
+ - **Cross-package imports use bare specifiers:** `import { Context } from '@mariachi/core'` (no extension)
10
+ - **All packages export from `src/index.ts`** as the single public entry point
11
+
12
+ ### tsup Config (every package)
13
+
14
+ ```ts
15
+ export default defineConfig({
16
+ entry: ['src/index.ts'],
17
+ format: ['esm', 'cjs'],
18
+ dts: true,
19
+ clean: true,
20
+ sourcemap: true,
21
+ splitting: false,
22
+ });
23
+ ```
24
+
25
+ ### tsconfig (every package)
26
+
27
+ Extend a shared base or use standard options. No additional compiler options needed unless the package has special requirements.
28
+
29
+ ## File Organization
30
+
31
+ - One file per concern: `types.ts`, `schema.ts`, `adapter.ts`, `middleware.ts`
32
+ - Adapters go in `src/adapters/` subdirectory
33
+ - Middleware goes in `src/middleware/` subdirectory
34
+ - Schema definitions go in `src/schema/` subdirectory
35
+ - Tests go alongside source in `test/` or `__tests__/` subdirectory
36
+
37
+ ## Dependency Rules
38
+
39
+ - `@mariachi/core` has zero internal dependencies (only `zod`)
40
+ - Other packages may depend on `@mariachi/core` freely
41
+ - Packages should depend on abstractions, not implementations (e.g., depend on `@mariachi/database`, not `@mariachi/database-postgres`)
42
+ - Apps may depend on any package
43
+ - Circular dependencies between packages are not allowed
44
+
45
+ ## Error Handling
46
+
47
+ - Always throw typed errors extending `MariachiError` from `@mariachi/core`
48
+ - Each package has its own error class: `DatabaseError`, `AuthError`, `CacheError`, etc.
49
+ - Never throw raw `Error` or string exceptions across package boundaries
50
+ - Errors map to HTTP status codes via `errorToHttpStatus()` in the API facade layer
51
+
52
+ ## Anti-Patterns (Do Not Do These)
53
+
54
+ **Do not import services from controllers.**
55
+ Controllers must only call `communication.call()`. Never import a service or its handler directly.
56
+
57
+ ```ts
58
+ // WRONG
59
+ import { UsersService } from '../../services/users/users.service';
60
+ const result = await UsersService.create(ctx, input);
61
+
62
+ // CORRECT
63
+ const result = await communication.call('users.create', ctx, input);
64
+ ```
65
+
66
+ **Do not use `process.env` outside `@mariachi/config`.**
67
+ All configuration flows through `loadConfig()` and `useConfig()`. Direct env access scatters configuration and bypasses validation.
68
+
69
+ ```ts
70
+ // WRONG
71
+ const dbUrl = process.env.DATABASE_URL;
72
+
73
+ // CORRECT
74
+ const config = useConfig();
75
+ const dbUrl = config.database.url;
76
+ ```
77
+
78
+ Exception: `PORT`, `ADMIN_PORT`, `WEBHOOK_PORT` in app entry points are acceptable since they're startup-only values.
79
+
80
+ **Do not skip context propagation.**
81
+ Every operation takes a `Context` as its first argument. Never create a new context mid-flow or drop context between layers.
82
+
83
+ ```ts
84
+ // WRONG
85
+ const user = await UsersService.create({ email, name });
86
+
87
+ // CORRECT
88
+ const user = await UsersService.create(ctx, { email, name });
89
+ ```
90
+
91
+ **Do not expose the Drizzle client directly.**
92
+ All database access goes through repository classes that extend `DrizzleRepository`. The ORM is an implementation detail.
93
+
94
+ **Do not hardcode adapter choices.**
95
+ Use factory functions (`createCache`, `createSearch`, `createJobQueue`) with config. Never instantiate adapters directly in application code.
96
+
97
+ ```ts
98
+ // WRONG
99
+ const cache = new RedisCacheAdapter({ url: 'redis://localhost' });
100
+
101
+ // CORRECT
102
+ const cache = createCache({ adapter: 'redis', url: config.redis.url });
103
+ ```
104
+
105
+ **Do not write migration files by hand.**
106
+ Schema changes go through the `defineTable` DSL in `@mariachi/database`. Migrations are generated by `drizzle-kit`.
107
+
108
+ **Do not register the same procedure name twice.**
109
+ Communication procedure names (`users.create`, `billing.charge`, etc.) must be globally unique. Duplicate registrations will silently overwrite each other.
@@ -0,0 +1,82 @@
1
+ # Notifications, Realtime, and NATS
2
+
3
+ > **Date:** 2026-02-24
4
+ > **Status:** Draft
5
+ > **Scope:** `@mariachi/events`, `@mariachi/notifications`, `@mariachi/realtime` (new)
6
+
7
+ ---
8
+
9
+ ## Problem
10
+
11
+ The current architecture has unclear boundaries between three distinct concerns:
12
+
13
+ 1. **Internal domain events** — service-to-service messaging (`@mariachi/events`, Redis Pub/Sub only)
14
+ 2. **Transactional notifications** — user-facing messages (`@mariachi/notifications`, email only, no queue, no multi-channel)
15
+ 3. **Realtime delivery** — pushing state to connected clients (does not exist)
16
+
17
+ There is no way to push updates to a connected browser. Transactional notifications are fire-and-forget with no retry. The event bus has no durable delivery option and no way to expose topics to authenticated clients.
18
+
19
+ ---
20
+
21
+ ## Architecture
22
+
23
+ ```
24
+ ┌─────────────────────────────────┐
25
+ │ NATS Server │
26
+ │ (subjects, JetStream, auth) │
27
+ └──────────┬──────────┬────────────┘
28
+ │ │
29
+ ┌───────────────┘ └───────────────┐
30
+ │ │
31
+ ┌──────────▼──────────┐ ┌───────────▼──────────┐
32
+ │ @mariachi/events │ │ @mariachi/realtime │
33
+ │ (internal pub/sub) │ │ (client-facing) │
34
+ │ │ │ │
35
+ │ Adapters: │ │ WebSocket / SSE │
36
+ │ - Redis Pub/Sub │ │ NATS bridge │
37
+ │ - NATS │◄──────────────────│ Auth via facade │
38
+ │ - NATS JetStream │ subscribes to │ Presence tracking │
39
+ │ (durable) │ internal events │ Channel management │
40
+ └──────────┬──────────┘ └──────────────────────┘
41
+ │ ▲
42
+ │ events trigger │
43
+ │ notifications │ pushes to
44
+ ▼ │ connected clients
45
+ ┌──────────────────────────────────┐ │
46
+ │ @mariachi/notifications │ │
47
+ │ (transactional, multi-channel) │ │
48
+ │ │ │
49
+ │ 1. Receive notification intent │ │
50
+ │ 2. Check user preferences (DB) │ │
51
+ │ 3. Enqueue via @mariachi/jobs │ │
52
+ │ 4. Deliver via channel: │ │
53
+ │ - Email (Resend) │ │
54
+ │ - SMS (Twilio) │ │
55
+ │ - Push (FCM) │ │
56
+ │ - In-App (DB write) ──────────┼──────────────────┘
57
+ └──────────────────────────────────┘
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Summary of Changes
63
+
64
+ | Package | Change |
65
+ |---|---|
66
+ | `@mariachi/events` | Add `NATSEventBusAdapter` + `NATSJetStreamAdapter`, update factory, add `nats` dep |
67
+ | `@mariachi/notifications` | Rewrite as multi-channel router. Add `notify()` method, SMS/Push adapters, job-based delivery, delivery tracking DB schema, user preference resolution |
68
+ | `@mariachi/realtime` | **New package.** WebSocket/SSE server, NATS bridge, auth integration, presence, channel management. Redis-backed connection state |
69
+ | `@mariachi/jobs` | Add notification delivery job definition |
70
+ | `@mariachi/auth` | Channel authorization support (can user subscribe to this realtime channel?) |
71
+
72
+ ### Implementation order
73
+
74
+ 1. `@mariachi/events` — add NATS adapter (no breaking changes)
75
+ 2. `@mariachi/notifications` — rewrite with multi-channel routing + job queue delivery
76
+ 3. `@mariachi/realtime` — new package, depends on events + auth + cache
77
+ 4. Wire in-app notifications → realtime push
78
+ 5. Add NATS subject naming convention enforcement to `@mariachi/create` validator
79
+
80
+ ---
81
+
82
+ For full design details (NATS adapter code, notification types, realtime API, client protocol), see the framework repository's `docs/improvements/` or the source ADR.
@@ -0,0 +1,70 @@
1
+ # Integrations
2
+
3
+ How to add and configure third-party integrations in Mariachi.
4
+
5
+ ## How to Add an Integration
6
+
7
+ 1. **Generate the scaffold** (optional, if using `@mariachi/cli`):
8
+
9
+ ```bash
10
+ mariachi generate integration <name>
11
+ ```
12
+
13
+ 2. **Define credentials** in `integrations/<name>/credentials.ts` using Zod:
14
+
15
+ ```ts
16
+ import { z } from 'zod';
17
+
18
+ export const MyCredentials = z.object({
19
+ apiKey: z.string().min(1),
20
+ // ... other fields
21
+ });
22
+
23
+ export type MyCredentials = z.infer<typeof MyCredentials>;
24
+ ```
25
+
26
+ 3. **Define integration functions** in `integrations/<name>/index.ts` using `defineIntegrationFn`:
27
+
28
+ ```ts
29
+ import { defineIntegrationFn } from '@mariachi/integrations';
30
+ import type { IntegrationContext } from '@mariachi/integrations';
31
+ import { MyCredentials } from './credentials';
32
+ import { InputSchema, OutputSchema } from './types';
33
+
34
+ export interface MyIntegrationContext extends IntegrationContext {
35
+ credentials: MyCredentials;
36
+ }
37
+
38
+ export const myAction = defineIntegrationFn({
39
+ name: 'my.action',
40
+ input: InputSchema,
41
+ output: OutputSchema,
42
+ handler: async (input, ctx) => {
43
+ const creds = (ctx as MyIntegrationContext).credentials;
44
+ // Call external API with creds
45
+ return result;
46
+ },
47
+ retry: { attempts: 3, backoff: 'exponential' },
48
+ });
49
+ ```
50
+
51
+ 4. **Register in the registry** (optional):
52
+
53
+ ```ts
54
+ registry.register({
55
+ name: 'my',
56
+ description: 'My integration',
57
+ credentialSchema: MyCredentials,
58
+ functions: ['my.action'],
59
+ });
60
+ ```
61
+
62
+ ## Credential Requirements
63
+
64
+ - Credentials are validated with Zod schemas.
65
+ - Store secrets via `@mariachi/config` (e.g., env adapter); never hardcode.
66
+ - Each integration defines its own credential schema.
67
+
68
+ ## Step-by-step recipe
69
+
70
+ For a full walkthrough with credentials, client, types, and tests, see [recipes/add-integration.md](./recipes/add-integration.md).
@@ -0,0 +1,66 @@
1
+ # Mariachi Packages
2
+
3
+ Quick reference for all 28 packages. Use this to decide which package to reach for.
4
+
5
+ ## Foundation
6
+
7
+ | Package | Use when you need... | Factory / Entry |
8
+ |---------|---------------------|-----------------|
9
+ | `@mariachi/core` | Types, errors, context, DI container, `Result<T,E>` | `getContainer()`, `createContext()`, `KEYS` |
10
+ | `@mariachi/config` | Typed config from env, secrets, feature flags | `loadConfig()`, `useConfig()` |
11
+ | `@mariachi/observability` | Logging, tracing, metrics, error tracking | `createObservability(config)` |
12
+ | `@mariachi/lifecycle` | App bootstrap, startup/shutdown hooks, health checks | `bootstrap()` |
13
+
14
+ ## Communication & API
15
+
16
+ | Package | Use when you need... | Factory / Entry |
17
+ |---------|---------------------|-----------------|
18
+ | `@mariachi/communication` | Inter-module procedure calls with middleware | `createCommunication()` |
19
+ | `@mariachi/api-facade` | HTTP servers with auth, rate limiting, controllers | `new FastifyAdapter()`, `BaseController` |
20
+ | `@mariachi/server` | Low-level Fastify adapter (used by api-facade) | `FastifyServerAdapter` |
21
+ | `@mariachi/webhooks` | Inbound webhook endpoints with auth and logging | `WebhookController`, `WebhookServer` |
22
+
23
+ ## Data & Storage
24
+
25
+ | Package | Use when you need... | Factory / Entry |
26
+ |---------|---------------------|-----------------|
27
+ | `@mariachi/database` | Schema definitions, repository interface, types | `defineTable()`, `column`, `Database` |
28
+ | `@mariachi/database-postgres` | PostgreSQL + Drizzle ORM implementation | `createPostgresDatabase()`, `DrizzleRepository` |
29
+ | `@mariachi/cache` | Redis caching, distributed locks, memoization | `createCache()`, `createLock()` |
30
+ | `@mariachi/storage` | File/object storage (S3, local) | `Storage`, `DefaultStorage` |
31
+
32
+ ## Async & Events
33
+
34
+ | Package | Use when you need... | Factory / Entry |
35
+ |---------|---------------------|-----------------|
36
+ | `@mariachi/events` | Pub/sub event bus (Redis or NATS) | `createEventBus()` |
37
+ | `@mariachi/jobs` | Background job queue and scheduling (BullMQ) | `createJobQueue()`, `defineJob` |
38
+ | `@mariachi/realtime` | WebSocket connections, channels, broadcasting | `Realtime`, `WSAdapter` |
39
+
40
+ ## Security & Access
41
+
42
+ | Package | Use when you need... | Factory / Entry |
43
+ |---------|---------------------|-----------------|
44
+ | `@mariachi/auth` | JWT, API keys, OAuth, RBAC | `createAuth()`, `createAuthorization()` |
45
+ | `@mariachi/auth-clerk` | Clerk authentication, webhook verification (Svix) | `createClerkAuth()`, `ClerkWebhookController` |
46
+ | `@mariachi/tenancy` | Multi-tenant isolation (subdomain, header, JWT) | `createTenantResolver()`, `createTenancyMiddleware()` |
47
+ | `@mariachi/rate-limit` | Redis sliding-window rate limiting | `createRateLimiter()` |
48
+
49
+ ## Domain Features
50
+
51
+ | Package | Use when you need... | Factory / Entry |
52
+ |---------|---------------------|-----------------|
53
+ | `@mariachi/billing` | Stripe payments, subscriptions, credits, webhooks | `createBilling()`, `StripeAdapter` |
54
+ | `@mariachi/notifications` | Email (Resend), SMS, push, in-app notifications | `Notifications`, `createEmailAdapter()` |
55
+ | `@mariachi/search` | Full-text search (Typesense) | `createSearch()` |
56
+ | `@mariachi/ai` | AI/LLM sessions, tools, prompts, agent loops | `createAI()`, `runAgent()` |
57
+ | `@mariachi/audit` | Append-only audit logging | `createAuditLogger()` |
58
+ | `@mariachi/integrations` | Third-party integration pattern | `defineIntegrationFn()` |
59
+
60
+ ## Tooling
61
+
62
+ | Package | Use when you need... | Factory / Entry |
63
+ |---------|---------------------|-----------------|
64
+ | `@mariachi/testing` | In-memory test doubles, factories | `createTestHarness()`, `TestRepository` |
65
+ | `@mariachi/create` | Code scaffolding and validation rules | `mariachi generate` |
66
+ | `@mariachi/cli` | CLI binary | `mariachi init/generate/validate` |