mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 02:17:31 +00:00
* feat(plugins): build-less TS/TSX plugins, typings and authoring tools - Plugin frames transpile .ts/.tsx/.mts/.jsx on load (sucrase on a shared worker thread with a 2 s budget, cached in main), resolve relative imports to exact files, and refuse bare package imports by name. Components and html panel/editor entries may be .ts/.tsx. - orca plugin types writes typings for the components orca module, window.orca, worker activate(orca), host.call and events into .orca/types, from one host-typings generator. - orca plugin create --template components; templates omit publisher and target 1.4.246. - orca plugin release-entry, dev inspect and restart; validate:plugin checks named files. - Guest console errors reach orca plugin logs; dev workers restart on edits; the dev watcher ignores .git, .orca and node_modules; Git and folder installs cannot replace catalog installs. - examples/plugins/hello-tsx, docs, and the plugin-buildless-tsx e2e spec. * ci: run the Windows parity probes with one tsgo checker tsconfig.node peaks near 22 GB with tsgo's default four checkers (each holds its own types) and about 16 GB with one; the 16 GB Windows runner paged the native probe past its 900 s timeout once this branch's package.json change triggered the job. One checker gives the same diagnostics (verified locally with --parity-only); the timing benchmark is unchanged.
128 lines
5.8 KiB
TypeScript
128 lines
5.8 KiB
TypeScript
import { PLUGIN_EVENT_PAYLOAD_SCHEMAS, PLUGIN_EVENT_SUMMARIES } from '../events/plugin-events'
|
|
import { PLUGIN_ERROR_CODES } from '../frames/plugin-protocol'
|
|
import { PLUGIN_EVENT_NAMES } from '../manifest/plugin-manifest'
|
|
import { PLUGIN_HOST_API_MAJOR } from '../workers/plugin-host-api-major'
|
|
import {
|
|
PLUGIN_HOST_API,
|
|
PLUGIN_HOST_API_MINOR,
|
|
PLUGIN_HOST_METHOD_NAMES,
|
|
pluginHostMethodsFor
|
|
} from '../workers/plugin-host-api'
|
|
import { zodTypeText, type SchemaTypeTextOptions } from '../workers/plugin-schema-type-text'
|
|
|
|
/** Checked-in output of `pnpm generate:plugin-author-typings`; `orca plugin types` copies it. */
|
|
export const PLUGIN_HOST_TYPINGS_FILE = 'resources/plugins/types/orca-host.d.ts'
|
|
|
|
/** Names the d.ts must declare beside the generated members, since JSON Schema cannot spell them. */
|
|
export const HOST_TYPE_TEXT: SchemaTypeTextOptions = {
|
|
json: 'JsonValue',
|
|
emptyObject: 'Record<string, never>'
|
|
}
|
|
|
|
const version = (minor: number): string => `${PLUGIN_HOST_API_MAJOR}.${minor}`
|
|
const union = (values: readonly string[]): string =>
|
|
values.map((value) => JSON.stringify(value)).join(' | ')
|
|
|
|
function docComment(indent: string, text: string): string {
|
|
return `${indent}/** ${text.replaceAll('*/', '*\\/')} */\n`
|
|
}
|
|
|
|
/**
|
|
* Members of a `HostMethods` type literal, one per host method:
|
|
* `{ params; result; surfaces }`, with `params` including `undefined` when it may be omitted.
|
|
* The one generator for host-method types: `orca/host` uses every method, and the components
|
|
* `orca` module (plugin-declarative-host-typings.ts) embeds a surface's methods.
|
|
*/
|
|
export function renderPluginHostMethodMembers(
|
|
indent = '',
|
|
options: {
|
|
surface?: Parameters<typeof pluginHostMethodsFor>[0]
|
|
/** One line per method, without `surfaces`: for a module already scoped to one surface. */
|
|
compact?: boolean
|
|
} = {}
|
|
): string {
|
|
const names =
|
|
options.surface === undefined ? PLUGIN_HOST_METHOD_NAMES : pluginHostMethodsFor(options.surface)
|
|
return names
|
|
.map((name) => {
|
|
const spec = PLUGIN_HOST_API[name]
|
|
const params = zodTypeText(spec.params, 'input', HOST_TYPE_TEXT)
|
|
const optional = spec.params.safeParse(undefined).success
|
|
const paramsText = `${params}${optional ? ' | undefined' : ''}`
|
|
const result = zodTypeText(spec.result, 'output', HOST_TYPE_TEXT)
|
|
if (options.compact) {
|
|
return `${docComment(indent, spec.summary)}${indent}${JSON.stringify(name)}: { params: ${paramsText}; result: ${result} }\n`
|
|
}
|
|
const since = `Since host API ${version(spec.since)}.`
|
|
const deprecated =
|
|
spec.deprecatedSince === undefined
|
|
? ''
|
|
: ` @deprecated since ${version(spec.deprecatedSince)}.`
|
|
return [
|
|
docComment(indent, `${spec.summary} ${since}${deprecated}`),
|
|
`${indent}${JSON.stringify(name)}: {\n`,
|
|
`${indent} params: ${paramsText}\n`,
|
|
`${indent} result: ${result}\n`,
|
|
`${indent} surfaces: ${union(spec.surfaces)}\n`,
|
|
`${indent}}\n`
|
|
].join('')
|
|
})
|
|
.join('')
|
|
}
|
|
|
|
/** Members of a `PluginEvents` type literal: event name → payload a worker handler receives. */
|
|
export function renderPluginEventPayloadMembers(indent = ''): string {
|
|
return PLUGIN_EVENT_NAMES.map((name) => {
|
|
const payload = zodTypeText(PLUGIN_EVENT_PAYLOAD_SCHEMAS[name], 'output', HOST_TYPE_TEXT)
|
|
return `${docComment(indent, PLUGIN_EVENT_SUMMARIES[name])}${indent}${JSON.stringify(name)}: ${payload}\n`
|
|
}).join('')
|
|
}
|
|
|
|
/** `declare module 'orca/host'`: the host API, errors and event payloads every surface shares. */
|
|
export function renderPluginHostTypings(): string {
|
|
return `// Generated by \`pnpm generate:plugin-author-typings\` from src/shared/plugins. Do not edit.
|
|
// Host API ${version(PLUGIN_HOST_API_MINOR)} types shared by frames (\`window.orca\`) and workers (\`activate(orca)\`).
|
|
|
|
declare module 'orca/host' {
|
|
export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }
|
|
export type Disposable = { dispose(): void }
|
|
/** \`orca.api\`: check \`minor\` (or \`host.has\`) before calling a method newer than your oldest Orca. */
|
|
export type HostApiVersion = { readonly major: number; readonly minor: number }
|
|
/** Open set: treat a code you do not know as \`action_failed\`. */
|
|
export type HostErrorCode = ${union(PLUGIN_ERROR_CODES)}
|
|
/** What a failed call rejects with; \`retryAfterMs\` is set for \`rate_limited\`. */
|
|
export type HostCallError = Error & {
|
|
code: HostErrorCode
|
|
retryable: boolean
|
|
retryAfterMs?: number
|
|
}
|
|
export type HostCallOptions = { signal?: AbortSignal }
|
|
export type HostSurface = 'worker' | 'panel' | 'editor'
|
|
|
|
export type HostMethods = {
|
|
${renderPluginHostMethodMembers(' ')} }
|
|
export type HostMethodName = keyof HostMethods
|
|
/** Methods a surface may call; any other gets \`not_permitted\`. */
|
|
export type HostMethodFor<S extends HostSurface> = {
|
|
[M in HostMethodName]: S extends HostMethods[M]['surfaces'] ? M : never
|
|
}[HostMethodName]
|
|
export type HostParams<M extends HostMethodName> = HostMethods[M]['params']
|
|
export type HostResult<M extends HostMethodName> = HostMethods[M]['result']
|
|
export type HostCallArgs<M extends HostMethodName> = undefined extends HostParams<M>
|
|
? [params?: HostParams<M>, options?: HostCallOptions]
|
|
: [params: HostParams<M>, options?: HostCallOptions]
|
|
export type HostApi<S extends HostSurface> = {
|
|
/** Rejects with a {@link HostCallError}. */
|
|
call<M extends HostMethodFor<S>>(method: M, ...args: HostCallArgs<M>): Promise<HostResult<M>>
|
|
/** Synchronous: whether this Orca offers \`method\` on this surface. */
|
|
has(method: string): boolean
|
|
}
|
|
|
|
/** Worker \`orca.events.on(event, handler)\` payloads. */
|
|
export type PluginEvents = {
|
|
${renderPluginEventPayloadMembers(' ')} }
|
|
export type PluginEventName = keyof PluginEvents
|
|
}
|
|
`
|
|
}
|