utilful 2.5.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/path.mjs CHANGED
@@ -56,7 +56,7 @@ function joinURL(...paths) {
56
56
  continue;
57
57
  }
58
58
  if (path === "/") continue;
59
- const resultHasTrailing = result[result.length - 1] === "/";
59
+ const resultHasTrailing = result.at(-1) === "/";
60
60
  const pathHasLeading = path[0] === "/";
61
61
  if (resultHasTrailing && pathHasLeading) result += path.slice(1);
62
62
  else if (!resultHasTrailing && !pathHasLeading) result += `/${path}`;
@@ -70,7 +70,7 @@ function joinURL(...paths) {
70
70
  function withBase(input = "", base = "") {
71
71
  if (!base || base === "/") return input;
72
72
  const _base = withoutTrailingSlash(base);
73
- if (input.startsWith(_base) && (input.length === _base.length || input[_base.length] === "/" || input[_base.length] === "?")) return input;
73
+ if (input.startsWith(_base) && (input.length === _base.length || input[_base.length] === "/" || input[_base.length] === "?" || input[_base.length] === "#")) return input;
74
74
  return joinURL(_base, input);
75
75
  }
76
76
  /**
@@ -79,15 +79,20 @@ function withBase(input = "", base = "") {
79
79
  function withoutBase(input = "", base = "") {
80
80
  if (!base || base === "/") return input;
81
81
  const _base = withoutTrailingSlash(base);
82
- if (!input.startsWith(_base) || input.length !== _base.length && input[_base.length] !== "/" && input[_base.length] !== "?") return input;
82
+ if (!input.startsWith(_base) || input.length !== _base.length && input[_base.length] !== "/" && input[_base.length] !== "?" && input[_base.length] !== "#") return input;
83
83
  const trimmed = input.slice(_base.length);
84
84
  return trimmed[0] === "/" ? trimmed : `/${trimmed}`;
85
85
  }
86
+ const URL_SCHEME_RE = /^[a-z][\w+.-]*:\/\//i;
86
87
  /**
87
88
  * Returns the pathname of the given path, which is the path without the query string or hash.
89
+ *
90
+ * @remarks
91
+ * Absolute URLs (with a scheme, e.g. `https://example.com/foo`) return the URL's pathname.
92
+ * All other inputs are returned unchanged with the query string and hash removed.
88
93
  */
89
94
  function getPathname(path = "/") {
90
- if (!path.startsWith("/")) return new URL(path, "http://localhost").pathname;
95
+ if (URL_SCHEME_RE.test(path)) return new URL(path).pathname;
91
96
  let pathEnd = path.length;
92
97
  const queryIndex = path.indexOf("?");
93
98
  const hashIndex = path.indexOf("#");
package/dist/result.d.mts CHANGED
@@ -10,10 +10,10 @@ interface ErrData<E> {
10
10
  }
11
11
  type ResultData<T, E> = OkData<T> | ErrData<E>;
12
12
  /**
13
- * Successful result variant.
14
- * @template T Success value type.
15
- * @template E Error type (phantom - for type unification).
16
- */
13
+ * Successful result variant.
14
+ * @template T Success value type.
15
+ * @template E Error type (phantom - for type unification).
16
+ */
17
17
  declare class Ok<T, E = never> {
18
18
  readonly value: T;
19
19
  readonly ok = true;
@@ -35,10 +35,10 @@ declare class Ok<T, E = never> {
35
35
  }): R;
36
36
  }
37
37
  /**
38
- * Error result variant.
39
- * @template T Success type (phantom - for type unification).
40
- * @template E Error value type.
41
- */
38
+ * Error result variant.
39
+ * @template T Success type (phantom - for type unification).
40
+ * @template E Error value type.
41
+ */
42
42
  declare class Err<T, E> {
43
43
  readonly error: E;
44
44
  readonly ok = false;
@@ -64,6 +64,14 @@ declare function err<T = never, E extends string = string>(error: E): Err<T, E>;
64
64
  declare function err<T = never, E = unknown>(error: E): Err<T, E>;
65
65
  declare function isOk<T, E>(result: Result<T, E>): result is Ok<T, E>;
66
66
  declare function isErr<T, E>(result: Result<T, E>): result is Err<T, E>;
67
+ /**
68
+ * Wraps a function call or promise in a `Result`.
69
+ *
70
+ * @remarks
71
+ * The function overload must be synchronous. For asynchronous work, pass the
72
+ * promise itself (e.g. `toResult(fn())`) – a function returning a promise
73
+ * throws a `TypeError`, since its rejection could not be captured as `Err`.
74
+ */
67
75
  declare function toResult<T, E = unknown>(fn: () => T): Result<T, E>;
68
76
  declare function toResult<T, E = unknown>(promise: Promise<T>): Promise<Result<T, E>>;
69
77
  declare function unwrapResult<T, E>(result: Ok<T, E>): OkData<T>;
package/dist/result.mjs CHANGED
@@ -5,9 +5,8 @@
5
5
  * @template E Error type (phantom - for type unification).
6
6
  */
7
7
  var Ok = class Ok {
8
- value;
9
- ok = true;
10
8
  constructor(value) {
9
+ this.ok = true;
11
10
  this.value = value;
12
11
  }
13
12
  /** Transforms the success value. */
@@ -41,9 +40,8 @@ var Ok = class Ok {
41
40
  * @template E Error value type.
42
41
  */
43
42
  var Err = class Err {
44
- error;
45
- ok = false;
46
43
  constructor(error) {
44
+ this.ok = false;
47
45
  this.error = error;
48
46
  }
49
47
  /** No-op on Err, returns self with new value type. */
@@ -85,11 +83,14 @@ function isErr(result) {
85
83
  }
86
84
  function toResult(fnOrPromise) {
87
85
  if (fnOrPromise instanceof Promise) return fnOrPromise.then(ok).catch(err);
86
+ let value;
88
87
  try {
89
- return ok(fnOrPromise());
88
+ value = fnOrPromise();
90
89
  } catch (error) {
91
90
  return err(error);
92
91
  }
92
+ if (value instanceof Promise) throw new TypeError("[toResult] The function returned a promise. Pass the promise itself instead, e.g. toResult(fn())");
93
+ return ok(value);
93
94
  }
94
95
  function unwrapResult(result) {
95
96
  return result.ok ? {
package/dist/string.d.mts CHANGED
@@ -1,20 +1,24 @@
1
1
  //#region src/string.d.ts
2
2
  /**
3
- * Simple template engine to replace variables in a string.
4
- *
5
- * @example
6
- * const str = 'Hello, {name}!'
7
- * const variables = { name: 'world' }
8
- *
9
- * console.log(template(str, variables)) // Hello, world!
10
- */
3
+ * Simple template engine to replace variables in a string.
4
+ *
5
+ * @remarks
6
+ * Only own properties of `variables` are substituted, so placeholders like
7
+ * `{constructor}` cannot leak prototype members.
8
+ *
9
+ * @example
10
+ * const str = 'Hello, {name}!'
11
+ * const variables = { name: 'world' }
12
+ *
13
+ * console.log(template(str, variables)) // Hello, world!
14
+ */
11
15
  declare function template(str: string, variables: Record<string | number, any>, fallback?: string | ((key: string) => string)): string;
12
16
  /**
13
- * Generates a random string.
14
- *
15
- * @remarks Ported from `nanoid`.
16
- * @see https://github.com/ai/nanoid
17
- */
17
+ * Generates a random string.
18
+ *
19
+ * @remarks Ported from `nanoid`.
20
+ * @see https://github.com/ai/nanoid
21
+ */
18
22
  declare function generateRandomId(size?: number, dict?: string): string;
19
23
  //#endregion
20
24
  export { generateRandomId, template };
package/dist/string.mjs CHANGED
@@ -1,8 +1,13 @@
1
1
  //#region src/string.ts
2
2
  const URL_ALPHABET = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
3
+ const TEMPLATE_PLACEHOLDER_RE = /\{(\w+)\}/g;
3
4
  /**
4
5
  * Simple template engine to replace variables in a string.
5
6
  *
7
+ * @remarks
8
+ * Only own properties of `variables` are substituted, so placeholders like
9
+ * `{constructor}` cannot leak prototype members.
10
+ *
6
11
  * @example
7
12
  * const str = 'Hello, {name}!'
8
13
  * const variables = { name: 'world' }
@@ -10,8 +15,9 @@ const URL_ALPHABET = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvw
10
15
  * console.log(template(str, variables)) // Hello, world!
11
16
  */
12
17
  function template(str, variables, fallback) {
13
- return str.replace(/\{(\w+)\}/g, (_, key) => {
14
- return variables[key] != null ? String(variables[key]) : (typeof fallback === "function" ? fallback(key) : fallback) ?? key;
18
+ return str.replace(TEMPLATE_PLACEHOLDER_RE, (_, key) => {
19
+ const value = Object.hasOwn(variables, key) ? variables[key] : void 0;
20
+ return value != null ? String(value) : (typeof fallback === "function" ? fallback(key) : fallback) ?? key;
15
21
  });
16
22
  }
17
23
  /**
package/dist/types.d.mts CHANGED
@@ -2,7 +2,7 @@
2
2
  type AutocompletableString = string & {};
3
3
  type LooseAutocomplete<T extends string> = T | AutocompletableString;
4
4
  /** Also commonly referred to as `Prettify` */
5
- type UnifyIntersection<T> = { [K in keyof T]: T[K] } & {};
5
+ type UnifyIntersection<T> = { [K in keyof T]: T[K]; } & {};
6
6
  declare const brand: unique symbol;
7
7
  type BrandedType<T, B> = T & {
8
8
  [brand]: B;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "utilful",
3
3
  "type": "module",
4
- "version": "2.5.2",
5
- "packageManager": "pnpm@10.30.3",
4
+ "version": "3.0.0",
5
+ "packageManager": "pnpm@11.15.1",
6
6
  "description": "A collection of TypeScript utilities",
7
7
  "author": "Johann Schopplich <hello@johannschopplich.com>",
8
8
  "license": "MIT",
@@ -78,19 +78,13 @@
78
78
  "release": "bumpp"
79
79
  },
80
80
  "devDependencies": {
81
- "@antfu/eslint-config": "^7.6.1",
82
- "@types/node": "^25.3.5",
83
- "bumpp": "^10.4.1",
84
- "eslint": "^10.0.2",
85
- "eslint-flat-config-utils": "^3.0.1",
86
- "tsdown": "^0.21.0",
87
- "typescript": "^5.9.3",
88
- "vitest": "^4.0.18"
89
- },
90
- "pnpm": {
91
- "onlyBuiltDependencies": [
92
- "esbuild",
93
- "unrs-resolver"
94
- ]
81
+ "@antfu/eslint-config": "^9.1.0",
82
+ "@types/node": "^26.1.1",
83
+ "bumpp": "^12.0.0",
84
+ "eslint": "^10.7.0",
85
+ "eslint-flat-config-utils": "^3.2.0",
86
+ "tsdown": "^0.22.13",
87
+ "typescript": "^6.0.3",
88
+ "vitest": "^4.1.10"
95
89
  }
96
90
  }