This commit is contained in:
157
packages/shared/src/index.ts
Normal file
157
packages/shared/src/index.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import { z } from 'zod'
|
||||
export * from './moderation'
|
||||
|
||||
export const abilityKeys = ['str', 'dex', 'con', 'int', 'wis', 'cha'] as const
|
||||
export const AbilityKeySchema = z.enum(abilityKeys)
|
||||
export type AbilityKey = z.infer<typeof AbilityKeySchema>
|
||||
|
||||
export const AbilityScoresSchema = z.object({
|
||||
str: z.number().int().min(1).max(30),
|
||||
dex: z.number().int().min(1).max(30),
|
||||
con: z.number().int().min(1).max(30),
|
||||
int: z.number().int().min(1).max(30),
|
||||
wis: z.number().int().min(1).max(30),
|
||||
cha: z.number().int().min(1).max(30),
|
||||
})
|
||||
export type AbilityScores = z.infer<typeof AbilityScoresSchema>
|
||||
|
||||
export const WorldEntitySchema = z.object({
|
||||
id: z.string().min(1),
|
||||
kind: z.enum(['location', 'npc', 'faction', 'quest']),
|
||||
name: z.string().min(1).max(120),
|
||||
summary: z.string().min(1).max(1200),
|
||||
tags: z.array(z.string().max(40)).max(12).default([]),
|
||||
secrets: z.array(z.string().max(500)).max(6).default([]),
|
||||
})
|
||||
export type WorldEntity = z.infer<typeof WorldEntitySchema>
|
||||
|
||||
export const WorldStarterSchema = z.object({
|
||||
title: z.string().min(3).max(100),
|
||||
genre: z.string().min(2).max(80),
|
||||
tone: z.string().min(2).max(160),
|
||||
premise: z.string().min(20).max(1800),
|
||||
contentBoundaries: z.array(z.string().max(120)).min(1).max(8),
|
||||
startingLocation: WorldEntitySchema.extend({ kind: z.literal('location') }),
|
||||
npcs: z.array(WorldEntitySchema.extend({ kind: z.literal('npc') })).length(3),
|
||||
factions: z.array(WorldEntitySchema.extend({ kind: z.literal('faction') })).length(2),
|
||||
hook: z.string().min(20).max(1200),
|
||||
hiddenThreat: z.string().min(10).max(1000),
|
||||
openingScene: z.string().min(40).max(2400),
|
||||
})
|
||||
export type WorldStarter = z.infer<typeof WorldStarterSchema>
|
||||
|
||||
export const CharacterSchema = z.object({
|
||||
id: z.string().min(1),
|
||||
name: z.string().min(1).max(80),
|
||||
concept: z.string().min(3).max(600),
|
||||
controller: z.enum(['human', 'ai', 'delegated']),
|
||||
userId: z.string().nullable().default(null),
|
||||
abilities: AbilityScoresSchema,
|
||||
hp: z.number().int().min(0),
|
||||
maxHp: z.number().int().min(1),
|
||||
defense: z.number().int().min(1).max(40),
|
||||
proficiency: z.number().int().min(1).max(10),
|
||||
inventory: z.array(z.string().max(120)).max(50).default([]),
|
||||
statuses: z.array(z.string().max(80)).max(12).default([]),
|
||||
})
|
||||
export type Character = z.infer<typeof CharacterSchema>
|
||||
|
||||
export const PlayerIntentSchema = z.object({
|
||||
id: z.string().min(1),
|
||||
roundId: z.string().min(1),
|
||||
memberId: z.string().min(1),
|
||||
characterId: z.string().min(1),
|
||||
action: z.string().trim().min(1).max(2000),
|
||||
ready: z.boolean().default(false),
|
||||
createdAt: z.string().datetime(),
|
||||
updatedAt: z.string().datetime(),
|
||||
})
|
||||
export type PlayerIntent = z.infer<typeof PlayerIntentSchema>
|
||||
|
||||
export const CheckRequestSchema = z.object({
|
||||
actorId: z.string().min(1),
|
||||
kind: z.enum(['ability', 'attack', 'initiative', 'damage', 'healing']),
|
||||
ability: AbilityKeySchema.optional(),
|
||||
difficulty: z.number().int().min(1).max(40).optional(),
|
||||
targetId: z.string().optional(),
|
||||
mode: z.enum(['normal', 'advantage', 'disadvantage']).default('normal'),
|
||||
dice: z.string().regex(/^\d+d\d+(?:[+-]\d+)?$/).optional(),
|
||||
reason: z.string().min(1).max(500),
|
||||
})
|
||||
export type CheckRequest = z.infer<typeof CheckRequestSchema>
|
||||
|
||||
export const DiceRollSchema = z.object({
|
||||
id: z.string(),
|
||||
checkKind: CheckRequestSchema.shape.kind,
|
||||
formula: z.string(),
|
||||
rolls: z.array(z.number().int()),
|
||||
kept: z.array(z.number().int()),
|
||||
modifier: z.number().int(),
|
||||
total: z.number().int(),
|
||||
difficulty: z.number().int().nullable(),
|
||||
success: z.boolean().nullable(),
|
||||
actorId: z.string(),
|
||||
targetId: z.string().nullable(),
|
||||
createdAt: z.string().datetime(),
|
||||
})
|
||||
export type DiceRoll = z.infer<typeof DiceRollSchema>
|
||||
|
||||
export const ProposedEventSchema = z.object({
|
||||
type: z.enum(['narrative', 'relationship', 'quest', 'inventory', 'status', 'damage', 'healing']),
|
||||
actorId: z.string().nullable().default(null),
|
||||
targetId: z.string().nullable().default(null),
|
||||
value: z.number().int().nullable().default(null),
|
||||
item: z.string().max(120).nullable().default(null),
|
||||
description: z.string().min(1).max(1000),
|
||||
})
|
||||
export type ProposedEvent = z.infer<typeof ProposedEventSchema>
|
||||
|
||||
export const RoundPlanSchema = z.object({
|
||||
checks: z.array(CheckRequestSchema).max(20),
|
||||
aiActions: z.array(z.object({
|
||||
characterId: z.string(),
|
||||
action: z.string().min(1).max(800),
|
||||
})).max(8),
|
||||
proposedEvents: z.array(ProposedEventSchema).max(30),
|
||||
relevantMemoryQueries: z.array(z.string().max(120)).max(8),
|
||||
})
|
||||
export type RoundPlan = z.infer<typeof RoundPlanSchema>
|
||||
|
||||
export const RoundResolutionSchema = z.object({
|
||||
narration: z.string().min(20).max(6000),
|
||||
events: z.array(ProposedEventSchema).max(30),
|
||||
memory: z.object({
|
||||
summary: z.string().min(10).max(1200),
|
||||
importance: z.number().int().min(1).max(5),
|
||||
tags: z.array(z.string().max(40)).max(12),
|
||||
entityIds: z.array(z.string()).max(20),
|
||||
}).nullable(),
|
||||
nextPrompt: z.string().min(3).max(500),
|
||||
})
|
||||
export type RoundResolution = z.infer<typeof RoundResolutionSchema>
|
||||
|
||||
export const CoauthorMessageSchema = z.object({
|
||||
role: z.enum(['user', 'assistant']),
|
||||
content: z.string().trim().min(1).max(5000),
|
||||
})
|
||||
|
||||
export const CreateWorldRequestSchema = z.object({
|
||||
messages: z.array(CoauthorMessageSchema).min(1).max(12),
|
||||
})
|
||||
|
||||
export const SubmitIntentRequestSchema = z.object({
|
||||
campaignId: z.string().min(1),
|
||||
characterId: z.string().min(1),
|
||||
action: z.string().trim().min(1).max(2000),
|
||||
ready: z.boolean().default(true),
|
||||
})
|
||||
|
||||
export type ID = string
|
||||
|
||||
export function makeId(prefix: string): string {
|
||||
const randomUuid = globalThis.crypto?.randomUUID?.()
|
||||
// IDs are identifiers, not authentication secrets. The fallback keeps the
|
||||
// shared browser/server contract usable in Node 18 VM contexts without Web Crypto.
|
||||
const id = randomUuid ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}-${Math.random().toString(36).slice(2)}`
|
||||
return `${prefix}_${id}`
|
||||
}
|
||||
Reference in New Issue
Block a user