@hybrd/utils 0.7.6 → 1.0.1

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.
Files changed (52) hide show
  1. package/.cache/tsbuildinfo.json +1 -0
  2. package/.turbo/turbo-build.log +5 -0
  3. package/.turbo/turbo-lint$colon$fix.log +6 -0
  4. package/.turbo/turbo-lint.log +6 -0
  5. package/.turbo/turbo-typecheck.log +5 -0
  6. package/dist/index.d.ts +10 -3130
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +9 -2291
  9. package/dist/lib/array.d.ts +25 -0
  10. package/dist/lib/array.d.ts.map +1 -0
  11. package/dist/lib/array.js +32 -0
  12. package/dist/lib/cloudflare.d.ts +22 -0
  13. package/dist/lib/cloudflare.d.ts.map +1 -0
  14. package/dist/lib/cloudflare.js +53 -0
  15. package/dist/lib/date.d.ts +17 -0
  16. package/dist/lib/date.d.ts.map +1 -0
  17. package/dist/lib/date.js +37 -0
  18. package/dist/lib/markdown.d.ts +7 -0
  19. package/dist/lib/markdown.d.ts.map +1 -0
  20. package/dist/lib/markdown.js +11 -0
  21. package/dist/lib/object.d.ts +15 -0
  22. package/dist/lib/object.d.ts.map +1 -0
  23. package/dist/lib/object.js +42 -0
  24. package/dist/lib/storage.d.ts +24 -0
  25. package/dist/lib/storage.d.ts.map +1 -0
  26. package/dist/lib/storage.js +50 -0
  27. package/dist/lib/string.d.ts +11 -0
  28. package/dist/lib/string.d.ts.map +1 -0
  29. package/dist/lib/string.js +12 -0
  30. package/dist/lib/urls.d.ts +28 -0
  31. package/dist/lib/urls.d.ts.map +1 -0
  32. package/dist/lib/urls.js +36 -0
  33. package/dist/lib/uuid.d.ts +2 -0
  34. package/dist/lib/uuid.d.ts.map +1 -0
  35. package/dist/lib/uuid.js +6 -0
  36. package/package.json +20 -51
  37. package/src/index.ts +9 -0
  38. package/src/lib/array.ts +35 -0
  39. package/src/lib/cloudflare.ts +74 -0
  40. package/src/lib/date.ts +40 -0
  41. package/src/lib/markdown.ts +12 -0
  42. package/src/lib/object.ts +54 -0
  43. package/src/lib/storage.ts +64 -0
  44. package/src/lib/string.ts +12 -0
  45. package/src/lib/urls.ts +39 -0
  46. package/src/lib/uuid.ts +7 -0
  47. package/tsconfig.json +9 -0
  48. package/LICENSE.md +0 -21
  49. package/README.md +0 -21
  50. package/dist/index.cjs +0 -2321
  51. package/dist/index.cjs.map +0 -1
  52. package/dist/index.js.map +0 -1
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Cloudflare environment detection and storage utilities
3
+ */
4
+
5
+ declare const process: {
6
+ env: Record<string, string | undefined>
7
+ cwd(): string
8
+ }
9
+
10
+ export interface CloudflareEnvironment {
11
+ isCloudflare: boolean
12
+ platform: 'pages' | 'workers' | 'local'
13
+ storagePath: string
14
+ }
15
+
16
+ /**
17
+ * Detects if running in Cloudflare environment and returns appropriate storage configuration
18
+ */
19
+ export function getCloudflareEnvironment(): CloudflareEnvironment {
20
+ if (process.env.CF_PAGES_BRANCH) {
21
+ return {
22
+ isCloudflare: true,
23
+ platform: 'pages',
24
+ storagePath: '/tmp'
25
+ }
26
+ }
27
+
28
+ if (process.env.CF_WORKER_NAME) {
29
+ return {
30
+ isCloudflare: true,
31
+ platform: 'workers',
32
+ storagePath: '/tmp'
33
+ }
34
+ }
35
+
36
+ return {
37
+ isCloudflare: false,
38
+ platform: 'local',
39
+ storagePath: process.env.PROJECT_ROOT || process.cwd()
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Gets the appropriate storage path for the current environment
45
+ * @param subPath - Optional subdirectory within the storage path
46
+ */
47
+ export function getCloudflareStoragePath(subPath?: string): string {
48
+ const env = getCloudflareEnvironment()
49
+
50
+ if (env.isCloudflare) {
51
+ return subPath ? `/tmp/${subPath}` : '/tmp'
52
+ }
53
+
54
+ const basePath = env.storagePath
55
+ const dataPath = subPath ? `${basePath}/.data/${subPath}` : `${basePath}/.data`
56
+
57
+ return dataPath
58
+ }
59
+
60
+ /**
61
+ * Gets service URL based on Cloudflare environment variables
62
+ */
63
+ export function getCloudflareServiceUrl(fallbackPort = 3000): string {
64
+ if (process.env.CF_PAGES_URL) {
65
+ return process.env.CF_PAGES_URL
66
+ }
67
+
68
+ if (process.env.CF_WORKER_URL) {
69
+ return process.env.CF_WORKER_URL
70
+ }
71
+
72
+ // Local development fallback
73
+ return `http://localhost:${fallbackPort}`
74
+ }
@@ -0,0 +1,40 @@
1
+ import { format, isToday, isYesterday } from "date-fns";
2
+
3
+ /**
4
+ * Formats a date string or Date object into a localized date string
5
+ * @param stringOrDate - Date string or Date object to format
6
+ * @returns Formatted date string (e.g., "Jan 1, 2024") or empty string if no input
7
+ */
8
+ export function formatDate(stringOrDate?: string | Date): string {
9
+ if (!stringOrDate) return "";
10
+
11
+ const date = new Date(stringOrDate);
12
+
13
+ return date.toLocaleDateString(undefined, {
14
+ year: "numeric",
15
+ month: "short",
16
+ day: "numeric",
17
+ });
18
+ }
19
+
20
+ /**
21
+ * Formats a date relative to the current time
22
+ * @param date - Date object to format
23
+ * @returns Formatted string with relative time:
24
+ * - "Today, [time]" for today's dates
25
+ * - "Yesterday, [time]" for yesterday's dates
26
+ * - "MMM d, h:mm a" for dates in current year
27
+ * - "MMM d, yyyy" for dates in other years
28
+ */
29
+ export function formatRelativeDate(date: Date) {
30
+ if (isToday(date)) {
31
+ return `Today, ${format(date, "h:mm a")}`;
32
+ }
33
+ if (isYesterday(date)) {
34
+ return `Yesterday, ${format(date, "h:mm a")}`;
35
+ }
36
+ if (new Date().getFullYear() === date.getFullYear()) {
37
+ return format(date, "MMM d, h:mm a");
38
+ }
39
+ return format(date, "MMM d, yyyy");
40
+ }
@@ -0,0 +1,12 @@
1
+ import { remark } from "remark"
2
+ import strip from "strip-markdown"
3
+
4
+ /**
5
+ * Strips markdown from a string
6
+ * @param markdown - The markdown string to strip
7
+ * @returns The stripped markdown string
8
+ */
9
+ export async function stripMarkdown(markdown: string) {
10
+ const file = await remark().use(strip).process(markdown)
11
+ return String(file)
12
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Stringifies all values in an object, including objects and null values.
3
+ * If obj is undefined, returns an empty object.
4
+ * @param obj - The object to stringify
5
+ * @returns A new object with the same keys as obj, but with all values stringified
6
+ */
7
+ export function stringifyValues(
8
+ obj: Record<string, unknown> | undefined
9
+ ): Record<string, string> {
10
+ const result: Record<string, string> = {}
11
+
12
+ if (!obj) {
13
+ return {}
14
+ }
15
+
16
+ for (const key in obj) {
17
+ const value = obj[key]
18
+ result[key] =
19
+ value === null
20
+ ? "null"
21
+ : typeof value === "object"
22
+ ? JSON.stringify(value)
23
+ : String(value)
24
+ }
25
+
26
+ return result
27
+ }
28
+
29
+ /**
30
+ * Removes empty values (undefined, null, empty strings) from an object
31
+ *
32
+ * @param obj - The object to prune
33
+ * @returns A new object with empty values removed
34
+ */
35
+ export function pruneEmpty<T extends Record<string, unknown>>(
36
+ obj: T | undefined
37
+ ): Partial<T> {
38
+ if (!obj) {
39
+ return {}
40
+ }
41
+
42
+ return Object.entries(obj).reduce(
43
+ (acc, [key, value]) => {
44
+ // Skip undefined, null, and empty strings
45
+ if (value === undefined || value === null || value === "") {
46
+ return acc
47
+ }
48
+ // Avoid spread syntax on accumulator
49
+ acc[key as keyof T] = value as any
50
+ return acc
51
+ },
52
+ {} as Partial<T>
53
+ )
54
+ }
@@ -0,0 +1,64 @@
1
+ import fs from "node:fs/promises"
2
+
3
+ export interface StorageAdapter {
4
+ uploadFile(localPath: string, remotePath: string): Promise<void>
5
+ downloadFile(remotePath: string, localPath: string): Promise<void>
6
+ exists(remotePath: string): Promise<boolean>
7
+ delete(remotePath: string): Promise<void>
8
+ }
9
+
10
+ export class R2StorageAdapter implements StorageAdapter {
11
+ constructor(private bucket: any) {}
12
+
13
+ async uploadFile(localPath: string, remotePath: string): Promise<void> {
14
+ const fileData = await fs.readFile(localPath)
15
+ await this.bucket.put(remotePath, fileData)
16
+ }
17
+
18
+ async downloadFile(remotePath: string, localPath: string): Promise<void> {
19
+ const object = await this.bucket.get(remotePath)
20
+ if (object) {
21
+ await fs.writeFile(localPath, await object.arrayBuffer())
22
+ }
23
+ }
24
+
25
+ async exists(remotePath: string): Promise<boolean> {
26
+ const object = await this.bucket.head(remotePath)
27
+ return object !== null
28
+ }
29
+
30
+ async delete(remotePath: string): Promise<void> {
31
+ await this.bucket.delete(remotePath)
32
+ }
33
+ }
34
+
35
+ export class ExternalDatabaseAdapter implements StorageAdapter {
36
+ constructor(private connectionString: string) {}
37
+
38
+ async uploadFile(localPath: string, remotePath: string): Promise<void> {
39
+ throw new Error('External database storage not yet implemented')
40
+ }
41
+
42
+ async downloadFile(remotePath: string, localPath: string): Promise<void> {
43
+ throw new Error('External database storage not yet implemented')
44
+ }
45
+
46
+ async exists(remotePath: string): Promise<boolean> {
47
+ return false
48
+ }
49
+
50
+ async delete(remotePath: string): Promise<void> {
51
+ }
52
+ }
53
+
54
+ export function createStorageAdapter(): StorageAdapter | null {
55
+ if (typeof globalThis !== 'undefined' && 'XMTP_STORAGE' in globalThis) {
56
+ return new R2StorageAdapter((globalThis as any).XMTP_STORAGE)
57
+ }
58
+
59
+ if (typeof process !== 'undefined' && process.env?.DATABASE_URL) {
60
+ return new ExternalDatabaseAdapter(process.env.DATABASE_URL)
61
+ }
62
+
63
+ return null
64
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Truncates a given string to a specified length, adding an ellipsis if the
3
+ * string is longer than the specified length.
4
+ *
5
+ * @param {string} str - The string to truncate.
6
+ * @param {number} length - The maximum length of the resulting string.
7
+ *
8
+ * @returns {string} The truncated string.
9
+ */
10
+ export function truncate(str: string, length: number) {
11
+ return str.length > length ? `${str.slice(0, length)}...` : str
12
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Get the agent application URL for webhooks and callbacks
3
+ *
4
+ * This function constructs the URL for the main agent application, used primarily
5
+ * for QStash webhooks and external callbacks. It supports multiple deployment
6
+ * environments with fallback logic.
7
+ *
8
+ * @param path - Optional path to append to the base URL (leading slashes are normalized)
9
+ * @returns The complete URL for the agent application
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * // Get base URL
14
+ * getUrl() // "http://localhost:8454/"
15
+ *
16
+ * // Get URL with path
17
+ * getUrl("/qstash/webhook") // "http://localhost:8454/qstash/webhook"
18
+ * getUrl("api/health") // "http://localhost:8454/api/health"
19
+ * ```
20
+ *
21
+ * @remarks
22
+ * URL resolution priority:
23
+ * 1. `AGENT_URL` environment variable (custom deployment)
24
+ * 2. `RAILWAY_PUBLIC_DOMAIN` environment variable (Railway deployment)
25
+ * 3. `http://localhost:8454/` (default)
26
+ */
27
+ export function getUrl(path = "") {
28
+ const trimmedPath = path.replace(/^\/+/, "")
29
+
30
+ if (process.env.AGENT_URL) {
31
+ return `https://${process.env.AGENT_URL}/${trimmedPath}`
32
+ }
33
+
34
+ if (process.env.RAILWAY_PUBLIC_DOMAIN) {
35
+ return `https://${process.env.RAILWAY_PUBLIC_DOMAIN}/${trimmedPath}`
36
+ }
37
+
38
+ return `https://localhost:8454/${trimmedPath}`
39
+ }
@@ -0,0 +1,7 @@
1
+ import { v4 as uuidv4 } from "uuid";
2
+
3
+ // The node:crypto version is not supported in the browser.
4
+ // Use the uuid package instead.
5
+ export function randomUUID(): string {
6
+ return uuidv4();
7
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "@hybrd/tsconfig/internal-package.json",
3
+ "compilerOptions": {
4
+ "emitDeclarationOnly": false,
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src/**/*.ts", "src/**/*.tsx"],
8
+ "exclude": ["node_modules"]
9
+ }
package/LICENSE.md DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) [2023] [Hybrid INC]
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
package/README.md DELETED
@@ -1,21 +0,0 @@
1
- # @hybrd/utils
2
-
3
- Solidity + TypeScript Framework for rapid Web3 development.
4
-
5
- ## Documentation
6
-
7
- For full documentation and examples, visit [hybrid.dev](https://hybrid.dev).
8
-
9
- ## Installation
10
-
11
- ```sh
12
- npm install hybrid
13
- ```
14
-
15
- ## Community
16
-
17
- Check out the following places for more wagmi-related content:
18
-
19
- - Join the discussions on [Discord](https://discord.gg/AcJFXZ9Mfk)
20
- - Follow [@hybrid\_\_dev](https://twitter.com/hybrid__dev) on Twitter for project updates
21
- - Contribute or fork on [GitHub](https://github.com/hybridhq/hybrid)