@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.
- package/.cursor/rules/mariachi.mdc +36 -0
- package/.mariachi/architecture.md +46 -0
- package/.mariachi/conventions.md +109 -0
- package/.mariachi/packages.md +66 -0
- package/.mariachi/patterns.md +71 -0
- package/README.md +42 -0
- package/dist/index.cjs +423 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +211 -0
- package/dist/index.d.ts +211 -0
- package/dist/index.js +362 -0
- package/dist/index.js.map +1 -0
- package/docs/adr/001-adapter-pattern.md +42 -0
- package/docs/ai-guide.md +222 -0
- package/docs/architecture.md +110 -0
- package/docs/conventions.md +109 -0
- package/docs/improvements/20260224231420_notifications_realtime_nats.md +82 -0
- package/docs/integrations.md +70 -0
- package/docs/packages.md +66 -0
- package/docs/patterns.md +71 -0
- package/docs/recipes/add-background-job.md +156 -0
- package/docs/recipes/add-domain-entity.md +248 -0
- package/docs/recipes/add-integration.md +198 -0
- package/docs/recipes/add-webhook-endpoint.md +169 -0
- package/docs/recipes/wiring-and-bootstrap.md +250 -0
- package/docs/runbook.md +84 -0
- package/package.json +38 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Recipe: Add a Webhook Endpoint
|
|
2
|
+
|
|
3
|
+
This walks through creating a webhook endpoint that receives callbacks from third-party services (e.g. Stripe, GitHub). Mariachi's `@mariachi/webhooks` package provides `WebhookController` with two processing modes: `direct` (synchronous via communication layer) and `queue` (asynchronous via job queue).
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Third-party POST → WebhookServer → AuthController.auth() → WebhookController handler
|
|
11
|
+
├── mode: 'direct' → communication.call(procedure, payload)
|
|
12
|
+
└── mode: 'queue' → jobQueue.enqueue(jobName, payload)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
All webhooks are logged via `WebhookLogStore`.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 1. Create an Auth Controller
|
|
20
|
+
|
|
21
|
+
Each webhook controller needs an `AuthController` that verifies the incoming request. Extend either `ApiKeyAuthController` or `OAuthAuthController`.
|
|
22
|
+
|
|
23
|
+
**File:** e.g. `src/webhooks/github-auth.ts`
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import type { RequestContext } from '@mariachi/server';
|
|
27
|
+
import { ApiKeyAuthController } from '@mariachi/webhooks';
|
|
28
|
+
|
|
29
|
+
export class GitHubWebhookAuth extends ApiKeyAuthController {
|
|
30
|
+
readonly provider = 'github';
|
|
31
|
+
protected readonly headerName = 'x-hub-signature-256';
|
|
32
|
+
|
|
33
|
+
protected async verify(signature: string, ctx: RequestContext): Promise<boolean> {
|
|
34
|
+
// Verify the HMAC signature against the webhook secret
|
|
35
|
+
return signature.length > 0;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Auth controller types:**
|
|
41
|
+
- `ApiKeyAuthController` — verifies a header value. Override `headerName` for custom headers.
|
|
42
|
+
- `OAuthAuthController` — verifies a Bearer token.
|
|
43
|
+
- Custom: extend `AuthController` directly and implement `auth(req, ctx)`.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 2. Create the Webhook Controller
|
|
48
|
+
|
|
49
|
+
Extend `WebhookController` with a `prefix`, `auth`, and route definitions in `init()`.
|
|
50
|
+
|
|
51
|
+
**File:** e.g. `src/webhooks/github.controller.ts`
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { WebhookController, type WebhookContext, type WebhookRouteOpts } from '@mariachi/webhooks';
|
|
55
|
+
import { GitHubWebhookAuth } from './github-auth';
|
|
56
|
+
|
|
57
|
+
export class GitHubWebhookController extends WebhookController {
|
|
58
|
+
readonly prefix = 'github';
|
|
59
|
+
readonly auth = new GitHubWebhookAuth();
|
|
60
|
+
|
|
61
|
+
init() {
|
|
62
|
+
this.post(this.buildPath('push'), {
|
|
63
|
+
mode: 'direct',
|
|
64
|
+
procedure: 'github.handlePush',
|
|
65
|
+
ttl: '30d',
|
|
66
|
+
}, this.handlePush);
|
|
67
|
+
|
|
68
|
+
this.post(this.buildPath('issue'), {
|
|
69
|
+
mode: 'queue',
|
|
70
|
+
jobName: 'github.processIssue',
|
|
71
|
+
ttl: '30d',
|
|
72
|
+
}, this.handleIssue);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
handlePush = async (ctx: WebhookContext, body: unknown) => {
|
|
76
|
+
ctx.logger.info('Received GitHub push webhook');
|
|
77
|
+
return body;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
handleIssue = async (ctx: WebhookContext, body: unknown) => {
|
|
81
|
+
ctx.logger.info('Received GitHub issue webhook');
|
|
82
|
+
return body;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Route options (`WebhookRouteOpts`):**
|
|
88
|
+
|
|
89
|
+
| Field | Required | Description |
|
|
90
|
+
|-------|----------|-------------|
|
|
91
|
+
| `mode` | Yes | `'direct'` (sync) or `'queue'` (async) |
|
|
92
|
+
| `procedure` | When `mode: 'direct'` | Communication procedure name |
|
|
93
|
+
| `jobName` | When `mode: 'queue'` | Job name for `@mariachi/jobs` |
|
|
94
|
+
| `ttl` | No | Log retention duration (e.g. `'7d'`, `'30d'`) |
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 3. Register on a WebhookServer
|
|
99
|
+
|
|
100
|
+
**File:** e.g. `src/index.ts`
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { WebhookServer } from '@mariachi/webhooks';
|
|
104
|
+
import { GitHubWebhookController } from './webhooks/github.controller';
|
|
105
|
+
|
|
106
|
+
const webhookServer = new WebhookServer(
|
|
107
|
+
{ name: 'webhooks', defaultTtl: '7d' },
|
|
108
|
+
{ communication, jobQueue, logStore },
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
webhookServer.registerController(new GitHubWebhookController());
|
|
112
|
+
|
|
113
|
+
startup.register({
|
|
114
|
+
name: 'webhook-server',
|
|
115
|
+
priority: 100,
|
|
116
|
+
fn: async () => {
|
|
117
|
+
await webhookServer.listen(WEBHOOK_PORT);
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 4. Register the Handler or Job
|
|
125
|
+
|
|
126
|
+
**For `mode: 'direct'`** — register a communication handler:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
communication.register('github.handlePush', {
|
|
130
|
+
schema: { input: GitHubPushInput, output: z.object({ ok: z.boolean() }) },
|
|
131
|
+
handler: async (ctx, input) => {
|
|
132
|
+
return { ok: true };
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**For `mode: 'queue'`** — register a job:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
export const ProcessGitHubIssueJob = {
|
|
141
|
+
name: 'github.processIssue',
|
|
142
|
+
schema: z.object({ action: z.string(), issue: z.object({ id: z.number() }) }),
|
|
143
|
+
retry: { attempts: 3, backoff: 'exponential' as const },
|
|
144
|
+
handler: async (data, ctx) => {
|
|
145
|
+
ctx.logger.info({ issueId: data.issue.id }, 'Processing GitHub issue');
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## When to Use Direct vs Queue
|
|
153
|
+
|
|
154
|
+
| | Direct (`mode: 'direct'`) | Queue (`mode: 'queue'`) |
|
|
155
|
+
|-|---------------------------|-------------------------|
|
|
156
|
+
| **Use when** | Response must be synchronous | Processing is slow or can be retried |
|
|
157
|
+
| **Backed by** | `communication.call()` | `jobQueue.enqueue()` (BullMQ/Redis) |
|
|
158
|
+
| **Retries** | No built-in retry | BullMQ retry with backoff |
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Checklist
|
|
163
|
+
|
|
164
|
+
- [ ] Auth controller created (extends `ApiKeyAuthController` or `OAuthAuthController`)
|
|
165
|
+
- [ ] Webhook controller created with `prefix`, `auth`, and routes in `init()`
|
|
166
|
+
- [ ] Routes configured with correct `mode`, `procedure`/`jobName`, and optional `ttl`
|
|
167
|
+
- [ ] Controller registered on `WebhookServer`
|
|
168
|
+
- [ ] Communication handler or job registered for each route
|
|
169
|
+
- [ ] Webhook secret stored via `@mariachi/config` (never hardcoded)
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# Recipe: Wiring and Bootstrap
|
|
2
|
+
|
|
3
|
+
This shows the full initialization sequence for a Mariachi application — from loading config to accepting requests. Understanding the wiring order is critical because components depend on each other: communication handlers must be registered before controllers call them, and infrastructure (DB, Redis) must connect before services that use them.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Initialization Order
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
1. Load config loadConfig() or bootstrap()
|
|
11
|
+
2. Create observability createObservability() → logger, tracer, metrics
|
|
12
|
+
3. Register in container container.register(KEYS.Logger, logger) etc.
|
|
13
|
+
4. Create infrastructure createPostgresDatabase(), createCache(), createEventBus()
|
|
14
|
+
5. Register infra in DI container.register(KEYS.Database, db) etc.
|
|
15
|
+
6. Create communication createCommunication()
|
|
16
|
+
7. Register handlers registerServiceHandlers(communication)
|
|
17
|
+
8. Create servers new FastifyAdapter() with auth + rate limiting
|
|
18
|
+
9. Register controllers server.registerController(new XxxController())
|
|
19
|
+
10. Connect infra startup hooks: db.connect(), cache.connect()
|
|
20
|
+
11. Start servers server.listen(port)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Steps 6-7 must happen before 8-9. If a controller calls `communication.call('users.create', ...)` but no handler is registered for `users.create`, it will fail at runtime.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Minimal Bootstrap
|
|
28
|
+
|
|
29
|
+
The simplest wiring uses `bootstrap()` from `@mariachi/lifecycle`, which handles steps 1-3:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { bootstrap } from '@mariachi/lifecycle';
|
|
33
|
+
import { createCommunication } from '@mariachi/communication';
|
|
34
|
+
import { registerServiceHandlers } from './services';
|
|
35
|
+
import { FastifyAdapter } from '@mariachi/api-facade';
|
|
36
|
+
import { UsersController } from './controllers/users.controller';
|
|
37
|
+
|
|
38
|
+
async function main() {
|
|
39
|
+
const { logger, startup, shutdown } = bootstrap();
|
|
40
|
+
|
|
41
|
+
const communication = createCommunication();
|
|
42
|
+
registerServiceHandlers(communication);
|
|
43
|
+
|
|
44
|
+
const server = new FastifyAdapter({ name: 'public' })
|
|
45
|
+
.withAuth(['session', 'api-key'])
|
|
46
|
+
.withRateLimit({ perUser: 1000, perApiKey: 5000, window: '1h' });
|
|
47
|
+
|
|
48
|
+
server.registerController(new UsersController());
|
|
49
|
+
|
|
50
|
+
startup.register({
|
|
51
|
+
name: 'server',
|
|
52
|
+
priority: 100,
|
|
53
|
+
fn: async () => {
|
|
54
|
+
await server.listen(3000);
|
|
55
|
+
logger.info({ port: 3000 }, 'Server started');
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
shutdown.register({
|
|
60
|
+
name: 'server',
|
|
61
|
+
priority: 100,
|
|
62
|
+
fn: () => server.close(),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await startup.runAll(logger);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
main().catch((err) => {
|
|
69
|
+
console.error(err);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Full Bootstrap (with all infrastructure)
|
|
77
|
+
|
|
78
|
+
For a production app that needs database, cache, events, auth, and billing:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { getContainer, KEYS } from '@mariachi/core';
|
|
82
|
+
import { loadConfig } from '@mariachi/config';
|
|
83
|
+
import { createObservability } from '@mariachi/observability';
|
|
84
|
+
import { bootstrap } from '@mariachi/lifecycle';
|
|
85
|
+
import { createPostgresDatabase } from '@mariachi/database-postgres';
|
|
86
|
+
import { createCache } from '@mariachi/cache';
|
|
87
|
+
import { createEventBus } from '@mariachi/events';
|
|
88
|
+
import { createAuth } from '@mariachi/auth';
|
|
89
|
+
import { createRateLimiter } from '@mariachi/rate-limit';
|
|
90
|
+
import { createCommunication } from '@mariachi/communication';
|
|
91
|
+
|
|
92
|
+
async function main() {
|
|
93
|
+
const config = loadConfig();
|
|
94
|
+
|
|
95
|
+
const { logger, tracer, metrics } = createObservability({
|
|
96
|
+
logging: { adapter: 'pino', level: 'info' },
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const container = getContainer();
|
|
100
|
+
container.register(KEYS.Config, config);
|
|
101
|
+
container.register(KEYS.Logger, logger);
|
|
102
|
+
container.register(KEYS.Tracer, tracer);
|
|
103
|
+
container.register(KEYS.Metrics, metrics);
|
|
104
|
+
|
|
105
|
+
const db = createPostgresDatabase({ url: config.database.url });
|
|
106
|
+
container.register(KEYS.Database, db);
|
|
107
|
+
|
|
108
|
+
const cache = createCache({ adapter: 'redis', url: config.redis!.url });
|
|
109
|
+
container.register(KEYS.Cache, cache);
|
|
110
|
+
|
|
111
|
+
const events = createEventBus({ adapter: 'redis', url: config.redis!.url });
|
|
112
|
+
container.register(KEYS.EventBus, events);
|
|
113
|
+
|
|
114
|
+
const auth = createAuth({ adapter: 'jwt', jwtSecret: config.auth!.jwtSecret! });
|
|
115
|
+
container.register(KEYS.Auth, auth);
|
|
116
|
+
|
|
117
|
+
const rateLimiter = createRateLimiter({ adapter: 'redis', url: config.redis!.url });
|
|
118
|
+
container.register(KEYS.RateLimit, rateLimiter);
|
|
119
|
+
|
|
120
|
+
const { startup, shutdown, health } = bootstrap();
|
|
121
|
+
|
|
122
|
+
const communication = createCommunication();
|
|
123
|
+
container.register(KEYS.Communication, communication);
|
|
124
|
+
registerServiceHandlers(communication);
|
|
125
|
+
|
|
126
|
+
const publicServer = new FastifyAdapter({ name: 'public' })
|
|
127
|
+
.withAuth(['session', 'api-key'])
|
|
128
|
+
.withRateLimit({ perUser: 1000, perApiKey: 5000, window: '1h' });
|
|
129
|
+
|
|
130
|
+
publicServer.registerController(new UsersController());
|
|
131
|
+
publicServer.registerController(new OrdersController());
|
|
132
|
+
|
|
133
|
+
startup.register({ name: 'database', priority: 1, fn: () => db.connect() });
|
|
134
|
+
startup.register({ name: 'cache', priority: 2, fn: () => cache.connect() });
|
|
135
|
+
startup.register({ name: 'events', priority: 3, fn: () => events.connect() });
|
|
136
|
+
|
|
137
|
+
startup.register({ name: 'servers', priority: 100, fn: async () => {
|
|
138
|
+
await publicServer.listen(3000);
|
|
139
|
+
logger.info({ port: 3000 }, 'Server started');
|
|
140
|
+
}});
|
|
141
|
+
|
|
142
|
+
shutdown.register({ name: 'servers', priority: 1, fn: () => publicServer.close() });
|
|
143
|
+
shutdown.register({ name: 'events', priority: 10, fn: () => events.disconnect() });
|
|
144
|
+
shutdown.register({ name: 'cache', priority: 20, fn: () => cache.disconnect() });
|
|
145
|
+
shutdown.register({ name: 'database', priority: 30, fn: () => db.disconnect() });
|
|
146
|
+
|
|
147
|
+
await startup.runAll(logger);
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Startup/Shutdown Priorities
|
|
154
|
+
|
|
155
|
+
Hooks run in order of priority (lowest first). Convention:
|
|
156
|
+
|
|
157
|
+
| Priority | Phase | What |
|
|
158
|
+
|----------|-------|------|
|
|
159
|
+
| 1-10 | Infrastructure | Database, cache, event bus connections |
|
|
160
|
+
| 50 | Services | Communication handler registration, event subscribers |
|
|
161
|
+
| 100 | Servers | HTTP server listen, WebSocket server start |
|
|
162
|
+
|
|
163
|
+
For shutdown, use the inverse: stop servers first (priority 1), then services, then infrastructure.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Worker Bootstrap
|
|
168
|
+
|
|
169
|
+
Workers follow the same pattern but with job queues:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
import { bootstrap } from '@mariachi/lifecycle';
|
|
173
|
+
import { createJobQueue } from '@mariachi/jobs';
|
|
174
|
+
import { SendEmailJob } from './jobs/send-email.job';
|
|
175
|
+
import { schedules } from './jobs/schedules';
|
|
176
|
+
|
|
177
|
+
const { config, logger, startup, shutdown } = bootstrap();
|
|
178
|
+
|
|
179
|
+
const jobQueue = createJobQueue({
|
|
180
|
+
adapter: 'bullmq',
|
|
181
|
+
redisUrl: config.redis!.url,
|
|
182
|
+
}, logger);
|
|
183
|
+
|
|
184
|
+
jobQueue.registerJob(SendEmailJob);
|
|
185
|
+
for (const schedule of schedules) {
|
|
186
|
+
jobQueue.register(schedule);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
startup.register({
|
|
190
|
+
name: 'job-queue',
|
|
191
|
+
priority: 10,
|
|
192
|
+
fn: async () => {
|
|
193
|
+
await jobQueue.connect();
|
|
194
|
+
await jobQueue.start();
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
shutdown.register({
|
|
199
|
+
name: 'job-queue',
|
|
200
|
+
priority: 10,
|
|
201
|
+
fn: async () => {
|
|
202
|
+
await jobQueue.stop();
|
|
203
|
+
await jobQueue.disconnect();
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
startup.runAll(logger).catch((err) => {
|
|
208
|
+
logger.error({ err }, 'Worker failed to start');
|
|
209
|
+
process.exit(1);
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## DI Container Keys
|
|
216
|
+
|
|
217
|
+
All well-known keys are defined in `@mariachi/core`:
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
import { KEYS } from '@mariachi/core';
|
|
221
|
+
|
|
222
|
+
KEYS.Config // AppConfig
|
|
223
|
+
KEYS.Logger // Logger
|
|
224
|
+
KEYS.Tracer // TracerAdapter
|
|
225
|
+
KEYS.Metrics // MetricsAdapter
|
|
226
|
+
KEYS.Database // Database / PostgresAdapter
|
|
227
|
+
KEYS.Cache // Cache / CacheClient
|
|
228
|
+
KEYS.EventBus // EventBus
|
|
229
|
+
KEYS.JobQueue // JobQueue
|
|
230
|
+
KEYS.Auth // Auth
|
|
231
|
+
KEYS.Communication // CommunicationLayer
|
|
232
|
+
// ... and others
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Services that extend `Instrumentable` automatically resolve `Logger`, `Tracer`, and `Metrics` from the container.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Checklist
|
|
240
|
+
|
|
241
|
+
- [ ] Config loaded and validated
|
|
242
|
+
- [ ] Logger + tracer + metrics created and registered in container
|
|
243
|
+
- [ ] Infrastructure created (DB, cache, events) and registered in container
|
|
244
|
+
- [ ] Communication layer created
|
|
245
|
+
- [ ] Service handlers registered on communication layer
|
|
246
|
+
- [ ] Servers created with auth and rate limiting
|
|
247
|
+
- [ ] Controllers registered on servers
|
|
248
|
+
- [ ] Startup hooks registered (infra connect at low priority, servers at high priority)
|
|
249
|
+
- [ ] Shutdown hooks registered (reverse order)
|
|
250
|
+
- [ ] `startup.runAll(logger)` called
|
package/docs/runbook.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Mariachi Runbook
|
|
2
|
+
|
|
3
|
+
Operational guide for developing and running Mariachi applications.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- **Node.js** ≥ 20
|
|
8
|
+
- **pnpm** (package manager)
|
|
9
|
+
|
|
10
|
+
## Getting Started
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm install
|
|
14
|
+
pnpm run build
|
|
15
|
+
pnpm run dev
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Running Tests
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm run test
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
For a single run (CI):
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pnpm run test:run
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Adding a New Service
|
|
31
|
+
|
|
32
|
+
Generate a service with handlers and tests (if using `@mariachi/cli`):
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
mariachi generate service <name>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Example:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
mariachi generate service orders
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Creates a domain folder with `orders.service.ts`, `orders.handler.ts`, and tests.
|
|
45
|
+
|
|
46
|
+
## Adding a New Integration
|
|
47
|
+
|
|
48
|
+
Generate an integration scaffold (if using `@mariachi/cli`):
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
mariachi generate integration <name>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Validating
|
|
55
|
+
|
|
56
|
+
Validate project structure and conventions (if using `@mariachi/cli`):
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
mariachi validate
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Optionally specify a path:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
mariachi validate ./apps
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Environment Variables
|
|
69
|
+
|
|
70
|
+
| Variable | Description | Default |
|
|
71
|
+
|----------|-------------|---------|
|
|
72
|
+
| `NODE_ENV` / `ENV` | Environment (`development`, `test`, `production`) | — |
|
|
73
|
+
| `PORT` | Public API port | `3000` |
|
|
74
|
+
| `ADMIN_PORT` | Admin API port | `3001` |
|
|
75
|
+
| `WEBHOOK_PORT` | Webhook server port | `3002` |
|
|
76
|
+
| `DATABASE_URL` | PostgreSQL connection string | — |
|
|
77
|
+
| `DATABASE_ADAPTER` | Database adapter | `postgres` |
|
|
78
|
+
| `DATABASE_POOL_MIN` | Connection pool minimum | `2` |
|
|
79
|
+
| `DATABASE_POOL_MAX` | Connection pool maximum | `10` |
|
|
80
|
+
| `REDIS_URL` | Redis connection string | — |
|
|
81
|
+
| `JWT_SECRET` | JWT signing secret | — |
|
|
82
|
+
| `SESSION_SECRET` | Session secret (fallback for JWT) | — |
|
|
83
|
+
|
|
84
|
+
Configuration is loaded via `@mariachi/config`; avoid using `process.env` directly outside that package.
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mariachi/core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"import": "./dist/index.js",
|
|
9
|
+
"require": "./dist/index.cjs"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/index.cjs",
|
|
13
|
+
"module": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"docs",
|
|
18
|
+
"README.md",
|
|
19
|
+
".mariachi",
|
|
20
|
+
".cursor"
|
|
21
|
+
],
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"zod": "^3.24"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"typescript": "^5.7",
|
|
30
|
+
"tsup": "^8"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsup",
|
|
34
|
+
"dev": "tsup --watch",
|
|
35
|
+
"typecheck": "tsc --noEmit",
|
|
36
|
+
"clean": "rm -rf dist .turbo"
|
|
37
|
+
}
|
|
38
|
+
}
|