gesso-vite-plugin 0.6.30 → 0.8.0
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.
- package/CHANGELOG.md +28 -0
- package/dist/catalog-CbeOrdxD.d.ts +305 -0
- package/dist/contracts-Dwn5pg-0.js +1299 -0
- package/dist/contracts-Dwn5pg-0.js.map +1 -0
- package/dist/gesso-channels.mjs +322 -24
- package/dist/gesso-channels.mjs.map +1 -1
- package/dist/index.d.ts +42 -114
- package/dist/index.js +317 -710
- package/dist/index.js.map +1 -1
- package/dist/library.d.ts +56 -0
- package/dist/library.js +56 -0
- package/dist/library.js.map +1 -0
- package/package.json +5 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gesso-channels.mjs","names":[],"sources":["../src/contracts.ts","../bin/gesso-channels.ts"],"sourcesContent":["import type * as TsApi from 'typescript/unstable/sync';\n\nimport { blankLiterals, findCalls, importSources } from './source.ts';\n\n/**\n * Channel contracts, read by the type checker and written back as JSON\n * Schema.\n *\n * A contract module already says everything an agent driving the\n * application would need to know: the view's keys and their types, each\n * command's parameters and their types, and, where the author wrote\n * them, what each one is for. All of it is types and comments, and\n * both are gone by the time the module runs. This puts them back, as a\n * `describeChannel(token, schema)` call appended to the module, so the\n * token carries its own description wherever it is imported.\n *\n * The checker rather than the syntax, because a contract names its\n * types where it likes: `ProductRow` from another file, a status union\n * behind an alias, a view written as an object literal whose type is\n * inferred. Only the checker resolves all of those the way the compiler\n * that checked the application did. TypeScript 7's checker runs in a\n * separate process behind an API its package calls unstable, so all of\n * it is in this file, and the rest of the plugin sees one function.\n *\n * What the comments carry:\n *\n * - The JSDoc on the exported token is the channel's description, on\n * a view key is that key's, and on a command is the command's.\n * - `@param name text` describes a parameter.\n * - `@destructive`, `@idempotent`, `@confirm` and `@hidden` on a\n * command are its annotations; `CommandSchema` says what each means.\n * - `@hidden` on a view key keeps that key from an agent; see\n * `ChannelSchema.hidden`. On the token, it keeps the whole channel\n * from one; see `ChannelSchema.internal`.\n *\n * And what it will not describe: a value that is not plain data, which\n * cannot cross a channel anyway. A function, a `Date`, a `Map`, a class\n * instance. Each is reported, by its path in the contract, and\n * described as `{}` so the rest of the schema still stands.\n */\n\n/** The parts of `typescript/unstable/sync` this file uses, values included. */\nexport type TypeScriptApi = typeof TsApi;\n\n/** A channel's description, in the shape `describeChannel` takes. */\nexport interface ChannelSchemaOut {\n description?: string;\n internal?: true;\n view: JsonSchema;\n hidden?: string[];\n commands: Record<string, CommandSchemaOut>;\n}\n\ninterface CommandSchemaOut {\n description?: string;\n parameters: string[];\n rest?: true;\n input: JsonSchema;\n destructive?: true;\n idempotent?: true;\n confirm?: true;\n hidden?: true;\n}\n\ntype JsonSchema = Record<string, unknown>;\n\n/** Every channel a module exports, and what could not be described. */\nexport interface ContractReading {\n /** Export name to schema, in declaration order. */\n readonly channels: ReadonlyMap<string, ChannelSchemaOut>;\n /** One sentence per value that could not be described, naming where it is. */\n readonly warnings: readonly string[];\n}\n\n/** The functions that declare a channel. */\nconst DECLARATIONS = ['channel', 'defineChannel'];\n\n/**\n * Whether a module declares a channel, without asking the checker.\n *\n * The checker is a process round trip per question, and every module\n * the application has passes through `transform`, so this is the gate:\n * a call to `channel` or `defineChannel` imported from\n * `gesso-framework`. A module that re-exports a token someone else\n * declared is not a contract, and the module that declared it is the\n * one described.\n */\nexport function declaresChannel(code: string): boolean {\n if (!code.includes('gesso-framework')) {\n return false;\n }\n const blank = blankLiterals(code);\n const imports = importSources(code, blank);\n // `channel<View, Commands>(...)` has type arguments between the name\n // and the parenthesis, which `findCalls` does not look past, and type\n // arguments are the form `channel` is usually written in.\n return DECLARATIONS.some(\n name =>\n imports.get(name) === 'gesso-framework' &&\n (findCalls(code, name, blank).length > 0 || new RegExp(`(?<![\\\\w$.])${name}\\\\s*<`).test(blank))\n );\n}\n\n/**\n * The code appended to a contract module.\n *\n * An import at the bottom is still hoisted, and appending rather than\n * rewriting leaves every line the author wrote where they wrote it, so\n * the module's source map needs nothing.\n */\nexport function describeCalls(channels: ReadonlyMap<string, ChannelSchemaOut>): string {\n const lines = [\"import { describeChannel as __gessoDescribeChannel } from 'gesso-framework';\"];\n for (const [name, schema] of channels) {\n lines.push(`__gessoDescribeChannel(${name}, ${JSON.stringify(schema)});`);\n }\n return `\\n${lines.join('\\n')}\\n`;\n}\n\n/**\n * A module that describes channels when it is imported, for a bundle\n * this plugin never sees.\n *\n * The plugin describes a contract by appending to it as Vite bundles\n * it. An Electrobun main process is bundled by Electrobun's own build,\n * which takes no plugins, so there the description is written ahead of\n * time instead: `gesso-channels` reads the contracts with the same\n * checker and writes this module, and the main process imports it once.\n * A token is imported from its contract, so the schema lands on the\n * same object the application serves.\n *\n * `outFile` and every `file` are absolute POSIX paths.\n */\nexport function describedModule(\n contracts: readonly { readonly file: string; readonly channels: ReadonlyMap<string, ChannelSchemaOut> }[],\n outFile: string\n): string {\n const lines = [\n '// Written by gesso-channels from the channel contracts it names below.',\n '// Run it again after changing a contract rather than editing this file.',\n \"import { describeChannel } from 'gesso-framework';\"\n ];\n const from = outFile.slice(0, outFile.lastIndexOf('/'));\n for (const contract of contracts) {\n if (contract.channels.size > 0) {\n const names = [...contract.channels.keys()].join(', ');\n lines.push(`import { ${names} } from '${importPath(from, contract.file)}';`);\n }\n }\n for (const contract of contracts) {\n for (const [name, schema] of contract.channels) {\n lines.push('', `describeChannel(${name}, ${JSON.stringify(schema, null, 2)});`);\n }\n }\n return `${lines.join('\\n')}\\n`;\n}\n\n/** `./Counter` from `/app/src/shared` to `/app/src/shared/Counter.ts`, without the extension. */\nfunction importPath(fromDir: string, file: string): string {\n const from = fromDir.split('/').filter(Boolean);\n const to = file\n .replace(/\\.[cm]?[jt]sx?$/, '')\n .split('/')\n .filter(Boolean);\n let shared = 0;\n while (shared < from.length && shared < to.length - 1 && from[shared] === to[shared]) {\n shared++;\n }\n const up = from.length - shared;\n const rest = to.slice(shared).join('/');\n return up === 0 ? `./${rest}` : `${'../'.repeat(up)}${rest}`;\n}\n\n/**\n * The checker, held open across reads.\n *\n * Spawning it and loading a project costs about a hundred\n * milliseconds; a question after that costs well under one. So one\n * reader lives as long as the dev server or the build, is told which\n * files changed between reads, and is closed at the end, because the\n * process it holds would otherwise keep the build from exiting.\n */\nexport class ContractReader {\n private api: TsApi.API | null = null;\n private readonly changed = new Set<string>();\n /** Each contract's tsconfig, found once. */\n private readonly configs = new Map<string, string>();\n\n /**\n * @param fs Callbacks the checker reads through before the disk, for\n * a spec that keeps its project in memory.\n */\n private readonly ts: TypeScriptApi;\n private readonly cwd: string;\n private readonly fs: TsApi.APIOptions['fs'];\n\n // Fields assigned by hand rather than parameter properties: the\n // plugin is loaded by Node's type stripping, which rejects those.\n constructor(ts: TypeScriptApi, cwd: string, fs?: TsApi.APIOptions['fs']) {\n this.ts = ts;\n this.cwd = cwd;\n this.fs = fs;\n }\n\n /** A file changed on disk since the last read. */\n invalidate(file: string): void {\n this.changed.add(file);\n }\n\n /** Every channel `file` exports, described. */\n read(file: string): ContractReading {\n const api = (this.api ??= new this.ts.API({ cwd: this.cwd, ...(this.fs === undefined ? {} : { fs: this.fs }) }));\n this.changed.add(file);\n let snapshot = api.updateSnapshot({ fileChanges: { changed: [...this.changed] } });\n this.changed.clear();\n let config = this.configs.get(file);\n if (config === undefined) {\n // A file opened through the API is an editor's buffer: the server\n // stops reading it from disk, and an edit the next save makes is\n // never seen. So the file is opened only long enough to learn\n // which project it belongs to, and the project is what stays open.\n config = api.updateSnapshot({ openFiles: [file] }).getDefaultProjectForFile(file)?.configFileName;\n snapshot = api.updateSnapshot({\n ...(config === undefined ? {} : { openProjects: [config] }),\n closeFiles: [file]\n });\n if (config !== undefined) {\n this.configs.set(file, config);\n }\n }\n const project = config === undefined ? undefined : snapshot.getProject(config);\n const source = project?.program.getSourceFile(file);\n if (project === undefined || source === undefined) {\n return { channels: new Map(), warnings: [`TypeScript has no project that includes ${file}.`] };\n }\n const checker = project.checker;\n const module = checker.getSymbolAtLocation(source);\n const describer = new Describer(this.ts, checker);\n const channels = new Map<string, ChannelSchemaOut>();\n for (const exported of module === undefined ? [] : checker.getExportsOfModule(module)) {\n const type = checker.getTypeOfSymbol(exported);\n if (type === undefined || type.getSymbol()?.name !== 'ChannelToken') {\n continue;\n }\n const [view, commands] = checker.getTypeArguments(type as TsApi.TypeReference);\n channels.set(exported.name, describer.channel(exported, view, commands));\n }\n return { channels, warnings: describer.warnings };\n }\n\n close(): void {\n this.api?.close();\n this.api = null;\n }\n}\n\n/**\n * Library types that are objects to the checker and not plain data to\n * a channel. They compare by reference, so `provide` rejects them on\n * their first value; describing their methods as properties would be\n * describing something that never arrives.\n */\nconst NOT_PLAIN = new Set([\n 'Date',\n 'Map',\n 'ReadonlyMap',\n 'Set',\n 'ReadonlySet',\n 'WeakMap',\n 'WeakSet',\n 'Promise',\n 'RegExp',\n 'Error',\n 'ArrayBuffer',\n 'SharedArrayBuffer',\n 'DataView',\n 'Blob',\n 'File',\n 'URL',\n 'Int8Array',\n 'Uint8Array',\n 'Uint8ClampedArray',\n 'Int16Array',\n 'Uint16Array',\n 'Int32Array',\n 'Uint32Array',\n 'Float32Array',\n 'Float64Array',\n 'BigInt64Array',\n 'BigUint64Array'\n]);\n\n/**\n * Bytes, which a command may carry and a view key may not.\n *\n * A command's argument is structured-cloned onto the owning thread, and\n * a typed array or an `ArrayBuffer` clones as itself: a file a person\n * dropped crosses as its bytes, with no encoding pass on the render\n * thread. A view key is diffed, and a differ that compares bytes by\n * reference reports a change on every emission, so there they stay\n * unplain. In a command's schema they are a base64 string, which is\n * what an agent can write, tagged with the type the command expects so\n * the agent surface can turn the string back into it.\n */\nexport const BINARY = new Set([\n 'ArrayBuffer',\n 'Int8Array',\n 'Uint8Array',\n 'Uint8ClampedArray',\n 'Int16Array',\n 'Uint16Array',\n 'Int32Array',\n 'Uint32Array',\n 'Float32Array',\n 'Float64Array',\n 'BigInt64Array',\n 'BigUint64Array'\n]);\n\n/** Turns checker types into JSON Schema for one module's channels. */\nclass Describer {\n readonly warnings: string[] = [];\n private readonly flags: TypeScriptApi['TypeFlags'];\n /** Describing a command's parameters, where bytes are allowed; see `BINARY`. */\n private inCommand = false;\n /** Types being described right now, to notice a type that contains itself. */\n private readonly inProgress = new Set<number>();\n /** Types found to contain themselves, which are described once in `$defs`. */\n private readonly recursive = new Set<number>();\n /** `$defs` for the schema being built. Reset per root, so each schema stands alone. */\n private defs = new Map<string, JsonSchema>();\n private readonly defNames = new Map<number, string>();\n\n private readonly ts: TypeScriptApi;\n private readonly checker: TsApi.Checker;\n\n constructor(ts: TypeScriptApi, checker: TsApi.Checker) {\n this.ts = ts;\n this.checker = checker;\n this.flags = ts.TypeFlags;\n }\n\n channel(token: TsApi.Symbol, view: TsApi.Type, commands: TsApi.Type | undefined): ChannelSchemaOut {\n const out: ChannelSchemaOut = { view: this.root(view, `${token.name}.view`), commands: {} };\n const description = this.checker.getDocumentationCommentOfSymbol(token);\n if (description !== '') {\n out.description = description;\n }\n if (this.checker.getJsDocTagsOfSymbol(token).some(tag => tag.name === 'hidden')) {\n out.internal = true;\n }\n const hidden = this.checker\n .getPropertiesOfType(view)\n .filter(key => this.checker.getJsDocTagsOfSymbol(key).some(tag => tag.name === 'hidden'))\n .map(key => key.name);\n if (hidden.length > 0) {\n out.hidden = hidden;\n }\n if (commands !== undefined) {\n for (const command of this.checker.getPropertiesOfType(commands)) {\n const described = this.command(command, `${token.name}.commands.${command.name}`);\n if (described !== null) {\n out.commands[command.name] = described;\n }\n }\n }\n return out;\n }\n\n /** A schema that stands alone: its own `$defs`, if anything in it recurs. */\n private root(type: TsApi.Type, path: string): JsonSchema {\n this.defs = new Map();\n const schema = this.describe(type, path);\n if (this.defs.size > 0) {\n return { ...schema, $defs: Object.fromEntries(this.defs) };\n }\n return schema;\n }\n\n private command(symbol: TsApi.Symbol, path: string): CommandSchemaOut | null {\n const type = this.checker.getTypeOfSymbol(symbol);\n const signatures = type === undefined ? [] : this.checker.getSignaturesOfType(type, this.ts.SignatureKind.Call);\n if (signatures.length === 0) {\n this.warnings.push(`${path} is not a function, so it is not a command an agent can send.`);\n return null;\n }\n if (signatures.length > 1) {\n this.warnings.push(`${path} is overloaded; only its first signature is described.`);\n }\n const signature = signatures[0];\n const tags = this.checker.getJsDocTagsOfSymbol(symbol);\n const parameters = signature.getParameters();\n\n this.defs = new Map();\n const properties: Record<string, JsonSchema> = {};\n const required: string[] = [];\n this.inCommand = true;\n parameters.forEach((parameter, index) => {\n const parameterType = this.checker.getTypeOfSymbol(parameter);\n const optional = parameterType !== undefined && this.includesUndefined(parameterType);\n const schema = parameterType === undefined ? {} : this.describe(parameterType, `${path}(${parameter.name})`);\n const description = this.parameterDescription(parameter, tags);\n properties[parameter.name] = description === '' ? schema : { ...schema, description };\n const rest = signature.hasRestParameter && index === parameters.length - 1;\n if (!optional && !rest) {\n required.push(parameter.name);\n }\n });\n this.inCommand = false;\n const input: JsonSchema = { type: 'object', properties, additionalProperties: false };\n if (required.length > 0) {\n input.required = required;\n }\n if (this.defs.size > 0) {\n input.$defs = Object.fromEntries(this.defs);\n }\n\n const out: CommandSchemaOut = { parameters: parameters.map(parameter => parameter.name), input };\n const description = this.checker.getDocumentationCommentOfSymbol(symbol);\n if (description !== '') {\n out.description = description;\n }\n if (signature.hasRestParameter) {\n out.rest = true;\n }\n for (const annotation of ['destructive', 'idempotent', 'confirm', 'hidden'] as const) {\n if (tags.some(tag => tag.name === annotation)) {\n out[annotation] = true;\n }\n }\n return out;\n }\n\n /**\n * A parameter's own comment, or failing that its `@param` line.\n *\n * The checker attaches `@param` text to the parameter of a method\n * signature. A command written as a property, `move: (from: number)\n * => void`, keeps its comment on the property, so the tag is read\n * from there instead.\n */\n private parameterDescription(parameter: TsApi.Symbol, tags: readonly TsApi.JSDocTagInfo[]): string {\n const own = this.checker.getDocumentationCommentOfSymbol(parameter);\n if (own !== '') {\n return own;\n }\n for (const tag of tags) {\n const match = tag.name === 'param' ? /^\\s*(\\S+)\\s*(?:-\\s*)?([\\s\\S]*)$/.exec(tag.text ?? '') : null;\n if (match !== null && match[1] === parameter.name) {\n return match[2].trim();\n }\n }\n return '';\n }\n\n private includesUndefined(type: TsApi.Type): boolean {\n const { Undefined, Void } = this.flags;\n if (type.isUnionType()) {\n return (type.getTypes() ?? []).some(member => (member.flags & (Undefined | Void)) !== 0);\n }\n return (type.flags & (Undefined | Void)) !== 0;\n }\n\n private describe(type: TsApi.Type, path: string): JsonSchema {\n const f = this.flags;\n const flags = type.flags;\n\n if (flags & (f.Any | f.Unknown)) {\n return {};\n }\n if (flags & f.Never) {\n this.warnings.push(\n `${path} is never, which describes no value. An empty array literal is never[] unless it says what it holds: write \\`[] as Row[]\\`.`\n );\n return { not: {} };\n }\n if (flags & (f.String | f.TemplateLiteral | f.StringMapping)) {\n return { type: 'string' };\n }\n if (flags & f.Number) {\n return { type: 'number' };\n }\n if (flags & f.Boolean) {\n return { type: 'boolean' };\n }\n if (flags & f.Null) {\n return { type: 'null' };\n }\n if (flags & (f.Undefined | f.Void)) {\n return {};\n }\n if (flags & f.BooleanLiteral) {\n return { const: (type as TsApi.IntrinsicType).intrinsicName === 'true' };\n }\n if (flags & (f.StringLiteral | f.NumberLiteral)) {\n return { const: (type as TsApi.LiteralType).value };\n }\n if (flags & (f.BigInt | f.BigIntLiteral | f.ESSymbol | f.UniqueESSymbol)) {\n return this.unplain(path, flags & f.BigIntLike ? 'a bigint' : 'a symbol');\n }\n if (type.isUnionType()) {\n return this.union(type, path);\n }\n if (flags & f.TypeParameter) {\n return {};\n }\n if (flags & (f.Object | f.Intersection)) {\n // An array is a container rather than a type that can contain\n // itself: `readonly Row[]` in a view and in `Row.children` is one\n // type, and treating it as named would put the array in `$defs`\n // instead of the row.\n if (this.checker.isArrayType(type)) {\n const [element] = this.checker.getTypeArguments(type as TsApi.TypeReference);\n return { type: 'array', items: element === undefined ? {} : this.describe(element, `${path}[]`) };\n }\n if (this.checker.isTupleType(type)) {\n return this.tuple(type as TsApi.TypeReference, path);\n }\n return this.named(type, path);\n }\n this.warnings.push(`${path} has a type this reader does not describe, so it is left open.`);\n return {};\n }\n\n /**\n * A union, with `undefined` taken out (optionality is said by\n * `required`), `true | false` folded back into `boolean`, and a union\n * of nothing but literals written as an `enum`.\n */\n private union(type: TsApi.UnionType, path: string): JsonSchema {\n const f = this.flags;\n let members = (type.getTypes() ?? []).filter(member => (member.flags & (f.Undefined | f.Void)) === 0);\n const booleans = members.filter(member => member.flags & f.BooleanLiteral);\n const schemas: JsonSchema[] = [];\n if (booleans.length === 2) {\n members = members.filter(member => !(member.flags & f.BooleanLiteral));\n schemas.push({ type: 'boolean' });\n }\n if (\n members.length > 0 &&\n schemas.length === 0 &&\n members.every(member => member.flags & (f.StringLiteral | f.NumberLiteral))\n ) {\n return { enum: members.map(member => (member as TsApi.LiteralType).value) };\n }\n // `null` last, so `string | null` reads as a string that may be absent.\n members.sort((a, b) => Number((a.flags & f.Null) !== 0) - Number((b.flags & f.Null) !== 0));\n schemas.push(...members.map(member => this.describe(member, path)));\n return schemas.length === 1 ? schemas[0] : { anyOf: schemas };\n }\n\n /**\n * An object type, described once however often it appears, and by\n * reference when it contains itself.\n */\n private named(type: TsApi.Type, path: string): JsonSchema {\n const id = type.id;\n if (this.inProgress.has(id)) {\n this.recursive.add(id);\n return { $ref: `#/$defs/${this.defName(type)}` };\n }\n this.inProgress.add(id);\n const schema = this.object(type, path);\n this.inProgress.delete(id);\n if (this.recursive.has(id)) {\n const name = this.defName(type);\n this.defs.set(name, schema);\n return { $ref: `#/$defs/${name}` };\n }\n return schema;\n }\n\n private defName(type: TsApi.Type): string {\n let name = this.defNames.get(type.id);\n if (name === undefined) {\n const base = (type.getAliasSymbol() ?? type.getSymbol())?.name ?? 'Type';\n const taken = new Set(this.defNames.values());\n name = taken.has(base) ? `${base}${type.id}` : base;\n this.defNames.set(type.id, name);\n }\n return name;\n }\n\n private object(type: TsApi.Type, path: string): JsonSchema {\n const checker = this.checker;\n const symbolName = type.getSymbol()?.name;\n if (symbolName !== undefined && this.inCommand && BINARY.has(symbolName)) {\n return { type: 'string', contentEncoding: 'base64', 'x-gesso-binary': symbolName };\n }\n // Shared memory may sit in a view: it compares by identity, which is\n // right for a handle on memory, and an agent is shown its size (see\n // `describeSharedMemory`). A command cannot take it from an agent,\n // which has no memory to share, so there it stays unplain.\n if (symbolName === 'SharedArrayBuffer' && !this.inCommand) {\n return { type: 'string', description: 'Shared memory, shown as its size.', 'x-gesso-shared': true };\n }\n if (symbolName !== undefined && NOT_PLAIN.has(symbolName)) {\n return this.unplain(path, `a ${symbolName}`);\n }\n if ((type as TsApi.ObjectType).objectFlags & this.ts.ObjectFlags.Class) {\n return this.unplain(path, `an instance of ${symbolName ?? 'a class'}`);\n }\n if (checker.getSignaturesOfType(type, this.ts.SignatureKind.Call).length > 0) {\n return this.unplain(path, 'a function');\n }\n\n const properties: Record<string, JsonSchema> = {};\n const required: string[] = [];\n for (const property of checker.getPropertiesOfType(type)) {\n const propertyType = checker.getTypeOfSymbol(property);\n const at = `${path}.${property.name}`;\n if (propertyType === undefined) {\n properties[property.name] = {};\n continue;\n }\n const schema = this.describe(propertyType, at);\n const description = checker.getDocumentationCommentOfSymbol(property);\n properties[property.name] = description === '' ? schema : { ...schema, description };\n const optional = (property.flags & this.ts.SymbolFlags.Optional) !== 0 || this.includesUndefined(propertyType);\n if (!optional) {\n required.push(property.name);\n }\n }\n const schema: JsonSchema = { type: 'object', properties };\n if (required.length > 0) {\n schema.required = required;\n }\n const index = checker.getIndexInfosOfType(type).find(info => info.keyType.flags & this.flags.String);\n if (index !== undefined) {\n schema.additionalProperties = this.describe(index.valueType, `${path}[key]`);\n }\n return schema;\n }\n\n private tuple(type: TsApi.TypeReference, path: string): JsonSchema {\n const elements = this.checker.getTypeArguments(type);\n const elementFlags = (type.getTarget() as TsApi.TupleType).elementFlags ?? [];\n const { Required, Rest } = this.ts.ElementFlags;\n const prefixItems: JsonSchema[] = [];\n let items: JsonSchema | false = false;\n let minItems = 0;\n elements.forEach((element, index) => {\n const schema = this.describe(element, `${path}[${index}]`);\n if (elementFlags[index] & Rest) {\n items = schema;\n } else {\n prefixItems.push(schema);\n if (elementFlags[index] & Required) {\n minItems = index + 1;\n }\n }\n });\n return { type: 'array', prefixItems, items, minItems };\n }\n\n private unplain(path: string, what: string): JsonSchema {\n this.warnings.push(`${path} is ${what}, which is not plain data and cannot cross a channel.`);\n return {};\n }\n}\n","#!/usr/bin/env node\n/**\n * `gesso-channels`: describes channel contracts for a bundle Vite does\n * not build.\n *\n * gesso-channels src/shared/Counter.ts --out src/shared/channels.described.ts\n * gesso-channels src/shared/*.ts --out src/shared/channels.described.ts --check\n *\n * `gesso-vite-plugin` describes a contract as Vite bundles it. An\n * Electrobun main process is bundled by Electrobun's own build, which\n * takes no plugins, so its channels would reach an agent undescribed:\n * no descriptions, and arguments as a positional list. This reads the\n * same contracts with the same checker and writes a module that\n * describes them when imported; the main process imports it once.\n *\n * `--check` writes nothing and fails when the module on disk is not\n * what it would write, for a gate that wants to know a contract\n * changed without the module following.\n *\n * It needs TypeScript 7 in the project, whose checker does the reading.\n */\nimport { existsSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\n\nimport { ContractReader, describedModule, type TypeScriptApi } from '../src/contracts.ts';\n\nconst USAGE = `Usage: gesso-channels <contract.ts>... --out <file> [--check]\n\nWrites a module that attaches a JSON Schema to every channel the\ncontracts export, for a bundle gesso-vite-plugin does not build, such as\nan Electrobun main process. Import the module once from that bundle.\n\n --out <file> Where to write the module.\n --check Write nothing; fail if the module is out of date.\n`;\n\nfunction fail(message: string): never {\n console.error(message);\n process.exit(1);\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n const files: string[] = [];\n let out: string | undefined;\n let check = false;\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === '--out') {\n out = args[++i];\n } else if (arg === '--check') {\n check = true;\n } else if (arg === '-h' || arg === '--help') {\n console.log(USAGE);\n return;\n } else if (arg.startsWith('-')) {\n fail(`Unknown option ${arg}.\\n\\n${USAGE}`);\n } else {\n files.push(arg);\n }\n }\n if (out === undefined || files.length === 0) {\n fail(USAGE);\n }\n\n let ts: TypeScriptApi;\n try {\n ts = (await import('typescript/unstable/sync')) as TypeScriptApi;\n } catch {\n fail('gesso-channels needs TypeScript 7 or later in the project: npm install --save-dev typescript@^7.');\n }\n\n // Real paths, because the checker knows files by where they really\n // are, and a symlinked temporary directory would otherwise read as a\n // file no project includes.\n const cwd = realpathSync(process.cwd());\n const outFile = resolve(realpathSync(dirname(resolve(cwd, out))), out.split(/[\\\\/]/).pop()!);\n const reader = new ContractReader(ts, cwd);\n const contracts = [];\n let warned = false;\n try {\n for (const file of files) {\n const path = realpathSync(resolve(cwd, file));\n const reading = reader.read(path);\n for (const warning of reading.warnings) {\n console.warn(`gesso-channels: ${warning}`);\n warned = true;\n }\n contracts.push({ file: path, channels: reading.channels });\n }\n } finally {\n reader.close();\n }\n\n const described = contracts.reduce((count, contract) => count + contract.channels.size, 0);\n if (described === 0) {\n fail(`None of ${files.join(', ')} exports a channel.`);\n }\n const module = describedModule(contracts, outFile);\n if (check) {\n const current = existsSync(outFile) ? readFileSync(outFile, 'utf8') : null;\n if (current !== module) {\n fail(`${out} is out of date. Run gesso-channels without --check to write it.`);\n }\n console.log(`${out} describes ${described} channel${described === 1 ? '' : 's'} and is current.`);\n return;\n }\n writeFileSync(outFile, module);\n console.log(\n `Wrote ${out}: ${described} channel${described === 1 ? '' : 's'} described${warned ? ', with warnings above' : ''}.`\n );\n}\n\nvoid main();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"gesso-channels.mjs","names":[],"sources":["../src/contracts.ts","../bin/gesso-channels.ts"],"sourcesContent":["import type * as TsApi from 'typescript/unstable/sync';\n\nimport { blankLiterals, findCalls, importSources } from './source.ts';\n\n/**\n * Channel contracts, read by the type checker and written back as JSON\n * Schema.\n *\n * A contract module already says everything an agent driving the\n * application would need to know: the view's keys and their types, each\n * command's parameters and their types, and, where the author wrote\n * them, what each one is for. All of it is types and comments, and\n * both are gone by the time the module runs. This puts them back, as a\n * `describeChannel(token, schema)` call appended to the module, so the\n * token carries its own description wherever it is imported.\n *\n * The checker rather than the syntax, because a contract names its\n * types where it likes: `ProductRow` from another file, a status union\n * behind an alias, a view written as an object literal whose type is\n * inferred. Only the checker resolves all of those the way the compiler\n * that checked the application did. TypeScript 7's checker runs in a\n * separate process behind an API its package calls unstable, so all of\n * it is in this file, and the rest of the plugin sees one function.\n *\n * What the comments carry:\n *\n * - The JSDoc on the exported token is the channel's description, on\n * a view key is that key's, and on a command is the command's.\n * - `@param name text` describes a parameter.\n * - `@destructive`, `@idempotent`, `@confirm` and `@hidden` on a\n * command are its annotations; `CommandSchema` says what each means.\n * - `@hidden` on a view key keeps that key from an agent; see\n * `ChannelSchema.hidden`. On the token, it keeps the whole channel\n * from one; see `ChannelSchema.internal`.\n *\n * And what it will not describe: a value that is not plain data, which\n * cannot cross a channel anyway. A function, a `Date`, a `Map`, a class\n * instance. Each is reported, by its path in the contract, and\n * described as `{}` so the rest of the schema still stands.\n */\n\n/** The parts of `typescript/unstable/sync` this file uses, values included. */\nexport type TypeScriptApi = typeof TsApi;\n\n/** A channel's description, in the shape `describeChannel` takes. */\nexport interface ChannelSchemaOut {\n description?: string;\n internal?: true;\n view: JsonSchema;\n hidden?: string[];\n commands: Record<string, CommandSchemaOut>;\n}\n\ninterface CommandSchemaOut {\n description?: string;\n parameters: string[];\n rest?: true;\n input: JsonSchema;\n destructive?: true;\n idempotent?: true;\n confirm?: true;\n hidden?: true;\n}\n\ntype JsonSchema = Record<string, unknown>;\n\n/**\n * A component's description: what it is for, and what each of its\n * inputs takes. The catalog an agent designing with the library reads,\n * so that it uses `Badge` rather than drawing one from boxes.\n */\nexport interface ComponentSchemaOut {\n /** The first paragraph of its JSDoc: what it is, for a list of many. */\n summary?: string;\n /** The whole JSDoc, for the one component being used. */\n description?: string;\n /**\n * Its inputs, as an object schema. Each input says its TypeScript\n * type as `x-gesso-type`, which an agent writing TSX reads more\n * readily than the schema; an input declared on a shared props type\n * (`ControlLayoutProps`) says which as `x-gesso-from`; a callback is\n * `x-gesso-callback` and a child `x-gesso-child`.\n */\n inputs: JsonSchema;\n /** The `@example` blocks, as written. */\n examples?: string[];\n /** The `@deprecated` text, or true when it gives none. */\n deprecated?: string | true;\n}\n\n/** Every component a module exports, and what could not be described. */\nexport interface ComponentReading {\n /** Export name to schema, in declaration order. */\n readonly components: ReadonlyMap<string, ComponentSchemaOut>;\n readonly warnings: readonly string[];\n}\n\n/** Every channel a module exports, and what could not be described. */\nexport interface ContractReading {\n /** Export name to schema, in declaration order. */\n readonly channels: ReadonlyMap<string, ChannelSchemaOut>;\n /** One sentence per value that could not be described, naming where it is. */\n readonly warnings: readonly string[];\n}\n\n/** The functions that declare a channel. */\nconst DECLARATIONS = ['channel', 'defineChannel'];\n\n/**\n * Whether a module declares a channel, without asking the checker.\n *\n * The checker is a process round trip per question, and every module\n * the application has passes through `transform`, so this is the gate:\n * a call to `channel` or `defineChannel` imported from\n * `gesso-framework`. A module that re-exports a token someone else\n * declared is not a contract, and the module that declared it is the\n * one described.\n */\nexport function declaresChannel(code: string): boolean {\n if (!code.includes('gesso-framework')) {\n return false;\n }\n const blank = blankLiterals(code);\n const imports = importSources(code, blank);\n // `channel<View, Commands>(...)` has type arguments between the name\n // and the parenthesis, which `findCalls` does not look past, and type\n // arguments are the form `channel` is usually written in.\n return DECLARATIONS.some(\n name =>\n imports.get(name) === 'gesso-framework' &&\n (findCalls(code, name, blank).length > 0 || new RegExp(`(?<![\\\\w$.])${name}\\\\s*<`).test(blank))\n );\n}\n\n/**\n * The code appended to a contract module.\n *\n * An import at the bottom is still hoisted, and appending rather than\n * rewriting leaves every line the author wrote where they wrote it, so\n * the module's source map needs nothing.\n */\nexport function describeCalls(channels: ReadonlyMap<string, ChannelSchemaOut>): string {\n const lines = [\"import { describeChannel as __gessoDescribeChannel } from 'gesso-framework';\"];\n for (const [name, schema] of channels) {\n lines.push(`__gessoDescribeChannel(${name}, ${JSON.stringify(schema)});`);\n }\n return `\\n${lines.join('\\n')}\\n`;\n}\n\n/**\n * A module that describes channels when it is imported, for a bundle\n * this plugin never sees.\n *\n * The plugin describes a contract by appending to it as Vite bundles\n * it. An Electrobun main process is bundled by Electrobun's own build,\n * which takes no plugins, so there the description is written ahead of\n * time instead: `gesso-channels` reads the contracts with the same\n * checker and writes this module, and the main process imports it once.\n * A token is imported from its contract, so the schema lands on the\n * same object the application serves.\n *\n * `outFile` and every `file` are absolute POSIX paths.\n */\nexport function describedModule(\n contracts: readonly { readonly file: string; readonly channels: ReadonlyMap<string, ChannelSchemaOut> }[],\n outFile: string\n): string {\n const lines = [\n '// Written by gesso-channels from the channel contracts it names below.',\n '// Run it again after changing a contract rather than editing this file.',\n \"import { describeChannel } from 'gesso-framework';\"\n ];\n const from = outFile.slice(0, outFile.lastIndexOf('/'));\n for (const contract of contracts) {\n if (contract.channels.size > 0) {\n const names = [...contract.channels.keys()].join(', ');\n lines.push(`import { ${names} } from '${importPath(from, contract.file)}';`);\n }\n }\n for (const contract of contracts) {\n for (const [name, schema] of contract.channels) {\n lines.push('', `describeChannel(${name}, ${JSON.stringify(schema, null, 2)});`);\n }\n }\n return `${lines.join('\\n')}\\n`;\n}\n\n/** `./Counter` from `/app/src/shared` to `/app/src/shared/Counter.ts`, without the extension. */\nfunction importPath(fromDir: string, file: string): string {\n const from = fromDir.split('/').filter(Boolean);\n const to = file\n .replace(/\\.[cm]?[jt]sx?$/, '')\n .split('/')\n .filter(Boolean);\n let shared = 0;\n while (shared < from.length && shared < to.length - 1 && from[shared] === to[shared]) {\n shared++;\n }\n const up = from.length - shared;\n const rest = to.slice(shared).join('/');\n return up === 0 ? `./${rest}` : `${'../'.repeat(up)}${rest}`;\n}\n\n/**\n * The checker, held open across reads.\n *\n * Spawning it and loading a project costs about a hundred\n * milliseconds; a question after that costs well under one. So one\n * reader lives as long as the dev server or the build, is told which\n * files changed between reads, and is closed at the end, because the\n * process it holds would otherwise keep the build from exiting.\n */\nexport class ContractReader {\n private api: TsApi.API | null = null;\n private readonly changed = new Set<string>();\n private readonly created = new Set<string>();\n private readonly deleted = new Set<string>();\n /** Each contract's tsconfig, found once. */\n private readonly configs = new Map<string, string>();\n\n /**\n * @param fs Callbacks the checker reads through before the disk, for\n * a spec that keeps its project in memory.\n */\n private readonly ts: TypeScriptApi;\n private readonly cwd: string;\n private readonly fs: TsApi.APIOptions['fs'];\n\n // Fields assigned by hand rather than parameter properties: the\n // plugin is loaded by Node's type stripping, which rejects those.\n constructor(ts: TypeScriptApi, cwd: string, fs?: TsApi.APIOptions['fs']) {\n this.ts = ts;\n this.cwd = cwd;\n this.fs = fs;\n }\n\n /**\n * A file changed on disk since the last read. A new or deleted one is\n * said so, because a program's list of files only changes for those:\n * a component in a new file is otherwise not in the catalog.\n */\n invalidate(file: string, event: 'update' | 'create' | 'delete' = 'update'): void {\n (event === 'create' ? this.created : event === 'delete' ? this.deleted : this.changed).add(file);\n }\n\n /**\n * A snapshot of the projects, with what changed since the last one.\n *\n * The checker keeps every project it has opened, and asked to update\n * one whose tsconfig has since been deleted it refuses the whole\n * update (\"project not found for update\"). That was a dev server gone\n * with it: a project's tsconfig removed while the server ran threw out\n * of the catalog's read and took the process down. A refused update is\n * a checker started afresh, which reads the disk as it now is and\n * opens only what is asked of it again.\n */\n private snapshot(request: NonNullable<Parameters<TsApi.API['updateSnapshot']>[0]>): TsApi.Snapshot {\n const api = (this.api ??= new this.ts.API({ cwd: this.cwd, ...(this.fs === undefined ? {} : { fs: this.fs }) }));\n try {\n return api.updateSnapshot({ ...request, fileChanges: this.takeChanges() });\n } catch {\n this.close();\n this.configs.clear();\n const fresh = (this.api = new this.ts.API({ cwd: this.cwd, ...(this.fs === undefined ? {} : { fs: this.fs }) }));\n const { fileChanges: _changes, ...rest } = request;\n return fresh.updateSnapshot(rest);\n }\n }\n\n /** What changed since the last snapshot, emptied for the next. */\n private takeChanges(): { changed: string[]; created: string[]; deleted: string[] } {\n const changes = { changed: [...this.changed], created: [...this.created], deleted: [...this.deleted] };\n this.changed.clear();\n this.created.clear();\n this.deleted.clear();\n return changes;\n }\n\n /** Every channel `file` exports, described. */\n read(file: string): ContractReading {\n const opened = this.open(file);\n if (typeof opened === 'string') {\n return { channels: new Map(), warnings: [opened] };\n }\n const { checker, exports } = opened;\n const describer = new Describer(this.ts, checker);\n const channels = new Map<string, ChannelSchemaOut>();\n for (const exported of exports) {\n const type = checker.getTypeOfSymbol(exported);\n if (type === undefined || type.getSymbol()?.name !== 'ChannelToken') {\n continue;\n }\n const [view, commands] = checker.getTypeArguments(type as TsApi.TypeReference);\n channels.set(exported.name, describer.channel(exported, view, commands));\n }\n return { channels, warnings: describer.warnings };\n }\n\n /**\n * Every component `file` exports, described: a function whose first\n * parameter is `Inputs<P>`, or a class extending `Component`, whose\n * inputs are its `InputCell` fields. A library's index re-exports its\n * components, and reading it reads all of them.\n */\n readComponents(file: string, config?: string): ComponentReading {\n const opened = this.open(file, config);\n if (typeof opened === 'string') {\n return { components: new Map(), warnings: [opened] };\n }\n const { checker, exports } = opened;\n const describer = new Describer(this.ts, checker, 'component');\n const components = new Map<string, ComponentSchemaOut>();\n for (const exported of exports) {\n const symbol = exported.flags & this.ts.SymbolFlags.Alias ? checker.getAliasedSymbol(exported) : exported;\n const type = checker.getTypeOfSymbol(symbol);\n const inputs = type === undefined ? undefined : this.inputsOf(checker, type);\n if (inputs !== undefined) {\n components.set(exported.name, describer.component(symbol, inputs));\n }\n }\n return { components, warnings: describer.warnings };\n }\n\n /**\n * The source files of the project `config` describes, with their\n * text: what the project itself wrote, so neither its dependencies\n * nor declaration files. The project's own tsconfig decides what that\n * is, which is the answer the person already gave their editor.\n */\n sources(config: string): { readonly file: string; readonly text: string }[] {\n const snapshot = this.snapshot({ openProjects: [config] });\n const program = snapshot.getProject(config)?.program;\n if (program === undefined) {\n return [];\n }\n const out: { file: string; text: string }[] = [];\n for (const file of program.getSourceFileNames()) {\n if (file.includes('/node_modules/') || /\\.d\\.[cm]?ts$/.test(file)) {\n continue;\n }\n const text = program.getSourceFile(file)?.text;\n if (text !== undefined) {\n out.push({ file, text });\n }\n }\n return out;\n }\n\n /** The inputs a component's type declares, or undefined when it is not a component. */\n private inputsOf(checker: TsApi.Checker, type: TsApi.Type): ComponentInputs | undefined {\n const { Call, Construct } = this.ts.SignatureKind;\n const [call] = checker.getSignaturesOfType(type, Call);\n const first = call?.getParameters()[0];\n if (first !== undefined) {\n const parameter = checker.getTypeOfSymbol(first);\n // A helper may take a component's inputs too (`layoutOf(inputs)`);\n // what makes a component is that it gives back something to draw.\n const returns = checker.getReturnTypeOfSignature(call!);\n if (parameter?.getAliasSymbol()?.name === 'Inputs' && returns !== undefined && this.drawable(returns)) {\n const [props] = parameter.getAliasTypeArguments();\n return props === undefined ? undefined : { kind: 'props', type: props };\n }\n return undefined;\n }\n const [construct] = checker.getSignaturesOfType(type, Construct);\n const instance = construct === undefined ? undefined : checker.getReturnTypeOfSignature(construct);\n if (instance === undefined || !this.extendsComponent(instance)) {\n return undefined;\n }\n const cells: { symbol: TsApi.Symbol; type: TsApi.Type }[] = [];\n for (const property of checker.getPropertiesOfType(instance)) {\n const propertyType = checker.getTypeOfSymbol(property);\n if (propertyType?.getSymbol()?.name === 'InputCell') {\n const [value] = checker.getTypeArguments(propertyType as TsApi.TypeReference);\n if (value !== undefined) {\n cells.push({ symbol: property, type: value });\n }\n }\n }\n return { kind: 'cells', cells };\n }\n\n /** Whether a function returning `type` returns a child: `UiChild`, an element, or a union of them. */\n private drawable(type: TsApi.Type): boolean {\n if (type.getAliasSymbol()?.name === 'UiChild') {\n return true;\n }\n const members = type.isUnionType() ? (type.getTypes() ?? []) : [type];\n return members.some(member => CHILD.has(member.getSymbol()?.name ?? ''));\n }\n\n private extendsComponent(type: TsApi.Type): boolean {\n for (let depth = 0, current: TsApi.Type | undefined = type; current !== undefined && depth < 16; depth++) {\n if (current.getSymbol()?.name === 'Component') {\n return true;\n }\n current = current.isClassOrInterface() ? current.getBaseTypes()?.[0] : undefined;\n }\n return false;\n }\n\n /**\n * The checker and the module's exports, or why there are none: read\n * in the project `preferred` names when it includes the file (the\n * catalog's own tsconfig, which may resolve its imports differently\n * from any beside it), else the project TypeScript finds for it.\n */\n private open(\n file: string,\n preferred?: string\n ): { checker: TsApi.Checker; exports: readonly TsApi.Symbol[] } | string {\n this.changed.add(file);\n let snapshot = this.snapshot(preferred === undefined ? {} : { openProjects: [preferred] });\n let config =\n preferred !== undefined && snapshot.getProject(preferred)?.program.getSourceFile(file) !== undefined\n ? preferred\n : this.configs.get(file);\n if (config === undefined) {\n // A file opened through the API is an editor's buffer: the server\n // stops reading it from disk, and an edit the next save makes is\n // never seen. So the file is opened only long enough to learn\n // which project it belongs to, and the project is what stays open.\n config = this.snapshot({ openFiles: [file] }).getDefaultProjectForFile(file)?.configFileName;\n snapshot = this.snapshot({\n ...(config === undefined ? {} : { openProjects: [config] }),\n closeFiles: [file]\n });\n if (config !== undefined) {\n this.configs.set(file, config);\n }\n }\n const project = config === undefined ? undefined : snapshot.getProject(config);\n const source = project?.program.getSourceFile(file);\n if (project === undefined || source === undefined) {\n return `TypeScript has no project that includes ${file}.`;\n }\n const checker = project.checker;\n const module = checker.getSymbolAtLocation(source);\n return { checker, exports: module === undefined ? [] : checker.getExportsOfModule(module) };\n }\n\n close(): void {\n this.api?.close();\n this.api = null;\n }\n}\n\n/**\n * Library types that are objects to the checker and not plain data to\n * a channel. They compare by reference, so `provide` rejects them on\n * their first value; describing their methods as properties would be\n * describing something that never arrives.\n */\nconst NOT_PLAIN = new Set([\n 'Date',\n 'Map',\n 'ReadonlyMap',\n 'Set',\n 'ReadonlySet',\n 'WeakMap',\n 'WeakSet',\n 'Promise',\n 'RegExp',\n 'Error',\n 'ArrayBuffer',\n 'SharedArrayBuffer',\n 'DataView',\n 'Blob',\n 'File',\n 'URL',\n 'Int8Array',\n 'Uint8Array',\n 'Uint8ClampedArray',\n 'Int16Array',\n 'Uint16Array',\n 'Int32Array',\n 'Uint32Array',\n 'Float32Array',\n 'Float64Array',\n 'BigInt64Array',\n 'BigUint64Array'\n]);\n\n/**\n * Bytes, which a command may carry and a view key may not.\n *\n * A command's argument is structured-cloned onto the owning thread, and\n * a typed array or an `ArrayBuffer` clones as itself: a file a person\n * dropped crosses as its bytes, with no encoding pass on the render\n * thread. A view key is diffed, and a differ that compares bytes by\n * reference reports a change on every emission, so there they stay\n * unplain. In a command's schema they are a base64 string, which is\n * what an agent can write, tagged with the type the command expects so\n * the agent surface can turn the string back into it.\n */\nexport const BINARY = new Set([\n 'ArrayBuffer',\n 'Int8Array',\n 'Uint8Array',\n 'Uint8ClampedArray',\n 'Int16Array',\n 'Uint16Array',\n 'Int32Array',\n 'Uint32Array',\n 'Float32Array',\n 'Float64Array',\n 'BigInt64Array',\n 'BigUint64Array'\n]);\n\n/**\n * What a component takes: a function's `P` in `Inputs<P>`, or a class's\n * `InputCell` fields with the type each holds.\n */\ntype ComponentInputs =\n | { readonly kind: 'props'; readonly type: TsApi.Type }\n | { readonly kind: 'cells'; readonly cells: readonly { symbol: TsApi.Symbol; type: TsApi.Type }[] };\n\n/**\n * The types a component takes as a child. `UiChild` is a union of\n * these and an Observable of them, and describing its members would\n * describe the element records a tag compiles to, which nobody writes.\n */\nconst CHILD = new Set(['UiElement', 'ComponentLikeElement', 'ComponentElement']);\n\n/**\n * Turns checker types into JSON Schema for one module's channels or\n * components.\n *\n * The two differ in what is allowed. A channel's values cross a thread\n * as plain data, so a function or a class instance in one is a mistake,\n * and is reported. A component's inputs stay on the render thread, where\n * a callback is how a component talks back and a child is how it is\n * composed, so there they are described for what they are and nothing\n * is reported.\n */\nclass Describer {\n readonly warnings: string[] = [];\n private readonly flags: TypeScriptApi['TypeFlags'];\n /** Describing a command's parameters, where bytes are allowed; see `BINARY`. */\n private inCommand = false;\n /** Types being described right now, to notice a type that contains itself. */\n private readonly inProgress = new Set<number>();\n /** Types found to contain themselves, which are described once in `$defs`. */\n private readonly recursive = new Set<number>();\n /** `$defs` for the schema being built. Reset per root, so each schema stands alone. */\n private defs = new Map<string, JsonSchema>();\n private readonly defNames = new Map<number, string>();\n /** In a component, each named type's `$defs` entry, or null when it is small enough to repeat. */\n private shared = new Map<number, string | null>();\n\n private readonly ts: TypeScriptApi;\n private readonly checker: TsApi.Checker;\n\n private readonly mode: 'channel' | 'component';\n\n constructor(ts: TypeScriptApi, checker: TsApi.Checker, mode: 'channel' | 'component' = 'channel') {\n this.ts = ts;\n this.checker = checker;\n this.flags = ts.TypeFlags;\n this.mode = mode;\n }\n\n component(symbol: TsApi.Symbol, inputs: ComponentInputs): ComponentSchemaOut {\n this.defs = new Map();\n this.shared = new Map();\n const checker = this.checker;\n const properties: Record<string, JsonSchema> = {};\n const required: string[] = [];\n const entries =\n inputs.kind === 'cells'\n ? inputs.cells\n : checker.getPropertiesOfType(inputs.type).map(property => ({\n symbol: property,\n type: checker.getTypeOfSymbol(property)\n }));\n const own = inputs.kind === 'props' ? (inputs.type.getAliasSymbol() ?? inputs.type.getSymbol())?.name : undefined;\n for (const { symbol: property, type: given } of entries) {\n const path = `${symbol.name}.${property.name}`;\n // `width?: UiLength` reads as `UiLength | undefined`, a union with\n // no name, and the name is what lets `UiLength` be described once.\n // Optionality is said by `required`, so the `undefined` can go.\n const type =\n given !== undefined && this.includesUndefined(given) && !this.includesNull(given)\n ? (checker.getNonNullableType(given) ?? given)\n : given;\n let schema: JsonSchema = type === undefined ? {} : this.describe(type, path);\n if (type !== undefined) {\n schema = { ...schema, 'x-gesso-type': this.typeText(type) };\n }\n const description = checker.getDocumentationCommentOfSymbol(property);\n if (description !== '') {\n schema.description = description;\n }\n const fallback = checker.getJsDocTagsOfSymbol(property).find(tag => tag.name === 'default');\n if (fallback?.text !== undefined) {\n schema.default = parseDefault(fallback.text);\n }\n const from = property.getParent()?.name;\n if (own !== undefined && from !== undefined && from !== own && !from.startsWith('__')) {\n schema['x-gesso-from'] = from;\n }\n properties[property.name] = schema;\n // A class's cells are all optional to the parent, as\n // `ComponentProps` makes them.\n const optional =\n inputs.kind === 'cells' ||\n (property.flags & this.ts.SymbolFlags.Optional) !== 0 ||\n (given !== undefined && this.includesUndefined(given));\n if (!optional) {\n required.push(property.name);\n }\n }\n const schema: JsonSchema = { type: 'object', properties, additionalProperties: false };\n if (required.length > 0) {\n schema.required = required;\n }\n if (this.defs.size > 0) {\n schema.$defs = Object.fromEntries(this.defs);\n }\n\n const out: ComponentSchemaOut = { inputs: schema };\n const description = this.componentDocumentation(symbol, inputs);\n if (description !== '') {\n out.summary = description\n .split(/\\n\\s*\\n/)[0]!\n .replace(/\\s+/g, ' ')\n .trim();\n out.description = description;\n }\n const tags = checker.getJsDocTagsOfSymbol(symbol);\n const examples = tags.filter(tag => tag.name === 'example' && tag.text !== undefined).map(tag => tag.text!);\n if (examples.length > 0) {\n out.examples = examples;\n }\n const deprecated = tags.find(tag => tag.name === 'deprecated');\n if (deprecated !== undefined) {\n out.deprecated = deprecated.text === undefined || deprecated.text.trim() === '' ? true : deprecated.text.trim();\n }\n return out;\n }\n\n /**\n * What a component is for: its own JSDoc, else its props type's. A\n * comment elsewhere in its file is not read: no rule about which one\n * describes the component holds across files, and a summary that is\n * sometimes about something else is worse to an agent than none.\n */\n private componentDocumentation(symbol: TsApi.Symbol, inputs: ComponentInputs): string {\n const own = this.checker.getDocumentationCommentOfSymbol(symbol);\n if (own !== '') {\n return own;\n }\n const props = inputs.kind === 'props' ? (inputs.type.getAliasSymbol() ?? inputs.type.getSymbol()) : undefined;\n return props === undefined ? '' : this.checker.getDocumentationCommentOfSymbol(props);\n }\n\n /** The type as TypeScript writes it, without the `undefined` an optional input carries. */\n private typeText(type: TsApi.Type): string {\n const defined = this.includesUndefined(type) ? this.checker.getNonNullableType(type) : type;\n const text = this.checker.typeToString(defined ?? type);\n // `getNonNullableType` takes `null` too, which an input may mean.\n return this.includesNull(type) && !/\\bnull\\b/.test(text) ? `${text} | null` : text;\n }\n\n private includesNull(type: TsApi.Type): boolean {\n const isNull = (member: TsApi.Type) => (member.flags & this.flags.Null) !== 0;\n return type.isUnionType() ? (type.getTypes() ?? []).some(isNull) : isNull(type);\n }\n\n channel(token: TsApi.Symbol, view: TsApi.Type, commands: TsApi.Type | undefined): ChannelSchemaOut {\n const out: ChannelSchemaOut = { view: this.root(view, `${token.name}.view`), commands: {} };\n const description = this.checker.getDocumentationCommentOfSymbol(token);\n if (description !== '') {\n out.description = description;\n }\n if (this.checker.getJsDocTagsOfSymbol(token).some(tag => tag.name === 'hidden')) {\n out.internal = true;\n }\n const hidden = this.checker\n .getPropertiesOfType(view)\n .filter(key => this.checker.getJsDocTagsOfSymbol(key).some(tag => tag.name === 'hidden'))\n .map(key => key.name);\n if (hidden.length > 0) {\n out.hidden = hidden;\n }\n if (commands !== undefined) {\n for (const command of this.checker.getPropertiesOfType(commands)) {\n const described = this.command(command, `${token.name}.commands.${command.name}`);\n if (described !== null) {\n out.commands[command.name] = described;\n }\n }\n }\n return out;\n }\n\n /** A schema that stands alone: its own `$defs`, if anything in it recurs. */\n private root(type: TsApi.Type, path: string): JsonSchema {\n this.defs = new Map();\n const schema = this.describe(type, path);\n if (this.defs.size > 0) {\n return { ...schema, $defs: Object.fromEntries(this.defs) };\n }\n return schema;\n }\n\n private command(symbol: TsApi.Symbol, path: string): CommandSchemaOut | null {\n const type = this.checker.getTypeOfSymbol(symbol);\n const signatures = type === undefined ? [] : this.checker.getSignaturesOfType(type, this.ts.SignatureKind.Call);\n if (signatures.length === 0) {\n this.warnings.push(`${path} is not a function, so it is not a command an agent can send.`);\n return null;\n }\n if (signatures.length > 1) {\n this.warnings.push(`${path} is overloaded; only its first signature is described.`);\n }\n const signature = signatures[0];\n const tags = this.checker.getJsDocTagsOfSymbol(symbol);\n const parameters = signature.getParameters();\n\n this.defs = new Map();\n const properties: Record<string, JsonSchema> = {};\n const required: string[] = [];\n this.inCommand = true;\n parameters.forEach((parameter, index) => {\n const parameterType = this.checker.getTypeOfSymbol(parameter);\n const optional = parameterType !== undefined && this.includesUndefined(parameterType);\n const schema = parameterType === undefined ? {} : this.describe(parameterType, `${path}(${parameter.name})`);\n const description = this.parameterDescription(parameter, tags);\n properties[parameter.name] = description === '' ? schema : { ...schema, description };\n const rest = signature.hasRestParameter && index === parameters.length - 1;\n if (!optional && !rest) {\n required.push(parameter.name);\n }\n });\n this.inCommand = false;\n const input: JsonSchema = { type: 'object', properties, additionalProperties: false };\n if (required.length > 0) {\n input.required = required;\n }\n if (this.defs.size > 0) {\n input.$defs = Object.fromEntries(this.defs);\n }\n\n const out: CommandSchemaOut = { parameters: parameters.map(parameter => parameter.name), input };\n const description = this.checker.getDocumentationCommentOfSymbol(symbol);\n if (description !== '') {\n out.description = description;\n }\n if (signature.hasRestParameter) {\n out.rest = true;\n }\n for (const annotation of ['destructive', 'idempotent', 'confirm', 'hidden'] as const) {\n if (tags.some(tag => tag.name === annotation)) {\n out[annotation] = true;\n }\n }\n return out;\n }\n\n /**\n * A parameter's own comment, or failing that its `@param` line.\n *\n * The checker attaches `@param` text to the parameter of a method\n * signature. A command written as a property, `move: (from: number)\n * => void`, keeps its comment on the property, so the tag is read\n * from there instead.\n */\n private parameterDescription(parameter: TsApi.Symbol, tags: readonly TsApi.JSDocTagInfo[]): string {\n const own = this.checker.getDocumentationCommentOfSymbol(parameter);\n if (own !== '') {\n return own;\n }\n for (const tag of tags) {\n const match = tag.name === 'param' ? /^\\s*(\\S+)\\s*(?:-\\s*)?([\\s\\S]*)$/.exec(tag.text ?? '') : null;\n if (match !== null && match[1] === parameter.name) {\n return match[2].trim();\n }\n }\n return '';\n }\n\n private includesUndefined(type: TsApi.Type): boolean {\n const { Undefined, Void } = this.flags;\n if (type.isUnionType()) {\n return (type.getTypes() ?? []).some(member => (member.flags & (Undefined | Void)) !== 0);\n }\n return (type.flags & (Undefined | Void)) !== 0;\n }\n\n /**\n * In a component, a named type an input uses (`UiLength`, a props\n * interface) is described once, in `$defs`, and referred to after.\n * Every control takes the same layout inputs, and spelled out at each\n * use they were most of each component's description: an agent reads\n * these, and pays for every repetition.\n */\n private describe(type: TsApi.Type, path: string): JsonSchema {\n if (this.mode !== 'component') {\n return this.describeType(type, path);\n }\n const known = this.shared.get(type.id);\n if (known !== undefined) {\n return known === null ? this.describeType(type, path) : { $ref: `#/$defs/${known}` };\n }\n const name = (type.getAliasSymbol() ?? type.getSymbol())?.name;\n if (name === undefined || name.startsWith('__') || name === 'Array' || name === 'ReadonlyArray') {\n return this.describeType(type, path);\n }\n const schema = this.describeType(type, path);\n if (schema.$ref !== undefined || isSmall(schema)) {\n this.shared.set(type.id, null);\n return schema;\n }\n const defName = this.defName(type);\n this.defs.set(defName, schema);\n this.shared.set(type.id, defName);\n return { $ref: `#/$defs/${defName}` };\n }\n\n private describeType(type: TsApi.Type, path: string): JsonSchema {\n const f = this.flags;\n const flags = type.flags;\n\n if (flags & (f.Any | f.Unknown)) {\n return {};\n }\n if (flags & f.Never) {\n this.warnings.push(\n `${path} is never, which describes no value. An empty array literal is never[] unless it says what it holds: write \\`[] as Row[]\\`.`\n );\n return { not: {} };\n }\n if (flags & (f.String | f.TemplateLiteral | f.StringMapping)) {\n return { type: 'string' };\n }\n if (flags & f.Number) {\n return { type: 'number' };\n }\n if (flags & f.Boolean) {\n return { type: 'boolean' };\n }\n if (flags & f.Null) {\n return { type: 'null' };\n }\n if (flags & (f.Undefined | f.Void)) {\n return {};\n }\n if (flags & f.BooleanLiteral) {\n return { const: (type as TsApi.IntrinsicType).intrinsicName === 'true' };\n }\n if (flags & (f.StringLiteral | f.NumberLiteral)) {\n return { const: (type as TsApi.LiteralType).value };\n }\n if (flags & (f.BigInt | f.BigIntLiteral | f.ESSymbol | f.UniqueESSymbol)) {\n return this.unplain(path, flags & f.BigIntLike ? 'a bigint' : 'a symbol');\n }\n if (type.isUnionType()) {\n return this.union(type, path);\n }\n if (flags & f.TypeParameter) {\n return {};\n }\n if (flags & (f.Object | f.Intersection)) {\n // An array is a container rather than a type that can contain\n // itself: `readonly Row[]` in a view and in `Row.children` is one\n // type, and treating it as named would put the array in `$defs`\n // instead of the row.\n if (this.checker.isArrayType(type)) {\n const [element] = this.checker.getTypeArguments(type as TsApi.TypeReference);\n return { type: 'array', items: element === undefined ? {} : this.describe(element, `${path}[]`) };\n }\n if (this.checker.isTupleType(type)) {\n return this.tuple(type as TsApi.TypeReference, path);\n }\n return this.named(type, path);\n }\n this.warnings.push(`${path} has a type this reader does not describe, so it is left open.`);\n return {};\n }\n\n /**\n * A union, with `undefined` taken out (optionality is said by\n * `required`), `true | false` folded back into `boolean`, and a union\n * of nothing but literals written as an `enum`.\n */\n private union(type: TsApi.UnionType, path: string): JsonSchema {\n const f = this.flags;\n let members = (type.getTypes() ?? []).filter(member => (member.flags & (f.Undefined | f.Void)) === 0);\n if (this.mode === 'component' && members.some(member => CHILD.has(member.getSymbol()?.name ?? ''))) {\n return { 'x-gesso-child': true };\n }\n const booleans = members.filter(member => member.flags & f.BooleanLiteral);\n const schemas: JsonSchema[] = [];\n if (booleans.length === 2) {\n members = members.filter(member => !(member.flags & f.BooleanLiteral));\n schemas.push({ type: 'boolean' });\n }\n if (\n members.length > 0 &&\n schemas.length === 0 &&\n members.every(member => member.flags & (f.StringLiteral | f.NumberLiteral))\n ) {\n return { enum: members.map(member => (member as TsApi.LiteralType).value) };\n }\n // `null` last, so `string | null` reads as a string that may be absent.\n members.sort((a, b) => Number((a.flags & f.Null) !== 0) - Number((b.flags & f.Null) !== 0));\n schemas.push(...members.map(member => this.describe(member, path)));\n return schemas.length === 1 ? schemas[0] : { anyOf: schemas };\n }\n\n /**\n * An object type, described once however often it appears, and by\n * reference when it contains itself.\n */\n private named(type: TsApi.Type, path: string): JsonSchema {\n const id = type.id;\n if (this.inProgress.has(id)) {\n this.recursive.add(id);\n return { $ref: `#/$defs/${this.defName(type)}` };\n }\n this.inProgress.add(id);\n const schema = this.object(type, path);\n this.inProgress.delete(id);\n if (this.recursive.has(id)) {\n const name = this.defName(type);\n this.defs.set(name, schema);\n return { $ref: `#/$defs/${name}` };\n }\n return schema;\n }\n\n private defName(type: TsApi.Type): string {\n let name = this.defNames.get(type.id);\n if (name === undefined) {\n const base = (type.getAliasSymbol() ?? type.getSymbol())?.name ?? 'Type';\n const taken = new Set(this.defNames.values());\n name = taken.has(base) ? `${base}${type.id}` : base;\n this.defNames.set(type.id, name);\n }\n return name;\n }\n\n private object(type: TsApi.Type, path: string): JsonSchema {\n const checker = this.checker;\n const symbolName = type.getSymbol()?.name;\n if (this.mode === 'component') {\n if (symbolName !== undefined && CHILD.has(symbolName)) {\n return { 'x-gesso-child': true };\n }\n if (checker.getSignaturesOfType(type, this.ts.SignatureKind.Call).length > 0) {\n return { 'x-gesso-callback': true };\n }\n // A generic class's instance, `Observable<string>`, is a reference\n // to the class rather than the class, so it is the symbol that\n // says what it is.\n if (\n (symbolName !== undefined && NOT_PLAIN.has(symbolName)) ||\n ((type.getSymbol()?.flags ?? 0) & this.ts.SymbolFlags.Class) !== 0\n ) {\n // Something only code can make, such as a `UiColor`: an agent\n // has the type's name to go on and nothing to fill in.\n return { 'x-gesso-type': checker.typeToString(type) };\n }\n }\n if (symbolName !== undefined && this.inCommand && BINARY.has(symbolName)) {\n return { type: 'string', contentEncoding: 'base64', 'x-gesso-binary': symbolName };\n }\n // Shared memory may sit in a view: it compares by identity, which is\n // right for a handle on memory, and an agent is shown its size (see\n // `describeSharedMemory`). A command cannot take it from an agent,\n // which has no memory to share, so there it stays unplain.\n if (symbolName === 'SharedArrayBuffer' && !this.inCommand) {\n return { type: 'string', description: 'Shared memory, shown as its size.', 'x-gesso-shared': true };\n }\n if (symbolName !== undefined && NOT_PLAIN.has(symbolName)) {\n return this.unplain(path, `a ${symbolName}`);\n }\n if ((type as TsApi.ObjectType).objectFlags & this.ts.ObjectFlags.Class) {\n return this.unplain(path, `an instance of ${symbolName ?? 'a class'}`);\n }\n if (checker.getSignaturesOfType(type, this.ts.SignatureKind.Call).length > 0) {\n return this.unplain(path, 'a function');\n }\n\n const properties: Record<string, JsonSchema> = {};\n const required: string[] = [];\n for (const property of checker.getPropertiesOfType(type)) {\n const propertyType = checker.getTypeOfSymbol(property);\n const at = `${path}.${property.name}`;\n if (propertyType === undefined) {\n properties[property.name] = {};\n continue;\n }\n const schema = this.describe(propertyType, at);\n const description = checker.getDocumentationCommentOfSymbol(property);\n properties[property.name] = description === '' ? schema : { ...schema, description };\n const optional = (property.flags & this.ts.SymbolFlags.Optional) !== 0 || this.includesUndefined(propertyType);\n if (!optional) {\n required.push(property.name);\n }\n }\n const schema: JsonSchema = { type: 'object', properties };\n if (required.length > 0) {\n schema.required = required;\n }\n const index = checker.getIndexInfosOfType(type).find(info => info.keyType.flags & this.flags.String);\n if (index !== undefined) {\n schema.additionalProperties = this.describe(index.valueType, `${path}[key]`);\n }\n return schema;\n }\n\n private tuple(type: TsApi.TypeReference, path: string): JsonSchema {\n const elements = this.checker.getTypeArguments(type);\n const elementFlags = (type.getTarget() as TsApi.TupleType).elementFlags ?? [];\n const { Required, Rest } = this.ts.ElementFlags;\n const prefixItems: JsonSchema[] = [];\n let items: JsonSchema | false = false;\n let minItems = 0;\n elements.forEach((element, index) => {\n const schema = this.describe(element, `${path}[${index}]`);\n if (elementFlags[index] & Rest) {\n items = schema;\n } else {\n prefixItems.push(schema);\n if (elementFlags[index] & Required) {\n minItems = index + 1;\n }\n }\n });\n return { type: 'array', prefixItems, items, minItems };\n }\n\n private unplain(path: string, what: string): JsonSchema {\n if (this.mode === 'component') {\n // Plain data is a channel's rule; see the class's comment.\n return {};\n }\n this.warnings.push(`${path} is ${what}, which is not plain data and cannot cross a channel.`);\n return {};\n }\n}\n\n/**\n * A `@default` tag's value: JSON when it is JSON (`99`, `'neutral'`\n * written with double quotes, `true`), a single-quoted string unquoted,\n * and otherwise the text as written.\n */\nfunction parseDefault(text: string): unknown {\n const trimmed = text.trim();\n try {\n return JSON.parse(trimmed);\n } catch {\n const quoted = /^'(.*)'$/.exec(trimmed) ?? /^`(.*)`$/.exec(trimmed);\n return quoted === null ? trimmed : quoted[1];\n }\n}\n\n/** Whether a schema is as short as a reference to it would be. */\nfunction isSmall(schema: JsonSchema): boolean {\n if (schema.enum !== undefined || schema.const !== undefined) {\n return true;\n }\n const keys = Object.keys(schema);\n return (\n keys.length === 0 || (keys.length === 1 && (typeof schema.type === 'string' || keys[0]!.startsWith('x-gesso-')))\n );\n}\n","#!/usr/bin/env node\n/**\n * `gesso-channels`: describes channel contracts for a bundle Vite does\n * not build.\n *\n * gesso-channels src/shared/Counter.ts --out src/shared/channels.described.ts\n * gesso-channels src/shared/*.ts --out src/shared/channels.described.ts --check\n *\n * `gesso-vite-plugin` describes a contract as Vite bundles it. An\n * Electrobun main process is bundled by Electrobun's own build, which\n * takes no plugins, so its channels would reach an agent undescribed:\n * no descriptions, and arguments as a positional list. This reads the\n * same contracts with the same checker and writes a module that\n * describes them when imported; the main process imports it once.\n *\n * `--check` writes nothing and fails when the module on disk is not\n * what it would write, for a gate that wants to know a contract\n * changed without the module following.\n *\n * It needs TypeScript 7 in the project, whose checker does the reading.\n */\nimport { existsSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\n\nimport { ContractReader, describedModule, type TypeScriptApi } from '../src/contracts.ts';\n\nconst USAGE = `Usage: gesso-channels <contract.ts>... --out <file> [--check]\n\nWrites a module that attaches a JSON Schema to every channel the\ncontracts export, for a bundle gesso-vite-plugin does not build, such as\nan Electrobun main process. Import the module once from that bundle.\n\n --out <file> Where to write the module.\n --check Write nothing; fail if the module is out of date.\n`;\n\nfunction fail(message: string): never {\n console.error(message);\n process.exit(1);\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n const files: string[] = [];\n let out: string | undefined;\n let check = false;\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === '--out') {\n out = args[++i];\n } else if (arg === '--check') {\n check = true;\n } else if (arg === '-h' || arg === '--help') {\n console.log(USAGE);\n return;\n } else if (arg.startsWith('-')) {\n fail(`Unknown option ${arg}.\\n\\n${USAGE}`);\n } else {\n files.push(arg);\n }\n }\n if (out === undefined || files.length === 0) {\n fail(USAGE);\n }\n\n let ts: TypeScriptApi;\n try {\n ts = (await import('typescript/unstable/sync')) as TypeScriptApi;\n } catch {\n fail('gesso-channels needs TypeScript 7 or later in the project: npm install --save-dev typescript@^7.');\n }\n\n // Real paths, because the checker knows files by where they really\n // are, and a symlinked temporary directory would otherwise read as a\n // file no project includes.\n const cwd = realpathSync(process.cwd());\n const outFile = resolve(realpathSync(dirname(resolve(cwd, out))), out.split(/[\\\\/]/).pop()!);\n const reader = new ContractReader(ts, cwd);\n const contracts = [];\n let warned = false;\n try {\n for (const file of files) {\n const path = realpathSync(resolve(cwd, file));\n const reading = reader.read(path);\n for (const warning of reading.warnings) {\n console.warn(`gesso-channels: ${warning}`);\n warned = true;\n }\n contracts.push({ file: path, channels: reading.channels });\n }\n } finally {\n reader.close();\n }\n\n const described = contracts.reduce((count, contract) => count + contract.channels.size, 0);\n if (described === 0) {\n fail(`None of ${files.join(', ')} exports a channel.`);\n }\n const module = describedModule(contracts, outFile);\n if (check) {\n const current = existsSync(outFile) ? readFileSync(outFile, 'utf8') : null;\n if (current !== module) {\n fail(`${out} is out of date. Run gesso-channels without --check to write it.`);\n }\n console.log(`${out} describes ${described} channel${described === 1 ? '' : 's'} and is current.`);\n return;\n }\n writeFileSync(outFile, module);\n console.log(\n `Wrote ${out}: ${described} channel${described === 1 ? '' : 's'} described${warned ? ', with warnings above' : ''}.`\n );\n}\n\nvoid main();\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,117 +1,5 @@
|
|
|
1
|
+
import { c as ComponentCatalogOptions, d as ComponentSchemaOut, f as ContractReader, h as describedModule, i as CatalogLibrary, l as mayDeclareComponent, p as ContractReading, r as CatalogEntry, s as ComponentCatalog, u as ComponentReading } from "./catalog-CbeOrdxD.js";
|
|
1
2
|
import { Plugin } from "vite";
|
|
2
|
-
import * as TsApi from "typescript/unstable/sync";
|
|
3
|
-
//#region src/contracts.d.ts
|
|
4
|
-
/**
|
|
5
|
-
* Channel contracts, read by the type checker and written back as JSON
|
|
6
|
-
* Schema.
|
|
7
|
-
*
|
|
8
|
-
* A contract module already says everything an agent driving the
|
|
9
|
-
* application would need to know: the view's keys and their types, each
|
|
10
|
-
* command's parameters and their types, and, where the author wrote
|
|
11
|
-
* them, what each one is for. All of it is types and comments, and
|
|
12
|
-
* both are gone by the time the module runs. This puts them back, as a
|
|
13
|
-
* `describeChannel(token, schema)` call appended to the module, so the
|
|
14
|
-
* token carries its own description wherever it is imported.
|
|
15
|
-
*
|
|
16
|
-
* The checker rather than the syntax, because a contract names its
|
|
17
|
-
* types where it likes: `ProductRow` from another file, a status union
|
|
18
|
-
* behind an alias, a view written as an object literal whose type is
|
|
19
|
-
* inferred. Only the checker resolves all of those the way the compiler
|
|
20
|
-
* that checked the application did. TypeScript 7's checker runs in a
|
|
21
|
-
* separate process behind an API its package calls unstable, so all of
|
|
22
|
-
* it is in this file, and the rest of the plugin sees one function.
|
|
23
|
-
*
|
|
24
|
-
* What the comments carry:
|
|
25
|
-
*
|
|
26
|
-
* - The JSDoc on the exported token is the channel's description, on
|
|
27
|
-
* a view key is that key's, and on a command is the command's.
|
|
28
|
-
* - `@param name text` describes a parameter.
|
|
29
|
-
* - `@destructive`, `@idempotent`, `@confirm` and `@hidden` on a
|
|
30
|
-
* command are its annotations; `CommandSchema` says what each means.
|
|
31
|
-
* - `@hidden` on a view key keeps that key from an agent; see
|
|
32
|
-
* `ChannelSchema.hidden`. On the token, it keeps the whole channel
|
|
33
|
-
* from one; see `ChannelSchema.internal`.
|
|
34
|
-
*
|
|
35
|
-
* And what it will not describe: a value that is not plain data, which
|
|
36
|
-
* cannot cross a channel anyway. A function, a `Date`, a `Map`, a class
|
|
37
|
-
* instance. Each is reported, by its path in the contract, and
|
|
38
|
-
* described as `{}` so the rest of the schema still stands.
|
|
39
|
-
*/
|
|
40
|
-
/** The parts of `typescript/unstable/sync` this file uses, values included. */
|
|
41
|
-
type TypeScriptApi = typeof TsApi;
|
|
42
|
-
/** A channel's description, in the shape `describeChannel` takes. */
|
|
43
|
-
interface ChannelSchemaOut {
|
|
44
|
-
description?: string;
|
|
45
|
-
internal?: true;
|
|
46
|
-
view: JsonSchema;
|
|
47
|
-
hidden?: string[];
|
|
48
|
-
commands: Record<string, CommandSchemaOut>;
|
|
49
|
-
}
|
|
50
|
-
interface CommandSchemaOut {
|
|
51
|
-
description?: string;
|
|
52
|
-
parameters: string[];
|
|
53
|
-
rest?: true;
|
|
54
|
-
input: JsonSchema;
|
|
55
|
-
destructive?: true;
|
|
56
|
-
idempotent?: true;
|
|
57
|
-
confirm?: true;
|
|
58
|
-
hidden?: true;
|
|
59
|
-
}
|
|
60
|
-
type JsonSchema = Record<string, unknown>;
|
|
61
|
-
/** Every channel a module exports, and what could not be described. */
|
|
62
|
-
interface ContractReading {
|
|
63
|
-
/** Export name to schema, in declaration order. */
|
|
64
|
-
readonly channels: ReadonlyMap<string, ChannelSchemaOut>;
|
|
65
|
-
/** One sentence per value that could not be described, naming where it is. */
|
|
66
|
-
readonly warnings: readonly string[];
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* A module that describes channels when it is imported, for a bundle
|
|
70
|
-
* this plugin never sees.
|
|
71
|
-
*
|
|
72
|
-
* The plugin describes a contract by appending to it as Vite bundles
|
|
73
|
-
* it. An Electrobun main process is bundled by Electrobun's own build,
|
|
74
|
-
* which takes no plugins, so there the description is written ahead of
|
|
75
|
-
* time instead: `gesso-channels` reads the contracts with the same
|
|
76
|
-
* checker and writes this module, and the main process imports it once.
|
|
77
|
-
* A token is imported from its contract, so the schema lands on the
|
|
78
|
-
* same object the application serves.
|
|
79
|
-
*
|
|
80
|
-
* `outFile` and every `file` are absolute POSIX paths.
|
|
81
|
-
*/
|
|
82
|
-
declare function describedModule(contracts: readonly {
|
|
83
|
-
readonly file: string;
|
|
84
|
-
readonly channels: ReadonlyMap<string, ChannelSchemaOut>;
|
|
85
|
-
}[], outFile: string): string;
|
|
86
|
-
/**
|
|
87
|
-
* The checker, held open across reads.
|
|
88
|
-
*
|
|
89
|
-
* Spawning it and loading a project costs about a hundred
|
|
90
|
-
* milliseconds; a question after that costs well under one. So one
|
|
91
|
-
* reader lives as long as the dev server or the build, is told which
|
|
92
|
-
* files changed between reads, and is closed at the end, because the
|
|
93
|
-
* process it holds would otherwise keep the build from exiting.
|
|
94
|
-
*/
|
|
95
|
-
declare class ContractReader {
|
|
96
|
-
private api;
|
|
97
|
-
private readonly changed;
|
|
98
|
-
/** Each contract's tsconfig, found once. */
|
|
99
|
-
private readonly configs;
|
|
100
|
-
/**
|
|
101
|
-
* @param fs Callbacks the checker reads through before the disk, for
|
|
102
|
-
* a spec that keeps its project in memory.
|
|
103
|
-
*/
|
|
104
|
-
private readonly ts;
|
|
105
|
-
private readonly cwd;
|
|
106
|
-
private readonly fs;
|
|
107
|
-
constructor(ts: TypeScriptApi, cwd: string, fs?: TsApi.APIOptions['fs']);
|
|
108
|
-
/** A file changed on disk since the last read. */
|
|
109
|
-
invalidate(file: string): void;
|
|
110
|
-
/** Every channel `file` exports, described. */
|
|
111
|
-
read(file: string): ContractReading;
|
|
112
|
-
close(): void;
|
|
113
|
-
}
|
|
114
|
-
//#endregion
|
|
115
3
|
//#region src/render.d.ts
|
|
116
4
|
/** What the plugin found in a render worker entry, and could wire. */
|
|
117
5
|
interface RenderWiring {
|
|
@@ -390,6 +278,46 @@ interface GessoPluginOptions {
|
|
|
390
278
|
* (default true). Development only: a build carries none of it.
|
|
391
279
|
*/
|
|
392
280
|
readonly agent?: boolean;
|
|
281
|
+
/**
|
|
282
|
+
* The name an agent lists the endpoint under, and so the prefix of
|
|
283
|
+
* its tools' names in most agents (`mcp__my-app__…`): the lines the
|
|
284
|
+
* dev server prints to connect one use it. Default the root folder's
|
|
285
|
+
* name.
|
|
286
|
+
*/
|
|
287
|
+
readonly agentName?: string;
|
|
288
|
+
/**
|
|
289
|
+
* What an agent is told when it connects, after what the catalog
|
|
290
|
+
* says: how this application is meant to be worked with, for one
|
|
291
|
+
* whose tools are best used in an order.
|
|
292
|
+
*/
|
|
293
|
+
readonly agentInstructions?: string;
|
|
294
|
+
/**
|
|
295
|
+
* Whether an agent's tool call may go ahead: undefined to let it, or
|
|
296
|
+
* what the agent is told instead. Checked on the server for every
|
|
297
|
+
* call, the page's tools and the catalog's alike.
|
|
298
|
+
*/
|
|
299
|
+
readonly agentGate?: (tool: string, args: Readonly<Record<string, unknown>> | undefined) => string | undefined;
|
|
300
|
+
/**
|
|
301
|
+
* Offer the project's components to an agent over the same endpoint,
|
|
302
|
+
* as `library_overview`, `library_search` and `library_get` (default
|
|
303
|
+
* true while `agent` is on). The components are the project's own,
|
|
304
|
+
* read from the files its tsconfig includes; name a library in
|
|
305
|
+
* `libraries` to offer its components too, such as `gesso-components`.
|
|
306
|
+
*/
|
|
307
|
+
readonly catalog?: boolean | {
|
|
308
|
+
/**
|
|
309
|
+
* The project whose components these are, absolute (default
|
|
310
|
+
* Vite's root). An application that designs another, such as a
|
|
311
|
+
* design editor serving a project from its own folder, names it.
|
|
312
|
+
*/
|
|
313
|
+
readonly root?: string;
|
|
314
|
+
/** The tsconfig whose files are the project's source (default `tsconfig.json` in `root`). */
|
|
315
|
+
readonly tsconfig?: string;
|
|
316
|
+
/** Packages whose components join the project's own, by the name they are imported by. */
|
|
317
|
+
readonly libraries?: readonly string[];
|
|
318
|
+
/** Folders under the root that are not the project's components nor uses of them: generated code, say. */
|
|
319
|
+
readonly exclude?: readonly string[];
|
|
320
|
+
};
|
|
393
321
|
}
|
|
394
322
|
/**
|
|
395
323
|
* The export conditions a Gesso application resolves its dependencies
|
|
@@ -411,5 +339,5 @@ interface GessoPluginOptions {
|
|
|
411
339
|
declare const workerConditions: readonly string[];
|
|
412
340
|
declare function gesso(options?: GessoPluginOptions): Plugin;
|
|
413
341
|
//#endregion
|
|
414
|
-
export { type CallSite, ContractReader, type ContractReading, GessoPluginOptions, type RenderWiring, type ShellCall, type ShellTransformOptions, type WorkerEntries, blankLiterals, gesso as default, gesso, describedModule, findCall, findCalls, findShellCall, firstArgumentName, importSources, transformRenderWorker, transformShell, transformSyncShell, workerConditions };
|
|
342
|
+
export { type CallSite, type CatalogEntry, type CatalogLibrary, ComponentCatalog, type ComponentCatalogOptions, type ComponentReading, type ComponentSchemaOut, ContractReader, type ContractReading, GessoPluginOptions, type RenderWiring, type ShellCall, type ShellTransformOptions, type WorkerEntries, blankLiterals, gesso as default, gesso, describedModule, findCall, findCalls, findShellCall, firstArgumentName, importSources, mayDeclareComponent, transformRenderWorker, transformShell, transformSyncShell, workerConditions };
|
|
415
343
|
//# sourceMappingURL=index.d.ts.map
|