life
Version:
Life.js is the first fullstack framework to build agentic web applications. It is minimal, extensible, and typesafe. Well, everything you love.
1 lines • 17.6 kB
Source Map (JSON)
{"version":3,"sources":["../shared/method-name.ts","../plugins/server/types.ts"],"sourcesContent":["import type { CamelCase } from \"type-fest\";\n\n/**\n * Type representing a camelCase method name.\n * Used to enforce type safety when working with method names.\n */\nexport type ToMethodName<T extends string = string> = CamelCase<\n T,\n { preserveConsecutiveUppercase: false }\n>;\n\nconst SEPARATOR_PATTERN = /[-\\s_]+/;\n\n/**\n * Converts a kebab-case, space-separated, or mixed-case string to camelCase.\n * Handles common separators: hyphens, spaces, and underscores.\n *\n * @param input - The string to convert (e.g., \"my-method\", \"my method\", \"my_method\")\n * @returns The camelCase version (e.g., \"myMethod\")\n *\n * @example\n * toMethodName(\"my-method\") // \"myMethod\"\n * toMethodName(\"my method\") // \"myMethod\"\n * toMethodName(\"my_method\") // \"myMethod\"\n * toMethodName(\"MyMethod\") // \"mymethod\"\n */\nexport function toMethodName<const T extends string>(input: T) {\n return input\n .split(SEPARATOR_PATTERN) // Split on hyphens, spaces, or underscores\n .filter(Boolean) // Remove empty strings from leading/trailing/consecutive separators\n .map((word, index) =>\n index === 0 ? word.toLowerCase() : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(),\n )\n .join(\"\") as ToMethodName<typeof input>;\n}\n","import z from \"zod\";\nimport type { AgentServer } from \"@/agent/server/class\";\nimport type { LifeErrorUnion } from \"@/shared/error\";\nimport type * as op from \"@/shared/operation\";\nimport type { Any, MaybePromise, Without } from \"@/shared/types\";\nimport type { ZodObjectWithTelemetry } from \"@/telemetry/helpers/zod\";\nimport type { TelemetrySpanHandle } from \"@/telemetry/types\";\nimport type { PluginBuilder } from \"./builder\";\n\n// Dependencies\nexport type PluginDependenciesDefinition = Without<PluginDefinition, \"dependencies\">[];\n\n// Config\nexport type PluginConfigDefinition = ZodObjectWithTelemetry<z.ZodObject, \"output\">;\n\nexport type PluginConfigInput<ConfigDef extends PluginConfigDefinition> = z.input<\n ConfigDef[\"schema\"]\n>;\nexport type PluginConfig<\n ConfigDef extends PluginConfigDefinition,\n T extends \"input\" | \"output\",\n> = T extends \"input\" ? z.input<ConfigDef[\"schema\"]> : z.output<ConfigDef[\"schema\"]>;\n\n// Context\nexport type PluginContextDefinition = ZodObjectWithTelemetry<z.ZodObject, \"output\">;\n\nexport type PluginContext<\n ContextDef extends PluginContextDefinition,\n T extends \"input\" | \"output\",\n> = T extends \"input\" ? z.input<ContextDef[\"schema\"]> : z.output<ContextDef[\"schema\"]>;\n\n// Events\nexport const eventSourceSchema = z.discriminatedUnion(\"type\", [\n z.object({\n type: z.literal(\"handler\"),\n plugin: z.string(),\n handler: z.string(),\n event: z.string(),\n }),\n z.object({\n type: z.literal(\"server\"),\n name: z.string(),\n }),\n z.object({\n type: z.literal(\"client\"),\n name: z.string(),\n }),\n]);\n\nexport type PluginEventSource = z.infer<typeof eventSourceSchema>;\n\nexport const pluginEventSchema = z.object({\n id: z.string(),\n name: z.string(),\n urgent: z.boolean().prefault(false),\n data: z.any().prefault(null),\n created: z.object({\n at: z.number(),\n by: eventSourceSchema,\n }),\n edited: z\n .array(\n z.object({\n at: z.number(),\n by: z.object({\n plugin: z.string(),\n handler: z.string(),\n }),\n reason: z.string(),\n dataBefore: z.any(),\n dataAfter: z.any(),\n }),\n )\n .or(z.literal(false))\n .default(false),\n dropped: z\n .object({\n at: z.number(),\n by: z.object({\n plugin: z.string(),\n handler: z.string(),\n }),\n reason: z.string(),\n })\n .or(z.literal(false))\n .prefault(false),\n contextChanges: z\n .array(\n z.object({\n at: z.number(),\n byHandler: z.string(),\n valueBefore: z.any(),\n valueAfter: z.any(),\n }),\n )\n .prefault([]),\n});\n\nexport const pluginEventInputSchema = pluginEventSchema.omit({\n id: true,\n created: true,\n edited: true,\n dropped: true,\n contextChanges: true,\n});\n\nexport type PluginEventDataSchema = z.ZodObject | z.ZodDiscriminatedUnion<z.ZodObject[]>;\n\nexport type PluginEventDefinition<Name extends string> = {\n name: Name;\n dataSchema?: PluginEventDataSchema;\n};\n\nexport type PluginEventsDefinition<Names extends string[] = string[]> = {\n [K in keyof Names]: PluginEventDefinition<Names[K]>;\n};\n\nexport type PluginEvent<EventsDef extends PluginEventsDefinition, T extends \"input\" | \"output\"> = {\n [K in keyof EventsDef]: Omit<\n T extends \"input\"\n ? Omit<\n z.input<typeof pluginEventSchema>,\n \"created\" | \"edited\" | \"dropped\" | \"id\" | \"contextChanges\"\n >\n : z.output<typeof pluginEventSchema>,\n \"data\" | \"name\"\n > & {\n name: EventsDef[K][\"name\"];\n } & (EventsDef[K][\"dataSchema\"] extends PluginEventDataSchema\n ? T extends \"input\"\n ? { data: z.input<EventsDef[K][\"dataSchema\"]> }\n : { data: z.output<EventsDef[K][\"dataSchema\"]> }\n : T extends \"input\"\n ? unknown\n : { data: null });\n}[number];\n\nexport const internalEventsDef = [\n {\n name: \"plugin.start\",\n dataSchema: z.object({\n isRestart: z.boolean().prefault(false),\n restartCount: z.number().prefault(0),\n }),\n },\n { name: \"plugin.stop\" },\n { name: \"plugin.test\" },\n {\n name: \"plugin.error\",\n dataSchema: z.object({\n error: z.custom<LifeErrorUnion>(),\n event: pluginEventSchema,\n }),\n },\n] as const satisfies PluginEventsDefinition;\n\nexport type PluginInternalEvent<T extends \"input\" | \"output\"> = PluginEvent<\n typeof internalEventsDef,\n T\n>;\n\n// Handlers\nexport type PluginHandlerBlockFunction<\n PluginDef extends PluginDefinition,\n StateDef extends PluginHandlerStateDefinition<PluginDef>,\n> = (params: {\n event: PluginEvent<PluginDef[\"events\"], \"output\">;\n state: PluginHandlerState<PluginDef, StateDef>;\n plugin: op.ToPublic<\n PluginAccessor<\n PluginDef,\n { type: \"handler\"; plugin: string; handler: string; event: string },\n \"write\"\n >\n >;\n agent: op.ToPublic<AgentServer>;\n dependencies: op.ToPublic<\n PluginDependenciesAccessor<\n PluginDef[\"dependencies\"],\n { type: \"handler\"; plugin: string; handler: string; event: string }\n >\n >;\n telemetry: TelemetrySpanHandle;\n}) => MaybePromise<unknown>;\n\nexport type PluginHandlerStreamFunction<\n PluginDef extends PluginDefinition,\n StateDef extends PluginHandlerStateDefinition<PluginDef>,\n> = (params: {\n event: PluginEvent<PluginDef[\"events\"], \"output\">;\n state: PluginHandlerState<PluginDef, StateDef>;\n plugin: op.ToPublic<\n PluginAccessor<PluginDef, { type: \"handler\"; plugin: string; handler: string; event: string }>\n >;\n agent: op.ToPublic<AgentServer>;\n dependencies: op.ToPublic<\n PluginDependenciesAccessor<\n PluginDef[\"dependencies\"],\n { type: \"handler\"; plugin: string; handler: string; event: string }\n >\n >;\n telemetry: TelemetrySpanHandle;\n}) => MaybePromise<unknown>;\n\nexport type PluginHandlerInterceptFunction<\n PluginDef extends PluginDefinition,\n StateDef extends PluginHandlerStateDefinition<PluginDef>,\n> = (params: {\n event: PluginEvent<PluginDef[\"dependencies\"][number][\"events\"], \"output\">;\n state: PluginHandlerState<PluginDef, StateDef>;\n next: (\n data: PluginEvent<PluginDef[\"dependencies\"][number][\"events\"], \"output\">[\"data\"],\n reason: string,\n ) => void;\n drop: (reason: string) => void;\n plugin: op.ToPublic<\n PluginAccessor<PluginDef, { type: \"handler\"; plugin: string; handler: string; event: string }>\n >;\n dependency: op.ToPublic<\n PluginAccessor<\n PluginDef[\"dependencies\"][number] & { dependencies: [] },\n { type: \"handler\"; plugin: string; handler: string; event: string }\n >\n >;\n agent: op.ToPublic<AgentServer>;\n telemetry: TelemetrySpanHandle;\n}) => MaybePromise<unknown>;\n\nexport type PluginHandlerStateDefinition<PluginDef extends PluginDefinition> =\n | Record<string, unknown>\n | ((params: { config: PluginConfig<PluginDef[\"config\"], \"output\"> }) => Record<string, unknown>);\n\nexport type PluginHandlerState<\n PluginDef extends PluginDefinition,\n StateDef extends PluginHandlerStateDefinition<PluginDef>,\n> = StateDef extends (p: infer _) => Record<string, unknown> ? ReturnType<StateDef> : StateDef;\n\nexport type PluginHandlerDefinition<\n PluginDef extends PluginDefinition = PluginDefinition,\n Name extends string = string,\n StateDef extends PluginHandlerStateDefinition<PluginDef> = Record<string, unknown>,\n> = {\n name: Name;\n state?: PluginHandlerStateDefinition<PluginDef>;\n} & (\n | { mode: \"block\"; onEvent: PluginHandlerBlockFunction<PluginDef, StateDef> }\n | { mode: \"stream\"; onEvent: PluginHandlerStreamFunction<PluginDef, StateDef> }\n | { mode: \"intercept\"; onEvent: PluginHandlerInterceptFunction<PluginDef, StateDef> }\n);\n\n// Helper type to extract context once (prevents inlining)\nexport type ExtractPluginContext<PluginDef extends PluginDefinition> = PluginContext<\n PluginDef[\"context\"],\n \"output\"\n>;\n\n// Accessor\nexport type PluginAccessor<\n PluginDef extends PluginDefinition,\n Source extends PluginEventSource,\n HandlerAccess extends \"read\" | \"write\" = \"read\",\n> = (Source[\"type\"] extends \"client\" // Exclude the config if the source is a client\n ? unknown\n : { config: PluginConfig<PluginDef[\"config\"], \"output\"> }) & {\n context: {\n get(): op.OperationResult<ExtractPluginContext<PluginDef>>;\n /** Subscribe to changes in the context. Returns a function to unsubscribe. */\n onChange(\n selector: (context: ExtractPluginContext<PluginDef>) => unknown,\n callback: (\n newContext: ExtractPluginContext<PluginDef>,\n oldContext: ExtractPluginContext<PluginDef>,\n ) => void,\n ): op.OperationResult<() => void>;\n /** Returns a cloned snapshot of the context. */\n } & (HandlerAccess extends \"write\"\n ? {\n /** Set a value in the context. */\n set(\n valueOrUpdater:\n | ExtractPluginContext<PluginDef>\n | ((ctx: ExtractPluginContext<PluginDef>) => ExtractPluginContext<PluginDef>),\n ): op.OperationResult<void>;\n }\n : unknown);\n events: {\n emit: (\n event: Exclude<\n PluginEvent<PluginDef[\"events\"], \"input\">,\n { name: (typeof internalEventsDef)[number][\"name\"] }\n >,\n ) => Source[\"type\"] extends \"client\"\n ? Promise<op.OperationResult<string>>\n : op.OperationResult<string>;\n\n /**\n * Wait for the return type of a specific handler when processing a given event.\n * Note that the intercept handlers don't return a result, and so are not supported by this method.\n * @param eventId\n * @param handlerName\n * @returns\n */\n waitForProcessing: (eventId: string) => Promise<op.OperationResult<void>>;\n waitForResult: <\n HandlerName extends Exclude<PluginDef[\"handlers\"][number], { mode: \"intercept\" }>[\"name\"],\n >(\n eventId: string,\n handlerName: HandlerName,\n ) => Promise<\n op.OperationResult<\n ReturnType<Extract<PluginDef[\"handlers\"][number], { name: HandlerName }>[\"onEvent\"]>\n >\n >;\n } & (Source[\"type\"] extends \"handler\" // Exclude the on/once methods inside handlers, redundant, encourages bad practices\n ? unknown\n : {\n on: <const Selector extends PluginEventsSelector<PluginDef[\"events\"]>>(\n selector: Selector,\n callback: (\n event: PluginEventsSelection<PluginDef[\"events\"], Selector>,\n ) => MaybePromise<void>,\n includeDropped?: boolean,\n ) => op.OperationResult<() => void>;\n once: <const Selector extends PluginEventsSelector<PluginDef[\"events\"]>>(\n selector: Selector,\n callback: (\n event: PluginEventsSelection<PluginDef[\"events\"], Selector>,\n ) => MaybePromise<void>,\n includeDropped?: boolean,\n ) => op.OperationResult<() => void>;\n });\n};\n\nexport type PluginDependenciesAccessor<\n DependenciesDef extends PluginDependenciesDefinition,\n Source extends PluginEventSource,\n> = {\n [DependencyDef in DependenciesDef[number] as DependencyDef[\"name\"]]: PluginAccessor<\n DependencyDef & { dependencies: [] },\n Source\n >;\n};\n\n// - Helper type to extract event names from event definitions\nexport type PluginEventsSelector<EventsDef extends PluginEventsDefinition> =\n | \"*\"\n | EventsDef[number][\"name\"]\n | EventsDef[number][\"name\"][]\n | { include: EventsDef[number][\"name\"][] | \"*\"; exclude?: EventsDef[number][\"name\"][] };\n\nexport type PluginEventsSelection<\n EventsDef extends PluginEventsDefinition,\n Selector extends PluginEventsSelector<EventsDef>,\n> = Selector extends \"*\"\n ? PluginEvent<EventsDef, \"output\">\n : Selector extends string\n ? Extract<PluginEvent<EventsDef, \"output\">, { name: Selector }>\n : Selector extends (infer S)[]\n ? S extends EventsDef[number][\"name\"]\n ? Extract<PluginEvent<EventsDef, \"output\">, { name: S }>\n : never\n : Selector extends { include: infer I; exclude?: infer E }\n ? I extends \"*\"\n ? E extends string[]\n ? Extract<\n PluginEvent<EventsDef, \"output\">,\n { name: Exclude<EventsDef[number][\"name\"], E[number]> }\n >\n : PluginEvent<EventsDef, \"output\">\n : I extends string[]\n ? E extends string[]\n ? Extract<PluginEvent<EventsDef, \"output\">, { name: Exclude<I[number], E[number]> }>\n : Extract<PluginEvent<EventsDef, \"output\">, { name: I[number] }>\n : never\n : never;\n\n// Definition\nexport interface PluginDefinition {\n name: string;\n dependencies: PluginDependenciesDefinition;\n config: PluginConfigDefinition;\n context: PluginContextDefinition;\n events: PluginEventsDefinition;\n handlers: PluginHandlerDefinition[];\n}\n\n// Builder\n// - Helper to return the builder without the excluded methods\nexport type TypedPluginBuilder<\n PluginDef extends PluginDefinition,\n Excluded extends string = never,\n> = Omit<PluginBuilder<PluginDef, Excluded>, Excluded>;\n\n// - Helper to filter out an handler by name in removeHandler() method\nexport type FilterHandlersByName<\n Handlers extends { name: string }[],\n Name extends string,\n> = Handlers extends [{ name: infer N }, ...infer Rest extends { name: string }[]]\n ? N extends Name\n ? FilterHandlersByName<Rest, Name>\n : [Handlers[0], ...FilterHandlersByName<Rest, Name>]\n : [];\n\n// Server\nexport type PluginEventsListener = {\n id: string;\n selector: PluginEventsSelector<PluginEventsDefinition>;\n callback: ((event: Any) => MaybePromise<void>) | \"remote\";\n includeDropped: boolean;\n};\n\nexport type PluginContextListener = {\n id: string;\n selector: (context: Any) => Any;\n callback: (newContext: Any, oldContext: Any) => MaybePromise<void>;\n};\n"],"mappings":"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","names":[]}