Skip to content
← Public packages

@kentcdodds/raycast

Raycast launcher: stored execute commands via webhooks (canonical; do not use raycast-shortcuts).

src/invoke.ts

68 lines · 2.2 KB · TypeScript
import getPackage from './get-package.ts'

export type InvokeInput = {
	kodyId?: string
	packageId?: string
	exportName: string
	params?: Record<string, unknown>
	/**
	 * Deprecated: packages.invoke sunset (#1750 / codemod 0008). Exactly-once
	 * is not available on this lean path — use a workflow. The key is ignored.
	 */
	idempotencyKey?: string
	/** Deprecated: ignored after packages.invoke sunset. */
	topic?: string
}

/**
 * Invoke one of the caller's saved package exports from an external client.
 *
 * HTTP callers authenticate against this `raycast` package, then pass the
 * target `kodyId` (or `packageId`) and `exportName` here. The target loads via
 * computed `import(specifier)` (name is data).
 *
 * @param params.kodyId - Target package kody.id (preferred).
 * @param params.packageId - Target saved package id when kodyId is omitted.
 * @param params.exportName - Export to run (`./post-message`, `post-message`, or `.`).
 * @param params.params - JSON object passed to the target export.
 * @returns The target export's unwrapped return value.
 *
 * @example
 * import invoke from 'kody:@kentcdodds/raycast/invoke'
 * const result = await invoke({
 *   kodyId: 'discord',
 *   exportName: './post-message',
 *   params: { content: 'hello' },
 * })
 */
export default async function invoke(input: InvokeInput) {
	const exportName = input.exportName?.trim()
	if (!exportName) {
		throw new Error('exportName is required.')
	}
	// idempotencyKey / topic intentionally ignored (no packages.invoke; no workflow).
	void input.idempotencyKey
	void input.topic
	const target = await getPackage({
		kodyId: input.kodyId,
		packageId: input.packageId,
	})
	const matched = target.exports.find((entry) => {
		return (
			entry.subpath === exportName ||
			entry.exportName === exportName.replace(/^\.\//, '') ||
			(exportName === '.' && entry.subpath === '.')
		)
	})
	if (!matched) {
		throw new Error(`Package ${target.name} does not export ${exportName}.`)
	}
	const raw = matched.importSpecifier
	const trimmed = typeof raw === 'string' ? raw.trim() : ''
	const specifier =
		typeof raw === 'string' && trimmed.startsWith('@')
			? `kody:${trimmed}`
			: trimmed
	const mod = await import(specifier)
	return await mod.default(input.params)
}