@hybrd/utils 1.4.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +149 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,58 +1,178 @@
|
|
|
1
1
|
# @hybrd/utils
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
General-purpose utility functions for the Hybrid AI agent framework. Compatible with Node.js, Cloudflare Workers, and browser environments.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Overview
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A collection of small helpers used across the monorepo. Includes environment detection, storage adapters, logging, date formatting, and cross-platform primitives.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## API Reference
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
### Array
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
```typescript
|
|
14
|
+
import { chunk, uniq, shuffle } from "@hybrd/utils"
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]
|
|
17
|
+
uniq([1, 1, 2, 3, 3]) // [1, 2, 3]
|
|
18
|
+
shuffle([1, 2, 3, 4]) // [3, 1, 4, 2] (random order)
|
|
18
19
|
```
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
### Cloudflare Environment Detection
|
|
22
|
+
|
|
23
|
+
Detects whether the code is running on Cloudflare Pages, Cloudflare Workers, or locally:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import {
|
|
27
|
+
getCloudflareEnvironment,
|
|
28
|
+
getCloudflareStoragePath,
|
|
29
|
+
getCloudflareServiceUrl
|
|
30
|
+
} from "@hybrd/utils"
|
|
21
31
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Visit [OpenRouter](https://openrouter.ai/keys), create an account and generate an API key
|
|
32
|
+
const env = getCloudflareEnvironment()
|
|
33
|
+
// { isCloudflare: boolean, platform: 'pages' | 'workers' | 'local', storagePath: string }
|
|
25
34
|
|
|
26
|
-
|
|
35
|
+
// Returns '/tmp/xmtp' on Cloudflare, '.data/xmtp' locally
|
|
36
|
+
const storagePath = getCloudflareStoragePath("xmtp")
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
|
|
38
|
+
// Returns CF_PAGES_URL, CF_WORKER_URL, or 'http://localhost:3000'
|
|
39
|
+
const serviceUrl = getCloudflareServiceUrl(3000)
|
|
30
40
|
```
|
|
31
41
|
|
|
32
|
-
###
|
|
42
|
+
### Date Formatting
|
|
33
43
|
|
|
34
|
-
```
|
|
35
|
-
|
|
44
|
+
```typescript
|
|
45
|
+
import { formatDate, formatRelativeDate } from "@hybrd/utils"
|
|
46
|
+
|
|
47
|
+
formatDate(new Date()) // "Mar 2, 2026"
|
|
48
|
+
formatDate("2026-01-15") // "Jan 15, 2026"
|
|
49
|
+
|
|
50
|
+
formatRelativeDate(new Date()) // "Today, 3:45 PM"
|
|
51
|
+
formatRelativeDate(yesterday) // "Yesterday, 3:45 PM"
|
|
52
|
+
formatRelativeDate(lastWeek) // "Feb 23, 3:45 PM"
|
|
53
|
+
formatRelativeDate(lastYear) // "Feb 23, 2025"
|
|
36
54
|
```
|
|
37
55
|
|
|
38
|
-
|
|
56
|
+
### Logger
|
|
39
57
|
|
|
40
|
-
|
|
41
|
-
|
|
58
|
+
Environment-aware logger. Debug output is suppressed unless `DEBUG` or `XMTP_DEBUG` is set:
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { logger } from "@hybrd/utils"
|
|
62
|
+
|
|
63
|
+
logger.debug("Verbose detail") // Only logs if DEBUG or XMTP_DEBUG env var is set
|
|
64
|
+
logger.log("General message")
|
|
65
|
+
logger.info("Info message")
|
|
66
|
+
logger.warn("Warning")
|
|
67
|
+
logger.error("Error occurred")
|
|
42
68
|
```
|
|
43
69
|
|
|
44
|
-
###
|
|
70
|
+
### Markdown
|
|
45
71
|
|
|
46
|
-
```
|
|
47
|
-
|
|
72
|
+
```typescript
|
|
73
|
+
import { stripMarkdown } from "@hybrd/utils"
|
|
74
|
+
|
|
75
|
+
const plain = await stripMarkdown("**bold** and _italic_ text")
|
|
76
|
+
// "bold and italic text"
|
|
48
77
|
```
|
|
49
78
|
|
|
50
|
-
|
|
79
|
+
### Object
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { stringifyValues, pruneEmpty } from "@hybrd/utils"
|
|
83
|
+
|
|
84
|
+
stringifyValues({ a: 1, b: { nested: true }, c: null })
|
|
85
|
+
// { a: "1", b: '{"nested":true}', c: "null" }
|
|
86
|
+
|
|
87
|
+
pruneEmpty({ a: 1, b: undefined, c: null, d: "" })
|
|
88
|
+
// { a: 1 }
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Storage
|
|
92
|
+
|
|
93
|
+
Auto-detects the available storage backend and returns an adapter:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import { createStorageAdapter } from "@hybrd/utils"
|
|
97
|
+
|
|
98
|
+
const adapter = createStorageAdapter()
|
|
99
|
+
// Returns R2StorageAdapter if globalThis.XMTP_STORAGE exists (Cloudflare)
|
|
100
|
+
// Returns null if no storage is configured (local dev)
|
|
101
|
+
|
|
102
|
+
if (adapter) {
|
|
103
|
+
await adapter.uploadFile("/local/path/file.db3", "remote/path/file.db3")
|
|
104
|
+
await adapter.downloadFile("remote/path/file.db3", "/local/path/file.db3")
|
|
105
|
+
const exists = await adapter.exists("remote/path/file.db3")
|
|
106
|
+
await adapter.delete("remote/path/file.db3")
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### R2StorageAdapter
|
|
111
|
+
|
|
112
|
+
Used automatically on Cloudflare Workers when `globalThis.XMTP_STORAGE` is bound:
|
|
51
113
|
|
|
52
|
-
|
|
114
|
+
```typescript
|
|
115
|
+
import { R2StorageAdapter } from "@hybrd/utils"
|
|
116
|
+
|
|
117
|
+
const adapter = new R2StorageAdapter(env.XMTP_STORAGE)
|
|
118
|
+
await adapter.uploadFile(localPath, remotePath)
|
|
119
|
+
await adapter.downloadFile(remotePath, localPath)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### String
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import { truncate } from "@hybrd/utils"
|
|
126
|
+
|
|
127
|
+
truncate("A very long string that needs to be shortened", 20)
|
|
128
|
+
// "A very long string t..."
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### URLs
|
|
132
|
+
|
|
133
|
+
Resolves the base URL for the agent service, with priority:
|
|
134
|
+
`AGENT_URL` env → `RAILWAY_PUBLIC_DOMAIN` → `http://localhost:8454`
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { getUrl } from "@hybrd/utils"
|
|
138
|
+
|
|
139
|
+
getUrl() // "http://localhost:8454"
|
|
140
|
+
getUrl("/api/chat") // "http://localhost:8454/api/chat"
|
|
141
|
+
|
|
142
|
+
// With AGENT_URL=https://my-agent.fly.dev
|
|
143
|
+
getUrl("/api/chat") // "https://my-agent.fly.dev/api/chat"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### UUID
|
|
147
|
+
|
|
148
|
+
Cross-platform UUID v4 generation. Works in Node.js, Cloudflare Workers, and browsers:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { randomUUID } from "@hybrd/utils"
|
|
152
|
+
|
|
153
|
+
randomUUID() // "550e8400-e29b-41d4-a716-446655440000"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Uses the `uuid` package rather than `node:crypto.randomUUID` for compatibility with environments where Node.js crypto is not available.
|
|
157
|
+
|
|
158
|
+
## Environment Variables
|
|
159
|
+
|
|
160
|
+
| Variable | Description |
|
|
161
|
+
|----------|-------------|
|
|
162
|
+
| `DEBUG` | Enable debug logging |
|
|
163
|
+
| `XMTP_DEBUG` | Enable debug logging (XMTP-specific alias) |
|
|
164
|
+
| `AGENT_URL` | Override agent service base URL |
|
|
165
|
+
| `RAILWAY_PUBLIC_DOMAIN` | Railway deployment domain (auto-set by Railway) |
|
|
166
|
+
| `CF_PAGES_BRANCH` | Set by Cloudflare Pages (used for environment detection) |
|
|
167
|
+
| `CF_WORKER_NAME` | Set by Cloudflare Workers (used for environment detection) |
|
|
168
|
+
|
|
169
|
+
## Testing
|
|
53
170
|
|
|
54
171
|
```bash
|
|
55
|
-
|
|
172
|
+
cd packages/utils
|
|
173
|
+
pnpm test
|
|
56
174
|
```
|
|
57
175
|
|
|
58
|
-
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
MIT
|