@brew-cms/api 0.2.1 → 0.2.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.
package/src/errors.ts DELETED
@@ -1,42 +0,0 @@
1
- import { DomainError } from '@brew-cms/core';
2
-
3
- export interface ApiErrorEnvelope {
4
- error: {
5
- code: string;
6
- message: string;
7
- requestId?: string;
8
- details?: unknown;
9
- };
10
- }
11
-
12
- export function formatErrorResponse(
13
- err: unknown,
14
- requestId?: string
15
- ): { status: number; body: ApiErrorEnvelope } {
16
- if (err instanceof DomainError) {
17
- return {
18
- status: err.statusCode,
19
- body: {
20
- error: {
21
- code: err.code,
22
- message: err.message,
23
- requestId,
24
- details: err.details,
25
- },
26
- },
27
- };
28
- }
29
-
30
- // Fallback for unexpected internal errors: NEVER expose raw stack traces or internal SQL details
31
- const fallbackMessage = err instanceof Error ? err.message : 'An unexpected internal error occurred.';
32
- return {
33
- status: 500,
34
- body: {
35
- error: {
36
- code: 'INTERNAL_ERROR',
37
- message: fallbackMessage,
38
- requestId,
39
- },
40
- },
41
- };
42
- }
@@ -1,41 +0,0 @@
1
- export interface IdempotencyRecord {
2
- key: string;
3
- actorId: string;
4
- responseStatus: number;
5
- responseBody: unknown;
6
- expiresAt: Date;
7
- }
8
-
9
- export class InMemoryIdempotencyStore {
10
- private cache = new Map<string, IdempotencyRecord>();
11
-
12
- get(key: string, actorId: string): IdempotencyRecord | null {
13
- const compositeKey = `${actorId}:${key}`;
14
- const record = this.cache.get(compositeKey);
15
- if (!record) return null;
16
-
17
- if (record.expiresAt < new Date()) {
18
- this.cache.delete(compositeKey);
19
- return null;
20
- }
21
- return record;
22
- }
23
-
24
- set(
25
- key: string,
26
- actorId: string,
27
- responseStatus: number,
28
- responseBody: unknown,
29
- ttlSeconds: number = 3600
30
- ): void {
31
- const compositeKey = `${actorId}:${key}`;
32
- const expiresAt = new Date(Date.now() + ttlSeconds * 1000);
33
- this.cache.set(compositeKey, {
34
- key,
35
- actorId,
36
- responseStatus,
37
- responseBody,
38
- expiresAt,
39
- });
40
- }
41
- }
package/src/router.ts DELETED
@@ -1,538 +0,0 @@
1
- import type {
2
- Actor,
3
- DocumentService,
4
- WorkflowService,
5
- AgentService,
6
- AuditService,
7
- TaxonomyService,
8
- RevisionRepository,
9
- MediaProvider,
10
- MediaAssetRepository,
11
- AgentRepository,
12
- } from '@brew-cms/core';
13
- import type {
14
- SourceRegistry,
15
- IndexingService,
16
- HybridRetrievalService,
17
- CanonicalSourceResolver,
18
- } from '@brew-cms/intelligence';
19
- import { compileContent } from '@brew-cms/content';
20
- import { formatErrorResponse } from './errors.js';
21
- import { InMemoryIdempotencyStore } from './idempotency.js';
22
- import {
23
- CreateDocumentRequestSchema,
24
- UpdateDocumentRequestSchema,
25
- PublishDocumentRequestSchema,
26
- ScheduleDocumentRequestSchema,
27
- CreateTopicRequestSchema,
28
- CreateTagRequestSchema,
29
- CreateSeriesRequestSchema,
30
- } from './validators.js';
31
-
32
- export interface ApiRequest {
33
- method: string;
34
- path: string;
35
- query?: Record<string, string | undefined>;
36
- headers?: Record<string, string | undefined>;
37
- body?: unknown;
38
- actor: Actor;
39
- }
40
-
41
- export interface ApiResponse {
42
- status: number;
43
- headers?: Record<string, string>;
44
- body: unknown;
45
- }
46
-
47
- export interface ApiContext {
48
- documentService: DocumentService;
49
- workflowService: WorkflowService;
50
- agentService: AgentService;
51
- auditService: AuditService;
52
- taxonomyService: TaxonomyService;
53
- revisionRepo: RevisionRepository;
54
- mediaProvider?: MediaProvider;
55
- mediaRepo?: MediaAssetRepository;
56
- agentRepo?: AgentRepository;
57
- idempotencyStore?: InMemoryIdempotencyStore;
58
- sourceRegistry?: SourceRegistry;
59
- indexingService?: IndexingService;
60
- retrievalService?: HybridRetrievalService;
61
- canonicalResolver?: CanonicalSourceResolver;
62
- }
63
-
64
- export async function handleApiRequest(
65
- req: ApiRequest,
66
- ctx: ApiContext
67
- ): Promise<ApiResponse> {
68
- const idempotencyKey = req.headers?.['idempotency-key'];
69
- const idempotencyStore = ctx.idempotencyStore;
70
-
71
- // 1. Idempotency Check for mutations
72
- if (idempotencyKey && req.method !== 'GET' && idempotencyStore) {
73
- const cached = idempotencyStore.get(idempotencyKey, req.actor.id);
74
- if (cached) {
75
- return {
76
- status: cached.responseStatus,
77
- headers: { 'X-Cache-Lookup': 'HIT', 'Idempotency-Key': idempotencyKey },
78
- body: cached.responseBody,
79
- };
80
- }
81
- }
82
-
83
- try {
84
- const response = await dispatchRoute(req, ctx);
85
-
86
- // Save in idempotency store if mutation
87
- if (idempotencyKey && req.method !== 'GET' && idempotencyStore && response.status < 400) {
88
- idempotencyStore.set(idempotencyKey, req.actor.id, response.status, response.body);
89
- }
90
-
91
- return response;
92
- } catch (err: any) {
93
- const errorFormatted = formatErrorResponse(err, req.headers?.['x-request-id']);
94
- return {
95
- status: errorFormatted.status,
96
- headers: { 'Content-Type': 'application/json' },
97
- body: errorFormatted.body,
98
- };
99
- }
100
- }
101
-
102
- async function dispatchRoute(req: ApiRequest, ctx: ApiContext): Promise<ApiResponse> {
103
- const { path: rawPath, method, actor } = req;
104
- const urlPath = rawPath.replace(/\/$/, '');
105
-
106
- // 1. Health Endpoints
107
- if (urlPath === '/api/health/live' && method === 'GET') {
108
- return { status: 200, body: { status: 'live', timestamp: new Date().toISOString() } };
109
- }
110
- if (urlPath === '/api/health/ready' && method === 'GET') {
111
- return { status: 200, body: { status: 'ready', database: 'connected', timestamp: new Date().toISOString() } };
112
- }
113
-
114
- // 2. Documents
115
- if (urlPath === '/api/v1/documents') {
116
- if (method === 'GET') {
117
- const result = await ctx.documentService.listDocuments({
118
- type: req.query?.type as any,
119
- status: req.query?.status as any,
120
- query: req.query?.q,
121
- limit: req.query?.limit ? Number(req.query.limit) : undefined,
122
- });
123
- return { status: 200, body: result };
124
- }
125
-
126
- if (method === 'POST') {
127
- const validated = CreateDocumentRequestSchema.parse(req.body);
128
- const compiled = compileContent(validated.sourceMarkdown, {
129
- frontmatterDefaults: validated.frontmatter,
130
- });
131
-
132
- const res = await ctx.documentService.createDraft(actor, {
133
- type: validated.type,
134
- slug: validated.slug,
135
- title: validated.title,
136
- excerpt: validated.excerpt,
137
- sourceMarkdown: compiled.sourceMarkdown,
138
- frontmatter: compiled.frontmatter,
139
- contentIr: compiled.contentIr,
140
- contentHash: compiled.contentHash,
141
- compilerVersion: compiled.compilerVersion,
142
- wordCount: compiled.wordCount,
143
- readingTimeSeconds: compiled.readingTimeSeconds,
144
- seo: validated.seo,
145
- });
146
-
147
- return { status: 201, body: res };
148
- }
149
- }
150
-
151
- // Document by ID: /api/v1/documents/:id
152
- const docIdMatch = urlPath.match(/^\/api\/v1\/documents\/([^/]+)$/);
153
- if (docIdMatch) {
154
- const id = docIdMatch[1];
155
- if (method === 'GET') {
156
- const doc = await ctx.documentService.getDocument(id);
157
- return { status: 200, body: doc };
158
- }
159
- if (method === 'PUT') {
160
- const validated = UpdateDocumentRequestSchema.parse(req.body);
161
- let compiled: ReturnType<typeof compileContent> | undefined;
162
- if (validated.sourceMarkdown !== undefined) {
163
- compiled = compileContent(validated.sourceMarkdown, {
164
- frontmatterDefaults: validated.frontmatter,
165
- });
166
- }
167
-
168
- const res = await ctx.documentService.updateDraft(actor, id, {
169
- title: validated.title,
170
- slug: validated.slug,
171
- excerpt: validated.excerpt,
172
- sourceMarkdown: compiled?.sourceMarkdown,
173
- frontmatter: compiled?.frontmatter,
174
- contentIr: compiled?.contentIr,
175
- contentHash: compiled?.contentHash,
176
- compilerVersion: compiled?.compilerVersion,
177
- wordCount: compiled?.wordCount,
178
- readingTimeSeconds: compiled?.readingTimeSeconds,
179
- seo: validated.seo,
180
- changeSummary: validated.changeSummary,
181
- });
182
-
183
- return { status: 200, body: res };
184
- }
185
- if (method === 'DELETE') {
186
- await ctx.documentService.deleteDocument(actor, id);
187
- return { status: 200, body: { success: true, id } };
188
- }
189
- }
190
-
191
- // Revisions for document: /api/v1/documents/:id/revisions
192
- const revMatch = urlPath.match(/^\/api\/v1\/documents\/([^/]+)\/revisions$/);
193
- if (revMatch && method === 'GET') {
194
- const id = revMatch[1];
195
- const revisions = await ctx.revisionRepo.listByDocumentId(id);
196
- return { status: 200, body: { revisions } };
197
- }
198
-
199
- // Publish: /api/v1/documents/:id/publish
200
- const publishMatch = urlPath.match(/^\/api\/v1\/documents\/([^/]+)\/publish$/);
201
- if (publishMatch && method === 'POST') {
202
- const id = publishMatch[1];
203
- const validated = PublishDocumentRequestSchema.parse(req.body || {});
204
- const res = await ctx.workflowService.publish(actor, id, validated.revisionId);
205
- return { status: 200, body: res };
206
- }
207
-
208
- // Submit for review: /api/v1/documents/:id/submit
209
- const submitMatch = urlPath.match(/^\/api\/v1\/documents\/([^/]+)\/submit$/);
210
- if (submitMatch && method === 'POST') {
211
- const id = submitMatch[1];
212
- const revisionId = (req.body as any)?.revisionId;
213
- const res = await ctx.workflowService.submitForReview(actor, id, revisionId);
214
- return { status: 200, body: res };
215
- }
216
-
217
- // Approve: /api/v1/documents/:id/approve
218
- const approveMatch = urlPath.match(/^\/api\/v1\/documents\/([^/]+)\/approve$/);
219
- if (approveMatch && method === 'POST') {
220
- const id = approveMatch[1];
221
- const revisionId = (req.body as any)?.revisionId;
222
- const res = await ctx.workflowService.approve(actor, id, revisionId);
223
- return { status: 200, body: res };
224
- }
225
-
226
- // Request Changes: /api/v1/documents/:id/request-changes
227
- const reqChangesMatch = urlPath.match(/^\/api\/v1\/documents\/([^/]+)\/request-changes$/);
228
- if (reqChangesMatch && method === 'POST') {
229
- const id = reqChangesMatch[1];
230
- const reason = (req.body as any)?.reason;
231
- const res = await ctx.workflowService.requestChanges(actor, id, reason);
232
- return { status: 200, body: res };
233
- }
234
-
235
- // Unpublish: /api/v1/documents/:id/unpublish
236
- const unpublishMatch = urlPath.match(/^\/api\/v1\/documents\/([^/]+)\/unpublish$/);
237
- if (unpublishMatch && method === 'POST') {
238
- const id = unpublishMatch[1];
239
- const res = await ctx.workflowService.unpublish(actor, id);
240
- return { status: 200, body: res };
241
- }
242
-
243
- // Archive: /api/v1/documents/:id/archive
244
- const archiveMatch = urlPath.match(/^\/api\/v1\/documents\/([^/]+)\/archive$/);
245
- if (archiveMatch && method === 'POST') {
246
- const id = archiveMatch[1];
247
- const res = await ctx.workflowService.archive(actor, id);
248
- return { status: 200, body: res };
249
- }
250
-
251
- // Restore Revision: /api/v1/documents/:id/restore
252
- const restoreMatch = urlPath.match(/^\/api\/v1\/documents\/([^/]+)\/restore$/);
253
- if (restoreMatch && method === 'POST') {
254
- const id = restoreMatch[1];
255
- const targetRevId = (req.body as any)?.revisionId;
256
- const changeSummary = (req.body as any)?.changeSummary;
257
- const res = await ctx.workflowService.restoreRevision(actor, id, targetRevId, changeSummary);
258
- return { status: 200, body: res };
259
- }
260
-
261
- // Schedule: /api/v1/documents/:id/schedule
262
- const scheduleMatch = urlPath.match(/^\/api\/v1\/documents\/([^/]+)\/schedule$/);
263
- if (scheduleMatch && method === 'POST') {
264
- const id = scheduleMatch[1];
265
- const validated = ScheduleDocumentRequestSchema.parse(req.body);
266
- const res = await ctx.workflowService.schedule(
267
- actor,
268
- id,
269
- new Date(validated.scheduledAt),
270
- validated.revisionId
271
- );
272
- return { status: 200, body: res };
273
- }
274
-
275
- // Taxonomies: Topics
276
- if (urlPath === '/api/v1/topics') {
277
- if (method === 'GET') {
278
- const items = await ctx.taxonomyService.listTopics();
279
- return { status: 200, body: { items } };
280
- }
281
- if (method === 'POST') {
282
- const validated = CreateTopicRequestSchema.parse(req.body);
283
- const res = await ctx.taxonomyService.createTopic({
284
- id: `top_${validated.slug}`,
285
- ...validated,
286
- });
287
- return { status: 201, body: res };
288
- }
289
- }
290
-
291
- // Taxonomies: Tags
292
- if (urlPath === '/api/v1/tags') {
293
- if (method === 'GET') {
294
- const items = await ctx.taxonomyService.listTags();
295
- return { status: 200, body: { items } };
296
- }
297
- if (method === 'POST') {
298
- const validated = CreateTagRequestSchema.parse(req.body);
299
- const res = await ctx.taxonomyService.createTag({
300
- id: `tag_${validated.slug}`,
301
- ...validated,
302
- });
303
- return { status: 201, body: res };
304
- }
305
- }
306
-
307
- // Taxonomies: Series
308
- if (urlPath === '/api/v1/series') {
309
- if (method === 'GET') {
310
- const items = await ctx.taxonomyService.listSeries();
311
- return { status: 200, body: { items } };
312
- }
313
- if (method === 'POST') {
314
- const validated = CreateSeriesRequestSchema.parse(req.body);
315
- const res = await ctx.taxonomyService.createSeries({
316
- id: `ser_${validated.slug}`,
317
- ...validated,
318
- });
319
- return { status: 201, body: res };
320
- }
321
- }
322
-
323
- // Media: /api/v1/media
324
- if (urlPath === '/api/v1/media') {
325
- if (method === 'GET') {
326
- const items = ctx.mediaRepo
327
- ? await ctx.mediaRepo.list({
328
- mimeType: req.query?.mimeType,
329
- limit: req.query?.limit ? Number(req.query.limit) : 50,
330
- })
331
- : [];
332
- return { status: 200, body: { items } };
333
- }
334
- if (method === 'POST') {
335
- const body = req.body as any;
336
- if (!ctx.mediaRepo) {
337
- return { status: 500, body: { error: { code: 'NO_MEDIA_REPO', message: 'Media repository not configured.' } } };
338
- }
339
- let stored: any = null;
340
- if (ctx.mediaProvider && body.contentBase64) {
341
- stored = await ctx.mediaProvider.put({
342
- filename: body.filename,
343
- mimeType: body.mimeType,
344
- content: Buffer.from(body.contentBase64, 'base64'),
345
- prefix: body.prefix,
346
- });
347
- }
348
- const asset = await ctx.mediaRepo.create({
349
- id: body.id || `med_${Date.now()}`,
350
- provider: stored?.provider || 'local',
351
- providerKey: stored?.providerKey || body.providerKey || body.filename,
352
- mimeType: body.mimeType || 'application/octet-stream',
353
- sizeBytes: stored?.sizeBytes || body.sizeBytes || 0,
354
- width: body.width ?? null,
355
- height: body.height ?? null,
356
- altText: body.altText ?? null,
357
- caption: body.caption ?? null,
358
- metadata: body.metadata ?? null,
359
- createdBy: actor.id,
360
- });
361
- return { status: 201, body: asset };
362
- }
363
- }
364
-
365
- // Audit Events
366
- if (urlPath === '/api/v1/audit' && method === 'GET') {
367
- const events = await ctx.auditService.listEvents({
368
- resourceType: req.query?.resourceType,
369
- resourceId: req.query?.resourceId,
370
- actorId: req.query?.actorId,
371
- limit: req.query?.limit ? Number(req.query.limit) : 50,
372
- });
373
- return { status: 200, body: { items: events } };
374
- }
375
-
376
- // Agents: /api/v1/agents
377
- if (urlPath === '/api/v1/agents') {
378
- if (method === 'GET') {
379
- const agents = await ctx.agentService.listAgents();
380
- return { status: 200, body: { items: agents } };
381
- }
382
- if (method === 'POST') {
383
- const body = req.body as any;
384
- const agent = await ctx.agentService.registerAgent(actor, {
385
- id: body.id,
386
- name: body.name,
387
- description: body.description,
388
- scopes: body.scopes || [],
389
- });
390
- return { status: 201, body: agent };
391
- }
392
- }
393
-
394
- // Agent Action Runs: /api/v1/agent/runs
395
- if (urlPath === '/api/v1/agent/runs') {
396
- if (method === 'GET') {
397
- const runs = ctx.agentRepo
398
- ? await ctx.agentRepo.listActionRuns({ limit: req.query?.limit ? Number(req.query.limit) : 50 })
399
- : [];
400
- return { status: 200, body: { items: runs } };
401
- }
402
- }
403
-
404
- // Agent Action Run Approval: /api/v1/agent/runs/:id/approve
405
- const approveRunMatch = urlPath.match(/^\/api\/v1\/agent\/runs\/([^/]+)\/approve$/);
406
- if (approveRunMatch && method === 'POST') {
407
- const runId = approveRunMatch[1];
408
- const res = await ctx.agentService.approveActionRun(actor, runId, (req.body as any)?.reason);
409
- return { status: 200, body: res };
410
- }
411
-
412
- // Agent Action Run Rejection: /api/v1/agent/runs/:id/reject
413
- const rejectRunMatch = urlPath.match(/^\/api\/v1\/agent\/runs\/([^/]+)\/reject$/);
414
- if (rejectRunMatch && method === 'POST') {
415
- const runId = rejectRunMatch[1];
416
- const res = await ctx.agentService.rejectActionRun(actor, runId, (req.body as any)?.reason);
417
- return { status: 200, body: res };
418
- }
419
-
420
- // Intelligence: Status / Sources
421
- if (urlPath === '/api/v1/intelligence/status' && method === 'GET') {
422
- const registry = ctx.sourceRegistry ?? ctx.indexingService?.registry;
423
- return {
424
- status: 200,
425
- body: {
426
- status: 'operational',
427
- hasIndexingService: Boolean(ctx.indexingService),
428
- hasRetrievalService: Boolean(ctx.retrievalService),
429
- hasCanonicalResolver: Boolean(ctx.canonicalResolver),
430
- sources: registry
431
- ? registry.list().map((s) => ({
432
- id: s.sourceId,
433
- capabilities: s.capabilities,
434
- }))
435
- : [],
436
- },
437
- };
438
- }
439
-
440
- // Intelligence: Hybrid Retrieval
441
- if (urlPath === '/api/v1/intelligence/search' && method === 'GET') {
442
- if (!ctx.retrievalService) {
443
- return {
444
- status: 503,
445
- body: {
446
- error: {
447
- code: 'INTELLIGENCE_UNAVAILABLE',
448
- message: 'Retrieval service is not configured.',
449
- },
450
- },
451
- };
452
- }
453
- const q = req.query?.q;
454
- if (!q || typeof q !== 'string') {
455
- return {
456
- status: 400,
457
- body: {
458
- error: {
459
- code: 'INVALID_QUERY',
460
- message: "Query parameter 'q' is required.",
461
- },
462
- },
463
- };
464
- }
465
- const limit = req.query?.limit ? Number(req.query.limit) : 10;
466
- const minScore = req.query?.minScore ? Number(req.query.minScore) : undefined;
467
- const sourceId = req.query?.sourceId;
468
-
469
- const results = await ctx.retrievalService.search({
470
- query: q,
471
- limit,
472
- minScore,
473
- sourceId,
474
- });
475
-
476
- const shouldResolve = req.query?.resolve === 'true' && ctx.canonicalResolver;
477
- if (shouldResolve && ctx.canonicalResolver) {
478
- const resolved = await ctx.canonicalResolver.resolveMany(results);
479
- return {
480
- status: 200,
481
- body: {
482
- query: q,
483
- total: results.length,
484
- items: resolved.map((r) => ({
485
- reference: r.reference,
486
- canonical: r.content,
487
- error: r.error,
488
- })),
489
- },
490
- };
491
- }
492
-
493
- return {
494
- status: 200,
495
- body: {
496
- query: q,
497
- total: results.length,
498
- items: results,
499
- },
500
- };
501
- }
502
-
503
- // Intelligence: Indexing
504
- if (urlPath === '/api/v1/intelligence/index' && method === 'POST') {
505
- if (!ctx.indexingService) {
506
- return {
507
- status: 503,
508
- body: {
509
- error: {
510
- code: 'INTELLIGENCE_UNAVAILABLE',
511
- message: 'Indexing service is not configured.',
512
- },
513
- },
514
- };
515
- }
516
- const body = (req.body || {}) as { sourceId?: string; contentId?: string; revisionId?: string };
517
- if (body.sourceId && body.contentId) {
518
- const result = await ctx.indexingService.indexDocument(body.sourceId, body.contentId, body.revisionId);
519
- return { status: 200, body: { success: true, result } };
520
- } else if (body.sourceId) {
521
- const summary = await ctx.indexingService.syncSource(body.sourceId);
522
- return { status: 200, body: { success: true, summary } };
523
- } else {
524
- const summaries = await ctx.indexingService.syncAll();
525
- return { status: 200, body: { success: true, summaries } };
526
- }
527
- }
528
-
529
- return {
530
- status: 404,
531
- body: {
532
- error: {
533
- code: 'ROUTE_NOT_FOUND',
534
- message: `Endpoint '${method} ${urlPath}' was not found.`,
535
- },
536
- },
537
- };
538
- }
package/src/validators.ts DELETED
@@ -1,63 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- export const CreateDocumentRequestSchema = z.object({
4
- type: z.enum(['post', 'page']),
5
- slug: z.string().min(1),
6
- title: z.string().min(1),
7
- excerpt: z.string().optional().nullable(),
8
- sourceMarkdown: z.string(),
9
- frontmatter: z.record(z.string(), z.unknown()).optional(),
10
- seo: z
11
- .object({
12
- metaTitle: z.string().optional(),
13
- metaDescription: z.string().optional(),
14
- ogImage: z.string().optional(),
15
- noIndex: z.boolean().optional(),
16
- })
17
- .optional()
18
- .nullable(),
19
- });
20
-
21
- export const UpdateDocumentRequestSchema = z.object({
22
- title: z.string().optional(),
23
- slug: z.string().optional(),
24
- excerpt: z.string().optional().nullable(),
25
- sourceMarkdown: z.string().optional(),
26
- frontmatter: z.record(z.string(), z.unknown()).optional(),
27
- seo: z
28
- .object({
29
- metaTitle: z.string().optional(),
30
- metaDescription: z.string().optional(),
31
- ogImage: z.string().optional(),
32
- noIndex: z.boolean().optional(),
33
- })
34
- .optional()
35
- .nullable(),
36
- changeSummary: z.string().optional().nullable(),
37
- });
38
-
39
- export const PublishDocumentRequestSchema = z.object({
40
- revisionId: z.string().optional(),
41
- });
42
-
43
- export const ScheduleDocumentRequestSchema = z.object({
44
- scheduledAt: z.string().datetime(),
45
- revisionId: z.string().optional(),
46
- });
47
-
48
- export const CreateTopicRequestSchema = z.object({
49
- slug: z.string().min(1),
50
- name: z.string().min(1),
51
- description: z.string().optional().nullable(),
52
- });
53
-
54
- export const CreateTagRequestSchema = z.object({
55
- slug: z.string().min(1),
56
- name: z.string().min(1),
57
- });
58
-
59
- export const CreateSeriesRequestSchema = z.object({
60
- slug: z.string().min(1),
61
- name: z.string().min(1),
62
- description: z.string().optional().nullable(),
63
- });
package/tsconfig.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "rootDir": "src",
5
- "outDir": "dist"
6
- },
7
- "references": [
8
- { "path": "../core" },
9
- { "path": "../content" },
10
- { "path": "../policy" },
11
- { "path": "../intelligence" }
12
- ],
13
- "include": ["src/**/*"]
14
- }