@fjell/core 4.4.61 → 4.4.63

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.
@@ -36,6 +36,7 @@ export interface ErrorInfo {
36
36
  retryable?: boolean;
37
37
  conflictingValue?: any;
38
38
  expectedValue?: any;
39
+ fieldErrors?: any[];
39
40
  };
40
41
  technical?: {
41
42
  timestamp: string;
@@ -1,4 +1,10 @@
1
1
  import { ActionError } from './ActionError';
2
+ export interface FieldError {
3
+ path: (string | number)[];
4
+ message: string;
5
+ code: string;
6
+ }
2
7
  export declare class ValidationError extends ActionError {
3
- constructor(message: string, validOptions?: string[], suggestedAction?: string, conflictingValue?: any);
8
+ fieldErrors?: FieldError[];
9
+ constructor(message: string, validOptions?: string[], suggestedAction?: string, conflictingValue?: any, fieldErrors?: FieldError[]);
4
10
  }
package/dist/index.d.ts CHANGED
@@ -15,7 +15,7 @@ export * from './validation';
15
15
  export * from './errors';
16
16
  export * from './operations';
17
17
  export * from './event';
18
- export type { Operations, OperationParams, AffectedKeys, CreateOptions } from './operations/Operations';
18
+ export type { Operations, OperationParams, AffectedKeys, CreateOptions, UpdateOptions } from './operations/Operations';
19
19
  export { isPriKey as isOperationPriKey, isComKey as isOperationComKey } from './operations/Operations';
20
20
  export type { GetMethod, CreateMethod, UpdateMethod, RemoveMethod, UpsertMethod, AllMethod, OneMethod, FindMethod, FindOneMethod, FinderMethod, ActionMethod, ActionOperationMethod, AllActionMethod, AllActionOperationMethod, FacetMethod, FacetOperationMethod, AllFacetMethod, AllFacetOperationMethod, OperationsExtensions } from './operations/methods';
21
21
  export * from './operations/wrappers';
package/dist/index.js CHANGED
@@ -1300,7 +1300,8 @@ var ActionError = class extends Error {
1300
1300
 
1301
1301
  // src/errors/ValidationError.ts
1302
1302
  var ValidationError = class extends ActionError {
1303
- constructor(message, validOptions, suggestedAction, conflictingValue) {
1303
+ fieldErrors;
1304
+ constructor(message, validOptions, suggestedAction, conflictingValue, fieldErrors) {
1304
1305
  super({
1305
1306
  code: "VALIDATION_ERROR",
1306
1307
  message,
@@ -1312,12 +1313,18 @@ var ValidationError = class extends ActionError {
1312
1313
  validOptions,
1313
1314
  suggestedAction,
1314
1315
  retryable: true,
1315
- conflictingValue
1316
+ conflictingValue,
1317
+ fieldErrors
1316
1318
  },
1317
1319
  technical: {
1318
1320
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1319
1321
  }
1320
1322
  });
1323
+ this.fieldErrors = fieldErrors;
1324
+ if (fieldErrors) {
1325
+ if (!this.errorInfo.details) this.errorInfo.details = {};
1326
+ this.errorInfo.details.fieldErrors = fieldErrors;
1327
+ }
1321
1328
  }
1322
1329
  };
1323
1330
 
@@ -1953,9 +1960,9 @@ Received: ${Array.isArray(item) ? "array" : typeof item}`
1953
1960
  var logger13 = logger_default.get("operations", "wrappers", "update");
1954
1961
  function createUpdateWrapper(coordinate, implementation, options = {}) {
1955
1962
  const operationName = options.operationName || "update";
1956
- return async (key, item) => {
1963
+ return async (key, item, updateOptions) => {
1957
1964
  if (options.debug) {
1958
- logger13.debug(`[${operationName}] Called with:`, { key, item });
1965
+ logger13.debug(`[${operationName}] Called with:`, { key, item, updateOptions });
1959
1966
  }
1960
1967
  if (!options.skipValidation) {
1961
1968
  validateKey(key, coordinate, operationName);
@@ -1969,7 +1976,7 @@ Received: ${Array.isArray(item) ? "array" : typeof item}`
1969
1976
  }
1970
1977
  }
1971
1978
  try {
1972
- const result = await implementation(key, item);
1979
+ const result = await implementation(key, item, updateOptions);
1973
1980
  if (options.debug) {
1974
1981
  logger13.debug(`[${operationName}] Updated item:`, result.key);
1975
1982
  }
@@ -1981,7 +1988,7 @@ Received: ${Array.isArray(item) ? "array" : typeof item}`
1981
1988
  if (options.onError) {
1982
1989
  const context = {
1983
1990
  operationName,
1984
- params: [key, item],
1991
+ params: [key, item, updateOptions],
1985
1992
  coordinate
1986
1993
  };
1987
1994
  throw options.onError(error, context);
@@ -1998,13 +2005,12 @@ Received: ${Array.isArray(item) ? "array" : typeof item}`
1998
2005
  var logger14 = logger_default.get("operations", "wrappers", "upsert");
1999
2006
  function createUpsertWrapper(coordinate, implementation, options = {}) {
2000
2007
  const operationName = options.operationName || "upsert";
2001
- return async (key, item, locations) => {
2008
+ return async (key, item, locations, updateOptions) => {
2002
2009
  if (options.debug) {
2003
- logger14.debug(`[${operationName}] Called with:`, { key, item, locations });
2010
+ logger14.debug(`[${operationName}] Called with:`, { key, item, locations, updateOptions });
2004
2011
  }
2005
2012
  if (!options.skipValidation) {
2006
2013
  validateKey(key, coordinate, operationName);
2007
- validateLocations(locations, coordinate, operationName);
2008
2014
  if (!item || typeof item !== "object" || Array.isArray(item)) {
2009
2015
  throw new Error(
2010
2016
  `[${operationName}] Invalid item parameter.
@@ -2015,7 +2021,7 @@ Received: ${Array.isArray(item) ? "array" : typeof item}`
2015
2021
  }
2016
2022
  }
2017
2023
  try {
2018
- const result = await implementation(key, item);
2024
+ const result = await implementation(key, item, locations, updateOptions);
2019
2025
  if (options.debug) {
2020
2026
  logger14.debug(`[${operationName}] Upserted item:`, result.key);
2021
2027
  }
@@ -2027,7 +2033,7 @@ Received: ${Array.isArray(item) ? "array" : typeof item}`
2027
2033
  if (options.onError) {
2028
2034
  const context = {
2029
2035
  operationName,
2030
- params: [key, item, locations],
2036
+ params: [key, item, locations, updateOptions],
2031
2037
  coordinate
2032
2038
  };
2033
2039
  throw options.onError(error, context);
@@ -19,6 +19,62 @@ export type CreateOptions<S extends string, L1 extends string = never, L2 extend
19
19
  key?: never;
20
20
  locations: LocKeyArray<L1, L2, L3, L4, L5>;
21
21
  };
22
+ /**
23
+ * Options for update operations across all Fjell libraries.
24
+ *
25
+ * These options provide explicit control over update behavior,
26
+ * with safe defaults that prevent accidental data loss.
27
+ *
28
+ * @public
29
+ */
30
+ export interface UpdateOptions {
31
+ /**
32
+ * Controls whether the update replaces the entire document/record or merges with existing data.
33
+ *
34
+ * **Default: `false` (merge/partial update)**
35
+ *
36
+ * When `false` (default):
37
+ * - Only the specified fields are updated
38
+ * - All other existing fields are preserved
39
+ * - Safe for partial updates
40
+ * - Recommended for most use cases
41
+ *
42
+ * When `true`:
43
+ * - The entire document/record is replaced with the provided data
44
+ * - Any fields not included in the update payload are DELETED
45
+ * - Use with extreme caution
46
+ * - Logs warning before operation (in implementations)
47
+ *
48
+ * ⚠️ **WARNING**: Setting `replace: true` can lead to permanent data loss.
49
+ * Always verify that your update payload contains ALL fields you want to preserve.
50
+ *
51
+ * @default false
52
+ *
53
+ * @example Merge update (default - safe)
54
+ * ```typescript
55
+ * // Existing: { id: '123', name: 'John', email: 'john@example.com', status: 'active' }
56
+ * await operations.update(
57
+ * { kt: 'user', pk: '123' },
58
+ * { status: 'inactive' }
59
+ * );
60
+ * // Result: { id: '123', name: 'John', email: 'john@example.com', status: 'inactive' }
61
+ * // ✅ All fields preserved except status
62
+ * ```
63
+ *
64
+ * @example Full replacement (use with caution)
65
+ * ```typescript
66
+ * // Existing: { id: '123', name: 'John', email: 'john@example.com', status: 'active' }
67
+ * await operations.update(
68
+ * { kt: 'user', pk: '123' },
69
+ * { status: 'inactive' },
70
+ * { replace: true }
71
+ * );
72
+ * // Result: { status: 'inactive' }
73
+ * // ❌ name and email are DELETED!
74
+ * ```
75
+ */
76
+ replace?: boolean;
77
+ }
22
78
  /**
23
79
  * Core Operations interface for Item-based data access.
24
80
  * This interface defines the standard contract for all fjell libraries
@@ -119,18 +175,32 @@ export interface Operations<V extends Item<S, L1, L2, L3, L4, L5>, S extends str
119
175
  *
120
176
  * @param key - Primary or composite key
121
177
  * @param item - Partial item properties to update
178
+ * @param options - Optional update options (controls merge vs replace behavior)
122
179
  * @returns The updated item
180
+ *
181
+ * @example Merge update (default - safe)
182
+ * ```typescript
183
+ * await operations.update(key, { status: 'active' });
184
+ * // Merges with existing data, preserves other fields
185
+ * ```
186
+ *
187
+ * @example Replace update (dangerous)
188
+ * ```typescript
189
+ * await operations.update(key, { status: 'active' }, { replace: true });
190
+ * // Replaces entire document, deletes unspecified fields
191
+ * ```
123
192
  */
124
- update(key: PriKey<S> | ComKey<S, L1, L2, L3, L4, L5>, item: Partial<Item<S, L1, L2, L3, L4, L5>>): Promise<V>;
193
+ update(key: PriKey<S> | ComKey<S, L1, L2, L3, L4, L5>, item: Partial<Item<S, L1, L2, L3, L4, L5>>, options?: UpdateOptions): Promise<V>;
125
194
  /**
126
195
  * Updates an item if it exists, creates it if it doesn't.
127
196
  *
128
197
  * @param key - Primary or composite key
129
198
  * @param item - Partial item properties
130
199
  * @param locations - Optional locations for creation
200
+ * @param options - Optional update options (used only if item exists, ignored for creation)
131
201
  * @returns The upserted item
132
202
  */
133
- upsert(key: PriKey<S> | ComKey<S, L1, L2, L3, L4, L5>, item: Partial<Item<S, L1, L2, L3, L4, L5>>, locations?: LocKeyArray<L1, L2, L3, L4, L5>): Promise<V>;
203
+ upsert(key: PriKey<S> | ComKey<S, L1, L2, L3, L4, L5>, item: Partial<Item<S, L1, L2, L3, L4, L5>>, locations?: LocKeyArray<L1, L2, L3, L4, L5>, options?: UpdateOptions): Promise<V>;
134
204
  /**
135
205
  * Removes an item.
136
206
  *
@@ -1,7 +1,7 @@
1
1
  import { Item } from "../items";
2
2
  import { ComKey, LocKeyArray, PriKey } from "../keys";
3
3
  import { ItemQuery } from "../item/ItemQuery";
4
- import { AffectedKeys, CreateOptions, OperationParams } from "./Operations";
4
+ import { AffectedKeys, CreateOptions, OperationParams, UpdateOptions } from "./Operations";
5
5
  /**
6
6
  * Get method signature - retrieves single item by key
7
7
  */
@@ -18,7 +18,7 @@ export interface CreateMethod<V extends Item<S, L1, L2, L3, L4, L5>, S extends s
18
18
  * Update method signature - updates existing item
19
19
  */
20
20
  export interface UpdateMethod<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> {
21
- (key: PriKey<S> | ComKey<S, L1, L2, L3, L4, L5>, item: Partial<Item<S, L1, L2, L3, L4, L5>>): Promise<V>;
21
+ (key: PriKey<S> | ComKey<S, L1, L2, L3, L4, L5>, item: Partial<Item<S, L1, L2, L3, L4, L5>>, options?: UpdateOptions): Promise<V>;
22
22
  }
23
23
  /**
24
24
  * Remove method signature - removes item
@@ -30,7 +30,7 @@ export interface RemoveMethod<V extends Item<S, L1, L2, L3, L4, L5>, S extends s
30
30
  * Upsert method signature - updates or creates item
31
31
  */
32
32
  export interface UpsertMethod<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> {
33
- (key: PriKey<S> | ComKey<S, L1, L2, L3, L4, L5>, item: Partial<Item<S, L1, L2, L3, L4, L5>>): Promise<V>;
33
+ (key: PriKey<S> | ComKey<S, L1, L2, L3, L4, L5>, item: Partial<Item<S, L1, L2, L3, L4, L5>>, locations?: LocKeyArray<L1, L2, L3, L4, L5>, options?: UpdateOptions): Promise<V>;
34
34
  }
35
35
  /**
36
36
  * All method signature - retrieves all items matching query
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * Provides automatic validation for upsert() operation parameters.
5
5
  */
6
- import type { Item } from "../../items";
7
6
  import type { Coordinate } from "../../Coordinate";
7
+ import type { Item } from "../../items";
8
8
  import type { UpsertMethod } from "../methods";
9
9
  import type { WrapperOptions } from "./types";
10
10
  /**
@@ -19,8 +19,8 @@ import type { WrapperOptions } from "./types";
19
19
  * ```typescript
20
20
  * const upsert = createUpsertWrapper(
21
21
  * coordinate,
22
- * async (key, item, locations) => {
23
- * return await database.upsert(key, item, locations);
22
+ * async (key, item, locations, options) => {
23
+ * return await database.upsert(key, item, locations, options);
24
24
  * }
25
25
  * );
26
26
  * ```
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fjell/core",
3
3
  "description": "Core Item and Key Framework for Fjell",
4
- "version": "4.4.61",
4
+ "version": "4.4.63",
5
5
  "keywords": [
6
6
  "core",
7
7
  "fjell"