import * as THREADS from 'node:worker_threads'; import { scopeDir, pluginsDir, pluginFile, labelFile } from './scopes.ts'; export { scopeDir, pluginsDir, pluginFile, labelFile }; /** What a host can ask of its plugins: each rule holds on top of the rest. */ export interface PluginRules { /** The sigstore identity plugins must be signed with. */ identity?: string | undefined; /** The sigstore OIDC issuer plugins must be signed through. */ issuer?: string | undefined; /** Attesters that must have vouched for each plugin, as `[kind@]did`. */ attesters?: string[] | undefined; /** How many of them (default: all). */ quorum?: number | undefined; /** DIDs whose bad verdict refuses a plugin. */ block?: string[] | undefined; /** A certificate (PEM text, or a path to one) every plugin's chain must lead to. */ ca?: string | undefined; /** * Plugins this host pins, by package name, to their whole-file hash * (`sha256:`): each must be installed and be exactly those bytes, * and needs no signature or attestation besides. On top of the parts the * host's package.json declares, which are pinned whether this says so or * not. The other rules here are for the plugins it does not name. */ pins?: Record | undefined; } export interface UseOptions { /** * Verify every plugin in the scope before `use()` returns: `true` under * what the runtime and the policy require, or rules of the host's own on * top of those. A verifying runtime verifies whether this is set or not, * and pinned plugins are checked against their pins either way. */ verify?: boolean | PluginRules | undefined; } /** One plugin `use()` refused, and every reason why. */ export interface Refused { /** The name it was installed as. */ name: string; /** The package name inside it. */ package: string; file: string; reasons: string[]; } /** A plugin in a scope, as `list()` tells a host about it. */ export interface PluginInfo { /** * What it was installed as — what `bundle install --for` was given, as * typed — or, for a part of the host or a plugin copied in by hand, its * package name. Only identifies: two plugins can share one. */ name: string; /** The package name inside it: what to import. */ package: string; /** Its archive; for a folder linked for development, the note naming the folder. */ file: string; /** The folder it is loaded from, when it is linked for development. */ link?: string | undefined; /** Its whole package.json, custom fields and entry points included. */ manifest: PackageJson; } export interface PackageJson { name?: unknown; main?: unknown; exports?: unknown; bin?: unknown; type?: unknown; [field: string]: unknown; } interface Plugin { /** The package name inside it: what apps import. */ name: string; /** The name it was installed as — what `bundle install --for` was given — or its package name. */ installed: string; file: string; manifest: PackageJson; /** Linked for development: the folder it is loaded from, as it is, never verified. */ link?: string | undefined; /** Where it is mounted, once it is. */ root?: string | undefined; /** The host module that first imported it: where its unresolved imports are resolved. */ host?: string | undefined; } interface Scope { scope: string; dir: string; plugins: Map; /** The options it was used with, to tell a second `use()` apart. */ options: string; /** What it was used with, for a worker to use it with too. */ verify: boolean | PluginRules | undefined; /** The pins in force, the host's parts included, and who set each. */ pins: Record; } /** * Make one scope's plugins resolvable in this thread. `scope` is the app's * package name (`bled`, `@acme/editor`), whose plugins `bundle install --for` * put in a directory of its own; or an absolute path to a directory of plugins. * * Calling it again for a scope already in use does nothing — unless the * options differ, which is an error rather than a quiet choice between them. * With verification, or pins, every plugin that needs checking is checked * before this returns, and if any is refused none of them is loaded and this * throws, saying why for each. * * When the package that calls this is itself called `scope` and declares parts * in its package.json (`bundle.parts`), they are pinned: see parts.ts. */ export declare function use(scope: string, options?: UseOptions): void; /** * The plugins installed in a scope, sorted by the name each was installed as, * then by package — so a host can tell its user what is installed, read what * its plugins say about themselves, and import each by `package`: * * for (const plugin of list('bled')) console.log(`${plugin.name}: ${plugin.manifest.description}`); * for (const plugin of list('bled')) await import(plugin.package); * * Nothing is verified, mounted or run to answer it. */ export declare function list(scope: string): PluginInfo[]; /** * Run a plugin as the main program: what `ordner-suite run auth` does with * `runPlugin('@ordner.fyi/suite', '@ordner.fyi/auth', argv)`. * * The scope is used first, if it is not yet — so a host's parts are checked * against its pins, as `use()` always does. Then the plugin is mounted, * `process.argv` is made what `--vfs-load` would make it — the plugin's * archive at `argv[1]`, then `argv` — and its entry point is loaded: `main`, * else the `.` of its `exports`, else `index.js`; or, with `bin`, that entry * of its package.json's `bin`. A CommonJS entry is required, an ES module * imported, as node decides between them. What it does from there is its own * business, as it would be run by itself; this returns once it has loaded. * * Inside it, `import.meta.url` (and `__filename`) is the entry's place in the * plugin's mount, `file:///dev/null/vfs//…` — not the archive's path, which * is `process.argv[1]` — so `new URL('./x', import.meta.url)` finds the * plugin's own files, and a worker started from there finds them too. */ export declare function runPlugin(scope: string, pkg: string, argv?: string[], { bin }?: { bin?: string | undefined; }): Promise; /** * `Worker` from `node:worker_threads`, starting a thread that has this * thread's plugins: every scope `use()`d here is used there with the same * options, and every plugin mounted here is mounted there at the same place — * so a URL into a plugin means the same file in both threads, and a pinned or * verified plugin is checked there again before anything loads. Then the * worker's script runs, as it would have. * * Plugins get this one when they import `worker_threads`, so their own * `new Worker(new URL('./worker.js', import.meta.url))` works with nothing * to change. A host that wants its workers to have its plugins imports it * from here. A thread that has used no scope starts an ordinary worker, and * so does `{ eval: true }`, which node runs without the module loader. */ export declare class Worker extends THREADS.Worker { constructor(filename: string | URL, options?: THREADS.WorkerOptions); } /** What a worker started through `Worker` is set up with: this thread's scopes and mounts. */ interface ThreadRecord { /** The worker's own script. */ target: string; scopes: { scope: string; dir: string; verify: boolean | PluginRules | undefined; pins: Scope['pins']; mounts: { name: string; root: string; host?: string | undefined; }[]; }[]; } /** * What a worker started through `Worker` runs first: its parent's scopes, its * parent's mounts at the same places — in the order they were made, the only * order node can give them — and then the worker's own script. Not for hosts * to call. */ export declare function enterWorker(record: ThreadRecord): Promise; //# sourceMappingURL=plugins.d.ts.map