/** * App public surface — the typed, destructurable face of a bounded context. * * `.public()` marks a BC's contract (events, queries, actions); everything * unmarked is internal. `createApp` exposes each `$public` primitive as a * property on the app instance, keyed by the camelCased last segment of the * primitive's `name` — so a neighbour destructures the contract and hands the * definition straight to the verb that matches intent: * * const { listCourses, enrollStudent, CourseWasPublished } = coursesApp * await ctx.query(listCourses, {}) // read * await ctx.send(enrollStudent, { ... }) // write, fire-and-forget * when(CourseWasPublished, ...) // subscribe * * Keys: `courses.list-courses` → `listCourses`; events get the event-symbol * casing — `enrollments.student-was-enrolled` → `StudentWasEnrolled`. * * Isolation is enforced by the type: only primitives whose type carries * `$public: true` (the `.public()` return type) appear on * {@link AppPublicSurface}, so destructuring an internal primitive does not * type-check. The properties yield the DEFINITION (the reference you pass to * `ctx.query` / `ctx.send` / `when`), never a bound callable — the wire keeps * owning transport. */ /** Last dot-segment of a primitive name: `"courses.list-courses"` → `"list-courses"`. */ type LastSegment = S extends `${string}.${infer Rest}` ? LastSegment : S; /** Kebab → camel: `"list-courses"` → `"listCourses"`. */ type KebabToCamel = S extends `${infer Head}-${infer Tail}` ? `${Head}${KebabToCamel>}` : S; /** * The surface key a primitive name maps to. Wide `string` names produce no * key — only definitions that kept their literal name (every `define*` does) * land on the typed surface. */ export type PublicSurfaceKey = string extends TName ? never : KebabToCamel>; type IsAny = 0 extends 1 & T ? true : false; /** Element union of a registry group; `any[]` groups (untyped registries) contribute nothing. */ type ElementOf = TGroup extends readonly (infer E)[] ? IsAny extends true ? never : E : never; type UnionToIntersection = (U extends unknown ? (x: U) => void : never) extends (x: infer I) => void ? I : never; /** One camelCase-keyed record per `$public` primitive (actions, queries). */ type CamelEntry = T extends { readonly $public: true; readonly name: infer N extends string; } ? { readonly [K in PublicSurfaceKey]: T; } : never; /** One PascalCase-keyed record per `$public` event — the event-symbol casing. */ type PascalEntry = T extends { readonly $public: true; readonly name: infer N extends string; } ? { readonly [K in Capitalize>]: T; } : never; type HandlerElements = TReg extends { readonly handlers?: infer G; } ? ElementOf : never; type EventElements = TReg extends { readonly events?: infer G; } ? ElementOf : never; /** * Split a channel's element union by event-ness so the type mirrors the * runtime keying: events surface under the PascalCase event-symbol key, * everything else camelCase. `{ $kind?: "event" }` matches only event-shaped * elements — `defineHandler` / `defineAction` / `defineQuery` results carry a * required non-event `$kind` literal, so they land on the camel side. The * `handlers` channel accepts ANY `define*` primitive (createApp routes by * kind), so it must split too — otherwise a `.public()` event passed there * would type under a camel key the runtime never attaches. */ type EventShaped = { readonly $kind?: "event"; }; /** The union of one-key records the surface intersects — `never` when nothing is public. */ type SurfaceEntries = CamelEntry | ElementOf, EventShaped>> | PascalEntry | Extract, EventShaped>>; /** * The app's external surface type, derived from what `createApp` was given: * the registry's `handlers` + `events` groups and the explicit `handlers` * array. Only `.public()` definitions survive the filter; everything else — * and every untyped channel (generated registries, discovery) — contributes * `{}`. (The `never` case is guarded before the intersection — an empty * union would otherwise intersect to `unknown`.) */ export type AppPublicSurface = [ SurfaceEntries ] extends [never] ? {} : UnionToIntersection>; /** The structural slice of a primitive the surface collector reads. */ interface SurfacePrimitive { readonly name: string; readonly $kind?: string; readonly $public?: boolean; } /** * Runtime mirror of {@link PublicSurfaceKey}: camelCase the last dot-segment; * events get the PascalCase event-symbol casing. */ export declare function publicSurfaceKey(name: string, kind?: string): string; /** * Collect every `$public` primitive registered on the runtime — dispatchable * handlers (actions, queries) plus `byKind("event")` — keyed by surface key. * A primitive registered through two channels (registry + discovery) counts * once; two DIFFERENT primitives colliding on one key fail loudly, because a * surface a neighbour can't destructure unambiguously is not a contract. */ export declare function collectPublicSurface(appName: string, runtime: { listHandlers(): readonly string[]; getHandler(name: string): unknown; byKind(kind: string): readonly unknown[]; }): ReadonlyMap; export {};