@hybrd/utils 1.0.8 → 1.1.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.
Files changed (42) hide show
  1. package/.cache/tsbuildinfo.json +1 -1
  2. package/.turbo/turbo-build.log +20 -2
  3. package/dist/index.cjs +261 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +152 -0
  6. package/dist/index.d.ts +152 -10
  7. package/dist/index.js +208 -9
  8. package/dist/index.js.map +1 -0
  9. package/package.json +31 -29
  10. package/tsconfig.json +8 -4
  11. package/tsup.config.ts +14 -0
  12. package/.turbo/turbo-lint$colon$fix.log +0 -6
  13. package/.turbo/turbo-lint.log +0 -6
  14. package/.turbo/turbo-typecheck.log +0 -5
  15. package/dist/index.d.ts.map +0 -1
  16. package/dist/lib/array.d.ts +0 -25
  17. package/dist/lib/array.d.ts.map +0 -1
  18. package/dist/lib/array.js +0 -32
  19. package/dist/lib/cloudflare.d.ts +0 -22
  20. package/dist/lib/cloudflare.d.ts.map +0 -1
  21. package/dist/lib/cloudflare.js +0 -53
  22. package/dist/lib/date.d.ts +0 -17
  23. package/dist/lib/date.d.ts.map +0 -1
  24. package/dist/lib/date.js +0 -37
  25. package/dist/lib/markdown.d.ts +0 -7
  26. package/dist/lib/markdown.d.ts.map +0 -1
  27. package/dist/lib/markdown.js +0 -11
  28. package/dist/lib/object.d.ts +0 -15
  29. package/dist/lib/object.d.ts.map +0 -1
  30. package/dist/lib/object.js +0 -42
  31. package/dist/lib/storage.d.ts +0 -24
  32. package/dist/lib/storage.d.ts.map +0 -1
  33. package/dist/lib/storage.js +0 -50
  34. package/dist/lib/string.d.ts +0 -11
  35. package/dist/lib/string.d.ts.map +0 -1
  36. package/dist/lib/string.js +0 -12
  37. package/dist/lib/urls.d.ts +0 -28
  38. package/dist/lib/urls.d.ts.map +0 -1
  39. package/dist/lib/urls.js +0 -36
  40. package/dist/lib/uuid.d.ts +0 -2
  41. package/dist/lib/uuid.d.ts.map +0 -1
  42. package/dist/lib/uuid.js +0 -6
package/dist/index.js CHANGED
@@ -1,9 +1,208 @@
1
- export * from "./lib/array";
2
- export * from "./lib/cloudflare";
3
- export * from "./lib/storage";
4
- export * from "./lib/date";
5
- export * from "./lib/object";
6
- export * from "./lib/markdown";
7
- export * from "./lib/string";
8
- export * from "./lib/urls";
9
- export * from "./lib/uuid";
1
+ // src/lib/array.ts
2
+ function chunk(arr, size) {
3
+ return Array.from(
4
+ { length: Math.ceil(arr.length / size) },
5
+ (_, i) => arr.slice(i * size, i * size + size)
6
+ );
7
+ }
8
+ function uniq(array) {
9
+ return array.filter((item, index, self) => self.indexOf(item) === index);
10
+ }
11
+ function shuffle(array) {
12
+ if (!array) return [];
13
+ return array.sort(() => Math.random() - 0.5);
14
+ }
15
+
16
+ // src/lib/cloudflare.ts
17
+ function getCloudflareEnvironment() {
18
+ if (process.env.CF_PAGES_BRANCH) {
19
+ return {
20
+ isCloudflare: true,
21
+ platform: "pages",
22
+ storagePath: "/tmp"
23
+ };
24
+ }
25
+ if (process.env.CF_WORKER_NAME) {
26
+ return {
27
+ isCloudflare: true,
28
+ platform: "workers",
29
+ storagePath: "/tmp"
30
+ };
31
+ }
32
+ return {
33
+ isCloudflare: false,
34
+ platform: "local",
35
+ storagePath: process.env.PROJECT_ROOT || process.cwd()
36
+ };
37
+ }
38
+ function getCloudflareStoragePath(subPath) {
39
+ const env = getCloudflareEnvironment();
40
+ if (env.isCloudflare) {
41
+ return subPath ? `/tmp/${subPath}` : "/tmp";
42
+ }
43
+ const basePath = env.storagePath;
44
+ const dataPath = subPath ? `${basePath}/.data/${subPath}` : `${basePath}/.data`;
45
+ return dataPath;
46
+ }
47
+ function getCloudflareServiceUrl(fallbackPort = 3e3) {
48
+ if (process.env.CF_PAGES_URL) {
49
+ return process.env.CF_PAGES_URL;
50
+ }
51
+ if (process.env.CF_WORKER_URL) {
52
+ return process.env.CF_WORKER_URL;
53
+ }
54
+ return `http://localhost:${fallbackPort}`;
55
+ }
56
+
57
+ // src/lib/storage.ts
58
+ import fs from "fs/promises";
59
+ var R2StorageAdapter = class {
60
+ constructor(bucket) {
61
+ this.bucket = bucket;
62
+ }
63
+ async uploadFile(localPath, remotePath) {
64
+ const fileData = await fs.readFile(localPath);
65
+ await this.bucket.put(remotePath, fileData);
66
+ }
67
+ async downloadFile(remotePath, localPath) {
68
+ const object = await this.bucket.get(remotePath);
69
+ if (object) {
70
+ await fs.writeFile(localPath, await object.arrayBuffer());
71
+ }
72
+ }
73
+ async exists(remotePath) {
74
+ const object = await this.bucket.head(remotePath);
75
+ return object !== null;
76
+ }
77
+ async delete(remotePath) {
78
+ await this.bucket.delete(remotePath);
79
+ }
80
+ };
81
+ var ExternalDatabaseAdapter = class {
82
+ constructor(connectionString) {
83
+ this.connectionString = connectionString;
84
+ }
85
+ async uploadFile(localPath, remotePath) {
86
+ throw new Error("External database storage not yet implemented");
87
+ }
88
+ async downloadFile(remotePath, localPath) {
89
+ throw new Error("External database storage not yet implemented");
90
+ }
91
+ async exists(remotePath) {
92
+ return false;
93
+ }
94
+ async delete(remotePath) {
95
+ }
96
+ };
97
+ function createStorageAdapter() {
98
+ if (typeof globalThis !== "undefined" && "XMTP_STORAGE" in globalThis) {
99
+ return new R2StorageAdapter(globalThis.XMTP_STORAGE);
100
+ }
101
+ if (typeof process !== "undefined" && process.env?.DATABASE_URL) {
102
+ return new ExternalDatabaseAdapter(process.env.DATABASE_URL);
103
+ }
104
+ return null;
105
+ }
106
+
107
+ // src/lib/date.ts
108
+ import { format, isToday, isYesterday } from "date-fns";
109
+ function formatDate(stringOrDate) {
110
+ if (!stringOrDate) return "";
111
+ const date = new Date(stringOrDate);
112
+ return date.toLocaleDateString(void 0, {
113
+ year: "numeric",
114
+ month: "short",
115
+ day: "numeric"
116
+ });
117
+ }
118
+ function formatRelativeDate(date) {
119
+ if (isToday(date)) {
120
+ return `Today, ${format(date, "h:mm a")}`;
121
+ }
122
+ if (isYesterday(date)) {
123
+ return `Yesterday, ${format(date, "h:mm a")}`;
124
+ }
125
+ if ((/* @__PURE__ */ new Date()).getFullYear() === date.getFullYear()) {
126
+ return format(date, "MMM d, h:mm a");
127
+ }
128
+ return format(date, "MMM d, yyyy");
129
+ }
130
+
131
+ // src/lib/object.ts
132
+ function stringifyValues(obj) {
133
+ const result = {};
134
+ if (!obj) {
135
+ return {};
136
+ }
137
+ for (const key in obj) {
138
+ const value = obj[key];
139
+ result[key] = value === null ? "null" : typeof value === "object" ? JSON.stringify(value) : String(value);
140
+ }
141
+ return result;
142
+ }
143
+ function pruneEmpty(obj) {
144
+ if (!obj) {
145
+ return {};
146
+ }
147
+ return Object.entries(obj).reduce(
148
+ (acc, [key, value]) => {
149
+ if (value === void 0 || value === null || value === "") {
150
+ return acc;
151
+ }
152
+ acc[key] = value;
153
+ return acc;
154
+ },
155
+ {}
156
+ );
157
+ }
158
+
159
+ // src/lib/markdown.ts
160
+ import { remark } from "remark";
161
+ import strip from "strip-markdown";
162
+ async function stripMarkdown(markdown) {
163
+ const file = await remark().use(strip).process(markdown);
164
+ return String(file);
165
+ }
166
+
167
+ // src/lib/string.ts
168
+ function truncate(str, length) {
169
+ return str.length > length ? `${str.slice(0, length)}...` : str;
170
+ }
171
+
172
+ // src/lib/urls.ts
173
+ function getUrl(path = "") {
174
+ const trimmedPath = path.replace(/^\/+/, "");
175
+ if (process.env.AGENT_URL) {
176
+ return `https://${process.env.AGENT_URL}/${trimmedPath}`;
177
+ }
178
+ if (process.env.RAILWAY_PUBLIC_DOMAIN) {
179
+ return `https://${process.env.RAILWAY_PUBLIC_DOMAIN}/${trimmedPath}`;
180
+ }
181
+ return `https://localhost:8454/${trimmedPath}`;
182
+ }
183
+
184
+ // src/lib/uuid.ts
185
+ import { v4 as uuidv4 } from "uuid";
186
+ function randomUUID() {
187
+ return uuidv4();
188
+ }
189
+ export {
190
+ ExternalDatabaseAdapter,
191
+ R2StorageAdapter,
192
+ chunk,
193
+ createStorageAdapter,
194
+ formatDate,
195
+ formatRelativeDate,
196
+ getCloudflareEnvironment,
197
+ getCloudflareServiceUrl,
198
+ getCloudflareStoragePath,
199
+ getUrl,
200
+ pruneEmpty,
201
+ randomUUID,
202
+ shuffle,
203
+ stringifyValues,
204
+ stripMarkdown,
205
+ truncate,
206
+ uniq
207
+ };
208
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/array.ts","../src/lib/cloudflare.ts","../src/lib/storage.ts","../src/lib/date.ts","../src/lib/object.ts","../src/lib/markdown.ts","../src/lib/string.ts","../src/lib/urls.ts","../src/lib/uuid.ts"],"sourcesContent":["/**\n * Splits an array into chunks of specified size.\n *\n * @template T - The type of the array elements.\n * @param {T[]} arr - The array to split into chunks.\n * @param {number} size - The maximum number of elements per chunk.\n * @returns {T[][]} An array of chunks, where each chunk is an array of T.\n */\nexport function chunk<T>(arr: T[], size: number): T[][] {\n\treturn Array.from({ length: Math.ceil(arr.length / size) }, (_, i) =>\n\t\tarr.slice(i * size, i * size + size)\n\t)\n}\n\n/**\n * Returns a new array with only unique elements from the input array.\n *\n * @template T - The type of the array elements.\n * @param {T[]} array - The array from which to remove duplicate elements.\n * @returns {T[]} A new array containing only unique elements.\n */\nexport function uniq<T>(array: T[]): T[] {\n\treturn array.filter((item, index, self) => self.indexOf(item) === index)\n}\n\n/**\n * Randomly shuffles the elements of an array.\n *\n * @param {any[] | undefined} array - The array to shuffle. If undefined, returns an empty array.\n * @returns {any[]} A shuffled copy of the input array.\n */\nexport function shuffle(array: any[] | undefined) {\n\tif (!array) return []\n\treturn array.sort(() => Math.random() - 0.5)\n}\n","/**\n * Cloudflare environment detection and storage utilities\n */\n\ndeclare const process: {\n env: Record<string, string | undefined>\n cwd(): string\n}\n\nexport interface CloudflareEnvironment {\n isCloudflare: boolean\n platform: 'pages' | 'workers' | 'local'\n storagePath: string\n}\n\n/**\n * Detects if running in Cloudflare environment and returns appropriate storage configuration\n */\nexport function getCloudflareEnvironment(): CloudflareEnvironment {\n if (process.env.CF_PAGES_BRANCH) {\n return {\n isCloudflare: true,\n platform: 'pages',\n storagePath: '/tmp'\n }\n }\n\n if (process.env.CF_WORKER_NAME) {\n return {\n isCloudflare: true,\n platform: 'workers',\n storagePath: '/tmp'\n }\n }\n\n return {\n isCloudflare: false,\n platform: 'local',\n storagePath: process.env.PROJECT_ROOT || process.cwd()\n }\n}\n\n/**\n * Gets the appropriate storage path for the current environment\n * @param subPath - Optional subdirectory within the storage path\n */\nexport function getCloudflareStoragePath(subPath?: string): string {\n const env = getCloudflareEnvironment()\n \n if (env.isCloudflare) {\n return subPath ? `/tmp/${subPath}` : '/tmp'\n }\n\n const basePath = env.storagePath\n const dataPath = subPath ? `${basePath}/.data/${subPath}` : `${basePath}/.data`\n \n return dataPath\n}\n\n/**\n * Gets service URL based on Cloudflare environment variables\n */\nexport function getCloudflareServiceUrl(fallbackPort = 3000): string {\n if (process.env.CF_PAGES_URL) {\n return process.env.CF_PAGES_URL\n }\n\n if (process.env.CF_WORKER_URL) {\n return process.env.CF_WORKER_URL\n }\n\n // Local development fallback\n return `http://localhost:${fallbackPort}`\n}\n","import fs from \"node:fs/promises\"\n\nexport interface StorageAdapter {\n uploadFile(localPath: string, remotePath: string): Promise<void>\n downloadFile(remotePath: string, localPath: string): Promise<void>\n exists(remotePath: string): Promise<boolean>\n delete(remotePath: string): Promise<void>\n}\n\nexport class R2StorageAdapter implements StorageAdapter {\n constructor(private bucket: any) {}\n \n async uploadFile(localPath: string, remotePath: string): Promise<void> {\n const fileData = await fs.readFile(localPath)\n await this.bucket.put(remotePath, fileData)\n }\n \n async downloadFile(remotePath: string, localPath: string): Promise<void> {\n const object = await this.bucket.get(remotePath)\n if (object) {\n await fs.writeFile(localPath, await object.arrayBuffer())\n }\n }\n \n async exists(remotePath: string): Promise<boolean> {\n const object = await this.bucket.head(remotePath)\n return object !== null\n }\n \n async delete(remotePath: string): Promise<void> {\n await this.bucket.delete(remotePath)\n }\n}\n\nexport class ExternalDatabaseAdapter implements StorageAdapter {\n constructor(private connectionString: string) {}\n \n async uploadFile(localPath: string, remotePath: string): Promise<void> {\n throw new Error('External database storage not yet implemented')\n }\n \n async downloadFile(remotePath: string, localPath: string): Promise<void> {\n throw new Error('External database storage not yet implemented')\n }\n \n async exists(remotePath: string): Promise<boolean> {\n return false\n }\n \n async delete(remotePath: string): Promise<void> {\n }\n}\n\nexport function createStorageAdapter(): StorageAdapter | null {\n if (typeof globalThis !== 'undefined' && 'XMTP_STORAGE' in globalThis) {\n return new R2StorageAdapter((globalThis as any).XMTP_STORAGE)\n }\n \n if (typeof process !== 'undefined' && process.env?.DATABASE_URL) {\n return new ExternalDatabaseAdapter(process.env.DATABASE_URL)\n }\n \n return null\n}\n","import { format, isToday, isYesterday } from \"date-fns\";\n\n/**\n * Formats a date string or Date object into a localized date string\n * @param stringOrDate - Date string or Date object to format\n * @returns Formatted date string (e.g., \"Jan 1, 2024\") or empty string if no input\n */\nexport function formatDate(stringOrDate?: string | Date): string {\n\tif (!stringOrDate) return \"\";\n\n\tconst date = new Date(stringOrDate);\n\n\treturn date.toLocaleDateString(undefined, {\n\t\tyear: \"numeric\",\n\t\tmonth: \"short\",\n\t\tday: \"numeric\",\n\t});\n}\n\n/**\n * Formats a date relative to the current time\n * @param date - Date object to format\n * @returns Formatted string with relative time:\n * - \"Today, [time]\" for today's dates\n * - \"Yesterday, [time]\" for yesterday's dates\n * - \"MMM d, h:mm a\" for dates in current year\n * - \"MMM d, yyyy\" for dates in other years\n */\nexport function formatRelativeDate(date: Date) {\n\tif (isToday(date)) {\n\t\treturn `Today, ${format(date, \"h:mm a\")}`;\n\t}\n\tif (isYesterday(date)) {\n\t\treturn `Yesterday, ${format(date, \"h:mm a\")}`;\n\t}\n\tif (new Date().getFullYear() === date.getFullYear()) {\n\t\treturn format(date, \"MMM d, h:mm a\");\n\t}\n\treturn format(date, \"MMM d, yyyy\");\n}\n","/**\n * Stringifies all values in an object, including objects and null values.\n * If obj is undefined, returns an empty object.\n * @param obj - The object to stringify\n * @returns A new object with the same keys as obj, but with all values stringified\n */\nexport function stringifyValues(\n\tobj: Record<string, unknown> | undefined\n): Record<string, string> {\n\tconst result: Record<string, string> = {}\n\n\tif (!obj) {\n\t\treturn {}\n\t}\n\n\tfor (const key in obj) {\n\t\tconst value = obj[key]\n\t\tresult[key] =\n\t\t\tvalue === null\n\t\t\t\t? \"null\"\n\t\t\t\t: typeof value === \"object\"\n\t\t\t\t\t? JSON.stringify(value)\n\t\t\t\t\t: String(value)\n\t}\n\n\treturn result\n}\n\n/**\n * Removes empty values (undefined, null, empty strings) from an object\n *\n * @param obj - The object to prune\n * @returns A new object with empty values removed\n */\nexport function pruneEmpty<T extends Record<string, unknown>>(\n\tobj: T | undefined\n): Partial<T> {\n\tif (!obj) {\n\t\treturn {}\n\t}\n\n\treturn Object.entries(obj).reduce(\n\t\t(acc, [key, value]) => {\n\t\t\t// Skip undefined, null, and empty strings\n\t\t\tif (value === undefined || value === null || value === \"\") {\n\t\t\t\treturn acc\n\t\t\t}\n\t\t\t// Avoid spread syntax on accumulator\n\t\t\tacc[key as keyof T] = value as any\n\t\t\treturn acc\n\t\t},\n\t\t{} as Partial<T>\n\t)\n}\n","import { remark } from \"remark\"\nimport strip from \"strip-markdown\"\n\n/**\n * Strips markdown from a string\n * @param markdown - The markdown string to strip\n * @returns The stripped markdown string\n */\nexport async function stripMarkdown(markdown: string) {\n\tconst file = await remark().use(strip).process(markdown)\n\treturn String(file)\n}\n","/**\n * Truncates a given string to a specified length, adding an ellipsis if the\n * string is longer than the specified length.\n *\n * @param {string} str - The string to truncate.\n * @param {number} length - The maximum length of the resulting string.\n *\n * @returns {string} The truncated string.\n */\nexport function truncate(str: string, length: number) {\n\treturn str.length > length ? `${str.slice(0, length)}...` : str\n}\n","/**\n * Get the agent application URL for webhooks and callbacks\n *\n * This function constructs the URL for the main agent application, used primarily\n * for QStash webhooks and external callbacks. It supports multiple deployment\n * environments with fallback logic.\n *\n * @param path - Optional path to append to the base URL (leading slashes are normalized)\n * @returns The complete URL for the agent application\n *\n * @example\n * ```typescript\n * // Get base URL\n * getUrl() // \"http://localhost:8454/\"\n *\n * // Get URL with path\n * getUrl(\"/qstash/webhook\") // \"http://localhost:8454/qstash/webhook\"\n * getUrl(\"api/health\") // \"http://localhost:8454/api/health\"\n * ```\n *\n * @remarks\n * URL resolution priority:\n * 1. `AGENT_URL` environment variable (custom deployment)\n * 2. `RAILWAY_PUBLIC_DOMAIN` environment variable (Railway deployment)\n * 3. `http://localhost:8454/` (default)\n */\nexport function getUrl(path = \"\") {\n\tconst trimmedPath = path.replace(/^\\/+/, \"\")\n\n\tif (process.env.AGENT_URL) {\n\t\treturn `https://${process.env.AGENT_URL}/${trimmedPath}`\n\t}\n\n\tif (process.env.RAILWAY_PUBLIC_DOMAIN) {\n\t\treturn `https://${process.env.RAILWAY_PUBLIC_DOMAIN}/${trimmedPath}`\n\t}\n\n\treturn `https://localhost:8454/${trimmedPath}`\n}\n","import { v4 as uuidv4 } from \"uuid\";\n\n// The node:crypto version is not supported in the browser.\n// Use the uuid package instead.\nexport function randomUUID(): string {\n\treturn uuidv4();\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,30 +1,32 @@
1
1
  {
2
- "name": "@hybrd/utils",
3
- "version": "1.0.8",
4
- "type": "module",
5
- "exports": {
6
- ".": {
7
- "import": "./dist/index.js",
8
- "types": "./dist/index.d.ts"
9
- }
10
- },
11
- "scripts": {
12
- "build": "tsc",
13
- "clean": "rm -rf .turbo dist",
14
- "lint": "biome lint --unsafe",
15
- "lint:fix": "biome lint --write --unsafe",
16
- "typecheck": "tsc --noEmit"
17
- },
18
- "dependencies": {
19
- "date-fns": "4.1.0",
20
- "mime": "^4.0.7",
21
- "remark": "^15.0.1",
22
- "strip-markdown": "^6.0.0",
23
- "uuid": "11.0.5"
24
- },
25
- "devDependencies": {
26
- "@hybrd/biome": "workspace:*",
27
- "@hybrd/tsconfig": "workspace:*",
28
- "@types/node": "catalog:stack"
29
- }
30
- }
2
+ "name": "@hybrd/utils",
3
+ "version": "1.1.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "import": "./dist/index.js",
9
+ "require": "./dist/index.cjs"
10
+ }
11
+ },
12
+ "dependencies": {
13
+ "date-fns": "4.1.0",
14
+ "mime": "^4.0.7",
15
+ "remark": "^15.0.1",
16
+ "strip-markdown": "^6.0.0",
17
+ "uuid": "11.0.5"
18
+ },
19
+ "devDependencies": {
20
+ "@types/node": "22.8.6",
21
+ "tsup": "^8.5.0",
22
+ "@config/tsconfig": "0.0.0",
23
+ "@config/biome": "0.0.0"
24
+ },
25
+ "scripts": {
26
+ "build": "tsup",
27
+ "clean": "rm -rf .turbo dist",
28
+ "lint": "biome lint --unsafe",
29
+ "lint:fix": "biome lint --write --unsafe",
30
+ "typecheck": "tsc --noEmit"
31
+ }
32
+ }
package/tsconfig.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
- "extends": "@hybrd/tsconfig/internal-package.json",
2
+ "extends": "@config/tsconfig/external-package.json",
3
3
  "compilerOptions": {
4
4
  "emitDeclarationOnly": false,
5
- "outDir": "dist"
5
+ "noEmit": false,
6
+ "outDir": "dist",
7
+ "module": "ES2020",
8
+ "moduleResolution": "node",
9
+ "target": "ES2020"
6
10
  },
7
- "include": ["src/**/*.ts", "src/**/*.tsx"],
8
- "exclude": ["node_modules"]
11
+ "include": ["src/**/*.ts"],
12
+ "exclude": ["node_modules", "dist"]
9
13
  }
package/tsup.config.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { defineConfig } from "tsup"
2
+
3
+ export default defineConfig({
4
+ entry: {
5
+ index: "src/index.ts"
6
+ },
7
+ format: ["cjs", "esm"],
8
+ dts: true,
9
+ splitting: false,
10
+ sourcemap: true,
11
+ clean: true,
12
+ outDir: "dist",
13
+ target: "es2020"
14
+ })
@@ -1,6 +0,0 @@
1
-
2
- 
3
- > @hybrd/utils@1.0.2 lint:fix /Users/ian/Projects/01/hybrid/packages/utils
4
- > biome lint --write --unsafe
5
-
6
- Checked 12 files in 12ms. No fixes applied.
@@ -1,6 +0,0 @@
1
-
2
- 
3
- > @repo/utils@ lint /Users/ian/Projects/01/cointoss/packages/utils
4
- > biome lint --unsafe
5
-
6
- Checked 10 files in 47ms. No fixes applied.
@@ -1,5 +0,0 @@
1
-
2
- 
3
- > @hybrd/utils@1.0.0-alpha.1 typecheck /Users/ian/Projects/01/hybrid/packages/utils
4
- > tsc --noEmit
5
-
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA"}
@@ -1,25 +0,0 @@
1
- /**
2
- * Splits an array into chunks of specified size.
3
- *
4
- * @template T - The type of the array elements.
5
- * @param {T[]} arr - The array to split into chunks.
6
- * @param {number} size - The maximum number of elements per chunk.
7
- * @returns {T[][]} An array of chunks, where each chunk is an array of T.
8
- */
9
- export declare function chunk<T>(arr: T[], size: number): T[][];
10
- /**
11
- * Returns a new array with only unique elements from the input array.
12
- *
13
- * @template T - The type of the array elements.
14
- * @param {T[]} array - The array from which to remove duplicate elements.
15
- * @returns {T[]} A new array containing only unique elements.
16
- */
17
- export declare function uniq<T>(array: T[]): T[];
18
- /**
19
- * Randomly shuffles the elements of an array.
20
- *
21
- * @param {any[] | undefined} array - The array to shuffle. If undefined, returns an empty array.
22
- * @returns {any[]} A shuffled copy of the input array.
23
- */
24
- export declare function shuffle(array: any[] | undefined): any[];
25
- //# sourceMappingURL=array.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"array.d.ts","sourceRoot":"","sources":["../../src/lib/array.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,EAAE,EAAE,CAItD;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAEvC;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,SAAS,SAG/C"}
package/dist/lib/array.js DELETED
@@ -1,32 +0,0 @@
1
- /**
2
- * Splits an array into chunks of specified size.
3
- *
4
- * @template T - The type of the array elements.
5
- * @param {T[]} arr - The array to split into chunks.
6
- * @param {number} size - The maximum number of elements per chunk.
7
- * @returns {T[][]} An array of chunks, where each chunk is an array of T.
8
- */
9
- export function chunk(arr, size) {
10
- return Array.from({ length: Math.ceil(arr.length / size) }, (_, i) => arr.slice(i * size, i * size + size));
11
- }
12
- /**
13
- * Returns a new array with only unique elements from the input array.
14
- *
15
- * @template T - The type of the array elements.
16
- * @param {T[]} array - The array from which to remove duplicate elements.
17
- * @returns {T[]} A new array containing only unique elements.
18
- */
19
- export function uniq(array) {
20
- return array.filter((item, index, self) => self.indexOf(item) === index);
21
- }
22
- /**
23
- * Randomly shuffles the elements of an array.
24
- *
25
- * @param {any[] | undefined} array - The array to shuffle. If undefined, returns an empty array.
26
- * @returns {any[]} A shuffled copy of the input array.
27
- */
28
- export function shuffle(array) {
29
- if (!array)
30
- return [];
31
- return array.sort(() => Math.random() - 0.5);
32
- }
@@ -1,22 +0,0 @@
1
- /**
2
- * Cloudflare environment detection and storage utilities
3
- */
4
- export interface CloudflareEnvironment {
5
- isCloudflare: boolean;
6
- platform: 'pages' | 'workers' | 'local';
7
- storagePath: string;
8
- }
9
- /**
10
- * Detects if running in Cloudflare environment and returns appropriate storage configuration
11
- */
12
- export declare function getCloudflareEnvironment(): CloudflareEnvironment;
13
- /**
14
- * Gets the appropriate storage path for the current environment
15
- * @param subPath - Optional subdirectory within the storage path
16
- */
17
- export declare function getCloudflareStoragePath(subPath?: string): string;
18
- /**
19
- * Gets service URL based on Cloudflare environment variables
20
- */
21
- export declare function getCloudflareServiceUrl(fallbackPort?: number): string;
22
- //# sourceMappingURL=cloudflare.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../src/lib/cloudflare.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,OAAO,CAAA;IACrB,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAA;IACvC,WAAW,EAAE,MAAM,CAAA;CACpB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,qBAAqB,CAsBhE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAWjE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,SAAO,GAAG,MAAM,CAWnE"}
@@ -1,53 +0,0 @@
1
- /**
2
- * Cloudflare environment detection and storage utilities
3
- */
4
- /**
5
- * Detects if running in Cloudflare environment and returns appropriate storage configuration
6
- */
7
- export function getCloudflareEnvironment() {
8
- if (process.env.CF_PAGES_BRANCH) {
9
- return {
10
- isCloudflare: true,
11
- platform: 'pages',
12
- storagePath: '/tmp'
13
- };
14
- }
15
- if (process.env.CF_WORKER_NAME) {
16
- return {
17
- isCloudflare: true,
18
- platform: 'workers',
19
- storagePath: '/tmp'
20
- };
21
- }
22
- return {
23
- isCloudflare: false,
24
- platform: 'local',
25
- storagePath: process.env.PROJECT_ROOT || process.cwd()
26
- };
27
- }
28
- /**
29
- * Gets the appropriate storage path for the current environment
30
- * @param subPath - Optional subdirectory within the storage path
31
- */
32
- export function getCloudflareStoragePath(subPath) {
33
- const env = getCloudflareEnvironment();
34
- if (env.isCloudflare) {
35
- return subPath ? `/tmp/${subPath}` : '/tmp';
36
- }
37
- const basePath = env.storagePath;
38
- const dataPath = subPath ? `${basePath}/.data/${subPath}` : `${basePath}/.data`;
39
- return dataPath;
40
- }
41
- /**
42
- * Gets service URL based on Cloudflare environment variables
43
- */
44
- export function getCloudflareServiceUrl(fallbackPort = 3000) {
45
- if (process.env.CF_PAGES_URL) {
46
- return process.env.CF_PAGES_URL;
47
- }
48
- if (process.env.CF_WORKER_URL) {
49
- return process.env.CF_WORKER_URL;
50
- }
51
- // Local development fallback
52
- return `http://localhost:${fallbackPort}`;
53
- }
@@ -1,17 +0,0 @@
1
- /**
2
- * Formats a date string or Date object into a localized date string
3
- * @param stringOrDate - Date string or Date object to format
4
- * @returns Formatted date string (e.g., "Jan 1, 2024") or empty string if no input
5
- */
6
- export declare function formatDate(stringOrDate?: string | Date): string;
7
- /**
8
- * Formats a date relative to the current time
9
- * @param date - Date object to format
10
- * @returns Formatted string with relative time:
11
- * - "Today, [time]" for today's dates
12
- * - "Yesterday, [time]" for yesterday's dates
13
- * - "MMM d, h:mm a" for dates in current year
14
- * - "MMM d, yyyy" for dates in other years
15
- */
16
- export declare function formatRelativeDate(date: Date): string;
17
- //# sourceMappingURL=date.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/lib/date.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAU/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,UAW5C"}
package/dist/lib/date.js DELETED
@@ -1,37 +0,0 @@
1
- import { format, isToday, isYesterday } from "date-fns";
2
- /**
3
- * Formats a date string or Date object into a localized date string
4
- * @param stringOrDate - Date string or Date object to format
5
- * @returns Formatted date string (e.g., "Jan 1, 2024") or empty string if no input
6
- */
7
- export function formatDate(stringOrDate) {
8
- if (!stringOrDate)
9
- return "";
10
- const date = new Date(stringOrDate);
11
- return date.toLocaleDateString(undefined, {
12
- year: "numeric",
13
- month: "short",
14
- day: "numeric",
15
- });
16
- }
17
- /**
18
- * Formats a date relative to the current time
19
- * @param date - Date object to format
20
- * @returns Formatted string with relative time:
21
- * - "Today, [time]" for today's dates
22
- * - "Yesterday, [time]" for yesterday's dates
23
- * - "MMM d, h:mm a" for dates in current year
24
- * - "MMM d, yyyy" for dates in other years
25
- */
26
- export function formatRelativeDate(date) {
27
- if (isToday(date)) {
28
- return `Today, ${format(date, "h:mm a")}`;
29
- }
30
- if (isYesterday(date)) {
31
- return `Yesterday, ${format(date, "h:mm a")}`;
32
- }
33
- if (new Date().getFullYear() === date.getFullYear()) {
34
- return format(date, "MMM d, h:mm a");
35
- }
36
- return format(date, "MMM d, yyyy");
37
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * Strips markdown from a string
3
- * @param markdown - The markdown string to strip
4
- * @returns The stripped markdown string
5
- */
6
- export declare function stripMarkdown(markdown: string): Promise<string>;
7
- //# sourceMappingURL=markdown.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/lib/markdown.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,mBAGnD"}
@@ -1,11 +0,0 @@
1
- import { remark } from "remark";
2
- import strip from "strip-markdown";
3
- /**
4
- * Strips markdown from a string
5
- * @param markdown - The markdown string to strip
6
- * @returns The stripped markdown string
7
- */
8
- export async function stripMarkdown(markdown) {
9
- const file = await remark().use(strip).process(markdown);
10
- return String(file);
11
- }
@@ -1,15 +0,0 @@
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 declare function stringifyValues(obj: Record<string, unknown> | undefined): Record<string, string>;
8
- /**
9
- * Removes empty values (undefined, null, empty strings) from an object
10
- *
11
- * @param obj - The object to prune
12
- * @returns A new object with empty values removed
13
- */
14
- export declare function pruneEmpty<T extends Record<string, unknown>>(obj: T | undefined): Partial<T>;
15
- //# sourceMappingURL=object.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"object.d.ts","sourceRoot":"","sources":["../../src/lib/object.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GACtC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAkBxB;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3D,GAAG,EAAE,CAAC,GAAG,SAAS,GAChB,OAAO,CAAC,CAAC,CAAC,CAiBZ"}