@backendkit-labs/result 0.1.0 → 0.1.2

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.
@@ -1,53 +1,16 @@
1
+ import { __decorateClass, isRich, run, track } from '../chunk-XLOUAMAH.js';
1
2
  import { Injectable, Module } from '@nestjs/common';
2
3
  import { map } from 'rxjs/operators';
3
4
 
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __decorateClass = (decorators, target, key, kind) => {
6
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
7
- for (var i = decorators.length - 1, decorator; i >= 0; i--)
8
- if (decorator = decorators[i])
9
- result = (decorator(result)) || result;
10
- return result;
11
- };
12
-
13
- // src/result/constructors.ts
14
- var ok = (value) => ({ ok: true, value });
15
- var fail = (error) => ({ ok: false, error });
16
-
17
- // src/result/run.ts
18
- async function run(fn, errorTransform) {
19
- try {
20
- return ok(await fn());
21
- } catch (caught) {
22
- return fail(caught);
23
- }
24
- }
25
- async function track(fn, options) {
26
- const start = performance.now();
27
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
28
- const meta = {
29
- durationMs: 0,
30
- timestamp,
31
- operation: options?.operation,
32
- correlationId: options?.correlationId,
33
- tags: options?.tags
34
- };
35
- try {
36
- const value = await fn();
37
- return { ok: true, value, ...meta, durationMs: Math.round(performance.now() - start) };
38
- } catch (caught) {
39
- const error = options?.errorTransform ? options.errorTransform(caught) : caught;
40
- return { ok: false, error, ...meta, durationMs: Math.round(performance.now() - start) };
41
- }
42
- }
43
-
44
5
  // src/nestjs/decorators.ts
45
6
  function AsResult(operation) {
46
7
  return function(_target, _key, descriptor) {
47
8
  const original = descriptor.value;
48
9
  descriptor.value = async function(...args) {
49
10
  return run(
50
- () => original.apply(this, args));
11
+ () => original.apply(this, args),
12
+ void 0
13
+ );
51
14
  };
52
15
  if (operation) {
53
16
  descriptor.value.operationName = operation;
@@ -65,13 +28,6 @@ function WithMetrics(options) {
65
28
  return descriptor;
66
29
  };
67
30
  }
68
-
69
- // src/result/guards.ts
70
- function isRich(result) {
71
- return "durationMs" in result;
72
- }
73
-
74
- // src/nestjs/interceptor.ts
75
31
  function isResult(value) {
76
32
  return typeof value === "object" && value !== null && "ok" in value && typeof value["ok"] === "boolean";
77
33
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/result/constructors.ts","../../src/result/run.ts","../../src/nestjs/decorators.ts","../../src/result/guards.ts","../../src/nestjs/interceptor.ts","../../src/nestjs/module.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Result } from './types.js';\n\n/** Creates a successful Result. */\nexport const ok = <T, E = never>(value: T): Result<T, E> =>\n ({ ok: true, value });\n\n/** Creates a failed Result. */\nexport const fail = <T = never, E = Error>(error: E): Result<T, E> =>\n ({ ok: false, error });\n\n/**\n * Wraps a synchronous throwable function. Catches any thrown value and\n * passes it through `errorTransform` (defaults to identity cast).\n *\n * @example\n * const result = fromThrowable(() => JSON.parse(raw), (e) => new ParseError(e));\n */\nexport function fromThrowable<T, E = Error>(\n fn: () => T,\n errorTransform?: (caught: unknown) => E,\n): Result<T, E> {\n try {\n return ok(fn());\n } catch (caught) {\n return fail(errorTransform ? errorTransform(caught) : (caught as E));\n }\n}\n\n/**\n * Converts a Promise to a `Promise<Result<T, E>>`, catching rejections.\n *\n * @example\n * const result = await fromPromise(fetch(url), (e) => new NetworkError(e));\n */\nexport async function fromPromise<T, E = Error>(\n promise: Promise<T>,\n errorTransform?: (caught: unknown) => E,\n): Promise<Result<T, E>> {\n try {\n return ok(await promise);\n } catch (caught) {\n return fail(errorTransform ? errorTransform(caught) : (caught as E));\n }\n}\n\n/**\n * Converts a nullable value to a Result.\n * Returns `ok(value)` if non-null/undefined, `fail(error)` otherwise.\n *\n * @example\n * const result = fromNullable(user, new NotFoundError('user'));\n */\nexport function fromNullable<T, E>(\n value: T | null | undefined,\n error: E,\n): Result<T, E> {\n return value != null ? ok(value) : fail(error);\n}\n","import type { Result, RichResult, TrackOptions } from './types.js';\nimport { ok, fail } from './constructors.js';\n\n/**\n * Executes an async (or sync) function and captures any thrown exception,\n * returning a `Result<T, E>` instead of propagating the error.\n *\n * @example\n * const result = await run(() => fetchUser(id));\n * const result = await run(() => fetchUser(id), (e) => new UserError(e));\n */\nexport async function run<T, E = Error>(\n fn: () => T | Promise<T>,\n errorTransform?: (caught: unknown) => E,\n): Promise<Result<T, E>> {\n try {\n return ok(await fn());\n } catch (caught) {\n return fail(errorTransform ? errorTransform(caught) : (caught as E));\n }\n}\n\n/**\n * Like `run()` but also captures timing and metadata, returning a `RichResult<T, E>`.\n *\n * @example\n * const result = await track(() => fetchUser(id), { operation: 'user.fetch', tags: ['db'] });\n */\nexport async function track<T, E = Error>(\n fn: () => T | Promise<T>,\n options?: TrackOptions & { errorTransform?: (caught: unknown) => E },\n): Promise<RichResult<T, E>> {\n const start = performance.now();\n const timestamp = new Date().toISOString();\n const meta = {\n durationMs: 0,\n timestamp,\n operation: options?.operation,\n correlationId: options?.correlationId,\n tags: options?.tags,\n };\n\n try {\n const value = await fn();\n return { ok: true, value, ...meta, durationMs: Math.round(performance.now() - start) };\n } catch (caught) {\n const error = options?.errorTransform ? options.errorTransform(caught) : (caught as E);\n return { ok: false, error, ...meta, durationMs: Math.round(performance.now() - start) };\n }\n}\n\n/**\n * Promotes a plain `Result<T, E>` to a `RichResult<T, E>` with a zero-duration snapshot.\n * Useful when you already have a Result and want to attach metadata.\n */\nexport function enrich<T, E>(result: Result<T, E>, options?: TrackOptions): RichResult<T, E> {\n return {\n ...result,\n durationMs: 0,\n timestamp: new Date().toISOString(),\n operation: options?.operation,\n correlationId: options?.correlationId,\n tags: options?.tags,\n };\n}\n\n/**\n * Strips observability metadata from a `RichResult`, returning a plain `Result<T, E>`.\n */\nexport function simplify<T, E>(rich: RichResult<T, E>): Result<T, E> {\n return rich.ok\n ? { ok: true, value: rich.value }\n : { ok: false, error: rich.error };\n}\n","import { run, track } from '../result/run.js';\nimport type { TrackOptions } from '../result/types.js';\n\n/**\n * Method decorator that wraps the return value in `run()`, converting any\n * thrown exception into a `Result<T, E>`.\n *\n * @example\n * @AsResult('user.find')\n * async findUser(id: string) {\n * return this.db.users.findOrThrow(id);\n * }\n */\nexport function AsResult(operation?: string) {\n return function (\n _target: unknown,\n _key: string,\n descriptor: PropertyDescriptor,\n ): PropertyDescriptor {\n const original = descriptor.value as (...args: unknown[]) => unknown;\n descriptor.value = async function (...args: unknown[]) {\n return run(\n () => original.apply(this, args) as Promise<unknown>,\n undefined,\n );\n };\n if (operation) {\n (descriptor.value as { operationName?: string }).operationName = operation;\n }\n return descriptor;\n };\n}\n\n/**\n * Method decorator that wraps the return value in `track()`, capturing\n * execution duration, timestamp, and optional metadata.\n *\n * @example\n * @WithMetrics({ operation: 'payment.charge', tags: ['stripe'] })\n * async charge(dto: ChargeDto) {\n * return this.stripe.charge(dto);\n * }\n */\nexport function WithMetrics(options?: string | TrackOptions) {\n const opts: TrackOptions =\n typeof options === 'string' ? { operation: options } : (options ?? {});\n\n return function (\n _target: unknown,\n _key: string,\n descriptor: PropertyDescriptor,\n ): PropertyDescriptor {\n const original = descriptor.value as (...args: unknown[]) => unknown;\n descriptor.value = async function (...args: unknown[]) {\n return track(() => original.apply(this, args) as Promise<unknown>, opts);\n };\n return descriptor;\n };\n}\n","import type { Result, RichResult } from './types.js';\n\ntype OkResult<T, E> = Extract<Result<T, E>, { ok: true }>;\ntype FailResult<T, E> = Extract<Result<T, E>, { ok: false }>;\n\n/** Narrows a `Result<T, E>` to its success branch. */\nexport function isOk<T, E>(result: Result<T, E>): result is OkResult<T, E> {\n return result.ok === true;\n}\n\n/** Narrows a `Result<T, E>` to its failure branch. */\nexport function isFail<T, E>(result: Result<T, E>): result is FailResult<T, E> {\n return result.ok === false;\n}\n\n/** Returns `true` if the result carries observability metadata (`track()` output). */\nexport function isRich<T, E>(result: Result<T, E>): result is RichResult<T, E> {\n return 'durationMs' in result;\n}\n","import {\n Injectable,\n NestInterceptor,\n ExecutionContext,\n CallHandler,\n} from '@nestjs/common';\nimport { Observable } from 'rxjs';\nimport { map } from 'rxjs/operators';\nimport type { Result, RichResult } from '../result/types.js';\nimport { isRich } from '../result/guards.js';\n\nfunction isResult(value: unknown): value is Result<unknown, unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'ok' in value &&\n typeof (value as Record<string, unknown>)['ok'] === 'boolean'\n );\n}\n\n/**\n * Transforms `Result<T, E>` and `RichResult<T, E>` return values into a\n * consistent JSON response shape, so controllers don't need to unwrap manually.\n *\n * Plain results:\n * ```json\n * { \"ok\": true, \"data\": <value> }\n * { \"ok\": false, \"error\": \"<string>\" }\n * ```\n *\n * Rich results also include a `meta` block:\n * ```json\n * { \"ok\": true, \"data\": <value>, \"meta\": { \"operation\": \"...\", \"durationMs\": 12, ... } }\n * ```\n *\n * Non-Result return values are passed through unchanged.\n *\n * @example\n * // Global\n * app.useGlobalInterceptors(app.get(ResultInterceptor));\n *\n * // Per-controller\n * @UseInterceptors(ResultInterceptor)\n * @Controller('users')\n * export class UsersController { ... }\n */\n@Injectable()\nexport class ResultInterceptor implements NestInterceptor {\n intercept(_ctx: ExecutionContext, next: CallHandler): Observable<unknown> {\n return next.handle().pipe(\n map(value => {\n if (!isResult(value)) return value;\n\n const base = value.ok\n ? { ok: true, data: (value as { value: unknown }).value }\n : { ok: false, error: String((value as { error: unknown }).error) };\n\n if (!isRich(value)) return base;\n\n const rich = value as RichResult<unknown, unknown>;\n return {\n ...base,\n meta: {\n ...(rich.operation ? { operation: rich.operation } : {}),\n ...(rich.correlationId ? { correlationId: rich.correlationId } : {}),\n ...(rich.tags?.length ? { tags: rich.tags } : {}),\n durationMs: rich.durationMs,\n timestamp: rich.timestamp,\n },\n };\n }),\n );\n }\n}\n","import { Module } from '@nestjs/common';\nimport { ResultInterceptor } from './interceptor.js';\n\n@Module({\n providers: [ResultInterceptor],\n exports: [ResultInterceptor],\n})\nexport class ResultModule {}\n"]}
1
+ {"version":3,"sources":["../../src/nestjs/decorators.ts","../../src/nestjs/interceptor.ts","../../src/nestjs/module.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { run, track } from '../result/run.js';\nimport type { TrackOptions } from '../result/types.js';\n\n/**\n * Method decorator that wraps the return value in `run()`, converting any\n * thrown exception into a `Result<T, E>`.\n *\n * @example\n * @AsResult('user.find')\n * async findUser(id: string) {\n * return this.db.users.findOrThrow(id);\n * }\n */\nexport function AsResult(operation?: string) {\n return function (\n _target: unknown,\n _key: string,\n descriptor: PropertyDescriptor,\n ): PropertyDescriptor {\n const original = descriptor.value as (...args: unknown[]) => unknown;\n descriptor.value = async function (...args: unknown[]) {\n return run(\n () => original.apply(this, args) as Promise<unknown>,\n undefined,\n );\n };\n if (operation) {\n (descriptor.value as { operationName?: string }).operationName = operation;\n }\n return descriptor;\n };\n}\n\n/**\n * Method decorator that wraps the return value in `track()`, capturing\n * execution duration, timestamp, and optional metadata.\n *\n * @example\n * @WithMetrics({ operation: 'payment.charge', tags: ['stripe'] })\n * async charge(dto: ChargeDto) {\n * return this.stripe.charge(dto);\n * }\n */\nexport function WithMetrics(options?: string | TrackOptions) {\n const opts: TrackOptions =\n typeof options === 'string' ? { operation: options } : (options ?? {});\n\n return function (\n _target: unknown,\n _key: string,\n descriptor: PropertyDescriptor,\n ): PropertyDescriptor {\n const original = descriptor.value as (...args: unknown[]) => unknown;\n descriptor.value = async function (...args: unknown[]) {\n return track(() => original.apply(this, args) as Promise<unknown>, opts);\n };\n return descriptor;\n };\n}\n","import {\n Injectable,\n NestInterceptor,\n ExecutionContext,\n CallHandler,\n} from '@nestjs/common';\nimport { Observable } from 'rxjs';\nimport { map } from 'rxjs/operators';\nimport type { Result, RichResult } from '../result/types.js';\nimport { isRich } from '../result/guards.js';\n\nfunction isResult(value: unknown): value is Result<unknown, unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'ok' in value &&\n typeof (value as Record<string, unknown>)['ok'] === 'boolean'\n );\n}\n\n/**\n * Transforms `Result<T, E>` and `RichResult<T, E>` return values into a\n * consistent JSON response shape, so controllers don't need to unwrap manually.\n *\n * Plain results:\n * ```json\n * { \"ok\": true, \"data\": <value> }\n * { \"ok\": false, \"error\": \"<string>\" }\n * ```\n *\n * Rich results also include a `meta` block:\n * ```json\n * { \"ok\": true, \"data\": <value>, \"meta\": { \"operation\": \"...\", \"durationMs\": 12, ... } }\n * ```\n *\n * Non-Result return values are passed through unchanged.\n *\n * @example\n * // Global\n * app.useGlobalInterceptors(app.get(ResultInterceptor));\n *\n * // Per-controller\n * @UseInterceptors(ResultInterceptor)\n * @Controller('users')\n * export class UsersController { ... }\n */\n@Injectable()\nexport class ResultInterceptor implements NestInterceptor {\n intercept(_ctx: ExecutionContext, next: CallHandler): Observable<unknown> {\n return next.handle().pipe(\n map(value => {\n if (!isResult(value)) return value;\n\n const base = value.ok\n ? { ok: true, data: (value as { value: unknown }).value }\n : { ok: false, error: String((value as { error: unknown }).error) };\n\n if (!isRich(value)) return base;\n\n const rich = value as RichResult<unknown, unknown>;\n return {\n ...base,\n meta: {\n ...(rich.operation ? { operation: rich.operation } : {}),\n ...(rich.correlationId ? { correlationId: rich.correlationId } : {}),\n ...(rich.tags?.length ? { tags: rich.tags } : {}),\n durationMs: rich.durationMs,\n timestamp: rich.timestamp,\n },\n };\n }),\n );\n }\n}\n","import { Module } from '@nestjs/common';\nimport { ResultInterceptor } from './interceptor.js';\n\n@Module({\n providers: [ResultInterceptor],\n exports: [ResultInterceptor],\n})\nexport class ResultModule {}\n"]}
package/package.json CHANGED
@@ -1,70 +1,71 @@
1
- {
2
- "name": "@backendkit-labs/result",
3
- "version": "0.1.0",
4
- "description": "Type-safe Result monad for Node.js — generic error types, observability, resilience, and optional NestJS integration",
5
- "type": "module",
6
- "main": "./dist/index.cjs",
7
- "module": "./dist/index.js",
8
- "types": "./dist/index.d.ts",
9
- "exports": {
10
- ".": {
11
- "types": "./dist/index.d.ts",
12
- "import": "./dist/index.js",
13
- "require": "./dist/index.cjs"
14
- },
15
- "./nestjs": {
16
- "types": "./dist/nestjs/index.d.ts",
17
- "import": "./dist/nestjs/index.js",
18
- "require": "./dist/nestjs/index.cjs"
19
- }
20
- },
21
- "files": ["dist", "README.md", "LICENSE"],
22
- "scripts": {
23
- "build": "tsup",
24
- "dev": "tsup --watch",
25
- "test": "vitest run",
26
- "test:watch": "vitest",
27
- "test:coverage": "vitest run --coverage",
28
- "typecheck": "tsc --noEmit",
29
- "lint": "eslint src/",
30
- "prepublishOnly": "npm run build && npm run test && npm run lint"
31
- },
32
- "keywords": [
33
- "result", "either", "monad", "error-handling",
34
- "functional", "railway", "nestjs", "typescript", "node"
35
- ],
36
- "author": "Mairon José Cuello Martínez",
37
- "license": "MIT",
38
- "homepage": "https://github.com/backendkit-dev/backendkit-monorepo/tree/master/packages/result#readme",
39
- "repository": {
40
- "type": "git",
41
- "url": "git+https://github.com/backendkit-dev/backendkit-monorepo.git",
42
- "directory": "packages/result"
43
- },
44
- "bugs": { "url": "https://github.com/backendkit-dev/backendkit-monorepo/issues" },
45
- "sideEffects": false,
46
- "engines": { "node": ">=18" },
47
- "peerDependencies": {
48
- "@nestjs/common": ">=10.0.0",
49
- "@nestjs/core": ">=10.0.0",
50
- "rxjs": ">=7.0.0"
51
- },
52
- "peerDependenciesMeta": {
53
- "@nestjs/common": { "optional": true },
54
- "@nestjs/core": { "optional": true },
55
- "rxjs": { "optional": true }
56
- },
57
- "devDependencies": {
58
- "@eslint/js": "^9.39.4",
59
- "@nestjs/common": "^10.0.0",
60
- "@nestjs/core": "^10.0.0",
61
- "@types/node": "^22.0.0",
62
- "eslint": "^9.0.0",
63
- "reflect-metadata": "^0.2.0",
64
- "rxjs": "^7.8.0",
65
- "tsup": "^8.0.0",
66
- "typescript": "^5.5.0",
67
- "typescript-eslint": "^8.59.3",
68
- "vitest": "^2.0.0"
69
- }
70
- }
1
+ {
2
+ "name": "@backendkit-labs/result",
3
+ "version": "0.1.2",
4
+ "description": "Type-safe Result monad for Node.js — generic error types, observability, resilience, and optional NestJS integration",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./nestjs": {
16
+ "types": "./dist/nestjs/index.d.ts",
17
+ "import": "./dist/nestjs/index.js",
18
+ "require": "./dist/nestjs/index.cjs"
19
+ }
20
+ },
21
+ "files": ["dist", "README.md", "LICENSE"],
22
+ "scripts": {
23
+ "build": "tsup",
24
+ "dev": "tsup --watch",
25
+ "test": "vitest run",
26
+ "test:watch": "vitest",
27
+ "test:coverage": "vitest run --coverage",
28
+ "typecheck": "tsc --noEmit",
29
+ "lint": "eslint src/",
30
+ "prepublishOnly": "npm run build && npm run test && npm run lint"
31
+ },
32
+ "keywords": [
33
+ "result", "either", "monad", "error-handling",
34
+ "functional", "railway", "nestjs", "typescript", "node"
35
+ ],
36
+ "author": "Mairon José Cuello Martínez",
37
+ "license": "MIT",
38
+ "homepage": "https://github.com/backendkit-dev/backendkit-monorepo/tree/master/packages/result#readme",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/backendkit-dev/backendkit-monorepo.git",
42
+ "directory": "packages/result"
43
+ },
44
+ "bugs": { "url": "https://github.com/backendkit-dev/backendkit-monorepo/issues" },
45
+ "publishConfig": { "access": "public" },
46
+ "sideEffects": false,
47
+ "engines": { "node": ">=18" },
48
+ "peerDependencies": {
49
+ "@nestjs/common": ">=10.0.0",
50
+ "@nestjs/core": ">=10.0.0",
51
+ "rxjs": ">=7.0.0"
52
+ },
53
+ "peerDependenciesMeta": {
54
+ "@nestjs/common": { "optional": true },
55
+ "@nestjs/core": { "optional": true },
56
+ "rxjs": { "optional": true }
57
+ },
58
+ "devDependencies": {
59
+ "@eslint/js": "^9.39.4",
60
+ "@nestjs/common": "^10.0.0",
61
+ "@nestjs/core": "^10.0.0",
62
+ "@types/node": "^22.0.0",
63
+ "eslint": "^9.0.0",
64
+ "reflect-metadata": "^0.2.0",
65
+ "rxjs": "^7.8.0",
66
+ "tsup": "^8.0.0",
67
+ "typescript": "^5.5.0",
68
+ "typescript-eslint": "^8.59.3",
69
+ "vitest": "^2.0.0"
70
+ }
71
+ }