From 97663c33e29018f1e8b5c49e370adb1be3c9711a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 15:56:50 +0000 Subject: [PATCH 01/13] docs(enhance): add comprehensive documentation and improve package description Co-Authored-By: yleflour@pairprog.io --- packages/enhance/README.md | 152 +++++++++++++++++++++++++++- packages/enhance/package.json | 7 +- packages/enhance/src/enhance.lib.ts | 27 +++++ 3 files changed, 183 insertions(+), 3 deletions(-) diff --git a/packages/enhance/README.md b/packages/enhance/README.md index 0464edc..95ed5ba 100644 --- a/packages/enhance/README.md +++ b/packages/enhance/README.md @@ -1 +1,151 @@ -# @synstack/enhance +# @synstack/enhance + +> Safely enhance JavaScript objects with additional properties + +This package provides a type-safe way to extend JavaScript objects with additional methods while maintaining access to the original object. + +> [!WARNING] +> This package is included in the [@synstack/synscript](https://github.com/pAIrprogio/synscript) package. It is not recommended to install both packages at the same time. + +## What is it for? + +Sometimes you need to add functionality to existing objects without modifying their prototype or risking property collisions. This package provides a safe way to enhance objects with new methods while maintaining type safety: + +```typescript +import { enhance } from "@synstack/enhance"; + +// Create an extension with new methods +const stringExtensions = { + reverse: () => this.split("").reverse().join(""), + capitalize: () => this.charAt(0).toUpperCase() + this.slice(1) +}; + +// Enhance a string with new methods +const enhanced = enhance("string", "hello world", stringExtensions); + +// Use the enhanced object +console.log(enhanced.capitalize()); // "Hello world" +console.log(enhanced.reverse()); // "dlrow olleh" + +// Access the original object +console.log(enhanced.$()); // "hello world" +``` + +## Installation + +```bash +# Using npm +npm install @synstack/enhance + +# Using yarn +yarn add @synstack/enhance + +# Using pnpm +pnpm add @synstack/enhance +``` + +## Features + +### Object Enhancement + +The package provides two main functions for enhancing objects: + +#### enhance() + +The `enhance()` function combines an object with extension methods: + +```typescript +import { enhance } from "@synstack/enhance"; + +// Define extension methods +const mathExtensions = { + square: function() { return this * this }, + double: function() { return this * 2 } +}; + +// Enhance a number +const num = enhance("math", 5, mathExtensions); + +console.log(num.square()); // 25 +console.log(num.double()); // 10 +console.log(num.$()); // 5 +``` + +#### enhanceFactory() + +Create reusable enhancers with `enhanceFactory()`: + +```typescript +import { enhanceFactory } from "@synstack/enhance"; + +// Create a reusable enhancer +const enhanceWithMath = enhanceFactory("math", { + square: function() { return this * this }, + double: function() { return this * 2 } +}); + +// Enhance multiple numbers +const num1 = enhanceWithMath(5); +const num2 = enhanceWithMath(10); + +console.log(num1.square()); // 25 +console.log(num2.double()); // 20 +``` + +## API Reference + +### enhance() + +```typescript +function enhance( + name: TName, + obj: TBaseObject, + extendObj: TExtension +): Enhanced +``` + +- `name`: Unique identifier for this enhancement +- `obj`: The base object to enhance +- `extendObj`: Object containing extension methods +- Returns: A proxy that combines the base object with extension methods + +### enhanceFactory() + +```typescript +function enhanceFactory( + name: TName, + extendObj: TExtension +): (obj: TBaseObject) => Enhanced +``` + +- `name`: Unique identifier for this enhancement factory +- `extendObj`: Object containing extension methods +- Returns: A function that enhances objects with the provided extensions + +### Enhanced Type + +```typescript +type Enhanced = { + $: TBaseObject; + [ENHANCER_NAME]: TName; +} & TExtension & TBaseObject +``` + +A type representing an enhanced object that combines: +- Original object properties (`TBaseObject`) +- Extension methods (`TExtension`) +- Special `$` property to access the original object +- Symbol property to identify the enhancer + +## TypeScript Support + +This package is written in TypeScript and provides full type definitions: + +- Generic type parameters for base objects and extensions +- Type-safe access to both original and enhanced methods +- Proper typing for extension method context (`this`) +- IntelliSense support for all enhanced properties + +## License + +Apache-2.0 - see LICENSE file for details. diff --git a/packages/enhance/package.json b/packages/enhance/package.json index e26f573..136b042 100644 --- a/packages/enhance/package.json +++ b/packages/enhance/package.json @@ -5,11 +5,14 @@ "access": "public" }, "version": "1.1.2", - "description": "Safely enhance a JS object with additional properties", + "description": "Safely enhance a JS object with additional properties while maintaining type safety", "keywords": [ "proxy", "object", - "enhance" + "enhance", + "extend", + "type-safe", + "typescript" ], "author": { "name": "pAIrprog", diff --git a/packages/enhance/src/enhance.lib.ts b/packages/enhance/src/enhance.lib.ts index 74d76ed..3b59d7d 100644 --- a/packages/enhance/src/enhance.lib.ts +++ b/packages/enhance/src/enhance.lib.ts @@ -1,12 +1,31 @@ +/** + * Symbol used to identify enhanced objects and their original enhancer name + */ export const ENHANCER_NAME = Symbol("EnhancerName"); // The order of the types is important as it affects overriding +/** + * Type representing an enhanced object that combines base object properties with extensions + * @template TName - The name identifier for the enhancer + * @template TBaseObject - The type of the original object being enhanced + * @template TExtension - The type of the extension object adding new functionality + */ export type Enhanced< TName extends string, TBaseObject extends object, TExtension extends object, > = { $: TBaseObject; [ENHANCER_NAME]: TName } & TExtension & TBaseObject; +/** + * Enhances an object by combining it with extension methods while maintaining access to the original object + * @template TName - The name identifier for the enhancer + * @template TBaseObject - The type of the object to enhance + * @template TExtension - The type of the extension object containing new methods + * @param name - Unique name for this enhancement + * @param obj - The base object to enhance + * @param extendObj - Object containing extension methods + * @returns A proxy that combines the base object with extension methods + */ export const enhance = < TName extends string, TBaseObject extends object | (object & { [ENHANCER_NAME]: TName; $: object }), @@ -36,6 +55,14 @@ export const enhance = < }) as unknown as Enhanced; }; +/** + * Creates a reusable enhancer function that applies the same extensions to multiple objects + * @template TName - The name identifier for the enhancer + * @template TExtension - The type of the extension object containing new methods + * @param name - Unique name for this enhancement factory + * @param extendObj - Object containing extension methods + * @returns A function that can enhance objects with the provided extensions + */ export const enhanceFactory = ( name: TName, From 42e6bbd4ce8a62461f076e4868414b7baedc567b Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:00:43 +0000 Subject: [PATCH 02/13] docs(fs-cache): add comprehensive documentation and improve package metadata Co-Authored-By: yleflour@pairprog.io --- packages/fs-cache/README.md | 144 ++++++++++++++++++++++++- packages/fs-cache/package.json | 10 +- packages/fs-cache/src/deepEqual.lib.ts | 12 +++ packages/fs-cache/src/fs-cache.lib.ts | 2 - 4 files changed, 163 insertions(+), 5 deletions(-) diff --git a/packages/fs-cache/README.md b/packages/fs-cache/README.md index 47ab659..0835ee7 100644 --- a/packages/fs-cache/README.md +++ b/packages/fs-cache/README.md @@ -1 +1,143 @@ -# @synstack/fs-cache +# @synstack/fs-cache + +> File system-based caching with type-safe function memoization + +This package provides a strongly-typed caching system that stores function results in the file system, with support for custom cache keys and value locking. + +> [!WARNING] +> This package is included in the [@synstack/synscript](https://github.com/pAIrprogio/synscript) package. It is not recommended to install both packages at the same time. + +## What is it for? + +Sometimes you need to cache expensive function results between program runs. This package makes it easy to cache function outputs to disk with type safety: + +```typescript +import { fsCache } from "@synstack/fs-cache"; + +// Create a cache in the .cache directory +const cache = fsCache(".cache") + .key(["expensive", "function"]) + .pretty(true); + +// Cache an expensive function +const expensiveFunction = cache.fn(async (input: string) => { + // Simulate expensive operation + await new Promise(resolve => setTimeout(resolve, 1000)); + return `Processed: ${input}`; +}); + +// First call: takes 1 second +await expensiveFunction("test"); // "Processed: test" + +// Second call: instant (reads from cache) +await expensiveFunction("test"); // "Processed: test" +``` + +## Installation + +```bash +# Using npm +npm install @synstack/fs-cache + +# Using yarn +yarn add @synstack/fs-cache + +# Using pnpm +pnpm add @synstack/fs-cache +``` + +## Features + +### Function Caching + +Cache expensive function results with type safety: + +```typescript +import { fsCache } from "@synstack/fs-cache"; + +const cache = fsCache(".cache"); + +// Cache with static key +const cachedFn = cache + .key("myFunction") + .fn((x: number) => x * x); + +// Cache with dynamic key based on arguments +const cachedFn2 = cache + .key([ + "myFunction", + (arg: string) => arg.length.toString() + ]) + .fn((arg: string) => arg.toUpperCase()); +``` + +### Cache Control + +Fine-grained control over cache behavior: + +```typescript +// Pretty-print cached JSON +const cache = fsCache(".cache").pretty(true); + +// Custom cache key generation +const cache2 = fsCache(".cache") + .signatureFn((arg: string) => arg.toLowerCase()) + .key("normalized"); + +// Lock cached values +await cache.lock(true, ["key"]); // Prevent updates +await cache.lock(false, ["key"]); // Allow updates + +// Manual cache operations +const [status, value] = await cache.get(["key"]); +await cache.set(["key"], "value"); +await cache.setDefault(["key"], "default"); +``` + +## API Reference + +### FsCache + +The main class for file system caching: + +#### Creation +- `fsCache(cwd)` - Create cache in working directory +- `key(keys)` - Set cache key or key generators +- `signatureFn(fn)` - Set input signature function +- `pretty(enabled)` - Enable/disable pretty JSON + +#### Cache Operations +- `get(args)` - Get cached value +- `set(args, value)` - Set cache value +- `setDefault(args, value)` - Set default value +- `lock(isLocked, args)` - Lock/unlock cached value +- `fn(function)` - Create cached function wrapper + +### Types + +#### KeyFn +```typescript +type KeyFn = + | string + | ((...args: TFnArgs) => string); +``` + +#### SignatureFn +```typescript +type SignatureFn = ( + ...args: TFnArgs +) => any; +``` + +## TypeScript Support + +This package is written in TypeScript and provides full type definitions: + +- Generic type parameters for function arguments +- Type-safe function wrapping +- Strongly typed cache operations +- IntelliSense support for all methods + +## License + +Apache-2.0 - see LICENSE file for details. diff --git a/packages/fs-cache/package.json b/packages/fs-cache/package.json index 8b12c1b..d93fac4 100644 --- a/packages/fs-cache/package.json +++ b/packages/fs-cache/package.json @@ -5,10 +5,16 @@ "access": "public" }, "version": "1.3.0", - "description": "Human-friendly file system caching", + "description": "Type-safe file system caching with function memoization and persistent storage", "keywords": [ "cache", - "memoize" + "memoize", + "file-system", + "typescript", + "type-safe", + "persistent", + "function-cache", + "fs" ], "author": { "name": "pAIrprog", diff --git a/packages/fs-cache/src/deepEqual.lib.ts b/packages/fs-cache/src/deepEqual.lib.ts index 9d5efcb..a83f386 100644 --- a/packages/fs-cache/src/deepEqual.lib.ts +++ b/packages/fs-cache/src/deepEqual.lib.ts @@ -1,3 +1,15 @@ +/** + * Performs a deep equality comparison between two values + * @param obj1 - First value to compare + * @param obj2 - Second value to compare + * @returns True if the values are deeply equal, false otherwise + * + * This function handles: + * - Primitive values (strings, numbers, booleans) + * - Objects and arrays (recursive comparison) + * - null and undefined values + * - Properties with undefined values are ignored + */ export function deepEqual(obj1: any, obj2: any) { if (obj1 === obj2) { return true; diff --git a/packages/fs-cache/src/fs-cache.lib.ts b/packages/fs-cache/src/fs-cache.lib.ts index e67a7a9..ad9bbd1 100644 --- a/packages/fs-cache/src/fs-cache.lib.ts +++ b/packages/fs-cache/src/fs-cache.lib.ts @@ -42,7 +42,6 @@ export class FsCache { } as TConfig & { signatureFn: FsCache.SignatureFn }; } - // eslint-disable-next-line @typescript-eslint/require-await private async serializeInput(...args: TFnArgs) { if (this._config.signatureFn) return this._config.signatureFn(...args); return args; @@ -70,7 +69,6 @@ export class FsCache { } private static keyToRelativePath(key: FsCache.Key, args: any[]) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument return `./${key.map((k) => (k instanceof Function ? k(...args) : k)).join("/")}.json`; } From 433c925d467f3620ed381a808787bc0dbec5eca9 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:03:01 +0000 Subject: [PATCH 03/13] docs(git): add comprehensive documentation and improve package metadata Co-Authored-By: yleflour@pairprog.io --- packages/git/README.md | 91 ++++++++++++++++++++++++++++++++++++- packages/git/package.json | 10 ++++ packages/git/src/git.lib.ts | 13 ++++++ 3 files changed, 113 insertions(+), 1 deletion(-) diff --git a/packages/git/README.md b/packages/git/README.md index a194d54..c0c978f 100644 --- a/packages/git/README.md +++ b/packages/git/README.md @@ -1 +1,90 @@ -# @synstack/pipe +# @synstack/git + +> Git operations with TypeScript support + +This package provides a strongly-typed interface for common Git operations, making it easy to interact with Git repositories programmatically. + +> [!WARNING] +> This package is included in the [@synstack/synscript](https://github.com/pAIrprogio/synscript) package. It is not recommended to install both packages at the same time. + +## What is it for? + +When you need to interact with Git repositories programmatically, this package provides a simple and type-safe way to perform common Git operations: + +```typescript +import { ls } from "@synstack/git"; + +// List all files in the repository (tracked, modified, and untracked) +const files = await ls("./my-repo"); +console.log(files); +// [ +// "src/index.ts", +// "package.json", +// "README.md", +// "new-file.txt" // untracked +// ] +``` + +## Installation + +```bash +# Using npm +npm install @synstack/git + +# Using yarn +yarn add @synstack/git + +# Using pnpm +pnpm add @synstack/git +``` + +## Features + +### File Listing + +List all files in a Git repository, including: +- Tracked files +- Modified files +- Untracked files (respecting .gitignore) + +```typescript +import { ls } from "@synstack/git"; + +// List files in current directory +const files = await ls(); + +// List files in specific directory +const repoFiles = await ls("./my-repo"); + +// List files in specific subdirectory +const srcFiles = await ls("./my-repo", "src"); +``` + +## API Reference + +### Functions + +#### `ls(cwd?: string, relativePath?: string): Promise` + +Lists all Git-tracked, modified, and untracked files. + +- **Parameters:** + - `cwd` (optional): Working directory for Git commands (defaults to ".") + - `relativePath` (optional): Path relative to working directory to list files from (defaults to ".") +- **Returns:** Promise resolving to array of file paths +- **Example:** + ```typescript + const files = await ls("./repo", "src"); + // ["src/index.ts", "src/utils.ts"] + ``` + +## TypeScript Support + +This package is written in TypeScript and provides full type definitions: +- Type-safe function parameters +- IntelliSense support for all functions +- Strongly typed return values + +## License + +Apache-2.0 - see LICENSE file for details. diff --git a/packages/git/package.json b/packages/git/package.json index d2263e7..22ee1bc 100644 --- a/packages/git/package.json +++ b/packages/git/package.json @@ -5,6 +5,16 @@ "access": "public" }, "version": "1.1.2", + "description": "Type-safe Git operations interface for programmatic repository management", + "keywords": [ + "git", + "typescript", + "type-safe", + "repository", + "file-listing", + "git-files", + "version-control" + ], "author": { "name": "pAIrprog", "url": "https://pairprog.io" diff --git a/packages/git/src/git.lib.ts b/packages/git/src/git.lib.ts index 7f8777d..ada84dd 100644 --- a/packages/git/src/git.lib.ts +++ b/packages/git/src/git.lib.ts @@ -1,5 +1,18 @@ import { execaCommand } from "execa"; +/** + * Lists all git-tracked, modified, and untracked files in a directory + * @param cwd - Working directory for git commands (defaults to ".") + * @param relativePath - Path relative to working directory to list files from (defaults to ".") + * @returns Promise resolving to array of file paths + * + * This function combines the output of multiple git commands to list: + * - Tracked files (git ls-files) + * - Modified files (git ls-files -m) + * - Untracked files (git ls-files --others --exclude-standard) + * + * Empty paths are filtered out from the results. + */ export async function ls(cwd: string = ".", relativePath: string = ".") { const res = await execaCommand( `( git ls-files ${relativePath}; git ls-files -m ${relativePath} ; git ls-files --others --exclude-standard ${relativePath} ) | sort | uniq`, From f8521ef831f0c9a14995c9f6e8d11a1c4427fea7 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:06:59 +0000 Subject: [PATCH 04/13] docs(glob): add comprehensive documentation and improve package metadata Co-Authored-By: yleflour@pairprog.io --- packages/glob/README.md | 147 +++++++++++++++++++++++++++++++++- packages/glob/package.json | 12 +++ packages/glob/src/glob.lib.ts | 19 ++++- 3 files changed, 174 insertions(+), 4 deletions(-) diff --git a/packages/glob/README.md b/packages/glob/README.md index 6029d93..eeea4ca 100644 --- a/packages/glob/README.md +++ b/packages/glob/README.md @@ -1 +1,146 @@ -# @synstack/markdown +# @synstack/glob + +> Type-safe glob pattern matching and file filtering utilities + +This package provides a strongly-typed interface for working with glob patterns, including file matching, filtering, and pattern capturing capabilities. + +> [!WARNING] +> This package is included in the [@synstack/synscript](https://github.com/pAIrprogio/synscript) package. It is not recommended to install both packages at the same time. + +## What is it for? + +When you need to find files using glob patterns or filter paths based on patterns, this package provides type-safe utilities: + +```typescript +import { glob, matches } from "@synstack/glob"; + +// Find all TypeScript files +const files = await glob.cwd("./src").find("**/*.ts"); +console.log(files); +// ["src/index.ts", "src/utils.ts"] + +// Check if a file matches a pattern +const isMatch = matches("src/file.ts", "**/*.ts"); +console.log(isMatch); // true + +// Exclude test files +const nonTestFiles = await glob.cwd().find(["**/*.ts", "!**/*.test.ts"]); +``` + +## Installation + +```bash +# Using npm +npm install @synstack/glob + +# Using yarn +yarn add @synstack/glob + +# Using pnpm +pnpm add @synstack/glob +``` + +## Features + +### File Finding + +Find files using glob patterns with support for exclusions: + +```typescript +import { glob } from "@synstack/glob"; + +// Find all files in a directory +const allFiles = await glob.cwd("./src").find("**/*"); + +// Find with multiple patterns +const tsFiles = await glob.cwd().find([ + "**/*.ts", // Include TypeScript files + "!**/*.test.ts", // Exclude test files + "!**/node_modules/**" // Exclude node_modules +]); + +// Synchronous finding +const configFiles = glob.cwd().findSync("**/*.config.ts"); +``` + +### Pattern Matching + +Check if paths match glob patterns: + +```typescript +import { matches, filterFactory } from "@synstack/glob"; + +// Simple matching +matches("src/file.ts", "**/*.ts"); // true +matches("test/file.ts", "!test/**"); // false + +// Create reusable filters +const filter = filterFactory([ + "**/*.ts", // Include TypeScript + "!**/*.test.ts" // Exclude tests +]); + +filter("src/utils.ts"); // true +filter("src/test.test.ts"); // false +``` + +### Pattern Capturing + +Extract values from glob patterns: + +```typescript +import { capture } from "@synstack/glob"; + +// Capture values from paths +const values = capture("**/(*)/(*).ts", "src/utils/format.ts"); +console.log(values); // ["utils", "format"] +``` + +## API Reference + +### Glob Class + +Static factory and instance methods for file finding: + +#### Creation +- `glob.cwd(dir?)` - Create instance with working directory +- `new Glob(cwd)` - Constructor (prefer using `cwd()`) + +#### Methods +- `find(patterns)` - Find files asynchronously +- `findSync(patterns)` - Find files synchronously + +### Functions + +#### `matches(path, patterns)` +Check if a path matches glob patterns: +- `path` - File path to check +- `patterns` - Glob pattern(s), prefix with "!" to exclude + +#### `capture(pattern, path)` +Extract values from a glob pattern: +- `pattern` - Glob pattern with (*) captures +- `path` - Path to extract from +- Returns captured values or null + +#### `filterFactory(patterns)` +Create a path filter function: +- `patterns` - Array of glob patterns +- Returns function that tests paths + +#### `sort(patterns)` +Split patterns into includes and excludes: +- `patterns` - Array of glob patterns +- Returns `{ includes, excludes }` + +## TypeScript Support + +This package is written in TypeScript and provides full type definitions: +- Type-safe function parameters +- Generic type parameters +- IntelliSense support +- Strongly typed return values + +## License + +Apache-2.0 - see LICENSE file for details. diff --git a/packages/glob/package.json b/packages/glob/package.json index 268d957..ae5cc68 100644 --- a/packages/glob/package.json +++ b/packages/glob/package.json @@ -5,6 +5,18 @@ "access": "public" }, "version": "1.1.3", + "description": "Type-safe glob pattern matching and file filtering utilities for Node.js", + "keywords": [ + "glob", + "pattern-matching", + "file-filtering", + "typescript", + "type-safe", + "filesystem", + "minimatch", + "file-search", + "path-matching" + ], "author": { "name": "pAIrprog", "url": "https://pairprog.io" diff --git a/packages/glob/src/glob.lib.ts b/packages/glob/src/glob.lib.ts index 1c18820..5bedfed 100644 --- a/packages/glob/src/glob.lib.ts +++ b/packages/glob/src/glob.lib.ts @@ -32,9 +32,18 @@ function flatten(array: Array | [Array]): Array { } /** - * @param filePath - * @param globs list of globs to match against globs prefixed with ! are excluded - * @returns boolean + * Checks if a file path matches any of the provided glob patterns + * @param filePath - Path to check against glob patterns + * @param globs - List of glob patterns to match against. Patterns prefixed with "!" are treated as exclusions + * @returns true if the path matches any include pattern and doesn't match any exclude pattern + * + * @example + * // Match TypeScript files + * matches("src/file.ts", "src/*.ts"); + * // Exclude specific directory + * matches("src/file.ts", "!test/*"); + * // Combine include and exclude patterns + * matches("src/file.ts", ["src/*.ts", "!test/*"]); */ export function matches( filePath: string, @@ -134,6 +143,10 @@ export class Glob { } } +/** + * Error thrown when an invalid glob pattern is provided + * @throws When a glob pattern cannot be converted to a regular expression + */ export class InvalidGlobException extends Error { constructor(glob: string) { super(`Invalid glob: ${glob}`); From 51af6110284a663e98f322ba0d1ec272a5eaef06 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:09:11 +0000 Subject: [PATCH 05/13] docs(json): add comprehensive documentation Co-Authored-By: yleflour@pairprog.io --- packages/json/README.md | 162 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 1 deletion(-) diff --git a/packages/json/README.md b/packages/json/README.md index 3b0c527..20ba14d 100644 --- a/packages/json/README.md +++ b/packages/json/README.md @@ -1 +1,161 @@ -# @synstack/json +# @synstack/json + +> Type-safe JSON serialization and deserialization with schema validation + +This package provides a strongly-typed interface for working with JSON data, including serialization, deserialization, and schema validation using Zod. + +> [!WARNING] +> This package is included in the [@synstack/synscript](https://github.com/pAIrprogio/synscript) package. It is not recommended to install both packages at the same time. + +## What is it for? + +When you need to work with JSON data in a type-safe way, this package provides simple, strongly-typed functions: + +```typescript +import { serialize, deserialize } from "@synstack/json"; +import { z } from "zod"; + +// Define a schema for type safety +const UserSchema = z.object({ + name: z.string(), + age: z.number(), +}); + +// Serialize data with schema validation +const jsonString = serialize( + { name: "John", age: 30 }, + { schema: UserSchema, pretty: true } +); + +// Deserialize with type inference +const user = deserialize(jsonString, { schema: UserSchema }); +console.log(user.name); // Type-safe access + +// Handle invalid JSON gracefully +try { + deserialize("invalid json"); +} catch (error) { + if (error instanceof JsonParseException) { + console.error("Failed to parse JSON:", error.message); + } +} +``` + +## Installation + +```bash +# Using npm +npm install @synstack/json + +# Using yarn +yarn add @synstack/json + +# Using pnpm +pnpm add @synstack/json +``` + +## Features + +### JSON Serialization + +Convert JavaScript objects to JSON strings with optional pretty printing: + +```typescript +import { serialize } from "@synstack/json"; + +// Basic serialization +const json = serialize({ hello: "world" }); + +// Pretty printing +const prettyJson = serialize({ hello: "world" }, { pretty: true }); +console.log(prettyJson); +// { +// "hello": "world" +// } +``` + +### Type-Safe Deserialization + +Parse JSON strings with TypeScript type inference: + +```typescript +import { deserialize } from "@synstack/json"; + +// Basic deserialization +const data = deserialize<{ count: number }>( + '{"count": 42}' +); +console.log(data.count); // TypeScript knows this is a number + +// Handle parsing errors +try { + deserialize('{"invalid": json}'); +} catch (error) { + console.error("Invalid JSON:", error.message); +} +``` + +### Schema Validation + +Use Zod schemas for runtime type checking: + +```typescript +import { serialize, deserialize } from "@synstack/json"; +import { z } from "zod"; + +// Define a schema +const ConfigSchema = z.object({ + port: z.number(), + host: z.string(), + debug: z.boolean(), +}); + +// Validate during serialization +const jsonString = serialize( + { port: 3000, host: "localhost", debug: true }, + { schema: ConfigSchema } +); + +// Validate during deserialization +const config = deserialize(jsonString, { + schema: ConfigSchema, +}); +// config has type { port: number; host: string; debug: boolean } +``` + +## API Reference + +### Functions + +#### `serialize(data, config?)` +Convert data to a JSON string: +- `data` - The data to serialize +- `config.pretty` - Whether to pretty print (indent with 2 spaces) +- `config.schema` - Optional Zod schema for validation +- Returns: JSON string + +#### `deserialize(content, config?)` +Parse a JSON string to data: +- `content` - The JSON string to parse +- `config.schema` - Optional Zod schema for validation +- Returns: Parsed data of type T + +### Classes + +#### `JsonParseException` +Error thrown when JSON parsing fails: +- Contains detailed error message +- Includes original JSON string +- Preserves underlying cause + +## TypeScript Support + +This package is written in TypeScript and provides full type definitions: +- Generic type parameters for deserialization +- Zod schema integration for runtime validation +- Type inference from schemas +- Strongly typed error handling + +## License + +Apache-2.0 - see LICENSE file for details. From afb3ff6994392350dbb52248bf0240ad5ec83cce Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:14:45 +0000 Subject: [PATCH 06/13] docs(llm): add documentation for types and message builders Co-Authored-By: yleflour@pairprog.io --- packages/llm/src/llm.types.ts | 58 +++++++++++++++++++++++++++++ packages/llm/src/message.builder.ts | 21 +++++++++++ 2 files changed, 79 insertions(+) diff --git a/packages/llm/src/llm.types.ts b/packages/llm/src/llm.types.ts index 4ea4677..0a8febe 100644 --- a/packages/llm/src/llm.types.ts +++ b/packages/llm/src/llm.types.ts @@ -3,7 +3,16 @@ import { type OneToN } from "../../shared/src/ts.utils.ts"; type $Partial = Partial; +/** + * Core namespace for LLM (Large Language Model) interactions + * Contains types for messages, completions, and tools + */ export declare namespace Llm { + /** + * Represents a tool that can be used by the LLM + * @template TName - The name of the tool + * @template TSchema - The Zod schema defining the tool's input parameters + */ export type Tool< TName extends string = string, TSchema extends ZodSchema = AnyZodObject, @@ -12,6 +21,19 @@ export declare namespace Llm { schema: TSchema; // Todo: force object schema }; + /** + * Configuration for an LLM completion request + * @property temperature - Controls randomness in the model's output (0-1) + * @property maxTokens - Maximum number of tokens to generate + * @property messages - Array of messages in the conversation + * @property system - Optional system message to set context + * @property topK - Optional parameter for top-k sampling + * @property topP - Optional parameter for nucleus sampling + * @property stopSequences - Optional array of sequences that will stop generation + * @property toolsConfig - Optional configuration for tool usage + * @property usage - Optional token usage statistics + * @property stopReason - Optional reason why generation stopped + */ export type Completion = { temperature: number; maxTokens: number; @@ -72,11 +94,23 @@ export declare namespace Llm { export type Partial = $Partial; } + /** + * Represents a message in the LLM conversation + * Can be either a user message or an assistant message + */ export type Message = User.Message | Assistant.Message; export namespace Message { + /** + * Role of the message sender + * Can be either "user" or "assistant" + */ export type Role = User.Role | Assistant.Role; + /** + * Content that can be included in a message + * Includes text, images, tool calls, and tool responses + */ export type Content = | Content.Text | Content.Image @@ -148,25 +182,49 @@ export declare namespace Llm { } } + /** + * Namespace for user-specific message types and content + */ export namespace User { export type Role = "user"; + /** + * Content types that can be included in a user message + * Supports text, images, and tool responses + */ export type Content = | Message.Content.Text | Message.Content.Image | Message.Content.ToolResponse; + /** + * Represents a message from the user + * @property role - Always "user" + * @property content - Array of content elements (text, images, tool responses) + */ export type Message = { role: Role; content: Array; }; } + /** + * Namespace for assistant-specific message types and content + */ export namespace Assistant { export type Role = "assistant"; + /** + * Content types that can be included in an assistant message + * Supports text and tool calls + */ export type Content = Message.Content.Text | Message.Content.ToolCall; + /** + * Represents a message from the assistant + * @property role - Always "assistant" + * @property content - Array of content elements (text, tool calls) + */ export type Message = { role: Role; content: Array; diff --git a/packages/llm/src/message.builder.ts b/packages/llm/src/message.builder.ts index 7cc31c4..a311417 100644 --- a/packages/llm/src/message.builder.ts +++ b/packages/llm/src/message.builder.ts @@ -6,6 +6,17 @@ import { type Base64Data } from "../../fs/src/file.lib.ts"; import { t, type Text, tParse } from "@synstack/text"; import { type Llm } from "./llm.types.ts"; +/** + * Creates a user message with support for text, images, and tool responses + * @param template - Template string containing the message content + * @param values - Values to interpolate into the template (text, base64 images, tool responses) + * @returns A strongly-typed user message object conforming to Llm.User.Message + * @example + * ```typescript + * const msg = userMsg`Hello, here's an image: ${base64Image}`; + * const msg2 = userMsg`Tool response: ${toolResponse}`; + * ``` + */ export const userMsg = < T extends Array = Array, @@ -36,6 +47,16 @@ export const userMsg = < }) as Llm.User.Message, ).$; +/** + * Creates an assistant message with support for text and tool calls + * @param template - Template string containing the message content + * @param values - Values to interpolate into the template (text, tool calls) + * @returns A strongly-typed assistant message object conforming to Llm.Assistant.Message + * @example + * ```typescript + * const msg = assistantMsg`Let me help you with that ${toolCall}`; + * ``` + */ export const assistantMsg = < T extends Array = Array, From cda21dba4059de0d244158875a03e8bda110705f Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:17:39 +0000 Subject: [PATCH 07/13] docs(llm): add documentation for content classes and message contents Co-Authored-By: yleflour@pairprog.io --- packages/llm/src/contents.lib.ts | 61 ++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/packages/llm/src/contents.lib.ts b/packages/llm/src/contents.lib.ts index f7ce79c..2b3a962 100644 --- a/packages/llm/src/contents.lib.ts +++ b/packages/llm/src/contents.lib.ts @@ -3,6 +3,15 @@ import { type Llm } from "./llm.types.ts"; // Todo: decide to delete or not +/** + * Represents text content in an LLM message + * Provides a type-safe wrapper for text content with value conversion methods + * @example + * ```typescript + * const text = TextContent.from("Hello, world!"); + * const value = text.valueOf(); // Convert to Llm.Message.Content.Text + * ``` + */ export class TextContent { constructor(public readonly _text: string) {} @@ -30,6 +39,15 @@ export class TextContent { } } +/** + * Represents image content in an LLM message + * Provides a type-safe wrapper for base64-encoded images with value conversion methods + * @example + * ```typescript + * const image = ImageContent.from({ type: "base64", data: "...", mimeType: "image/png" }); + * const value = image.valueOf(); // Convert to Llm.Message.Content.Image + * ``` + */ export class ImageContent { constructor(public readonly _image: Llm.Message.Content.Image.Base64) {} @@ -53,6 +71,15 @@ export class ImageContent { } } +/** + * Represents a tool call in an LLM message + * Provides a type-safe wrapper for tool calls with value conversion methods + * @example + * ```typescript + * const toolCall = ToolCallContent.from({ toolCallId: "123", toolName: "calculator", toolArgs: { x: 1, y: 2 } }); + * const value = toolCall.valueOf(); // Convert to Llm.Message.Content.ToolCall + * ``` + */ export class ToolCallContent { constructor(public readonly _toolCall: Llm.Message.Content.ToolCall) {} @@ -73,6 +100,15 @@ export class ToolCallContent { } } +/** + * Represents a tool response in an LLM message + * Provides a type-safe wrapper for tool responses with value conversion methods + * @example + * ```typescript + * const response = ToolResponseContent.from({ toolCallId: "123", toolOutput: { result: 3 } }); + * const value = response.valueOf(); // Convert to Llm.Message.Content.ToolResponse + * ``` + */ export class ToolResponseContent { constructor( public readonly _toolResponse: Omit< @@ -104,13 +140,24 @@ export class ToolResponseContent { } } +/** + * Union type of all possible message content types + * Used for type-safe message content handling + */ type MessageContent = | TextContent | ImageContent | ToolCallContent | ToolResponseContent; +/** + * Interface defining additional methods available on message content arrays + */ interface ContentsMethods { + /** + * Filters the array to return only tool call contents + * @returns Array of ToolCallContent objects + */ toolCalls(this: Array): Array; } @@ -120,12 +167,26 @@ const customMethods: ContentsMethods = { }, }; +/** + * Enhanced type for arrays of message contents + * Adds utility methods for working with message content arrays + */ type MessageContents = Enhanced< "contents", Array, ContentsMethods >; +/** + * Creates an enhanced array of message contents with additional utility methods + * @param contents - Array of message content objects (text, images, tool calls, tool responses) + * @returns Enhanced array with additional methods like toolCalls() for filtering tool call contents + * @example + * ```typescript + * const contents = MessageContents([textContent, toolCallContent]); + * const toolCalls = contents.toolCalls(); // Get all tool calls + * ``` + */ export const MessageContents = ( contents: Array, ): MessageContents => From 27b1d8f2bb755b9d10d3879a167d34c7cd78d0fd Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:20:10 +0000 Subject: [PATCH 08/13] docs(llm): update package documentation and metadata Co-Authored-By: yleflour@pairprog.io --- packages/llm/README.md | 154 +++++++++++++++++++++++++++++++++++++- packages/llm/package.json | 8 +- 2 files changed, 159 insertions(+), 3 deletions(-) diff --git a/packages/llm/README.md b/packages/llm/README.md index 3b0c527..c0d2820 100644 --- a/packages/llm/README.md +++ b/packages/llm/README.md @@ -1 +1,153 @@ -# @synstack/json +# @synstack/llm + +> Type-safe LLM message handling and content management + +This package provides a strongly-typed API for working with LLM messages, including support for text, images, tool calls, and tool responses. + +> [!WARNING] +> This package is included in the [@synstack/synscript](https://github.com/pAIrprogio/synscript) package. It is not recommended to install both packages at the same time. + +## What is it for? + +Working with LLM messages should be type-safe and intuitive. This package provides a structured way to create and handle different types of message content: + +```typescript +import { userMsg, assistantMsg, TextContent, ToolCallContent } from "@synstack/llm"; + +// Create strongly-typed user messages with text and images +const message = userMsg`Here's my question: ${TextContent.from("How does this work?")}`; + +// Handle tool calls and responses in assistant messages +const toolCall = ToolCallContent.from({ + toolCallId: "123", + toolName: "calculator", + toolArgs: { x: 1, y: 2 } +}); +const response = assistantMsg`Let me calculate that for you ${toolCall}`; +``` + +## Installation + +```bash +# Using npm +npm install @synstack/llm + +# Using yarn +yarn add @synstack/llm + +# Using pnpm +pnpm add @synstack/llm +``` + +## Features + +### Message Building + +Create type-safe messages using template literals: + +```typescript +import { userMsg, assistantMsg } from "@synstack/llm"; + +// User messages with text and images +const userMessage = userMsg`Here's an image: ${imageContent}`; + +// Assistant messages with tool calls +const assistantMessage = assistantMsg`Let me help you with that ${toolCall}`; +``` + +### Content Types + +Work with different types of message content: + +```typescript +import { TextContent, ImageContent, ToolCallContent, ToolResponseContent } from "@synstack/llm"; + +// Text content +const text = TextContent.from("Hello, world!"); + +// Image content (base64) +const image = ImageContent.from({ + type: "base64", + data: "...", + mimeType: "image/png" +}); + +// Tool calls +const toolCall = ToolCallContent.from({ + toolCallId: "123", + toolName: "calculator", + toolArgs: { x: 1, y: 2 } +}); + +// Tool responses +const toolResponse = ToolResponseContent.from({ + toolCallId: "123", + toolOutput: { result: 3 } +}); +``` + +### Message Content Arrays + +Handle collections of message contents: + +```typescript +import { MessageContents } from "@synstack/llm"; + +// Create enhanced array of contents +const contents = MessageContents([textContent, toolCallContent]); + +// Filter tool calls +const toolCalls = contents.toolCalls(); +``` + +## API Reference + +### Message Builders + +#### userMsg +- Template literal function for creating user messages +- Supports text, images, and tool responses + +#### assistantMsg +- Template literal function for creating assistant messages +- Supports text and tool calls + +### Content Classes + +#### TextContent +- `from(text: string)` - Create text content +- `valueOf()` - Convert to message content format +- `toString()` - Get raw text content + +#### ImageContent +- `from(image: Base64Image)` - Create image content +- `valueOf()` - Convert to message content format +- `image` - Access raw image data + +#### ToolCallContent +- `from(toolCall: ToolCall)` - Create tool call content +- `valueOf()` - Convert to message content format +- `toolCall` - Access tool call details + +#### ToolResponseContent +- `from(toolResponse: ToolResponse)` - Create tool response content +- `valueOf()` - Convert to message content format +- `toolResponse` - Access tool response details + +### MessageContents + +Enhanced array type with additional methods: +- `toolCalls()` - Filter and return tool call contents + +## TypeScript Support + +This package is written in TypeScript and provides comprehensive type definitions: + +- Generic type parameters for message content +- Type-safe message builders using template literals +- Strongly typed content classes +- Type inference for tool calls and responses + +## License + +Apache-2.0 - see LICENSE file for details. diff --git a/packages/llm/package.json b/packages/llm/package.json index e171b38..e75124f 100644 --- a/packages/llm/package.json +++ b/packages/llm/package.json @@ -5,14 +5,18 @@ "access": "public" }, "version": "1.4.4", - "description": "Immutable & chainable LLM tools", + "description": "Type-safe LLM message handling with support for text, images, tool calls, and responses", "keywords": [ "llm", "prompt", "ai", "immutable", "anthropic", - "openai" + "openai", + "type-safe", + "typescript", + "message", + "tool-calls" ], "author": { "name": "pAIrprog", From b767263fe1bc86908a3fc9ab5cc14e26ee6f354a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:24:22 +0000 Subject: [PATCH 09/13] docs(markdown): add comprehensive documentation and improve package metadata Co-Authored-By: yleflour@pairprog.io --- packages/markdown/README.md | 155 +++++++++++++++++++++++++- packages/markdown/package.json | 10 +- packages/markdown/src/markdown.lib.ts | 112 +++++++++++++++++++ 3 files changed, 274 insertions(+), 3 deletions(-) diff --git a/packages/markdown/README.md b/packages/markdown/README.md index 6029d93..0f96819 100644 --- a/packages/markdown/README.md +++ b/packages/markdown/README.md @@ -1 +1,154 @@ -# @synstack/markdown +# @synstack/markdown + +> Type-safe markdown processing with YAML frontmatter support + +This package provides a strongly-typed API for working with markdown documents, including HTML conversion and YAML frontmatter handling with schema validation. + +> [!WARNING] +> This package is included in the [@synstack/synscript](https://github.com/pAIrprogio/synscript) package. It is not recommended to install both packages at the same time. + +## What is it for? + +Working with markdown documents should be type-safe and intuitive. This package provides tools for converting HTML to markdown, managing YAML frontmatter, and handling markdown content: + +```typescript +import { fromHtml, MdDoc } from "@synstack/markdown"; + +// Convert HTML to markdown +const markdown = fromHtml("

Hello

"); +// Returns: "# Hello" + +// Work with markdown documents and frontmatter +interface PostFrontmatter { + title: string; + date: string; +} + +const doc = MdDoc + .withOptions({ schema: postSchema }) + .fromString(`--- +title: Hello World +date: 2024-01-01 +--- +# Content here`); + +// Access typed frontmatter data +console.log(doc.data.title); // "Hello World" + +// Update content while preserving frontmatter +const updated = doc.setBody("# New content"); +``` + +## Installation + +```bash +# Using npm +npm install @synstack/markdown + +# Using yarn +yarn add @synstack/markdown + +# Using pnpm +pnpm add @synstack/markdown +``` + +## Features + +### HTML to Markdown Conversion + +Convert HTML content to markdown with consistent styling: + +```typescript +import { fromHtml } from "@synstack/markdown"; + +const markdown = fromHtml("

Title

Content

"); +// Returns: +// # Title +// +// Content +``` + +### YAML Frontmatter Handling + +Work with YAML frontmatter in markdown documents: + +```typescript +import { getHeaderData, setHeaderData } from "@synstack/markdown"; + +// Extract frontmatter data +const data = getHeaderData("---\ntitle: Hello\n---\n# Content"); +// Returns: { title: "Hello" } + +// Set frontmatter data +const text = setHeaderData("# Content", { title: "Hello" }); +// Returns: "---\ntitle: Hello\n---\n# Content" +``` + +### Type-safe Document Management (MdDoc) + +Handle markdown documents with type-safe frontmatter: + +```typescript +import { MdDoc } from "@synstack/markdown"; + +// Create from markdown string +const doc = MdDoc.fromString(`--- +title: Hello +--- +# Content`); + +// Create from HTML +const htmlDoc = MdDoc.fromHtml("

Title

"); + +// Update content +const updated = doc + .setData({ title: "New Title" }) + .setBody("# Updated content"); +``` + +## API Reference + +### HTML Conversion + +- `fromHtml(html)` - Convert HTML content to markdown + +### Frontmatter Operations + +- `getHeaderData(text, options?)` - Extract and parse YAML frontmatter +- `setHeaderData(text, data, options?)` - Set YAML frontmatter +- `getBody(text)` - Get markdown content without frontmatter +- `setBody(text, body)` - Set markdown content while preserving frontmatter + +### MdDoc Class + +#### Creation +- `MdDoc.withOptions(options)` - Create instance with validation options +- `MdDoc.fromString(text)` - Create from markdown text +- `MdDoc.fromHtml(html)` - Create from HTML content + +#### Properties +- `body` - Get markdown body content +- `data` - Get frontmatter data +- `header` - Get serialized YAML frontmatter +- `options` - Get validation options + +#### Methods +- `fromString(text)` - Create new instance from markdown text +- `fromHtml(html)` - Create new instance from HTML +- `setData(data)` - Update frontmatter data +- `setBody(text)` - Update markdown content +- `toMd()` - Convert to markdown string +- `toString()` - Convert to string (alias for toMd) + +## TypeScript Support + +This package is written in TypeScript and provides comprehensive type definitions: + +- Generic type parameters for frontmatter data +- Zod schema validation for frontmatter +- Type-safe document operations +- Strongly typed HTML conversion + +## License + +Apache-2.0 - see LICENSE file for details. diff --git a/packages/markdown/package.json b/packages/markdown/package.json index 950b69c..3c3b893 100644 --- a/packages/markdown/package.json +++ b/packages/markdown/package.json @@ -5,10 +5,16 @@ "access": "public" }, "version": "1.1.2", - "description": "Opiniated Mardown utils", + "description": "Type-safe markdown processing with YAML frontmatter support and HTML conversion capabilities", "keywords": [ "markdown", - "html" + "html", + "frontmatter", + "yaml", + "type-safe", + "documentation", + "turndown", + "typescript" ], "author": { "name": "pAIrprog", diff --git a/packages/markdown/src/markdown.lib.ts b/packages/markdown/src/markdown.lib.ts index b291d44..ad5a912 100644 --- a/packages/markdown/src/markdown.lib.ts +++ b/packages/markdown/src/markdown.lib.ts @@ -3,6 +3,16 @@ import TurndownService from "turndown"; import { ZodSchema } from "zod"; import { type Stringable } from "../../shared/src/ts.utils.ts"; +/** + * Converts HTML content to Markdown format with consistent styling + * @param html - HTML content to convert + * @returns Markdown formatted string + * @example + * ```typescript + * const markdown = fromHtml("

Hello

"); + * // Returns: "# Hello" + * ``` + */ export const fromHtml = (html: Stringable) => { const turndown = new TurndownService({ headingStyle: "atx", @@ -18,6 +28,17 @@ export const fromHtml = (html: Stringable) => { const HEADER_REGEX = /^---\n([\s\S]*?)\n---\n/; +/** + * Extracts and parses YAML frontmatter from markdown text + * @param text - Markdown text with optional YAML frontmatter + * @param options - Optional schema for validating frontmatter data + * @returns Parsed frontmatter data or undefined if no frontmatter exists + * @example + * ```typescript + * const data = getHeaderData("---\ntitle: Hello\n---\n# Content"); + * // Returns: { title: "Hello" } + * ``` + */ export const getHeaderData = ( text: Stringable, { schema }: { schema?: ZodSchema } = {}, @@ -27,6 +48,18 @@ export const getHeaderData = ( return yaml.deserialize(header, { schema }); }; +/** + * Sets YAML frontmatter in markdown text + * @param text - Markdown text to update + * @param data - Data to serialize as YAML frontmatter + * @param options - Optional schema for validating frontmatter data + * @returns Markdown text with updated frontmatter + * @example + * ```typescript + * const text = setHeaderData("# Content", { title: "Hello" }); + * // Returns: "---\ntitle: Hello\n---\n# Content" + * ``` + */ export const setHeaderData = ( text: Stringable, data: TFormat, @@ -35,15 +68,53 @@ export const setHeaderData = ( return `---\n${yaml.serialize(data, { schema: options.schema })}---\n${getBody(text.toString())}`; }; +/** + * Removes YAML frontmatter from markdown text + * @param text - Markdown text with optional frontmatter + * @returns Markdown text without frontmatter + * @example + * ```typescript + * const body = getBody("---\ntitle: Hello\n---\n# Content"); + * // Returns: "# Content" + * ``` + */ export const getBody = (text: string) => { return text.replace(HEADER_REGEX, ""); }; +/** + * Sets the body content of markdown text while preserving frontmatter + * @param text - Original markdown text with optional frontmatter + * @param body - New body content + * @returns Updated markdown text with preserved frontmatter + * @example + * ```typescript + * const text = setBody("---\ntitle: Hello\n---\nold", "new"); + * // Returns: "---\ntitle: Hello\n---\nnew" + * ``` + */ export const setBody = (text: string, body: string) => { const header = text.match(HEADER_REGEX)?.[0]; return `${header ?? ""}${body}`; }; +/** + * Type-safe markdown document with optional YAML frontmatter + * @template TShape - Shape of the frontmatter data + * @template TData - Type of the current frontmatter data + * @template TBody - Type of the current body content + * @example + * ```typescript + * interface PostFrontmatter { + * title: string; + * date: string; + * } + * + * const doc = MdDoc + * .withOptions({ schema: postSchema }) + * .fromString("---\ntitle: Hello\ndate: 2024-01-01\n---\n# Content"); + * ``` + */ export class MdDoc< TShape = never, TData extends TShape | undefined = never, @@ -63,6 +134,11 @@ export class MdDoc< this._options = options ?? {}; } + /** + * Creates a new MdDoc instance with validation options + * @param options - Schema for validating frontmatter data + * @returns Empty MdDoc instance with specified options + */ public static withOptions( this: void, options: { schema?: ZodSchema }, @@ -74,6 +150,11 @@ export class MdDoc< ); } + /** + * Creates a new MdDoc from markdown text + * @param text - Markdown text with optional frontmatter + * @returns MdDoc instance with parsed frontmatter and body + */ public static fromString(this: void, text: string) { return new MdDoc( getHeaderData(text) as TShape, @@ -81,26 +162,40 @@ export class MdDoc< ); } + /** + * Creates a new MdDoc from HTML content + * @param html - HTML content to convert to markdown + * @returns MdDoc instance with converted markdown body + */ public static fromHtml(this: void, html: string) { return new MdDoc(undefined, fromHtml(html)); } + /** Gets the markdown body content */ public get body(): string { return this._body ?? ""; } + /** Gets the frontmatter data */ public get data(): TData { return this._data; } + /** Gets the serialized YAML frontmatter */ public get header(): string { return this._data ? `---\n${yaml.serialize(this._data)}---\n` : ""; } + /** Gets the validation options */ public get options(): { schema?: ZodSchema } { return this._options; } + /** + * Creates a new MdDoc from markdown text using current options + * @param text - Markdown text with optional frontmatter + * @returns New MdDoc instance with parsed content + */ public fromString(text: string) { const validatedData = getHeaderData(text, { schema: this._options.schema, @@ -112,10 +207,20 @@ export class MdDoc< ); } + /** + * Creates a new MdDoc from HTML content using current options + * @param html - HTML content to convert + * @returns New MdDoc instance with converted content + */ public fromHtml(html: string) { return new MdDoc(this._data, fromHtml(html), this._options); } + /** + * Updates frontmatter data with validation + * @param data - New frontmatter data + * @returns New MdDoc instance with updated data + */ public setData(data: TShape) { const validatedData = this._options.schema ? this._options.schema.parse(data) @@ -123,14 +228,21 @@ export class MdDoc< return new MdDoc(validatedData, this._body, this._options); } + /** + * Updates markdown body content + * @param text - New markdown content + * @returns New MdDoc instance with updated body + */ public setBody(text: string) { return new MdDoc(this._data, text, this._options); } + /** Alias for toString() */ public toMd() { return this.toString(); } + /** Converts document to markdown string with frontmatter */ public toString() { return `${this.header}${this.body}`; } From cbc2329595c3d835393c8b2dea175c0d7e758557 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 22:09:43 +0000 Subject: [PATCH 10/13] docs(llm): comprehensive documentation with type-safe examples Co-Authored-By: yleflour@pairprog.io --- README.md | 15 ++- packages/enhance/README.md | 54 ++++----- packages/enhance/package.json | 11 +- packages/fs-cache/src/fs-cache.lib.ts | 2 + packages/llm/README.md | 102 +++++++++------- packages/llm/docs/completion.md | 130 +++++++++++++++++++++ packages/llm/docs/message-templating.md | 118 +++++++++++++++++++ packages/llm/docs/runners.md | 117 +++++++++++++++++++ packages/llm/docs/tool-calls.md | 147 ++++++++++++++++++++++++ packages/llm/package.json | 3 +- 10 files changed, 624 insertions(+), 75 deletions(-) create mode 100644 packages/llm/docs/completion.md create mode 100644 packages/llm/docs/message-templating.md create mode 100644 packages/llm/docs/runners.md create mode 100644 packages/llm/docs/tool-calls.md diff --git a/README.md b/README.md index 0c59e78..7769cd0 100644 --- a/README.md +++ b/README.md @@ -16,17 +16,26 @@ - [@synstack/xml](./packages/xml/README.md) - Lax, non spec-compliant XML utils tailored for LLMs - [@synstack/yaml](./packages/yaml/README.md) - Safe and opiniated YAML serialization and deserialization -#### Functional programming +#### System Utilities + +- [@synstack/enhance](./packages/enhance/README.md) - Utility functions for enhancing objects and functions with additional capabilities +- [@synstack/fs-cache](./packages/fs-cache/README.md) - File system caching with deep equality checks and automatic invalidation +- [@synstack/git](./packages/git/README.md) - Git operations with type-safe command building and execution +- [@synstack/glob](./packages/glob/README.md) - Type-safe glob pattern matching and file filtering utilities + +#### Functional Programming - [@synstack/pipe](./packages/pipe/README.md) - Simple typesafe pipe utility for Functional Programming - [@synstack/resolved](./packages/resolved/README.md) - A piping utility which preserves the sync/async state of the value -#### Text manipulation +#### Text and Document Processing +- [@synstack/llm](./packages/llm/README.md) - Type-safe LLM message handling with support for text, images, and tool calls +- [@synstack/markdown](./packages/markdown/README.md) - Type-safe markdown processing with YAML frontmatter support - [@synstack/str](./packages/str/README.md) - Advanced chainable string manipulation - [@synstack/text](./packages/text/README.md) - String templating as it was meant to be -#### Web scraping +#### Web Scraping - [@synstack/web](./packages/web/README.md) - Web scraping utilities diff --git a/packages/enhance/README.md b/packages/enhance/README.md index 95ed5ba..8be7bfe 100644 --- a/packages/enhance/README.md +++ b/packages/enhance/README.md @@ -1,8 +1,8 @@ # @synstack/enhance -> Safely enhance JavaScript objects with additional properties +> Type-safe object enhancement with proxy-based method extension -This package provides a type-safe way to extend JavaScript objects with additional methods while maintaining access to the original object. +This package provides a type-safe way to extend JavaScript objects with additional methods while maintaining access to the original object through proxies. > [!WARNING] > This package is included in the [@synstack/synscript](https://github.com/pAIrprogio/synscript) package. It is not recommended to install both packages at the same time. @@ -15,20 +15,21 @@ Sometimes you need to add functionality to existing objects without modifying th import { enhance } from "@synstack/enhance"; // Create an extension with new methods -const stringExtensions = { - reverse: () => this.split("").reverse().join(""), - capitalize: () => this.charAt(0).toUpperCase() + this.slice(1) +const objExtensions = { + stringify: function() { return JSON.stringify(this) }, + clone: function() { return { ...this } } }; -// Enhance a string with new methods -const enhanced = enhance("string", "hello world", stringExtensions); +// Enhance an object with new methods +const obj = { name: "example", value: 42 }; +const enhanced = enhance("object", obj, objExtensions); // Use the enhanced object -console.log(enhanced.capitalize()); // "Hello world" -console.log(enhanced.reverse()); // "dlrow olleh" +console.log(enhanced.stringify()); // '{"name":"example","value":42}' +console.log(enhanced.clone()); // { name: "example", value: 42 } // Access the original object -console.log(enhanced.$()); // "hello world" +console.log(enhanced.$()); // { name: "example", value: 42 } ``` ## Installation @@ -58,17 +59,18 @@ The `enhance()` function combines an object with extension methods: import { enhance } from "@synstack/enhance"; // Define extension methods -const mathExtensions = { - square: function() { return this * this }, - double: function() { return this * 2 } +const loggerExtensions = { + log: function() { console.log(JSON.stringify(this)) }, + getTimestamp: function() { return { ...this, timestamp: Date.now() } } }; -// Enhance a number -const num = enhance("math", 5, mathExtensions); +// Enhance an object +const data = { id: 1, message: "Hello" }; +const enhanced = enhance("logger", data, loggerExtensions); -console.log(num.square()); // 25 -console.log(num.double()); // 10 -console.log(num.$()); // 5 +// Use enhanced methods +enhanced.log(); // Logs: {"id":1,"message":"Hello"} +const timestamped = enhanced.getTimestamp(); // { id: 1, message: "Hello", timestamp: 1234567890 } ``` #### enhanceFactory() @@ -79,17 +81,17 @@ Create reusable enhancers with `enhanceFactory()`: import { enhanceFactory } from "@synstack/enhance"; // Create a reusable enhancer -const enhanceWithMath = enhanceFactory("math", { - square: function() { return this * this }, - double: function() { return this * 2 } +const withLogging = enhanceFactory("logger", { + log: function() { console.log(JSON.stringify(this)) }, + getTimestamp: function() { return { ...this, timestamp: Date.now() } } }); -// Enhance multiple numbers -const num1 = enhanceWithMath(5); -const num2 = enhanceWithMath(10); +// Enhance multiple objects +const obj1 = withLogging({ id: 1, name: "First" }); +const obj2 = withLogging({ id: 2, name: "Second" }); -console.log(num1.square()); // 25 -console.log(num2.double()); // 20 +obj1.log(); // Logs: {"id":1,"name":"First"} +obj2.log(); // Logs: {"id":2,"name":"Second"} ``` ## API Reference diff --git a/packages/enhance/package.json b/packages/enhance/package.json index 136b042..69bb92d 100644 --- a/packages/enhance/package.json +++ b/packages/enhance/package.json @@ -5,14 +5,15 @@ "access": "public" }, "version": "1.1.2", - "description": "Safely enhance a JS object with additional properties while maintaining type safety", + "description": "Type-safe object enhancement with proxy-based method extension - for objects only, not primitives", "keywords": [ "proxy", - "object", - "enhance", - "extend", + "object-enhancement", "type-safe", - "typescript" + "method-extension", + "object-only", + "typescript", + "javascript-proxy" ], "author": { "name": "pAIrprog", diff --git a/packages/fs-cache/src/fs-cache.lib.ts b/packages/fs-cache/src/fs-cache.lib.ts index ad9bbd1..e67a7a9 100644 --- a/packages/fs-cache/src/fs-cache.lib.ts +++ b/packages/fs-cache/src/fs-cache.lib.ts @@ -42,6 +42,7 @@ export class FsCache { } as TConfig & { signatureFn: FsCache.SignatureFn }; } + // eslint-disable-next-line @typescript-eslint/require-await private async serializeInput(...args: TFnArgs) { if (this._config.signatureFn) return this._config.signatureFn(...args); return args; @@ -69,6 +70,7 @@ export class FsCache { } private static keyToRelativePath(key: FsCache.Key, args: any[]) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument return `./${key.map((k) => (k instanceof Function ? k(...args) : k)).join("/")}.json`; } diff --git a/packages/llm/README.md b/packages/llm/README.md index c0d2820..caf8e4a 100644 --- a/packages/llm/README.md +++ b/packages/llm/README.md @@ -1,29 +1,42 @@ # @synstack/llm -> Type-safe LLM message handling and content management +> Immutable & chainable LLM tools with type-safe message handling -This package provides a strongly-typed API for working with LLM messages, including support for text, images, tool calls, and tool responses. +This package provides a strongly-typed, immutable API for interacting with Large Language Models, featuring chainable message building, type-safe tool handling, and flexible completion configuration. > [!WARNING] > This package is included in the [@synstack/synscript](https://github.com/pAIrprogio/synscript) package. It is not recommended to install both packages at the same time. -## What is it for? +## Documentation -Working with LLM messages should be type-safe and intuitive. This package provides a structured way to create and handle different types of message content: +Detailed documentation is available in separate files: +- [Tool Calls](docs/tool-calls.md) - Type-safe tool definitions and execution +- [Message Templating](docs/message-templating.md) - Immutable message building +- [Completions](docs/completion.md) - Completion configuration and execution +- [Runners](docs/runners.md) - Model-specific completion runners -```typescript -import { userMsg, assistantMsg, TextContent, ToolCallContent } from "@synstack/llm"; +## What is it for? -// Create strongly-typed user messages with text and images -const message = userMsg`Here's my question: ${TextContent.from("How does this work?")}`; +Working with Large Language Models should be type-safe and predictable. This package turns complex LLM interactions into chainable, immutable operations: -// Handle tool calls and responses in assistant messages -const toolCall = ToolCallContent.from({ - toolCallId: "123", - toolName: "calculator", - toolArgs: { x: 1, y: 2 } -}); -const response = assistantMsg`Let me calculate that for you ${toolCall}`; +```typescript +import { CompletionBuilder, MessageTemplate } from "@synstack/llm"; + +// Create an immutable completion chain +const baseCompletion = CompletionBuilder.new + .model("gpt-4") + .temperature(0.7) + .system("You are a helpful assistant"); + +// Each operation returns a new instance +const userCompletion = baseCompletion + .user("What is TypeScript?"); + +// Messages are also immutable and chainable +const template = MessageTemplate.new + .system("You are a helpful assistant") + .user("Hello!") + .assistant("How can I help?"); ``` ## Installation @@ -41,9 +54,9 @@ pnpm add @synstack/llm ## Features -### Message Building +### Immutable Message Building -Create type-safe messages using template literals: +Build messages with a chainable, immutable API: ```typescript import { userMsg, assistantMsg } from "@synstack/llm"; @@ -55,49 +68,58 @@ const userMessage = userMsg`Here's an image: ${imageContent}`; const assistantMessage = assistantMsg`Let me help you with that ${toolCall}`; ``` -### Content Types +### Type-Safe Content Types Work with different types of message content: ```typescript -import { TextContent, ImageContent, ToolCallContent, ToolResponseContent } from "@synstack/llm"; +import { TextContent, ImageContent, ToolCallContent } from "@synstack/llm"; -// Text content +// Each content type has its own factory const text = TextContent.from("Hello, world!"); - -// Image content (base64) const image = ImageContent.from({ type: "base64", data: "...", mimeType: "image/png" }); -// Tool calls +// Tool calls are strongly typed const toolCall = ToolCallContent.from({ toolCallId: "123", toolName: "calculator", toolArgs: { x: 1, y: 2 } }); +``` -// Tool responses -const toolResponse = ToolResponseContent.from({ - toolCallId: "123", - toolOutput: { result: 3 } -}); +### Completion Configuration + +Configure completions with immutable builders: + +```typescript +import { CompletionBuilder } from "@synstack/llm"; + +const base = CompletionBuilder.new + .model("gpt-4") + .temperature(0.7); + +// Create variations without modifying the base +const withTools = base.tools([calculator, translator]); +const withSystem = base.system("You are an expert"); ``` -### Message Content Arrays +### Provider-Agnostic Runners -Handle collections of message contents: +Support for multiple LLM providers with a unified interface: ```typescript -import { MessageContents } from "@synstack/llm"; +import { AnthropicRunner, OpenAIRunner } from "@synstack/llm"; -// Create enhanced array of contents -const contents = MessageContents([textContent, toolCallContent]); +// Use any supported provider +const anthropic = new AnthropicRunner({ apiKey: "key" }); +const openai = new OpenAIRunner({ apiKey: "key" }); -// Filter tool calls -const toolCalls = contents.toolCalls(); +// Same interface for all runners +const response = await runner.complete(completion); ``` ## API Reference @@ -141,12 +163,12 @@ Enhanced array type with additional methods: ## TypeScript Support -This package is written in TypeScript and provides comprehensive type definitions: - -- Generic type parameters for message content -- Type-safe message builders using template literals +This package is written in TypeScript and provides: +- Full type inference for messages and tools +- Immutable builder patterns +- Type-safe parameter validation +- Provider-specific type definitions - Strongly typed content classes -- Type inference for tool calls and responses ## License diff --git a/packages/llm/docs/completion.md b/packages/llm/docs/completion.md new file mode 100644 index 0000000..4e102fa --- /dev/null +++ b/packages/llm/docs/completion.md @@ -0,0 +1,130 @@ +# Completions in @synstack/llm + +> Type-safe completion configuration with immutable builders + +This document describes how to configure and execute completions with Large Language Models using type-safe, immutable builders. + +## What are Completions? + +Completions are the core interaction method with Large Language Models. The completion builder provides a type-safe way to configure these interactions while maintaining immutability. + +## Features + +### Immutable Configuration + +Configure completions using an immutable builder pattern: + +```typescript +import { CompletionBuilder } from "@synstack/llm"; + +const completion = CompletionBuilder.new + .temperature(0.7) + .maxTokens(1000) + .system("You are a helpful assistant") + .user("Tell me about TypeScript"); +``` + +### Type-Safe Parameters + +All completion parameters are type-checked: + +```typescript +const completion = CompletionBuilder.new + .temperature(0.7) // Must be between 0 and 1 + .maxTokens(1000) // Must be a positive integer + .topP(0.9) // Must be between 0 and 1 + .topK(40) // Must be a positive integer + .stopSequences(["###"]); // Must be an array of strings +``` + +### Tool Configuration + +Configure tool usage with type safety: + +```typescript +// Single tool mode +const singleTool = completion.tool(calculator); + +// Multi-tool mode +const multiTool = completion.tools([calculator, translator], true); +``` + +## API Reference + +### CompletionBuilder Class + +```typescript +class CompletionBuilder> { + // Core configuration + temperature(value: number): CompletionBuilder; + maxTokens(value: number): CompletionBuilder; + system(content: string): CompletionBuilder; + + // Advanced parameters + topK(value: number): CompletionBuilder; + topP(value: number): CompletionBuilder; + stopSequences(sequences: string[]): CompletionBuilder; + + // Tool configuration + tool(tool: Tool): CompletionBuilder; + tools(tools: Tool[], requireToolUse?: boolean): CompletionBuilder; + clearTools(): CompletionBuilder; + + // Message handling + messages(messages: Message[]): CompletionBuilder; + addMessages(messages: Message[]): CompletionBuilder; + addAssistantMessage(message: AssistantMessage): CompletionBuilder; + + // Execution + run(runner: CompletionRunner): Promise; +} +``` + +### Completion Type + +```typescript +type Completion = { + temperature: number; + maxTokens: number; + messages: Array; + system?: string; + topK?: number; + topP?: number; + stopSequences?: string[]; + toolsConfig?: ToolConfig; + usage?: Usage; + stopReason?: StopReason; +}; +``` + +## Implementation Details + +### Immutability +- Each method returns new instance +- Uses merge pattern internally +- Original completion unchanged +- Thread-safe operations + +### Type Safety +- Generic type parameters +- Parameter validation +- Tool configuration types +- Message content validation + +### Response Handling +- Usage tracking +- Stop reason tracking +- Tool response handling +- Message accumulation + +## TypeScript Support + +The completion system provides: +- Type-safe parameter configuration +- Immutable operations +- Full IntelliSense support +- Generic type inference + +## License + +Apache-2.0 - see LICENSE file for details. diff --git a/packages/llm/docs/message-templating.md b/packages/llm/docs/message-templating.md new file mode 100644 index 0000000..791de47 --- /dev/null +++ b/packages/llm/docs/message-templating.md @@ -0,0 +1,118 @@ +# Message Templating in @synstack/llm + +> Type-safe message building with role-specific content types + +This document describes how to build messages for Large Language Model interactions using type-safe template literals and role-specific content types. + +## What is Message Templating? + +Message templating provides a type-safe way to construct messages for LLM interactions using template literals. The system enforces role-specific content types and provides proper type inference. + +## Features + +### User Messages + +Create user messages with support for text, images, and tool responses: + +```typescript +import { userMsg } from "@synstack/llm"; + +// Text-only message +const textMsg = userMsg`Hello, how can you help me?`; + +// Message with image +const imageMsg = userMsg`Here's an image: ${base64Image}`; + +// Message with tool response +const toolMsg = userMsg`Previous calculation: ${toolResponse}`; +``` + +### Assistant Messages + +Create assistant messages with support for text and tool calls: + +```typescript +import { assistantMsg } from "@synstack/llm"; + +// Text-only message +const textMsg = assistantMsg`I can help you with various tasks.`; + +// Message with tool call +const toolMsg = assistantMsg`Let me calculate that: ${toolCall}`; +``` + +### Content Type Safety + +Role-specific content types are enforced: + +```typescript +// User messages can contain: +type UserContent = + | TextContent // Plain text + | ImageContent // Base64 images + | ToolResponseContent; // Tool responses + +// Assistant messages can contain: +type AssistantContent = + | TextContent // Plain text + | ToolCallContent; // Tool calls +``` + +## API Reference + +### Message Template Functions + +```typescript +// User message template +function userMsg>( + template: TemplateStringsArray, + ...values: T +): Llm.User.Message; + +// Assistant message template +function assistantMsg>( + template: TemplateStringsArray, + ...values: T +): Llm.Assistant.Message; +``` + +### Message Types + +```typescript +type Message = { + role: "user" | "assistant"; + content: Array; +}; + +type MessageContent = + | TextContent + | ImageContent + | ToolCallContent + | ToolResponseContent; +``` + +## Implementation Details + +### Content Type Restrictions +- User messages: text, images, tool responses +- Assistant messages: text, tool calls +- No mixing of incompatible types +- Type checking at compile time + +### Template Processing +- Uses @synstack/text for parsing +- Pipe-based transformations +- Type-safe interpolation +- Role-specific validation + +## TypeScript Support + +The message templating system provides: +- Type inference for template values +- Role-specific content validation +- Compile-time type checking +- Full IntelliSense support + +## License + +Apache-2.0 - see LICENSE file for details. diff --git a/packages/llm/docs/runners.md b/packages/llm/docs/runners.md new file mode 100644 index 0000000..74a4391 --- /dev/null +++ b/packages/llm/docs/runners.md @@ -0,0 +1,117 @@ +# LLM Runners in @synstack/llm + +> Provider-specific completion runners for different LLM providers + +This document describes how to use different LLM runners to execute completions with various model providers. + +## What are Runners? + +Runners are responsible for executing completions with specific LLM providers. Each runner is optimized for its respective provider's API and requirements, with built-in support for retries, caching, and provider-specific features. + +## Features + +### Anthropic Runner + +```typescript +import { AnthropicRunner } from "@synstack/llm"; + +// Create a new runner instance +const runner = AnthropicRunner.new + .client(anthropicClient) + .model(AnthropicRunner.MODELS.CLAUDE_3_MEDIUM) + .retries(3) + .retryBackoff(1000) + .continues(2) + .cache(cacheImplementation); + +// Available models +const models = { + CLAUDE_3_SMALL: "claude-3-haiku-20240307", + CLAUDE_3_MEDIUM: "claude-3-sonnet-20240229", + CLAUDE_3_LARGE: "claude-3-opus-20240229", + CLAUDE_3_5_MEDIUM: "claude-3-5-sonnet-latest", +}; +``` + +### OpenAI Runner + +```typescript +import { OpenAIUtils } from "@synstack/llm"; + +// Available models +const models = { + GPT_3_5_TURBO: "gpt-3.5-turbo", + GPT_4: "gpt-4", + GPT_4_TURBO: "gpt-4-turbo", + GPT_4O: "gpt-4o", + GPT_4O_MINI: "gpt-4o-mini", +}; +``` + +### Runner Configuration + +#### Anthropic-specific options: +- `retries`: Number of retry attempts for failed requests +- `retryBackoff`: Delay in milliseconds between retries +- `continues`: Number of continuation attempts for max_tokens responses +- `cache`: Optional cache implementation for responses + +## API Reference + +### CompletionRunner Interface + +```typescript +interface CompletionRunner { + chatCompletion( + completion: CompletionBuilder>, + ): Promise<{ + message: Llm.Assistant.Message; + usage: Llm.Completion.Usage; + stopReason: Llm.Completion.StopReason; + }>; + + runChatCompletion( + completion: CompletionBuilder>, + ): Promise>>; +} +``` + +### Response Types + +```typescript +type Response = { + message: Llm.Assistant.Message; + usage: { + inputTokens: number; + outputTokens: number; + }; + stopReason: "end" | "max_tokens" | "tool_call" | "stop_sequence"; +}; +``` + +## Provider-Specific Features + +### Anthropic +- Automatic retry mechanism with configurable backoff +- Continuation support for max_tokens responses +- Response caching capability +- Tool calls using Anthropic's tool_use format +- Stop sequence handling with suffix support + +### OpenAI +- Function call format for tools +- System message handling as separate messages +- Required tool usage configuration +- Image URL support in messages + +## TypeScript Support + +The runner system provides: +- Type-safe configuration options +- Provider-specific type definitions +- Full IntelliSense support +- Strongly typed responses + +## License + +Apache-2.0 - see LICENSE file for details. diff --git a/packages/llm/docs/tool-calls.md b/packages/llm/docs/tool-calls.md new file mode 100644 index 0000000..be8dbd3 --- /dev/null +++ b/packages/llm/docs/tool-calls.md @@ -0,0 +1,147 @@ +# Tool Calls in @synstack/llm + +> Type-safe tool definitions and execution for LLM interactions + +This document describes how to define and use tools with Large Language Models in a type-safe manner. + +## What are Tool Calls? + +Tool calls allow Large Language Models to interact with external functions in a structured way. The @synstack/llm package provides a type-safe way to define tools and handle their execution, with support for both single-tool and multi-tool configurations. + +## Features + +### Type-Safe Tool Definitions + +Define tools with complete type safety using Zod schemas: + +```typescript +import { z } from "zod"; +import { type Tool } from "@synstack/llm"; + +const calculator: Tool = { + name: "calculator", + schema: z.object({ + operation: z.enum(["add", "subtract", "multiply", "divide"]), + a: z.number(), + b: z.number() + }) +}; +``` + +### Tool Configuration + +Configure tool usage in completions: + +```typescript +import { CompletionBuilder } from "@synstack/llm"; + +// Single tool mode - LLM will always use this tool +const singleTool = CompletionBuilder.new + .tool(calculator); + +// Multi-tool mode - LLM can choose which tool to use +const multiTool = CompletionBuilder.new + .tools([calculator, translator], true); // true = require tool use +``` + +### Provider-Specific Implementation + +Tools are mapped to provider-specific formats: + +```typescript +// Anthropic format +{ + type: "tool_use", + id: "tool-123", + name: "calculator", + input: { operation: "add", a: 1, b: 2 } +} + +// OpenAI format +{ + type: "function", + function: { + name: "calculator", + arguments: { operation: "add", a: 1, b: 2 } + } +} +``` + +## API Reference + +### Tool Type + +```typescript +type Tool< + TName extends string = string, + TSchema extends ZodSchema = AnyZodObject, +> = { + name: TName; + schema: TSchema; +}; +``` + +### Tool Configuration Types + +```typescript +type ToolConfig = Single | Multi; + +type Single = { + type: "single"; + tool: TTool; +}; + +type Multi< + TTools extends Array = Array, + TRequire extends boolean = boolean, +> = { + type: "multi"; + tools: TTools; + requireToolUse: TRequire; +}; +``` + +### Tool Call Content + +```typescript +type ToolCall = { + type: "tool_call"; + toolCallId: string; + toolName: TTool["name"]; + toolArgs: z.output; +}; +``` + +### Tool Response Content + +```typescript +type ToolResponse = { + type: "tool_response"; + toolCallId: string; + toolOutput: any; +}; +``` + +## Provider Differences + +### Anthropic +- Uses `tool_use` format +- Tool calls include unique IDs +- Arguments passed directly in `input` field + +### OpenAI +- Uses function call format +- Arguments wrapped in `function.arguments` +- Supports required tool usage flag + +## TypeScript Support + +The tool system provides: +- Full type inference for tool parameters +- Schema validation using Zod +- Provider-specific type mappings +- Strongly typed tool responses + +## License + +Apache-2.0 - see LICENSE file for details. diff --git a/packages/llm/package.json b/packages/llm/package.json index e75124f..c5008f4 100644 --- a/packages/llm/package.json +++ b/packages/llm/package.json @@ -5,12 +5,13 @@ "access": "public" }, "version": "1.4.4", - "description": "Type-safe LLM message handling with support for text, images, tool calls, and responses", + "description": "Immutable & chainable LLM tools with type-safe message handling and support for text, images, tool calls, and responses", "keywords": [ "llm", "prompt", "ai", "immutable", + "chainable", "anthropic", "openai", "type-safe", From e587993c60b3309a3be33ad54ae66974ab6f8fa2 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:53:23 +0000 Subject: [PATCH 11/13] docs(llm): remove incorrect API documentation Co-Authored-By: yleflour@pairprog.io --- packages/llm/docs/completion.md | 130 --------------------- packages/llm/docs/message-templating.md | 118 ------------------- packages/llm/docs/runners.md | 117 ------------------- packages/llm/docs/tool-calls.md | 147 ------------------------ 4 files changed, 512 deletions(-) delete mode 100644 packages/llm/docs/completion.md delete mode 100644 packages/llm/docs/message-templating.md delete mode 100644 packages/llm/docs/runners.md delete mode 100644 packages/llm/docs/tool-calls.md diff --git a/packages/llm/docs/completion.md b/packages/llm/docs/completion.md deleted file mode 100644 index 4e102fa..0000000 --- a/packages/llm/docs/completion.md +++ /dev/null @@ -1,130 +0,0 @@ -# Completions in @synstack/llm - -> Type-safe completion configuration with immutable builders - -This document describes how to configure and execute completions with Large Language Models using type-safe, immutable builders. - -## What are Completions? - -Completions are the core interaction method with Large Language Models. The completion builder provides a type-safe way to configure these interactions while maintaining immutability. - -## Features - -### Immutable Configuration - -Configure completions using an immutable builder pattern: - -```typescript -import { CompletionBuilder } from "@synstack/llm"; - -const completion = CompletionBuilder.new - .temperature(0.7) - .maxTokens(1000) - .system("You are a helpful assistant") - .user("Tell me about TypeScript"); -``` - -### Type-Safe Parameters - -All completion parameters are type-checked: - -```typescript -const completion = CompletionBuilder.new - .temperature(0.7) // Must be between 0 and 1 - .maxTokens(1000) // Must be a positive integer - .topP(0.9) // Must be between 0 and 1 - .topK(40) // Must be a positive integer - .stopSequences(["###"]); // Must be an array of strings -``` - -### Tool Configuration - -Configure tool usage with type safety: - -```typescript -// Single tool mode -const singleTool = completion.tool(calculator); - -// Multi-tool mode -const multiTool = completion.tools([calculator, translator], true); -``` - -## API Reference - -### CompletionBuilder Class - -```typescript -class CompletionBuilder> { - // Core configuration - temperature(value: number): CompletionBuilder; - maxTokens(value: number): CompletionBuilder; - system(content: string): CompletionBuilder; - - // Advanced parameters - topK(value: number): CompletionBuilder; - topP(value: number): CompletionBuilder; - stopSequences(sequences: string[]): CompletionBuilder; - - // Tool configuration - tool(tool: Tool): CompletionBuilder; - tools(tools: Tool[], requireToolUse?: boolean): CompletionBuilder; - clearTools(): CompletionBuilder; - - // Message handling - messages(messages: Message[]): CompletionBuilder; - addMessages(messages: Message[]): CompletionBuilder; - addAssistantMessage(message: AssistantMessage): CompletionBuilder; - - // Execution - run(runner: CompletionRunner): Promise; -} -``` - -### Completion Type - -```typescript -type Completion = { - temperature: number; - maxTokens: number; - messages: Array; - system?: string; - topK?: number; - topP?: number; - stopSequences?: string[]; - toolsConfig?: ToolConfig; - usage?: Usage; - stopReason?: StopReason; -}; -``` - -## Implementation Details - -### Immutability -- Each method returns new instance -- Uses merge pattern internally -- Original completion unchanged -- Thread-safe operations - -### Type Safety -- Generic type parameters -- Parameter validation -- Tool configuration types -- Message content validation - -### Response Handling -- Usage tracking -- Stop reason tracking -- Tool response handling -- Message accumulation - -## TypeScript Support - -The completion system provides: -- Type-safe parameter configuration -- Immutable operations -- Full IntelliSense support -- Generic type inference - -## License - -Apache-2.0 - see LICENSE file for details. diff --git a/packages/llm/docs/message-templating.md b/packages/llm/docs/message-templating.md deleted file mode 100644 index 791de47..0000000 --- a/packages/llm/docs/message-templating.md +++ /dev/null @@ -1,118 +0,0 @@ -# Message Templating in @synstack/llm - -> Type-safe message building with role-specific content types - -This document describes how to build messages for Large Language Model interactions using type-safe template literals and role-specific content types. - -## What is Message Templating? - -Message templating provides a type-safe way to construct messages for LLM interactions using template literals. The system enforces role-specific content types and provides proper type inference. - -## Features - -### User Messages - -Create user messages with support for text, images, and tool responses: - -```typescript -import { userMsg } from "@synstack/llm"; - -// Text-only message -const textMsg = userMsg`Hello, how can you help me?`; - -// Message with image -const imageMsg = userMsg`Here's an image: ${base64Image}`; - -// Message with tool response -const toolMsg = userMsg`Previous calculation: ${toolResponse}`; -``` - -### Assistant Messages - -Create assistant messages with support for text and tool calls: - -```typescript -import { assistantMsg } from "@synstack/llm"; - -// Text-only message -const textMsg = assistantMsg`I can help you with various tasks.`; - -// Message with tool call -const toolMsg = assistantMsg`Let me calculate that: ${toolCall}`; -``` - -### Content Type Safety - -Role-specific content types are enforced: - -```typescript -// User messages can contain: -type UserContent = - | TextContent // Plain text - | ImageContent // Base64 images - | ToolResponseContent; // Tool responses - -// Assistant messages can contain: -type AssistantContent = - | TextContent // Plain text - | ToolCallContent; // Tool calls -``` - -## API Reference - -### Message Template Functions - -```typescript -// User message template -function userMsg>( - template: TemplateStringsArray, - ...values: T -): Llm.User.Message; - -// Assistant message template -function assistantMsg>( - template: TemplateStringsArray, - ...values: T -): Llm.Assistant.Message; -``` - -### Message Types - -```typescript -type Message = { - role: "user" | "assistant"; - content: Array; -}; - -type MessageContent = - | TextContent - | ImageContent - | ToolCallContent - | ToolResponseContent; -``` - -## Implementation Details - -### Content Type Restrictions -- User messages: text, images, tool responses -- Assistant messages: text, tool calls -- No mixing of incompatible types -- Type checking at compile time - -### Template Processing -- Uses @synstack/text for parsing -- Pipe-based transformations -- Type-safe interpolation -- Role-specific validation - -## TypeScript Support - -The message templating system provides: -- Type inference for template values -- Role-specific content validation -- Compile-time type checking -- Full IntelliSense support - -## License - -Apache-2.0 - see LICENSE file for details. diff --git a/packages/llm/docs/runners.md b/packages/llm/docs/runners.md deleted file mode 100644 index 74a4391..0000000 --- a/packages/llm/docs/runners.md +++ /dev/null @@ -1,117 +0,0 @@ -# LLM Runners in @synstack/llm - -> Provider-specific completion runners for different LLM providers - -This document describes how to use different LLM runners to execute completions with various model providers. - -## What are Runners? - -Runners are responsible for executing completions with specific LLM providers. Each runner is optimized for its respective provider's API and requirements, with built-in support for retries, caching, and provider-specific features. - -## Features - -### Anthropic Runner - -```typescript -import { AnthropicRunner } from "@synstack/llm"; - -// Create a new runner instance -const runner = AnthropicRunner.new - .client(anthropicClient) - .model(AnthropicRunner.MODELS.CLAUDE_3_MEDIUM) - .retries(3) - .retryBackoff(1000) - .continues(2) - .cache(cacheImplementation); - -// Available models -const models = { - CLAUDE_3_SMALL: "claude-3-haiku-20240307", - CLAUDE_3_MEDIUM: "claude-3-sonnet-20240229", - CLAUDE_3_LARGE: "claude-3-opus-20240229", - CLAUDE_3_5_MEDIUM: "claude-3-5-sonnet-latest", -}; -``` - -### OpenAI Runner - -```typescript -import { OpenAIUtils } from "@synstack/llm"; - -// Available models -const models = { - GPT_3_5_TURBO: "gpt-3.5-turbo", - GPT_4: "gpt-4", - GPT_4_TURBO: "gpt-4-turbo", - GPT_4O: "gpt-4o", - GPT_4O_MINI: "gpt-4o-mini", -}; -``` - -### Runner Configuration - -#### Anthropic-specific options: -- `retries`: Number of retry attempts for failed requests -- `retryBackoff`: Delay in milliseconds between retries -- `continues`: Number of continuation attempts for max_tokens responses -- `cache`: Optional cache implementation for responses - -## API Reference - -### CompletionRunner Interface - -```typescript -interface CompletionRunner { - chatCompletion( - completion: CompletionBuilder>, - ): Promise<{ - message: Llm.Assistant.Message; - usage: Llm.Completion.Usage; - stopReason: Llm.Completion.StopReason; - }>; - - runChatCompletion( - completion: CompletionBuilder>, - ): Promise>>; -} -``` - -### Response Types - -```typescript -type Response = { - message: Llm.Assistant.Message; - usage: { - inputTokens: number; - outputTokens: number; - }; - stopReason: "end" | "max_tokens" | "tool_call" | "stop_sequence"; -}; -``` - -## Provider-Specific Features - -### Anthropic -- Automatic retry mechanism with configurable backoff -- Continuation support for max_tokens responses -- Response caching capability -- Tool calls using Anthropic's tool_use format -- Stop sequence handling with suffix support - -### OpenAI -- Function call format for tools -- System message handling as separate messages -- Required tool usage configuration -- Image URL support in messages - -## TypeScript Support - -The runner system provides: -- Type-safe configuration options -- Provider-specific type definitions -- Full IntelliSense support -- Strongly typed responses - -## License - -Apache-2.0 - see LICENSE file for details. diff --git a/packages/llm/docs/tool-calls.md b/packages/llm/docs/tool-calls.md deleted file mode 100644 index be8dbd3..0000000 --- a/packages/llm/docs/tool-calls.md +++ /dev/null @@ -1,147 +0,0 @@ -# Tool Calls in @synstack/llm - -> Type-safe tool definitions and execution for LLM interactions - -This document describes how to define and use tools with Large Language Models in a type-safe manner. - -## What are Tool Calls? - -Tool calls allow Large Language Models to interact with external functions in a structured way. The @synstack/llm package provides a type-safe way to define tools and handle their execution, with support for both single-tool and multi-tool configurations. - -## Features - -### Type-Safe Tool Definitions - -Define tools with complete type safety using Zod schemas: - -```typescript -import { z } from "zod"; -import { type Tool } from "@synstack/llm"; - -const calculator: Tool = { - name: "calculator", - schema: z.object({ - operation: z.enum(["add", "subtract", "multiply", "divide"]), - a: z.number(), - b: z.number() - }) -}; -``` - -### Tool Configuration - -Configure tool usage in completions: - -```typescript -import { CompletionBuilder } from "@synstack/llm"; - -// Single tool mode - LLM will always use this tool -const singleTool = CompletionBuilder.new - .tool(calculator); - -// Multi-tool mode - LLM can choose which tool to use -const multiTool = CompletionBuilder.new - .tools([calculator, translator], true); // true = require tool use -``` - -### Provider-Specific Implementation - -Tools are mapped to provider-specific formats: - -```typescript -// Anthropic format -{ - type: "tool_use", - id: "tool-123", - name: "calculator", - input: { operation: "add", a: 1, b: 2 } -} - -// OpenAI format -{ - type: "function", - function: { - name: "calculator", - arguments: { operation: "add", a: 1, b: 2 } - } -} -``` - -## API Reference - -### Tool Type - -```typescript -type Tool< - TName extends string = string, - TSchema extends ZodSchema = AnyZodObject, -> = { - name: TName; - schema: TSchema; -}; -``` - -### Tool Configuration Types - -```typescript -type ToolConfig = Single | Multi; - -type Single = { - type: "single"; - tool: TTool; -}; - -type Multi< - TTools extends Array = Array, - TRequire extends boolean = boolean, -> = { - type: "multi"; - tools: TTools; - requireToolUse: TRequire; -}; -``` - -### Tool Call Content - -```typescript -type ToolCall = { - type: "tool_call"; - toolCallId: string; - toolName: TTool["name"]; - toolArgs: z.output; -}; -``` - -### Tool Response Content - -```typescript -type ToolResponse = { - type: "tool_response"; - toolCallId: string; - toolOutput: any; -}; -``` - -## Provider Differences - -### Anthropic -- Uses `tool_use` format -- Tool calls include unique IDs -- Arguments passed directly in `input` field - -### OpenAI -- Uses function call format -- Arguments wrapped in `function.arguments` -- Supports required tool usage flag - -## TypeScript Support - -The tool system provides: -- Full type inference for tool parameters -- Schema validation using Zod -- Provider-specific type mappings -- Strongly typed tool responses - -## License - -Apache-2.0 - see LICENSE file for details. From a15cc5aec07e18c93ccc5c8ff49501d70a0f6b56 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:56:49 +0000 Subject: [PATCH 12/13] docs(pipe,llm): demonstrate immutability in examples and fix API docs Co-Authored-By: yleflour@pairprog.io --- packages/llm/README.md | 197 +++++++++++++++++++++------------------- packages/pipe/README.md | 124 ++++++++++++++++++++++++- 2 files changed, 226 insertions(+), 95 deletions(-) diff --git a/packages/llm/README.md b/packages/llm/README.md index caf8e4a..66718d1 100644 --- a/packages/llm/README.md +++ b/packages/llm/README.md @@ -1,42 +1,29 @@ # @synstack/llm -> Immutable & chainable LLM tools with type-safe message handling +> Type-safe LLM message handling and content management -This package provides a strongly-typed, immutable API for interacting with Large Language Models, featuring chainable message building, type-safe tool handling, and flexible completion configuration. +This package provides a strongly-typed API for working with LLM messages, including support for text, images, tool calls, and tool responses. > [!WARNING] > This package is included in the [@synstack/synscript](https://github.com/pAIrprogio/synscript) package. It is not recommended to install both packages at the same time. -## Documentation - -Detailed documentation is available in separate files: -- [Tool Calls](docs/tool-calls.md) - Type-safe tool definitions and execution -- [Message Templating](docs/message-templating.md) - Immutable message building -- [Completions](docs/completion.md) - Completion configuration and execution -- [Runners](docs/runners.md) - Model-specific completion runners - ## What is it for? -Working with Large Language Models should be type-safe and predictable. This package turns complex LLM interactions into chainable, immutable operations: +Working with LLM messages should be type-safe and intuitive. This package provides a structured way to create and handle different types of message content: ```typescript -import { CompletionBuilder, MessageTemplate } from "@synstack/llm"; - -// Create an immutable completion chain -const baseCompletion = CompletionBuilder.new - .model("gpt-4") - .temperature(0.7) - .system("You are a helpful assistant"); - -// Each operation returns a new instance -const userCompletion = baseCompletion - .user("What is TypeScript?"); - -// Messages are also immutable and chainable -const template = MessageTemplate.new - .system("You are a helpful assistant") - .user("Hello!") - .assistant("How can I help?"); +import { userMsg, assistantMsg } from "@synstack/llm"; + +// Create strongly-typed user messages with text and images +const message = userMsg`Here's my question: ${TextContent.from("How does this work?")}`; + +// Handle tool calls and responses in assistant messages +const toolCall = ToolCallContent.from({ + toolCallId: "123", + toolName: "calculator", + toolArgs: { x: 1, y: 2 } +}); +const response = assistantMsg`Let me calculate that for you ${toolCall}`; ``` ## Installation @@ -54,9 +41,9 @@ pnpm add @synstack/llm ## Features -### Immutable Message Building +### Message Building -Build messages with a chainable, immutable API: +Create type-safe messages using template literals: ```typescript import { userMsg, assistantMsg } from "@synstack/llm"; @@ -68,107 +55,129 @@ const userMessage = userMsg`Here's an image: ${imageContent}`; const assistantMessage = assistantMsg`Let me help you with that ${toolCall}`; ``` -### Type-Safe Content Types +### Content Types Work with different types of message content: ```typescript -import { TextContent, ImageContent, ToolCallContent } from "@synstack/llm"; - -// Each content type has its own factory -const text = TextContent.from("Hello, world!"); -const image = ImageContent.from({ - type: "base64", - data: "...", - mimeType: "image/png" -}); +import { type Llm } from "@synstack/llm"; + +// Text content +const text: Llm.Message.Content.Text = { + type: "text", + text: "Hello, world!" +}; + +// Image content +const image: Llm.Message.Content.Image = { + type: "image", + image: { + type: "base64", + data: "...", + mimeType: "image/png" + } +}; // Tool calls are strongly typed -const toolCall = ToolCallContent.from({ +const toolCall: Llm.Message.Content.ToolCall = { + type: "tool_call", toolCallId: "123", toolName: "calculator", toolArgs: { x: 1, y: 2 } -}); -``` - -### Completion Configuration - -Configure completions with immutable builders: - -```typescript -import { CompletionBuilder } from "@synstack/llm"; - -const base = CompletionBuilder.new - .model("gpt-4") - .temperature(0.7); - -// Create variations without modifying the base -const withTools = base.tools([calculator, translator]); -const withSystem = base.system("You are an expert"); +}; ``` -### Provider-Agnostic Runners +### Tool Configuration -Support for multiple LLM providers with a unified interface: +Configure tool usage with type-safe definitions: ```typescript -import { AnthropicRunner, OpenAIRunner } from "@synstack/llm"; - -// Use any supported provider -const anthropic = new AnthropicRunner({ apiKey: "key" }); -const openai = new OpenAIRunner({ apiKey: "key" }); - -// Same interface for all runners -const response = await runner.complete(completion); +import { type Llm } from "@synstack/llm"; +import { z } from "zod"; + +// Define a tool with Zod schema +const calculator: Llm.Tool = { + name: "calculator", + schema: z.object({ + x: z.number(), + y: z.number() + }) +}; + +// Use in completion configuration +const config: Llm.Completion = { + temperature: 0.7, + maxTokens: 1000, + messages: [], + toolsConfig: { + type: "single", + tool: calculator + } +}; ``` ## API Reference -### Message Builders +### Message Functions #### userMsg - Template literal function for creating user messages - Supports text, images, and tool responses +- Returns `Llm.User.Message` #### assistantMsg - Template literal function for creating assistant messages - Supports text and tool calls +- Returns `Llm.Assistant.Message` -### Content Classes - -#### TextContent -- `from(text: string)` - Create text content -- `valueOf()` - Convert to message content format -- `toString()` - Get raw text content - -#### ImageContent -- `from(image: Base64Image)` - Create image content -- `valueOf()` - Convert to message content format -- `image` - Access raw image data +### Message Types -#### ToolCallContent -- `from(toolCall: ToolCall)` - Create tool call content -- `valueOf()` - Convert to message content format -- `toolCall` - Access tool call details +#### Llm.Message.Content.Text +```typescript +{ + type: "text"; + text: string; +} +``` -#### ToolResponseContent -- `from(toolResponse: ToolResponse)` - Create tool response content -- `valueOf()` - Convert to message content format -- `toolResponse` - Access tool response details +#### Llm.Message.Content.Image +```typescript +{ + type: "image"; + image: { + type: "base64"; + data: string; + mimeType: string; + }; +} +``` -### MessageContents +#### Llm.Message.Content.ToolCall +```typescript +{ + type: "tool_call"; + toolCallId: string; + toolName: string; + toolArgs: Record; +} +``` -Enhanced array type with additional methods: -- `toolCalls()` - Filter and return tool call contents +#### Llm.Message.Content.ToolResponse +```typescript +{ + type: "tool_response"; + toolCallId: string; + toolOutput: unknown; +} +``` ## TypeScript Support This package is written in TypeScript and provides: - Full type inference for messages and tools -- Immutable builder patterns - Type-safe parameter validation -- Provider-specific type definitions -- Strongly typed content classes +- Strongly typed content interfaces +- Zod schema validation for tool arguments ## License diff --git a/packages/pipe/README.md b/packages/pipe/README.md index a194d54..c2f6b15 100644 --- a/packages/pipe/README.md +++ b/packages/pipe/README.md @@ -1 +1,123 @@ -# @synstack/pipe +# @synstack/pipe + +> Type-safe chainable operations with immutable transformations + +This package provides the foundation for creating chainable, immutable operations in TypeScript. It's used by other @synstack packages to implement type-safe method chaining. + +> [!WARNING] +> This package is included in the [@synstack/synscript](https://github.com/pAIrprogio/synscript) package. It is not recommended to install both packages at the same time. + +## What is it for? + +Create type-safe chainable operations that maintain immutability. Each operation returns a new instance, allowing for safe method chaining: + +```typescript +import { pipe } from '@synstack/pipe' + +// Each operation returns a new instance +const result = pipe('hello') + ._((str) => str.toUpperCase()) // Returns new Pipeable + ._((str) => str.split('')) // Returns new Pipeable + ._((arr) => arr.reverse()) // Returns new Pipeable + .$ // Get final value + +console.log(result) // ['O','L','L','E','H'] + +// Original value remains unchanged +const original = 'hello' +const modified = pipe(original) + ._((str) => str.toUpperCase()) + .$ + +console.log(original) // 'hello' +console.log(modified) // 'HELLO' +``` + +## Installation + +```bash +# Using npm +npm install @synstack/pipe + +# Using yarn +yarn add @synstack/pipe + +# Using pnpm +pnpm add @synstack/pipe +``` + +## Features + +### Immutable Chaining + +The `pipe` function creates a `Pipeable` instance that wraps a value and provides chainable methods: + +```typescript +import { pipe } from '@synstack/pipe' + +// Chain multiple transformations +const result = pipe({ count: 1 }) + ._((obj) => ({ ...obj, doubled: obj.count * 2 })) + ._((obj) => ({ ...obj, squared: obj.doubled ** 2 })) + .$ + +console.log(result) // { count: 1, doubled: 2, squared: 4 } +``` + +### Type-Safe Operations + +TypeScript types are preserved through the chain: + +```typescript +import { pipe } from '@synstack/pipe' + +interface User { + name: string + age: number +} + +const user: User = { name: 'John', age: 30 } + +// Types are inferred correctly through the chain +const result = pipe(user) + ._((u) => ({ ...u, email: 'john@example.com' })) + ._((u) => ({ ...u, isAdult: u.age >= 18 })) + .$ + +// Result type is inferred as: +// { name: string; age: number; email: string; isAdult: boolean } +``` + +## API Reference + +### pipe() + +```typescript +function pipe(value: T): Pipeable +``` + +Creates a new `Pipeable` instance wrapping the provided value. + +### Pipeable Class + +#### Methods + +- `_(fn: (value: T) => U): Pipeable` + - Apply a transformation function + - Returns a new Pipeable instance with the transformed value + +- `$: T` + - Get the current value + - Accessor property that returns the wrapped value + +## TypeScript Support + +This package is written in TypeScript and provides: +- Full type inference through transformation chains +- Type-safe operation composition +- Generic type parameters for maximum flexibility +- IntelliSense support for all operations + +## License + +Apache-2.0 - see LICENSE file for details. From b07af4b9227497615f40572ddddde22ea672597c Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:35:44 +0000 Subject: [PATCH 13/13] docs(fs): demonstrate immutability in examples and API reference Co-Authored-By: yleflour@pairprog.io --- packages/fs/README.md | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/packages/fs/README.md b/packages/fs/README.md index 6efee1e..e69fe95 100644 --- a/packages/fs/README.md +++ b/packages/fs/README.md @@ -9,30 +9,33 @@ This package provides a strongly-typed, chainable API for file system operations ## What is it for? -Working with files and directories should be simple and type-safe. This package turns verbose file operations into chainable, strongly-typed commands: +Working with files and directories should be simple, type-safe, and predictable. This package provides an immutable API where each operation returns a new instance, making it safe to chain operations while preserving original file references: ```typescript import { file, dir } from "@synstack/fs"; -// Read and validate JSON with schema -const configFile = file("./config.json") - .schema(ConfigSchema) - .read.json(); +// Each operation returns a new instance, preserving the original +const configFile = file("./config.json"); +const validatedConfig = configFile.schema(ConfigSchema); // New instance with schema +const configData = await validatedConfig.read.json(); // Read validated JSON -// Write text files with different encodings -const templateFile = file("./template.txt") - .write.text("Hello ${name}!"); +// Chain operations while maintaining immutability +const templateFile = file("./template.txt"); +const updatedTemplate = templateFile + .write.text("Hello ${name}!"); // Returns new instance -// Find and filter files by glob patterns and MIME types -const images = dir("./assets") - .glob("**/*.{png,jpg}") - .filterMimeTypes("image/png", "image/jpeg"); +// Directory operations are also immutable +const rootDir = dir("./assets"); +const imagesDir = rootDir.to("images"); // New instance for subdirectory +const imageFiles = await imagesDir + .glob("**/*.{png,jpg}") // Returns new array + .filterMimeTypes("image/png", "image/jpeg"); // Returns filtered array -// Work with relative paths safely +// Each path operation returns a new instance const sourceFile = file("/path/to/source.ts"); const targetFile = sourceFile - .toFile("../dist/output.js") - .write.text(compiledContent); + .toFile("../dist/output.js"); // New instance with different path +await targetFile.write.text(compiledContent); // Handle JSON with pretty formatting const packageJson = file("package.json") @@ -146,7 +149,8 @@ The `FsFile` class provides methods for working with individual files: #### Creation - `file(...paths)` - Create a new file instance from path segments -- `schema(zodSchema)` - Add schema validation for JSON/YAML operations +- `schema(zodSchema)` - Create new instance with schema validation +- All operations return new instances, preserving original file references #### Read Operations - `read.text()` - Read file as text @@ -190,6 +194,7 @@ The `FsDir` class provides methods for working with directories: - `dir(...paths)` - Create new directory instance - `to(relativePath)` - Create new subdirectory instance - `file(relativePath)` - Create new file instance in directory +- All operations return new instances, preserving original directory references #### Directory Operations - `exists()` - Check if directory exists