@useatlas/plugin-sdk
Advanced tools
| /** | ||
| * Testing utilities for Atlas plugin authors. | ||
| * | ||
| * Provides mock factories for AtlasPluginContext, PluginDBConnection, and | ||
| * PluginExploreBackend so plugin tests can avoid duplicating boilerplate. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { createMockContext } from "@useatlas/plugin-sdk/testing"; | ||
| * | ||
| * const { ctx, logs, registeredTools } = createMockContext(); | ||
| * await plugin.initialize(ctx); | ||
| * expect(logs.some(l => l.msg.includes("initialized"))).toBe(true); | ||
| * ``` | ||
| */ | ||
| import type { AtlasPluginContext, PluginDBConnection, PluginExploreBackend, PluginExecResult, PluginLogger, PluginQueryResult } from "./types"; | ||
| export interface CapturedLog { | ||
| level: keyof PluginLogger; | ||
| msg: string; | ||
| obj?: Record<string, unknown>; | ||
| } | ||
| /** | ||
| * Create a PluginLogger that captures all log calls to an array. | ||
| * Optionally pass an existing array to append to (useful for sharing | ||
| * a log sink across multiple loggers). | ||
| */ | ||
| export declare function createMockLogger(logs?: CapturedLog[]): { | ||
| logger: PluginLogger; | ||
| logs: CapturedLog[]; | ||
| }; | ||
| export interface MockConnectionOptions { | ||
| /** The result to return from query(). Can be overridden later via mockQueryResult(). */ | ||
| queryResult?: PluginQueryResult; | ||
| /** If set, query() rejects with this error. Resets after one throw. */ | ||
| queryError?: Error; | ||
| } | ||
| export interface MockConnection extends PluginDBConnection { | ||
| /** All calls made to query(), in order (read-only — populated by query()). */ | ||
| readonly queryCalls: ReadonlyArray<{ | ||
| sql: string; | ||
| timeoutMs?: number; | ||
| }>; | ||
| /** Whether close() has been called. */ | ||
| readonly closed: boolean; | ||
| /** Set the query result for all subsequent calls. Also clears any pending error. */ | ||
| mockQueryResult(result: PluginQueryResult): void; | ||
| /** Make the next query() call reject with this error. Resets after one throw. */ | ||
| mockQueryError(error: Error): void; | ||
| } | ||
| /** | ||
| * Create a mock PluginDBConnection with configurable query results | ||
| * and call tracking. | ||
| */ | ||
| export declare function createMockConnection(opts?: MockConnectionOptions): MockConnection; | ||
| export interface MockExploreBackendOptions { | ||
| /** The result to return from exec(). Can be overridden later via mockExecResult(). */ | ||
| execResult?: PluginExecResult; | ||
| /** If set, exec() rejects with this error. Resets after one throw. */ | ||
| execError?: Error; | ||
| } | ||
| export interface MockExploreBackend extends PluginExploreBackend { | ||
| /** All commands passed to exec(), in order (read-only — populated by exec()). */ | ||
| readonly execCalls: ReadonlyArray<string>; | ||
| /** Whether close() has been called. */ | ||
| readonly closed: boolean; | ||
| /** Set the exec result for all subsequent calls. Also clears any pending error. */ | ||
| mockExecResult(result: PluginExecResult): void; | ||
| /** Make the next exec() call reject with this error. Resets after one throw. */ | ||
| mockExecError(error: Error): void; | ||
| } | ||
| /** | ||
| * Create a mock PluginExploreBackend with configurable exec results | ||
| * and call tracking. | ||
| */ | ||
| export declare function createMockExploreBackend(opts?: MockExploreBackendOptions): MockExploreBackend; | ||
| export interface MockContextOverrides { | ||
| /** Override internal db. Pass `null` to simulate no DATABASE_URL. */ | ||
| db?: AtlasPluginContext["db"] | null; | ||
| /** Override the connections registry. */ | ||
| connections?: Partial<AtlasPluginContext["connections"]>; | ||
| /** Override the tools registry. */ | ||
| tools?: Partial<AtlasPluginContext["tools"]>; | ||
| /** Override the logger. */ | ||
| logger?: PluginLogger; | ||
| /** Override the config record. */ | ||
| config?: Record<string, unknown>; | ||
| } | ||
| export interface RegisteredTool { | ||
| name: string; | ||
| description: string; | ||
| tool: unknown; | ||
| } | ||
| export interface MockContextResult { | ||
| ctx: AtlasPluginContext; | ||
| /** All log entries captured by the mock logger. */ | ||
| logs: CapturedLog[]; | ||
| /** All tools registered via ctx.tools.register(). */ | ||
| registeredTools: RegisteredTool[]; | ||
| } | ||
| /** | ||
| * Create a mock AtlasPluginContext with sensible defaults. | ||
| * All fields can be overridden. | ||
| * | ||
| * Returns the context along with captured log entries and registered tools | ||
| * for easy assertions. Note: when `logger` or `tools` are overridden, the | ||
| * returned `logs` and `registeredTools` arrays are not connected to the | ||
| * overridden implementations. | ||
| */ | ||
| export declare function createMockContext(overrides?: MockContextOverrides): MockContextResult; |
+119
| // src/testing.ts | ||
| function createMockLogger(logs) { | ||
| const captured = logs ?? []; | ||
| function makeHandler(level) { | ||
| return (...args) => { | ||
| if (typeof args[0] === "string") { | ||
| captured.push({ level, msg: args[0] }); | ||
| } else if (typeof args[0] === "object" && args[0] !== null) { | ||
| captured.push({ | ||
| level, | ||
| obj: args[0], | ||
| msg: typeof args[1] === "string" ? args[1] : "" | ||
| }); | ||
| } else { | ||
| captured.push({ level, msg: String(args[0]) }); | ||
| } | ||
| }; | ||
| } | ||
| const logger = { | ||
| info: makeHandler("info"), | ||
| warn: makeHandler("warn"), | ||
| error: makeHandler("error"), | ||
| debug: makeHandler("debug") | ||
| }; | ||
| return { logger, logs: captured }; | ||
| } | ||
| function createMockConnection(opts = {}) { | ||
| let nextResult = opts.queryResult ?? { | ||
| columns: [], | ||
| rows: [] | ||
| }; | ||
| let nextError = opts.queryError; | ||
| const queryCalls = []; | ||
| const conn = { | ||
| queryCalls, | ||
| closed: false, | ||
| async query(sql, timeoutMs) { | ||
| queryCalls.push({ sql, timeoutMs }); | ||
| if (nextError) { | ||
| const err = nextError; | ||
| nextError = undefined; | ||
| throw err; | ||
| } | ||
| return nextResult; | ||
| }, | ||
| async close() { | ||
| conn.closed = true; | ||
| }, | ||
| mockQueryResult(result) { | ||
| nextResult = result; | ||
| nextError = undefined; | ||
| }, | ||
| mockQueryError(error) { | ||
| nextError = error; | ||
| } | ||
| }; | ||
| return conn; | ||
| } | ||
| function createMockExploreBackend(opts = {}) { | ||
| let nextResult = opts.execResult ?? { | ||
| stdout: "", | ||
| stderr: "", | ||
| exitCode: 0 | ||
| }; | ||
| let nextError = opts.execError; | ||
| const execCalls = []; | ||
| const backend = { | ||
| execCalls, | ||
| closed: false, | ||
| async exec(command) { | ||
| execCalls.push(command); | ||
| if (nextError) { | ||
| const err = nextError; | ||
| nextError = undefined; | ||
| throw err; | ||
| } | ||
| return nextResult; | ||
| }, | ||
| async close() { | ||
| backend.closed = true; | ||
| }, | ||
| mockExecResult(result) { | ||
| nextResult = result; | ||
| nextError = undefined; | ||
| }, | ||
| mockExecError(error) { | ||
| nextError = error; | ||
| } | ||
| }; | ||
| return backend; | ||
| } | ||
| function createMockContext(overrides = {}) { | ||
| const logs = []; | ||
| const registeredTools = []; | ||
| const { logger } = createMockLogger(logs); | ||
| const ctx = { | ||
| db: overrides.db ?? null, | ||
| connections: { | ||
| get: overrides.connections?.get ?? ((id) => { | ||
| throw new Error(`No connection "${id}" registered in mock context. ` + `Pass a connections override to createMockContext() to provide one.`); | ||
| }), | ||
| list: overrides.connections?.list ?? (() => []) | ||
| }, | ||
| tools: { | ||
| register: overrides.tools?.register ?? ((tool) => { | ||
| registeredTools.push(tool); | ||
| }) | ||
| }, | ||
| logger: overrides.logger ?? logger, | ||
| config: overrides.config ?? {} | ||
| }; | ||
| return { ctx, logs, registeredTools }; | ||
| } | ||
| export { | ||
| createMockLogger, | ||
| createMockExploreBackend, | ||
| createMockContext, | ||
| createMockConnection | ||
| }; |
@@ -17,3 +17,3 @@ /** | ||
| * id: "my-datasource", | ||
| * type: "datasource", | ||
| * types: ["datasource"], | ||
| * version: "1.0.0", | ||
@@ -68,3 +68,3 @@ * connection: { | ||
| * id: "bigquery", | ||
| * type: "datasource" as const, | ||
| * types: ["datasource"] as const, | ||
| * version: "1.0.0", | ||
@@ -71,0 +71,0 @@ * config, |
+30
-12
@@ -10,6 +10,11 @@ // src/helpers.ts | ||
| } | ||
| if (!VALID_TYPES.has(plugin.type)) { | ||
| throw new Error(`Invalid plugin type "${plugin.type}" — must be one of: datasource, context, interaction, action, sandbox`); | ||
| if (!Array.isArray(plugin.types) || plugin.types.length === 0) { | ||
| throw new Error(`Plugin "types" must be a non-empty array of plugin types`); | ||
| } | ||
| if (plugin.type === "datasource") { | ||
| for (const t of plugin.types) { | ||
| if (!VALID_TYPES.has(t)) { | ||
| throw new Error(`Invalid plugin type "${t}" — must be one of: datasource, context, interaction, action, sandbox`); | ||
| } | ||
| } | ||
| if (plugin.types.includes("datasource")) { | ||
| const ds = plugin; | ||
@@ -35,2 +40,15 @@ if (!ds.connection || typeof ds.connection !== "object") { | ||
| } | ||
| if (ds.connection.parserDialect !== undefined && (typeof ds.connection.parserDialect !== "string" || !ds.connection.parserDialect.trim())) { | ||
| throw new Error('Datasource plugin connection "parserDialect" must be a non-empty string'); | ||
| } | ||
| if (ds.connection.forbiddenPatterns !== undefined) { | ||
| if (!Array.isArray(ds.connection.forbiddenPatterns)) { | ||
| throw new Error('Datasource plugin connection "forbiddenPatterns" must be an array of RegExp'); | ||
| } | ||
| for (const p of ds.connection.forbiddenPatterns) { | ||
| if (!(p instanceof RegExp)) { | ||
| throw new Error('Datasource plugin connection "forbiddenPatterns" entries must each be a RegExp'); | ||
| } | ||
| } | ||
| } | ||
| if (ds.dialect !== undefined && (typeof ds.dialect !== "string" || !ds.dialect.trim())) { | ||
@@ -40,3 +58,3 @@ throw new Error('Datasource plugin "dialect" must be a non-empty string'); | ||
| } | ||
| if (plugin.type === "context") { | ||
| if (plugin.types.includes("context")) { | ||
| const ctx = plugin; | ||
@@ -47,3 +65,3 @@ if (!ctx.contextProvider || typeof ctx.contextProvider !== "object") { | ||
| } | ||
| if (plugin.type === "interaction") { | ||
| if (plugin.types.includes("interaction")) { | ||
| const int = plugin; | ||
@@ -54,3 +72,3 @@ if (int.routes !== undefined && typeof int.routes !== "function") { | ||
| } | ||
| if (plugin.type === "action") { | ||
| if (plugin.types.includes("action")) { | ||
| const act = plugin; | ||
@@ -61,3 +79,3 @@ if (!Array.isArray(act.actions)) { | ||
| } | ||
| if (plugin.type === "sandbox") { | ||
| if (plugin.types.includes("sandbox")) { | ||
| const sb = plugin; | ||
@@ -100,15 +118,15 @@ if (!sb.sandbox || typeof sb.sandbox !== "object") { | ||
| function isDatasourcePlugin(plugin) { | ||
| return plugin.type === "datasource"; | ||
| return plugin.types.includes("datasource"); | ||
| } | ||
| function isContextPlugin(plugin) { | ||
| return plugin.type === "context"; | ||
| return plugin.types.includes("context"); | ||
| } | ||
| function isInteractionPlugin(plugin) { | ||
| return plugin.type === "interaction"; | ||
| return plugin.types.includes("interaction"); | ||
| } | ||
| function isActionPlugin(plugin) { | ||
| return plugin.type === "action"; | ||
| return plugin.types.includes("action"); | ||
| } | ||
| function isSandboxPlugin(plugin) { | ||
| return plugin.type === "sandbox"; | ||
| return plugin.types.includes("sandbox"); | ||
| } | ||
@@ -115,0 +133,0 @@ export { |
+1
-1
| /** | ||
| * @useatlas/plugin-sdk — Public API for authoring Atlas plugins. | ||
| */ | ||
| export type { PluginQueryResult, PluginDBConnection, QueryValidationResult, PluginDBType, PluginType, PluginStatus, PluginHealthResult, PluginLogger, AtlasPluginContext, PluginHookEntry, QueryHookContext, QueryHookMutation, AfterQueryHookContext, ExploreHookContext, ExploreHookMutation, AfterExploreHookContext, RequestHookContext, ResponseHookContext, PluginHooks, PluginFieldDefinition, PluginTableDefinition, AtlasPluginBase, PluginEntity, EntityProvider, AtlasDatasourcePlugin, AtlasContextPlugin, AtlasInteractionPlugin, PluginAction, AtlasActionPlugin, PluginExecResult, PluginExploreBackend, AtlasSandboxPlugin, ActionApprovalMode, AtlasPlugin, $InferServerPlugin, } from "./types"; | ||
| export type { PluginQueryResult, PluginDBConnection, QueryValidationResult, PluginDBType, ParserDialect, PluginType, PluginStatus, PluginHealthResult, PluginLogger, AtlasPluginContext, PluginHookEntry, QueryHookContext, QueryHookMutation, AfterQueryHookContext, ExploreHookContext, ExploreHookMutation, AfterExploreHookContext, RequestHookContext, ResponseHookContext, PluginHooks, PluginFieldDefinition, PluginTableDefinition, AtlasPluginBase, PluginEntity, EntityProvider, AtlasDatasourcePlugin, AtlasContextPlugin, AtlasInteractionPlugin, PluginAction, AtlasActionPlugin, PluginExecResult, PluginExploreBackend, AtlasSandboxPlugin, ActionApprovalMode, AtlasPlugin, $InferServerPlugin, } from "./types"; | ||
| export { SANDBOX_DEFAULT_PRIORITY } from "./types"; | ||
| export { definePlugin, createPlugin, isDatasourcePlugin, isContextPlugin, isInteractionPlugin, isActionPlugin, isSandboxPlugin, } from "./helpers"; | ||
| export type { CreatePluginOptions } from "./helpers"; |
+30
-12
@@ -13,6 +13,11 @@ // src/types.ts | ||
| } | ||
| if (!VALID_TYPES.has(plugin.type)) { | ||
| throw new Error(`Invalid plugin type "${plugin.type}" — must be one of: datasource, context, interaction, action, sandbox`); | ||
| if (!Array.isArray(plugin.types) || plugin.types.length === 0) { | ||
| throw new Error(`Plugin "types" must be a non-empty array of plugin types`); | ||
| } | ||
| if (plugin.type === "datasource") { | ||
| for (const t of plugin.types) { | ||
| if (!VALID_TYPES.has(t)) { | ||
| throw new Error(`Invalid plugin type "${t}" — must be one of: datasource, context, interaction, action, sandbox`); | ||
| } | ||
| } | ||
| if (plugin.types.includes("datasource")) { | ||
| const ds = plugin; | ||
@@ -38,2 +43,15 @@ if (!ds.connection || typeof ds.connection !== "object") { | ||
| } | ||
| if (ds.connection.parserDialect !== undefined && (typeof ds.connection.parserDialect !== "string" || !ds.connection.parserDialect.trim())) { | ||
| throw new Error('Datasource plugin connection "parserDialect" must be a non-empty string'); | ||
| } | ||
| if (ds.connection.forbiddenPatterns !== undefined) { | ||
| if (!Array.isArray(ds.connection.forbiddenPatterns)) { | ||
| throw new Error('Datasource plugin connection "forbiddenPatterns" must be an array of RegExp'); | ||
| } | ||
| for (const p of ds.connection.forbiddenPatterns) { | ||
| if (!(p instanceof RegExp)) { | ||
| throw new Error('Datasource plugin connection "forbiddenPatterns" entries must each be a RegExp'); | ||
| } | ||
| } | ||
| } | ||
| if (ds.dialect !== undefined && (typeof ds.dialect !== "string" || !ds.dialect.trim())) { | ||
@@ -43,3 +61,3 @@ throw new Error('Datasource plugin "dialect" must be a non-empty string'); | ||
| } | ||
| if (plugin.type === "context") { | ||
| if (plugin.types.includes("context")) { | ||
| const ctx = plugin; | ||
@@ -50,3 +68,3 @@ if (!ctx.contextProvider || typeof ctx.contextProvider !== "object") { | ||
| } | ||
| if (plugin.type === "interaction") { | ||
| if (plugin.types.includes("interaction")) { | ||
| const int = plugin; | ||
@@ -57,3 +75,3 @@ if (int.routes !== undefined && typeof int.routes !== "function") { | ||
| } | ||
| if (plugin.type === "action") { | ||
| if (plugin.types.includes("action")) { | ||
| const act = plugin; | ||
@@ -64,3 +82,3 @@ if (!Array.isArray(act.actions)) { | ||
| } | ||
| if (plugin.type === "sandbox") { | ||
| if (plugin.types.includes("sandbox")) { | ||
| const sb = plugin; | ||
@@ -103,15 +121,15 @@ if (!sb.sandbox || typeof sb.sandbox !== "object") { | ||
| function isDatasourcePlugin(plugin) { | ||
| return plugin.type === "datasource"; | ||
| return plugin.types.includes("datasource"); | ||
| } | ||
| function isContextPlugin(plugin) { | ||
| return plugin.type === "context"; | ||
| return plugin.types.includes("context"); | ||
| } | ||
| function isInteractionPlugin(plugin) { | ||
| return plugin.type === "interaction"; | ||
| return plugin.types.includes("interaction"); | ||
| } | ||
| function isActionPlugin(plugin) { | ||
| return plugin.type === "action"; | ||
| return plugin.types.includes("action"); | ||
| } | ||
| function isSandboxPlugin(plugin) { | ||
| return plugin.type === "sandbox"; | ||
| return plugin.types.includes("sandbox"); | ||
| } | ||
@@ -118,0 +136,0 @@ export { |
+42
-12
@@ -36,2 +36,7 @@ /** | ||
| export type PluginDBType = "postgres" | "mysql" | "clickhouse" | "snowflake" | "duckdb" | (string & {}); | ||
| /** | ||
| * Known node-sql-parser dialect strings, plus an escape hatch for future dialects. | ||
| * Values are case-sensitive — use exactly as listed (e.g. "PostgresQL", not "postgresql"). | ||
| */ | ||
| export type ParserDialect = "Athena" | "BigQuery" | "Db2" | "FlinkSQL" | "Hive" | "MariaDb" | "MySQL" | "NoQL" | "PostgresQL" | "Redshift" | "Snowflake" | "SQLite" | "TransactSQL" | "Trino" | (string & {}); | ||
| export type PluginType = "datasource" | "context" | "interaction" | "action" | "sandbox"; | ||
@@ -176,3 +181,4 @@ export type PluginStatus = "registered" | "initializing" | "healthy" | "unhealthy" | "teardown"; | ||
| readonly id: string; | ||
| readonly type: PluginType; | ||
| /** Plugin type(s). A plugin can implement multiple types (e.g. ["interaction", "action"]). */ | ||
| readonly types: readonly PluginType[]; | ||
| /** SemVer version string. */ | ||
@@ -204,3 +210,3 @@ readonly version: string; | ||
| * Declarative table definitions for the internal database. | ||
| * Tables are auto-migrated at boot (migration logic in #151). | ||
| * Tables are auto-migrated at boot (see packages/api/src/lib/plugins/migrate.ts). | ||
| */ | ||
@@ -225,3 +231,2 @@ schema?: Record<string, PluginTableDefinition>; | ||
| export interface AtlasDatasourcePlugin<TConfig = undefined> extends AtlasPluginBase<TConfig> { | ||
| readonly type: "datasource"; | ||
| readonly connection: { | ||
@@ -242,2 +247,31 @@ /** Factory: create a DBConnection for the registry. */ | ||
| validate?(query: string): QueryValidationResult; | ||
| /** | ||
| * node-sql-parser dialect string for SQL validation. When not provided, | ||
| * the core pipeline auto-detects from `dbType`. Override this when `dbType` | ||
| * uses the `string & {}` escape hatch or when the auto-detected dialect is | ||
| * wrong for your database. | ||
| * | ||
| * Values are case-sensitive (e.g. `"PostgresQL"`, not `"postgresql"`). | ||
| * Use patterns with the `/i` flag for case-insensitive matching. | ||
| * | ||
| * Ignored when a custom `validate` function is provided (which replaces | ||
| * the entire SQL validation pipeline). | ||
| * | ||
| * **Note:** Wired into the core pipeline in #15. Until then, this field | ||
| * is validated at registration time but not yet consumed at query time. | ||
| */ | ||
| parserDialect?: ParserDialect; | ||
| /** | ||
| * Additional regex patterns to block beyond the base DML/DDL guard. | ||
| * Each pattern is tested against the trimmed SQL string. Use `\b` word | ||
| * boundaries and the `/i` flag for case-insensitive matching, consistent | ||
| * with core forbidden patterns (see `FORBIDDEN_PATTERNS` in sql.ts). | ||
| * | ||
| * Ignored when a custom `validate` function is provided (which replaces | ||
| * the entire SQL validation pipeline). | ||
| * | ||
| * **Note:** Wired into the core pipeline in #15. Until then, this field | ||
| * is validated at registration time but not yet consumed at query time. | ||
| */ | ||
| forbiddenPatterns?: RegExp[]; | ||
| }; | ||
@@ -257,3 +291,2 @@ /** | ||
| export interface AtlasContextPlugin<TConfig = undefined> extends AtlasPluginBase<TConfig> { | ||
| readonly type: "context"; | ||
| readonly contextProvider: { | ||
@@ -267,3 +300,2 @@ /** Load context (e.g. additional system prompt fragments, entity YAMLs). */ | ||
| export interface AtlasInteractionPlugin<TConfig = undefined> extends AtlasPluginBase<TConfig> { | ||
| readonly type: "interaction"; | ||
| /** | ||
@@ -286,3 +318,2 @@ * Mount routes on the Hono app. Optional — not all interaction plugins | ||
| export interface AtlasActionPlugin<TConfig = undefined> extends AtlasPluginBase<TConfig> { | ||
| readonly type: "action"; | ||
| readonly actions: PluginAction[]; | ||
@@ -319,3 +350,2 @@ } | ||
| export interface AtlasSandboxPlugin<TConfig = undefined> extends AtlasPluginBase<TConfig> { | ||
| readonly type: "sandbox"; | ||
| readonly sandbox: { | ||
@@ -369,5 +399,5 @@ /** | ||
| Config: C; | ||
| /** The plugin type literal. */ | ||
| Type: P extends { | ||
| type: infer U; | ||
| /** The plugin type(s). */ | ||
| Types: P extends { | ||
| types: infer U; | ||
| } ? U : never; | ||
@@ -403,7 +433,7 @@ /** The plugin ID. */ | ||
| * import type { $InferServerPlugin } from "@useatlas/plugin-sdk"; | ||
| * import type { clickhousePlugin } from "@atlas/plugin-clickhouse-datasource"; | ||
| * import type { clickhousePlugin } from "@useatlas/clickhouse"; | ||
| * | ||
| * type CH = $InferServerPlugin<typeof clickhousePlugin>; | ||
| * // CH["Config"] → { url: string; database?: string } | ||
| * // CH["Type"] → "datasource" | ||
| * // CH["Types"] → readonly ["datasource"] | ||
| * // CH["Id"] → string | ||
@@ -410,0 +440,0 @@ * ``` |
+8
-3
| { | ||
| "name": "@useatlas/plugin-sdk", | ||
| "version": "0.0.2", | ||
| "version": "0.0.3", | ||
| "description": "Type definitions and helpers for authoring Atlas plugins", | ||
| "type": "module", | ||
| "scripts": { | ||
| "build": "rm -rf dist && bun build src/index.ts src/types.ts src/helpers.ts src/ai.ts src/hono.ts --outdir dist --target node --packages external && npx tsc -p tsconfig.build.json", | ||
| "build": "rm -rf dist && bun build src/index.ts src/types.ts src/helpers.ts src/ai.ts src/hono.ts src/testing.ts --outdir dist --target node --packages external && npx tsc -p tsconfig.build.json", | ||
| "prepublishOnly": "bun run build", | ||
| "test": "bun test src/__tests__/types.test.ts && bun test src/__tests__/infer.test.ts" | ||
| "test": "bun test src/__tests__/types.test.ts && bun test src/__tests__/infer.test.ts && bun test src/__tests__/testing.test.ts" | ||
| }, | ||
@@ -36,2 +36,7 @@ "exports": { | ||
| "default": "./dist/hono.js" | ||
| }, | ||
| "./testing": { | ||
| "types": "./dist/testing.d.ts", | ||
| "import": "./dist/testing.js", | ||
| "default": "./dist/testing.js" | ||
| } | ||
@@ -38,0 +43,0 @@ }, |
+23
-23
@@ -15,3 +15,3 @@ # @useatlas/plugin-sdk | ||
| id: "my-datasource", | ||
| type: "datasource" as const, | ||
| types: ["datasource"] as const, | ||
| version: "1.0.0", | ||
@@ -53,3 +53,3 @@ config, | ||
| **Reference:** [`clickhouse-datasource`](../../plugins/clickhouse-datasource/index.ts) | ||
| **Reference:** [`clickhouse`](../../plugins/clickhouse/index.ts) | ||
@@ -75,3 +75,3 @@ ### Context (`AtlasContextPlugin`) | ||
| **Reference:** [`slack-interaction`](../../plugins/slack-interaction/src/index.ts), [`mcp-interaction`](../../plugins/mcp-interaction/src/index.ts) | ||
| **Reference:** [`slack`](../../plugins/slack/src/index.ts), [`mcp`](../../plugins/mcp/src/index.ts) | ||
@@ -92,3 +92,3 @@ ### Action (`AtlasActionPlugin`) | ||
| **Reference:** [`jira-action`](../../plugins/jira-action/index.ts), [`email-action`](../../plugins/email-action/index.ts) | ||
| **Reference:** [`jira`](../../plugins/jira/index.ts), [`email`](../../plugins/email/index.ts) | ||
@@ -105,3 +105,3 @@ ### Sandbox (`AtlasSandboxPlugin`) | ||
| **Reference:** [`nsjail-sandbox`](../../plugins/nsjail-sandbox/index.ts), [`vercel-sandbox`](../../plugins/vercel-sandbox/index.ts) | ||
| **Reference:** [`nsjail`](../../plugins/nsjail/index.ts), [`vercel-sandbox`](../../plugins/vercel-sandbox/index.ts) | ||
@@ -160,3 +160,3 @@ ## Base Fields | ||
| id: "my-datasource", | ||
| type: "datasource" as const, | ||
| types: ["datasource"] as const, | ||
| version: "1.0.0", | ||
@@ -178,3 +178,3 @@ config, | ||
| id: "my-context", | ||
| type: "context", | ||
| types: ["context"], | ||
| version: "1.0.0", | ||
@@ -246,3 +246,3 @@ contextProvider: { async load() { return "Extra context..."; } }, | ||
| import type { $InferServerPlugin } from "@useatlas/plugin-sdk"; | ||
| import type { clickhousePlugin } from "@atlas/plugin-clickhouse-datasource"; | ||
| import type { clickhousePlugin } from "@useatlas/clickhouse"; | ||
@@ -289,20 +289,20 @@ type CH = $InferServerPlugin<typeof clickhousePlugin>; | ||
| > **Note:** These are internal workspace packages (`@atlas/plugin-*`) within the monorepo, not published to npm. Use them as reference when authoring your own plugins. | ||
| > **Note:** These are workspace packages (`@useatlas/*`) within the monorepo. Use them as reference when authoring your own plugins. | ||
| | Plugin | Type | Package | Description | | ||
| |--------|------|---------|-------------| | ||
| | [clickhouse-datasource](../../plugins/clickhouse-datasource/) | Datasource | `@atlas/plugin-clickhouse-datasource` | ClickHouse HTTP transport adapter | | ||
| | [mysql-datasource](../../plugins/mysql-datasource/) | Datasource | `@atlas/plugin-mysql-datasource` | MySQL pool-based adapter | | ||
| | [snowflake-datasource](../../plugins/snowflake-datasource/) | Datasource | `@atlas/plugin-snowflake-datasource` | Snowflake callback-based adapter | | ||
| | [duckdb-datasource](../../plugins/duckdb-datasource/) | Datasource | `@atlas/plugin-duckdb-datasource` | DuckDB in-process adapter | | ||
| | [yaml-context](../../plugins/yaml-context/) | Context | `@atlas/plugin-yaml-context` | YAML semantic layer context provider | | ||
| | [mcp-interaction](../../plugins/mcp-interaction/) | Interaction | `@atlas/plugin-mcp-interaction` | MCP server lifecycle (stdio + SSE) | | ||
| | [slack-interaction](../../plugins/slack-interaction/) | Interaction | `@atlas/plugin-slack-interaction` | Slack bot (slash commands, threads, OAuth) | | ||
| | [jira-action](../../plugins/jira-action/) | Action | `@atlas/plugin-jira-action` | Create JIRA tickets from analysis | | ||
| | [email-action](../../plugins/email-action/) | Action | `@atlas/plugin-email-action` | Send email reports via Resend | | ||
| | [nsjail-sandbox](../../plugins/nsjail-sandbox/) | Sandbox | `@atlas/plugin-nsjail-sandbox` | Linux namespace isolation via nsjail | | ||
| | [sidecar-sandbox](../../plugins/sidecar-sandbox/) | Sandbox | `@atlas/plugin-sidecar-sandbox` | HTTP-isolated container sidecar | | ||
| | [vercel-sandbox](../../plugins/vercel-sandbox/) | Sandbox | `@atlas/plugin-vercel-sandbox` | Firecracker microVM via @vercel/sandbox | | ||
| | [daytona-sandbox](../../plugins/daytona-sandbox/) | Sandbox | `@atlas/plugin-daytona-sandbox` | Daytona managed cloud sandbox | | ||
| | [e2b-sandbox](../../plugins/e2b-sandbox/) | Sandbox | `@atlas/plugin-e2b-sandbox` | E2B Firecracker microVM (managed) | | ||
| | [clickhouse](../../plugins/clickhouse/) | Datasource | `@useatlas/clickhouse` | ClickHouse HTTP transport adapter | | ||
| | [mysql](../../plugins/mysql/) | Datasource | `@useatlas/mysql` | MySQL pool-based adapter | | ||
| | [snowflake](../../plugins/snowflake/) | Datasource | `@useatlas/snowflake` | Snowflake callback-based adapter | | ||
| | [duckdb](../../plugins/duckdb/) | Datasource | `@useatlas/duckdb` | DuckDB in-process adapter | | ||
| | [yaml-context](../../plugins/yaml-context/) | Context | `@useatlas/yaml-context` | YAML semantic layer context provider | | ||
| | [mcp](../../plugins/mcp/) | Interaction | `@useatlas/mcp` | MCP server lifecycle (stdio + SSE) | | ||
| | [slack](../../plugins/slack/) | Interaction | `@useatlas/slack` | Slack bot (slash commands, threads, OAuth) | | ||
| | [jira](../../plugins/jira/) | Action | `@useatlas/jira` | Create JIRA tickets from analysis | | ||
| | [email](../../plugins/email/) | Action | `@useatlas/email` | Send email reports via Resend | | ||
| | [nsjail](../../plugins/nsjail/) | Sandbox | `@useatlas/nsjail` | Linux namespace isolation via nsjail | | ||
| | [sidecar](../../plugins/sidecar/) | Sandbox | `@useatlas/sidecar` | HTTP-isolated container sidecar | | ||
| | [vercel-sandbox](../../plugins/vercel-sandbox/) | Sandbox | `@useatlas/vercel-sandbox` | Firecracker microVM via @vercel/sandbox | | ||
| | [daytona](../../plugins/daytona/) | Sandbox | `@useatlas/daytona` | Daytona managed cloud sandbox | | ||
| | [e2b](../../plugins/e2b/) | Sandbox | `@useatlas/e2b` | E2B Firecracker microVM (managed) | | ||
@@ -309,0 +309,0 @@ ## Source |
55810
23.88%14
16.67%1054
38.68%