ts-ag 1.0.6 → 1.0.8

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,4 +1,4 @@
1
- import { error_lambda_badRequest, error_lambda_conflict, error_lambda_internal, error_lambda_unauthorized, error_lambda_forbidden, error_lambda_notFound } from '$lambda/errors.js';
1
+ import { error_lambda_badRequest, error_lambda_conflict, error_lambda_internal, error_lambda_unauthorized, error_lambda_forbidden, error_lambda_notFound } from '../lambda/errors.js';
2
2
  export const error_cognito_forbidden = { group: 'cognito', type: 'cognito_forbidden' };
3
3
  export const error_cognito_internal = { group: 'cognito', type: 'cognito_internal' };
4
4
  export const error_cognito_role = { group: 'cognito', type: 'cognito_role' };
@@ -1,4 +1,4 @@
1
- import { error_lambda_internal } from '$lambda/errors.js';
1
+ import { error_lambda_internal } from '../lambda/errors.js';
2
2
  export const error_dynamo = { type: 'dynamo' };
3
3
  export function error_lambda_fromDynamo(e) {
4
4
  return error_lambda_internal('Internal server error');
@@ -16,7 +16,7 @@ export type type_error_response = Omit<ErrorRawProxyResultV2, 'headers' | 'body'
16
16
  headers: NonNullable<ErrorRawProxyResultV2['headers']>;
17
17
  body: NonNullable<ErrorRawProxyResultV2['body']>;
18
18
  };
19
- export type LambdaErrorResponse<Type extends string = '', Extras extends object | never = never> = {
19
+ export type LambdaErrorResponse<Type extends string = '', Extras extends object = object> = {
20
20
  headers: Record<string, string>;
21
21
  statusCode: 400;
22
22
  body: {
@@ -59,7 +59,7 @@ export type LambdaErrorResponse<Type extends string = '', Extras extends object
59
59
  type: Type;
60
60
  } & Extras;
61
61
  };
62
- export declare function response_error<Type extends string, Extras extends object | never = never>(e: type_error_lambda, headers: Record<string, string>, type?: Type, extras?: Extras): LambdaErrorResponse<Type, Extras>;
62
+ export declare function response_error<Type extends string, Extras extends object = object>(e: type_error_lambda, headers: Record<string, string>, type?: Type, extras?: Extras): LambdaErrorResponse<Type, Extras>;
63
63
  /**
64
64
  * Helper function to get a reasonable default error response from a valibot parse result
65
65
  * @param res - The output from calling safeParse on the input
@@ -67,7 +67,7 @@ export declare function response_error<Type extends string, Extras extends objec
67
67
  */
68
68
  export declare function response_valibotError(res: Extract<SafeParseResult<any>, {
69
69
  success: false;
70
- }>, headers: any): LambdaErrorResponse<string, never>;
70
+ }>, headers: any): LambdaErrorResponse<string, object>;
71
71
  export declare function response_ok<Body extends {
72
72
  message: string;
73
73
  }>(body: Body, headers: any, cookies?: string[] | undefined): {
@@ -1 +1 @@
1
- {"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/lambda/response.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/lambda/response.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { Result } from 'neverthrow';
2
2
  import { parse } from 'cookie';
3
- import { error_lambda_unauthorized } from '$lambda/errors.js';
3
+ import { error_lambda_unauthorized } from '../errors.js';
4
4
  /**
5
5
  * Wraps cookies parse along with the api gateway event with neverthrow
6
6
  */
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ // NOTE: dont use aliases here cause this file needs to be compiled first
2
3
  import { existsSync, statSync, readFileSync } from 'fs';
3
4
  import { dirname, join, basename } from 'path';
4
5
  import { watch } from 'chokidar';
@@ -12,13 +12,12 @@
12
12
  export type DeepOverride<T, R> = {
13
13
  [K in keyof T]: K extends keyof R ? R[K] extends infer RK ? RK extends Record<string, any> ? T[K] extends Record<string, any> ? DeepOverride<T[K], RK> : RK : RK extends Array<infer RU> ? T[K] extends Array<infer TU> ? Array<DeepOverride<TU, RU>> : RK : RK : never : T[K];
14
14
  };
15
- /**
16
- *
17
- */
18
- export type DeepPartial<T> = {
19
- [P in keyof T]?: T[P] extends object ? T[P] extends Function ? T[P] : DeepPartial<T[P]> : T[P];
20
- };
15
+ type Primitive = string | number | boolean | bigint | symbol | null | undefined | Date | RegExp | Function;
16
+ export type DeepPartial<T> = T extends Primitive ? T : T extends readonly (infer U)[] ? readonly U[] : T extends object ? {
17
+ [K in keyof T]?: DeepPartial<T[K]>;
18
+ } : T;
21
19
  export type DeepRequired<T> = {
22
20
  [P in keyof T]-?: NonNullable<T[P] extends object ? (T[P] extends Function ? T[P] : DeepRequired<T[P]>) : T[P]>;
23
21
  };
22
+ export {};
24
23
  //# sourceMappingURL=deep.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"deep.d.ts","sourceRoot":"","sources":["../../src/types/deep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,IAAI;KAC9B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,GAC7B,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,EAAE,GACnB,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC9B,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GACtB,EAAE,GACJ,EAAE,SAAS,KAAK,CAAC,MAAM,EAAE,CAAC,GACxB,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,EAAE,CAAC,GAC1B,KAAK,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,GAC3B,EAAE,GACJ,EAAE,GACN,KAAK,GACP,CAAC,CAAC,CAAC,CAAC;CACT,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;KAC1B,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,GAEhC,CAAC,CAAC,CAAC,CAAC,SAAS,QAAQ,GACnB,CAAC,CAAC,CAAC,CAAC,GACJ,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GACnB,CAAC,CAAC,CAAC,CAAC;CACT,CAAC;AAEF,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;KAE3B,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;CAChH,CAAC"}
1
+ {"version":3,"file":"deep.d.ts","sourceRoot":"","sources":["../../src/types/deep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,IAAI;KAC9B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,GAC7B,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,EAAE,GACnB,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC9B,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GACtB,EAAE,GACJ,EAAE,SAAS,KAAK,CAAC,MAAM,EAAE,CAAC,GACxB,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,EAAE,CAAC,GAC1B,KAAK,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,GAC3B,EAAE,GACJ,EAAE,GACN,KAAK,GACP,CAAC,CAAC,CAAC,CAAC;CACT,CAAC;AAGF,KAAK,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE3G,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS,GAC5C,CAAC,GACD,CAAC,SAAS,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,GAC5B,SAAS,CAAC,EAAE,GACZ,CAAC,SAAS,MAAM,GACd;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GACtC,CAAC,CAAC;AAEV,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;KAE3B,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;CAChH,CAAC"}
@@ -1,3 +1,4 @@
1
+ import type { DeepPartial } from '../types/deep.js';
1
2
  /**
2
3
  * Sets the value for an object by its dot path
3
4
  * @param obj - any object
@@ -13,4 +14,25 @@ export declare function setByPath<T extends object>(obj: T, path: string, value:
13
14
  * @returns - the value at the given path or undefined
14
15
  */
15
16
  export declare function getByPath<T extends object>(obj: T, path: string): any;
17
+ /**
18
+ * Returns a deep "patch" object containing only the fields from `b`
19
+ * that are different from `a`.
20
+ *
21
+ * Behavior:
22
+ * - Only keys from `b` can appear in the result.
23
+ * - New keys in `b` are included.
24
+ * - Changed primitive/array values are included as the value from `b`.
25
+ * - For nested plain objects, it recurses and returns only the differing nested fields.
26
+ * - Arrays are treated as atomic (if different, the whole array from `b` is returned).
27
+ *
28
+ * Typing:
29
+ * - Output is `DeepPartial<B>` because only a subset of `b`'s shape is returned.
30
+ *
31
+ * @template A
32
+ * @template B
33
+ * @param {A} a - Base/original object (can be a different shape than `b`).
34
+ * @param {B} b - Updated object; output keys come from this object.
35
+ * @returns {DeepPartial<B>} Deep partial of `b` containing only differences vs `a`.
36
+ */
37
+ export declare const deepDiff: <A extends object, B extends object>(a: A, b: B) => DeepPartial<B>;
16
38
  //# sourceMappingURL=object.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"object.d.ts","sourceRoot":"","sources":["../../src/utils/object.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,CAkB/E;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,GAAG,CAWrE"}
1
+ {"version":3,"file":"object.d.ts","sourceRoot":"","sources":["../../src/utils/object.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,CAkB/E;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,GAAG,CAWrE;AAID;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,iBAAiB,CAAC,8CAsB3C,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { isEqual, isObject } from 'radash';
1
2
  /**
2
3
  * Sets the value for an object by its dot path
3
4
  * @param obj - any object
@@ -41,3 +42,44 @@ export function getByPath(obj, path) {
41
42
  }
42
43
  return curr;
43
44
  }
45
+ const isPlainRecord = (v) => isObject(v) && !Array.isArray(v);
46
+ /**
47
+ * Returns a deep "patch" object containing only the fields from `b`
48
+ * that are different from `a`.
49
+ *
50
+ * Behavior:
51
+ * - Only keys from `b` can appear in the result.
52
+ * - New keys in `b` are included.
53
+ * - Changed primitive/array values are included as the value from `b`.
54
+ * - For nested plain objects, it recurses and returns only the differing nested fields.
55
+ * - Arrays are treated as atomic (if different, the whole array from `b` is returned).
56
+ *
57
+ * Typing:
58
+ * - Output is `DeepPartial<B>` because only a subset of `b`'s shape is returned.
59
+ *
60
+ * @template A
61
+ * @template B
62
+ * @param {A} a - Base/original object (can be a different shape than `b`).
63
+ * @param {B} b - Updated object; output keys come from this object.
64
+ * @returns {DeepPartial<B>} Deep partial of `b` containing only differences vs `a`.
65
+ */
66
+ export const deepDiff = (a, b) => {
67
+ const out = {};
68
+ for (const key of Object.keys(b)) {
69
+ const aVal = a?.[key];
70
+ const bVal = b[key];
71
+ if (!(key in a)) {
72
+ out[key] = bVal;
73
+ continue;
74
+ }
75
+ if (isPlainRecord(aVal) && isPlainRecord(bVal)) {
76
+ const nested = deepDiff(aVal, bVal);
77
+ if (Object.keys(nested).length)
78
+ out[key] = nested;
79
+ continue;
80
+ }
81
+ if (!isEqual(aVal, bVal))
82
+ out[key] = bVal;
83
+ }
84
+ return out;
85
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-ag",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Useful TS stuff",
5
5
  "bugs": "https://github.com/ageorgeh/ts-ag/issues",
6
6
  "repository": {
@@ -31,7 +31,7 @@
31
31
  "./dist"
32
32
  ],
33
33
  "scripts": {
34
- "build": "tsgo",
34
+ "build": "tsgo && node dist/scripts/ts-alias.js",
35
35
  "deps:bump": "pnpm dlx npm-check-updates -u --target minor",
36
36
  "deps:refresh": "pnpm deps:bump && pnpm install",
37
37
  "deps:update": "pnpm -r update",
@@ -41,6 +41,7 @@
41
41
  "publish:prerelease": "pnpm publish --tag dev --access public --no-git-checks --registry=https://registry.npmjs.org/ --json > ./publish.json",
42
42
  "run-publish": "pnpm publish --access public --no-git-checks --registry=https://registry.npmjs.org/ --json > ./publish.json",
43
43
  "run-version": "pnpm version patch --no-git-tag-version",
44
+ "test": "vitest --run",
44
45
  "tsc:watch": "concurrently \" npx tsc -w \" \" npx ts-alias -w \"",
45
46
  "version:prerelease": "pnpm version prerelease --preid dev --no-git-tag-version"
46
47
  },
@@ -59,6 +60,7 @@
59
60
  "glob": "13.0.1",
60
61
  "jose": "6.1.3",
61
62
  "neverthrow": "8.2.0",
63
+ "radash": "^12.1.1",
62
64
  "rehype-parse": "9.0.1",
63
65
  "tsc-alias": "1.8.16",
64
66
  "tsconfck": "3.1.6",
@@ -88,6 +90,11 @@
88
90
  "semantic-release": "^25.0.3",
89
91
  "typescript": "^5.9.3",
90
92
  "typescript-eslint": "^8.54.0",
91
- "typescript-svelte-plugin": "0.3.50"
93
+ "typescript-svelte-plugin": "0.3.50",
94
+ "vite-tsconfig-paths": "^6.1.1",
95
+ "vitest": "^4.0.18"
96
+ },
97
+ "peerDependencies": {
98
+ "@types/aws-lambda": "8.10.160"
92
99
  }
93
100
  }
@@ -0,0 +1,12 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { error_dynamo, error_lambda_fromDynamo } from './errors.js';
3
+
4
+ describe('dynamo errors', () => {
5
+ it('exports the dynamo error constant and maps it to a lambda internal error', () => {
6
+ expect(error_dynamo).toEqual({ type: 'dynamo' });
7
+ expect(error_lambda_fromDynamo(error_dynamo)).toEqual({
8
+ type: 'lambda_internal',
9
+ message: 'Internal server error'
10
+ });
11
+ });
12
+ });
@@ -0,0 +1,38 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ error_lambda_badRequest,
4
+ error_lambda_conflict,
5
+ error_lambda_forbidden,
6
+ error_lambda_internal,
7
+ error_lambda_notFound,
8
+ error_lambda_unauthorized
9
+ } from './errors.js';
10
+
11
+ describe('lambda error factories', () => {
12
+ it('creates bad request errors with optional field metadata', () => {
13
+ expect(error_lambda_badRequest('Invalid', 'email', 'bad@value')).toEqual({
14
+ type: 'lambda_badRequest',
15
+ message: 'Invalid',
16
+ fieldName: 'email',
17
+ fieldValue: 'bad@value'
18
+ });
19
+ });
20
+
21
+ it('creates remaining lambda error types with expected shape', () => {
22
+ expect(error_lambda_unauthorized('No auth')).toEqual({ type: 'lambda_unauthorized', message: 'No auth' });
23
+ expect(error_lambda_forbidden('No access')).toEqual({ type: 'lambda_forbidden', message: 'No access' });
24
+ expect(error_lambda_notFound('Missing', 'id', '123')).toEqual({
25
+ type: 'lambda_notFound',
26
+ message: 'Missing',
27
+ fieldName: 'id',
28
+ fieldValue: '123'
29
+ });
30
+ expect(error_lambda_conflict('Conflict', 'id', '123')).toEqual({
31
+ type: 'lambda_conflict',
32
+ message: 'Conflict',
33
+ fieldName: 'id',
34
+ fieldValue: '123'
35
+ });
36
+ expect(error_lambda_internal('Oops')).toEqual({ type: 'lambda_internal', message: 'Oops' });
37
+ });
38
+ });
@@ -0,0 +1,30 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { parse } from 'devalue';
3
+ import { wrapHandler } from './handlerUtils.js';
4
+
5
+ describe('wrapHandler', () => {
6
+ it('serializes object bodies with devalue and preserves other fields', async () => {
7
+ const wrapped = wrapHandler(async () => ({
8
+ statusCode: 200,
9
+ headers: { 'content-type': 'application/json' },
10
+ body: { ok: true, nested: { value: 123 } },
11
+ cookies: ['a=1']
12
+ }));
13
+
14
+ const response = await wrapped({} as never, {} as never);
15
+
16
+ expect(response.statusCode).toBe(200);
17
+ expect(response.headers).toEqual({ 'content-type': 'application/json' });
18
+ expect(response.cookies).toEqual(['a=1']);
19
+ expect(typeof response.body).toBe('string');
20
+ expect(parse(response.body as string)).toEqual({ ok: true, nested: { value: 123 } });
21
+ });
22
+
23
+ it('keeps body undefined when raw handler has no body', async () => {
24
+ const wrapped = wrapHandler(async () => ({ statusCode: 204 }));
25
+
26
+ const response = await wrapped({} as never, {} as never);
27
+
28
+ expect(response).toEqual({ statusCode: 204, body: undefined });
29
+ });
30
+ });
@@ -13,7 +13,7 @@ export type type_error_response = Omit<ErrorRawProxyResultV2, 'headers' | 'body'
13
13
  body: NonNullable<ErrorRawProxyResultV2['body']>;
14
14
  };
15
15
 
16
- export type LambdaErrorResponse<Type extends string = '', Extras extends object | never = never> =
16
+ export type LambdaErrorResponse<Type extends string = '', Extras extends object = object> =
17
17
  | {
18
18
  headers: Record<string, string>;
19
19
  statusCode: 400;
@@ -33,7 +33,7 @@ export type LambdaErrorResponse<Type extends string = '', Extras extends object
33
33
  }
34
34
  | { headers: Record<string, string>; statusCode: 500; body: { message: string; type: Type } & Extras };
35
35
 
36
- export function response_error<Type extends string, Extras extends object | never = never>(
36
+ export function response_error<Type extends string, Extras extends object = object>(
37
37
  e: type_error_lambda,
38
38
  headers: Record<string, string>,
39
39
  type: Type = '' as Type,
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ // NOTE: dont use aliases here cause this file needs to be compiled first
3
4
  import { existsSync, statSync, readFileSync } from 'fs';
4
5
  import { dirname, join, basename } from 'path';
5
6
  import type { FSWatcher } from 'chokidar';
package/src/types/deep.ts CHANGED
@@ -25,17 +25,16 @@ export type DeepOverride<T, R> = {
25
25
  : T[K]; // Keep original type if not overridden
26
26
  };
27
27
 
28
- /**
29
- *
30
- */
31
- export type DeepPartial<T> = {
32
- [P in keyof T]?: T[P] extends object
33
- ? // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
34
- T[P] extends Function
35
- ? T[P]
36
- : DeepPartial<T[P]>
37
- : T[P];
38
- };
28
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
29
+ type Primitive = string | number | boolean | bigint | symbol | null | undefined | Date | RegExp | Function;
30
+
31
+ export type DeepPartial<T> = T extends Primitive
32
+ ? T
33
+ : T extends readonly (infer U)[]
34
+ ? readonly U[] // treat arrays as atomic values
35
+ : T extends object
36
+ ? { [K in keyof T]?: DeepPartial<T[K]> }
37
+ : T;
39
38
 
40
39
  export type DeepRequired<T> = {
41
40
  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
@@ -1,3 +1,6 @@
1
+ import { isEqual, isObject } from 'radash';
2
+ import type { DeepPartial } from '../types/deep.js';
3
+
1
4
  /**
2
5
  * Sets the value for an object by its dot path
3
6
  * @param obj - any object
@@ -43,3 +46,49 @@ export function getByPath<T extends object>(obj: T, path: string): any {
43
46
 
44
47
  return curr;
45
48
  }
49
+
50
+ const isPlainRecord = (v: unknown): v is Record<string, unknown> => isObject(v) && !Array.isArray(v);
51
+
52
+ /**
53
+ * Returns a deep "patch" object containing only the fields from `b`
54
+ * that are different from `a`.
55
+ *
56
+ * Behavior:
57
+ * - Only keys from `b` can appear in the result.
58
+ * - New keys in `b` are included.
59
+ * - Changed primitive/array values are included as the value from `b`.
60
+ * - For nested plain objects, it recurses and returns only the differing nested fields.
61
+ * - Arrays are treated as atomic (if different, the whole array from `b` is returned).
62
+ *
63
+ * Typing:
64
+ * - Output is `DeepPartial<B>` because only a subset of `b`'s shape is returned.
65
+ *
66
+ * @template A
67
+ * @template B
68
+ * @param {A} a - Base/original object (can be a different shape than `b`).
69
+ * @param {B} b - Updated object; output keys come from this object.
70
+ * @returns {DeepPartial<B>} Deep partial of `b` containing only differences vs `a`.
71
+ */
72
+ export const deepDiff = <A extends object, B extends object>(a: A, b: B): DeepPartial<B> => {
73
+ const out: Record<string, unknown> = {};
74
+
75
+ for (const key of Object.keys(b) as Array<keyof B>) {
76
+ const aVal = (a as any)?.[key];
77
+ const bVal = (b as any)[key];
78
+
79
+ if (!((key as any) in (a as any))) {
80
+ out[key as any] = bVal;
81
+ continue;
82
+ }
83
+
84
+ if (isPlainRecord(aVal) && isPlainRecord(bVal)) {
85
+ const nested = deepDiff(aVal, bVal);
86
+ if (Object.keys(nested as any).length) out[key as any] = nested;
87
+ continue;
88
+ }
89
+
90
+ if (!isEqual(aVal, bVal)) out[key as any] = bVal;
91
+ }
92
+
93
+ return out as DeepPartial<B>;
94
+ };
@@ -0,0 +1,60 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { deepDiff, getByPath, setByPath } from './object.js';
3
+
4
+ describe('deepDiff', () => {
5
+ it('returns an empty object when values are deeply equal', () => {
6
+ const a = { id: 1, nested: { enabled: true, label: 'a' } };
7
+ const b = { id: 1, nested: { enabled: true, label: 'a' } };
8
+
9
+ expect(deepDiff(a, b)).toEqual({});
10
+ });
11
+
12
+ it('includes new keys that exist only in the updated object', () => {
13
+ const a = { id: 1 };
14
+ const b = { id: 1, createdBy: 'alex' };
15
+
16
+ expect(deepDiff(a, b)).toEqual({ createdBy: 'alex' });
17
+ });
18
+
19
+ it('returns only changed nested fields for plain objects', () => {
20
+ const a = { profile: { name: 'Alex', age: 30, flags: { admin: false, beta: false } } };
21
+ const b = { profile: { name: 'Alex', age: 31, flags: { admin: true, beta: false } } };
22
+
23
+ expect(deepDiff(a, b)).toEqual({ profile: { age: 31, flags: { admin: true } } });
24
+ });
25
+
26
+ it('treats arrays as atomic and returns full replacement when changed', () => {
27
+ const a = { tags: ['a', 'b'] };
28
+ const b = { tags: ['a', 'c'] };
29
+
30
+ expect(deepDiff(a, b)).toEqual({ tags: ['a', 'c'] });
31
+ });
32
+
33
+ it('does not include keys that only exist on the base object', () => {
34
+ const a = { keepInAOnly: true, nested: { removed: 'x' } };
35
+ const b = { nested: {} };
36
+
37
+ expect(deepDiff(a, b)).toEqual({});
38
+ });
39
+ });
40
+
41
+ describe('setByPath/getByPath', () => {
42
+ it('sets and reads nested object values', () => {
43
+ const target = {} as { user?: { profile?: { name?: string } } };
44
+
45
+ setByPath(target, 'user.profile.name', 'Alex');
46
+
47
+ expect(target).toEqual({ user: { profile: { name: 'Alex' } } });
48
+ expect(getByPath(target, 'user.profile.name')).toBe('Alex');
49
+ });
50
+
51
+ it('handles array-style path segments and missing values', () => {
52
+ const target = {} as { rows?: Array<{ value?: number }> };
53
+
54
+ setByPath(target, 'rows.0.value', 42);
55
+
56
+ expect(target).toEqual({ rows: [{ value: 42 }] });
57
+ expect(getByPath(target, 'rows.0.value')).toBe(42);
58
+ expect(getByPath(target, 'rows.1.value')).toBeUndefined();
59
+ });
60
+ });