@reasonabletech/utils 0.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.
- package/README.md +102 -0
- package/dist/datetime.js +81 -0
- package/dist/datetime.js.map +1 -0
- package/dist/index.js +347 -0
- package/dist/index.js.map +1 -0
- package/dist/object.js +48 -0
- package/dist/object.js.map +1 -0
- package/dist/result.js +80 -0
- package/dist/result.js.map +1 -0
- package/dist/retry.js +76 -0
- package/dist/retry.js.map +1 -0
- package/dist/src/async.d.ts +43 -0
- package/dist/src/async.d.ts.map +1 -0
- package/dist/src/datetime.d.ts +166 -0
- package/dist/src/datetime.d.ts.map +1 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/object.d.ts +292 -0
- package/dist/src/object.d.ts.map +1 -0
- package/dist/src/result.d.ts +113 -0
- package/dist/src/result.d.ts.map +1 -0
- package/dist/src/retry.d.ts +145 -0
- package/dist/src/retry.d.ts.map +1 -0
- package/dist/src/string.d.ts +75 -0
- package/dist/src/string.d.ts.map +1 -0
- package/dist/src/type-guards.d.ts +26 -0
- package/dist/src/type-guards.d.ts.map +1 -0
- package/dist/string.js +48 -0
- package/dist/string.js.map +1 -0
- package/docs/README.md +35 -0
- package/docs/guides/migration.md +47 -0
- package/docs/guides/usage-guide.md +99 -0
- package/docs/utility-functions.md +2489 -0
- package/package.json +100 -0
package/dist/string.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// src/string.ts
|
|
2
|
+
function isEmptyString(value) {
|
|
3
|
+
return value == null || value.trim().length === 0;
|
|
4
|
+
}
|
|
5
|
+
function isNonEmptyString(value) {
|
|
6
|
+
return value != null && value.trim().length > 0;
|
|
7
|
+
}
|
|
8
|
+
function truncateString(str, maxLength) {
|
|
9
|
+
if (str.length <= maxLength) {
|
|
10
|
+
return str;
|
|
11
|
+
}
|
|
12
|
+
return `${str.slice(0, maxLength - 3)}...`;
|
|
13
|
+
}
|
|
14
|
+
function capitalize(str) {
|
|
15
|
+
if (str.length === 0) {
|
|
16
|
+
return str;
|
|
17
|
+
}
|
|
18
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
19
|
+
}
|
|
20
|
+
function encodeBase64Url(data) {
|
|
21
|
+
const base64 = Buffer.from(data).toString("base64");
|
|
22
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
23
|
+
}
|
|
24
|
+
function decodeBase64Url(encoded) {
|
|
25
|
+
const base64 = encoded.replace(/-/g, "+").replace(/_/g, "/");
|
|
26
|
+
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
27
|
+
return Buffer.from(padded, "base64");
|
|
28
|
+
}
|
|
29
|
+
function isValidBase64Url(str) {
|
|
30
|
+
const base64UrlRegex = /^[A-Za-z0-9_-]*$/;
|
|
31
|
+
return base64UrlRegex.test(str);
|
|
32
|
+
}
|
|
33
|
+
function getErrorMessage(error) {
|
|
34
|
+
if (error instanceof Error) {
|
|
35
|
+
return error.message;
|
|
36
|
+
}
|
|
37
|
+
if (typeof error === "string") {
|
|
38
|
+
return error;
|
|
39
|
+
}
|
|
40
|
+
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
41
|
+
return error.message;
|
|
42
|
+
}
|
|
43
|
+
return String(error);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { capitalize, decodeBase64Url, encodeBase64Url, getErrorMessage, isEmptyString, isNonEmptyString, isValidBase64Url, truncateString };
|
|
47
|
+
//# sourceMappingURL=string.js.map
|
|
48
|
+
//# sourceMappingURL=string.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/string.ts"],"names":[],"mappings":";AASO,SAAS,cAAc,KAAA,EAA2C;AACvE,EAAA,OAAO,KAAA,IAAS,IAAA,IAAQ,KAAA,CAAM,IAAA,GAAO,MAAA,KAAW,CAAA;AAClD;AAOO,SAAS,iBACd,KAAA,EACiB;AACjB,EAAA,OAAO,KAAA,IAAS,IAAA,IAAQ,KAAA,CAAM,IAAA,GAAO,MAAA,GAAS,CAAA;AAChD;AAQO,SAAS,cAAA,CAAe,KAAa,SAAA,EAA2B;AACrE,EAAA,IAAI,GAAA,CAAI,UAAU,SAAA,EAAW;AAC3B,IAAA,OAAO,GAAA;AAAA,EACT;AACA,EAAA,OAAO,GAAG,GAAA,CAAI,KAAA,CAAM,CAAA,EAAG,SAAA,GAAY,CAAC,CAAC,CAAA,GAAA,CAAA;AACvC;AAOO,SAAS,WAAW,GAAA,EAAqB;AAC9C,EAAA,IAAI,GAAA,CAAI,WAAW,CAAA,EAAG;AACpB,IAAA,OAAO,GAAA;AAAA,EACT;AACA,EAAA,OAAO,GAAA,CAAI,OAAO,CAAC,CAAA,CAAE,aAAY,GAAI,GAAA,CAAI,MAAM,CAAC,CAAA;AAClD;AAOO,SAAS,gBAAgB,IAAA,EAA+B;AAC7D,EAAA,MAAM,SAAS,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,SAAS,QAAQ,CAAA;AAClD,EAAA,OAAO,MAAA,CAAO,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,IAAA,EAAM,EAAE,CAAA;AACxE;AAOO,SAAS,gBAAgB,OAAA,EAAyB;AACvD,EAAA,MAAM,MAAA,GAAS,QAAQ,OAAA,CAAQ,IAAA,EAAM,GAAG,CAAA,CAAE,OAAA,CAAQ,MAAM,GAAG,CAAA;AAE3D,EAAA,MAAM,MAAA,GAAS,SAAS,GAAA,CAAI,MAAA,CAAA,CAAQ,IAAK,MAAA,CAAO,MAAA,GAAS,KAAM,CAAC,CAAA;AAChE,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAA,EAAQ,QAAQ,CAAA;AACrC;AAOO,SAAS,iBAAiB,GAAA,EAAsB;AACrD,EAAA,MAAM,cAAA,GAAiB,kBAAA;AACvB,EAAA,OAAO,cAAA,CAAe,KAAK,GAAG,CAAA;AAChC;AA6BO,SAAS,gBAAgB,KAAA,EAAwB;AACtD,EAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,IAAA,OAAO,KAAA,CAAM,OAAA;AAAA,EACf;AACA,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,IACE,OAAO,KAAA,KAAU,QAAA,IACjB,KAAA,KAAU,IAAA,IACV,aAAa,KAAA,IACb,OAAO,KAAA,CAAM,OAAA,KAAY,QAAA,EACzB;AACA,IAAA,OAAO,KAAA,CAAM,OAAA;AAAA,EACf;AACA,EAAA,OAAO,OAAO,KAAK,CAAA;AACrB","file":"string.js","sourcesContent":["/**\n * String utility functions\n */\n\n/**\n * Check if a string is empty or only contains whitespace\n * @param value - The string to check\n * @returns true if the string is empty, null, undefined, or only whitespace\n */\nexport function isEmptyString(value: string | null | undefined): boolean {\n return value == null || value.trim().length === 0;\n}\n\n/**\n * Check if a string is not empty and contains non-whitespace characters\n * @param value - The string to check\n * @returns true if the string has content\n */\nexport function isNonEmptyString(\n value: string | null | undefined,\n): value is string {\n return value != null && value.trim().length > 0;\n}\n\n/**\n * Truncate a string to a maximum length with ellipsis\n * @param str - The string to truncate\n * @param maxLength - Maximum length (including ellipsis)\n * @returns Truncated string with ellipsis if needed\n */\nexport function truncateString(str: string, maxLength: number): string {\n if (str.length <= maxLength) {\n return str;\n }\n return `${str.slice(0, maxLength - 3)}...`;\n}\n\n/**\n * Capitalize the first letter of a string\n * @param str - The string to capitalize\n * @returns String with first letter capitalized\n */\nexport function capitalize(str: string): string {\n if (str.length === 0) {\n return str;\n }\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n/**\n * Create a base64url encoded string\n * @param data - Data to encode (string or Buffer)\n * @returns Base64url encoded string (URL-safe, no padding)\n */\nexport function encodeBase64Url(data: string | Buffer): string {\n const base64 = Buffer.from(data).toString(\"base64\");\n return base64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=/g, \"\");\n}\n\n/**\n * Decode a base64url encoded string\n * @param encoded - Base64url encoded string\n * @returns Decoded data as Buffer\n */\nexport function decodeBase64Url(encoded: string): Buffer {\n const base64 = encoded.replace(/-/g, \"+\").replace(/_/g, \"/\");\n // Add padding if needed\n const padded = base64 + \"=\".repeat((4 - (base64.length % 4)) % 4);\n return Buffer.from(padded, \"base64\");\n}\n\n/**\n * Check if a string is a valid base64url format\n * @param str - String to validate\n * @returns True if the string is valid base64url format\n */\nexport function isValidBase64Url(str: string): boolean {\n const base64UrlRegex = /^[A-Za-z0-9_-]*$/;\n return base64UrlRegex.test(str);\n}\n\n/**\n * Extract a message string from an unknown error value\n * @deprecated This utility is unnecessary. The logger accepts `ErrorLike` (unknown)\n * directly via `logger.error(tag, message, error)`. Pass errors directly to the\n * logger instead of manually converting to strings.\n *\n * See: docs/standards/error-boundary-patterns.md\n * @example\n * ```typescript\n * // ❌ DEPRECATED: Manual error string extraction\n * try {\n * await operation();\n * } catch (error) {\n * logger.error(\"Component\", getErrorMessage(error));\n * }\n *\n * // ✅ CORRECT: Pass error directly to logger\n * try {\n * await operation();\n * } catch (error) {\n * logger.error(\"Component\", \"Operation failed\", error);\n * }\n * ```\n * @param error - Error value (Error object, string, or other)\n * @returns Error message string\n * @internal\n */\nexport function getErrorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n if (typeof error === \"string\") {\n return error;\n }\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"message\" in error &&\n typeof error.message === \"string\"\n ) {\n return error.message;\n }\n return String(error);\n}\n"]}
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @reasonabletech/utils Documentation
|
|
2
|
+
|
|
3
|
+
Reference documentation for shared runtime utility helpers in the core-utils monorepo.
|
|
4
|
+
|
|
5
|
+
## Start Here
|
|
6
|
+
|
|
7
|
+
- [Usage Guide](./guides/usage-guide.md)
|
|
8
|
+
- [Utility Function Reference](./utility-functions.md)
|
|
9
|
+
|
|
10
|
+
## Quick Example
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { err, ok, type Result } from "@reasonabletech/utils";
|
|
14
|
+
|
|
15
|
+
function divide(a: number, b: number): Result<number, string> {
|
|
16
|
+
if (b === 0) {
|
|
17
|
+
return err("Division by zero");
|
|
18
|
+
}
|
|
19
|
+
return ok(a / b);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const result = divide(10, 2);
|
|
23
|
+
if (result.success) {
|
|
24
|
+
console.log("Result:", result.value);
|
|
25
|
+
} else {
|
|
26
|
+
console.error("Error:", result.error);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Monorepo Context
|
|
31
|
+
|
|
32
|
+
- [Package README](../README.md)
|
|
33
|
+
- [Architecture](../../../docs/architecture.md) — How packages relate
|
|
34
|
+
- [Tooling](../../../docs/tooling.md) — Turbo, Changesets details
|
|
35
|
+
- [Contributing](../../../CONTRIBUTING.md)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Migration Guide
|
|
2
|
+
|
|
3
|
+
This guide documents breaking changes and how to migrate between major versions of @reasonabletech/utils.
|
|
4
|
+
|
|
5
|
+
## Current Version
|
|
6
|
+
|
|
7
|
+
The current major version is **0.x** (pre-1.0). The API is stabilizing but may have breaking changes before 1.0.
|
|
8
|
+
|
|
9
|
+
## Future Migration Notes
|
|
10
|
+
|
|
11
|
+
_No breaking changes documented yet. This section will be updated when breaking changes are released._
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Migration Template
|
|
16
|
+
|
|
17
|
+
When documenting a breaking change, use this structure:
|
|
18
|
+
|
|
19
|
+
### Migrating from X.x to Y.0
|
|
20
|
+
|
|
21
|
+
#### Breaking Changes
|
|
22
|
+
|
|
23
|
+
1. **Change description** - Brief explanation of what changed
|
|
24
|
+
|
|
25
|
+
**Before:**
|
|
26
|
+
```typescript
|
|
27
|
+
// Old usage
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**After:**
|
|
31
|
+
```typescript
|
|
32
|
+
// New usage
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
2. **Another change** - Description
|
|
36
|
+
|
|
37
|
+
#### Deprecations
|
|
38
|
+
|
|
39
|
+
- `oldFunction()` is deprecated in favor of `newFunction()`
|
|
40
|
+
|
|
41
|
+
#### New Features
|
|
42
|
+
|
|
43
|
+
- Feature description
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
_Last updated: [date]_
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# @reasonabletech/utils Usage Guide
|
|
2
|
+
|
|
3
|
+
This guide covers canonical usage patterns for shared utility helpers in greenfield packages and applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @reasonabletech/utils
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { err, ok, type Result } from "@reasonabletech/utils";
|
|
15
|
+
|
|
16
|
+
function divide(a: number, b: number): Result<number, string> {
|
|
17
|
+
if (b === 0) {
|
|
18
|
+
return err("Division by zero");
|
|
19
|
+
}
|
|
20
|
+
return ok(a / b);
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Core Use Cases
|
|
25
|
+
|
|
26
|
+
### Result-Based Error Handling
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { andThen, err, ok, type Result } from "@reasonabletech/utils";
|
|
30
|
+
|
|
31
|
+
function parseId(input: string): Result<number, string> {
|
|
32
|
+
const parsed = Number(input);
|
|
33
|
+
return Number.isInteger(parsed) ? ok(parsed) : err("Invalid ID");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const result = andThen(parseId("42"), (id) => ok({ id }));
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Retry Helpers
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { retryWithBackoff } from "@reasonabletech/utils";
|
|
43
|
+
|
|
44
|
+
const response = await retryWithBackoff(
|
|
45
|
+
async () => await fetch("https://api.example.com/health"),
|
|
46
|
+
3,
|
|
47
|
+
500,
|
|
48
|
+
);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Object Construction Helpers
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { includeIf, includeIfDefined } from "@reasonabletech/utils";
|
|
55
|
+
|
|
56
|
+
const payload = {
|
|
57
|
+
name: "core-utils",
|
|
58
|
+
...includeIf("description", process.env.PKG_DESCRIPTION),
|
|
59
|
+
...includeIfDefined({
|
|
60
|
+
homepage: process.env.PKG_HOMEPAGE,
|
|
61
|
+
repository: process.env.PKG_REPOSITORY,
|
|
62
|
+
}),
|
|
63
|
+
};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Datetime + Async Helpers
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { addDays, now, pipeAsync } from "@reasonabletech/utils";
|
|
70
|
+
|
|
71
|
+
const nextWeek = addDays(now(), 7);
|
|
72
|
+
|
|
73
|
+
const normalized = await pipeAsync(" core-utils ", [
|
|
74
|
+
async (value) => value.trim(),
|
|
75
|
+
async (value) => value.toUpperCase(),
|
|
76
|
+
]);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Subpath Imports
|
|
80
|
+
|
|
81
|
+
Use subpaths when you want narrower imports:
|
|
82
|
+
|
|
83
|
+
- `@reasonabletech/utils/result`
|
|
84
|
+
- `@reasonabletech/utils/datetime`
|
|
85
|
+
- `@reasonabletech/utils/object`
|
|
86
|
+
- `@reasonabletech/utils/string`
|
|
87
|
+
- `@reasonabletech/utils/retry`
|
|
88
|
+
|
|
89
|
+
## Troubleshooting
|
|
90
|
+
|
|
91
|
+
### `Result` shape confusion
|
|
92
|
+
|
|
93
|
+
The shared `Result` type uses `success` + `value`/`error` fields. Prefer helper functions (`ok`, `err`, `isSuccess`, `isFailure`) instead of creating objects manually.
|
|
94
|
+
|
|
95
|
+
## Related Documentation
|
|
96
|
+
|
|
97
|
+
- [Package Docs Index](../README.md)
|
|
98
|
+
- [Utility Function Reference](../utility-functions.md)
|
|
99
|
+
- [Package README](../../README.md)
|