@rizom/brain 0.2.0-alpha.219 → 0.2.0-alpha.220

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.
@@ -47,7 +47,7 @@
47
47
  "'use strict';\n\nconst getEngine = require('./engine');\nconst defaults = require('./defaults');\n\nmodule.exports = function(language, str, options) {\n const opts = defaults(options);\n const engine = getEngine(language, opts);\n if (typeof engine.parse !== 'function') {\n throw new TypeError('expected \"' + language + '.parse\" to be a function');\n }\n return engine.parse(str, opts);\n};\n",
48
48
  "'use strict';\n\nconst fs = require('fs');\nconst sections = require('section-matter');\nconst defaults = require('./lib/defaults');\nconst stringify = require('./lib/stringify');\nconst excerpt = require('./lib/excerpt');\nconst engines = require('./lib/engines');\nconst toFile = require('./lib/to-file');\nconst parse = require('./lib/parse');\nconst utils = require('./lib/utils');\n\n/**\n * Takes a string or object with `content` property, extracts\n * and parses front-matter from the string, then returns an object\n * with `data`, `content` and other [useful properties](#returned-object).\n *\n * ```js\n * const matter = require('gray-matter');\n * console.log(matter('---\\ntitle: Home\\n---\\nOther stuff'));\n * //=> { data: { title: 'Home'}, content: 'Other stuff' }\n * ```\n * @param {Object|String} `input` String, or object with `content` string\n * @param {Object} `options`\n * @return {Object}\n * @api public\n */\n\nfunction matter(input, options) {\n if (input === '') {\n return { data: {}, content: input, excerpt: '', orig: input };\n }\n\n let file = toFile(input);\n const cached = matter.cache[file.content];\n\n if (!options) {\n if (cached) {\n file = Object.assign({}, cached);\n file.orig = cached.orig;\n return file;\n }\n\n // only cache if there are no options passed. if we cache when options\n // are passed, we would need to also cache options values, which would\n // negate any performance benefits of caching\n matter.cache[file.content] = file;\n }\n\n return parseMatter(file, options);\n}\n\n/**\n * Parse front matter\n */\n\nfunction parseMatter(file, options) {\n const opts = defaults(options);\n const open = opts.delimiters[0];\n const close = '\\n' + opts.delimiters[1];\n let str = file.content;\n\n if (opts.language) {\n file.language = opts.language;\n }\n\n // get the length of the opening delimiter\n const openLen = open.length;\n if (!utils.startsWith(str, open, openLen)) {\n excerpt(file, opts);\n return file;\n }\n\n // if the next character after the opening delimiter is\n // a character from the delimiter, then it's not a front-\n // matter delimiter\n if (str.charAt(openLen) === open.slice(-1)) {\n return file;\n }\n\n // strip the opening delimiter\n str = str.slice(openLen);\n const len = str.length;\n\n // use the language defined after first delimiter, if it exists\n const language = matter.language(str, opts);\n if (language.name) {\n file.language = language.name;\n str = str.slice(language.raw.length);\n }\n\n // get the index of the closing delimiter\n let closeIndex = str.indexOf(close);\n if (closeIndex === -1) {\n closeIndex = len;\n }\n\n // get the raw front-matter block\n file.matter = str.slice(0, closeIndex);\n\n const block = file.matter.replace(/^\\s*#[^\\n]+/gm, '').trim();\n if (block === '') {\n file.isEmpty = true;\n file.empty = file.content;\n file.data = {};\n } else {\n\n // create file.data by parsing the raw file.matter block\n file.data = parse(file.language, file.matter, opts);\n }\n\n // update file.content\n if (closeIndex === len) {\n file.content = '';\n } else {\n file.content = str.slice(closeIndex + close.length);\n if (file.content[0] === '\\r') {\n file.content = file.content.slice(1);\n }\n if (file.content[0] === '\\n') {\n file.content = file.content.slice(1);\n }\n }\n\n excerpt(file, opts);\n\n if (opts.sections === true || typeof opts.section === 'function') {\n sections(file, opts.section);\n }\n return file;\n}\n\n/**\n * Expose engines\n */\n\nmatter.engines = engines;\n\n/**\n * Stringify an object to YAML or the specified language, and\n * append it to the given string. By default, only YAML and JSON\n * can be stringified. See the [engines](#engines) section to learn\n * how to stringify other languages.\n *\n * ```js\n * console.log(matter.stringify('foo bar baz', {title: 'Home'}));\n * // results in:\n * // ---\n * // title: Home\n * // ---\n * // foo bar baz\n * ```\n * @param {String|Object} `file` The content string to append to stringified front-matter, or a file object with `file.content` string.\n * @param {Object} `data` Front matter to stringify.\n * @param {Object} `options` [Options](#options) to pass to gray-matter and [js-yaml].\n * @return {String} Returns a string created by wrapping stringified yaml with delimiters, and appending that to the given string.\n * @api public\n */\n\nmatter.stringify = function(file, data, options) {\n if (typeof file === 'string') file = matter(file, options);\n return stringify(file, data, options);\n};\n\n/**\n * Synchronously read a file from the file system and parse\n * front matter. Returns the same object as the [main function](#matter).\n *\n * ```js\n * const file = matter.read('./content/blog-post.md');\n * ```\n * @param {String} `filepath` file path of the file to read.\n * @param {Object} `options` [Options](#options) to pass to gray-matter.\n * @return {Object} Returns [an object](#returned-object) with `data` and `content`\n * @api public\n */\n\nmatter.read = function(filepath, options) {\n const str = fs.readFileSync(filepath, 'utf8');\n const file = matter(str, options);\n file.path = filepath;\n return file;\n};\n\n/**\n * Returns true if the given `string` has front matter.\n * @param {String} `string`\n * @param {Object} `options`\n * @return {Boolean} True if front matter exists.\n * @api public\n */\n\nmatter.test = function(str, options) {\n return utils.startsWith(str, defaults(options).delimiters[0]);\n};\n\n/**\n * Detect the language to use, if one is defined after the\n * first front-matter delimiter.\n * @param {String} `string`\n * @param {Object} `options`\n * @return {Object} Object with `raw` (actual language string), and `name`, the language with whitespace trimmed\n */\n\nmatter.language = function(str, options) {\n const opts = defaults(options);\n const open = opts.delimiters[0];\n\n if (matter.test(str)) {\n str = str.slice(open.length);\n }\n\n const language = str.slice(0, str.search(/\\r?\\n/));\n return {\n raw: language,\n name: language ? language.trim() : ''\n };\n};\n\n/**\n * Expose `matter`\n */\n\nmatter.cache = {};\nmatter.clearCache = function() {\n matter.cache = {};\n};\nmodule.exports = matter;\n",
49
49
  "import { z } from \"@brains/utils/zod\";\n\nconst canonicalContentVisibilitySchema: z.ZodEnum<{\n public: \"public\";\n shared: \"shared\";\n restricted: \"restricted\";\n}> = z.enum([\"public\", \"shared\", \"restricted\"]);\n\nexport type ContentVisibility = z.infer<\n typeof canonicalContentVisibilitySchema\n>;\nexport type RawContentVisibility = ContentVisibility | \"private\";\n\nexport const contentVisibilitySchema: z.ZodPipe<\n z.ZodOptional<\n z.ZodUnion<\n readonly [\n typeof canonicalContentVisibilitySchema,\n z.ZodLiteral<\"private\">,\n ]\n >\n >,\n z.ZodTransform<ContentVisibility, RawContentVisibility | undefined>\n> = z\n .union([canonicalContentVisibilitySchema, z.literal(\"private\")])\n .optional()\n .transform((value): ContentVisibility => {\n if (value === undefined) return \"public\";\n if (value === \"private\") return \"restricted\";\n return value;\n });\n\nexport function normalizeContentVisibility(\n visibility: RawContentVisibility | undefined,\n): ContentVisibility {\n return contentVisibilitySchema.parse(visibility);\n}\n\nconst visibleContentVisibilitiesByScope: Record<\n ContentVisibility,\n ContentVisibility[]\n> = {\n public: [\"public\"],\n shared: [\"public\", \"shared\"],\n restricted: [\"public\", \"shared\", \"restricted\"],\n};\n\nexport function getVisibleContentVisibilities(\n scope: ContentVisibility,\n): ContentVisibility[] {\n return visibleContentVisibilitiesByScope[scope];\n}\n\nexport function isVisibleWithinScope(\n visibility: ContentVisibility | undefined,\n scope: ContentVisibility,\n): boolean {\n return getVisibleContentVisibilities(scope).includes(visibility ?? \"public\");\n}\n\n/**\n * Map a caller's permission level to the content-visibility scope they may see.\n * public → public (only public content)\n * trusted → shared (public + shared)\n * anchor → restricted (public + shared + restricted)\n *\n * Defaults to \"public\" when no permission level is provided, so missing\n * context fails closed.\n */\nexport function permissionToVisibilityScope(\n level: \"anchor\" | \"trusted\" | \"public\" | undefined,\n): ContentVisibility {\n if (level === \"anchor\") return \"restricted\";\n if (level === \"trusted\") return \"shared\";\n return \"public\";\n}\n\n/**\n * Whether a caller at `level` is allowed to author or update an entity at\n * `visibility`. A user may only write content at a visibility they themselves\n * can read — otherwise they could ghost-write content into a higher trust\n * level than their permission, which is a write-side escalation vector.\n *\n * public → may write \"public\"\n * trusted → may write \"public\" | \"shared\"\n * anchor → may write \"public\" | \"shared\" | \"restricted\"\n */\nexport function canWriteVisibility(\n level: \"anchor\" | \"trusted\" | \"public\" | undefined,\n visibility: ContentVisibility,\n): boolean {\n return isVisibleWithinScope(visibility, permissionToVisibilityScope(level));\n}\n",
50
- "import { z } from \"@brains/utils/zod\";\nimport {\n contentVisibilitySchema,\n type ContentVisibility,\n type RawContentVisibility,\n} from \"./visibility\";\n\n/**\n * Entity type for unstructured notes (the \"note\" entity type).\n * Used as a sentinel for the default catch-all markdown file shape —\n * no typed frontmatter schema required, content is the entire file body.\n */\nexport const NOTE_ENTITY_TYPE = \"note\";\n\n/**\n * Embedding job data - minimal data for job queue\n * Content is NOT stored to avoid large base64 data in job queue\n * (which would end up in dashboard hydration props JSON)\n * Handler fetches fresh content from entity when processing\n */\nexport interface EmbeddingJobData {\n id: string;\n entityType: string;\n /** Hash of content at job creation time - for staleness detection */\n contentHash: string;\n operation: \"create\" | \"update\";\n}\n\n/**\n * Options for entity mutation operations (create, update, upsert)\n */\nexport interface EntityMutationEventContext {\n conversationId?: string;\n channelId?: string;\n runId?: string;\n toolCallId?: string;\n}\n\nexport interface EntityJobOptions {\n priority?: number;\n maxRetries?: number;\n eventContext?: EntityMutationEventContext;\n}\n\nexport {\n contentVisibilitySchema,\n normalizeContentVisibility,\n getVisibleContentVisibilities,\n isVisibleWithinScope,\n permissionToVisibilityScope,\n canWriteVisibility,\n} from \"./visibility\";\nexport type { ContentVisibility, RawContentVisibility } from \"./visibility\";\n\n/**\n * Options for entity creation (extends EntityJobOptions with deduplication)\n */\nexport interface CreateEntityOptions extends EntityJobOptions {\n deduplicateId?: boolean;\n}\n\n/** Options for updating an existing entity. */\nexport interface UpdateEntityOptions extends EntityJobOptions {\n /** Apply only while the stored entity still has this content hash. */\n expectedContentHash?: string | undefined;\n}\n\n/**\n * Result of an entity mutation that triggers an embedding job.\n * When skipped is true, no DB write, event, or embedding job was produced.\n */\nexport interface EntityMutationResult {\n entityId: string;\n jobId: string;\n skipped: boolean;\n skipReason?: \"content-conflict\" | undefined;\n}\n\n/**\n * Input for adapter-validated direct creation from finalized markdown.\n */\nexport interface CreateEntityFromMarkdownInput {\n entityType: string;\n id: string;\n markdown: string;\n}\n\n/**\n * Data for storing an embedding for an entity\n */\nexport interface StoreEmbeddingData {\n entityId: string;\n entityType: string;\n embedding: Float32Array;\n contentHash: string;\n}\n\n/**\n * Base entity schema that all entities must extend\n */\nexport const baseEntitySchema: z.ZodObject<{\n id: z.ZodString;\n entityType: z.ZodString;\n content: z.ZodString;\n created: z.ZodString;\n updated: z.ZodString;\n visibility: typeof contentVisibilitySchema;\n metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;\n contentHash: z.ZodString;\n}> = z.object({\n id: z.string(),\n entityType: z.string(),\n content: z.string(),\n created: z.string().datetime(),\n updated: z.string().datetime(),\n visibility: contentVisibilitySchema,\n metadata: z.record(z.string(), z.unknown()),\n contentHash: z.string(),\n});\n\nconst canonicalContentVisibilityParserSchema: z.ZodEnum<{\n public: \"public\";\n shared: \"shared\";\n restricted: \"restricted\";\n}> = z.enum([\"public\", \"shared\", \"restricted\"]);\n\n/** Zod 4-owned parser base for entity schemas whose registration slot is structural. */\nexport const baseEntityParserSchema: z.ZodObject<{\n id: z.ZodString;\n entityType: z.ZodString;\n content: z.ZodString;\n created: z.ZodString;\n updated: z.ZodString;\n visibility: z.ZodPipe<\n z.ZodOptional<\n z.ZodUnion<\n readonly [\n typeof canonicalContentVisibilityParserSchema,\n z.ZodLiteral<\"private\">,\n ]\n >\n >,\n z.ZodTransform<ContentVisibility, RawContentVisibility | undefined>\n >;\n metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;\n contentHash: z.ZodString;\n}> = z.object({\n id: z.string(),\n entityType: z.string(),\n content: z.string(),\n created: z.string().datetime(),\n updated: z.string().datetime(),\n visibility: z\n .union([canonicalContentVisibilityParserSchema, z.literal(\"private\")])\n .optional()\n .transform((value): ContentVisibility => {\n if (value === undefined) return \"public\";\n if (value === \"private\") return \"restricted\";\n return value;\n }),\n metadata: z.record(z.string(), z.unknown()),\n contentHash: z.string(),\n});\n\n/** Shared empty frontmatter schema for entity types with no typed frontmatter. */\nexport const emptyFrontmatterSchema: z.ZodObject<Record<string, never>> =\n z.object({});\n\nexport type EntitySchema<T> = z.ZodType<T, unknown>;\n/** @deprecated Use EntitySchema<T>. */\nexport type EntitySchemaParser<T> = EntitySchema<T>;\n\nexport type UnknownEntitySchema = EntitySchema<unknown>;\nexport type FrontmatterSchema = z.ZodObject<z.ZodRawShape>;\n\n/**\n * Base entity type - generic to support typed metadata\n * TMetadata defaults to Record<string, unknown> for backward compatibility\n */\nexport interface BaseEntity<TMetadata = Record<string, unknown>> {\n id: string;\n entityType: string;\n content: string;\n created: string;\n updated: string;\n visibility: ContentVisibility;\n metadata: TMetadata;\n /** SHA256 hash of content for change detection */\n contentHash: string;\n}\n\n/**\n * Entity input type for creation - allows partial entities with optional system fields\n * contentHash is excluded because it's computed automatically by the entity service\n */\nexport type EntityInput<T extends BaseEntity> = Omit<\n T,\n \"id\" | \"created\" | \"updated\" | \"contentHash\" | \"visibility\"\n> & {\n id?: string;\n created?: string;\n updated?: string;\n visibility?: RawContentVisibility;\n};\n\n/**\n * Search result type\n */\nexport interface SearchResult<T extends BaseEntity = BaseEntity> {\n entity: T;\n score: number;\n excerpt: string;\n}\n\n/**\n * Normalized system_create input shape used by plugin create interceptors.\n */\nexport interface CreateCoverImageInput {\n generate?: boolean | undefined;\n prompt?: string | undefined;\n}\n\nexport interface CreateFromAttachmentInput {\n kind: \"entity-attachment\";\n sourceEntityType: string;\n sourceEntityId: string;\n attachmentType: string;\n}\n\nexport interface CreateFromUploadInput {\n kind: \"upload\";\n id: string;\n}\n\nexport interface CreateFromConversationMessageInput {\n kind: \"conversation-message\";\n messageId?: string | undefined;\n}\n\nexport type CreateFromInput =\n | CreateFromAttachmentInput\n | CreateFromUploadInput\n | CreateFromConversationMessageInput;\n\nexport type CreateTransform = \"extract-markdown\" | \"preserve\";\n\nexport interface CreateInput {\n entityType: string;\n prompt?: string;\n title?: string;\n content?: string;\n url?: string;\n from?: CreateFromInput;\n transform?: CreateTransform;\n replace?: boolean;\n sourceEntityType?: string;\n sourceEntityId?: string;\n sourceEntityIds?: string[];\n targetEntityType?: string;\n targetEntityId?: string;\n coverImage?: boolean | CreateCoverImageInput;\n}\n\n/**\n * Minimal caller context forwarded to plugin create interceptors.\n */\nexport interface CreateExecutionContext {\n interfaceType: string;\n userId: string;\n channelId?: string;\n channelName?: string;\n}\n\n/**\n * Result returned to system_create when a plugin fully handles creation.\n */\nexport const createResultAttachmentSchema: z.ZodObject<{\n mediaType: z.ZodString;\n url: z.ZodString;\n downloadUrl: z.ZodOptional<z.ZodString>;\n previewUrl: z.ZodOptional<z.ZodString>;\n filename: z.ZodOptional<z.ZodString>;\n sizeBytes: z.ZodOptional<z.ZodNumber>;\n source: z.ZodOptional<\n z.ZodObject<{\n entityType: z.ZodOptional<z.ZodString>;\n entityId: z.ZodOptional<z.ZodString>;\n attachmentType: z.ZodOptional<z.ZodString>;\n }>\n >;\n}> = z.object({\n mediaType: z.string(),\n url: z.string(),\n downloadUrl: z.string().optional(),\n previewUrl: z.string().optional(),\n filename: z.string().optional(),\n sizeBytes: z.number().optional(),\n source: z\n .object({\n entityType: z.string().optional(),\n entityId: z.string().optional(),\n attachmentType: z.string().optional(),\n })\n .optional(),\n});\n\nexport type CreateResultAttachment = z.infer<\n typeof createResultAttachmentSchema\n>;\n\nexport type CreateResult =\n | {\n success: true;\n data: {\n entityId?: string;\n jobId?: string;\n status: string;\n attachment?: CreateResultAttachment;\n };\n }\n | { success: false; error: string };\n\n/**\n * Plugin create interceptors can either fully handle creation,\n * or continue with a rewritten normalized input.\n */\nexport type CreateInterceptionResult =\n | { kind: \"handled\"; result: CreateResult }\n | { kind: \"continue\"; input: CreateInput };\n\nexport type CreateInterceptor = (\n input: CreateInput,\n executionContext: CreateExecutionContext,\n) => Promise<CreateInterceptionResult>;\n\nexport interface UploadSaveInput {\n upload: CreateFromUploadInput;\n title?: string;\n}\n\nexport type UploadSaveHandler = (\n input: UploadSaveInput,\n executionContext: CreateExecutionContext,\n) => Promise<CreateResult>;\n\nexport interface UploadSaveHandlerRegistration {\n entityType: string;\n mediaTypes: string[];\n handler: UploadSaveHandler;\n}\n\n/**\n * Called before an entity is persisted (on create or update). Throws to reject\n * the write with an operator-facing error. Use this for cross-entity invariants\n * the per-entity Zod schema cannot express.\n */\nexport type PersistValidator<T extends BaseEntity = BaseEntity> = (\n entity: T,\n context: { operation: \"create\" | \"update\" },\n) => Promise<void>;\n\n/**\n * Minimal event bus contract used by entity-service for lifecycle events.\n * Kept structural to avoid coupling this package to a concrete messaging service.\n */\nexport interface EntityEventBus {\n send(request: {\n type: string;\n payload: Record<string, unknown>;\n sender: string;\n target?: string;\n metadata?: Record<string, unknown>;\n broadcast?: boolean;\n }): Promise<unknown>;\n}\n\n/**\n * Interface for entity adapter - handles conversion between entities and markdown\n * following the hybrid storage model\n *\n * @template TEntity - The full entity type\n * @template TMetadata - The metadata type (defaults to Record<string, unknown>)\n */\nexport interface EntityAdapter<\n TEntity extends BaseEntity<TMetadata>,\n TMetadata = Record<string, unknown>,\n> {\n entityType: string;\n\n /**\n * One declarative sentence describing what this entity type is. This is the\n * data the model reads to select `entityType`, replacing hardcoded example\n * phrasings in system instructions. Required: every type must define itself.\n */\n purpose: string;\n\n schema: EntitySchema<TEntity>;\n\n // Convert entity to markdown content (may include frontmatter for entity-specific fields)\n toMarkdown(entity: TEntity): string;\n\n // Extract entity-specific fields from markdown\n // Returns Partial<TEntity> as core fields come from database\n fromMarkdown(markdown: string): Partial<TEntity>;\n\n // Extract metadata from entity for search/filtering - now strongly typed\n extractMetadata(entity: TEntity): TMetadata;\n\n // Parse frontmatter metadata from markdown\n parseFrontMatter<TFrontmatter>(\n markdown: string,\n schema: { parse(data: unknown): TFrontmatter },\n ): TFrontmatter;\n\n // Generate frontmatter for markdown\n generateFrontMatter(entity: TEntity): string;\n\n /** Optional: Zod schema for frontmatter fields. Used by CMS config generation. */\n frontmatterSchema?: z.ZodObject<z.ZodRawShape>;\n\n /** Optional: Declares this entity type is a singleton (one file, e.g., identity/identity.md). Used by CMS to generate files collection. */\n isSingleton?: boolean;\n\n /** Optional: Whether this entity has a free-form markdown body below frontmatter. Defaults to true. When false, CMS omits the body widget. */\n hasBody?: boolean;\n\n /** Returns a markdown body template with section headings for this entity type. Empty string for free-form entities. */\n getBodyTemplate(): string;\n\n /** Optional: Declares that this entity type supports cover images via coverImageId in frontmatter */\n supportsCoverImage?: boolean;\n\n /**\n * Optional: the `metadata.status` values that count as published for\n * `publishedOnly` queries (production site builds). Declaring this makes the\n * list exact — entities without a status are NOT published. When absent, the\n * default lifecycle semantics apply: status `published`, `active`, or no\n * status at all.\n */\n publishedStatuses?: string[];\n\n /** Optional: Extract coverImageId from entity content/frontmatter */\n getCoverImageId?(entity: TEntity): string | undefined;\n\n /**\n * Optional: build the markdown content and metadata for a queued-generation stub.\n * When undefined, this entity type does not support prompt-based queued creation\n * via system_create; the tool will reject the call rather than silently degrade.\n * The returned metadata must satisfy this entity's metadata schema (with\n * status set to \"generating\"); central code only stamps id/timestamps/visibility.\n */\n buildStub?(input: { id: string; title: string }): {\n content: string;\n metadata: TMetadata;\n };\n}\n\n/**\n * Sort field specification for multi-field sorting\n */\nexport interface SortField {\n /** Field to sort by - \"created\", \"updated\", or a metadata field name */\n field: string;\n /** Sort direction */\n direction: \"asc\" | \"desc\";\n /** Sort NULL values before non-NULL values (default: false / SQLite default) */\n nullsFirst?: boolean;\n}\n\n/**\n * List entities options\n * Generic over metadata type for type-safe filtering\n */\nexport interface ListOptions<TMetadata = Record<string, unknown>> {\n limit?: number;\n offset?: number;\n /** Multi-field sorting - supports system fields (created, updated) and metadata fields */\n sortFields?: SortField[];\n filter?: {\n // Typed metadata filter - partial match on metadata fields\n metadata?: Partial<TMetadata>;\n visibilityScope?: ContentVisibility;\n };\n /** Filter to only entities with metadata.status = \"published\" */\n publishedOnly?: boolean;\n}\n\n/**\n * Search options\n */\nexport interface SearchOptions {\n limit?: number;\n offset?: number;\n types?: string[];\n excludeTypes?: string[];\n sortBy?: \"relevance\" | \"created\" | \"updated\";\n sortDirection?: \"asc\" | \"desc\";\n /** Score multipliers per entity type - applied after initial search */\n weight?: Record<string, number>;\n visibilityScope?: ContentVisibility;\n /** Include queued/failed generation stubs in search results (default: false) */\n includeUngenerated?: boolean;\n /** Minimum relevance score to return. Omit for no score cutoff. */\n minScore?: number;\n}\n\nexport type ProjectionSourceRole =\n \"canonical\" | \"primary\" | \"supporting\" | \"ambient\" | \"excluded\";\n\n/**\n * Configuration for entity type registration\n */\nexport interface EntityTypeConfig {\n /** Score multiplier for search results (default: 1.0) */\n weight?: number;\n /** Whether to generate embeddings for this entity type (default: true).\n * Set to false for entity types with non-textual content (e.g., images). */\n embeddable?: boolean;\n /** Whether this entity type may be used as source material for derived projections (default: true).\n * Set to false for projection outputs that would create feedback loops. */\n projectionSource?: boolean;\n /** Default source authority role for derived projections. Entity types own\n * their durable/source character; consumers may map roles to local policy. */\n projectionSourceRole?: ProjectionSourceRole;\n /** Publish semantics for status-bearing entity types. Statuses listed here\n * represent publication commitment/execution states and require the\n * `publish` entity action when entered or modified. */\n publish?: {\n publishStatuses: string[];\n };\n}\n\n/**\n * Core entity service interface for read-only operations\n * Used by core plugins that need entity access but shouldn't modify entities\n */\nexport interface GetEntityRequest {\n entityType: string;\n id: string;\n /**\n * Optional visibility scope. Undefined fails closed to \"public\" — callers\n * with elevated access must opt up explicitly.\n */\n visibilityScope?: ContentVisibility;\n}\n\nexport type GetEntityRawRequest = GetEntityRequest;\n\nexport interface ListEntitiesRequest {\n entityType: string;\n options?: ListOptions | undefined;\n}\n\nexport interface CountEntitiesRequest {\n entityType: string;\n options?: Pick<ListOptions, \"publishedOnly\" | \"filter\"> | undefined;\n}\n\nexport interface CreateEntityRequest<T extends BaseEntity> {\n entity: EntityInput<T>;\n options?: CreateEntityOptions | undefined;\n}\n\nexport interface CreateEntityFromMarkdownRequest {\n input: CreateEntityFromMarkdownInput;\n options?: CreateEntityOptions | undefined;\n}\n\nexport interface UpdateEntityRequest<T extends BaseEntity> {\n entity: T;\n options?: UpdateEntityOptions | undefined;\n}\n\nexport interface DeleteEntityRequest {\n entityType: string;\n id: string;\n}\n\nexport interface UpsertEntityRequest<T extends BaseEntity> {\n entity: T;\n options?: EntityJobOptions | undefined;\n}\n\nexport interface EntitySearchRequest {\n query: string;\n options?: SearchOptions | undefined;\n}\n\nexport interface SearchWithDistancesRequest {\n query: string;\n}\n\nexport interface SemanticEntityReference {\n entityId: string;\n entityType: string;\n}\n\nexport interface ProjectSemanticSpaceRequest {\n /** Entity types to include as projected points. Empty or omitted includes all types. */\n types?: string[];\n /** Optional entity used as the semantic origin. Missing origins fall back to the point centroid. */\n origin?: SemanticEntityReference;\n /** Include pairwise neighbors at or below this cosine distance. */\n maxNeighborDistance?: number;\n /** Undefined fails closed to public-only visibility. */\n visibilityScope?: ContentVisibility;\n}\n\nexport interface SemanticSpacePoint extends SemanticEntityReference {\n /** Coordinates on the first two principal components of the semantic space. */\n coordinates: [number, number];\n /** Cosine distance from the requested origin or the fallback centroid. */\n distanceToOrigin: number;\n}\n\nexport interface SemanticSpaceNeighbor {\n source: SemanticEntityReference;\n target: SemanticEntityReference;\n distance: number;\n}\n\nexport type SemanticSpaceOrigin =\n ({ kind: \"entity\" } & SemanticEntityReference) | { kind: \"centroid\" };\n\nexport interface SemanticSpaceDistanceRange {\n min: number;\n max: number;\n}\n\nexport interface SemanticSpaceProjection {\n origin: SemanticSpaceOrigin;\n points: SemanticSpacePoint[];\n neighbors: SemanticSpaceNeighbor[];\n /** Observed origin-distance extent for projection-independent scaling. */\n distanceRange: SemanticSpaceDistanceRange;\n}\n\n/**\n * Context passed to all DataSource operations\n * Contains internal state that should not be mixed with user query parameters\n */\nexport interface BaseDataSourceContext {\n /**\n * Whether to filter to only published/complete content\n * Set by site-builder: true for production, false for preview\n */\n publishedOnly?: boolean;\n\n /**\n * Scoped entity service that auto-applies publishedOnly filter\n * Datasources should use this instead of their injected entityService\n * to ensure consistent filtering behavior across environments\n */\n entityService: EntityService;\n}\n\nexport type DataSourceSchema<T> = z.ZodType<T, unknown>;\n\n/**\n * DataSource Interface\n *\n * Provides data for templates through fetch, generate, or transform operations.\n * DataSources are registered in the DataSourceRegistry and referenced by templates\n * via their dataSourceId property.\n */\nexport interface DataSource {\n /**\n * Unique identifier for this data source\n */\n id: string;\n\n /**\n * Human-readable name for this data source\n */\n name: string;\n\n /**\n * Optional description of what this data source provides\n */\n description?: string;\n\n /**\n * Optional: Fetch existing data\n * Used by data sources that aggregate or retrieve data (e.g., dashboard stats, API data)\n * DataSources validate output using the provided schema\n * @param query - Query parameters for fetching data\n * @param outputSchema - Schema for validating output data\n * @param context - Context with environment\n */\n fetch?: <T>(\n query: unknown,\n outputSchema: z.ZodSchema<T>,\n context: BaseDataSourceContext,\n ) => Promise<T>;\n\n /**\n * Optional: Generate new content\n * Used by data sources that create content (e.g., AI-generated content, reports)\n */\n generate?: <T>(request: unknown, schema: z.ZodSchema<T>) => Promise<T>;\n\n /**\n * Optional: Transform content between formats\n * Used by data sources that convert content (e.g., markdown to HTML, data formatting)\n */\n transform?: <T>(\n content: unknown,\n format: string,\n schema: z.ZodSchema<T>,\n ) => Promise<T>;\n}\n\n/**\n * DataSource capabilities for discovery and validation\n */\nexport interface DataSourceCapabilities {\n canFetch: boolean;\n canGenerate: boolean;\n canTransform: boolean;\n}\n\nexport interface ICoreEntityService {\n // Read-only operations\n getEntity<T extends BaseEntity>(request: GetEntityRequest): Promise<T | null>;\n\n /**\n * Get entity without content resolution (raw)\n * Used internally to avoid recursion when resolving image references\n */\n getEntityRaw<T extends BaseEntity>(\n request: GetEntityRawRequest,\n ): Promise<T | null>;\n\n listEntities<T extends BaseEntity>(\n request: ListEntitiesRequest,\n ): Promise<T[]>;\n\n search<T extends BaseEntity = BaseEntity>(\n request: EntitySearchRequest,\n ): Promise<SearchResult<T>[]>;\n\n /** Return embedded entities with raw cosine distance to a query. */\n searchWithDistances(\n request: SearchWithDistancesRequest,\n ): Promise<Array<{ entityId: string; entityType: string; distance: number }>>;\n\n /** Project visible entities into a provider-independent semantic space. */\n projectSemanticSpace(\n request: ProjectSemanticSpaceRequest,\n ): Promise<SemanticSpaceProjection>;\n\n // Entity type information\n getEntityTypes(): string[];\n hasEntityType(type: string): boolean;\n\n // Entity counts\n countEntities(request: CountEntitiesRequest): Promise<number>;\n /**\n * Group counts by entity type. Fails closed: undefined visibilityScope\n * filters to public-only counts so aggregate insights cannot reveal\n * non-public entity existence.\n */\n getEntityCounts(\n visibilityScope?: ContentVisibility,\n ): Promise<Array<{ entityType: string; count: number }>>;\n\n /** Get configuration for a specific entity type */\n getEntityTypeConfig(type: string): EntityTypeConfig;\n\n /** Get weight map for all registered entity types with non-default weights */\n getWeightMap(): Record<string, number>;\n}\n\n/**\n * Entity service interface for managing brain entities\n */\nexport interface IEntitiesNamespace {\n /** Register a new entity type with schema and adapter */\n register<TEntity extends BaseEntity>(\n entityType: string,\n schema: EntitySchema<TEntity>,\n adapter: EntityAdapter<TEntity>,\n config?: EntityTypeConfig,\n ): void;\n\n /**\n * Get the adapter for an entity type.\n *\n * Returns the structural `EntityAdapter<BaseEntity>` view — namespace\n * consumers don't narrow by entity type. For typed access tied to a\n * specific `TEntity`, use the underlying `EntityRegistry.getAdapter<T>`\n * directly (see `entity-serializer.ts`).\n */\n getAdapter(entityType: string): EntityAdapter<BaseEntity> | undefined;\n\n /** Extend an adapter's frontmatterSchema with additional fields */\n extendFrontmatterSchema(\n type: string,\n extension: z.ZodObject<z.ZodRawShape>,\n ): void;\n\n /** Get effective frontmatter schema (base + extensions) for an entity type */\n getEffectiveFrontmatterSchema(\n type: string,\n ): z.ZodObject<z.ZodRawShape> | undefined;\n\n /** Update an existing entity */\n update<TEntity extends BaseEntity>(\n entity: TEntity,\n ): Promise<{ entityId: string; jobId: string }>;\n\n /** Register a data source for dynamic content */\n registerDataSource(dataSource: DataSource): void;\n\n /** Register a create interceptor for this plugin's entity type */\n registerCreateInterceptor(\n entityType: string,\n interceptor: CreateInterceptor,\n ): void;\n\n /** Register a raw-upload durable save handler for this plugin's entity type */\n registerUploadSaveHandler(registration: UploadSaveHandlerRegistration): void;\n\n /** Look up the registered upload-save handler claiming a media type */\n getUploadSaveHandler(\n mediaType: string,\n ): UploadSaveHandlerRegistration | undefined;\n}\n\nexport interface EmbeddingBackfillResult {\n queued: number;\n skipped: number;\n}\n\nexport interface EmbeddingFailureReference {\n entityId: string;\n entityType: string;\n contentHash: string;\n}\n\nexport interface EmbeddingIndexStats {\n missingEmbeddings: number;\n staleEmbeddings: number;\n failedEmbeddings: number;\n /** Entities whose type generates embeddings (non-embeddable types excluded). */\n embeddableEntities: number;\n /** Embeddable entities whose embedding is present and current. */\n embeddedEntities: number;\n}\n\nexport interface IndexReadinessOptions {\n /** Stop polling after this duration. Omit for an owning runtime monitor. */\n timeoutMs?: number;\n intervalMs?: number;\n /** Cancels readiness polling when the owning runtime shuts down. */\n signal?: AbortSignal;\n}\n\nexport interface IndexReadinessStatus extends EmbeddingIndexStats {\n ready: boolean;\n degraded: boolean;\n activeEmbeddingJobs: number;\n}\n\nexport interface EntityService extends ICoreEntityService {\n // Mutations\n createEntity<T extends BaseEntity>(\n request: CreateEntityRequest<T>,\n ): Promise<EntityMutationResult>;\n createEntityFromMarkdown(\n request: CreateEntityFromMarkdownRequest,\n ): Promise<EntityMutationResult>;\n updateEntity<T extends BaseEntity>(\n request: UpdateEntityRequest<T>,\n ): Promise<EntityMutationResult>;\n deleteEntity(request: DeleteEntityRequest): Promise<boolean>;\n upsertEntity<T extends BaseEntity>(\n request: UpsertEntityRequest<T>,\n ): Promise<EntityMutationResult & { created: boolean }>;\n storeEmbedding(data: StoreEmbeddingData): Promise<void>;\n backfillMissingEmbeddings(): Promise<EmbeddingBackfillResult>;\n isIndexReady(): boolean;\n awaitIndexReady(\n options: IndexReadinessOptions,\n ): Promise<IndexReadinessStatus>;\n\n // Serialization\n serializeEntity(entity: BaseEntity): string;\n deserializeEntity(markdown: string, entityType: string): Partial<BaseEntity>;\n\n // Counts\n countEmbeddings(): Promise<number>;\n\n // Diagnostics\n searchWithDistances(\n request: SearchWithDistancesRequest,\n ): Promise<Array<{ entityId: string; entityType: string; distance: number }>>;\n\n // Lifecycle\n initialize(): Promise<void>;\n\n // Job status\n getAsyncJobStatus(jobId: string): Promise<{\n status: \"pending\" | \"processing\" | \"completed\" | \"failed\";\n error?: string;\n } | null>;\n}\n\n/**\n * Entity Registry interface for managing entity types and their schemas/adapters\n */\nexport interface EntityRegistry {\n registerEntityType<\n TEntity extends BaseEntity<TMetadata>,\n TMetadata = Record<string, unknown>,\n >(\n type: string,\n schema: UnknownEntitySchema,\n adapter: EntityAdapter<TEntity, TMetadata>,\n config?: EntityTypeConfig,\n ): void;\n\n /** Remove an entity type after failed plugin registration or shell teardown. */\n unregisterEntityType(type: string): void;\n\n getSchema(type: string): UnknownEntitySchema;\n\n getAdapter<\n TEntity extends BaseEntity<TMetadata>,\n TMetadata = Record<string, unknown>,\n >(\n type: string,\n ): EntityAdapter<TEntity, TMetadata>;\n\n hasEntityType(type: string): boolean;\n\n validateEntity(type: string, entity: unknown): BaseEntity;\n\n getAllEntityTypes(): string[];\n\n /** Get configuration for a specific entity type */\n getEntityTypeConfig(type: string): EntityTypeConfig;\n\n /** Get weight map for all registered entity types with non-default weights */\n getWeightMap(): Record<string, number>;\n\n registerCreateInterceptor(type: string, interceptor: CreateInterceptor): void;\n\n getCreateInterceptor(type: string): CreateInterceptor | undefined;\n\n registerUploadSaveHandler(registration: UploadSaveHandlerRegistration): void;\n\n getUploadSaveHandler(\n mediaType: string,\n ): UploadSaveHandlerRegistration | undefined;\n\n registerPersistValidator(type: string, validator: PersistValidator): void;\n\n getPersistValidator(type: string): PersistValidator | undefined;\n\n /**\n * Extend an adapter's frontmatterSchema with additional fields.\n * Used by plugins to add domain-specific fields (e.g., professional-site adds tagline to profile).\n * Extensions are merged into the effective schema returned by getEffectiveFrontmatterSchema().\n */\n extendFrontmatterSchema(\n type: string,\n extension: z.ZodObject<z.ZodRawShape>,\n ): void;\n\n /**\n * Get the effective frontmatter schema for an entity type,\n * with all registered extensions merged in.\n * Returns undefined if the adapter has no frontmatterSchema.\n */\n getEffectiveFrontmatterSchema(\n type: string,\n ): z.ZodObject<z.ZodRawShape> | undefined;\n}\n\n/**\n * Database configuration for entity service\n */\nexport type { DbConfig as EntityDbConfig } from \"@brains/contracts\";\n",
50
+ "import { z } from \"@brains/utils/zod\";\nimport {\n contentVisibilitySchema,\n type ContentVisibility,\n type RawContentVisibility,\n} from \"./visibility\";\n\n/**\n * Entity type for unstructured notes (the \"note\" entity type).\n * Used as a sentinel for the default catch-all markdown file shape —\n * no typed frontmatter schema required, content is the entire file body.\n */\nexport const NOTE_ENTITY_TYPE = \"note\";\n\n/**\n * Embedding job data - minimal data for job queue\n * Content is NOT stored to avoid large base64 data in job queue\n * (which would end up in dashboard hydration props JSON)\n * Handler fetches fresh content from entity when processing\n */\nexport interface EmbeddingJobData {\n id: string;\n entityType: string;\n /** Hash of content at job creation time - for staleness detection */\n contentHash: string;\n operation: \"create\" | \"update\";\n}\n\n/**\n * Options for entity mutation operations (create, update, upsert)\n */\nexport interface EntityMutationEventContext {\n conversationId?: string;\n channelId?: string;\n runId?: string;\n toolCallId?: string;\n}\n\nexport interface EntityJobOptions {\n priority?: number;\n maxRetries?: number;\n eventContext?: EntityMutationEventContext;\n}\n\nexport {\n contentVisibilitySchema,\n normalizeContentVisibility,\n getVisibleContentVisibilities,\n isVisibleWithinScope,\n permissionToVisibilityScope,\n canWriteVisibility,\n} from \"./visibility\";\nexport type { ContentVisibility, RawContentVisibility } from \"./visibility\";\n\n/**\n * Options for entity creation (extends EntityJobOptions with deduplication)\n */\nexport interface CreateEntityOptions extends EntityJobOptions {\n deduplicateId?: boolean;\n}\n\n/** Options for updating an existing entity. */\nexport interface UpdateEntityOptions extends EntityJobOptions {\n /** Apply only while the stored entity still has this content hash. */\n expectedContentHash?: string | undefined;\n}\n\n/**\n * Result of an entity mutation that triggers an embedding job.\n * When skipped is true, no DB write, event, or embedding job was produced.\n */\nexport interface EntityMutationResult {\n entityId: string;\n jobId: string;\n skipped: boolean;\n skipReason?: \"content-conflict\" | undefined;\n}\n\n/**\n * Input for adapter-validated direct creation from finalized markdown.\n */\nexport interface CreateEntityFromMarkdownInput {\n entityType: string;\n id: string;\n markdown: string;\n}\n\n/**\n * Data for storing an embedding for an entity\n */\nexport interface StoreEmbeddingData {\n entityId: string;\n entityType: string;\n embedding: Float32Array;\n contentHash: string;\n}\n\n/**\n * Base entity schema that all entities must extend\n */\nexport const baseEntitySchema: z.ZodObject<{\n id: z.ZodString;\n entityType: z.ZodString;\n content: z.ZodString;\n created: z.ZodString;\n updated: z.ZodString;\n visibility: typeof contentVisibilitySchema;\n metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;\n contentHash: z.ZodString;\n}> = z.object({\n id: z.string(),\n entityType: z.string(),\n content: z.string(),\n created: z.string().datetime(),\n updated: z.string().datetime(),\n visibility: contentVisibilitySchema,\n metadata: z.record(z.string(), z.unknown()),\n contentHash: z.string(),\n});\n\nconst canonicalContentVisibilityParserSchema: z.ZodEnum<{\n public: \"public\";\n shared: \"shared\";\n restricted: \"restricted\";\n}> = z.enum([\"public\", \"shared\", \"restricted\"]);\n\n/** Zod 4-owned parser base for entity schemas whose registration slot is structural. */\nexport const baseEntityParserSchema: z.ZodObject<{\n id: z.ZodString;\n entityType: z.ZodString;\n content: z.ZodString;\n created: z.ZodString;\n updated: z.ZodString;\n visibility: z.ZodPipe<\n z.ZodOptional<\n z.ZodUnion<\n readonly [\n typeof canonicalContentVisibilityParserSchema,\n z.ZodLiteral<\"private\">,\n ]\n >\n >,\n z.ZodTransform<ContentVisibility, RawContentVisibility | undefined>\n >;\n metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;\n contentHash: z.ZodString;\n}> = z.object({\n id: z.string(),\n entityType: z.string(),\n content: z.string(),\n created: z.string().datetime(),\n updated: z.string().datetime(),\n visibility: z\n .union([canonicalContentVisibilityParserSchema, z.literal(\"private\")])\n .optional()\n .transform((value): ContentVisibility => {\n if (value === undefined) return \"public\";\n if (value === \"private\") return \"restricted\";\n return value;\n }),\n metadata: z.record(z.string(), z.unknown()),\n contentHash: z.string(),\n});\n\n/** Shared empty frontmatter schema for entity types with no typed frontmatter. */\nexport const emptyFrontmatterSchema: z.ZodObject<Record<string, never>> =\n z.object({});\n\nexport type EntitySchema<T> = z.ZodType<T, unknown>;\n/** @deprecated Use EntitySchema<T>. */\nexport type EntitySchemaParser<T> = EntitySchema<T>;\n\nexport type UnknownEntitySchema = EntitySchema<unknown>;\nexport type FrontmatterSchema = z.ZodObject<z.ZodRawShape>;\n\n/**\n * Base entity type - generic to support typed metadata\n * TMetadata defaults to Record<string, unknown> for backward compatibility\n */\nexport interface BaseEntity<TMetadata = Record<string, unknown>> {\n id: string;\n entityType: string;\n content: string;\n created: string;\n updated: string;\n visibility: ContentVisibility;\n metadata: TMetadata;\n /** SHA256 hash of content for change detection */\n contentHash: string;\n}\n\n/**\n * Entity input type for creation - allows partial entities with optional system fields\n * contentHash is excluded because it's computed automatically by the entity service\n */\nexport type EntityInput<T extends BaseEntity> = Omit<\n T,\n \"id\" | \"created\" | \"updated\" | \"contentHash\" | \"visibility\"\n> & {\n id?: string;\n created?: string;\n updated?: string;\n visibility?: RawContentVisibility;\n};\n\n/**\n * Search result type\n */\nexport interface SearchResult<T extends BaseEntity = BaseEntity> {\n entity: T;\n score: number;\n excerpt: string;\n}\n\n/**\n * Normalized system_create input shape used by plugin create interceptors.\n */\nexport interface CreateCoverImageInput {\n generate?: boolean | undefined;\n prompt?: string | undefined;\n}\n\nexport interface CreateFromAttachmentInput {\n kind: \"entity-attachment\";\n sourceEntityType: string;\n sourceEntityId: string;\n attachmentType: string;\n}\n\nexport interface CreateFromUploadInput {\n kind: \"upload\";\n id: string;\n}\n\nexport interface CreateFromConversationMessageInput {\n kind: \"conversation-message\";\n messageId?: string | undefined;\n}\n\nexport type CreateFromInput =\n | CreateFromAttachmentInput\n | CreateFromUploadInput\n | CreateFromConversationMessageInput;\n\nexport type CreateTransform = \"extract-markdown\" | \"preserve\";\n\nexport interface CreateInput {\n entityType: string;\n prompt?: string;\n title?: string;\n content?: string;\n url?: string;\n from?: CreateFromInput;\n transform?: CreateTransform;\n replace?: boolean;\n sourceEntityType?: string;\n sourceEntityId?: string;\n sourceEntityIds?: string[];\n targetEntityType?: string;\n targetEntityId?: string;\n coverImage?: boolean | CreateCoverImageInput;\n}\n\n/**\n * Minimal caller context forwarded to plugin create interceptors.\n */\nexport interface CreateExecutionContext {\n interfaceType: string;\n userId: string;\n channelId?: string;\n channelName?: string;\n}\n\n/**\n * Result returned to system_create when a plugin fully handles creation.\n */\nexport const createResultAttachmentSchema: z.ZodObject<{\n mediaType: z.ZodString;\n url: z.ZodString;\n downloadUrl: z.ZodOptional<z.ZodString>;\n previewUrl: z.ZodOptional<z.ZodString>;\n filename: z.ZodOptional<z.ZodString>;\n sizeBytes: z.ZodOptional<z.ZodNumber>;\n source: z.ZodOptional<\n z.ZodObject<{\n entityType: z.ZodOptional<z.ZodString>;\n entityId: z.ZodOptional<z.ZodString>;\n attachmentType: z.ZodOptional<z.ZodString>;\n }>\n >;\n}> = z.object({\n mediaType: z.string(),\n url: z.string(),\n downloadUrl: z.string().optional(),\n previewUrl: z.string().optional(),\n filename: z.string().optional(),\n sizeBytes: z.number().optional(),\n source: z\n .object({\n entityType: z.string().optional(),\n entityId: z.string().optional(),\n attachmentType: z.string().optional(),\n })\n .optional(),\n});\n\nexport type CreateResultAttachment = z.infer<\n typeof createResultAttachmentSchema\n>;\n\nexport type CreateResult =\n | {\n success: true;\n data: {\n entityId?: string;\n jobId?: string;\n status: string;\n attachment?: CreateResultAttachment;\n };\n }\n | { success: false; error: string };\n\n/**\n * Plugin create interceptors can either fully handle creation,\n * or continue with a rewritten normalized input.\n */\nexport type CreateInterceptionResult =\n | { kind: \"handled\"; result: CreateResult }\n | { kind: \"continue\"; input: CreateInput };\n\nexport type CreateInterceptor = (\n input: CreateInput,\n executionContext: CreateExecutionContext,\n) => Promise<CreateInterceptionResult>;\n\nexport interface UploadSaveInput {\n upload: CreateFromUploadInput;\n title?: string;\n}\n\nexport type UploadSaveHandler = (\n input: UploadSaveInput,\n executionContext: CreateExecutionContext,\n) => Promise<CreateResult>;\n\nexport interface UploadSaveHandlerRegistration {\n entityType: string;\n mediaTypes: string[];\n handler: UploadSaveHandler;\n}\n\n/**\n * Called before an entity is persisted (on create or update). Throws to reject\n * the write with an operator-facing error. Use this for cross-entity invariants\n * the per-entity Zod schema cannot express.\n */\nexport type PersistValidator<T extends BaseEntity = BaseEntity> = (\n entity: T,\n context: { operation: \"create\" | \"update\" },\n) => Promise<void>;\n\n/**\n * Minimal event bus contract used by entity-service for lifecycle events.\n * Kept structural to avoid coupling this package to a concrete messaging service.\n */\nexport interface EntityEventBus {\n send(request: {\n type: string;\n payload: Record<string, unknown>;\n sender: string;\n target?: string;\n metadata?: Record<string, unknown>;\n broadcast?: boolean;\n }): Promise<unknown>;\n}\n\n/**\n * Interface for entity adapter - handles conversion between entities and markdown\n * following the hybrid storage model\n *\n * @template TEntity - The full entity type\n * @template TMetadata - The metadata type (defaults to Record<string, unknown>)\n */\nexport interface EntityAdapter<\n TEntity extends BaseEntity<TMetadata>,\n TMetadata = Record<string, unknown>,\n> {\n entityType: string;\n\n /**\n * One declarative sentence describing what this entity type is. This is the\n * data the model reads to select `entityType`, replacing hardcoded example\n * phrasings in system instructions. Required: every type must define itself.\n */\n purpose: string;\n\n schema: EntitySchema<TEntity>;\n\n // Convert entity to markdown content (may include frontmatter for entity-specific fields)\n toMarkdown(entity: TEntity): string;\n\n // Extract entity-specific fields from markdown\n // Returns Partial<TEntity> as core fields come from database\n fromMarkdown(markdown: string): Partial<TEntity>;\n\n // Extract metadata from entity for search/filtering - now strongly typed\n extractMetadata(entity: TEntity): TMetadata;\n\n // Parse frontmatter metadata from markdown\n parseFrontMatter<TFrontmatter>(\n markdown: string,\n schema: { parse(data: unknown): TFrontmatter },\n ): TFrontmatter;\n\n // Generate frontmatter for markdown\n generateFrontMatter(entity: TEntity): string;\n\n /** Optional: Zod schema for frontmatter fields. Used by CMS config generation. */\n frontmatterSchema?: z.ZodObject<z.ZodRawShape>;\n\n /** Optional: Declares this entity type is a singleton (one file, e.g., identity/identity.md). Used by CMS to generate files collection. */\n isSingleton?: boolean;\n\n /** Optional: Whether this entity has a free-form markdown body below frontmatter. Defaults to true. When false, CMS omits the body widget. */\n hasBody?: boolean;\n\n /** Returns a markdown body template with section headings for this entity type. Empty string for free-form entities. */\n getBodyTemplate(): string;\n\n /** Optional: Declares that this entity type supports cover images via coverImageId in frontmatter */\n supportsCoverImage?: boolean;\n\n /**\n * Optional: the `metadata.status` values that count as published for\n * `publishedOnly` queries (production site builds). Declaring this makes the\n * list exact — entities without a status are NOT published. When absent, the\n * default lifecycle semantics apply: status `published`, `active`, or no\n * status at all.\n */\n publishedStatuses?: string[];\n\n /** Optional: Extract coverImageId from entity content/frontmatter */\n getCoverImageId?(entity: TEntity): string | undefined;\n\n /**\n * Optional: build the markdown content and metadata for a queued-generation stub.\n * When undefined, this entity type does not support prompt-based queued creation\n * via system_create; the tool will reject the call rather than silently degrade.\n * The returned metadata must satisfy this entity's metadata schema (with\n * status set to \"generating\"); central code only stamps id/timestamps/visibility.\n */\n buildStub?(input: { id: string; title: string }): {\n content: string;\n metadata: TMetadata;\n };\n}\n\n/**\n * Sort field specification for multi-field sorting\n */\nexport interface SortField {\n /** Field to sort by - \"created\", \"updated\", or a metadata field name */\n field: string;\n /** Sort direction */\n direction: \"asc\" | \"desc\";\n /** Sort NULL values before non-NULL values (default: false / SQLite default) */\n nullsFirst?: boolean;\n}\n\n/**\n * List entities options\n * Generic over metadata type for type-safe filtering\n */\nexport interface ListOptions<TMetadata = Record<string, unknown>> {\n limit?: number;\n offset?: number;\n /** Multi-field sorting - supports system fields (created, updated) and metadata fields */\n sortFields?: SortField[];\n filter?: {\n // Typed metadata filter - partial match on metadata fields\n metadata?: Partial<TMetadata>;\n visibilityScope?: ContentVisibility;\n };\n /** Filter to only entities with metadata.status = \"published\" */\n publishedOnly?: boolean;\n}\n\n/**\n * Search options\n */\nexport interface SearchOptions {\n limit?: number;\n offset?: number;\n types?: string[];\n excludeTypes?: string[];\n sortBy?: \"relevance\" | \"created\" | \"updated\";\n sortDirection?: \"asc\" | \"desc\";\n /** Score multipliers per entity type - applied after initial search */\n weight?: Record<string, number>;\n visibilityScope?: ContentVisibility;\n /** Include queued/failed generation stubs in search results (default: false) */\n includeUngenerated?: boolean;\n /** Minimum relevance score to return. Omit for no score cutoff. */\n minScore?: number;\n}\n\nexport type ProjectionSourceRole =\n \"canonical\" | \"primary\" | \"secondary\" | \"supporting\" | \"ambient\" | \"excluded\";\n\n/**\n * Configuration for entity type registration\n */\nexport interface EntityTypeConfig {\n /** Score multiplier for search results (default: 1.0) */\n weight?: number;\n /** Whether to generate embeddings for this entity type (default: true).\n * Set to false for entity types with non-textual content (e.g., images). */\n embeddable?: boolean;\n /** Whether this entity type may be used as source material for derived projections (default: true).\n * Set to false for projection outputs that would create feedback loops. */\n projectionSource?: boolean;\n /** Default source authority role for derived projections. Entity types own\n * their durable/source character; consumers may map roles to local policy. */\n projectionSourceRole?: ProjectionSourceRole;\n /** Publish semantics for status-bearing entity types. Statuses listed here\n * represent publication commitment/execution states and require the\n * `publish` entity action when entered or modified. */\n publish?: {\n publishStatuses: string[];\n };\n}\n\n/**\n * Core entity service interface for read-only operations\n * Used by core plugins that need entity access but shouldn't modify entities\n */\nexport interface GetEntityRequest {\n entityType: string;\n id: string;\n /**\n * Optional visibility scope. Undefined fails closed to \"public\" — callers\n * with elevated access must opt up explicitly.\n */\n visibilityScope?: ContentVisibility;\n}\n\nexport type GetEntityRawRequest = GetEntityRequest;\n\nexport interface ListEntitiesRequest {\n entityType: string;\n options?: ListOptions | undefined;\n}\n\nexport interface CountEntitiesRequest {\n entityType: string;\n options?: Pick<ListOptions, \"publishedOnly\" | \"filter\"> | undefined;\n}\n\nexport interface CreateEntityRequest<T extends BaseEntity> {\n entity: EntityInput<T>;\n options?: CreateEntityOptions | undefined;\n}\n\nexport interface CreateEntityFromMarkdownRequest {\n input: CreateEntityFromMarkdownInput;\n options?: CreateEntityOptions | undefined;\n}\n\nexport interface UpdateEntityRequest<T extends BaseEntity> {\n entity: T;\n options?: UpdateEntityOptions | undefined;\n}\n\nexport interface DeleteEntityRequest {\n entityType: string;\n id: string;\n}\n\nexport interface UpsertEntityRequest<T extends BaseEntity> {\n entity: T;\n options?: EntityJobOptions | undefined;\n}\n\nexport interface EntitySearchRequest {\n query: string;\n options?: SearchOptions | undefined;\n}\n\nexport interface SearchWithDistancesRequest {\n query: string;\n}\n\nexport interface SemanticEntityReference {\n entityId: string;\n entityType: string;\n}\n\nexport interface ProjectSemanticSpaceRequest {\n /** Entity types to include as projected points. Empty or omitted includes all types. */\n types?: string[];\n /** Optional entity used as the semantic origin. Missing origins fall back to the point centroid. */\n origin?: SemanticEntityReference;\n /** Include pairwise neighbors at or below this cosine distance. */\n maxNeighborDistance?: number;\n /** Undefined fails closed to public-only visibility. */\n visibilityScope?: ContentVisibility;\n}\n\nexport interface SemanticSpacePoint extends SemanticEntityReference {\n /** Coordinates on the first two principal components of the semantic space. */\n coordinates: [number, number];\n /** Cosine distance from the requested origin or the fallback centroid. */\n distanceToOrigin: number;\n}\n\nexport interface SemanticSpaceNeighbor {\n source: SemanticEntityReference;\n target: SemanticEntityReference;\n distance: number;\n}\n\nexport type SemanticSpaceOrigin =\n ({ kind: \"entity\" } & SemanticEntityReference) | { kind: \"centroid\" };\n\nexport interface SemanticSpaceDistanceRange {\n min: number;\n max: number;\n}\n\nexport interface SemanticSpaceProjection {\n origin: SemanticSpaceOrigin;\n points: SemanticSpacePoint[];\n neighbors: SemanticSpaceNeighbor[];\n /** Observed origin-distance extent for projection-independent scaling. */\n distanceRange: SemanticSpaceDistanceRange;\n}\n\n/**\n * Context passed to all DataSource operations\n * Contains internal state that should not be mixed with user query parameters\n */\nexport interface BaseDataSourceContext {\n /**\n * Whether to filter to only published/complete content\n * Set by site-builder: true for production, false for preview\n */\n publishedOnly?: boolean;\n\n /**\n * Scoped entity service that auto-applies publishedOnly filter\n * Datasources should use this instead of their injected entityService\n * to ensure consistent filtering behavior across environments\n */\n entityService: EntityService;\n}\n\nexport type DataSourceSchema<T> = z.ZodType<T, unknown>;\n\n/**\n * DataSource Interface\n *\n * Provides data for templates through fetch, generate, or transform operations.\n * DataSources are registered in the DataSourceRegistry and referenced by templates\n * via their dataSourceId property.\n */\nexport interface DataSource {\n /**\n * Unique identifier for this data source\n */\n id: string;\n\n /**\n * Human-readable name for this data source\n */\n name: string;\n\n /**\n * Optional description of what this data source provides\n */\n description?: string;\n\n /**\n * Optional: Fetch existing data\n * Used by data sources that aggregate or retrieve data (e.g., dashboard stats, API data)\n * DataSources validate output using the provided schema\n * @param query - Query parameters for fetching data\n * @param outputSchema - Schema for validating output data\n * @param context - Context with environment\n */\n fetch?: <T>(\n query: unknown,\n outputSchema: z.ZodSchema<T>,\n context: BaseDataSourceContext,\n ) => Promise<T>;\n\n /**\n * Optional: Generate new content\n * Used by data sources that create content (e.g., AI-generated content, reports)\n */\n generate?: <T>(request: unknown, schema: z.ZodSchema<T>) => Promise<T>;\n\n /**\n * Optional: Transform content between formats\n * Used by data sources that convert content (e.g., markdown to HTML, data formatting)\n */\n transform?: <T>(\n content: unknown,\n format: string,\n schema: z.ZodSchema<T>,\n ) => Promise<T>;\n}\n\n/**\n * DataSource capabilities for discovery and validation\n */\nexport interface DataSourceCapabilities {\n canFetch: boolean;\n canGenerate: boolean;\n canTransform: boolean;\n}\n\nexport interface ICoreEntityService {\n // Read-only operations\n getEntity<T extends BaseEntity>(request: GetEntityRequest): Promise<T | null>;\n\n /**\n * Get entity without content resolution (raw)\n * Used internally to avoid recursion when resolving image references\n */\n getEntityRaw<T extends BaseEntity>(\n request: GetEntityRawRequest,\n ): Promise<T | null>;\n\n listEntities<T extends BaseEntity>(\n request: ListEntitiesRequest,\n ): Promise<T[]>;\n\n search<T extends BaseEntity = BaseEntity>(\n request: EntitySearchRequest,\n ): Promise<SearchResult<T>[]>;\n\n /** Return embedded entities with raw cosine distance to a query. */\n searchWithDistances(\n request: SearchWithDistancesRequest,\n ): Promise<Array<{ entityId: string; entityType: string; distance: number }>>;\n\n /** Project visible entities into a provider-independent semantic space. */\n projectSemanticSpace(\n request: ProjectSemanticSpaceRequest,\n ): Promise<SemanticSpaceProjection>;\n\n // Entity type information\n getEntityTypes(): string[];\n hasEntityType(type: string): boolean;\n\n // Entity counts\n countEntities(request: CountEntitiesRequest): Promise<number>;\n /**\n * Group counts by entity type. Fails closed: undefined visibilityScope\n * filters to public-only counts so aggregate insights cannot reveal\n * non-public entity existence.\n */\n getEntityCounts(\n visibilityScope?: ContentVisibility,\n ): Promise<Array<{ entityType: string; count: number }>>;\n\n /** Get configuration for a specific entity type */\n getEntityTypeConfig(type: string): EntityTypeConfig;\n\n /** Get weight map for all registered entity types with non-default weights */\n getWeightMap(): Record<string, number>;\n}\n\n/**\n * Entity service interface for managing brain entities\n */\nexport interface IEntitiesNamespace {\n /** Register a new entity type with schema and adapter */\n register<TEntity extends BaseEntity>(\n entityType: string,\n schema: EntitySchema<TEntity>,\n adapter: EntityAdapter<TEntity>,\n config?: EntityTypeConfig,\n ): void;\n\n /**\n * Get the adapter for an entity type.\n *\n * Returns the structural `EntityAdapter<BaseEntity>` view — namespace\n * consumers don't narrow by entity type. For typed access tied to a\n * specific `TEntity`, use the underlying `EntityRegistry.getAdapter<T>`\n * directly (see `entity-serializer.ts`).\n */\n getAdapter(entityType: string): EntityAdapter<BaseEntity> | undefined;\n\n /** Extend an adapter's frontmatterSchema with additional fields */\n extendFrontmatterSchema(\n type: string,\n extension: z.ZodObject<z.ZodRawShape>,\n ): void;\n\n /** Get effective frontmatter schema (base + extensions) for an entity type */\n getEffectiveFrontmatterSchema(\n type: string,\n ): z.ZodObject<z.ZodRawShape> | undefined;\n\n /** Update an existing entity */\n update<TEntity extends BaseEntity>(\n entity: TEntity,\n ): Promise<{ entityId: string; jobId: string }>;\n\n /** Register a data source for dynamic content */\n registerDataSource(dataSource: DataSource): void;\n\n /** Register a create interceptor for this plugin's entity type */\n registerCreateInterceptor(\n entityType: string,\n interceptor: CreateInterceptor,\n ): void;\n\n /** Register a raw-upload durable save handler for this plugin's entity type */\n registerUploadSaveHandler(registration: UploadSaveHandlerRegistration): void;\n\n /** Look up the registered upload-save handler claiming a media type */\n getUploadSaveHandler(\n mediaType: string,\n ): UploadSaveHandlerRegistration | undefined;\n}\n\nexport interface EmbeddingBackfillResult {\n queued: number;\n skipped: number;\n}\n\nexport interface EmbeddingFailureReference {\n entityId: string;\n entityType: string;\n contentHash: string;\n}\n\nexport interface EmbeddingIndexStats {\n missingEmbeddings: number;\n staleEmbeddings: number;\n failedEmbeddings: number;\n /** Entities whose type generates embeddings (non-embeddable types excluded). */\n embeddableEntities: number;\n /** Embeddable entities whose embedding is present and current. */\n embeddedEntities: number;\n}\n\nexport interface IndexReadinessOptions {\n /** Stop polling after this duration. Omit for an owning runtime monitor. */\n timeoutMs?: number;\n intervalMs?: number;\n /** Cancels readiness polling when the owning runtime shuts down. */\n signal?: AbortSignal;\n}\n\nexport interface IndexReadinessStatus extends EmbeddingIndexStats {\n ready: boolean;\n degraded: boolean;\n activeEmbeddingJobs: number;\n}\n\nexport interface EntityService extends ICoreEntityService {\n // Mutations\n createEntity<T extends BaseEntity>(\n request: CreateEntityRequest<T>,\n ): Promise<EntityMutationResult>;\n createEntityFromMarkdown(\n request: CreateEntityFromMarkdownRequest,\n ): Promise<EntityMutationResult>;\n updateEntity<T extends BaseEntity>(\n request: UpdateEntityRequest<T>,\n ): Promise<EntityMutationResult>;\n deleteEntity(request: DeleteEntityRequest): Promise<boolean>;\n upsertEntity<T extends BaseEntity>(\n request: UpsertEntityRequest<T>,\n ): Promise<EntityMutationResult & { created: boolean }>;\n storeEmbedding(data: StoreEmbeddingData): Promise<void>;\n backfillMissingEmbeddings(): Promise<EmbeddingBackfillResult>;\n isIndexReady(): boolean;\n awaitIndexReady(\n options: IndexReadinessOptions,\n ): Promise<IndexReadinessStatus>;\n\n // Serialization\n serializeEntity(entity: BaseEntity): string;\n deserializeEntity(markdown: string, entityType: string): Partial<BaseEntity>;\n\n // Counts\n countEmbeddings(): Promise<number>;\n\n // Diagnostics\n searchWithDistances(\n request: SearchWithDistancesRequest,\n ): Promise<Array<{ entityId: string; entityType: string; distance: number }>>;\n\n // Lifecycle\n initialize(): Promise<void>;\n\n // Job status\n getAsyncJobStatus(jobId: string): Promise<{\n status: \"pending\" | \"processing\" | \"completed\" | \"failed\";\n error?: string;\n } | null>;\n}\n\n/**\n * Entity Registry interface for managing entity types and their schemas/adapters\n */\nexport interface EntityRegistry {\n registerEntityType<\n TEntity extends BaseEntity<TMetadata>,\n TMetadata = Record<string, unknown>,\n >(\n type: string,\n schema: UnknownEntitySchema,\n adapter: EntityAdapter<TEntity, TMetadata>,\n config?: EntityTypeConfig,\n ): void;\n\n /** Remove an entity type after failed plugin registration or shell teardown. */\n unregisterEntityType(type: string): void;\n\n getSchema(type: string): UnknownEntitySchema;\n\n getAdapter<\n TEntity extends BaseEntity<TMetadata>,\n TMetadata = Record<string, unknown>,\n >(\n type: string,\n ): EntityAdapter<TEntity, TMetadata>;\n\n hasEntityType(type: string): boolean;\n\n validateEntity(type: string, entity: unknown): BaseEntity;\n\n getAllEntityTypes(): string[];\n\n /** Get configuration for a specific entity type */\n getEntityTypeConfig(type: string): EntityTypeConfig;\n\n /** Get weight map for all registered entity types with non-default weights */\n getWeightMap(): Record<string, number>;\n\n registerCreateInterceptor(type: string, interceptor: CreateInterceptor): void;\n\n getCreateInterceptor(type: string): CreateInterceptor | undefined;\n\n registerUploadSaveHandler(registration: UploadSaveHandlerRegistration): void;\n\n getUploadSaveHandler(\n mediaType: string,\n ): UploadSaveHandlerRegistration | undefined;\n\n registerPersistValidator(type: string, validator: PersistValidator): void;\n\n getPersistValidator(type: string): PersistValidator | undefined;\n\n /**\n * Extend an adapter's frontmatterSchema with additional fields.\n * Used by plugins to add domain-specific fields (e.g., professional-site adds tagline to profile).\n * Extensions are merged into the effective schema returned by getEffectiveFrontmatterSchema().\n */\n extendFrontmatterSchema(\n type: string,\n extension: z.ZodObject<z.ZodRawShape>,\n ): void;\n\n /**\n * Get the effective frontmatter schema for an entity type,\n * with all registered extensions merged in.\n * Returns undefined if the adapter has no frontmatterSchema.\n */\n getEffectiveFrontmatterSchema(\n type: string,\n ): z.ZodObject<z.ZodRawShape> | undefined;\n}\n\n/**\n * Database configuration for entity service\n */\nexport type { DbConfig as EntityDbConfig } from \"@brains/contracts\";\n",
51
51
  "import matter from \"gray-matter\";\nimport { z } from \"@brains/utils/zod\";\nimport type { BaseEntity, ContentVisibility } from \"./types\";\n\n/**\n * Configuration for frontmatter handling\n */\nexport interface FrontmatterConfig<T extends BaseEntity> {\n /**\n * Fields to explicitly include in frontmatter\n * If not specified, includes all non-system fields\n */\n includeFields?: (keyof T)[];\n\n /**\n * Fields to exclude from frontmatter\n * By default excludes: id, entityType, content, created, updated\n */\n excludeFields?: (keyof T)[];\n\n /**\n * Custom serializers for complex fields\n */\n customSerializers?: {\n [K in keyof T]?: (value: T[K]) => unknown;\n };\n\n /**\n * Custom deserializers for complex fields\n */\n customDeserializers?: {\n [K in keyof T]?: (value: unknown) => T[K];\n };\n}\n\nexport interface FrontmatterValidationSchema<T> {\n parse(data: unknown): T;\n}\n\n// Default system fields that should not be in frontmatter\nconst DEFAULT_SYSTEM_FIELDS: Array<keyof BaseEntity> = [\n \"id\",\n \"entityType\",\n \"content\",\n \"contentHash\",\n \"created\",\n \"updated\",\n \"visibility\",\n];\n\n/**\n * Extract metadata fields from an entity for frontmatter\n * Returns only non-system fields by default\n */\nexport function extractMetadata<T extends BaseEntity>(\n entity: T,\n config?: FrontmatterConfig<T>,\n): Record<string, unknown> {\n const { includeFields, excludeFields = [], customSerializers } = config ?? {};\n const excludedFieldNames = new Set<string>([\n ...DEFAULT_SYSTEM_FIELDS.map(String),\n ...excludeFields.map(String),\n ]);\n\n const metadata: Record<string, unknown> = {};\n\n // Get all fields from the entity\n const allFields = Object.keys(entity) as Array<keyof T>;\n\n // Determine which fields to include\n let fieldsToProcess: Array<keyof T>;\n if (includeFields) {\n // If includeFields is specified, only include those\n fieldsToProcess = includeFields.filter(\n (field) => !excludedFieldNames.has(String(field)),\n );\n } else {\n // Otherwise include all fields except excluded ones\n fieldsToProcess = allFields.filter(\n (field) => !excludedFieldNames.has(String(field)),\n );\n }\n\n // Process each field\n for (const field of fieldsToProcess) {\n const value = entity[field];\n\n // Skip undefined values\n if (value === undefined) {\n continue;\n }\n\n // Use custom serializer if available\n if (customSerializers && field in customSerializers) {\n const serializer = customSerializers[field];\n if (serializer) {\n metadata[field as string] = serializer(value);\n }\n } else {\n metadata[field as string] = value;\n }\n }\n\n return metadata;\n}\n\n/**\n * Generate markdown with frontmatter from content and metadata\n */\nexport function generateMarkdownWithFrontmatter(\n content: string,\n metadata: Record<string, unknown>,\n): string {\n // Only add frontmatter if there's metadata\n if (Object.keys(metadata).length === 0) {\n return content;\n }\n\n const cleaned = Object.fromEntries(\n Object.entries(metadata).filter(([, v]) => v !== undefined),\n );\n return matter.stringify(content, cleaned);\n}\n\n/**\n * Helper to convert all Date objects to ISO strings recursively\n */\nfunction convertDatesToStrings(obj: unknown): unknown {\n if (obj instanceof Date) {\n return obj.toISOString();\n }\n if (Array.isArray(obj)) {\n return obj.map(convertDatesToStrings);\n }\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = convertDatesToStrings(value);\n }\n return result;\n }\n return obj;\n}\n\n/**\n * Parse markdown with frontmatter into content and metadata\n */\nexport function parseMarkdownWithFrontmatter<T>(\n markdown: string,\n schema: FrontmatterValidationSchema<T>,\n): {\n content: string;\n metadata: T;\n} {\n const { content, data } = matter(markdown);\n\n // Convert all Date objects to strings before parsing with Zod\n const normalizedData = convertDatesToStrings(data);\n\n return {\n content: content.trim(),\n metadata: schema.parse(normalizedData),\n };\n}\n\n/**\n * Apply custom deserializers to metadata\n */\nexport function deserializeMetadata<T extends BaseEntity>(\n metadata: Record<string, unknown>,\n config?: FrontmatterConfig<T>,\n): Record<string, unknown> {\n if (!config?.customDeserializers) {\n return metadata;\n }\n\n const result: Record<string, unknown> = { ...metadata };\n\n for (const [field, deserializer] of Object.entries(\n config.customDeserializers,\n )) {\n if (field in metadata) {\n result[field] = deserializer(metadata[field]);\n }\n }\n\n return result;\n}\n\n/**\n * Generate frontmatter string from metadata\n */\nexport function generateFrontmatter(metadata: Record<string, unknown>): string {\n if (Object.keys(metadata).length === 0) {\n return \"\";\n }\n\n // Use gray-matter to generate frontmatter\n const fullMarkdown = matter.stringify(\"\", metadata);\n\n // Extract just the frontmatter part\n const match = fullMarkdown.match(/^---\\n[\\s\\S]*?\\n---/);\n return match ? match[0] : \"\";\n}\n\nconst visibilityFrontmatterSchema = z.object({\n visibility: z\n .enum([\"public\", \"shared\", \"restricted\", \"private\"])\n .optional()\n .transform((value): ContentVisibility => {\n if (value === undefined) return \"public\";\n return value === \"private\" ? \"restricted\" : value;\n }),\n});\n\nexport function extractVisibilityFromMarkdown(\n markdown: string,\n): ContentVisibility {\n const parsed = matter(markdown);\n return visibilityFrontmatterSchema.parse(parsed.data).visibility;\n}\n\nexport function hasVisibilityFrontmatter(markdown: string): boolean {\n const frontmatterMatch = markdown.match(/^---\\r?\\n[\\s\\S]*?\\r?\\n---/);\n const visibilityMatch = frontmatterMatch?.[0].match(/^visibility:/m);\n return visibilityMatch !== null && visibilityMatch !== undefined;\n}\n\nexport function applyVisibilityToMarkdown(\n markdown: string,\n visibility: ContentVisibility,\n): string {\n if (visibility === \"public\" && !hasVisibilityFrontmatter(markdown)) {\n return markdown;\n }\n\n const parsed = matter(markdown);\n const frontmatter = Object.fromEntries(\n Object.entries(parsed.data).filter(([key]) => key !== \"visibility\"),\n );\n\n if (visibility !== \"public\") {\n frontmatter[\"visibility\"] = visibility;\n }\n\n return generateMarkdownWithFrontmatter(parsed.content.trim(), frontmatter);\n}\n",
52
52
  "/**\n * Extract a human-readable error message from an unknown error value.\n * Handles Error objects, strings, and other types.\n */\nexport function getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/**\n * Coerce an unknown thrown value to an Error instance, preserving the\n * original when it's already an Error so stack traces and subclasses\n * survive rethrow.\n */\nexport function toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n",
53
53
  "import { getErrorMessage } from \"@brains/utils/error\";\nimport { z } from \"@brains/utils/zod\";\n\nconst validationIssuesErrorSchema = z.looseObject({\n issues: z.array(\n z.looseObject({\n message: z.string(),\n }),\n ),\n});\n\nexport class EntityValidationError extends Error {\n public readonly entityType: string;\n public readonly originalError: unknown;\n\n constructor(entityType: string, originalError: unknown) {\n super(\n `Invalid entity data for ${entityType}: ${getErrorMessage(originalError)}`,\n );\n this.name = \"EntityValidationError\";\n this.entityType = entityType;\n this.originalError = originalError;\n }\n}\n\nexport function hasValidationIssues(error: unknown): boolean {\n return validationIssuesErrorSchema.safeParse(error).success;\n}\n\nexport function isEntityValidationError(error: unknown): boolean {\n return error instanceof EntityValidationError || hasValidationIssues(error);\n}\n\nexport function toEntityValidationError(\n entityType: string,\n error: unknown,\n): EntityValidationError | undefined {\n if (error instanceof EntityValidationError) {\n return error;\n }\n return hasValidationIssues(error)\n ? new EntityValidationError(entityType, error)\n : undefined;\n}\n",
@@ -307,7 +307,7 @@ interface SearchOptions {
307
307
  /** Minimum relevance score to return. Omit for no score cutoff. */
308
308
  minScore?: number;
309
309
  }
310
- type ProjectionSourceRole = "canonical" | "primary" | "supporting" | "ambient" | "excluded";
310
+ type ProjectionSourceRole = "canonical" | "primary" | "secondary" | "supporting" | "ambient" | "excluded";
311
311
  /**
312
312
  * Configuration for entity type registration
313
313
  */
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{Qa as r,Ra as o}from"./chunks/index-r4f9aab7.js";import"./chunks/index-7j5wm2z5.js";function t(i){return i}var e={name:"@rizom/brain",version:"0.2.0-alpha.219",description:"Brain runtime + CLI \u2014 scaffold, run, and manage AI brain instances",keywords:["brain","ai","cli","mcp","agent","personal-ai","knowledge-management"],license:"Apache-2.0",author:"Yeehaa <yeehaa@rizom.ai> (https://rizom.ai)",bin:{brain:"./dist/brain.js"},bugs:"https://github.com/rizom-ai/brains/issues",dependencies:{"@clack/prompts":"^1.5.1","@modelcontextprotocol/sdk":"^1.29.0","@tailwindcss/postcss":"^4.3.1","@tailwindcss/typography":"^0.5.20",postcss:"^8.5.15",preact:"^10.27.2","preact-render-to-string":"^6.7.0",tailwindcss:"^4.3.1",zod:"^4.1.8"},devDependencies:{"@brains/app":"workspace:*","@brains/build-tools":"workspace:*","@brains/content-formatters":"workspace:*","@brains/deploy-support":"workspace:*","@brains/eslint-config":"workspace:*","@brains/mcp-service":"workspace:*","@brains/plugins":"workspace:*","@brains/ranger":"workspace:*","@brains/relay":"workspace:*","@brains/rover":"workspace:*","@brains/site-composition":"workspace:*","@brains/site-default":"workspace:*","@brains/site-personal":"workspace:*","@brains/site-professional":"workspace:*","@brains/theme-rizom":"workspace:*","@brains/typescript-config":"workspace:*","@brains/utils":"workspace:*","@rizom/theme-default":"workspace:*","@types/bun":"^1.3.14",rolldown:"^1.0.0","rolldown-plugin-dts":"^0.27.4",typescript:"^7.0.2"},engines:{bun:">=1.3.3"},exports:{".":{types:"./dist/index.d.ts",import:"./dist/index.js"},"./cli":"./dist/brain.js","./plugins":{types:"./dist/plugins.d.ts",import:"./dist/plugins.js"},"./entities":{types:"./dist/entities.d.ts",import:"./dist/entities.js"},"./services":{types:"./dist/services.d.ts",import:"./dist/services.js"},"./interfaces":{types:"./dist/interfaces.d.ts",import:"./dist/interfaces.js"},"./templates":{types:"./dist/templates.d.ts",import:"./dist/templates.js"},"./site":{types:"./dist/site.d.ts",import:"./dist/site.js"},"./themes":{types:"./dist/themes.d.ts",import:"./dist/themes.js"},"./deploy":{types:"./dist/deploy.d.ts",import:"./dist/deploy.js"},"./tsconfig.instance.json":"./tsconfig.instance.json"},files:["dist","templates","tsconfig.instance.json"],homepage:"https://github.com/rizom-ai/brains/tree/main/packages/brain-cli#readme",optionalDependencies:{"@bitwarden/sdk-napi":"^1.0.0","@libsql/client":"^0.17.4","@tailwindcss/oxide":"^4.1.4",lightningcss:"^1.29.2","playwright-core":"^1.61.0","react-devtools-core":"^7.0.1",sharp:"^0.35.1"},publishConfig:{access:"public"},repository:{type:"git",url:"https://github.com/rizom-ai/brains.git",directory:"packages/brain-cli"},scripts:{build:"bun scripts/build.ts","dev:start":"bun scripts/build.ts && bun dist/brain.js start",lint:"eslint . --max-warnings 0","lint:fix":"eslint . --max-warnings 0 --fix",prepublishOnly:"bun scripts/build.ts",test:"bun test",typecheck:"tsc --noEmit"},type:"module"};var n=e.version;export{o as z,t as defineBrain,r as ZodError,n as PLUGIN_API_VERSION};
2
+ import{Qa as r,Ra as o}from"./chunks/index-r4f9aab7.js";import"./chunks/index-7j5wm2z5.js";function t(i){return i}var e={name:"@rizom/brain",version:"0.2.0-alpha.220",description:"Brain runtime + CLI \u2014 scaffold, run, and manage AI brain instances",keywords:["brain","ai","cli","mcp","agent","personal-ai","knowledge-management"],license:"Apache-2.0",author:"Yeehaa <yeehaa@rizom.ai> (https://rizom.ai)",bin:{brain:"./dist/brain.js"},bugs:"https://github.com/rizom-ai/brains/issues",dependencies:{"@clack/prompts":"^1.5.1","@modelcontextprotocol/sdk":"^1.29.0","@tailwindcss/postcss":"^4.3.1","@tailwindcss/typography":"^0.5.20",postcss:"^8.5.15",preact:"^10.27.2","preact-render-to-string":"^6.7.0",tailwindcss:"^4.3.1",zod:"^4.1.8"},devDependencies:{"@brains/app":"workspace:*","@brains/build-tools":"workspace:*","@brains/content-formatters":"workspace:*","@brains/deploy-support":"workspace:*","@brains/eslint-config":"workspace:*","@brains/mcp-service":"workspace:*","@brains/plugins":"workspace:*","@brains/ranger":"workspace:*","@brains/relay":"workspace:*","@brains/rover":"workspace:*","@brains/site-composition":"workspace:*","@brains/site-default":"workspace:*","@brains/site-personal":"workspace:*","@brains/site-professional":"workspace:*","@brains/theme-rizom":"workspace:*","@brains/typescript-config":"workspace:*","@brains/utils":"workspace:*","@rizom/theme-default":"workspace:*","@types/bun":"^1.3.14",rolldown:"^1.0.0","rolldown-plugin-dts":"^0.27.4",typescript:"^7.0.2"},engines:{bun:">=1.3.3"},exports:{".":{types:"./dist/index.d.ts",import:"./dist/index.js"},"./cli":"./dist/brain.js","./plugins":{types:"./dist/plugins.d.ts",import:"./dist/plugins.js"},"./entities":{types:"./dist/entities.d.ts",import:"./dist/entities.js"},"./services":{types:"./dist/services.d.ts",import:"./dist/services.js"},"./interfaces":{types:"./dist/interfaces.d.ts",import:"./dist/interfaces.js"},"./templates":{types:"./dist/templates.d.ts",import:"./dist/templates.js"},"./site":{types:"./dist/site.d.ts",import:"./dist/site.js"},"./themes":{types:"./dist/themes.d.ts",import:"./dist/themes.js"},"./deploy":{types:"./dist/deploy.d.ts",import:"./dist/deploy.js"},"./tsconfig.instance.json":"./tsconfig.instance.json"},files:["dist","templates","tsconfig.instance.json"],homepage:"https://github.com/rizom-ai/brains/tree/main/packages/brain-cli#readme",optionalDependencies:{"@bitwarden/sdk-napi":"^1.0.0","@libsql/client":"^0.17.4","@tailwindcss/oxide":"^4.1.4",lightningcss:"^1.29.2","playwright-core":"^1.61.0","react-devtools-core":"^7.0.1",sharp:"^0.35.1"},publishConfig:{access:"public"},repository:{type:"git",url:"https://github.com/rizom-ai/brains.git",directory:"packages/brain-cli"},scripts:{build:"bun scripts/build.ts","dev:start":"bun scripts/build.ts && bun dist/brain.js start",lint:"eslint . --max-warnings 0","lint:fix":"eslint . --max-warnings 0 --fix",prepublishOnly:"bun scripts/build.ts",test:"bun test",typecheck:"tsc --noEmit"},type:"module"};var n=e.version;export{o as z,t as defineBrain,r as ZodError,n as PLUGIN_API_VERSION};
3
3
 
4
- //# debugId=6A706C74AF69574364756E2164756E21
4
+ //# debugId=9979BBC968D0B78E64756E2164756E21
5
5
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -6,6 +6,6 @@
6
6
  "import packageJson from \"../package.json\" with { type: \"json\" };\n\n/**\n * Pre-v1 external plugin API marker.\n *\n * During alpha, the external plugin API compatibility marker tracks the\n * published @rizom/brain package version. Once the plugin API is declared\n * stable, this can move to an independent semver contract such as 1.0.0.\n */\nexport const PLUGIN_API_VERSION: string = packageJson.version;\n"
7
7
  ],
8
8
  "mappings": ";0FA0EO,UAAS,CAAW,CAAC,EAA8C,CACxE,OAAO,6yFClEF,IAAM,EAA6B,EAAY",
9
- "debugId": "6A706C74AF69574364756E2164756E21",
9
+ "debugId": "9979BBC968D0B78E64756E2164756E21",
10
10
  "names": []
11
11
  }
package/dist/plugins.d.ts CHANGED
@@ -433,7 +433,7 @@ interface SearchOptions {
433
433
  /** Minimum relevance score to return. Omit for no score cutoff. */
434
434
  minScore?: number;
435
435
  }
436
- type ProjectionSourceRole = "canonical" | "primary" | "supporting" | "ambient" | "excluded";
436
+ type ProjectionSourceRole = "canonical" | "primary" | "secondary" | "supporting" | "ambient" | "excluded";
437
437
  /**
438
438
  * Configuration for entity type registration
439
439
  */
@@ -139,7 +139,7 @@ interface SearchOptions {
139
139
  /** Minimum relevance score to return. Omit for no score cutoff. */
140
140
  minScore?: number;
141
141
  }
142
- type ProjectionSourceRole = "canonical" | "primary" | "supporting" | "ambient" | "excluded";
142
+ type ProjectionSourceRole = "canonical" | "primary" | "secondary" | "supporting" | "ambient" | "excluded";
143
143
  /**
144
144
  * Configuration for entity type registration
145
145
  */
package/dist/site.js CHANGED
@@ -1372,7 +1372,7 @@ ${r.join(`
1372
1372
  letter-spacing: 0.02em;
1373
1373
  }
1374
1374
  .printable-footer a { color: inherit; text-decoration: none; overflow-wrap: anywhere; }
1375
- `}},void 0,!1,void 0,this),nr("header",{className:"printable-hero",children:[nr("div",{className:"printable-masthead",children:r.brandLabel&&nr("p",{className:"printable-kicker",children:r.brandLabel},void 0,!1,void 0,this)},void 0,!1,void 0,this),nr("div",{children:[nr("h1",{className:"printable-title",children:r.title},void 0,!1,void 0,this),r.excerpt&&nr("p",{className:"printable-excerpt",children:r.excerpt},void 0,!1,void 0,this),nr("div",{className:"printable-meta",children:[r.author&&nr("span",{children:["By ",r.author]},void 0,!0,void 0,this),f&&nr("span",{children:f},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this),r.coverImageUrl&&nr("figure",{className:"printable-cover-wrap",children:nr("img",{className:"printable-cover",src:r.coverImageUrl,alt:""},void 0,!1,void 0,this)},void 0,!1,void 0,this),nr(P1,{markdown:r.body,className:"printable-body"},void 0,!1,void 0,this),(Boolean(r.canonicalUrl)||Boolean(r.brandLabel))&&nr("footer",{className:"printable-footer",children:[nr("span",{children:r.brandLabel??"Printable PDF"},void 0,!1,void 0,this),r.canonicalUrl&&nr("a",{href:r.canonicalUrl,children:r.canonicalUrl},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}var sI=26214400,CI=60000;class j6{metadata={outputEntityType:"document"};context;renderPdf;constructor(l,r={}){this.context=l,this.renderPdf=r.renderPdf??dt}async resolve(l){if(l.sourceEntityType!=="post"||l.attachmentType!==fo)return;let r=await this.context.entityService.getEntity({entityType:"post",id:l.sourceEntityId});if(!r)return;let f=eI(r,{brandLabel:this.resolveBrandLabel(),coverImageUrl:await this.resolveCoverImageUrl(r)}),i=await xI(EI(jI(),"brain-blog-printable-"));try{let n=await S1({outputDir:i,mediaPath:`/_media/printable/post/${r.id}`,template:TK,format:"pdf",content:f,siteConfig:{title:f.title,themeMode:"light"},themeCSS:this.context.themeCSS}),t=await j1({rootDir:i});try{return{type:"document",data:await this.renderPdf(t.urlFor(n.urlPath),{maxBytes:sI,timeoutMs:CI,printBackground:!0,preferCSSPageSize:!0}),mimeType:"application/pdf",filename:`${lq(r)}-printable.pdf`}}finally{await t.close()}}finally{await SI(i,{recursive:!0,force:!0})}}resolveBrandLabel(){let l=this.context.domain?.trim();if(l&&l.length>0)return l;let r=this.context.identity.getProfile().name.trim();return r.length>0?r:void 0}async resolveCoverImageUrl(l){let{frontmatter:r}=X1(l.content),f=Cl.parse(r);if(!f.coverImageId)return;let i=await this.context.entityService.getEntity({entityType:"image",id:f.coverImageId});return i?.content.startsWith("data:image/")?i.content:void 0}}function eI(l,r={}){let{frontmatter:f,content:i}=X1(l.content),n=Cl.parse(f);return{title:n.title,body:i,...n.excerpt?{excerpt:n.excerpt}:{},...n.author?{author:n.author}:{},...n.publishedAt?{publishedAt:n.publishedAt}:{},...n.canonicalUrl?{canonicalUrl:n.canonicalUrl}:{},...r.coverImageUrl?{coverImageUrl:r.coverImageUrl}:{},...r.brandLabel?{brandLabel:r.brandLabel}:{}}}function lq(l){let r=l.metadata.slug;return r.length>0?r:sl(l.metadata.title)}import{mkdtemp as tq,rm as uq}from"fs/promises";import{tmpdir as hq}from"os";import{join as oq}from"path";import{jsxDEV as nq}from"preact/jsx-dev-runtime";var ru="og-image",rq="blog:og-image",RK=X.object({title:X.string().min(1),excerpt:X.string().optional(),author:X.string().optional(),publishedAt:X.string().optional(),brandLabel:X.string().optional(),coverImageUrl:X.string().optional()}),LK={name:rq,pluginId:"blog",schema:RK,renderers:{image:iq}};function fq(l){if(!l)return;let r=new Date(l);if(Number.isNaN(r.getTime()))return;return r.toLocaleDateString("en-GB",{year:"numeric",month:"short",day:"2-digit"})}function iq(l){let r=RK.parse(l);return nq(ot,{brandLabel:r.brandLabel??r.title,eyebrow:"Journal",title:r.title,subtitle:r.excerpt,meta:[r.author],tag:fq(r.publishedAt)},void 0,!1,void 0,this)}var wq={width:1200,height:630},vq=60000;class E6{metadata={outputEntityType:"image",targetField:"ogImageId"};context;screenshotPng;constructor(l,r={}){this.context=l,this.screenshotPng=r.screenshotPng??yt}async resolve(l){if(l.sourceEntityType!=="post"||l.attachmentType!==ru)return;let r=await this.context.entityService.getEntity({entityType:"post",id:l.sourceEntityId});if(!r)return;let f=mq(r,{brandLabel:this.resolveBrandLabel(),coverImageUrl:await this.resolveCoverImageUrl(r)}),i=await tq(oq(hq(),"brain-blog-og-image-"));try{let n=await S1({outputDir:i,mediaPath:`/_media/og/post/${r.id}`,template:LK,format:"image",content:f,siteConfig:{title:f.title,themeMode:"light"},themeCSS:this.context.themeCSS}),t=await j1({rootDir:i});try{return{type:"image",data:await this.screenshotPng(t.urlFor(n.urlPath),wq,{timeoutMs:vq,fullPage:!1,omitBackground:!1}),mimeType:"image/png",filename:`${kq(r)}-og.png`}}finally{await t.close()}}finally{await uq(i,{recursive:!0,force:!0})}}resolveBrandLabel(){let l=this.context.domain?.trim();if(l&&l.length>0)return l;let r=this.context.identity.getProfile().name.trim();return r.length>0?r:void 0}async resolveCoverImageUrl(l){let{frontmatter:r}=X1(l.content),f=Cl.parse(r);if(!f.coverImageId)return;let i=await this.context.entityService.getEntity({entityType:"image",id:f.coverImageId});return i?.content.startsWith("data:image/")?i.content:void 0}}function mq(l,r={}){let{frontmatter:f}=X1(l.content),i=Cl.parse(f);return{title:i.title,...i.excerpt?{excerpt:i.excerpt}:{},...i.author?{author:i.author}:{},...i.publishedAt?{publishedAt:i.publishedAt}:{},...r.coverImageUrl?{coverImageUrl:r.coverImageUrl}:{},...r.brandLabel?{brandLabel:r.brandLabel}:{}}}function kq(l){let r=l.metadata.slug;return r.length>0?r:sl(l.metadata.title)}var PK={name:"@brains/blog",version:"0.2.0-alpha.219",description:"AI-powered blog post generation from existing brain content",dependencies:{"@brains/atproto-contracts":"workspace:*","@brains/contracts":"workspace:*","@brains/media-page-composer":"workspace:*","@brains/media-renderer":"workspace:*","@brains/plugins":"workspace:*","@brains/ui-library":"workspace:*","@brains/utils":"workspace:*"},devDependencies:{"@brains/app":"workspace:*","@brains/eslint-config":"workspace:*","@brains/typescript-config":"workspace:*","@types/bun":"^1.3.14","bun-types":"^1.3.14",eslint:"^10.5.0",typescript:"^7.0.2"},exports:{".":"./src/index.ts"},private:!0,scripts:{eval:"cd evals && bun run brain-eval",lint:"eslint . --max-warnings 0","lint:fix":"eslint . --max-warnings 0 --fix",test:"bun test",typecheck:"tsc --noEmit"},type:"module"};class MK extends Bn{entityType=bf.entityType;schema=s6;adapter=bf;unregisterAtprotoProjection;unregisterPrintableAttachmentProvider;unregisterOgImageAttachmentProvider;constructor(l={}){super("blog",PK,l,Po)}getEntityTypeConfig(){return{weight:2,projectionSourceRole:"primary",publish:{publishStatuses:["queued","published"]}}}createGenerationHandler(l){return new Mo(this.logger.child("BlogGenerationJobHandler"),l)}getTemplates(){return h$()}getDataSources(){return[new Io(this.logger.child("BlogDataSource"))]}async onRegister(l){let{RSSDataSource:r}=await import("./chunks/rss-datasource-zj8k12xw.js");l.entities.registerDataSource(new r(this.logger.child("RSSDataSource"))),o$(l,this.logger),w$(l,this.logger),m$(l,this.logger),k$(l),this.unregisterPrintableAttachmentProvider=l.attachments.register("post",fo,new j6(l)),this.unregisterOgImageAttachmentProvider=l.attachments.register("post",ru,new E6(l)),this.deferPublishAssetRegistration(l),this.unregisterAtprotoProjection=rf.getInstance().register(Cw()),this.logger.info("Blog plugin registered (routes auto-generated at /posts/)")}deferPublishAssetRegistration(l){l.messaging.subscribe(ei.pluginsRegistered,async()=>{return await l.messaging.send({type:"publish-assets:register",payload:{entityType:"post",attachmentType:ru,mediaEntityType:"image",targetEntityField:{location:"frontmatter",field:"ogImageId"},requiredWhen:{status:"published"},autoGenerate:!0,jobType:"image:image-render-source"}}),{success:!0}})}async onShutdown(){this.unregisterPrintableAttachmentProvider?.(),this.unregisterPrintableAttachmentProvider=void 0,this.unregisterOgImageAttachmentProvider?.(),this.unregisterOgImageAttachmentProvider=void 0,this.unregisterAtprotoProjection?.(),this.unregisterAtprotoProjection=void 0}}class io{postsListUrl;id="personal:homepage";name="Personal Homepage DataSource";description="Fetches profile and blog posts for a personal homepage";constructor(l){this.postsListUrl=l}async fetch(l,r,f){let i=f.entityService,[n,t,u]=await Promise.all([Jf(i,Qn),Ji(i,{entityType:"post",count:6,parse:Lf}),Rf(i)]),o={profile:n,posts:t,postsListUrl:this.postsListUrl,cta:Hn(u.cta)};return r.parse(o)}}class no{id="personal:about";name="About Page DataSource";description="Fetches full profile data for the about page";async fetch(l,r,f){let n={profile:await Jf(f.entityService,Qn)};return r.parse(n)}}import{jsxDEV as _0,Fragment as VK}from"preact/jsx-dev-runtime";function bq(l){return new Date(l).toLocaleDateString("en-US",{year:"numeric",month:"long",day:"numeric"})}function yq(l){return l.split(/(\*[^*]+\*)/).map((r,f)=>{if(r.startsWith("*")&&r.endsWith("*")&&r.length>2)return _0("em",{className:"font-sans italic font-medium text-theme-inverse tracking-[-0.015em] [font-variation-settings:'opsz'_72]",children:r.slice(1,-1)},f,!1,void 0,this);return r})}var C6=({profile:l,posts:r,postsListUrl:f,cta:i})=>{let n=l.tagline??l.description,t=l.name.split(" ")[0];return _0(VK,{children:[_0(Ll,{title:l.name,description:l.description??n??"Personal site",ogType:"website"},void 0,!1,void 0,this),_0("header",{className:"hero-decor bg-brand text-theme-inverse relative flex flex-col items-center justify-center px-6 md:px-12 pt-32 pb-24 gap-7",children:_0("div",{className:"w-full max-w-[1100px] mx-auto text-center flex flex-col items-center gap-7",children:[n&&_0("h1",{className:"font-heading font-bold text-[clamp(2.5rem,6vw,5rem)] leading-[1.05] tracking-[-0.035em] text-theme-inverse text-balance m-0 [font-variation-settings:'wdth'_90,'opsz'_96]",children:yq(n)},void 0,!1,void 0,this),l.description&&_0("p",{className:"text-[clamp(1.0625rem,1.4vw,1.3125rem)] leading-[1.5] text-theme-inverse opacity-95 max-w-[600px] mx-auto m-0",children:l.description},void 0,!1,void 0,this),_0("div",{className:"flex flex-wrap justify-center gap-3.5 mt-2",children:[_0("a",{href:f,className:"inline-flex items-center gap-2 rounded-full py-3.5 px-7 bg-theme text-brand border-2 border-theme font-heading font-semibold text-[15px] hover:bg-theme-subtle hover:-translate-y-0.5 transition-all [font-variation-settings:'wdth'_92,'opsz'_18]",children:["Read the Blog ",_0("span",{"aria-hidden":"true",children:"\u2192"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),_0("a",{href:"/about",className:"rounded-full py-3.5 px-7 border-2 border-theme-light text-theme-inverse font-heading font-semibold text-[15px] hover:bg-brand-dark hover:border-theme hover:-translate-y-0.5 transition-all [font-variation-settings:'wdth'_92,'opsz'_18]",children:["About ",t]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),r.length>0&&_0("section",{className:"flex flex-col py-24 px-6 md:px-12 gap-11 bg-theme-subtle",children:[_0("div",{className:"flex justify-between items-end max-w-layout mx-auto w-full gap-6 flex-wrap",children:[_0("div",{children:[_0("span",{className:"block font-heading font-semibold text-[13px] uppercase tracking-[0.22em] text-brand mb-3 [font-variation-settings:'wdth'_85,'opsz'_12]",children:"The Archive"},void 0,!1,void 0,this),_0("h2",{className:"font-heading font-bold text-[clamp(2.25rem,5vw,3.75rem)] leading-none tracking-[-0.03em] text-heading m-0 [font-variation-settings:'wdth'_92,'opsz'_64]",children:["Recent"," ",_0("em",{className:"font-sans italic font-medium text-brand [font-variation-settings:'opsz'_60]",children:"posts"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this),_0("a",{href:f,className:"font-heading font-semibold text-[15px] text-brand inline-flex items-center gap-1.5 pb-1.5 border-b-2 border-current hover:text-brand-dark transition-colors [font-variation-settings:'wdth'_88,'opsz'_16]",children:["View the whole archive ",_0("span",{"aria-hidden":"true",children:"\u2192"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this),_0("div",{className:"grid grid-cols-[repeat(auto-fit,minmax(min(100%,320px),1fr))] gap-8 max-w-layout mx-auto w-full",children:r.map((u)=>_0("a",{href:u.url,className:"group flex flex-col bg-theme border border-theme rounded-md overflow-hidden hover:-translate-y-1 hover:border-brand/50 transition-all no-underline shadow-sm hover:shadow-lg",children:[u.coverImageUrl?_0("img",{src:u.coverImageUrl,alt:u.metadata.title,className:"w-full aspect-[16/10] object-cover bg-theme-muted"},void 0,!1,void 0,this):_0("div",{className:"w-full aspect-[16/10] bg-gradient-to-br from-brand to-accent","aria-hidden":"true"},void 0,!1,void 0,this),_0("div",{className:"flex flex-col gap-4 p-7 flex-1",children:[_0("div",{className:"flex flex-wrap items-center gap-3.5 font-heading text-[11px] font-semibold uppercase tracking-[0.18em] text-theme-muted [font-variation-settings:'wdth'_85,'opsz'_11]",children:[u.frontmatter.seriesName&&_0(VK,{children:[_0("span",{className:"text-brand",children:u.frontmatter.seriesName},void 0,!1,void 0,this),_0("span",{className:"w-[3px] h-[3px] rounded-full bg-current opacity-50","aria-hidden":"true"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),_0("time",{children:bq(u.metadata.publishedAt??u.created)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),_0("h3",{className:"font-sans font-semibold text-[clamp(1.375rem,1.8vw,1.625rem)] leading-[1.18] tracking-[-0.012em] text-heading m-0 text-balance",children:u.metadata.title},void 0,!1,void 0,this),u.frontmatter.excerpt&&_0("p",{className:"text-base leading-[1.55] text-theme-muted m-0 flex-1",children:u.frontmatter.excerpt},void 0,!1,void 0,this),_0("span",{className:"inline-flex items-center gap-1.5 font-heading font-semibold text-[13px] uppercase tracking-[0.14em] text-brand mt-1 [font-variation-settings:'wdth'_88,'opsz'_14]",children:["Read the post"," ",_0("span",{"aria-hidden":"true",className:"transition-transform group-hover:translate-x-1",children:"\u2192"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},u.id,!0,void 0,this))},void 0,!1,void 0,this)]},void 0,!0,void 0,this),_0("section",{className:"cta-decor bg-theme-dark text-theme-on-dark flex-grow flex flex-col items-center justify-center py-24 px-6 md:px-12 gap-5",children:[_0("span",{className:"font-heading font-medium text-xs uppercase tracking-[0.24em] text-accent [font-variation-settings:'wdth'_85,'opsz'_12]",children:"Get in touch"},void 0,!1,void 0,this),_0("h2",{className:"text-center font-heading font-bold text-[clamp(2rem,4vw,3rem)] leading-none tracking-[-0.03em] text-theme-on-dark m-0 [font-variation-settings:'wdth'_90,'opsz'_64]",children:i.heading},void 0,!1,void 0,this),i.subtitle&&_0("p",{className:"text-center text-base leading-[1.55] text-theme-on-dark opacity-80 max-w-[480px] m-0",children:i.subtitle},void 0,!1,void 0,this),_0("a",{href:i.buttonLink,className:"inline-flex items-center gap-2 rounded-full py-3.5 px-7 bg-brand text-theme-inverse border-2 border-brand font-heading font-semibold text-[15px] hover:bg-brand-dark hover:-translate-y-0.5 transition-all mt-2 [font-variation-settings:'wdth'_92,'opsz'_18]",children:[i.buttonText," ",_0("span",{"aria-hidden":"true",children:"\u2192"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)};import{jsxDEV as tr,Fragment as dq}from"preact/jsx-dev-runtime";var e6=({profile:l})=>{let r=`About ${l.name}`,f=l.description??l.intro??"About page",i=Boolean(l.email??l.website);return tr(dq,{children:[tr(Ll,{title:r,description:f,ogType:"profile"},void 0,!1,void 0,this),tr("section",{className:"hero-decor bg-brand text-theme-inverse relative flex flex-col px-6 md:px-12 pt-32 pb-24",children:tr("div",{className:"w-full max-w-3xl mx-auto flex flex-col gap-6",children:[tr("h1",{className:"font-heading font-bold text-[clamp(2.5rem,6vw,4.5rem)] leading-[1.05] tracking-[-0.035em] text-theme-inverse text-balance m-0 [font-variation-settings:'wdth'_90,'opsz'_96]",children:l.name},void 0,!1,void 0,this),l.description&&tr("p",{className:"text-[clamp(1.0625rem,1.4vw,1.25rem)] leading-[1.55] text-theme-inverse opacity-95 max-w-2xl m-0",children:l.description},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),l.story&&tr("section",{className:`py-24 px-6 md:px-12 bg-theme-subtle${i?"":" flex-grow"}`,children:tr("div",{className:"max-w-3xl mx-auto",children:tr(P1,{markdown:l.story},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),i&&tr("section",{className:"cta-decor bg-theme-dark text-theme-on-dark flex-grow flex flex-col items-center justify-center py-24 px-6 md:px-12 gap-5",children:[tr("span",{className:"font-heading font-medium text-xs uppercase tracking-[0.24em] text-accent [font-variation-settings:'wdth'_85,'opsz'_12]",children:"Get in touch"},void 0,!1,void 0,this),tr("h2",{className:"text-center font-heading font-bold text-[clamp(2rem,4vw,3rem)] leading-none tracking-[-0.03em] text-theme-on-dark m-0 [font-variation-settings:'wdth'_90,'opsz'_64]",children:["Say"," ",tr("em",{className:"font-sans italic font-medium text-accent [font-variation-settings:'opsz'_72]",children:"hi."},void 0,!1,void 0,this)]},void 0,!0,void 0,this),tr("div",{className:"flex flex-wrap justify-center gap-3.5 mt-2",children:[l.email&&tr("a",{href:`mailto:${l.email}`,className:"inline-flex items-center gap-2 rounded-full py-3.5 px-7 bg-brand text-theme-inverse border-2 border-brand font-heading font-semibold text-[15px] hover:bg-brand-dark hover:-translate-y-0.5 transition-all [font-variation-settings:'wdth'_92,'opsz'_18]",children:["Get in Touch ",tr("span",{"aria-hidden":"true",children:"\u2192"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),l.website&&tr("a",{href:l.website,className:"rounded-full py-3.5 px-7 border-2 border-theme-light text-theme-on-dark font-heading font-semibold text-[15px] hover:bg-brand-dark hover:border-theme hover:-translate-y-0.5 transition-all [font-variation-settings:'wdth'_92,'opsz'_18]",children:"Website"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)};var pK={name:"@brains/site-personal",version:"0.2.0-alpha.219",description:"Personal site composition package",dependencies:{"@brains/blog":"workspace:*","@brains/plugins":"workspace:*","@brains/site-composition":"workspace:*","@brains/site-engine":"workspace:*","@brains/site-info":"workspace:*","@brains/templates":"workspace:*","@brains/ui-library":"workspace:*","@brains/utils":"workspace:*",preact:"^10.27.2"},devDependencies:{"@brains/eslint-config":"workspace:*","@brains/typescript-config":"workspace:*","@types/bun":"^1.3.14","bun-types":"^1.3.14",eslint:"^10.5.0",typescript:"^7.0.2"},exports:{".":"./src/index.ts"},files:["src"],private:!0,scripts:{lint:"eslint . --max-warnings 0","lint:fix":"eslint . --max-warnings 0 --fix",typecheck:"tsc --noEmit"},type:"module"};var cq=X.object({entityDisplay:X.object({post:X.object({label:X.string().default("Post"),pluralName:X.string().optional()}).default({label:"Post"})}).default({post:{label:"Post"}})}),Aq=X.object({heading:X.string(),buttonText:X.string(),buttonLink:X.string(),subtitle:X.string().optional()}),IK=X.looseObject({name:X.string(),kind:X.enum(["professional","team","collective"]),organization:X.string().optional(),description:X.string().optional(),avatar:X.string().optional(),website:X.string().optional(),email:X.string().optional(),socialLinks:X.array(X.object({platform:X.enum(["github","instagram","linkedin","email","website"]),url:X.string(),label:X.string().optional()})).optional(),tagline:X.string().optional(),intro:X.string().optional(),story:X.string().optional()}),Kq=X.looseObject({id:X.string(),entityType:X.literal("post"),content:X.string(),created:X.string(),updated:X.string(),contentHash:X.string(),metadata:X.looseObject({title:X.string(),publishedAt:X.string().optional()}),frontmatter:X.looseObject({excerpt:X.string(),seriesName:X.string().optional(),seriesIndex:X.number().optional()}),body:X.string(),url:X.string().optional(),typeLabel:X.string().optional(),listUrl:X.string().optional(),listLabel:X.string().optional(),seriesUrl:X.string().optional(),coverImageUrl:X.string().optional(),ogImageUrl:X.string().optional(),coverImageWidth:X.number().optional(),coverImageHeight:X.number().optional(),coverImageSrcset:X.string().optional(),coverImageSizes:X.string().optional()});class l4 extends to{dependencies=["blog"];constructor(l={}){super("personal-site",pK,l,cq)}async onRegister(l){l.entities.extendFrontmatterSchema("anchor-profile",Gu);let r=this.config.entityDisplay.post,f=`/${r.pluralName??r.label.toLowerCase()+"s"}`,i=new io(f);l.entities.registerDataSource(i);let n=new no;l.entities.registerDataSource(n);let t=X.object({profile:IK,posts:X.array(Kq),postsListUrl:X.string(),cta:Aq}),u=X.object({profile:IK});l.templates.register({homepage:Ol({name:"homepage",description:"Personal homepage with recent blog posts",schema:t,dataSourceId:"personal:homepage",requiredPermission:"public",layout:{component:C6}}),about:Ol({name:"about",description:"About page with profile",schema:u,dataSourceId:"personal:about",requiredPermission:"public",layout:{component:e6}})}),this.logger.info("Personal site plugin registered successfully")}async getTools(){return[]}async getResources(){return[]}}function r4(l){return new l4(l??{})}var f4=[{id:"home",path:"/",title:"Home",description:"Personal site homepage",layout:"default",navigation:{show:!0,label:"Home",slot:"secondary",priority:10},sections:[{id:"homepage",template:"personal-site:homepage",dataQuery:{}}]},{id:"about",path:"/about",title:"About",description:"About page",layout:"default",navigation:{show:!0,label:"About",slot:"primary",priority:90},sections:[{id:"about",template:"personal-site:about",dataQuery:{}}]}];import{jsxDEV as Tr}from"preact/jsx-dev-runtime";function i4({sections:l,siteInfo:r}){let f=[...r.navigation.primary,...r.navigation.secondary].filter((i)=>i.label!=="Home");return Tr("div",{className:"flex flex-col min-h-screen bg-theme overflow-x-clip",children:[Tr(ht,{title:r.title,titleClassName:"font-heading font-bold text-2xl",navigation:r.navigation.primary,showThemeToggle:!0,themeToggleClassName:"bg-theme-toggle text-theme-toggle-icon hover:bg-theme-toggle-hover rounded-[10px]",...r.logo!==void 0?{logo:r.logo}:{}},void 0,!1,void 0,this),Tr("main",{className:"flex-grow flex flex-col",children:l},void 0,!1,void 0,this),Tr("footer",{className:"bg-footer text-footer border-t border-theme",children:Tr("div",{className:"max-w-layout mx-auto flex flex-col md:flex-row justify-between items-center py-8 px-6 md:px-8",children:[Tr("div",{className:"flex flex-col gap-1 mb-4 md:mb-0",children:[Tr("span",{className:"text-brand font-heading font-bold text-lg",children:r.title},void 0,!1,void 0,this),r.description&&Tr("span",{className:"text-theme-muted text-xs",children:r.description},void 0,!1,void 0,this)]},void 0,!0,void 0,this),Tr("nav",{className:"flex flex-wrap justify-center gap-x-6 gap-y-2 mb-4 md:mb-0",children:f.map((i)=>Tr("a",{href:i.href,className:"text-theme-muted text-[13px] hover:text-brand transition-colors",children:i.label},i.href,!1,void 0,this))},void 0,!1,void 0,this),Tr("div",{className:"flex items-center gap-4",children:[r.copyright&&Tr("span",{className:"text-theme-light text-[11px]",children:r.copyright},void 0,!1,void 0,this),Tr(tf,{variant:"footer",size:"sm"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var fu=Hu.extend(n4.shape);var qK=X.enum(["generating","draft","queued","published","failed"]),_K=X.enum(["generating","draft","queued","published","failed"]),t4="publishedAt is required when deck status is published",u4=(l)=>l.status==="published"&&!l.publishedAt,h4=(l)=>{if(u4(l))throw Error(t4)},Y1=X.object({title:X.string(),slug:X.string().optional(),description:X.string().optional(),author:X.string().optional(),status:qK,publishedAt:X.string().datetime().optional(),event:X.string().optional(),coverImageId:X.string().optional(),ogImageId:X.string().optional()}),Tu0=X.object({title:X.string(),description:X.string().optional(),status:qK,publishedAt:X.string().datetime().optional(),coverImageId:X.string().optional(),slug:X.string(),error:X.string().optional()}).superRefine((l,r)=>{if(!u4(l))return;r.addIssue({code:X.ZodIssueCode.custom,path:["publishedAt"],message:t4})}),Wq=X.object({title:X.string(),description:X.string().optional(),status:_K,publishedAt:X.string().datetime().optional(),coverImageId:X.string().optional(),slug:X.string(),error:X.string().optional()}).superRefine((l,r)=>{if(!u4(l))return;r.addIssue({code:"custom",path:["publishedAt"],message:t4})}),Zq=X.object({title:X.string(),slug:X.string().optional(),description:X.string().optional(),author:X.string().optional(),status:_K,publishedAt:X.string().datetime().optional(),event:X.string().optional(),coverImageId:X.string().optional(),ogImageId:X.string().optional()}),cn=zi.extend({entityType:X.literal("deck"),metadata:Wq}),uo=cn.extend({frontmatter:Zq,body:X.string(),ogImageUrl:X.string().optional()}),iu=uo.extend({url:X.string().optional(),typeLabel:X.string().optional(),listUrl:X.string().optional(),listLabel:X.string().optional(),coverImageUrl:X.string().optional(),ogImageUrl:X.string().optional(),coverImageWidth:X.number().optional(),coverImageHeight:X.number().optional()});class o4 extends Fu{constructor(){super({entityType:"deck",purpose:"A slide-deck presentation.",schema:cn,frontmatterSchema:Y1,supportsCoverImage:!0})}validateSlideStructure(l){if(!/^---$/gm.test(l))throw Error("Invalid deck: markdown must contain slide separators (---) to be a valid presentation")}toMarkdown(l){let r=this.extractBody(l.content);this.validateSlideStructure(r);let f=this.parseFrontMatter(l.content,Y1);h4(f);let i={...f,slug:f.slug??l.metadata.slug};return this.buildMarkdown(r,i)}fromMarkdown(l){let r=this.parseFrontmatter(l),f=this.extractBody(l);h4(r),this.validateSlideStructure(f);let i=r.slug??sl(r.title),n=r.status;return{entityType:"deck",content:l,metadata:{slug:i,title:r.title,description:r.description,status:n,publishedAt:r.publishedAt,coverImageId:r.coverImageId}}}generateTitle(l){return l.metadata.title}generateSummary(l){if(l.metadata.description)return l.metadata.description;return`Presentation: ${l.metadata.title}`}generateFrontMatter(l){return this.toMarkdown(l)}buildStub(l){let r={title:l.title,slug:l.id,status:"generating"};return{content:this.buildMarkdown(`---
1375
+ `}},void 0,!1,void 0,this),nr("header",{className:"printable-hero",children:[nr("div",{className:"printable-masthead",children:r.brandLabel&&nr("p",{className:"printable-kicker",children:r.brandLabel},void 0,!1,void 0,this)},void 0,!1,void 0,this),nr("div",{children:[nr("h1",{className:"printable-title",children:r.title},void 0,!1,void 0,this),r.excerpt&&nr("p",{className:"printable-excerpt",children:r.excerpt},void 0,!1,void 0,this),nr("div",{className:"printable-meta",children:[r.author&&nr("span",{children:["By ",r.author]},void 0,!0,void 0,this),f&&nr("span",{children:f},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this),r.coverImageUrl&&nr("figure",{className:"printable-cover-wrap",children:nr("img",{className:"printable-cover",src:r.coverImageUrl,alt:""},void 0,!1,void 0,this)},void 0,!1,void 0,this),nr(P1,{markdown:r.body,className:"printable-body"},void 0,!1,void 0,this),(Boolean(r.canonicalUrl)||Boolean(r.brandLabel))&&nr("footer",{className:"printable-footer",children:[nr("span",{children:r.brandLabel??"Printable PDF"},void 0,!1,void 0,this),r.canonicalUrl&&nr("a",{href:r.canonicalUrl,children:r.canonicalUrl},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}var sI=26214400,CI=60000;class j6{metadata={outputEntityType:"document"};context;renderPdf;constructor(l,r={}){this.context=l,this.renderPdf=r.renderPdf??dt}async resolve(l){if(l.sourceEntityType!=="post"||l.attachmentType!==fo)return;let r=await this.context.entityService.getEntity({entityType:"post",id:l.sourceEntityId});if(!r)return;let f=eI(r,{brandLabel:this.resolveBrandLabel(),coverImageUrl:await this.resolveCoverImageUrl(r)}),i=await xI(EI(jI(),"brain-blog-printable-"));try{let n=await S1({outputDir:i,mediaPath:`/_media/printable/post/${r.id}`,template:TK,format:"pdf",content:f,siteConfig:{title:f.title,themeMode:"light"},themeCSS:this.context.themeCSS}),t=await j1({rootDir:i});try{return{type:"document",data:await this.renderPdf(t.urlFor(n.urlPath),{maxBytes:sI,timeoutMs:CI,printBackground:!0,preferCSSPageSize:!0}),mimeType:"application/pdf",filename:`${lq(r)}-printable.pdf`}}finally{await t.close()}}finally{await SI(i,{recursive:!0,force:!0})}}resolveBrandLabel(){let l=this.context.domain?.trim();if(l&&l.length>0)return l;let r=this.context.identity.getProfile().name.trim();return r.length>0?r:void 0}async resolveCoverImageUrl(l){let{frontmatter:r}=X1(l.content),f=Cl.parse(r);if(!f.coverImageId)return;let i=await this.context.entityService.getEntity({entityType:"image",id:f.coverImageId});return i?.content.startsWith("data:image/")?i.content:void 0}}function eI(l,r={}){let{frontmatter:f,content:i}=X1(l.content),n=Cl.parse(f);return{title:n.title,body:i,...n.excerpt?{excerpt:n.excerpt}:{},...n.author?{author:n.author}:{},...n.publishedAt?{publishedAt:n.publishedAt}:{},...n.canonicalUrl?{canonicalUrl:n.canonicalUrl}:{},...r.coverImageUrl?{coverImageUrl:r.coverImageUrl}:{},...r.brandLabel?{brandLabel:r.brandLabel}:{}}}function lq(l){let r=l.metadata.slug;return r.length>0?r:sl(l.metadata.title)}import{mkdtemp as tq,rm as uq}from"fs/promises";import{tmpdir as hq}from"os";import{join as oq}from"path";import{jsxDEV as nq}from"preact/jsx-dev-runtime";var ru="og-image",rq="blog:og-image",RK=X.object({title:X.string().min(1),excerpt:X.string().optional(),author:X.string().optional(),publishedAt:X.string().optional(),brandLabel:X.string().optional(),coverImageUrl:X.string().optional()}),LK={name:rq,pluginId:"blog",schema:RK,renderers:{image:iq}};function fq(l){if(!l)return;let r=new Date(l);if(Number.isNaN(r.getTime()))return;return r.toLocaleDateString("en-GB",{year:"numeric",month:"short",day:"2-digit"})}function iq(l){let r=RK.parse(l);return nq(ot,{brandLabel:r.brandLabel??r.title,eyebrow:"Journal",title:r.title,subtitle:r.excerpt,meta:[r.author],tag:fq(r.publishedAt)},void 0,!1,void 0,this)}var wq={width:1200,height:630},vq=60000;class E6{metadata={outputEntityType:"image",targetField:"ogImageId"};context;screenshotPng;constructor(l,r={}){this.context=l,this.screenshotPng=r.screenshotPng??yt}async resolve(l){if(l.sourceEntityType!=="post"||l.attachmentType!==ru)return;let r=await this.context.entityService.getEntity({entityType:"post",id:l.sourceEntityId});if(!r)return;let f=mq(r,{brandLabel:this.resolveBrandLabel(),coverImageUrl:await this.resolveCoverImageUrl(r)}),i=await tq(oq(hq(),"brain-blog-og-image-"));try{let n=await S1({outputDir:i,mediaPath:`/_media/og/post/${r.id}`,template:LK,format:"image",content:f,siteConfig:{title:f.title,themeMode:"light"},themeCSS:this.context.themeCSS}),t=await j1({rootDir:i});try{return{type:"image",data:await this.screenshotPng(t.urlFor(n.urlPath),wq,{timeoutMs:vq,fullPage:!1,omitBackground:!1}),mimeType:"image/png",filename:`${kq(r)}-og.png`}}finally{await t.close()}}finally{await uq(i,{recursive:!0,force:!0})}}resolveBrandLabel(){let l=this.context.domain?.trim();if(l&&l.length>0)return l;let r=this.context.identity.getProfile().name.trim();return r.length>0?r:void 0}async resolveCoverImageUrl(l){let{frontmatter:r}=X1(l.content),f=Cl.parse(r);if(!f.coverImageId)return;let i=await this.context.entityService.getEntity({entityType:"image",id:f.coverImageId});return i?.content.startsWith("data:image/")?i.content:void 0}}function mq(l,r={}){let{frontmatter:f}=X1(l.content),i=Cl.parse(f);return{title:i.title,...i.excerpt?{excerpt:i.excerpt}:{},...i.author?{author:i.author}:{},...i.publishedAt?{publishedAt:i.publishedAt}:{},...r.coverImageUrl?{coverImageUrl:r.coverImageUrl}:{},...r.brandLabel?{brandLabel:r.brandLabel}:{}}}function kq(l){let r=l.metadata.slug;return r.length>0?r:sl(l.metadata.title)}var PK={name:"@brains/blog",version:"0.2.0-alpha.220",description:"AI-powered blog post generation from existing brain content",dependencies:{"@brains/atproto-contracts":"workspace:*","@brains/contracts":"workspace:*","@brains/media-page-composer":"workspace:*","@brains/media-renderer":"workspace:*","@brains/plugins":"workspace:*","@brains/ui-library":"workspace:*","@brains/utils":"workspace:*"},devDependencies:{"@brains/app":"workspace:*","@brains/eslint-config":"workspace:*","@brains/typescript-config":"workspace:*","@types/bun":"^1.3.14","bun-types":"^1.3.14",eslint:"^10.5.0",typescript:"^7.0.2"},exports:{".":"./src/index.ts"},private:!0,scripts:{eval:"cd evals && bun run brain-eval",lint:"eslint . --max-warnings 0","lint:fix":"eslint . --max-warnings 0 --fix",test:"bun test",typecheck:"tsc --noEmit"},type:"module"};class MK extends Bn{entityType=bf.entityType;schema=s6;adapter=bf;unregisterAtprotoProjection;unregisterPrintableAttachmentProvider;unregisterOgImageAttachmentProvider;constructor(l={}){super("blog",PK,l,Po)}getEntityTypeConfig(){return{weight:2,projectionSourceRole:"primary",publish:{publishStatuses:["queued","published"]}}}createGenerationHandler(l){return new Mo(this.logger.child("BlogGenerationJobHandler"),l)}getTemplates(){return h$()}getDataSources(){return[new Io(this.logger.child("BlogDataSource"))]}async onRegister(l){let{RSSDataSource:r}=await import("./chunks/rss-datasource-zj8k12xw.js");l.entities.registerDataSource(new r(this.logger.child("RSSDataSource"))),o$(l,this.logger),w$(l,this.logger),m$(l,this.logger),k$(l),this.unregisterPrintableAttachmentProvider=l.attachments.register("post",fo,new j6(l)),this.unregisterOgImageAttachmentProvider=l.attachments.register("post",ru,new E6(l)),this.deferPublishAssetRegistration(l),this.unregisterAtprotoProjection=rf.getInstance().register(Cw()),this.logger.info("Blog plugin registered (routes auto-generated at /posts/)")}deferPublishAssetRegistration(l){l.messaging.subscribe(ei.pluginsRegistered,async()=>{return await l.messaging.send({type:"publish-assets:register",payload:{entityType:"post",attachmentType:ru,mediaEntityType:"image",targetEntityField:{location:"frontmatter",field:"ogImageId"},requiredWhen:{status:"published"},autoGenerate:!0,jobType:"image:image-render-source"}}),{success:!0}})}async onShutdown(){this.unregisterPrintableAttachmentProvider?.(),this.unregisterPrintableAttachmentProvider=void 0,this.unregisterOgImageAttachmentProvider?.(),this.unregisterOgImageAttachmentProvider=void 0,this.unregisterAtprotoProjection?.(),this.unregisterAtprotoProjection=void 0}}class io{postsListUrl;id="personal:homepage";name="Personal Homepage DataSource";description="Fetches profile and blog posts for a personal homepage";constructor(l){this.postsListUrl=l}async fetch(l,r,f){let i=f.entityService,[n,t,u]=await Promise.all([Jf(i,Qn),Ji(i,{entityType:"post",count:6,parse:Lf}),Rf(i)]),o={profile:n,posts:t,postsListUrl:this.postsListUrl,cta:Hn(u.cta)};return r.parse(o)}}class no{id="personal:about";name="About Page DataSource";description="Fetches full profile data for the about page";async fetch(l,r,f){let n={profile:await Jf(f.entityService,Qn)};return r.parse(n)}}import{jsxDEV as _0,Fragment as VK}from"preact/jsx-dev-runtime";function bq(l){return new Date(l).toLocaleDateString("en-US",{year:"numeric",month:"long",day:"numeric"})}function yq(l){return l.split(/(\*[^*]+\*)/).map((r,f)=>{if(r.startsWith("*")&&r.endsWith("*")&&r.length>2)return _0("em",{className:"font-sans italic font-medium text-theme-inverse tracking-[-0.015em] [font-variation-settings:'opsz'_72]",children:r.slice(1,-1)},f,!1,void 0,this);return r})}var C6=({profile:l,posts:r,postsListUrl:f,cta:i})=>{let n=l.tagline??l.description,t=l.name.split(" ")[0];return _0(VK,{children:[_0(Ll,{title:l.name,description:l.description??n??"Personal site",ogType:"website"},void 0,!1,void 0,this),_0("header",{className:"hero-decor bg-brand text-theme-inverse relative flex flex-col items-center justify-center px-6 md:px-12 pt-32 pb-24 gap-7",children:_0("div",{className:"w-full max-w-[1100px] mx-auto text-center flex flex-col items-center gap-7",children:[n&&_0("h1",{className:"font-heading font-bold text-[clamp(2.5rem,6vw,5rem)] leading-[1.05] tracking-[-0.035em] text-theme-inverse text-balance m-0 [font-variation-settings:'wdth'_90,'opsz'_96]",children:yq(n)},void 0,!1,void 0,this),l.description&&_0("p",{className:"text-[clamp(1.0625rem,1.4vw,1.3125rem)] leading-[1.5] text-theme-inverse opacity-95 max-w-[600px] mx-auto m-0",children:l.description},void 0,!1,void 0,this),_0("div",{className:"flex flex-wrap justify-center gap-3.5 mt-2",children:[_0("a",{href:f,className:"inline-flex items-center gap-2 rounded-full py-3.5 px-7 bg-theme text-brand border-2 border-theme font-heading font-semibold text-[15px] hover:bg-theme-subtle hover:-translate-y-0.5 transition-all [font-variation-settings:'wdth'_92,'opsz'_18]",children:["Read the Blog ",_0("span",{"aria-hidden":"true",children:"\u2192"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),_0("a",{href:"/about",className:"rounded-full py-3.5 px-7 border-2 border-theme-light text-theme-inverse font-heading font-semibold text-[15px] hover:bg-brand-dark hover:border-theme hover:-translate-y-0.5 transition-all [font-variation-settings:'wdth'_92,'opsz'_18]",children:["About ",t]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),r.length>0&&_0("section",{className:"flex flex-col py-24 px-6 md:px-12 gap-11 bg-theme-subtle",children:[_0("div",{className:"flex justify-between items-end max-w-layout mx-auto w-full gap-6 flex-wrap",children:[_0("div",{children:[_0("span",{className:"block font-heading font-semibold text-[13px] uppercase tracking-[0.22em] text-brand mb-3 [font-variation-settings:'wdth'_85,'opsz'_12]",children:"The Archive"},void 0,!1,void 0,this),_0("h2",{className:"font-heading font-bold text-[clamp(2.25rem,5vw,3.75rem)] leading-none tracking-[-0.03em] text-heading m-0 [font-variation-settings:'wdth'_92,'opsz'_64]",children:["Recent"," ",_0("em",{className:"font-sans italic font-medium text-brand [font-variation-settings:'opsz'_60]",children:"posts"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this),_0("a",{href:f,className:"font-heading font-semibold text-[15px] text-brand inline-flex items-center gap-1.5 pb-1.5 border-b-2 border-current hover:text-brand-dark transition-colors [font-variation-settings:'wdth'_88,'opsz'_16]",children:["View the whole archive ",_0("span",{"aria-hidden":"true",children:"\u2192"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this),_0("div",{className:"grid grid-cols-[repeat(auto-fit,minmax(min(100%,320px),1fr))] gap-8 max-w-layout mx-auto w-full",children:r.map((u)=>_0("a",{href:u.url,className:"group flex flex-col bg-theme border border-theme rounded-md overflow-hidden hover:-translate-y-1 hover:border-brand/50 transition-all no-underline shadow-sm hover:shadow-lg",children:[u.coverImageUrl?_0("img",{src:u.coverImageUrl,alt:u.metadata.title,className:"w-full aspect-[16/10] object-cover bg-theme-muted"},void 0,!1,void 0,this):_0("div",{className:"w-full aspect-[16/10] bg-gradient-to-br from-brand to-accent","aria-hidden":"true"},void 0,!1,void 0,this),_0("div",{className:"flex flex-col gap-4 p-7 flex-1",children:[_0("div",{className:"flex flex-wrap items-center gap-3.5 font-heading text-[11px] font-semibold uppercase tracking-[0.18em] text-theme-muted [font-variation-settings:'wdth'_85,'opsz'_11]",children:[u.frontmatter.seriesName&&_0(VK,{children:[_0("span",{className:"text-brand",children:u.frontmatter.seriesName},void 0,!1,void 0,this),_0("span",{className:"w-[3px] h-[3px] rounded-full bg-current opacity-50","aria-hidden":"true"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),_0("time",{children:bq(u.metadata.publishedAt??u.created)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),_0("h3",{className:"font-sans font-semibold text-[clamp(1.375rem,1.8vw,1.625rem)] leading-[1.18] tracking-[-0.012em] text-heading m-0 text-balance",children:u.metadata.title},void 0,!1,void 0,this),u.frontmatter.excerpt&&_0("p",{className:"text-base leading-[1.55] text-theme-muted m-0 flex-1",children:u.frontmatter.excerpt},void 0,!1,void 0,this),_0("span",{className:"inline-flex items-center gap-1.5 font-heading font-semibold text-[13px] uppercase tracking-[0.14em] text-brand mt-1 [font-variation-settings:'wdth'_88,'opsz'_14]",children:["Read the post"," ",_0("span",{"aria-hidden":"true",className:"transition-transform group-hover:translate-x-1",children:"\u2192"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},u.id,!0,void 0,this))},void 0,!1,void 0,this)]},void 0,!0,void 0,this),_0("section",{className:"cta-decor bg-theme-dark text-theme-on-dark flex-grow flex flex-col items-center justify-center py-24 px-6 md:px-12 gap-5",children:[_0("span",{className:"font-heading font-medium text-xs uppercase tracking-[0.24em] text-accent [font-variation-settings:'wdth'_85,'opsz'_12]",children:"Get in touch"},void 0,!1,void 0,this),_0("h2",{className:"text-center font-heading font-bold text-[clamp(2rem,4vw,3rem)] leading-none tracking-[-0.03em] text-theme-on-dark m-0 [font-variation-settings:'wdth'_90,'opsz'_64]",children:i.heading},void 0,!1,void 0,this),i.subtitle&&_0("p",{className:"text-center text-base leading-[1.55] text-theme-on-dark opacity-80 max-w-[480px] m-0",children:i.subtitle},void 0,!1,void 0,this),_0("a",{href:i.buttonLink,className:"inline-flex items-center gap-2 rounded-full py-3.5 px-7 bg-brand text-theme-inverse border-2 border-brand font-heading font-semibold text-[15px] hover:bg-brand-dark hover:-translate-y-0.5 transition-all mt-2 [font-variation-settings:'wdth'_92,'opsz'_18]",children:[i.buttonText," ",_0("span",{"aria-hidden":"true",children:"\u2192"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)};import{jsxDEV as tr,Fragment as dq}from"preact/jsx-dev-runtime";var e6=({profile:l})=>{let r=`About ${l.name}`,f=l.description??l.intro??"About page",i=Boolean(l.email??l.website);return tr(dq,{children:[tr(Ll,{title:r,description:f,ogType:"profile"},void 0,!1,void 0,this),tr("section",{className:"hero-decor bg-brand text-theme-inverse relative flex flex-col px-6 md:px-12 pt-32 pb-24",children:tr("div",{className:"w-full max-w-3xl mx-auto flex flex-col gap-6",children:[tr("h1",{className:"font-heading font-bold text-[clamp(2.5rem,6vw,4.5rem)] leading-[1.05] tracking-[-0.035em] text-theme-inverse text-balance m-0 [font-variation-settings:'wdth'_90,'opsz'_96]",children:l.name},void 0,!1,void 0,this),l.description&&tr("p",{className:"text-[clamp(1.0625rem,1.4vw,1.25rem)] leading-[1.55] text-theme-inverse opacity-95 max-w-2xl m-0",children:l.description},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),l.story&&tr("section",{className:`py-24 px-6 md:px-12 bg-theme-subtle${i?"":" flex-grow"}`,children:tr("div",{className:"max-w-3xl mx-auto",children:tr(P1,{markdown:l.story},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),i&&tr("section",{className:"cta-decor bg-theme-dark text-theme-on-dark flex-grow flex flex-col items-center justify-center py-24 px-6 md:px-12 gap-5",children:[tr("span",{className:"font-heading font-medium text-xs uppercase tracking-[0.24em] text-accent [font-variation-settings:'wdth'_85,'opsz'_12]",children:"Get in touch"},void 0,!1,void 0,this),tr("h2",{className:"text-center font-heading font-bold text-[clamp(2rem,4vw,3rem)] leading-none tracking-[-0.03em] text-theme-on-dark m-0 [font-variation-settings:'wdth'_90,'opsz'_64]",children:["Say"," ",tr("em",{className:"font-sans italic font-medium text-accent [font-variation-settings:'opsz'_72]",children:"hi."},void 0,!1,void 0,this)]},void 0,!0,void 0,this),tr("div",{className:"flex flex-wrap justify-center gap-3.5 mt-2",children:[l.email&&tr("a",{href:`mailto:${l.email}`,className:"inline-flex items-center gap-2 rounded-full py-3.5 px-7 bg-brand text-theme-inverse border-2 border-brand font-heading font-semibold text-[15px] hover:bg-brand-dark hover:-translate-y-0.5 transition-all [font-variation-settings:'wdth'_92,'opsz'_18]",children:["Get in Touch ",tr("span",{"aria-hidden":"true",children:"\u2192"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),l.website&&tr("a",{href:l.website,className:"rounded-full py-3.5 px-7 border-2 border-theme-light text-theme-on-dark font-heading font-semibold text-[15px] hover:bg-brand-dark hover:border-theme hover:-translate-y-0.5 transition-all [font-variation-settings:'wdth'_92,'opsz'_18]",children:"Website"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)};var pK={name:"@brains/site-personal",version:"0.2.0-alpha.220",description:"Personal site composition package",dependencies:{"@brains/blog":"workspace:*","@brains/plugins":"workspace:*","@brains/site-composition":"workspace:*","@brains/site-engine":"workspace:*","@brains/site-info":"workspace:*","@brains/templates":"workspace:*","@brains/ui-library":"workspace:*","@brains/utils":"workspace:*",preact:"^10.27.2"},devDependencies:{"@brains/eslint-config":"workspace:*","@brains/typescript-config":"workspace:*","@types/bun":"^1.3.14","bun-types":"^1.3.14",eslint:"^10.5.0",typescript:"^7.0.2"},exports:{".":"./src/index.ts"},files:["src"],private:!0,scripts:{lint:"eslint . --max-warnings 0","lint:fix":"eslint . --max-warnings 0 --fix",typecheck:"tsc --noEmit"},type:"module"};var cq=X.object({entityDisplay:X.object({post:X.object({label:X.string().default("Post"),pluralName:X.string().optional()}).default({label:"Post"})}).default({post:{label:"Post"}})}),Aq=X.object({heading:X.string(),buttonText:X.string(),buttonLink:X.string(),subtitle:X.string().optional()}),IK=X.looseObject({name:X.string(),kind:X.enum(["professional","team","collective"]),organization:X.string().optional(),description:X.string().optional(),avatar:X.string().optional(),website:X.string().optional(),email:X.string().optional(),socialLinks:X.array(X.object({platform:X.enum(["github","instagram","linkedin","email","website"]),url:X.string(),label:X.string().optional()})).optional(),tagline:X.string().optional(),intro:X.string().optional(),story:X.string().optional()}),Kq=X.looseObject({id:X.string(),entityType:X.literal("post"),content:X.string(),created:X.string(),updated:X.string(),contentHash:X.string(),metadata:X.looseObject({title:X.string(),publishedAt:X.string().optional()}),frontmatter:X.looseObject({excerpt:X.string(),seriesName:X.string().optional(),seriesIndex:X.number().optional()}),body:X.string(),url:X.string().optional(),typeLabel:X.string().optional(),listUrl:X.string().optional(),listLabel:X.string().optional(),seriesUrl:X.string().optional(),coverImageUrl:X.string().optional(),ogImageUrl:X.string().optional(),coverImageWidth:X.number().optional(),coverImageHeight:X.number().optional(),coverImageSrcset:X.string().optional(),coverImageSizes:X.string().optional()});class l4 extends to{dependencies=["blog"];constructor(l={}){super("personal-site",pK,l,cq)}async onRegister(l){l.entities.extendFrontmatterSchema("anchor-profile",Gu);let r=this.config.entityDisplay.post,f=`/${r.pluralName??r.label.toLowerCase()+"s"}`,i=new io(f);l.entities.registerDataSource(i);let n=new no;l.entities.registerDataSource(n);let t=X.object({profile:IK,posts:X.array(Kq),postsListUrl:X.string(),cta:Aq}),u=X.object({profile:IK});l.templates.register({homepage:Ol({name:"homepage",description:"Personal homepage with recent blog posts",schema:t,dataSourceId:"personal:homepage",requiredPermission:"public",layout:{component:C6}}),about:Ol({name:"about",description:"About page with profile",schema:u,dataSourceId:"personal:about",requiredPermission:"public",layout:{component:e6}})}),this.logger.info("Personal site plugin registered successfully")}async getTools(){return[]}async getResources(){return[]}}function r4(l){return new l4(l??{})}var f4=[{id:"home",path:"/",title:"Home",description:"Personal site homepage",layout:"default",navigation:{show:!0,label:"Home",slot:"secondary",priority:10},sections:[{id:"homepage",template:"personal-site:homepage",dataQuery:{}}]},{id:"about",path:"/about",title:"About",description:"About page",layout:"default",navigation:{show:!0,label:"About",slot:"primary",priority:90},sections:[{id:"about",template:"personal-site:about",dataQuery:{}}]}];import{jsxDEV as Tr}from"preact/jsx-dev-runtime";function i4({sections:l,siteInfo:r}){let f=[...r.navigation.primary,...r.navigation.secondary].filter((i)=>i.label!=="Home");return Tr("div",{className:"flex flex-col min-h-screen bg-theme overflow-x-clip",children:[Tr(ht,{title:r.title,titleClassName:"font-heading font-bold text-2xl",navigation:r.navigation.primary,showThemeToggle:!0,themeToggleClassName:"bg-theme-toggle text-theme-toggle-icon hover:bg-theme-toggle-hover rounded-[10px]",...r.logo!==void 0?{logo:r.logo}:{}},void 0,!1,void 0,this),Tr("main",{className:"flex-grow flex flex-col",children:l},void 0,!1,void 0,this),Tr("footer",{className:"bg-footer text-footer border-t border-theme",children:Tr("div",{className:"max-w-layout mx-auto flex flex-col md:flex-row justify-between items-center py-8 px-6 md:px-8",children:[Tr("div",{className:"flex flex-col gap-1 mb-4 md:mb-0",children:[Tr("span",{className:"text-brand font-heading font-bold text-lg",children:r.title},void 0,!1,void 0,this),r.description&&Tr("span",{className:"text-theme-muted text-xs",children:r.description},void 0,!1,void 0,this)]},void 0,!0,void 0,this),Tr("nav",{className:"flex flex-wrap justify-center gap-x-6 gap-y-2 mb-4 md:mb-0",children:f.map((i)=>Tr("a",{href:i.href,className:"text-theme-muted text-[13px] hover:text-brand transition-colors",children:i.label},i.href,!1,void 0,this))},void 0,!1,void 0,this),Tr("div",{className:"flex items-center gap-4",children:[r.copyright&&Tr("span",{className:"text-theme-light text-[11px]",children:r.copyright},void 0,!1,void 0,this),Tr(tf,{variant:"footer",size:"sm"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var fu=Hu.extend(n4.shape);var qK=X.enum(["generating","draft","queued","published","failed"]),_K=X.enum(["generating","draft","queued","published","failed"]),t4="publishedAt is required when deck status is published",u4=(l)=>l.status==="published"&&!l.publishedAt,h4=(l)=>{if(u4(l))throw Error(t4)},Y1=X.object({title:X.string(),slug:X.string().optional(),description:X.string().optional(),author:X.string().optional(),status:qK,publishedAt:X.string().datetime().optional(),event:X.string().optional(),coverImageId:X.string().optional(),ogImageId:X.string().optional()}),Tu0=X.object({title:X.string(),description:X.string().optional(),status:qK,publishedAt:X.string().datetime().optional(),coverImageId:X.string().optional(),slug:X.string(),error:X.string().optional()}).superRefine((l,r)=>{if(!u4(l))return;r.addIssue({code:X.ZodIssueCode.custom,path:["publishedAt"],message:t4})}),Wq=X.object({title:X.string(),description:X.string().optional(),status:_K,publishedAt:X.string().datetime().optional(),coverImageId:X.string().optional(),slug:X.string(),error:X.string().optional()}).superRefine((l,r)=>{if(!u4(l))return;r.addIssue({code:"custom",path:["publishedAt"],message:t4})}),Zq=X.object({title:X.string(),slug:X.string().optional(),description:X.string().optional(),author:X.string().optional(),status:_K,publishedAt:X.string().datetime().optional(),event:X.string().optional(),coverImageId:X.string().optional(),ogImageId:X.string().optional()}),cn=zi.extend({entityType:X.literal("deck"),metadata:Wq}),uo=cn.extend({frontmatter:Zq,body:X.string(),ogImageUrl:X.string().optional()}),iu=uo.extend({url:X.string().optional(),typeLabel:X.string().optional(),listUrl:X.string().optional(),listLabel:X.string().optional(),coverImageUrl:X.string().optional(),ogImageUrl:X.string().optional(),coverImageWidth:X.number().optional(),coverImageHeight:X.number().optional()});class o4 extends Fu{constructor(){super({entityType:"deck",purpose:"A slide-deck presentation.",schema:cn,frontmatterSchema:Y1,supportsCoverImage:!0})}validateSlideStructure(l){if(!/^---$/gm.test(l))throw Error("Invalid deck: markdown must contain slide separators (---) to be a valid presentation")}toMarkdown(l){let r=this.extractBody(l.content);this.validateSlideStructure(r);let f=this.parseFrontMatter(l.content,Y1);h4(f);let i={...f,slug:f.slug??l.metadata.slug};return this.buildMarkdown(r,i)}fromMarkdown(l){let r=this.parseFrontmatter(l),f=this.extractBody(l);h4(r),this.validateSlideStructure(f);let i=r.slug??sl(r.title),n=r.status;return{entityType:"deck",content:l,metadata:{slug:i,title:r.title,description:r.description,status:n,publishedAt:r.publishedAt,coverImageId:r.coverImageId}}}generateTitle(l){return l.metadata.title}generateSummary(l){if(l.metadata.description)return l.metadata.description;return`Presentation: ${l.metadata.title}`}generateFrontMatter(l){return this.toMarkdown(l)}buildStub(l){let r={title:l.title,slug:l.id,status:"generating"};return{content:this.buildMarkdown(`---
1376
1376
  `,r),metadata:{title:l.title,slug:l.id,status:"generating"}}}}var nu=new o4;var Xq=X.object({markdown:X.string().describe("Markdown content with slide separators (---)"),deck:iu.optional()}),w4=Ol({name:"deck-detail",description:"Render a presentation deck as Reveal.js slides",schema:Xq,dataSourceId:"decks:entities",requiredPermission:"public",layout:{component:Fw,fullscreen:!0}});var v4=X.object({decks:X.array(uo)}),m4=X.object({decks:X.array(iu),pageTitle:X.string().optional(),pageLabel:X.string().optional()});import{jsxDEV as k4}from"preact/jsx-dev-runtime";var Yq="Presentations",$4=({decks:l,pageLabel:r})=>{let f=l.map((n)=>({id:n.id,url:n.url,title:n.frontmatter.title,date:n.frontmatter.publishedAt??n.created,description:n.frontmatter.description}));return k4("div",{className:"deck-list bg-theme",children:k4("div",{className:"container mx-auto max-w-[1100px] px-6 py-16 md:px-12 md:py-24",children:k4(wt,{label:r&&r!=="Decks"?r:Yq,items:f},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)};class ho extends DK{constructor(){super(v4,{title:"Deck List",mappings:[{key:"decks",label:"Decks",type:"array",itemType:"object"}]})}}var b4=Ol({name:"deck-list",description:"List view of all presentation decks",schema:m4,dataSourceId:"decks:entities",requiredPermission:"public",formatter:new ho,layout:{component:$4}});var zq=X.object({title:X.string().max(80).describe("A short, punchy title (2-5 words) that's memorable and evocative"),content:X.string().describe("Full slide deck content in markdown format with slide separators (---). Each slide should have a header and focused content."),description:X.string().describe("A concise 1-2 sentence summary that captures the essence of the talk")}),xK=Ol({name:"decks:generation",description:"Template for AI to generate complete slide decks from prompts",schema:zq,dataSourceId:"shell:ai-content",requiredPermission:"public",useKnowledgeContext:!0,basePrompt:`You are creating slide decks in a distinctive voice that blends philosophy, technology, and culture.
1377
1377
 
1378
1378
  Your task is to generate a complete slide deck based on the user's prompt.
@@ -1680,12 +1680,12 @@ ${o}`,templateName:"decks:description"})).description,await this.reportProgress(
1680
1680
  color: var(--carousel-accent);
1681
1681
  font-weight: 600;
1682
1682
  }
1683
- `}},void 0,!1,void 0,this),n.map((v,k)=>mr("section",{className:`deck-carousel-slide${k===0?" is-cover":""}`,children:[mr("header",{className:"deck-carousel-header",children:mr("span",{className:"deck-carousel-wordmark","aria-label":f??r,children:[mr("span",{className:"wm-primary",children:t.primary},void 0,!1,void 0,this),t.secondary!==void 0&&mr(Lq,{children:[mr("span",{className:"wm-dot",children:"."},void 0,!1,void 0,this),mr("span",{className:"wm-secondary",children:t.secondary},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),mr("div",{className:"deck-carousel-body",children:mr("div",{className:"deck-carousel-content",dangerouslySetInnerHTML:{__html:w(v.markdown)}},void 0,!1,void 0,this)},void 0,!1,void 0,this),mr("footer",{className:"deck-carousel-footer",children:[mr("span",{className:"deck-carousel-footer-meta",children:i??r},void 0,!1,void 0,this),mr("span",{className:"deck-carousel-counter","aria-label":`Slide ${k+1} of ${u}`,children:[mr("span",{className:"deck-carousel-counter-current",children:String(k+1).padStart(2,"0")},void 0,!1,void 0,this)," / ",mr("span",{children:o},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},k,!0,void 0,this))]},void 0,!0,void 0,this)}import{mkdtemp as Pq,rm as Mq}from"fs/promises";import{tmpdir as Vq}from"os";import{join as pq}from"path";var Iq=26214400,qq=60000,hW=20;class W4{metadata={outputEntityType:"document"};context;renderPdf;getThemeMode;constructor(l,r={}){this.context=l,this.renderPdf=r.renderPdf??dt,this.getThemeMode=r.getThemeMode??(async()=>"dark")}async resolve(l){if(l.sourceEntityType!=="deck")return;let r=await this.context.entityService.getEntity({entityType:"deck",id:l.sourceEntityId});if(!r)return;let f=_q(r,{brandLabel:this.resolveBrandLabel()});if(f.slides.length>hW)throw Error(`Refusing to render carousel with ${f.slides.length} slides; maxSlides=${hW}`);let i=await this.getThemeMode(),n=await Pq(pq(Vq(),"brain-deck-carousel-"));try{let t=await S1({outputDir:n,mediaPath:`/_media/carousel/${r.id}`,template:uW,format:"pdf",content:f,siteConfig:{title:f.title,themeMode:i},themeCSS:this.context.themeCSS}),u=await j1({rootDir:n});try{return{type:"document",data:await this.renderPdf(u.urlFor(t.urlPath),{maxBytes:Iq,timeoutMs:qq,printBackground:!0,preferCSSPageSize:!0}),mimeType:"application/pdf",filename:`${Dq(r)}-carousel.pdf`}}finally{await u.close()}}finally{await Mq(n,{recursive:!0,force:!0})}}resolveBrandLabel(){let l=this.context.domain?.trim();if(l&&l.length>0)return l;let r=this.context.identity.getProfile().name;return r.length>0?r:void 0}}function _q(l,r={}){let{frontmatter:f,content:i}=X1(l.content),n=typeof f.title==="string"?f.title:l.metadata.title,t=typeof f.event==="string"&&f.event.length>0?f.event:void 0,u=i.split(/^---$/gm).map((o)=>o.trim()).filter((o)=>o.length>0).map((o)=>({markdown:o}));return{title:n,slides:u,...r.brandLabel?{brandLabel:r.brandLabel}:{},...t?{eyebrow:t}:{}}}function Dq(l){let r=l.metadata.slug;return r.length>0?r:sl(l.metadata.title)}import{jsxDEV as jq}from"preact/jsx-dev-runtime";var vo="og-image",xq="decks:og-image",oW=X.object({title:X.string().min(1),description:X.string().optional(),event:X.string().optional(),brandLabel:X.string().optional(),slideCount:X.number().int().positive().optional(),coverImageUrl:X.string().optional()}),wW={name:xq,pluginId:"decks",schema:oW,renderers:{image:Sq}};function Sq(l){let r=oW.parse(l),f=r.slideCount?`${r.slideCount} slide${r.slideCount===1?"":"s"}`:void 0;return jq(ot,{brandLabel:r.brandLabel??r.title,eyebrow:"Deck",title:r.title,subtitle:r.description,meta:[r.event],tag:f},void 0,!1,void 0,this)}class Z4{metadata={outputEntityType:"image",targetField:"ogImageId"};context;deps;constructor(l,r={}){this.context=l,this.deps=r}async resolve(l){if(l.sourceEntityType!=="deck"||l.attachmentType!==vo)return;let r=await this.context.entityService.getEntity({entityType:"deck",id:l.sourceEntityId});if(!r)return;let{frontmatter:f,content:i}=X1(r.content),n=Y1.parse(f),t=Eq(i),u=this.resolveBrandLabel(),o=await this.resolveCoverImageUrl(n.coverImageId),w={title:n.title,...n.description?{description:n.description}:{},...n.event?{event:n.event}:{},...t?{slideCount:t}:{},...o?{coverImageUrl:o}:{},...u?{brandLabel:u}:{}};return{type:"image",data:await S6({mediaPath:`/_media/og/deck/${r.id}`,template:wW,content:w,title:w.title,themeMode:"dark",themeCSS:this.context.themeCSS,tmpPrefix:"brain-deck-og-image-",...this.deps.screenshotPng&&{screenshotPng:this.deps.screenshotPng}}),mimeType:"image/png",filename:`${sq(r)}-og.png`}}resolveBrandLabel(){let l=this.context.domain?.trim();if(l&&l.length>0)return l;let r=this.context.identity.getProfile().name.trim();return r.length>0?r:void 0}async resolveCoverImageUrl(l){if(!l)return;let r=await this.context.entityService.getEntity({entityType:"image",id:l});return r?.content.startsWith("data:image/")?r.content:void 0}}function Eq(l){return l.split(/^---$/gm).map((r)=>r.trim()).filter((r)=>r.length>0).length}function sq(l){let r=l.metadata.slug;return r.length>0?r:sl(l.metadata.title)}async function vW({entity:l,config:r}){let f=cn.parse(l),i=cr(f.content,Y1),n=i.metadata;return{$type:"ai.rizom.brain.deck",title:n.title,...n.slug&&{slug:n.slug},...n.description&&{description:n.description},body:i.content,format:"text/markdown",...n.author&&{author:n.author},...n.event&&{event:n.event},...n.publishedAt&&{publishedAt:n.publishedAt},...r.brainDid&&{brainDid:r.brainDid},...r.anchorDid&&{anchorDid:r.anchorDid},sourceEntityType:"deck",sourceEntityId:f.id,createdAt:f.created,...f.updated&&{updatedAt:f.updated}}}function X4(){return{entityType:"deck",collection:"ai.rizom.brain.deck",lexicon:dr["ai.rizom.brain.deck"],validate:!1,buildRecord:vW}}var mW={name:"@brains/decks",version:"0.2.0-alpha.219",description:"Presentation decks plugin for creating and viewing slide presentations",dependencies:{"@brains/atproto-contracts":"workspace:*","@brains/content-formatters":"workspace:*","@brains/contracts":"workspace:*","@brains/image":"workspace:*","@brains/media-page-composer":"workspace:*","@brains/media-renderer":"workspace:*","@brains/plugins":"workspace:*","@brains/site-info":"workspace:*","@brains/ui-library":"workspace:*","@brains/utils":"workspace:*"},devDependencies:{"@brains/app":"workspace:*","@brains/eslint-config":"workspace:*","@brains/typescript-config":"workspace:*","@types/bun":"^1.3.14","bun-types":"^1.3.14",eslint:"^10.5.0",typescript:"^7.0.2"},exports:{".":"./src/index.ts"},private:!0,scripts:{eval:"cd evals && bun run brain-eval",lint:"eslint . --max-warnings 0","lint:fix":"eslint . --max-warnings 0 --fix",test:"bun test",typecheck:"tsc --noEmit"},type:"module"};var eq=X.object({prompt:X.string(),event:X.string().optional()}),l_=X.object({title:X.string(),content:X.string()});class kW extends Bn{deps;entityType=nu.entityType;schema=nu.schema;adapter=nu;unregisterCarouselAttachmentProvider;unregisterOgImageAttachmentProvider;unregisterAtprotoProjection;constructor(l={}){super("decks",mW,{},No);this.deps=l}createGenerationHandler(l){return new K4(this.logger.child("DeckGenerationJobHandler"),l)}getTemplates(){return{"deck-detail":w4,"deck-list":b4,generation:xK,description:SK}}getDataSources(){return[new A4(this.logger)]}getEntityTypeConfig(){return{weight:1.5,projectionSourceRole:"primary"}}async onRegister(l){this.deferPublishRegistration(l),this.subscribeToPublishExecute(l),this.registerCarouselAttachmentProvider(l),this.registerOgImageAttachmentProvider(l),this.registerEvalHandlers(l),this.unregisterAtprotoProjection=rf.getInstance().register(X4()),this.logger.info("Decks plugin registered")}async onShutdown(){this.unregisterCarouselAttachmentProvider?.(),this.unregisterCarouselAttachmentProvider=void 0,this.unregisterOgImageAttachmentProvider?.(),this.unregisterOgImageAttachmentProvider=void 0,this.unregisterAtprotoProjection?.(),this.unregisterAtprotoProjection=void 0}deferPublishRegistration(l){l.messaging.subscribe(ei.pluginsRegistered,async()=>{return await l.messaging.send({type:"publish:register",payload:{entityType:"deck",provider:{name:"internal",publish:async()=>({id:"internal"})},config:{executionMode:"provider"}}}),{success:!0}})}subscribeToPublishExecute(l){l.messaging.subscribe("publish:execute",async(r)=>{let{entityType:f,entityId:i}=r.payload;if(f!=="deck")return{success:!0};try{let n=await l.entityService.getEntity({entityType:"deck",id:i});if(!n)return await l.messaging.send({type:"publish:report:failure",payload:{entityType:f,entityId:i,error:`Deck not found: ${i}`}}),{success:!0};if(n.metadata.status==="published")return{success:!0};let t=new Date().toISOString(),u={...n,metadata:{...n.metadata,status:"published",publishedAt:t}};await l.entityService.updateEntity({entity:{...u,content:this.adapter.toMarkdown(u)}}),await l.messaging.send({type:"publish:report:success",payload:{entityType:f,entityId:i,result:{id:i}}})}catch(n){await l.messaging.send({type:"publish:report:failure",payload:{entityType:f,entityId:i,error:D2(n)}})}return{success:!0}})}registerCarouselAttachmentProvider(l){let r={...this.deps,getThemeMode:this.deps.getThemeMode??(async()=>{try{return(await Rf(l.entityService)).themeMode??"dark"}catch{return"dark"}})};this.unregisterCarouselAttachmentProvider=l.attachments.register("deck",nW,new W4(l,r))}registerOgImageAttachmentProvider(l){this.unregisterOgImageAttachmentProvider=l.attachments.register("deck",vo,new Z4(l))}registerEvalHandlers(l){l.eval.registerHandler("generateDeck",async(r)=>{let f=eq.parse(r);return l.ai.generate({prompt:`${f.prompt}${f.event?`
1683
+ `}},void 0,!1,void 0,this),n.map((v,k)=>mr("section",{className:`deck-carousel-slide${k===0?" is-cover":""}`,children:[mr("header",{className:"deck-carousel-header",children:mr("span",{className:"deck-carousel-wordmark","aria-label":f??r,children:[mr("span",{className:"wm-primary",children:t.primary},void 0,!1,void 0,this),t.secondary!==void 0&&mr(Lq,{children:[mr("span",{className:"wm-dot",children:"."},void 0,!1,void 0,this),mr("span",{className:"wm-secondary",children:t.secondary},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),mr("div",{className:"deck-carousel-body",children:mr("div",{className:"deck-carousel-content",dangerouslySetInnerHTML:{__html:w(v.markdown)}},void 0,!1,void 0,this)},void 0,!1,void 0,this),mr("footer",{className:"deck-carousel-footer",children:[mr("span",{className:"deck-carousel-footer-meta",children:i??r},void 0,!1,void 0,this),mr("span",{className:"deck-carousel-counter","aria-label":`Slide ${k+1} of ${u}`,children:[mr("span",{className:"deck-carousel-counter-current",children:String(k+1).padStart(2,"0")},void 0,!1,void 0,this)," / ",mr("span",{children:o},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},k,!0,void 0,this))]},void 0,!0,void 0,this)}import{mkdtemp as Pq,rm as Mq}from"fs/promises";import{tmpdir as Vq}from"os";import{join as pq}from"path";var Iq=26214400,qq=60000,hW=20;class W4{metadata={outputEntityType:"document"};context;renderPdf;getThemeMode;constructor(l,r={}){this.context=l,this.renderPdf=r.renderPdf??dt,this.getThemeMode=r.getThemeMode??(async()=>"dark")}async resolve(l){if(l.sourceEntityType!=="deck")return;let r=await this.context.entityService.getEntity({entityType:"deck",id:l.sourceEntityId});if(!r)return;let f=_q(r,{brandLabel:this.resolveBrandLabel()});if(f.slides.length>hW)throw Error(`Refusing to render carousel with ${f.slides.length} slides; maxSlides=${hW}`);let i=await this.getThemeMode(),n=await Pq(pq(Vq(),"brain-deck-carousel-"));try{let t=await S1({outputDir:n,mediaPath:`/_media/carousel/${r.id}`,template:uW,format:"pdf",content:f,siteConfig:{title:f.title,themeMode:i},themeCSS:this.context.themeCSS}),u=await j1({rootDir:n});try{return{type:"document",data:await this.renderPdf(u.urlFor(t.urlPath),{maxBytes:Iq,timeoutMs:qq,printBackground:!0,preferCSSPageSize:!0}),mimeType:"application/pdf",filename:`${Dq(r)}-carousel.pdf`}}finally{await u.close()}}finally{await Mq(n,{recursive:!0,force:!0})}}resolveBrandLabel(){let l=this.context.domain?.trim();if(l&&l.length>0)return l;let r=this.context.identity.getProfile().name;return r.length>0?r:void 0}}function _q(l,r={}){let{frontmatter:f,content:i}=X1(l.content),n=typeof f.title==="string"?f.title:l.metadata.title,t=typeof f.event==="string"&&f.event.length>0?f.event:void 0,u=i.split(/^---$/gm).map((o)=>o.trim()).filter((o)=>o.length>0).map((o)=>({markdown:o}));return{title:n,slides:u,...r.brandLabel?{brandLabel:r.brandLabel}:{},...t?{eyebrow:t}:{}}}function Dq(l){let r=l.metadata.slug;return r.length>0?r:sl(l.metadata.title)}import{jsxDEV as jq}from"preact/jsx-dev-runtime";var vo="og-image",xq="decks:og-image",oW=X.object({title:X.string().min(1),description:X.string().optional(),event:X.string().optional(),brandLabel:X.string().optional(),slideCount:X.number().int().positive().optional(),coverImageUrl:X.string().optional()}),wW={name:xq,pluginId:"decks",schema:oW,renderers:{image:Sq}};function Sq(l){let r=oW.parse(l),f=r.slideCount?`${r.slideCount} slide${r.slideCount===1?"":"s"}`:void 0;return jq(ot,{brandLabel:r.brandLabel??r.title,eyebrow:"Deck",title:r.title,subtitle:r.description,meta:[r.event],tag:f},void 0,!1,void 0,this)}class Z4{metadata={outputEntityType:"image",targetField:"ogImageId"};context;deps;constructor(l,r={}){this.context=l,this.deps=r}async resolve(l){if(l.sourceEntityType!=="deck"||l.attachmentType!==vo)return;let r=await this.context.entityService.getEntity({entityType:"deck",id:l.sourceEntityId});if(!r)return;let{frontmatter:f,content:i}=X1(r.content),n=Y1.parse(f),t=Eq(i),u=this.resolveBrandLabel(),o=await this.resolveCoverImageUrl(n.coverImageId),w={title:n.title,...n.description?{description:n.description}:{},...n.event?{event:n.event}:{},...t?{slideCount:t}:{},...o?{coverImageUrl:o}:{},...u?{brandLabel:u}:{}};return{type:"image",data:await S6({mediaPath:`/_media/og/deck/${r.id}`,template:wW,content:w,title:w.title,themeMode:"dark",themeCSS:this.context.themeCSS,tmpPrefix:"brain-deck-og-image-",...this.deps.screenshotPng&&{screenshotPng:this.deps.screenshotPng}}),mimeType:"image/png",filename:`${sq(r)}-og.png`}}resolveBrandLabel(){let l=this.context.domain?.trim();if(l&&l.length>0)return l;let r=this.context.identity.getProfile().name.trim();return r.length>0?r:void 0}async resolveCoverImageUrl(l){if(!l)return;let r=await this.context.entityService.getEntity({entityType:"image",id:l});return r?.content.startsWith("data:image/")?r.content:void 0}}function Eq(l){return l.split(/^---$/gm).map((r)=>r.trim()).filter((r)=>r.length>0).length}function sq(l){let r=l.metadata.slug;return r.length>0?r:sl(l.metadata.title)}async function vW({entity:l,config:r}){let f=cn.parse(l),i=cr(f.content,Y1),n=i.metadata;return{$type:"ai.rizom.brain.deck",title:n.title,...n.slug&&{slug:n.slug},...n.description&&{description:n.description},body:i.content,format:"text/markdown",...n.author&&{author:n.author},...n.event&&{event:n.event},...n.publishedAt&&{publishedAt:n.publishedAt},...r.brainDid&&{brainDid:r.brainDid},...r.anchorDid&&{anchorDid:r.anchorDid},sourceEntityType:"deck",sourceEntityId:f.id,createdAt:f.created,...f.updated&&{updatedAt:f.updated}}}function X4(){return{entityType:"deck",collection:"ai.rizom.brain.deck",lexicon:dr["ai.rizom.brain.deck"],validate:!1,buildRecord:vW}}var mW={name:"@brains/decks",version:"0.2.0-alpha.220",description:"Presentation decks plugin for creating and viewing slide presentations",dependencies:{"@brains/atproto-contracts":"workspace:*","@brains/content-formatters":"workspace:*","@brains/contracts":"workspace:*","@brains/image":"workspace:*","@brains/media-page-composer":"workspace:*","@brains/media-renderer":"workspace:*","@brains/plugins":"workspace:*","@brains/site-info":"workspace:*","@brains/ui-library":"workspace:*","@brains/utils":"workspace:*"},devDependencies:{"@brains/app":"workspace:*","@brains/eslint-config":"workspace:*","@brains/typescript-config":"workspace:*","@types/bun":"^1.3.14","bun-types":"^1.3.14",eslint:"^10.5.0",typescript:"^7.0.2"},exports:{".":"./src/index.ts"},private:!0,scripts:{eval:"cd evals && bun run brain-eval",lint:"eslint . --max-warnings 0","lint:fix":"eslint . --max-warnings 0 --fix",test:"bun test",typecheck:"tsc --noEmit"},type:"module"};var eq=X.object({prompt:X.string(),event:X.string().optional()}),l_=X.object({title:X.string(),content:X.string()});class kW extends Bn{deps;entityType=nu.entityType;schema=nu.schema;adapter=nu;unregisterCarouselAttachmentProvider;unregisterOgImageAttachmentProvider;unregisterAtprotoProjection;constructor(l={}){super("decks",mW,{},No);this.deps=l}createGenerationHandler(l){return new K4(this.logger.child("DeckGenerationJobHandler"),l)}getTemplates(){return{"deck-detail":w4,"deck-list":b4,generation:xK,description:SK}}getDataSources(){return[new A4(this.logger)]}getEntityTypeConfig(){return{weight:1.5,projectionSourceRole:"primary"}}async onRegister(l){this.deferPublishRegistration(l),this.subscribeToPublishExecute(l),this.registerCarouselAttachmentProvider(l),this.registerOgImageAttachmentProvider(l),this.registerEvalHandlers(l),this.unregisterAtprotoProjection=rf.getInstance().register(X4()),this.logger.info("Decks plugin registered")}async onShutdown(){this.unregisterCarouselAttachmentProvider?.(),this.unregisterCarouselAttachmentProvider=void 0,this.unregisterOgImageAttachmentProvider?.(),this.unregisterOgImageAttachmentProvider=void 0,this.unregisterAtprotoProjection?.(),this.unregisterAtprotoProjection=void 0}deferPublishRegistration(l){l.messaging.subscribe(ei.pluginsRegistered,async()=>{return await l.messaging.send({type:"publish:register",payload:{entityType:"deck",provider:{name:"internal",publish:async()=>({id:"internal"})},config:{executionMode:"provider"}}}),{success:!0}})}subscribeToPublishExecute(l){l.messaging.subscribe("publish:execute",async(r)=>{let{entityType:f,entityId:i}=r.payload;if(f!=="deck")return{success:!0};try{let n=await l.entityService.getEntity({entityType:"deck",id:i});if(!n)return await l.messaging.send({type:"publish:report:failure",payload:{entityType:f,entityId:i,error:`Deck not found: ${i}`}}),{success:!0};if(n.metadata.status==="published")return{success:!0};let t=new Date().toISOString(),u={...n,metadata:{...n.metadata,status:"published",publishedAt:t}};await l.entityService.updateEntity({entity:{...u,content:this.adapter.toMarkdown(u)}}),await l.messaging.send({type:"publish:report:success",payload:{entityType:f,entityId:i,result:{id:i}}})}catch(n){await l.messaging.send({type:"publish:report:failure",payload:{entityType:f,entityId:i,error:D2(n)}})}return{success:!0}})}registerCarouselAttachmentProvider(l){let r={...this.deps,getThemeMode:this.deps.getThemeMode??(async()=>{try{return(await Rf(l.entityService)).themeMode??"dark"}catch{return"dark"}})};this.unregisterCarouselAttachmentProvider=l.attachments.register("deck",nW,new W4(l,r))}registerOgImageAttachmentProvider(l){this.unregisterOgImageAttachmentProvider=l.attachments.register("deck",vo,new Z4(l))}registerEvalHandlers(l){l.eval.registerHandler("generateDeck",async(r)=>{let f=eq.parse(r);return l.ai.generate({prompt:`${f.prompt}${f.event?`
1684
1684
 
1685
1685
  Note: This presentation is for "${f.event}".`:""}`,templateName:"decks:generation"})}),l.eval.registerHandler("generateDescription",async(r)=>{let f=l_.parse(r);return l.ai.generate({prompt:`Title: ${f.title}
1686
1686
 
1687
1687
  Content:
1688
- ${f.content}`,templateName:"decks:description"})})}}class mo{postsListUrl;decksListUrl;id="professional:homepage-list";name="Homepage List DataSource";description="Fetches profile, blog posts, and presentation decks for homepage";constructor(l,r){this.postsListUrl=l,this.decksListUrl=r}async fetch(l,r,f){let i=f.entityService,[n,t,u,o]=await Promise.all([Jf(i,fu),Ji(i,{entityType:"post",count:3,parse:Lf}),Ji(i,{entityType:"deck",count:3,parse:tu}),Rf(i)]),w={profile:n,posts:t,decks:u,postsListUrl:this.postsListUrl,decksListUrl:this.decksListUrl,cta:Hn(o.cta),sections:o.sections??{}};return r.parse(w)}}class ko{id="professional:about";name="About Page DataSource";description="Fetches full profile data for the about page";async fetch(l,r,f){let n={profile:await Jf(f.entityService,fu)};return r.parse(n)}}import{jsxDEV as Kl,Fragment as i_}from"preact/jsx-dev-runtime";var r_="grid md:grid-cols-[14rem_1px_1fr] gap-y-2 gap-x-0 md:gap-16 items-start",f_="border-t md:border-t-0 md:border-l border-rule-strong md:self-stretch",Y4=({number:l,title:r,blurb:f,children:i})=>Kl("section",{className:"py-20 border-b border-rule px-6 md:px-12",children:Kl("div",{className:"max-w-6xl mx-auto",children:Kl("div",{className:r_,children:[Kl(Dw,{title:r,number:l,blurb:f},void 0,!1,void 0,this),Kl("div",{className:f_,"aria-hidden":"true"},void 0,!1,void 0,this),Kl("div",{className:"mt-6 md:mt-0",children:i},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),z4=({profile:l,posts:r,decks:f,postsListUrl:i,decksListUrl:n,cta:t,sections:u})=>{let o=l.tagline?.length?l.tagline:l.description,w=r.map((d)=>({id:d.id,url:d.url,title:d.metadata.title,date:d.metadata.publishedAt??d.created,description:d.frontmatter.excerpt,series:d.frontmatter.seriesName&&d.frontmatter.seriesIndex?{name:d.frontmatter.seriesName,index:d.frontmatter.seriesIndex}:void 0})),v=f.map((d)=>({id:d.id,url:d.url,title:d.frontmatter.title||d.id,date:d.frontmatter.publishedAt??d.created,description:d.frontmatter.description})),k=l.name||"Home",$=[l.intro,l.description,o].find((d)=>d)??"Professional site",b=Boolean(l.description)||l.expertise!==void 0&&l.expertise.length>0;return Kl(i_,{children:[Kl(Ll,{title:k,description:$,ogType:"website"},void 0,!1,void 0,this),Kl("div",{className:"homepage-list bg-theme",children:[Kl("header",{className:"hero-bg-pattern relative w-full px-6 md:px-12 pt-28 pb-24 md:pt-28 md:pb-24 overflow-hidden border-b border-rule",children:Kl("div",{className:"relative z-10 max-w-6xl mx-auto w-full",children:[l.name&&Kl("div",{className:"flex items-center gap-[0.6rem] mb-6 font-mono text-[0.7rem] font-medium uppercase tracking-[0.22em] text-accent",children:[Kl("span",{className:"w-[18px] h-px bg-accent","aria-hidden":"true"},void 0,!1,void 0,this),Kl("span",{children:l.name},void 0,!1,void 0,this)]},void 0,!0,void 0,this),o&&Kl("h1",{className:"font-heading text-[clamp(2.75rem,6.5vw,5.5rem)] font-normal text-heading leading-[1.02] tracking-[-0.025em] max-w-[18ch] [font-variation-settings:'opsz'_144,'SOFT'_30]",children:_2(o,"italic font-normal text-accent [font-variation-settings:'opsz'_144,'SOFT'_80]")},void 0,!1,void 0,this),l.intro&&Kl("p",{className:"font-heading font-light text-[clamp(1.1rem,1.8vw,1.4rem)] leading-[1.5] text-theme-muted max-w-[42ch] mt-8 [font-variation-settings:'opsz'_24]",children:_2(l.intro,"italic text-accent")},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Kl(Y4,{number:"01",title:"Essays",blurb:u.essays?.blurb,children:Kl(q2,{items:w,viewAllUrl:i,viewAllLabel:"View all essays"},void 0,!1,void 0,this)},void 0,!1,void 0,this),v.length>0&&Kl(Y4,{number:"02",title:"Presentations",blurb:u.presentations?.blurb,children:Kl(q2,{items:v,viewAllUrl:n,viewAllLabel:"View all presentations"},void 0,!1,void 0,this)},void 0,!1,void 0,this),b&&Kl(Y4,{number:"03",title:"About",blurb:u.about?.blurb,children:Kl("div",{className:"flex flex-col gap-8",children:[l.description&&Kl("p",{className:"font-heading font-light text-[1.25rem] leading-[1.55] text-theme max-w-[55ch] [font-variation-settings:'opsz'_24,'SOFT'_50]",children:l.description},void 0,!1,void 0,this),l.expertise&&l.expertise.length>0&&Kl(xw,{subjects:l.expertise},void 0,!1,void 0,this),Kl("a",{href:"/about",className:"mt-6 inline-flex items-center gap-2 font-mono text-[0.7rem] font-medium uppercase tracking-[0.18em] text-accent pb-1 relative before:content-[''] before:absolute before:left-0 before:right-full before:bottom-0 before:h-px before:bg-accent before:transition-[right] before:duration-300 hover:before:right-0",children:["Learn more",Kl("span",{"aria-hidden":"true",children:"\u2192"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Kl(Tw,{cta:t,variant:"editorial",socialLinks:l.socialLinks},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)};import{jsxDEV as kl,Fragment as n_}from"preact/jsx-dev-runtime";var O4=({profile:l})=>{let r=`About ${l.name||"Me"}`,f=[l.description,l.intro].find((n)=>n)??"About page",i=l.expertise!==void 0&&l.expertise.length>0||Boolean(l.currentFocus)||Boolean(l.availability)||Boolean(l.email)||Boolean(l.website)||l.socialLinks!==void 0&&l.socialLinks.length>0;return kl(n_,{children:[kl(Ll,{title:r,description:f,ogType:"profile"},void 0,!1,void 0,this),kl("div",{className:"about-page bg-theme",children:[kl("header",{className:"hero-bg-pattern relative w-full py-16 md:py-24 px-6 md:px-12 bg-theme overflow-hidden",children:kl("div",{className:"relative z-10 max-w-4xl mx-auto",children:[kl("h1",{className:"text-5xl md:text-6xl font-semibold mb-6 text-heading",children:["About ",l.name||"Me"]},void 0,!0,void 0,this),l.description&&kl("p",{className:"text-xl md:text-2xl text-theme-muted leading-relaxed",children:l.description},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),kl("div",{className:"container mx-auto px-6 md:px-12 max-w-4xl py-12 md:py-16",children:[l.story&&kl("section",{className:"content-section-reveal mb-20 md:mb-28",children:kl(P1,{markdown:l.story},void 0,!1,void 0,this)},void 0,!1,void 0,this),i&&kl("div",{className:"content-section-reveal grid md:grid-cols-2 gap-x-16 gap-y-12",children:[l.expertise&&l.expertise.length>0&&kl("section",{children:[kl("h2",{className:"text-sm tracking-widest uppercase text-theme-muted mb-6",children:"Expertise"},void 0,!1,void 0,this),kl("ul",{className:"flex flex-wrap gap-3",children:l.expertise.map((n,t)=>kl("li",{className:pw({variant:"accent",size:"lg"}),children:n},t,!1,void 0,this))},void 0,!1,void 0,this)]},void 0,!0,void 0,this),l.currentFocus&&kl("section",{children:[kl("h2",{className:"text-sm tracking-widest uppercase text-theme-muted mb-6",children:"Current Focus"},void 0,!1,void 0,this),kl("p",{className:"text-lg text-theme leading-relaxed",children:l.currentFocus},void 0,!1,void 0,this)]},void 0,!0,void 0,this),l.availability&&kl("section",{children:[kl("h2",{className:"text-sm tracking-widest uppercase text-theme-muted mb-6",children:"Availability"},void 0,!1,void 0,this),kl("p",{className:"text-lg text-theme leading-relaxed",children:l.availability},void 0,!1,void 0,this)]},void 0,!0,void 0,this),(Boolean(l.email)||Boolean(l.website)||l.socialLinks!==void 0&&l.socialLinks.length>0)&&kl("section",{children:[kl("h2",{className:"text-sm tracking-widest uppercase text-theme-muted mb-6",children:"Contact"},void 0,!1,void 0,this),kl("div",{className:"space-y-4",children:[l.email&&kl("p",{className:"text-lg",children:kl("a",{href:`mailto:${l.email}`,className:"text-brand hover:text-brand-dark transition-colors",children:l.email},void 0,!1,void 0,this)},void 0,!1,void 0,this),l.website&&kl("p",{className:"text-lg",children:kl("a",{href:l.website,target:"_blank",rel:"noopener noreferrer",className:"text-brand hover:text-brand-dark transition-colors",children:l.website},void 0,!1,void 0,this)},void 0,!1,void 0,this),l.socialLinks&&l.socialLinks.length>0&&kl("div",{className:"flex flex-wrap gap-4 mt-4",children:l.socialLinks.map((n,t)=>kl(Vr,{href:n.url,external:!0,variant:"secondary",size:"md",children:n.label?.length?n.label:n.platform},t,!1,void 0,this))},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)};import{jsxDEV as kr,Fragment as $W}from"preact/jsx-dev-runtime";var J4=()=>{return kr($W,{children:[kr(Ll,{title:"Thanks for subscribing!",description:"You've successfully subscribed to the newsletter."},void 0,!1,void 0,this),kr("div",{className:"min-h-[60vh] flex items-center justify-center px-6",children:kr("div",{className:"text-center max-w-md",children:[kr("div",{className:"text-6xl mb-6",children:"\uD83C\uDF89"},void 0,!1,void 0,this),kr("h1",{className:"text-3xl font-semibold mb-4 text-heading",children:"Thanks for subscribing!"},void 0,!1,void 0,this),kr("p",{className:"text-lg text-theme-muted mb-8",children:"You'll receive a confirmation email shortly. Check your inbox to confirm your subscription."},void 0,!1,void 0,this),kr(Vr,{href:"/",variant:"primary",children:"Back to Home"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},a4=()=>{return kr($W,{children:[kr(Ll,{title:"Subscription failed",description:"There was a problem with your subscription."},void 0,!1,void 0,this),kr("div",{className:"min-h-[60vh] flex items-center justify-center px-6",children:kr("div",{className:"text-center max-w-md",children:[kr("div",{className:"text-6xl mb-6",children:"\uD83D\uDE22"},void 0,!1,void 0,this),kr("h1",{className:"text-3xl font-semibold mb-4 text-heading",children:"Something went wrong"},void 0,!1,void 0,this),kr("p",{className:"text-lg text-theme-muted mb-8",children:"We couldn't process your subscription. Please try again or contact us if the problem persists."},void 0,!1,void 0,this),kr(Vr,{href:"/",variant:"primary",children:"Back to Home"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)};var bW=X.object({label:X.string().describe("Display label for entity type (e.g., 'Essay')"),pluralName:X.string().optional().describe("URL path segment (defaults to label.toLowerCase() + 's')")}),t_={post:{label:"Post"},deck:{label:"Deck"}},yW=X.object({entityDisplay:X.object({post:bW,deck:bW}).default(t_).describe("Display metadata for post and deck entity types used by the homepage")});var dW={name:"@brains/site-professional",version:"0.2.0-alpha.219",description:"Professional site composition package",dependencies:{"@brains/blog":"workspace:*","@brains/decks":"workspace:*","@brains/plugins":"workspace:*","@brains/site-composition":"workspace:*","@brains/site-engine":"workspace:*","@brains/site-info":"workspace:*","@brains/templates":"workspace:*","@brains/ui-library":"workspace:*","@brains/utils":"workspace:*",preact:"^10.27.2"},devDependencies:{"@brains/eslint-config":"workspace:*","@brains/typescript-config":"workspace:*","@types/bun":"^1.3.14","bun-types":"^1.3.14",eslint:"^10.5.0",typescript:"^7.0.2"},exports:{".":"./src/index.ts"},files:["src"],private:!0,scripts:{lint:"eslint . --max-warnings 0","lint:fix":"eslint . --max-warnings 0 --fix",typecheck:"tsc --noEmit"},type:"module"};var h_=X.object({blurb:X.string().optional().describe("Short italic subtitle under the section title")}),o_=X.object({heading:X.string(),buttonText:X.string(),buttonLink:X.string()}),gW=X.looseObject({name:X.string(),kind:X.enum(["professional","team","collective"]),organization:X.string().optional(),description:X.string().optional(),avatar:X.string().optional(),website:X.string().optional(),email:X.string().optional(),socialLinks:X.array(X.object({platform:X.enum(["github","instagram","linkedin","email","website"]),url:X.string(),label:X.string().optional()})).optional(),tagline:X.string().optional(),intro:X.string().optional(),story:X.string().optional(),role:X.string().optional(),audience:X.string().optional(),expertise:X.array(X.string()).optional(),currentFocus:X.string().optional(),availability:X.string().optional(),desiredTone:X.string().optional()}),w_=X.looseObject({id:X.string(),entityType:X.literal("post"),content:X.string(),created:X.string(),updated:X.string(),contentHash:X.string(),metadata:X.looseObject({title:X.string(),publishedAt:X.string().optional()}),frontmatter:X.looseObject({excerpt:X.string(),seriesName:X.string().optional(),seriesIndex:X.number().optional()}),body:X.string(),url:X.string().optional(),typeLabel:X.string().optional(),listUrl:X.string().optional(),listLabel:X.string().optional(),seriesUrl:X.string().optional(),coverImageUrl:X.string().optional(),ogImageUrl:X.string().optional(),coverImageWidth:X.number().optional(),coverImageHeight:X.number().optional(),coverImageSrcset:X.string().optional(),coverImageSizes:X.string().optional()}),v_=X.looseObject({id:X.string(),entityType:X.literal("deck"),content:X.string(),created:X.string(),updated:X.string(),contentHash:X.string(),metadata:X.looseObject({title:X.string(),publishedAt:X.string().optional()}),frontmatter:X.looseObject({title:X.string(),description:X.string().optional(),publishedAt:X.string().optional()}),body:X.string(),url:X.string().optional(),typeLabel:X.string().optional(),listUrl:X.string().optional(),listLabel:X.string().optional(),coverImageUrl:X.string().optional(),ogImageUrl:X.string().optional(),coverImageWidth:X.number().optional(),coverImageHeight:X.number().optional()});class Q4 extends to{dependencies=["blog","decks"];constructor(l){super("professional-site",dW,l,yW)}async onRegister(l){let r=this.config.entityDisplay.post,f=this.config.entityDisplay.deck,i=`/${r.pluralName??r.label.toLowerCase()+"s"}`,n=`/${f.pluralName??f.label.toLowerCase()+"s"}`,t=new mo(i,n);l.entities.registerDataSource(t);let u=new ko;l.entities.registerDataSource(u);let o=X.object({profile:gW,posts:X.array(w_),decks:X.array(v_),postsListUrl:X.string(),decksListUrl:X.string(),cta:o_,sections:X.record(X.string(),h_)}),w=X.object({profile:gW}),v=X.object({});l.templates.register({"homepage-list":Ol({name:"homepage-list",description:"Professional homepage with essays and presentations",schema:o,dataSourceId:"professional:homepage-list",requiredPermission:"public",layout:{component:z4}}),about:Ol({name:"about",description:"About page with full profile information",schema:w,dataSourceId:"professional:about",requiredPermission:"public",layout:{component:O4}}),"subscribe-thanks":Ol({name:"subscribe-thanks",description:"Newsletter subscription success page",schema:v,requiredPermission:"public",layout:{component:J4}}),"subscribe-error":Ol({name:"subscribe-error",description:"Newsletter subscription error page",schema:v,requiredPermission:"public",layout:{component:a4}})}),this.logger.info("Professional site plugin registered successfully")}async getTools(){return[]}async getResources(){return[]}}function B4(l){return new Q4(l??{})}var H4=[{id:"home",path:"/",title:"Home",description:"Professional site homepage",layout:"default",navigation:{show:!0,label:"Home",slot:"secondary",priority:10},sections:[{id:"homepage",template:"professional-site:homepage-list",dataQuery:{}}]},{id:"about",path:"/about",title:"About",description:"About page",layout:"default",navigation:{show:!0,label:"About",slot:"primary",priority:90},sections:[{id:"about",template:"professional-site:about",dataQuery:{}}]},{id:"subscribe-thanks",path:"/subscribe/thanks",title:"Thanks for subscribing",description:"Newsletter subscription confirmation",layout:"default",navigation:{show:!1},sections:[{id:"subscribe-thanks",template:"professional-site:subscribe-thanks",dataQuery:{},content:{}}]},{id:"subscribe-error",path:"/subscribe/error",title:"Subscription failed",description:"Newsletter subscription error",layout:"default",navigation:{show:!1},sections:[{id:"subscribe-error",template:"professional-site:subscribe-error",dataQuery:{},content:{}}]}];import{jsxDEV as $o}from"preact/jsx-dev-runtime";function G4({sections:l,siteInfo:r,slots:f,wordmark:i}){return $o("div",{className:"flex flex-col min-h-screen bg-theme overflow-x-clip",children:[$o(ht,{title:r.title,navigation:r.navigation.primary,showThemeToggle:!0,...r.logo!==void 0?{logo:r.logo}:{},...i!==void 0?{wordmark:i}:{}},void 0,!1,void 0,this),$o("main",{className:"flex-grow flex flex-col bg-theme",children:l},void 0,!1,void 0,this),$o(Pw,{primaryNavigation:r.navigation.primary,secondaryNavigation:r.navigation.secondary,copyright:r.copyright,socialLinks:r.socialLinks,title:r.title,tagline:r.description,children:f?.getSlot("footer-top").map((n)=>n.render())},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function cW(l){return l}var t50=i4;function u50(l){return cW(r4(l))}var m_=f4,h50=m_,o50=G4;function w50(l){return cW(B4(l))}var v50=H4;export{m_ as routes,w50 as professionalSitePlugin,v50 as professionalRoutes,u50 as personalSitePlugin,h50 as personalRoutes,o50 as ProfessionalLayout,t50 as PersonalLayout};
1688
+ ${f.content}`,templateName:"decks:description"})})}}class mo{postsListUrl;decksListUrl;id="professional:homepage-list";name="Homepage List DataSource";description="Fetches profile, blog posts, and presentation decks for homepage";constructor(l,r){this.postsListUrl=l,this.decksListUrl=r}async fetch(l,r,f){let i=f.entityService,[n,t,u,o]=await Promise.all([Jf(i,fu),Ji(i,{entityType:"post",count:3,parse:Lf}),Ji(i,{entityType:"deck",count:3,parse:tu}),Rf(i)]),w={profile:n,posts:t,decks:u,postsListUrl:this.postsListUrl,decksListUrl:this.decksListUrl,cta:Hn(o.cta),sections:o.sections??{}};return r.parse(w)}}class ko{id="professional:about";name="About Page DataSource";description="Fetches full profile data for the about page";async fetch(l,r,f){let n={profile:await Jf(f.entityService,fu)};return r.parse(n)}}import{jsxDEV as Kl,Fragment as i_}from"preact/jsx-dev-runtime";var r_="grid md:grid-cols-[14rem_1px_1fr] gap-y-2 gap-x-0 md:gap-16 items-start",f_="border-t md:border-t-0 md:border-l border-rule-strong md:self-stretch",Y4=({number:l,title:r,blurb:f,children:i})=>Kl("section",{className:"py-20 border-b border-rule px-6 md:px-12",children:Kl("div",{className:"max-w-6xl mx-auto",children:Kl("div",{className:r_,children:[Kl(Dw,{title:r,number:l,blurb:f},void 0,!1,void 0,this),Kl("div",{className:f_,"aria-hidden":"true"},void 0,!1,void 0,this),Kl("div",{className:"mt-6 md:mt-0",children:i},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),z4=({profile:l,posts:r,decks:f,postsListUrl:i,decksListUrl:n,cta:t,sections:u})=>{let o=l.tagline?.length?l.tagline:l.description,w=r.map((d)=>({id:d.id,url:d.url,title:d.metadata.title,date:d.metadata.publishedAt??d.created,description:d.frontmatter.excerpt,series:d.frontmatter.seriesName&&d.frontmatter.seriesIndex?{name:d.frontmatter.seriesName,index:d.frontmatter.seriesIndex}:void 0})),v=f.map((d)=>({id:d.id,url:d.url,title:d.frontmatter.title||d.id,date:d.frontmatter.publishedAt??d.created,description:d.frontmatter.description})),k=l.name||"Home",$=[l.intro,l.description,o].find((d)=>d)??"Professional site",b=Boolean(l.description)||l.expertise!==void 0&&l.expertise.length>0;return Kl(i_,{children:[Kl(Ll,{title:k,description:$,ogType:"website"},void 0,!1,void 0,this),Kl("div",{className:"homepage-list bg-theme",children:[Kl("header",{className:"hero-bg-pattern relative w-full px-6 md:px-12 pt-28 pb-24 md:pt-28 md:pb-24 overflow-hidden border-b border-rule",children:Kl("div",{className:"relative z-10 max-w-6xl mx-auto w-full",children:[l.name&&Kl("div",{className:"flex items-center gap-[0.6rem] mb-6 font-mono text-[0.7rem] font-medium uppercase tracking-[0.22em] text-accent",children:[Kl("span",{className:"w-[18px] h-px bg-accent","aria-hidden":"true"},void 0,!1,void 0,this),Kl("span",{children:l.name},void 0,!1,void 0,this)]},void 0,!0,void 0,this),o&&Kl("h1",{className:"font-heading text-[clamp(2.75rem,6.5vw,5.5rem)] font-normal text-heading leading-[1.02] tracking-[-0.025em] max-w-[18ch] [font-variation-settings:'opsz'_144,'SOFT'_30]",children:_2(o,"italic font-normal text-accent [font-variation-settings:'opsz'_144,'SOFT'_80]")},void 0,!1,void 0,this),l.intro&&Kl("p",{className:"font-heading font-light text-[clamp(1.1rem,1.8vw,1.4rem)] leading-[1.5] text-theme-muted max-w-[42ch] mt-8 [font-variation-settings:'opsz'_24]",children:_2(l.intro,"italic text-accent")},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Kl(Y4,{number:"01",title:"Essays",blurb:u.essays?.blurb,children:Kl(q2,{items:w,viewAllUrl:i,viewAllLabel:"View all essays"},void 0,!1,void 0,this)},void 0,!1,void 0,this),v.length>0&&Kl(Y4,{number:"02",title:"Presentations",blurb:u.presentations?.blurb,children:Kl(q2,{items:v,viewAllUrl:n,viewAllLabel:"View all presentations"},void 0,!1,void 0,this)},void 0,!1,void 0,this),b&&Kl(Y4,{number:"03",title:"About",blurb:u.about?.blurb,children:Kl("div",{className:"flex flex-col gap-8",children:[l.description&&Kl("p",{className:"font-heading font-light text-[1.25rem] leading-[1.55] text-theme max-w-[55ch] [font-variation-settings:'opsz'_24,'SOFT'_50]",children:l.description},void 0,!1,void 0,this),l.expertise&&l.expertise.length>0&&Kl(xw,{subjects:l.expertise},void 0,!1,void 0,this),Kl("a",{href:"/about",className:"mt-6 inline-flex items-center gap-2 font-mono text-[0.7rem] font-medium uppercase tracking-[0.18em] text-accent pb-1 relative before:content-[''] before:absolute before:left-0 before:right-full before:bottom-0 before:h-px before:bg-accent before:transition-[right] before:duration-300 hover:before:right-0",children:["Learn more",Kl("span",{"aria-hidden":"true",children:"\u2192"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Kl(Tw,{cta:t,variant:"editorial",socialLinks:l.socialLinks},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)};import{jsxDEV as kl,Fragment as n_}from"preact/jsx-dev-runtime";var O4=({profile:l})=>{let r=`About ${l.name||"Me"}`,f=[l.description,l.intro].find((n)=>n)??"About page",i=l.expertise!==void 0&&l.expertise.length>0||Boolean(l.currentFocus)||Boolean(l.availability)||Boolean(l.email)||Boolean(l.website)||l.socialLinks!==void 0&&l.socialLinks.length>0;return kl(n_,{children:[kl(Ll,{title:r,description:f,ogType:"profile"},void 0,!1,void 0,this),kl("div",{className:"about-page bg-theme",children:[kl("header",{className:"hero-bg-pattern relative w-full py-16 md:py-24 px-6 md:px-12 bg-theme overflow-hidden",children:kl("div",{className:"relative z-10 max-w-4xl mx-auto",children:[kl("h1",{className:"text-5xl md:text-6xl font-semibold mb-6 text-heading",children:["About ",l.name||"Me"]},void 0,!0,void 0,this),l.description&&kl("p",{className:"text-xl md:text-2xl text-theme-muted leading-relaxed",children:l.description},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),kl("div",{className:"container mx-auto px-6 md:px-12 max-w-4xl py-12 md:py-16",children:[l.story&&kl("section",{className:"content-section-reveal mb-20 md:mb-28",children:kl(P1,{markdown:l.story},void 0,!1,void 0,this)},void 0,!1,void 0,this),i&&kl("div",{className:"content-section-reveal grid md:grid-cols-2 gap-x-16 gap-y-12",children:[l.expertise&&l.expertise.length>0&&kl("section",{children:[kl("h2",{className:"text-sm tracking-widest uppercase text-theme-muted mb-6",children:"Expertise"},void 0,!1,void 0,this),kl("ul",{className:"flex flex-wrap gap-3",children:l.expertise.map((n,t)=>kl("li",{className:pw({variant:"accent",size:"lg"}),children:n},t,!1,void 0,this))},void 0,!1,void 0,this)]},void 0,!0,void 0,this),l.currentFocus&&kl("section",{children:[kl("h2",{className:"text-sm tracking-widest uppercase text-theme-muted mb-6",children:"Current Focus"},void 0,!1,void 0,this),kl("p",{className:"text-lg text-theme leading-relaxed",children:l.currentFocus},void 0,!1,void 0,this)]},void 0,!0,void 0,this),l.availability&&kl("section",{children:[kl("h2",{className:"text-sm tracking-widest uppercase text-theme-muted mb-6",children:"Availability"},void 0,!1,void 0,this),kl("p",{className:"text-lg text-theme leading-relaxed",children:l.availability},void 0,!1,void 0,this)]},void 0,!0,void 0,this),(Boolean(l.email)||Boolean(l.website)||l.socialLinks!==void 0&&l.socialLinks.length>0)&&kl("section",{children:[kl("h2",{className:"text-sm tracking-widest uppercase text-theme-muted mb-6",children:"Contact"},void 0,!1,void 0,this),kl("div",{className:"space-y-4",children:[l.email&&kl("p",{className:"text-lg",children:kl("a",{href:`mailto:${l.email}`,className:"text-brand hover:text-brand-dark transition-colors",children:l.email},void 0,!1,void 0,this)},void 0,!1,void 0,this),l.website&&kl("p",{className:"text-lg",children:kl("a",{href:l.website,target:"_blank",rel:"noopener noreferrer",className:"text-brand hover:text-brand-dark transition-colors",children:l.website},void 0,!1,void 0,this)},void 0,!1,void 0,this),l.socialLinks&&l.socialLinks.length>0&&kl("div",{className:"flex flex-wrap gap-4 mt-4",children:l.socialLinks.map((n,t)=>kl(Vr,{href:n.url,external:!0,variant:"secondary",size:"md",children:n.label?.length?n.label:n.platform},t,!1,void 0,this))},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)};import{jsxDEV as kr,Fragment as $W}from"preact/jsx-dev-runtime";var J4=()=>{return kr($W,{children:[kr(Ll,{title:"Thanks for subscribing!",description:"You've successfully subscribed to the newsletter."},void 0,!1,void 0,this),kr("div",{className:"min-h-[60vh] flex items-center justify-center px-6",children:kr("div",{className:"text-center max-w-md",children:[kr("div",{className:"text-6xl mb-6",children:"\uD83C\uDF89"},void 0,!1,void 0,this),kr("h1",{className:"text-3xl font-semibold mb-4 text-heading",children:"Thanks for subscribing!"},void 0,!1,void 0,this),kr("p",{className:"text-lg text-theme-muted mb-8",children:"You'll receive a confirmation email shortly. Check your inbox to confirm your subscription."},void 0,!1,void 0,this),kr(Vr,{href:"/",variant:"primary",children:"Back to Home"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},a4=()=>{return kr($W,{children:[kr(Ll,{title:"Subscription failed",description:"There was a problem with your subscription."},void 0,!1,void 0,this),kr("div",{className:"min-h-[60vh] flex items-center justify-center px-6",children:kr("div",{className:"text-center max-w-md",children:[kr("div",{className:"text-6xl mb-6",children:"\uD83D\uDE22"},void 0,!1,void 0,this),kr("h1",{className:"text-3xl font-semibold mb-4 text-heading",children:"Something went wrong"},void 0,!1,void 0,this),kr("p",{className:"text-lg text-theme-muted mb-8",children:"We couldn't process your subscription. Please try again or contact us if the problem persists."},void 0,!1,void 0,this),kr(Vr,{href:"/",variant:"primary",children:"Back to Home"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)};var bW=X.object({label:X.string().describe("Display label for entity type (e.g., 'Essay')"),pluralName:X.string().optional().describe("URL path segment (defaults to label.toLowerCase() + 's')")}),t_={post:{label:"Post"},deck:{label:"Deck"}},yW=X.object({entityDisplay:X.object({post:bW,deck:bW}).default(t_).describe("Display metadata for post and deck entity types used by the homepage")});var dW={name:"@brains/site-professional",version:"0.2.0-alpha.220",description:"Professional site composition package",dependencies:{"@brains/blog":"workspace:*","@brains/decks":"workspace:*","@brains/plugins":"workspace:*","@brains/site-composition":"workspace:*","@brains/site-engine":"workspace:*","@brains/site-info":"workspace:*","@brains/templates":"workspace:*","@brains/ui-library":"workspace:*","@brains/utils":"workspace:*",preact:"^10.27.2"},devDependencies:{"@brains/eslint-config":"workspace:*","@brains/typescript-config":"workspace:*","@types/bun":"^1.3.14","bun-types":"^1.3.14",eslint:"^10.5.0",typescript:"^7.0.2"},exports:{".":"./src/index.ts"},files:["src"],private:!0,scripts:{lint:"eslint . --max-warnings 0","lint:fix":"eslint . --max-warnings 0 --fix",typecheck:"tsc --noEmit"},type:"module"};var h_=X.object({blurb:X.string().optional().describe("Short italic subtitle under the section title")}),o_=X.object({heading:X.string(),buttonText:X.string(),buttonLink:X.string()}),gW=X.looseObject({name:X.string(),kind:X.enum(["professional","team","collective"]),organization:X.string().optional(),description:X.string().optional(),avatar:X.string().optional(),website:X.string().optional(),email:X.string().optional(),socialLinks:X.array(X.object({platform:X.enum(["github","instagram","linkedin","email","website"]),url:X.string(),label:X.string().optional()})).optional(),tagline:X.string().optional(),intro:X.string().optional(),story:X.string().optional(),role:X.string().optional(),audience:X.string().optional(),expertise:X.array(X.string()).optional(),currentFocus:X.string().optional(),availability:X.string().optional(),desiredTone:X.string().optional()}),w_=X.looseObject({id:X.string(),entityType:X.literal("post"),content:X.string(),created:X.string(),updated:X.string(),contentHash:X.string(),metadata:X.looseObject({title:X.string(),publishedAt:X.string().optional()}),frontmatter:X.looseObject({excerpt:X.string(),seriesName:X.string().optional(),seriesIndex:X.number().optional()}),body:X.string(),url:X.string().optional(),typeLabel:X.string().optional(),listUrl:X.string().optional(),listLabel:X.string().optional(),seriesUrl:X.string().optional(),coverImageUrl:X.string().optional(),ogImageUrl:X.string().optional(),coverImageWidth:X.number().optional(),coverImageHeight:X.number().optional(),coverImageSrcset:X.string().optional(),coverImageSizes:X.string().optional()}),v_=X.looseObject({id:X.string(),entityType:X.literal("deck"),content:X.string(),created:X.string(),updated:X.string(),contentHash:X.string(),metadata:X.looseObject({title:X.string(),publishedAt:X.string().optional()}),frontmatter:X.looseObject({title:X.string(),description:X.string().optional(),publishedAt:X.string().optional()}),body:X.string(),url:X.string().optional(),typeLabel:X.string().optional(),listUrl:X.string().optional(),listLabel:X.string().optional(),coverImageUrl:X.string().optional(),ogImageUrl:X.string().optional(),coverImageWidth:X.number().optional(),coverImageHeight:X.number().optional()});class Q4 extends to{dependencies=["blog","decks"];constructor(l){super("professional-site",dW,l,yW)}async onRegister(l){let r=this.config.entityDisplay.post,f=this.config.entityDisplay.deck,i=`/${r.pluralName??r.label.toLowerCase()+"s"}`,n=`/${f.pluralName??f.label.toLowerCase()+"s"}`,t=new mo(i,n);l.entities.registerDataSource(t);let u=new ko;l.entities.registerDataSource(u);let o=X.object({profile:gW,posts:X.array(w_),decks:X.array(v_),postsListUrl:X.string(),decksListUrl:X.string(),cta:o_,sections:X.record(X.string(),h_)}),w=X.object({profile:gW}),v=X.object({});l.templates.register({"homepage-list":Ol({name:"homepage-list",description:"Professional homepage with essays and presentations",schema:o,dataSourceId:"professional:homepage-list",requiredPermission:"public",layout:{component:z4}}),about:Ol({name:"about",description:"About page with full profile information",schema:w,dataSourceId:"professional:about",requiredPermission:"public",layout:{component:O4}}),"subscribe-thanks":Ol({name:"subscribe-thanks",description:"Newsletter subscription success page",schema:v,requiredPermission:"public",layout:{component:J4}}),"subscribe-error":Ol({name:"subscribe-error",description:"Newsletter subscription error page",schema:v,requiredPermission:"public",layout:{component:a4}})}),this.logger.info("Professional site plugin registered successfully")}async getTools(){return[]}async getResources(){return[]}}function B4(l){return new Q4(l??{})}var H4=[{id:"home",path:"/",title:"Home",description:"Professional site homepage",layout:"default",navigation:{show:!0,label:"Home",slot:"secondary",priority:10},sections:[{id:"homepage",template:"professional-site:homepage-list",dataQuery:{}}]},{id:"about",path:"/about",title:"About",description:"About page",layout:"default",navigation:{show:!0,label:"About",slot:"primary",priority:90},sections:[{id:"about",template:"professional-site:about",dataQuery:{}}]},{id:"subscribe-thanks",path:"/subscribe/thanks",title:"Thanks for subscribing",description:"Newsletter subscription confirmation",layout:"default",navigation:{show:!1},sections:[{id:"subscribe-thanks",template:"professional-site:subscribe-thanks",dataQuery:{},content:{}}]},{id:"subscribe-error",path:"/subscribe/error",title:"Subscription failed",description:"Newsletter subscription error",layout:"default",navigation:{show:!1},sections:[{id:"subscribe-error",template:"professional-site:subscribe-error",dataQuery:{},content:{}}]}];import{jsxDEV as $o}from"preact/jsx-dev-runtime";function G4({sections:l,siteInfo:r,slots:f,wordmark:i}){return $o("div",{className:"flex flex-col min-h-screen bg-theme overflow-x-clip",children:[$o(ht,{title:r.title,navigation:r.navigation.primary,showThemeToggle:!0,...r.logo!==void 0?{logo:r.logo}:{},...i!==void 0?{wordmark:i}:{}},void 0,!1,void 0,this),$o("main",{className:"flex-grow flex flex-col bg-theme",children:l},void 0,!1,void 0,this),$o(Pw,{primaryNavigation:r.navigation.primary,secondaryNavigation:r.navigation.secondary,copyright:r.copyright,socialLinks:r.socialLinks,title:r.title,tagline:r.description,children:f?.getSlot("footer-top").map((n)=>n.render())},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function cW(l){return l}var t50=i4;function u50(l){return cW(r4(l))}var m_=f4,h50=m_,o50=G4;function w50(l){return cW(B4(l))}var v50=H4;export{m_ as routes,w50 as professionalSitePlugin,v50 as professionalRoutes,u50 as personalSitePlugin,h50 as personalRoutes,o50 as ProfessionalLayout,t50 as PersonalLayout};
1689
1689
 
1690
- //# debugId=31B269A6757D3E3D64756E2164756E21
1690
+ //# debugId=DA180DCB61D3103A64756E2164756E21
1691
1691
  //# sourceMappingURL=site.js.map