diff --git a/bun.lock b/bun.lock index da3d2eae3..34821dec7 100644 --- a/bun.lock +++ b/bun.lock @@ -249,6 +249,18 @@ "typescript": "latest", }, }, + "examples/docs-sdk-quickstart": { + "name": "@executor-js/example-docs-sdk-quickstart", + "version": "0.0.0", + "dependencies": { + "@executor-js/plugin-openapi": "workspace:*", + "@executor-js/sdk": "workspace:*", + }, + "devDependencies": { + "@types/node": "catalog:", + "typescript": "catalog:", + }, + }, "examples/promise-sdk": { "name": "@executor-js/example-promise-sdk", "version": "0.0.2", @@ -1319,6 +1331,8 @@ "@executor-js/example-all-plugins": ["@executor-js/example-all-plugins@workspace:examples/all-plugins"], + "@executor-js/example-docs-sdk-quickstart": ["@executor-js/example-docs-sdk-quickstart@workspace:examples/docs-sdk-quickstart"], + "@executor-js/example-promise-sdk": ["@executor-js/example-promise-sdk@workspace:examples/promise-sdk"], "@executor-js/execution": ["@executor-js/execution@workspace:packages/core/execution"], diff --git a/docs/cloud/api-keys.mdx b/docs/cloud/api-keys.mdx new file mode 100644 index 000000000..f9e1cd03b --- /dev/null +++ b/docs/cloud/api-keys.mdx @@ -0,0 +1,6 @@ +--- +title: API Keys +description: Authenticate to Executor Cloud. +--- + +This page will document cloud API key creation, rotation, and usage once the public flow is finalized. diff --git a/docs/cloud/hosting-model.mdx b/docs/cloud/hosting-model.mdx new file mode 100644 index 000000000..4809fadae --- /dev/null +++ b/docs/cloud/hosting-model.mdx @@ -0,0 +1,6 @@ +--- +title: Hosting Model +description: Understand what runs in Executor Cloud. +--- + +This page will describe the hosted runtime boundaries for Executor Cloud. diff --git a/docs/cloud/overview.mdx b/docs/cloud/overview.mdx new file mode 100644 index 000000000..cb8cae5c1 --- /dev/null +++ b/docs/cloud/overview.mdx @@ -0,0 +1,6 @@ +--- +title: Overview +description: Understand Executor Cloud. +--- + +Executor Cloud provides hosted workspaces for shared sources, credentials, policies, and agent access. diff --git a/docs/cloud/sources.mdx b/docs/cloud/sources.mdx new file mode 100644 index 000000000..4449efb87 --- /dev/null +++ b/docs/cloud/sources.mdx @@ -0,0 +1,6 @@ +--- +title: Sources +description: Manage hosted source connections. +--- + +Cloud sources make shared tools available to the agents and applications connected to a workspace. diff --git a/docs/cloud/workspaces.mdx b/docs/cloud/workspaces.mdx new file mode 100644 index 000000000..791c55d48 --- /dev/null +++ b/docs/cloud/workspaces.mdx @@ -0,0 +1,6 @@ +--- +title: Workspaces +description: Organize shared Executor resources. +--- + +Workspaces group sources, policies, and credentials for a team or application. diff --git a/docs/concepts/connections.mdx b/docs/concepts/connections.mdx new file mode 100644 index 000000000..f50ccb3f7 --- /dev/null +++ b/docs/concepts/connections.mdx @@ -0,0 +1,8 @@ +--- +title: Connections +description: OAuth and provider-backed credentials connected to Executor. +--- + +Connections represent authenticated relationships with external providers. + +Use connections when a source needs provider-managed credentials, OAuth refresh behavior, or an identity label that users can recognize and manage separately from bare secrets. diff --git a/docs/concepts/executor.mdx b/docs/concepts/executor.mdx new file mode 100644 index 000000000..54c7904e1 --- /dev/null +++ b/docs/concepts/executor.mdx @@ -0,0 +1,6 @@ +--- +title: Executor +description: The runtime that connects sources, tools, auth, and policy. +--- + +An Executor is the runtime surface that applications and agents use to discover tools, inspect schemas, invoke tools, and resolve credentials. diff --git a/docs/concepts/policies.mdx b/docs/concepts/policies.mdx new file mode 100644 index 000000000..22e2eafbc --- /dev/null +++ b/docs/concepts/policies.mdx @@ -0,0 +1,6 @@ +--- +title: Policies +description: Control which tools can run. +--- + +Policies decide whether tool invocations are allowed, blocked, or require approval. diff --git a/docs/concepts/scopes.mdx b/docs/concepts/scopes.mdx new file mode 100644 index 000000000..c6444f315 --- /dev/null +++ b/docs/concepts/scopes.mdx @@ -0,0 +1,6 @@ +--- +title: Scopes +description: Resource ownership and precedence. +--- + +Scopes describe where resources belong and how Executor resolves resources across user, workspace, or organization boundaries. diff --git a/docs/concepts/secrets.mdx b/docs/concepts/secrets.mdx new file mode 100644 index 000000000..a21f5e782 --- /dev/null +++ b/docs/concepts/secrets.mdx @@ -0,0 +1,6 @@ +--- +title: Secrets +description: Credential references shared by sources and tools. +--- + +Secrets store credentials separately from source definitions so tools can authenticate without exposing sensitive values. diff --git a/docs/concepts/sources.mdx b/docs/concepts/sources.mdx new file mode 100644 index 000000000..f6e3d0bd3 --- /dev/null +++ b/docs/concepts/sources.mdx @@ -0,0 +1,6 @@ +--- +title: Sources +description: External systems connected to Executor. +--- + +A source is an external system or protocol endpoint that contributes tools to Executor. diff --git a/docs/concepts/tools.mdx b/docs/concepts/tools.mdx new file mode 100644 index 000000000..5b8387149 --- /dev/null +++ b/docs/concepts/tools.mdx @@ -0,0 +1,6 @@ +--- +title: Tools +description: Callable actions exposed by sources. +--- + +A tool is a typed callable action with metadata, input schema, and invocation behavior. diff --git a/docs/docs.json b/docs/docs.json new file mode 100644 index 000000000..5c0c62ae9 --- /dev/null +++ b/docs/docs.json @@ -0,0 +1,93 @@ +{ + "$schema": "https://mintlify.com/docs.json", + "theme": "aspen", + "name": "Executor", + "colors": { + "primary": "#009f83", + "light": "#009f83", + "dark": "#6ee7c8" + }, + "navigation": { + "tabs": [ + { + "tab": "Getting Started", + "groups": [ + { + "group": "Start Here", + "pages": [ + "index", + "getting-started/overview", + "getting-started/local", + "getting-started/cloud", + "getting-started/sdk" + ] + } + ] + }, + { + "tab": "Concepts", + "groups": [ + { + "group": "Core Concepts", + "pages": [ + "concepts/executor", + "concepts/sources", + "concepts/tools", + "concepts/scopes", + "concepts/policies", + "concepts/secrets", + "concepts/connections" + ] + } + ] + }, + { + "tab": "SDK", + "groups": [ + { + "group": "SDK", + "pages": [ + "sdk/quickstart", + "sdk/create-executor", + "sdk/sources", + "sdk/tools", + "sdk/secrets", + "sdk/approvals", + "sdk/errors" + ] + } + ] + }, + { + "tab": "Local", + "groups": [ + { + "group": "Local Product", + "pages": [ + "local/install", + "local/web-ui", + "local/mcp-server", + "local/cli", + "local/auth-and-secrets" + ] + } + ] + }, + { + "tab": "Cloud", + "groups": [ + { + "group": "Cloud Product", + "pages": [ + "cloud/overview", + "cloud/workspaces", + "cloud/sources", + "cloud/api-keys", + "cloud/hosting-model" + ] + } + ] + } + ] + } +} diff --git a/docs/getting-started/cloud.mdx b/docs/getting-started/cloud.mdx new file mode 100644 index 000000000..4b494172e --- /dev/null +++ b/docs/getting-started/cloud.mdx @@ -0,0 +1,8 @@ +--- +title: Cloud Quickstart +description: Start with Executor Cloud. +--- + +Use Executor Cloud when your team needs shared workspaces, hosted source management, and centralized credentials. + +This page will be filled in as the cloud onboarding flow stabilizes. diff --git a/docs/getting-started/local.mdx b/docs/getting-started/local.mdx new file mode 100644 index 000000000..c89906dd4 --- /dev/null +++ b/docs/getting-started/local.mdx @@ -0,0 +1,8 @@ +--- +title: Local Quickstart +description: Start Executor locally. +--- + +Use the local product when you want Executor running on your machine with a web UI, CLI, and MCP server for local agents. + +This page will be backed by a smoke-tested local workflow. diff --git a/docs/getting-started/overview.mdx b/docs/getting-started/overview.mdx new file mode 100644 index 000000000..1aa856a00 --- /dev/null +++ b/docs/getting-started/overview.mdx @@ -0,0 +1,10 @@ +--- +title: Overview +description: Choose the right Executor entrypoint. +--- + +Executor has three main entrypoints: + +- Local product: run Executor on your machine and connect agents through MCP or the CLI. +- Cloud product: manage hosted workspaces, shared sources, and team-facing auth. +- SDK: embed Executor in your own TypeScript application through `@executor-js/sdk`. diff --git a/docs/getting-started/sdk.mdx b/docs/getting-started/sdk.mdx new file mode 100644 index 000000000..2a275c535 --- /dev/null +++ b/docs/getting-started/sdk.mdx @@ -0,0 +1,8 @@ +--- +title: SDK Quickstart +description: Embed Executor with the SDK. +--- + +Use the SDK when you want to create an Executor instance inside your own TypeScript application. + +All examples on this path should import the Executor API from `@executor-js/sdk`. diff --git a/docs/index.mdx b/docs/index.mdx new file mode 100644 index 000000000..b2997f71b --- /dev/null +++ b/docs/index.mdx @@ -0,0 +1,8 @@ +--- +title: Executor +description: Product and SDK documentation for Executor. +--- + +Executor gives AI agents one shared tool catalog, auth layer, and policy system across local, cloud, and SDK workflows. + +These docs are scaffolded around realistic examples. SDK examples should compile against the public SDK packages before their snippets graduate into guide content. diff --git a/docs/local/auth-and-secrets.mdx b/docs/local/auth-and-secrets.mdx new file mode 100644 index 000000000..26ad4fdf9 --- /dev/null +++ b/docs/local/auth-and-secrets.mdx @@ -0,0 +1,6 @@ +--- +title: Auth And Secrets +description: Manage local credentials. +--- + +This page will explain how local credentials are stored, resolved, and used by sources. diff --git a/docs/local/cli.mdx b/docs/local/cli.mdx new file mode 100644 index 000000000..72ff8fd07 --- /dev/null +++ b/docs/local/cli.mdx @@ -0,0 +1,6 @@ +--- +title: CLI +description: Use Executor from the command line. +--- + +The CLI starts local services, inspects tools, invokes tools, and manages runtime workflows. diff --git a/docs/local/install.mdx b/docs/local/install.mdx new file mode 100644 index 000000000..271fea9ba --- /dev/null +++ b/docs/local/install.mdx @@ -0,0 +1,6 @@ +--- +title: Install +description: Install and start Executor locally. +--- + +This page will cover local installation and startup commands. diff --git a/docs/local/mcp-server.mdx b/docs/local/mcp-server.mdx new file mode 100644 index 000000000..818c8c8d0 --- /dev/null +++ b/docs/local/mcp-server.mdx @@ -0,0 +1,6 @@ +--- +title: MCP Server +description: Connect local agents to Executor through MCP. +--- + +Executor can run as an MCP server so compatible agents can use the same shared tool catalog. diff --git a/docs/local/web-ui.mdx b/docs/local/web-ui.mdx new file mode 100644 index 000000000..5a3e964ec --- /dev/null +++ b/docs/local/web-ui.mdx @@ -0,0 +1,6 @@ +--- +title: Web UI +description: Manage sources, tools, secrets, and policies locally. +--- + +The local web UI is the fastest way to configure Executor on your machine. diff --git a/docs/sdk/approvals.mdx b/docs/sdk/approvals.mdx new file mode 100644 index 000000000..ad29db2a2 --- /dev/null +++ b/docs/sdk/approvals.mdx @@ -0,0 +1,6 @@ +--- +title: Approvals +description: Handle approval and elicitation requests. +--- + +Approvals let a tool ask for confirmation or additional input while it is being invoked. diff --git a/docs/sdk/create-executor.mdx b/docs/sdk/create-executor.mdx new file mode 100644 index 000000000..091a0837f --- /dev/null +++ b/docs/sdk/create-executor.mdx @@ -0,0 +1,6 @@ +--- +title: Create An Executor +description: Configure scopes, plugins, storage, and elicitation. +--- + +This page will explain the public `createExecutor` flow. diff --git a/docs/sdk/errors.mdx b/docs/sdk/errors.mdx new file mode 100644 index 000000000..40a0e2991 --- /dev/null +++ b/docs/sdk/errors.mdx @@ -0,0 +1,6 @@ +--- +title: Errors +description: Handle SDK failures. +--- + +This page will document the public error behavior exposed by `@executor-js/sdk`. diff --git a/docs/sdk/quickstart.mdx b/docs/sdk/quickstart.mdx new file mode 100644 index 000000000..2305296a7 --- /dev/null +++ b/docs/sdk/quickstart.mdx @@ -0,0 +1,70 @@ +--- +title: Quickstart +description: Create an Executor, add a source, and inspect its tools. +--- + +import CreateExecutor from "/snippets/sdk/quickstart/create-executor.mdx"; +import AddSource from "/snippets/sdk/quickstart/add-source.mdx"; +import ListTools from "/snippets/sdk/quickstart/list-tools.mdx"; +import InspectSchema from "/snippets/sdk/quickstart/inspect-schema.mdx"; +import CloseExecutor from "/snippets/sdk/quickstart/close-executor.mdx"; + +{/* Code snippets on this page are generated from examples/docs-sdk-quickstart/src/main.ts. */} + +The SDK lets you embed Executor in a TypeScript application. + +This quickstart creates an Executor, loads an OpenAPI source from a local spec, lists the tools it contributed, and inspects one tool schema. The snippets on this page are generated from a runnable example in `examples/docs-sdk-quickstart`. + +## Install + + + +```bash npm +npm install @executor-js/sdk @executor-js/plugin-openapi +``` + +```bash pnpm +pnpm add @executor-js/sdk @executor-js/plugin-openapi +``` + +```bash yarn +yarn add @executor-js/sdk @executor-js/plugin-openapi +``` + +```bash bun +bun add @executor-js/sdk @executor-js/plugin-openapi +``` + + + +## Create An Executor + +Import the SDK and configure the plugins you want to expose. + + + +`onElicitation: "accept-all"` is convenient for scripts and examples. Applications with interactive approval flows should pass a handler instead. + +## Add A Source + +Sources add tools to the Executor catalog. This example registers an OpenAPI document under the `inventory` namespace. + + + +## List Tools + +After a source is registered, its tools appear in the unified tool catalog. + + + +## Inspect A Tool + +Tools expose schema metadata that agents and applications can use before invoking them. + + + +## Shut Down + +Close the Executor when your process is done with it. + + diff --git a/docs/sdk/secrets.mdx b/docs/sdk/secrets.mdx new file mode 100644 index 000000000..065740203 --- /dev/null +++ b/docs/sdk/secrets.mdx @@ -0,0 +1,6 @@ +--- +title: Secrets +description: Store and reference credentials from SDK workflows. +--- + +Secrets let sources and tools use credentials without hard-coding sensitive values in source definitions. diff --git a/docs/sdk/sources.mdx b/docs/sdk/sources.mdx new file mode 100644 index 000000000..a2355a92e --- /dev/null +++ b/docs/sdk/sources.mdx @@ -0,0 +1,6 @@ +--- +title: Sources +description: Add MCP, OpenAPI, GraphQL, and other sources. +--- + +Sources turn external systems into tools that agents can discover and call. diff --git a/docs/sdk/tools.mdx b/docs/sdk/tools.mdx new file mode 100644 index 000000000..98a86ce37 --- /dev/null +++ b/docs/sdk/tools.mdx @@ -0,0 +1,6 @@ +--- +title: Tools +description: List, inspect, and invoke tools. +--- + +Tools are the callable actions exposed by sources. This page will cover listing tools, reading schemas, filtering by source, and invoking tools. diff --git a/docs/snippets/README.md b/docs/snippets/README.md new file mode 100644 index 000000000..7a074ea82 --- /dev/null +++ b/docs/snippets/README.md @@ -0,0 +1,7 @@ +# Generated snippets + +Files in this directory are generated by `bun run docs:snippets`. + +Edit the source example files under `examples/` between `docs:start` and +`docs:end` markers, then regenerate snippets. Do not edit generated `.mdx` +snippet files directly. diff --git a/docs/snippets/sdk/quickstart/add-source.mdx b/docs/snippets/sdk/quickstart/add-source.mdx new file mode 100644 index 000000000..2d004ee75 --- /dev/null +++ b/docs/snippets/sdk/quickstart/add-source.mdx @@ -0,0 +1,12 @@ +{/_ +This file is generated by scripts/generate-doc-snippets.ts. +Edit examples/docs-sdk-quickstart/src/main.ts between the docs:start/docs:end markers instead. +_/} + +```ts +await executor.openapi.addSpec({ + namespace: "inventory", + scope: "docs-workspace", + spec: JSON.stringify(inventoryApi), +}); +``` diff --git a/docs/snippets/sdk/quickstart/close-executor.mdx b/docs/snippets/sdk/quickstart/close-executor.mdx new file mode 100644 index 000000000..34df34ee1 --- /dev/null +++ b/docs/snippets/sdk/quickstart/close-executor.mdx @@ -0,0 +1,8 @@ +{/_ +This file is generated by scripts/generate-doc-snippets.ts. +Edit examples/docs-sdk-quickstart/src/main.ts between the docs:start/docs:end markers instead. +_/} + +```ts +await executor.close(); +``` diff --git a/docs/snippets/sdk/quickstart/create-executor.mdx b/docs/snippets/sdk/quickstart/create-executor.mdx new file mode 100644 index 000000000..c9717176e --- /dev/null +++ b/docs/snippets/sdk/quickstart/create-executor.mdx @@ -0,0 +1,12 @@ +{/_ +This file is generated by scripts/generate-doc-snippets.ts. +Edit examples/docs-sdk-quickstart/src/main.ts between the docs:start/docs:end markers instead. +_/} + +```ts +const executor = await createExecutor({ + scopes: [{ id: "docs-workspace", name: "Docs Workspace" }], + plugins: [openApiPlugin()], + onElicitation: "accept-all", +}); +``` diff --git a/docs/snippets/sdk/quickstart/inspect-schema.mdx b/docs/snippets/sdk/quickstart/inspect-schema.mdx new file mode 100644 index 000000000..7409b681c --- /dev/null +++ b/docs/snippets/sdk/quickstart/inspect-schema.mdx @@ -0,0 +1,10 @@ +{/_ +This file is generated by scripts/generate-doc-snippets.ts. +Edit examples/docs-sdk-quickstart/src/main.ts between the docs:start/docs:end markers instead. +_/} + +```ts +const schema = await executor.tools.schema("inventory.listItems"); + +console.log(schema?.inputTypeScript ?? "No input required"); +``` diff --git a/docs/snippets/sdk/quickstart/list-tools.mdx b/docs/snippets/sdk/quickstart/list-tools.mdx new file mode 100644 index 000000000..3857085d0 --- /dev/null +++ b/docs/snippets/sdk/quickstart/list-tools.mdx @@ -0,0 +1,12 @@ +{/_ +This file is generated by scripts/generate-doc-snippets.ts. +Edit examples/docs-sdk-quickstart/src/main.ts between the docs:start/docs:end markers instead. +_/} + +```ts +const tools = await executor.tools.list({ sourceId: "inventory" }); + +for (const tool of tools) { + console.log(`${tool.id}: ${tool.description}`); +} +``` diff --git a/examples/docs-sdk-quickstart/CHANGELOG.md b/examples/docs-sdk-quickstart/CHANGELOG.md new file mode 100644 index 000000000..8b69ab6b6 --- /dev/null +++ b/examples/docs-sdk-quickstart/CHANGELOG.md @@ -0,0 +1,6 @@ +# @executor-js/example-docs-sdk-quickstart changelog + +This file exists for `changesets/action@v1` compatibility (it reads every +workspace package's `CHANGELOG.md` to build the Version Packages PR). +Canonical user-facing release notes are at `apps/cli/release-notes/next.md` +and on the GitHub Releases page. diff --git a/examples/docs-sdk-quickstart/package.json b/examples/docs-sdk-quickstart/package.json new file mode 100644 index 000000000..fd7cca19a --- /dev/null +++ b/examples/docs-sdk-quickstart/package.json @@ -0,0 +1,19 @@ +{ + "name": "@executor-js/example-docs-sdk-quickstart", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "start": "bun run src/main.ts", + "typecheck": "tsgo --noEmit", + "typecheck:slow": "tsc --noEmit" + }, + "dependencies": { + "@executor-js/plugin-openapi": "workspace:*", + "@executor-js/sdk": "workspace:*" + }, + "devDependencies": { + "@types/node": "catalog:", + "typescript": "catalog:" + } +} diff --git a/examples/docs-sdk-quickstart/src/main.ts b/examples/docs-sdk-quickstart/src/main.ts new file mode 100644 index 000000000..2a54a4095 --- /dev/null +++ b/examples/docs-sdk-quickstart/src/main.ts @@ -0,0 +1,104 @@ +// This example is the source of truth for docs snippets on /sdk/quickstart. +// Run `bun run docs:snippets` after editing docs:start/docs:end blocks. +import { createExecutor } from "@executor-js/sdk/promise"; +import { openApiPlugin } from "@executor-js/plugin-openapi/promise"; + +const inventoryApi = { + openapi: "3.0.0", + info: { + title: "Inventory API", + version: "1.0.0", + }, + servers: [{ url: "https://inventory.example.test" }], + paths: { + "/items": { + get: { + operationId: "listItems", + summary: "List inventory items", + responses: { + "200": { + description: "Inventory items", + content: { + "application/json": { + schema: { + type: "array", + items: { $ref: "#/components/schemas/Item" }, + }, + }, + }, + }, + }, + }, + }, + "/items/{id}": { + get: { + operationId: "getItem", + summary: "Get an inventory item", + parameters: [ + { + name: "id", + in: "path", + required: true, + schema: { type: "string" }, + }, + ], + responses: { + "200": { + description: "Inventory item", + content: { + "application/json": { + schema: { $ref: "#/components/schemas/Item" }, + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: { + Item: { + type: "object", + required: ["id", "name"], + properties: { + id: { type: "string" }, + name: { type: "string" }, + }, + }, + }, + }, +}; + +// docs:start create-executor +const executor = await createExecutor({ + scopes: [{ id: "docs-workspace", name: "Docs Workspace" }], + plugins: [openApiPlugin()], + onElicitation: "accept-all", +}); +// docs:end create-executor + +// docs:start add-source +await executor.openapi.addSpec({ + namespace: "inventory", + scope: "docs-workspace", + spec: JSON.stringify(inventoryApi), +}); +// docs:end add-source + +// docs:start list-tools +const tools = await executor.tools.list({ sourceId: "inventory" }); + +for (const tool of tools) { + console.log(`${tool.id}: ${tool.description}`); +} +// docs:end list-tools + +// docs:start inspect-schema +const schema = await executor.tools.schema("inventory.listItems"); + +console.log(schema?.inputTypeScript ?? "No input required"); +// docs:end inspect-schema + +// docs:start close-executor +await executor.close(); +// docs:end close-executor diff --git a/examples/docs-sdk-quickstart/tsconfig.json b/examples/docs-sdk-quickstart/tsconfig.json new file mode 100644 index 000000000..8cd04d0ac --- /dev/null +++ b/examples/docs-sdk-quickstart/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "outDir": "dist", + "rootDir": "src", + "types": ["node"] + }, + "include": ["src"] +} diff --git a/package.json b/package.json index 12cf236d8..59e3864e3 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,8 @@ "lint:release-notes": "bun run scripts/check-release-notes.ts", "lint:changelog-stubs": "bun run scripts/check-changelog-stubs.ts", "lint:fix": "oxlint -c .oxlintrc.jsonc --fix .", + "docs:snippets": "bun run scripts/generate-doc-snippets.ts", + "docs:smoke:install": "bun run scripts/smoke-docs-install.ts", "format": "oxfmt .", "format:check": "oxfmt --check .", "pull:references": "bun run scripts/pull-references.ts", diff --git a/packages/core/fumadb/tsup.config.ts b/packages/core/fumadb/tsup.config.ts index aa74e580d..3cee38869 100644 --- a/packages/core/fumadb/tsup.config.ts +++ b/packages/core/fumadb/tsup.config.ts @@ -8,6 +8,7 @@ export default defineConfig({ "adapters/index": "src/adapters/index.ts", "adapters/drizzle/index": "src/adapters/drizzle/index.ts", "adapters/kysely/index": "src/adapters/kysely/index.ts", + "adapters/memory/index": "src/adapters/memory/index.ts", "cli/index": "src/cli/index.ts", cuid: "src/cuid.ts", }, diff --git a/scripts/generate-doc-snippets.ts b/scripts/generate-doc-snippets.ts new file mode 100644 index 000000000..f5b6c9e9f --- /dev/null +++ b/scripts/generate-doc-snippets.ts @@ -0,0 +1,70 @@ +import { mkdir, writeFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; + +interface SnippetSource { + readonly source: string; + readonly outputDir: string; + readonly language: string; +} + +const sources: readonly SnippetSource[] = [ + { + source: "examples/docs-sdk-quickstart/src/main.ts", + outputDir: "docs/snippets/sdk/quickstart", + language: "ts", + }, +]; + +const markerPattern = /^\s*\/\/ docs:(start|end) ([a-z0-9-]+)\s*$/; + +const dedent = (lines: readonly string[]): string => { + const nonBlank = lines.filter((line) => line.trim().length > 0); + const indent = Math.min( + ...nonBlank.map((line) => { + const match = line.match(/^\s*/); + return match?.[0].length ?? 0; + }), + ); + + return lines + .map((line) => line.slice(indent)) + .join("\n") + .trim(); +}; + +for (const source of sources) { + const text = await Bun.file(source.source).text(); + const snippets = new Map(); + let current: { readonly name: string; readonly lines: string[] } | null = null; + + for (const line of text.split("\n")) { + const marker = line.match(markerPattern); + if (marker) { + const [, kind, name] = marker; + if (kind === "start") { + if (current) throw new Error(`Nested snippet marker in ${source.source}: ${name}`); + current = { name, lines: [] }; + } else { + if (!current || current.name !== name) { + throw new Error(`Unmatched snippet end in ${source.source}: ${name}`); + } + snippets.set(name, current.lines); + current = null; + } + continue; + } + + if (current) current.lines.push(line); + } + + if (current) throw new Error(`Unclosed snippet marker in ${source.source}: ${current.name}`); + + for (const [name, lines] of snippets) { + const output = join(source.outputDir, `${name}.mdx`); + await mkdir(dirname(output), { recursive: true }); + await writeFile( + output, + `{/_\nThis file is generated by scripts/generate-doc-snippets.ts.\nEdit ${source.source} between the docs:start/docs:end markers instead.\n_/}\n\n\`\`\`${source.language}\n${dedent(lines)}\n\`\`\`\n`, + ); + } +} diff --git a/scripts/smoke-docs-install.ts b/scripts/smoke-docs-install.ts new file mode 100644 index 000000000..0a74fcd9a --- /dev/null +++ b/scripts/smoke-docs-install.ts @@ -0,0 +1,97 @@ +#!/usr/bin/env bun +/** + * Verifies the SDK install command shown in docs resolves to a package we can + * actually publish and consume. + * + * This intentionally installs the packed tarball under the documented package + * name (`@executor-js/sdk`) instead of relying on workspace resolution. + */ +import { $ } from "bun"; +import { existsSync, readdirSync } from "node:fs"; +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const documentedPackages = ["@executor-js/sdk", "@executor-js/plugin-openapi"] as const; +const publicPackageDirs = [ + "packages/core/fumadb", + "packages/core/sdk", + "packages/core/config", + "packages/plugins/openapi", +] as const; + +const readPackageName = async (pkgDir: string): Promise => { + const raw = await readFile(join(pkgDir, "package.json"), "utf8"); + return (JSON.parse(raw) as { name: string }).name; +}; + +const findTarball = (pkgDir: string, packageName: string): string => { + const tarball = readdirSync(pkgDir).find((entry) => entry.endsWith(".tgz")); + if (!tarball) { + throw new Error(`No packed tarball found for ${packageName}`); + } + return join(pkgDir, tarball); +}; + +console.log(`[docs-smoke] packing documented SDK packages`); +await $`bun run scripts/publish-packages.ts --dry-run`.cwd(repoRoot); + +const tarballs = new Map(); +for (const relDir of publicPackageDirs) { + const pkgDir = join(repoRoot, relDir); + const name = await readPackageName(pkgDir); + tarballs.set(name, findTarball(pkgDir, name)); +} + +const tmp = await mkdtemp(join(tmpdir(), "executor-docs-install-")); + +try { + const dependencies: Record = {}; + const overrides: Record = {}; + for (const [name, tarball] of tarballs) { + overrides[name] = `file:${tarball}`; + } + for (const name of documentedPackages) { + const tarball = tarballs.get(name); + if (!tarball) { + throw new Error(`No packed tarball found for documented package ${name}`); + } + dependencies[name] = `file:${tarball}`; + } + + const fixture = { + name: "executor-docs-install-smoke", + version: "0.0.0", + private: true, + type: "module", + dependencies, + overrides, + }; + + await writeFile(join(tmp, "package.json"), `${JSON.stringify(fixture, null, 2)}\n`); + + console.log(`[docs-smoke] npm install ${documentedPackages.join(" ")}`); + await $`npm install --no-audit --no-fund --legacy-peer-deps`.cwd(tmp); + + for (const packageName of documentedPackages) { + const installedManifest = join(tmp, "node_modules", ...packageName.split("/"), "package.json"); + if (!existsSync(installedManifest)) { + throw new Error(`Expected ${packageName} to be installed at ${installedManifest}`); + } + const manifest = await import(installedManifest, { with: { type: "json" } }); + if (manifest.default.name !== packageName) { + throw new Error( + `Expected installed package name to be ${packageName}, got ${manifest.default.name}`, + ); + } + } + + console.log(`[docs-smoke] import documented SDK packages`); + await $`node --input-type=module --eval ${`const sdk = await import("@executor-js/sdk"); const openapi = await import("@executor-js/plugin-openapi"); if (typeof sdk.createExecutor !== "function") throw new Error("missing createExecutor"); if (typeof openapi.openApiPlugin !== "function") throw new Error("missing openApiPlugin");`}`.cwd( + tmp, + ); +} finally { + await rm(tmp, { recursive: true, force: true }); +}