Initial commit (unpacked platform)
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@app/events",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Typad eventkatalog (spec §55) – används av outbox, analytics, memory och training",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "vitest run --passWithNoTests"
|
||||
},
|
||||
"dependencies": {
|
||||
"@app/shared-types": "workspace:*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import type { EventType, Unit } from "@app/shared-types";
|
||||
|
||||
/**
|
||||
* Typade payloads för domänhändelser (spec §55).
|
||||
* Events skrivs i outbox-tabellen (domain_events) i samma transaktion som
|
||||
* affärsdatan och konsumeras asynkront av worker (memory, analytics, training).
|
||||
*/
|
||||
export interface EventPayloadMap {
|
||||
PRODUCT_ADDED: {
|
||||
inventoryItemId: string;
|
||||
canonicalIngredientId?: string | null;
|
||||
quantity: number;
|
||||
unit: Unit;
|
||||
source: string;
|
||||
};
|
||||
PRODUCT_UPDATED: { inventoryItemId: string; changes: Record<string, unknown> };
|
||||
PRODUCT_CONSUMED: {
|
||||
inventoryItemId: string;
|
||||
quantity: number;
|
||||
unit: Unit;
|
||||
refType?: string | null;
|
||||
};
|
||||
PRODUCT_DISCARDED: {
|
||||
inventoryItemId: string;
|
||||
quantity: number;
|
||||
unit: Unit;
|
||||
valueMinor?: number | null;
|
||||
reason?: string | null;
|
||||
};
|
||||
RECIPE_COOKED: { recipeId: string; portions: number; mealBoxPortions: number };
|
||||
RECIPE_RATED: { recipeId: string; stars: number; feedbackTags: string[] };
|
||||
RECIPE_CREATED: { recipeId: string; sourceType: string };
|
||||
RECIPE_FORKED: { recipeId: string; forkedFromRecipeId: string };
|
||||
MEAL_LOGGED: { mealId: string; mealType: string; kcal: number; source: string };
|
||||
MEAL_PHOTO_ANALYZED: { scanJobId: string; matched: boolean; kcalMostLikely?: number | null };
|
||||
HOUSEHOLD_MEMBER_ADDED: { householdId: string; newUserId: string; role: string };
|
||||
SUBSCRIPTION_STARTED: { subscriptionId: string; plan: string; provider: string };
|
||||
SUBSCRIPTION_CHANGED: { subscriptionId: string; status: string };
|
||||
AI_CORRECTED: { scanJobId?: string | null; taskType: string; field: string };
|
||||
MEMORY_UPDATED: { memoryItemId: string; kind: string; origin: string };
|
||||
SHOPPING_COMPLETED: { shoppingListId: string; itemsAdded: number };
|
||||
WEEK_PLAN_UPDATED: { weekPlanId: string; reason?: string | null };
|
||||
MEAL_BOX_CREATED: { mealBoxId: string; portions: number };
|
||||
MEAL_BOX_CONSUMED: { mealBoxId: string; portions: number };
|
||||
}
|
||||
|
||||
/** Kontroll i kompileringstid att kartan täcker alla EventType. */
|
||||
type AssertCoversAll = EventType extends keyof EventPayloadMap ? true : never;
|
||||
const _coversAll: AssertCoversAll = true;
|
||||
void _coversAll;
|
||||
|
||||
export interface NewDomainEvent<T extends EventType = EventType> {
|
||||
type: T;
|
||||
payload: T extends keyof EventPayloadMap ? EventPayloadMap[T] : never;
|
||||
userId?: string | undefined;
|
||||
householdId?: string | undefined;
|
||||
correlationId?: string | undefined;
|
||||
}
|
||||
|
||||
/** Hjälpare med full typinferens: makeEvent("RECIPE_COOKED", {...}). */
|
||||
export function makeEvent<T extends EventType>(
|
||||
type: T,
|
||||
payload: T extends keyof EventPayloadMap ? EventPayloadMap[T] : never,
|
||||
scope: { userId?: string; householdId?: string; correlationId?: string } = {},
|
||||
): NewDomainEvent<T> {
|
||||
return { type, payload, ...scope };
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"include": ["src"],
|
||||
"compilerOptions": {
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user