@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 ADDED
@@ -0,0 +1,102 @@
1
+ # @reasonabletech/utils
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@reasonabletech/utils.svg)](https://www.npmjs.com/package/@reasonabletech/utils)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@reasonabletech/utils.svg)](https://www.npmjs.com/package/@reasonabletech/utils)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
7
+
8
+ `@reasonabletech/utils` provides runtime helpers for Result-based error handling, datetime operations, object construction, string utilities, retries, and async pipelines. It has no external runtime dependencies.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ pnpm add @reasonabletech/utils
14
+ ```
15
+
16
+ ## Requirements
17
+
18
+ This package has **no peer dependencies** and no external runtime dependencies. It works with any modern JavaScript/TypeScript environment (Node.js >= 22, modern browsers).
19
+
20
+ ## Exported Entry Points
21
+
22
+ | Import Path | Purpose |
23
+ | -------------------------------- | -------------------------------------------------- |
24
+ | `@reasonabletech/utils` | Combined exports for all utility modules |
25
+ | `@reasonabletech/utils/result` | Result types and combinators |
26
+ | `@reasonabletech/utils/datetime` | Date and time helpers |
27
+ | `@reasonabletech/utils/object` | Object construction and property filtering helpers |
28
+ | `@reasonabletech/utils/string` | String and base64url helpers |
29
+ | `@reasonabletech/utils/retry` | Retry and backoff helpers |
30
+
31
+ ## Usage
32
+
33
+ ### Result Helpers
34
+
35
+ `Result<T, E>` represents either a successful value (`ok`) or a typed error (`err`). It avoids thrown exceptions for expected failure cases, making error handling explicit and composable. `andThen` chains operations that may themselves fail.
36
+
37
+ ```ts
38
+ import { andThen, err, ok, type Result } from "@reasonabletech/utils";
39
+
40
+ function parsePort(value: string): Result<number, string> {
41
+ const parsed = Number(value);
42
+ return Number.isInteger(parsed) && parsed > 0
43
+ ? ok(parsed)
44
+ : err("Invalid port");
45
+ }
46
+
47
+ const result = andThen(parsePort("3000"), (port) => ok({ port }));
48
+ ```
49
+
50
+ ### Retry Helpers
51
+
52
+ `retryWithBackoff` re-attempts an async operation up to a maximum number of tries, waiting an increasing delay between each attempt. Useful for network calls or external services that may transiently fail.
53
+
54
+ ```ts
55
+ import { retryWithBackoff } from "@reasonabletech/utils";
56
+
57
+ const response = await retryWithBackoff(
58
+ async () => await fetch("https://api.example.com/health"),
59
+ 3,
60
+ 500,
61
+ );
62
+ ```
63
+
64
+ ### Object Construction Helpers
65
+
66
+ ```ts
67
+ import { includeIf, includeIfDefined } from "@reasonabletech/utils";
68
+
69
+ const payload = {
70
+ name: "core-utils",
71
+ ...includeIf("description", process.env.PKG_DESCRIPTION),
72
+ ...includeIfDefined({
73
+ homepage: process.env.PKG_HOMEPAGE,
74
+ repository: process.env.PKG_REPOSITORY,
75
+ }),
76
+ };
77
+ ```
78
+
79
+ ### Datetime + Async Helpers
80
+
81
+ ```ts
82
+ import { addDays, now, pipeAsync } from "@reasonabletech/utils";
83
+
84
+ const nextWeek = addDays(now(), 7);
85
+
86
+ const normalized = await pipeAsync(" core-utils ", [
87
+ async (value) => value.trim(),
88
+ async (value) => value.toUpperCase(),
89
+ ]);
90
+ ```
91
+
92
+ ## Changelog
93
+
94
+ See [CHANGELOG.md](./CHANGELOG.md) for release history.
95
+
96
+ This package follows [Semantic Versioning](https://semver.org/). Breaking changes are documented with migration guides when applicable.
97
+
98
+ ## Additional References
99
+
100
+ - [Usage Guide](./docs/guides/usage-guide.md)
101
+ - [Package Docs](./docs/README.md)
102
+ - [Utility Function Docs](./docs/utility-functions.md)
@@ -0,0 +1,81 @@
1
+ // src/datetime.ts
2
+ function dateToUnixTimestamp(date) {
3
+ return Math.floor(date.getTime() / 1e3);
4
+ }
5
+ function unixTimestampToDate(timestamp) {
6
+ return new Date(timestamp * 1e3);
7
+ }
8
+ function dateToISOString(date) {
9
+ return date.toISOString();
10
+ }
11
+ function isoStringToDate(isoString) {
12
+ return new Date(isoString);
13
+ }
14
+ function isDateInPast(date) {
15
+ return date.getTime() < Date.now();
16
+ }
17
+ function isDateInFuture(date) {
18
+ return date.getTime() > Date.now();
19
+ }
20
+ function addSeconds(date, seconds) {
21
+ return new Date(date.getTime() + seconds * 1e3);
22
+ }
23
+ function subtractSeconds(date, seconds) {
24
+ return new Date(date.getTime() - seconds * 1e3);
25
+ }
26
+ function addMinutes(date, minutes) {
27
+ return addSeconds(date, minutes * 60);
28
+ }
29
+ function subtractMinutes(date, minutes) {
30
+ return subtractSeconds(date, minutes * 60);
31
+ }
32
+ function addHours(date, hours) {
33
+ return addMinutes(date, hours * 60);
34
+ }
35
+ function subtractHours(date, hours) {
36
+ return subtractMinutes(date, hours * 60);
37
+ }
38
+ function addDays(date, days) {
39
+ return addHours(date, days * 24);
40
+ }
41
+ function subtractDays(date, days) {
42
+ return subtractHours(date, days * 24);
43
+ }
44
+ function now() {
45
+ return /* @__PURE__ */ new Date();
46
+ }
47
+ function normalizeToDate(dateOrString) {
48
+ if (dateOrString instanceof Date) {
49
+ return dateOrString;
50
+ }
51
+ return isoStringToDate(dateOrString);
52
+ }
53
+ function diffInSeconds(laterDate, earlierDate) {
54
+ return Math.floor((laterDate.getTime() - earlierDate.getTime()) / 1e3);
55
+ }
56
+ function diffInMinutes(laterDate, earlierDate) {
57
+ return Math.floor(diffInSeconds(laterDate, earlierDate) / 60);
58
+ }
59
+ function diffInHours(laterDate, earlierDate) {
60
+ return Math.floor(diffInMinutes(laterDate, earlierDate) / 60);
61
+ }
62
+ function diffInDays(laterDate, earlierDate) {
63
+ return Math.floor(diffInHours(laterDate, earlierDate) / 24);
64
+ }
65
+ function isSameDay(date1, date2) {
66
+ return date1.getUTCFullYear() === date2.getUTCFullYear() && date1.getUTCMonth() === date2.getUTCMonth() && date1.getUTCDate() === date2.getUTCDate();
67
+ }
68
+ function formatDateISO(date) {
69
+ const parts = date.toISOString().split("T");
70
+ return parts[0] ?? "";
71
+ }
72
+ function formatTimeISO(date) {
73
+ const parts = date.toISOString().split("T");
74
+ const timePart = parts[1];
75
+ const timeOnly = timePart.split(".")[0];
76
+ return timeOnly;
77
+ }
78
+
79
+ export { addDays, addHours, addMinutes, addSeconds, dateToISOString, dateToUnixTimestamp, diffInDays, diffInHours, diffInMinutes, diffInSeconds, formatDateISO, formatTimeISO, isDateInFuture, isDateInPast, isSameDay, isoStringToDate, normalizeToDate, now, subtractDays, subtractHours, subtractMinutes, subtractSeconds, unixTimestampToDate };
80
+ //# sourceMappingURL=datetime.js.map
81
+ //# sourceMappingURL=datetime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/datetime.ts"],"names":[],"mappings":"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","file":"datetime.js","sourcesContent":["/**\n * Date/Time Utility Functions\n *\n * This module provides standardized date/time handling utilities for the `@reasonabletech` platform.\n * It ensures consistent use of Date objects internally while providing standard conversions\n * when required by specifications or APIs.\n *\n * Key principles:\n * - Use Date objects for all internal date/time representations\n * - Only convert to strings/numbers when required by external specs/APIs\n * - Provide clear, descriptive function names\n * - Handle edge cases gracefully\n */\n\n/**\n * Converts a Date object to Unix timestamp (seconds since epoch)\n * @param date - The Date object to convert\n * @returns Unix timestamp in seconds\n */\nexport function dateToUnixTimestamp(date: Date): number {\n return Math.floor(date.getTime() / 1000);\n}\n\n/**\n * Converts a Unix timestamp (seconds since epoch) to a Date object\n * @param timestamp - Unix timestamp in seconds\n * @returns Date object\n */\nexport function unixTimestampToDate(timestamp: number): Date {\n return new Date(timestamp * 1000);\n}\n\n/**\n * Converts a Date object to ISO string\n * @param date - The Date object to convert\n * @returns ISO string representation\n */\nexport function dateToISOString(date: Date): string {\n return date.toISOString();\n}\n\n/**\n * Converts an ISO string to a Date object\n * @param isoString - ISO string representation\n * @returns Date object\n */\nexport function isoStringToDate(isoString: string): Date {\n return new Date(isoString);\n}\n\n/**\n * Checks if a Date object represents a time in the past\n * @param date - The Date object to check\n * @returns true if the date is in the past\n */\nexport function isDateInPast(date: Date): boolean {\n return date.getTime() < Date.now();\n}\n\n/**\n * Checks if a Date object represents a time in the future\n * @param date - The Date object to check\n * @returns true if the date is in the future\n */\nexport function isDateInFuture(date: Date): boolean {\n return date.getTime() > Date.now();\n}\n\n/**\n * Adds seconds to a Date object\n * @param date - The base Date object\n * @param seconds - Number of seconds to add\n * @returns New Date object with added seconds\n */\nexport function addSeconds(date: Date, seconds: number): Date {\n return new Date(date.getTime() + (seconds * 1000));\n}\n\n/**\n * Subtracts seconds from a Date object\n * @param date - The base Date object\n * @param seconds - Number of seconds to subtract\n * @returns New Date object with subtracted seconds\n */\nexport function subtractSeconds(date: Date, seconds: number): Date {\n return new Date(date.getTime() - (seconds * 1000));\n}\n\n/**\n * Adds minutes to a Date object\n * @param date - The base Date object\n * @param minutes - Number of minutes to add\n * @returns New Date object with added minutes\n */\nexport function addMinutes(date: Date, minutes: number): Date {\n return addSeconds(date, minutes * 60);\n}\n\n/**\n * Subtracts minutes from a Date object\n * @param date - The base Date object\n * @param minutes - Number of minutes to subtract\n * @returns New Date object with subtracted minutes\n */\nexport function subtractMinutes(date: Date, minutes: number): Date {\n return subtractSeconds(date, minutes * 60);\n}\n\n/**\n * Adds hours to a Date object\n * @param date - The base Date object\n * @param hours - Number of hours to add\n * @returns New Date object with added hours\n */\nexport function addHours(date: Date, hours: number): Date {\n return addMinutes(date, hours * 60);\n}\n\n/**\n * Subtracts hours from a Date object\n * @param date - The base Date object\n * @param hours - Number of hours to subtract\n * @returns New Date object with subtracted hours\n */\nexport function subtractHours(date: Date, hours: number): Date {\n return subtractMinutes(date, hours * 60);\n}\n\n/**\n * Adds days to a Date object\n * @param date - The base Date object\n * @param days - Number of days to add\n * @returns New Date object with added days\n */\nexport function addDays(date: Date, days: number): Date {\n return addHours(date, days * 24);\n}\n\n/**\n * Subtracts days from a Date object\n * @param date - The base Date object\n * @param days - Number of days to subtract\n * @returns New Date object with subtracted days\n */\nexport function subtractDays(date: Date, days: number): Date {\n return subtractHours(date, days * 24);\n}\n\n/**\n * Gets the current Date object\n * @returns Current Date object\n */\nexport function now(): Date {\n return new Date();\n}\n\n/**\n * Converts a Date | string union to a Date object\n * This is a utility to help with migration from Date | string patterns\n * @param dateOrString - Date object or ISO string\n * @returns Date object\n */\nexport function normalizeToDate(dateOrString: Date | string): Date {\n if (dateOrString instanceof Date) {\n return dateOrString;\n }\n return isoStringToDate(dateOrString);\n}\n\n/**\n * Calculates the difference between two dates in seconds\n * @param laterDate - The later date\n * @param earlierDate - The earlier date\n * @returns Difference in seconds (positive if laterDate is after earlierDate)\n */\nexport function diffInSeconds(laterDate: Date, earlierDate: Date): number {\n return Math.floor((laterDate.getTime() - earlierDate.getTime()) / 1000);\n}\n\n/**\n * Calculates the difference between two dates in minutes\n * @param laterDate - The later date\n * @param earlierDate - The earlier date\n * @returns Difference in minutes (positive if laterDate is after earlierDate)\n */\nexport function diffInMinutes(laterDate: Date, earlierDate: Date): number {\n return Math.floor(diffInSeconds(laterDate, earlierDate) / 60);\n}\n\n/**\n * Calculates the difference between two dates in hours\n * @param laterDate - The later date\n * @param earlierDate - The earlier date\n * @returns Difference in hours (positive if laterDate is after earlierDate)\n */\nexport function diffInHours(laterDate: Date, earlierDate: Date): number {\n return Math.floor(diffInMinutes(laterDate, earlierDate) / 60);\n}\n\n/**\n * Calculates the difference between two dates in days\n * @param laterDate - The later date\n * @param earlierDate - The earlier date\n * @returns Difference in days (positive if laterDate is after earlierDate)\n */\nexport function diffInDays(laterDate: Date, earlierDate: Date): number {\n return Math.floor(diffInHours(laterDate, earlierDate) / 24);\n}\n\n/**\n * Checks if two dates represent the same day (ignoring time)\n * Uses UTC to avoid timezone issues\n * @param date1 - First date\n * @param date2 - Second date\n * @returns true if both dates represent the same calendar day in UTC\n */\nexport function isSameDay(date1: Date, date2: Date): boolean {\n return (\n date1.getUTCFullYear() === date2.getUTCFullYear() &&\n date1.getUTCMonth() === date2.getUTCMonth() &&\n date1.getUTCDate() === date2.getUTCDate()\n );\n}\n\n/**\n * Formats a date as YYYY-MM-DD\n * @param date - The date to format\n * @returns Date string in YYYY-MM-DD format\n */\nexport function formatDateISO(date: Date): string {\n const parts = date.toISOString().split(\"T\");\n return parts[0] ?? \"\";\n}\n\n/**\n * Formats a date as HH:MM:SS\n * @param date - The date to format\n * @returns Time string in HH:MM:SS format\n */\nexport function formatTimeISO(date: Date): string {\n const parts = date.toISOString().split(\"T\");\n const timePart = parts[1];\n const timeOnly = timePart.split(\".\")[0];\n return timeOnly;\n}\n"]}
package/dist/index.js ADDED
@@ -0,0 +1,347 @@
1
+ // src/result.ts
2
+ function ok(value) {
3
+ return { success: true, value };
4
+ }
5
+ function err(error) {
6
+ return { success: false, error };
7
+ }
8
+ function isSuccess(result) {
9
+ return result.success;
10
+ }
11
+ function isFailure(result) {
12
+ return !result.success;
13
+ }
14
+ async function fromPromise(promise) {
15
+ try {
16
+ const value = await promise;
17
+ return ok(value);
18
+ } catch (error) {
19
+ return err(error instanceof Error ? error : new Error(String(error)));
20
+ }
21
+ }
22
+ function map(result, fn) {
23
+ if (result.success) {
24
+ return ok(fn(result.value));
25
+ }
26
+ return result;
27
+ }
28
+ function mapErr(result, fn) {
29
+ if (!result.success) {
30
+ return err(fn(result.error));
31
+ }
32
+ return ok(result.value);
33
+ }
34
+ function andThen(result, fn) {
35
+ if (result.success) {
36
+ return fn(result.value);
37
+ }
38
+ return result;
39
+ }
40
+ function orElse(result, fn) {
41
+ if (!result.success) {
42
+ return fn(result.error);
43
+ }
44
+ return result;
45
+ }
46
+ function unwrap(result) {
47
+ if (result.success) {
48
+ return result.value;
49
+ }
50
+ if (result.error instanceof Error) {
51
+ throw result.error;
52
+ }
53
+ throw new Error(String(result.error));
54
+ }
55
+ function unwrapOr(result, defaultValue) {
56
+ if (result.success) {
57
+ return result.value;
58
+ }
59
+ return defaultValue;
60
+ }
61
+ function unwrapOrElse(result, fn) {
62
+ if (result.success) {
63
+ return result.value;
64
+ }
65
+ return fn(result.error);
66
+ }
67
+ function combine(results) {
68
+ const values = [];
69
+ for (const result of results) {
70
+ if (!result.success) {
71
+ return result;
72
+ }
73
+ values.push(result.value);
74
+ }
75
+ return ok(values);
76
+ }
77
+
78
+ // src/datetime.ts
79
+ function dateToUnixTimestamp(date) {
80
+ return Math.floor(date.getTime() / 1e3);
81
+ }
82
+ function unixTimestampToDate(timestamp) {
83
+ return new Date(timestamp * 1e3);
84
+ }
85
+ function dateToISOString(date) {
86
+ return date.toISOString();
87
+ }
88
+ function isoStringToDate(isoString) {
89
+ return new Date(isoString);
90
+ }
91
+ function isDateInPast(date) {
92
+ return date.getTime() < Date.now();
93
+ }
94
+ function isDateInFuture(date) {
95
+ return date.getTime() > Date.now();
96
+ }
97
+ function addSeconds(date, seconds) {
98
+ return new Date(date.getTime() + seconds * 1e3);
99
+ }
100
+ function subtractSeconds(date, seconds) {
101
+ return new Date(date.getTime() - seconds * 1e3);
102
+ }
103
+ function addMinutes(date, minutes) {
104
+ return addSeconds(date, minutes * 60);
105
+ }
106
+ function subtractMinutes(date, minutes) {
107
+ return subtractSeconds(date, minutes * 60);
108
+ }
109
+ function addHours(date, hours) {
110
+ return addMinutes(date, hours * 60);
111
+ }
112
+ function subtractHours(date, hours) {
113
+ return subtractMinutes(date, hours * 60);
114
+ }
115
+ function addDays(date, days) {
116
+ return addHours(date, days * 24);
117
+ }
118
+ function subtractDays(date, days) {
119
+ return subtractHours(date, days * 24);
120
+ }
121
+ function now() {
122
+ return /* @__PURE__ */ new Date();
123
+ }
124
+ function normalizeToDate(dateOrString) {
125
+ if (dateOrString instanceof Date) {
126
+ return dateOrString;
127
+ }
128
+ return isoStringToDate(dateOrString);
129
+ }
130
+ function diffInSeconds(laterDate, earlierDate) {
131
+ return Math.floor((laterDate.getTime() - earlierDate.getTime()) / 1e3);
132
+ }
133
+ function diffInMinutes(laterDate, earlierDate) {
134
+ return Math.floor(diffInSeconds(laterDate, earlierDate) / 60);
135
+ }
136
+ function diffInHours(laterDate, earlierDate) {
137
+ return Math.floor(diffInMinutes(laterDate, earlierDate) / 60);
138
+ }
139
+ function diffInDays(laterDate, earlierDate) {
140
+ return Math.floor(diffInHours(laterDate, earlierDate) / 24);
141
+ }
142
+ function isSameDay(date1, date2) {
143
+ return date1.getUTCFullYear() === date2.getUTCFullYear() && date1.getUTCMonth() === date2.getUTCMonth() && date1.getUTCDate() === date2.getUTCDate();
144
+ }
145
+ function formatDateISO(date) {
146
+ const parts = date.toISOString().split("T");
147
+ return parts[0] ?? "";
148
+ }
149
+ function formatTimeISO(date) {
150
+ const parts = date.toISOString().split("T");
151
+ const timePart = parts[1];
152
+ const timeOnly = timePart.split(".")[0];
153
+ return timeOnly;
154
+ }
155
+
156
+ // src/object.ts
157
+ function includeIf(key, value) {
158
+ return value !== void 0 ? { [key]: value } : {};
159
+ }
160
+ function includeIfDefined(obj) {
161
+ const result = {};
162
+ for (const [key, value] of Object.entries(obj)) {
163
+ if (value !== void 0) {
164
+ result[key] = value;
165
+ }
166
+ }
167
+ return result;
168
+ }
169
+ function omitUndefined(obj) {
170
+ return includeIfDefined(obj);
171
+ }
172
+ function conditionalProps(conditions) {
173
+ const result = {};
174
+ for (const [condition, props] of Object.entries(conditions)) {
175
+ if (condition === "true") {
176
+ Object.assign(result, props);
177
+ }
178
+ }
179
+ return result;
180
+ }
181
+ function pick(obj, keys) {
182
+ const result = {};
183
+ for (const key of keys) {
184
+ if (key in obj) {
185
+ result[key] = obj[key];
186
+ }
187
+ }
188
+ return result;
189
+ }
190
+ function omit(obj, keys) {
191
+ const result = {};
192
+ const keysSet = new Set(keys);
193
+ for (const [key, value] of Object.entries(obj)) {
194
+ if (!keysSet.has(key)) {
195
+ result[key] = value;
196
+ }
197
+ }
198
+ return result;
199
+ }
200
+
201
+ // src/string.ts
202
+ function isEmptyString(value) {
203
+ return value == null || value.trim().length === 0;
204
+ }
205
+ function isNonEmptyString(value) {
206
+ return value != null && value.trim().length > 0;
207
+ }
208
+ function truncateString(str, maxLength) {
209
+ if (str.length <= maxLength) {
210
+ return str;
211
+ }
212
+ return `${str.slice(0, maxLength - 3)}...`;
213
+ }
214
+ function capitalize(str) {
215
+ if (str.length === 0) {
216
+ return str;
217
+ }
218
+ return str.charAt(0).toUpperCase() + str.slice(1);
219
+ }
220
+ function encodeBase64Url(data) {
221
+ const base64 = Buffer.from(data).toString("base64");
222
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
223
+ }
224
+ function decodeBase64Url(encoded) {
225
+ const base64 = encoded.replace(/-/g, "+").replace(/_/g, "/");
226
+ const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
227
+ return Buffer.from(padded, "base64");
228
+ }
229
+ function isValidBase64Url(str) {
230
+ const base64UrlRegex = /^[A-Za-z0-9_-]*$/;
231
+ return base64UrlRegex.test(str);
232
+ }
233
+ function getErrorMessage(error) {
234
+ if (error instanceof Error) {
235
+ return error.message;
236
+ }
237
+ if (typeof error === "string") {
238
+ return error;
239
+ }
240
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
241
+ return error.message;
242
+ }
243
+ return String(error);
244
+ }
245
+
246
+ // src/retry.ts
247
+ var DEFAULT_RETRY_OPTIONS = {
248
+ maxAttempts: 3,
249
+ initialDelay: 1e3,
250
+ maxDelay: 3e4,
251
+ backoffMultiplier: 2,
252
+ jitterFactor: 0.1,
253
+ shouldRetry: () => true
254
+ };
255
+ function calculateDelay(attempt, options) {
256
+ const exponentialDelay = options.initialDelay * Math.pow(options.backoffMultiplier, attempt - 1);
257
+ const cappedDelay = Math.min(exponentialDelay, options.maxDelay);
258
+ const jitter = cappedDelay * options.jitterFactor * Math.random();
259
+ return Math.round(cappedDelay + jitter);
260
+ }
261
+ async function sleep(ms) {
262
+ await new Promise((resolve) => setTimeout(resolve, ms));
263
+ }
264
+ async function retry(operation, options = {}) {
265
+ const config = { ...DEFAULT_RETRY_OPTIONS, ...options };
266
+ let lastError;
267
+ let actualAttempts = 0;
268
+ for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
269
+ actualAttempts = attempt;
270
+ try {
271
+ const result = await operation();
272
+ return {
273
+ success: true,
274
+ value: result,
275
+ attempts: attempt
276
+ };
277
+ } catch (error) {
278
+ lastError = error;
279
+ if (config.onError !== void 0) {
280
+ await config.onError(error, attempt);
281
+ }
282
+ if (attempt >= config.maxAttempts || !config.shouldRetry(error, attempt)) {
283
+ break;
284
+ }
285
+ const delay = config.getDelay !== void 0 ? config.getDelay(attempt, error) : calculateDelay(attempt, config);
286
+ await sleep(delay);
287
+ }
288
+ }
289
+ return {
290
+ success: false,
291
+ error: lastError,
292
+ attempts: actualAttempts
293
+ };
294
+ }
295
+ async function retryWithPolling(operation, maxAttempts, interval, shouldRetry = () => true) {
296
+ return await retry(operation, {
297
+ maxAttempts,
298
+ initialDelay: interval,
299
+ maxDelay: interval,
300
+ backoffMultiplier: 1,
301
+ // No backoff
302
+ jitterFactor: 0,
303
+ // No jitter for consistent polling
304
+ shouldRetry
305
+ });
306
+ }
307
+ async function retryWithBackoff(operation, maxRetries = 3, baseDelay = 1e3) {
308
+ const result = await retry(operation, {
309
+ maxAttempts: maxRetries + 1,
310
+ // Convert retries to attempts
311
+ initialDelay: baseDelay
312
+ });
313
+ if (result.success) {
314
+ return result.value;
315
+ }
316
+ throw result.error;
317
+ }
318
+
319
+ // src/async.ts
320
+ async function pipeAsync(initial, fns) {
321
+ return await fns.reduce(
322
+ async (promise, fn) => await promise.then(fn),
323
+ Promise.resolve(initial)
324
+ );
325
+ }
326
+ async function runSequentially(fns) {
327
+ const results = [];
328
+ await fns.reduce(
329
+ async (promise, fn) => {
330
+ await promise.then(async () => {
331
+ const result = await fn();
332
+ results.push(result);
333
+ });
334
+ },
335
+ Promise.resolve()
336
+ );
337
+ return results;
338
+ }
339
+
340
+ // src/type-guards.ts
341
+ function isPresent(value) {
342
+ return value !== null && value !== void 0;
343
+ }
344
+
345
+ export { addDays, addHours, addMinutes, addSeconds, andThen, capitalize, combine, conditionalProps, dateToISOString, dateToUnixTimestamp, decodeBase64Url, diffInDays, diffInHours, diffInMinutes, diffInSeconds, encodeBase64Url, err, formatDateISO, formatTimeISO, fromPromise, getErrorMessage, includeIf, includeIfDefined, isDateInFuture, isDateInPast, isEmptyString, isFailure, isNonEmptyString, isPresent, isSameDay, isSuccess, isValidBase64Url, isoStringToDate, map, mapErr, normalizeToDate, now, ok, omit, omitUndefined, orElse, pick, pipeAsync, retry, retryWithBackoff, retryWithPolling, runSequentially, sleep, subtractDays, subtractHours, subtractMinutes, subtractSeconds, truncateString, unixTimestampToDate, unwrap, unwrapOr, unwrapOrElse };
346
+ //# sourceMappingURL=index.js.map
347
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/result.ts","../src/datetime.ts","../src/object.ts","../src/string.ts","../src/retry.ts","../src/async.ts","../src/type-guards.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Represents a successful Result with a value.\n * Can be used for type narrowing in tests and assertions.\n */\nexport interface Success<T> {\n success: true;\n value: T;\n error?: undefined;\n}\n\n/**\n * Represents a failed Result with an error.\n * Can be used for type narrowing in tests and assertions.\n */\nexport interface Failure<E> {\n success: false;\n error: E;\n value?: undefined;\n}\n\n/**\n * A simplified Result type inspired by Rust's Result.\n *\n * This type represents either a successful value (ok) or an error (err).\n * It is used for consistent error handling across the `@reasonabletech` platform.\n */\nexport type Result<T, E = Error> = Success<T> | Failure<E>;\n\n/**\n * Creates a successful Result.\n * @param value - Optional value to wrap in a successful Result\n * @returns A successful Result containing the value\n */\nexport function ok<T, E = Error>(value: T): Result<T, E>;\nexport function ok<E = Error>(): Result<void, E>;\nexport function ok<T, E = Error>(value?: T): Result<T | void, E> {\n return { success: true, value: value as T | void };\n}\n\n/**\n * Creates an error Result.\n * @param error The error to wrap in an error Result\n * @returns An error Result containing the error\n */\nexport function err<T = never, E = Error>(error: E): Result<T, E> {\n return { success: false, error };\n}\n\n/**\n * Type guard to check if a Result is successful.\n * @param result The Result to check\n * @returns True if the Result is successful\n */\nexport function isSuccess<T, E = Error>(\n result: Readonly<Result<T, E>>,\n): result is Success<T> {\n return result.success;\n}\n\n/**\n * Type guard to check if a Result is an error.\n * @param result The Result to check\n * @returns True if the Result is an error\n */\nexport function isFailure<T, E = Error>(\n result: Readonly<Result<T, E>>,\n): result is Failure<E> {\n return !result.success;\n}\n\n/**\n * Wraps a Promise to return a Result.\n * @param promise The Promise to wrap\n * @returns A Promise that resolves to a Result\n */\nexport async function fromPromise<T>(\n promise: Promise<T>,\n): Promise<Result<T, Error>> {\n try {\n const value = await promise;\n return ok(value);\n } catch (error) {\n return err(error instanceof Error ? error : new Error(String(error)));\n }\n}\n\n/**\n * Maps a successful Result to a new Result with a transformed value.\n * @param result The Result to map\n * @param fn The function to apply to the value\n * @returns A new Result with the transformed value or the original error\n */\nexport function map<T, U, E = Error>(\n result: Readonly<Result<T, E>>,\n fn: (value: T) => U,\n): Result<U, E> {\n if (result.success) {\n return ok(fn(result.value));\n }\n return result as Result<U, E>;\n}\n\n/**\n * Maps an error Result to a new Result with a transformed error.\n * @param result The Result to map\n * @param fn The function to apply to the error\n * @returns A new Result with the transformed error or the original value\n */\nexport function mapErr<T, E = Error, F = Error>(\n result: Readonly<Result<T, E>>,\n fn: (error: E) => F,\n): Result<T, F> {\n if (!result.success) {\n return err(fn(result.error));\n }\n return ok(result.value);\n}\n\n/**\n * Chains a function that returns a Result after a successful Result.\n * @param result The Result to chain\n * @param fn The function to apply to the value that returns a Result\n * @returns The Result returned by the function or the original error\n */\nexport function andThen<T, U, E = Error>(\n result: Readonly<Result<T, E>>,\n fn: (value: T) => Result<U, E>,\n): Result<U, E> {\n if (result.success) {\n return fn(result.value);\n }\n return result as Result<U, E>;\n}\n\n/**\n * Applies a fallback function to an error Result.\n * @param result The Result to check\n * @param fn The function to apply to the error that returns a Result\n * @returns The original Result if successful, or the Result returned by the function\n */\nexport function orElse<T, E = Error, F = Error>(\n result: Readonly<Result<T, E>>,\n fn: (error: E) => Result<T, F>,\n): Result<T, F> {\n if (!result.success) {\n return fn(result.error);\n }\n return result as unknown as Result<T, F>;\n}\n\n/**\n * Unwraps a Result, returning the value or throwing the error.\n * @param result The Result to unwrap\n * @returns The value if the Result is successful\n * @throws {Error} The error if the Result is an error\n */\nexport function unwrap<T, E = Error>(result: Readonly<Result<T, E>>): T {\n if (result.success) {\n return result.value;\n }\n if (result.error instanceof Error) {\n throw result.error;\n }\n throw new Error(String(result.error));\n}\n\n/**\n * Unwraps a Result, returning the value or a default value.\n * @param result The Result to unwrap\n * @param defaultValue The default value to return if the Result is an error\n * @returns The value if the Result is successful, or the default value\n */\nexport function unwrapOr<T, E = Error>(\n result: Readonly<Result<T, E>>,\n defaultValue: T,\n): T {\n if (result.success) {\n return result.value;\n }\n return defaultValue;\n}\n\n/**\n * Unwraps a Result, returning the value or computing a default value from the error.\n * @param result The Result to unwrap\n * @param fn The function to compute the default value from the error\n * @returns The value if the Result is successful, or the computed default value\n */\nexport function unwrapOrElse<T, E = Error>(\n result: Readonly<Result<T, E>>,\n fn: (error: E) => T,\n): T {\n if (result.success) {\n return result.value;\n }\n return fn(result.error);\n}\n\n/**\n * Combines an array of Results into a single Result containing an array of values.\n * If any Result is an error, returns the first error.\n * @param results Array of Results to combine\n * @returns A Result containing an array of values or the first error\n */\nexport function combine<T, E = Error>(\n results: ReadonlyArray<Result<T, E>>,\n): Result<T[], E> {\n const values: T[] = [];\n for (const result of results) {\n if (!result.success) {\n return result as Result<T[], E>;\n }\n values.push(result.value);\n }\n return ok(values);\n}\n","/**\n * Date/Time Utility Functions\n *\n * This module provides standardized date/time handling utilities for the `@reasonabletech` platform.\n * It ensures consistent use of Date objects internally while providing standard conversions\n * when required by specifications or APIs.\n *\n * Key principles:\n * - Use Date objects for all internal date/time representations\n * - Only convert to strings/numbers when required by external specs/APIs\n * - Provide clear, descriptive function names\n * - Handle edge cases gracefully\n */\n\n/**\n * Converts a Date object to Unix timestamp (seconds since epoch)\n * @param date - The Date object to convert\n * @returns Unix timestamp in seconds\n */\nexport function dateToUnixTimestamp(date: Date): number {\n return Math.floor(date.getTime() / 1000);\n}\n\n/**\n * Converts a Unix timestamp (seconds since epoch) to a Date object\n * @param timestamp - Unix timestamp in seconds\n * @returns Date object\n */\nexport function unixTimestampToDate(timestamp: number): Date {\n return new Date(timestamp * 1000);\n}\n\n/**\n * Converts a Date object to ISO string\n * @param date - The Date object to convert\n * @returns ISO string representation\n */\nexport function dateToISOString(date: Date): string {\n return date.toISOString();\n}\n\n/**\n * Converts an ISO string to a Date object\n * @param isoString - ISO string representation\n * @returns Date object\n */\nexport function isoStringToDate(isoString: string): Date {\n return new Date(isoString);\n}\n\n/**\n * Checks if a Date object represents a time in the past\n * @param date - The Date object to check\n * @returns true if the date is in the past\n */\nexport function isDateInPast(date: Date): boolean {\n return date.getTime() < Date.now();\n}\n\n/**\n * Checks if a Date object represents a time in the future\n * @param date - The Date object to check\n * @returns true if the date is in the future\n */\nexport function isDateInFuture(date: Date): boolean {\n return date.getTime() > Date.now();\n}\n\n/**\n * Adds seconds to a Date object\n * @param date - The base Date object\n * @param seconds - Number of seconds to add\n * @returns New Date object with added seconds\n */\nexport function addSeconds(date: Date, seconds: number): Date {\n return new Date(date.getTime() + (seconds * 1000));\n}\n\n/**\n * Subtracts seconds from a Date object\n * @param date - The base Date object\n * @param seconds - Number of seconds to subtract\n * @returns New Date object with subtracted seconds\n */\nexport function subtractSeconds(date: Date, seconds: number): Date {\n return new Date(date.getTime() - (seconds * 1000));\n}\n\n/**\n * Adds minutes to a Date object\n * @param date - The base Date object\n * @param minutes - Number of minutes to add\n * @returns New Date object with added minutes\n */\nexport function addMinutes(date: Date, minutes: number): Date {\n return addSeconds(date, minutes * 60);\n}\n\n/**\n * Subtracts minutes from a Date object\n * @param date - The base Date object\n * @param minutes - Number of minutes to subtract\n * @returns New Date object with subtracted minutes\n */\nexport function subtractMinutes(date: Date, minutes: number): Date {\n return subtractSeconds(date, minutes * 60);\n}\n\n/**\n * Adds hours to a Date object\n * @param date - The base Date object\n * @param hours - Number of hours to add\n * @returns New Date object with added hours\n */\nexport function addHours(date: Date, hours: number): Date {\n return addMinutes(date, hours * 60);\n}\n\n/**\n * Subtracts hours from a Date object\n * @param date - The base Date object\n * @param hours - Number of hours to subtract\n * @returns New Date object with subtracted hours\n */\nexport function subtractHours(date: Date, hours: number): Date {\n return subtractMinutes(date, hours * 60);\n}\n\n/**\n * Adds days to a Date object\n * @param date - The base Date object\n * @param days - Number of days to add\n * @returns New Date object with added days\n */\nexport function addDays(date: Date, days: number): Date {\n return addHours(date, days * 24);\n}\n\n/**\n * Subtracts days from a Date object\n * @param date - The base Date object\n * @param days - Number of days to subtract\n * @returns New Date object with subtracted days\n */\nexport function subtractDays(date: Date, days: number): Date {\n return subtractHours(date, days * 24);\n}\n\n/**\n * Gets the current Date object\n * @returns Current Date object\n */\nexport function now(): Date {\n return new Date();\n}\n\n/**\n * Converts a Date | string union to a Date object\n * This is a utility to help with migration from Date | string patterns\n * @param dateOrString - Date object or ISO string\n * @returns Date object\n */\nexport function normalizeToDate(dateOrString: Date | string): Date {\n if (dateOrString instanceof Date) {\n return dateOrString;\n }\n return isoStringToDate(dateOrString);\n}\n\n/**\n * Calculates the difference between two dates in seconds\n * @param laterDate - The later date\n * @param earlierDate - The earlier date\n * @returns Difference in seconds (positive if laterDate is after earlierDate)\n */\nexport function diffInSeconds(laterDate: Date, earlierDate: Date): number {\n return Math.floor((laterDate.getTime() - earlierDate.getTime()) / 1000);\n}\n\n/**\n * Calculates the difference between two dates in minutes\n * @param laterDate - The later date\n * @param earlierDate - The earlier date\n * @returns Difference in minutes (positive if laterDate is after earlierDate)\n */\nexport function diffInMinutes(laterDate: Date, earlierDate: Date): number {\n return Math.floor(diffInSeconds(laterDate, earlierDate) / 60);\n}\n\n/**\n * Calculates the difference between two dates in hours\n * @param laterDate - The later date\n * @param earlierDate - The earlier date\n * @returns Difference in hours (positive if laterDate is after earlierDate)\n */\nexport function diffInHours(laterDate: Date, earlierDate: Date): number {\n return Math.floor(diffInMinutes(laterDate, earlierDate) / 60);\n}\n\n/**\n * Calculates the difference between two dates in days\n * @param laterDate - The later date\n * @param earlierDate - The earlier date\n * @returns Difference in days (positive if laterDate is after earlierDate)\n */\nexport function diffInDays(laterDate: Date, earlierDate: Date): number {\n return Math.floor(diffInHours(laterDate, earlierDate) / 24);\n}\n\n/**\n * Checks if two dates represent the same day (ignoring time)\n * Uses UTC to avoid timezone issues\n * @param date1 - First date\n * @param date2 - Second date\n * @returns true if both dates represent the same calendar day in UTC\n */\nexport function isSameDay(date1: Date, date2: Date): boolean {\n return (\n date1.getUTCFullYear() === date2.getUTCFullYear() &&\n date1.getUTCMonth() === date2.getUTCMonth() &&\n date1.getUTCDate() === date2.getUTCDate()\n );\n}\n\n/**\n * Formats a date as YYYY-MM-DD\n * @param date - The date to format\n * @returns Date string in YYYY-MM-DD format\n */\nexport function formatDateISO(date: Date): string {\n const parts = date.toISOString().split(\"T\");\n return parts[0] ?? \"\";\n}\n\n/**\n * Formats a date as HH:MM:SS\n * @param date - The date to format\n * @returns Time string in HH:MM:SS format\n */\nexport function formatTimeISO(date: Date): string {\n const parts = date.toISOString().split(\"T\");\n const timePart = parts[1];\n const timeOnly = timePart.split(\".\")[0];\n return timeOnly;\n}\n","/**\n * Object utility functions for handling exactOptionalPropertyTypes and clean object construction\n *\n * These utilities are designed to work with TypeScript's strict mode and exactOptionalPropertyTypes\n * configuration, which prevents assigning undefined to optional properties. They provide clean,\n * type-safe ways to construct objects with conditional properties.\n *\n * ## Key Use Cases\n *\n * 1. **exactOptionalPropertyTypes compliance** - When you cannot assign undefined to optional properties\n * 2. **Clean object construction** - Building objects with conditional properties based on runtime values\n * 3. **API response formatting** - Creating response objects that omit undefined fields\n * 4. **Configuration objects** - Building config objects where some properties may not be defined\n *\n * ## Examples\n *\n * ### Basic Conditional Properties\n * ```typescript\n * // Instead of this (fails with exactOptionalPropertyTypes):\n * const badObj = {\n * name: \"test\",\n * description: undefined, // ❌ Error: undefined not assignable to optional property\n * };\n *\n * // Use this:\n * const goodObj = {\n * name: \"test\",\n * ...includeIf(\"description\", maybeDescription),\n * };\n * ```\n *\n * ### Multiple Conditional Properties\n * ```typescript\n * const config = {\n * host: \"localhost\",\n * port: 3000,\n * ...includeIfDefined({\n * ssl: enableSsl ? { cert: certPath, key: keyPath } : undefined,\n * auth: authConfig, // may be undefined\n * timeout: customTimeout, // may be undefined\n * }),\n * };\n * ```\n *\n * ### API Response Building\n * ```typescript\n * function buildApiResponse(user: User) {\n * return {\n * id: user.id,\n * name: user.name,\n * ...includeIf(\"email\", user.email), // only include if user has email\n * ...includeIf(\"avatar\", user.avatarUrl), // only include if user has avatar\n * ...includeIf(\"lastSeen\", user.lastSeenAt ? lastSeenAt.toISOString()), // convert and include if exists\n * };\n * }\n * ```\n * @module object\n * @since 1.0.0\n */\n\n/**\n * Conditionally includes a property in an object if the value is not undefined.\n * This is useful for exactOptionalPropertyTypes compliance where you cannot assign undefined to optional properties.\n * @param key - The property key to conditionally include\n * @param value - The value to include, or undefined to omit the property\n * @returns An empty object if value is undefined, otherwise an object with the key-value pair\n * @example Basic usage\n * ```typescript\n * const obj = {\n * required : \"value\",\n * ...includeIf(\"optional\", maybeUndefinedValue),\n * };\n * // If maybeUndefinedValue is \"hello\": { required: \"value\", optional: \"hello\" }\n * // If maybeUndefinedValue is undefined: { required: \"value\" }\n * ```\n * @example API response building\n * ```typescript\n * function createUserResponse(user: User) {\n * return {\n * id: user.id,\n * name: user.name,\n * ...includeIf(\"email\", user.email),\n * ...includeIf(\"avatar\", user.avatar ? avatar.url),\n * ...includeIf(\"lastLogin\", user.lastLoginAt?.toISOString()),\n * };\n * }\n * ```\n * @example Billing service usage (real project example)\n * ```typescript\n * return {\n * amount : pricing.amount,\n * ...includeIf(\"setupFee\", pricing.setupFee),\n * ...includeIf(\"savings\", calculatedSavings),\n * };\n * ```\n */\nexport function includeIf<K extends string, V>(\n key: K,\n value: V | undefined,\n): Record<string, unknown> {\n return value !== undefined ? { [key]: value } : {};\n}\n\n/**\n * Conditionally includes multiple properties from an object, omitting any with undefined values.\n * This creates a new object with only the defined properties.\n * @param obj - Object containing key-value pairs where values may be undefined\n * @returns New object containing only the properties where values are not undefined\n * @example Basic usage\n * ```typescript\n * const obj = {\n * required: \"value\",\n * ...includeIfDefined({\n * optional1: maybeUndefined1, // included only if not undefined\n * optional2: maybeUndefined2, // included only if not undefined\n * }),\n * };\n * ```\n * @example Configuration object building\n * ```typescript\n * const config = {\n * host: \"localhost\",\n * port: 3000,\n * ...includeIfDefined({\n * ssl: sslEnabled ? sslEnabled.sslConfig : undefined,\n * auth: authConfig, // may be undefined\n * timeout: userTimeout, // may be undefined\n * retries: retryCount, // may be undefined\n * }),\n * };\n * ```\n * @example Form data processing\n * ```typescript\n * const formData = {\n * name: data.name,\n * email: data.email,\n * ...includeIfDefined({\n * phone: data.phone ? phone.trim() || undefined,\n * company : data.company?.trim() || undefined,\n * website: data.website ? website.startsWith('http') ? data.website : undefined,\n * }),\n * };\n * ```\n */\nexport function includeIfDefined<T extends Record<string, unknown>>(\n obj: T,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (value !== undefined) {\n result[key] = value;\n }\n }\n return result;\n}\n\n/**\n * Omits properties with undefined values from an object.\n * This is useful for cleaning up objects before assignment when using exactOptionalPropertyTypes.\n * Note: This only removes undefined values, not other falsy values like null, 0, \"\", or false.\n * @param obj - The object to clean of undefined properties\n * @returns New object with undefined properties removed\n * @example Basic cleanup\n * ```typescript\n * const cleanObj = omitUndefined({\n * a: \"defined\",\n * b: undefined, // ❌ removed\n * c: null, // ✅ preserved (null ≠ undefined)\n * d: 0, // ✅ preserved (falsy but defined)\n * e: \"\", // ✅ preserved (falsy but defined)\n * f: false, // ✅ preserved (falsy but defined)\n * });\n * // Result: { a: \"defined\", c: null, d: 0, e: \"\", f: false }\n * ```\n * @example API data cleaning\n * ```typescript\n * function sanitizeApiResponse(rawData: any) {\n * return omitUndefined({\n * id: rawData.id,\n * name: rawData.name,\n * description: rawData.description, // may be undefined\n * metadata: rawData.metadata, // may be undefined\n * createdAt: rawData.created_at,\n * });\n * }\n * ```\n * @example Before database save\n * ```typescript\n * const userUpdate = omitUndefined({\n * name: formData.name,\n * email: formData.email,\n * avatar: formData.avatar, // only update if provided\n * settings: formData.settings, // only update if provided\n * });\n * ```\n */\nexport function omitUndefined<T extends Record<string, unknown>>(\n obj: T,\n): Record<string, unknown> {\n return includeIfDefined(obj);\n}\n\n/**\n * Creates an object with conditional properties based on boolean conditions.\n * Each condition is checked and only truthy conditions include their corresponding properties.\n * @param conditions - Object where keys are boolean conditions (as strings) and values are objects to include\n * @returns Object containing properties from all truthy conditions\n * @example Basic conditional properties\n * ```typescript\n * const obj = {\n * always: \"included\",\n * ...conditionalProps({\n * [String(isEnabled)]: { feature: \"enabled\" },\n * [String(hasPermission)]: { admin: true },\n * }),\n * };\n * ```\n * @example Feature flags\n * ```typescript\n * const config = {\n * baseUrl: \"https://api.example.com\",\n * ...conditionalProps({\n * [String(enableLogging)]: { logging: { level: \"debug\" } },\n * [String(enableMetrics)]: { metrics: { endpoint: \"/metrics\" } },\n * [String(enableAuth)]: { auth: { provider: \"oauth\" } },\n * }),\n * };\n * ```\n * @example User permission-based UI\n * ```typescript\n * const uiConfig = {\n * showProfile: true,\n * ...conditionalProps({\n * [String(user.isAdmin)]: { showAdminPanel: true },\n * [String(user.isPremium)]: { showPremiumFeatures: true },\n * [String(user.canEdit)]: { showEditTools: true },\n * }),\n * };\n * ```\n */\nexport function conditionalProps(\n conditions: Record<string, Record<string, unknown>>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [condition, props] of Object.entries(conditions)) {\n if (condition === \"true\") {\n Object.assign(result, props);\n }\n }\n return result;\n}\n\n/**\n * Type-safe way to pick properties from an object, with undefined handling.\n * Unlike Lodash pick, this preserves exact types and handles undefined values correctly.\n * Only includes properties that exist in the source object.\n * @param obj - The source object to pick properties from\n * @param keys - Array of keys to pick from the object\n * @returns New object containing only the specified properties\n * @example Basic property picking\n * ```typescript\n * const user = { id: 1, name: \"John\", email: \"john@example.com\", password: \"secret\" };\n * const publicUser = pick(user, ['id', 'name', 'email']);\n * // Result: { id: 1, name: \"John\", email: \"john@example.com\" }\n * ```\n * @example API response filtering\n * ```typescript\n * function createPublicProfile(fullUser: FullUser) {\n * return pick(fullUser, [\n * 'id',\n * 'username',\n * 'displayName',\n * 'avatar',\n * 'joinedAt'\n * ]);\n * }\n * ```\n * @example Configuration subset\n * ```typescript\n * const fullConfig = { host: \"localhost\", port: 3000, ssl: true, debug: true, secret: \"xxx\" };\n * const clientConfig = pick(fullConfig, ['host', 'port', 'ssl']);\n * // Result: { host: \"localhost\", port: 3000, ssl: true }\n * ```\n */\nexport function pick<T extends Record<string, unknown>, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Pick<T, K> {\n const result = {} as Pick<T, K>;\n for (const key of keys) {\n if (key in obj) {\n result[key] = obj[key];\n }\n }\n return result;\n}\n\n/**\n * Type-safe way to omit properties from an object, with undefined handling.\n * Unlike Lodash omit, this preserves exact types and handles undefined values correctly.\n * Creates a new object excluding the specified properties.\n * @param obj - The source object to omit properties from\n * @param keys - Array of keys to omit from the object\n * @returns New object with the specified properties removed\n * @example Remove sensitive data\n * ```typescript\n * const user = { id: 1, name: \"John\", email: \"john@example.com\", password: \"secret\", ssn: \"xxx\" };\n * const safeUser = omit(user, ['password', 'ssn']);\n * // Result: { id: 1, name: \"John\", email: \"john@example.com\" }\n * ```\n * @example Remove internal properties\n * ```typescript\n * function toApiResponse(internalObj: InternalUser) {\n * return omit(internalObj, [\n * '_id',\n * '_version',\n * '_internal',\n * 'hashedPassword',\n * 'secretKey'\n * ]);\n * }\n * ```\n * @example Configuration sanitization\n * ```typescript\n * const fullConfig = {\n * host: \"localhost\",\n * port: 3000,\n * ssl: true,\n * debug: true,\n * apiSecret: \"xxx\",\n * dbPassword: \"yyy\"\n * };\n * const publicConfig = omit(fullConfig, ['apiSecret', 'dbPassword']);\n * // Result: { host: \"localhost\", port: 3000, ssl: true, debug: true }\n * ```\n */\nexport function omit<T extends Record<string, unknown>, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Omit<T, K> {\n const result = {} as Omit<T, K>;\n const keysSet = new Set(keys);\n for (const [key, value] of Object.entries(obj)) {\n if (!keysSet.has(key as K)) {\n (result as Record<string, unknown>)[key] = value;\n }\n }\n return result;\n}\n","/**\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","/**\n * Retry utility functions for the `@reasonabletech` platform\n */\n\n/**\n * Core retry configuration fields that have default values\n */\ninterface RetryConfigDefaults {\n /**\n * Maximum number of attempts (including the initial attempt)\n * @default 3\n */\n maxAttempts: number;\n /**\n * Initial delay in milliseconds before the first retry\n * @default 1000\n */\n initialDelay: number;\n /**\n * Maximum delay in milliseconds between retries\n * @default 30000\n */\n maxDelay: number;\n /**\n * Multiplier for exponential backoff\n * @default 2\n */\n backoffMultiplier: number;\n /**\n * Jitter factor (0-1) to add randomness to delays\n * @default 0.1\n */\n jitterFactor: number;\n /**\n * Function to determine if an error should trigger a retry\n * @default () => true (always retry)\n */\n shouldRetry: (error: unknown, attempt: number) => boolean;\n}\n\n/**\n * Optional callback hooks for retry operations\n */\ninterface RetryCallbacks {\n /**\n * Callback invoked when an attempt fails (before retry decision).\n * Use this for logging, running interceptors, or other side effects.\n */\n onError?: (error: unknown, attempt: number) => void | Promise<void>;\n\n /**\n * Custom delay calculator that overrides the default exponential backoff.\n * Useful when the error contains retry timing hints (e.g., Retry-After header).\n */\n getDelay?: (attempt: number, error: unknown) => number;\n}\n\n/**\n * Resolved configuration after applying defaults\n */\ntype ResolvedRetryConfig = RetryConfigDefaults & RetryCallbacks;\n\n/**\n * Configuration options for retry operations (all fields optional for user input)\n * @example\n * ```typescript\n * const result = await retry(() => fetchData(), {\n * maxAttempts: 5,\n * initialDelay: 500,\n * onError: (error, attempt) => console.log(`Attempt ${attempt} failed`),\n * });\n * ```\n */\nexport interface RetryOptions\n extends Partial<RetryConfigDefaults>,\n RetryCallbacks {}\n\n/**\n * Result of a retry operation\n */\nexport interface RetryResult<T> {\n /** Whether the operation succeeded */\n success: boolean;\n /** The result value if successful */\n value?: T;\n /** The final error if all attempts failed */\n error?: unknown;\n /** Number of attempts made */\n attempts: number;\n}\n\n/**\n * Default retry configuration\n */\nconst DEFAULT_RETRY_OPTIONS: RetryConfigDefaults = {\n maxAttempts: 3,\n initialDelay: 1000,\n maxDelay: 30000,\n backoffMultiplier: 2,\n jitterFactor: 0.1,\n shouldRetry: () => true,\n};\n\n/**\n * Calculate delay with exponential backoff and jitter\n * @param attempt - Current attempt number (1-based)\n * @param options - Retry configuration options\n * @returns Delay in milliseconds\n */\nfunction calculateDelay(attempt: number, options: RetryConfigDefaults): number {\n const exponentialDelay =\n options.initialDelay * Math.pow(options.backoffMultiplier, attempt - 1);\n const cappedDelay = Math.min(exponentialDelay, options.maxDelay);\n\n // Add jitter to prevent thundering herd\n const jitter = cappedDelay * options.jitterFactor * Math.random();\n return Math.round(cappedDelay + jitter);\n}\n\n/**\n * Sleep for the specified number of milliseconds\n * @param ms - Milliseconds to sleep\n * @returns Promise that resolves after the delay\n */\nexport async function sleep(ms: number): Promise<void> {\n await new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Retry an async operation with exponential backoff and jitter\n * @param operation - The async operation to retry\n * @param options - Retry configuration options\n * @returns Promise resolving to retry result\n * @example\n * ```typescript\n * // Basic retry with defaults (3 attempts, 1s initial delay)\n * const result = await retry(() => fetchData());\n *\n * // Custom configuration with error callback\n * const result = await retry(() => apiClient.post('/users', userData), {\n * maxAttempts: 5,\n * initialDelay: 500,\n * onError: (error, attempt) => {\n * logger.warn('API', `Attempt ${attempt} failed`, { error });\n * },\n * });\n *\n * // Use server-provided Retry-After hint\n * const result = await retry(() => rateLimitedApi.call(), {\n * getDelay: (attempt, error) => {\n * if (error instanceof ApiError && error.retryAfter) {\n * return error.retryAfter; // Use server-provided delay\n * }\n * return 1000 * Math.pow(2, attempt - 1); // Fallback to exponential\n * },\n * });\n * ```\n */\nexport async function retry<T>(\n operation: () => Promise<T>,\n options: RetryOptions = {},\n): Promise<RetryResult<T>> {\n const config: ResolvedRetryConfig = { ...DEFAULT_RETRY_OPTIONS, ...options };\n let lastError: unknown;\n let actualAttempts = 0;\n\n for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {\n actualAttempts = attempt;\n try {\n const result = await operation();\n return {\n success: true,\n value: result,\n attempts: attempt,\n };\n } catch (error) {\n lastError = error;\n\n // Invoke onError callback if provided\n if (config.onError !== undefined) {\n await config.onError(error, attempt);\n }\n\n // Don't retry if we've reached max attempts or if shouldRetry says no\n if (\n attempt >= config.maxAttempts ||\n !config.shouldRetry(error, attempt)\n ) {\n break;\n }\n\n // Calculate delay: use custom getDelay if provided, otherwise default calculation\n const delay =\n config.getDelay !== undefined\n ? config.getDelay(attempt, error)\n : calculateDelay(attempt, config);\n\n await sleep(delay);\n }\n }\n\n return {\n success: false,\n error: lastError,\n attempts: actualAttempts,\n };\n}\n\n/**\n * Retry an operation with polling (fixed interval, no exponential backoff)\n * @param operation - The async operation to retry\n * @param maxAttempts - Maximum number of attempts\n * @param interval - Fixed interval between attempts in milliseconds\n * @param shouldRetry - Function to determine if retry should continue\n * @returns Promise resolving to retry result\n */\nexport async function retryWithPolling<T>(\n operation: () => Promise<T>,\n maxAttempts: number,\n interval: number,\n shouldRetry: (error: unknown, attempt: number) => boolean = () => true,\n): Promise<RetryResult<T>> {\n return await retry(operation, {\n maxAttempts,\n initialDelay: interval,\n maxDelay: interval,\n backoffMultiplier: 1, // No backoff\n jitterFactor: 0, // No jitter for consistent polling\n shouldRetry,\n });\n}\n\n/**\n * Retry an operation with exponential backoff (simplified interface)\n * This function throws on failure instead of returning a result object.\n * @param operation - The async operation to retry\n * @param maxRetries - Maximum number of retries (attempts after the first try, default: 3)\n * @param baseDelay - Base delay in milliseconds (default: 1000)\n * @returns Promise resolving to the operation's result\n * @throws {unknown} The last error if all attempts fail\n * @example\n * ```typescript\n * // Retry up to 3 times with exponential backoff\n * const result = await retryWithBackoff(() => fetchData(), 3, 500);\n *\n * // Use default retries (3) and delay (1000ms)\n * const user = await retryWithBackoff(() => createUser(userData));\n * ```\n */\nexport async function retryWithBackoff<T>(\n operation: () => Promise<T>,\n maxRetries: number = 3,\n baseDelay: number = 1000,\n): Promise<T> {\n const result = await retry(operation, {\n maxAttempts: maxRetries + 1, // Convert retries to attempts\n initialDelay: baseDelay,\n });\n\n if (result.success) {\n return result.value as T;\n }\n\n // Throw the error to match the simpler interface\n throw result.error;\n}\n","/**\n * Async utility functions for the `@reasonabletech` platform\n */\n\n/**\n * Execute an array of async transform functions sequentially, piping the result\n * of each function to the next.\n *\n * This is useful for patterns like request/response interceptors where each\n * function must transform the result of the previous one in order.\n * @param initial - The initial value to pass to the first function\n * @param fns - Array of async transform functions to execute in order\n * @returns Promise resolving to the final transformed value\n * @example\n * ```typescript\n * // Request interceptor pipeline\n * const finalConfig = await pipeAsync(initialConfig, [\n * async (config) => ({ ...config, headers: { ...config.headers, 'X-Request-Id': id } }),\n * async (config) => ({ ...config, timestamp: Date.now() }),\n * ]);\n *\n * // Response interceptor pipeline\n * const finalResponse = await pipeAsync(response, responseInterceptors);\n * ```\n */\nexport async function pipeAsync<T>(\n initial: T,\n fns: ReadonlyArray<(value: T) => T | Promise<T>>,\n): Promise<T> {\n // Use reduce with promise chaining to avoid eslint no-await-in-loop\n return await fns.reduce<Promise<T>>(\n async (promise, fn) => await promise.then(fn),\n Promise.resolve(initial),\n );\n}\n\n/**\n * Execute an array of async functions sequentially, collecting all results.\n *\n * Unlike Promise.all which runs in parallel, this ensures each function\n * completes before the next one starts.\n * @param fns - Array of async functions to execute in order\n * @returns Promise resolving to array of results in the same order\n * @example\n * ```typescript\n * const results = await runSequentially([\n * () => fetchUser(1),\n * () => fetchUser(2),\n * () => fetchUser(3),\n * ]);\n * ```\n */\nexport async function runSequentially<T>(\n fns: ReadonlyArray<() => T | Promise<T>>,\n): Promise<T[]> {\n const results: T[] = [];\n\n // Use reduce with promise chaining to avoid eslint no-await-in-loop\n await fns.reduce<Promise<void>>(\n async (promise, fn) =>\n { await promise.then(async () => {\n const result = await fn();\n results.push(result);\n }); },\n Promise.resolve(),\n );\n\n return results;\n}\n","/**\n * Type guard utilities for common type checking patterns\n * @module type-guards\n */\n\n/**\n * Type guard to check if value is neither null nor undefined\n * @template T - The value type\n * @param value - The value to check\n * @returns True if value is present (not null or undefined), with type narrowing\n * @example\n * ```typescript\n * // Optional configuration value\n * const redirectUrl: string | null | undefined = config.redirectUrl;\n * if (isPresent(redirectUrl)) {\n * window.location.href = redirectUrl; // redirectUrl is string\n * }\n *\n * // Database lookup result\n * const user: User | null = await db.users.findById(id);\n * if (isPresent(user)) {\n * console.log(user.email); // user is User\n * }\n * ```\n */\nexport function isPresent<T>(value: T | null | undefined): value is T {\n return value !== null && value !== undefined;\n}\n"]}