Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .vitepress/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string[]> = {
// Add section-specific overrides here if needed
// Example: 'guides': ['overview', 'quickstart', 'tailordb', ...]
function: ["overview", "builtin-interfaces"],
"app-shell": [
"api",
"authentication",
Expand Down
16 changes: 11 additions & 5 deletions .vitepress/config/nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[] {
Expand Down Expand Up @@ -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]}`;
}
}

Expand Down
6 changes: 4 additions & 2 deletions .vitepress/config/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
263 changes: 263 additions & 0 deletions docs/guides/function/builtin-interfaces.md
Original file line number Diff line number Diff line change
@@ -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<ListUsersResponse>` | List users with optional filtering and pagination |
| `user(userId)` | `Promise<User>` | Get a user by ID |
| `createUser(input)` | `Promise<User>` | Create a new user |
| `updateUser(input)` | `Promise<User>` | Update an existing user |
| `deleteUser(userId)` | `Promise<boolean>` | Delete a user by ID |
| `sendPasswordResetEmail(input)` | `Promise<boolean>` | 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<string \| undefined>` | Get a single secret. Returns `undefined` if not found |
| `getSecrets(vault, names)` | `Promise<Partial<Record<string, string>>>` | 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<any>` | 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<string>` | 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<void>` | Open the database connection |
| `end()` | `Promise<void>` | Close the database connection |
| `queryObject<T>(sql, args?)` | `Promise<QueryResult<T>>` | 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<FileUploadResponse>` | Upload a file |
| `download(...)` | `Promise<FileDownloadResponse>` | Download a file as `Uint8Array`. Throws if >10MB |
| `downloadAsBase64(...)` | `Promise<FileDownloadAsBase64Response>` | Download as Base64 string. Throws if >10MB |
| `delete(...)` | `Promise<void>` | Delete a file |
| `getMetadata(...)` | `Promise<FileMetadata>` | Get file metadata without downloading |
| `openDownloadStream(...)` | `Promise<FileStreamIterator>` | Stream large files in chunks |
3 changes: 3 additions & 0 deletions docs/guides/function/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand Down