diff --git a/.vitepress/config/constants.ts b/.vitepress/config/constants.ts index 814675c..fc56d32 100644 --- a/.vitepress/config/constants.ts +++ b/.vitepress/config/constants.ts @@ -88,8 +88,7 @@ export const defaultSidebarOrder: string[] = ["overview", "quickstart"]; // Section-specific overrides (only if you need different ordering for a specific section) export const sidebarItemOrder: Record = { - // Add section-specific overrides here if needed - // Example: 'guides': ['overview', 'quickstart', 'tailordb', ...] + function: ["overview", "builtin-interfaces"], "app-shell": [ "api", "authentication", diff --git a/.vitepress/config/nav.ts b/.vitepress/config/nav.ts index a771791..2ee67bd 100644 --- a/.vitepress/config/nav.ts +++ b/.vitepress/config/nav.ts @@ -2,7 +2,7 @@ import type { DefaultTheme } from "vitepress"; import fs from "node:fs"; import path from "node:path"; import { toTitle } from "./utils.js"; -import { navGroups, navItemOrder } from "./constants.js"; +import { navGroups, navItemOrder, sidebarItemOrder, defaultSidebarOrder } from "./constants.js"; // Generate grouped nav items with organized dropdown structure export function generateNav(docsDir: string): DefaultTheme.NavItem[] { @@ -38,10 +38,16 @@ export function generateNav(docsDir: string): DefaultTheme.NavItem[] { const subFiles = fs .readdirSync(path.join(sectionPath, e.name)) .filter((f) => f.endsWith(".md") && !f.startsWith(".")) - .sort(); - - if (subFiles.length > 0) { - link = `/${section}/${e.name}/${subFiles[0].replace(/\.md$/, "")}`; + .map((f) => f.replace(/\.md$/, "")); + + // Use sidebarItemOrder, then defaultSidebarOrder, then alphabetical + const order = sidebarItemOrder[e.name] || defaultSidebarOrder; + const firstOrdered = order.find((item) => subFiles.includes(item)); + if (firstOrdered) { + link = `/${section}/${e.name}/${firstOrdered}`; + } else if (subFiles.length > 0) { + subFiles.sort(); + link = `/${section}/${e.name}/${subFiles[0]}`; } } diff --git a/.vitepress/config/sidebar.ts b/.vitepress/config/sidebar.ts index c8215dc..69ba1e4 100644 --- a/.vitepress/config/sidebar.ts +++ b/.vitepress/config/sidebar.ts @@ -25,9 +25,11 @@ export function generateSidebar( const entries = fs.readdirSync(fullPath, { withFileTypes: true }); - // Get section name for custom ordering + // Get custom ordering: check current directory name first, then top-level section + const currentDirName = path.basename(dir); const sectionName = dir.split(path.sep)[0]; - const customOrder = sidebarItemOrder[sectionName] || defaultSidebarOrder; + const customOrder = + sidebarItemOrder[currentDirName] || sidebarItemOrder[sectionName] || defaultSidebarOrder; // Get markdown files (excluding index.md) const files = entries diff --git a/docs/guides/function/builtin-interfaces.md b/docs/guides/function/builtin-interfaces.md new file mode 100644 index 0000000..06b7c7a --- /dev/null +++ b/docs/guides/function/builtin-interfaces.md @@ -0,0 +1,263 @@ +--- +doc_type: guide +--- + +# Built-in interfaces + +Functions (Resolvers, Workflows, Executors) have access to built-in interfaces as global variables. +These provide direct access to platform services without external HTTP calls or token management. + +All interfaces are typed via [`@tailor-platform/function-types`](https://github.com/tailor-platform/function/tree/main/packages/types) — install the package to get full TypeScript support. + +## Overview + +| Service | Description | +| --- | --- | +| [IdP Client](#idp-client) | Create, update, delete IdP users and send password reset emails | +| [Secret Manager](#secret-manager) | Retrieve secrets from vaults | +| [Auth Connection](#auth-connection) | Get access tokens for external auth connections | +| [Character Encoding](#character-encoding) | Convert between character encodings (iconv) | +| [Workflow](#workflow) | Trigger workflows and job functions | +| [TailorDB Client](#tailordb-client) | Execute SQL queries against TailorDB | +| [TailorDB File](#tailordb-file) | Upload, download, and manage files in TailorDB | + +## IdP Client + +**Interface**: `tailor.idp.Client` + +Manage Built-in IdP users directly from your function. No external HTTP calls or machine user tokens needed. + +**See also**: [Managing IdP Users in Functions](/guides/function/managing-idp-users) + +```typescript +const client = new tailor.idp.Client({ namespace: "my-namespace" }); + +// Create a user +const user = await client.createUser({ name: "user@example.com", password: "secret" }); + +// List users with filtering +const { users, totalCount } = await client.users({ + first: 10, + query: { names: ["user@example.com"] }, +}); + +// Get, update, delete +const user = await client.user(userId); +await client.updateUser({ id: userId, name: "new@example.com" }); +await client.deleteUser(userId); + +// Send password reset email +await client.sendPasswordResetEmail({ + userId: user.id, + redirectUri: "https://app.example.com/reset", +}); +``` + +| Method | Returns | Description | +| --- | --- | --- | +| `users(options?)` | `Promise` | List users with optional filtering and pagination | +| `user(userId)` | `Promise` | Get a user by ID | +| `createUser(input)` | `Promise` | Create a new user | +| `updateUser(input)` | `Promise` | Update an existing user | +| `deleteUser(userId)` | `Promise` | Delete a user by ID | +| `sendPasswordResetEmail(input)` | `Promise` | Send a password reset email | + +## Secret Manager + +**Interface**: `tailor.secretmanager` + +Retrieve secrets stored in Tailor Platform vaults. + +```typescript +// Get a single secret +const apiKey = await tailor.secretmanager.getSecret("my-vault", "API_KEY"); + +// Get multiple secrets at once +const secrets = await tailor.secretmanager.getSecrets("my-vault", [ + "API_KEY", + "API_SECRET", +] as const); +// secrets.API_KEY, secrets.API_SECRET +``` + +| Function | Returns | Description | +| --- | --- | --- | +| `getSecret(vault, name)` | `Promise` | Get a single secret. Returns `undefined` if not found | +| `getSecrets(vault, names)` | `Promise>>` | Get multiple secrets. Missing keys are omitted | + +## Auth Connection + +**Interface**: `tailor.authconnection` + +Get access tokens for configured external auth connections (e.g., OAuth providers). + +```typescript +const token = await tailor.authconnection.getConnectionToken("my-google-connection"); +// Use token to call external APIs +``` + +| Function | Returns | Description | +| --- | --- | --- | +| `getConnectionToken(connectionName)` | `Promise` | Get the access token for a named auth connection | + +## Character Encoding + +**Interface**: `tailor.iconv` + +Convert between character encodings. Useful for processing files in non-UTF-8 encodings (e.g., Shift_JIS CSV files). + +```typescript +// Convert Shift_JIS buffer to UTF-8 string +const text = tailor.iconv.decode(shiftJisBuffer, "Shift_JIS"); + +// Encode a string to Shift_JIS +const encoded = tailor.iconv.encode("こんにちは", "Shift_JIS"); + +// Convert between encodings +const result = tailor.iconv.convert(buffer, "EUC-JP", "UTF-8"); + +// List supported encodings +const encodings = tailor.iconv.encodings(); +``` + +| Function | Returns | Description | +| --- | --- | --- | +| `convert(str, fromEncoding, toEncoding)` | `string \| Uint8Array` | Convert between encodings. Returns `string` when target is UTF-8 | +| `decode(buffer, encoding)` | `string` | Decode a buffer to a UTF-8 string | +| `encode(str, encoding)` | `string \| Uint8Array` | Encode a string to the specified encoding | +| `encodings()` | `string[]` | List all supported encodings | + +The `Iconv` class is also available for node-iconv compatibility: + +```typescript +const converter = new tailor.iconv.Iconv("Shift_JIS", "UTF-8"); +const result = converter.convert(inputBuffer); +``` + +## Workflow + +**Interface**: `tailor.workflow` + +Trigger workflows and job functions from within a function. + +```typescript +// Trigger a workflow +const executionId = await tailor.workflow.triggerWorkflow("processOrder", { + orderId: "order-123", +}); + +// Trigger with a specific machine user +const executionId = await tailor.workflow.triggerWorkflow( + "processOrder", + { orderId: "order-123" }, + { + authInvoker: { + namespace: "my-namespace", + machineUserName: "workflow-runner", + }, + }, +); + +// Trigger a job function +const result = await tailor.workflow.triggerJobFunction("calculateTax", { + amount: 1000, +}); +``` + +| Function | Returns | Description | +| --- | --- | --- | +| `triggerWorkflow(name, args?, options?)` | `Promise` | Trigger a workflow. Returns the execution ID | +| `triggerJobFunction(name, args?)` | `any` | Trigger a job function and return its result | + +## TailorDB Client + +**Interface**: `tailordb.Client` + +Execute SQL queries directly against TailorDB. For ORM-style access, consider using `@tailor-platform/function-kysely-tailordb`. + +**See also**: [Accessing TailorDB](/guides/function/accessing-tailordb) + +```typescript +const db = new tailordb.Client({ namespace: "my-namespace" }); +await db.connect(); + +try { + const result = await db.queryObject<{ id: string; name: string }>( + "SELECT id, name FROM users WHERE active = $1", + [true], + ); + console.log(result.rows); // [{ id: "...", name: "..." }, ...] +} finally { + await db.end(); +} +``` + +| Method | Returns | Description | +| --- | --- | --- | +| `connect()` | `Promise` | Open the database connection | +| `end()` | `Promise` | Close the database connection | +| `queryObject(sql, args?)` | `Promise>` | Execute a SQL query with parameterized arguments | + +## TailorDB File + +**Interface**: `tailordb.file` + +Upload, download, and manage files attached to TailorDB records. + +```typescript +// Upload a file +const { metadata } = await tailordb.file.upload( + "my-namespace", + "Document", + "attachment", + recordId, + fileData, + { contentType: "application/pdf" }, +); + +// Download a file +const { data, metadata } = await tailordb.file.download( + "my-namespace", + "Document", + "attachment", + recordId, +); + +// Download as Base64 (for embedding in JSON responses) +const { data: base64 } = await tailordb.file.downloadAsBase64( + "my-namespace", + "Document", + "attachment", + recordId, +); + +// Stream large files (>10MB) +const stream = await tailordb.file.openDownloadStream( + "my-namespace", + "Document", + "attachment", + recordId, +); +for await (const chunk of stream) { + if (chunk.type === "chunk") { + // process chunk.data + } +} + +// Get metadata without downloading +const meta = await tailordb.file.getMetadata("my-namespace", "Document", "attachment", recordId); + +// Delete a file +await tailordb.file.delete("my-namespace", "Document", "attachment", recordId); +``` + +**Note**: All methods take `(namespace, typeName, fieldName, recordId)` as the first four arguments. + +| Method | Returns | Description | +| --- | --- | --- | +| `upload(..., data, options?)` | `Promise` | Upload a file | +| `download(...)` | `Promise` | Download a file as `Uint8Array`. Throws if >10MB | +| `downloadAsBase64(...)` | `Promise` | Download as Base64 string. Throws if >10MB | +| `delete(...)` | `Promise` | Delete a file | +| `getMetadata(...)` | `Promise` | Get file metadata without downloading | +| `openDownloadStream(...)` | `Promise` | Stream large files in chunks | diff --git a/docs/guides/function/overview.md b/docs/guides/function/overview.md index 29116ba..f6e9327 100644 --- a/docs/guides/function/overview.md +++ b/docs/guides/function/overview.md @@ -14,12 +14,14 @@ With Function service, you can: - Send HTTP requests to 3rd party applications - Access and execute SQL queries to Tailor DB +- Manage IdP users, secrets, files, and workflows via [built-in interfaces](/guides/function/builtin-interfaces) - Process data transformations - Implement custom business logic with npm packages To keep the this guide simple, we'll focus on a basic example of writing a function that returns a greeting message. For more advanced use cases, you can refer to these sections: +- [Built-in interfaces](/guides/function/builtin-interfaces) — all platform APIs available as global variables - [Sending HTTP request](/guides/function/sending-request) - [Accessing Tailor DB](/guides/function/accessing-tailordb) @@ -192,6 +194,7 @@ export default createExecutor({ For more advanced use cases, you can refer to these sections: +- [Built-in interfaces](/guides/function/builtin-interfaces) — all platform APIs available as global variables (IdP, secrets, files, workflows, etc.) - [Sending HTTP request](/guides/function/sending-request) - [Accessing Tailor DB](/guides/function/accessing-tailordb)