@fjell/core 4.4.48 → 4.4.50

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.
Files changed (94) hide show
  1. package/README.md +92 -0
  2. package/dist/Coordinate.d.ts +7 -0
  3. package/dist/errors/ActionError.d.ts +51 -0
  4. package/dist/errors/BusinessLogicError.d.ts +4 -0
  5. package/dist/errors/DuplicateError.d.ts +4 -0
  6. package/dist/errors/NotFoundError.d.ts +4 -0
  7. package/dist/errors/PermissionError.d.ts +4 -0
  8. package/dist/errors/ValidationError.d.ts +4 -0
  9. package/dist/errors/index.d.ts +6 -0
  10. package/dist/event/emitter.d.ts +140 -0
  11. package/dist/event/events.d.ts +81 -0
  12. package/dist/event/index.d.ts +38 -0
  13. package/dist/event/matching.d.ts +54 -0
  14. package/dist/event/subscription.d.ts +74 -0
  15. package/dist/event/types.d.ts +186 -0
  16. package/dist/index.d.ts +13 -0
  17. package/dist/index.js +1584 -47
  18. package/dist/item/IUtils.d.ts +6 -3
  19. package/dist/operations/OperationContext.d.ts +10 -0
  20. package/dist/operations/Operations.d.ts +259 -0
  21. package/dist/operations/contained.d.ts +65 -0
  22. package/dist/operations/errorEnhancer.d.ts +79 -0
  23. package/dist/operations/index.d.ts +2 -0
  24. package/dist/operations/methods.d.ts +134 -0
  25. package/dist/operations/primary.d.ts +57 -0
  26. package/dist/operations/specialized.d.ts +41 -0
  27. package/dist/operations/wrappers/createActionWrapper.d.ts +28 -0
  28. package/dist/operations/wrappers/createAllActionWrapper.d.ts +28 -0
  29. package/dist/operations/wrappers/createAllFacetWrapper.d.ts +27 -0
  30. package/dist/operations/wrappers/createAllWrapper.d.ts +28 -0
  31. package/dist/operations/wrappers/createCreateWrapper.d.ts +28 -0
  32. package/dist/operations/wrappers/createFacetWrapper.d.ts +27 -0
  33. package/dist/operations/wrappers/createFindOneWrapper.d.ts +28 -0
  34. package/dist/operations/wrappers/createFindWrapper.d.ts +28 -0
  35. package/dist/operations/wrappers/createGetWrapper.d.ts +28 -0
  36. package/dist/operations/wrappers/createOneWrapper.d.ts +38 -0
  37. package/dist/operations/wrappers/createRemoveWrapper.d.ts +28 -0
  38. package/dist/operations/wrappers/createUpdateWrapper.d.ts +28 -0
  39. package/dist/operations/wrappers/createUpsertWrapper.d.ts +28 -0
  40. package/dist/operations/wrappers/index.d.ts +34 -0
  41. package/dist/operations/wrappers/types.d.ts +48 -0
  42. package/dist/validation/ItemValidator.d.ts +43 -0
  43. package/dist/validation/KeyValidator.d.ts +56 -0
  44. package/dist/validation/LocationValidator.d.ts +39 -0
  45. package/dist/validation/QueryValidator.d.ts +57 -0
  46. package/dist/validation/index.d.ts +15 -0
  47. package/dist/validation/index.js +501 -0
  48. package/dist/validation/types.d.ts +38 -0
  49. package/package.json +7 -2
  50. package/src/Coordinate.ts +35 -0
  51. package/src/errors/ActionError.ts +69 -0
  52. package/src/errors/BusinessLogicError.ts +24 -0
  53. package/src/errors/DuplicateError.ts +57 -0
  54. package/src/errors/NotFoundError.ts +24 -0
  55. package/src/errors/PermissionError.ts +31 -0
  56. package/src/errors/ValidationError.ts +27 -0
  57. package/src/errors/index.ts +7 -0
  58. package/src/event/emitter.ts +247 -0
  59. package/src/event/events.ts +178 -0
  60. package/src/event/index.ts +130 -0
  61. package/src/event/matching.ts +264 -0
  62. package/src/event/subscription.ts +181 -0
  63. package/src/event/types.ts +282 -0
  64. package/src/index.ts +57 -0
  65. package/src/item/IUtils.ts +9 -80
  66. package/src/operations/OperationContext.ts +12 -0
  67. package/src/operations/Operations.ts +357 -0
  68. package/src/operations/contained.ts +134 -0
  69. package/src/operations/errorEnhancer.ts +204 -0
  70. package/src/operations/index.ts +2 -0
  71. package/src/operations/methods.ts +363 -0
  72. package/src/operations/primary.ts +101 -0
  73. package/src/operations/specialized.ts +71 -0
  74. package/src/operations/wrappers/createActionWrapper.ts +108 -0
  75. package/src/operations/wrappers/createAllActionWrapper.ts +109 -0
  76. package/src/operations/wrappers/createAllFacetWrapper.ts +98 -0
  77. package/src/operations/wrappers/createAllWrapper.ts +103 -0
  78. package/src/operations/wrappers/createCreateWrapper.ts +117 -0
  79. package/src/operations/wrappers/createFacetWrapper.ts +97 -0
  80. package/src/operations/wrappers/createFindOneWrapper.ts +105 -0
  81. package/src/operations/wrappers/createFindWrapper.ts +105 -0
  82. package/src/operations/wrappers/createGetWrapper.ts +96 -0
  83. package/src/operations/wrappers/createOneWrapper.ts +128 -0
  84. package/src/operations/wrappers/createRemoveWrapper.ts +91 -0
  85. package/src/operations/wrappers/createUpdateWrapper.ts +106 -0
  86. package/src/operations/wrappers/createUpsertWrapper.ts +108 -0
  87. package/src/operations/wrappers/index.ts +39 -0
  88. package/src/operations/wrappers/types.ts +63 -0
  89. package/src/validation/ItemValidator.ts +131 -0
  90. package/src/validation/KeyValidator.ts +365 -0
  91. package/src/validation/LocationValidator.ts +136 -0
  92. package/src/validation/QueryValidator.ts +250 -0
  93. package/src/validation/index.ts +32 -0
  94. package/src/validation/types.ts +45 -0
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Query and parameter validation
3
+ *
4
+ * Validates ItemQuery and OperationParams structures.
5
+ */
6
+ import type { ItemQuery } from "../item/ItemQuery";
7
+ import type { OperationParams } from "../operations/Operations";
8
+ /**
9
+ * Validates that a query parameter is a valid ItemQuery object.
10
+ *
11
+ * @param query - The query to validate
12
+ * @param operation - The operation name (for error messages)
13
+ * @throws Error if query is invalid
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * validateQuery({ filter: { status: 'active' } }, 'one');
18
+ * ```
19
+ */
20
+ export declare const validateQuery: (query: ItemQuery | undefined, operation: string) => void;
21
+ /**
22
+ * Validates that operation parameters are valid.
23
+ *
24
+ * @param params - The parameters to validate
25
+ * @param operation - The operation name (for error messages)
26
+ * @throws Error if parameters are invalid
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * validateOperationParams({ email: 'test@example.com' }, 'find');
31
+ * ```
32
+ */
33
+ export declare const validateOperationParams: (params: OperationParams | undefined, operation: string) => void;
34
+ /**
35
+ * Validates that a finder name is valid.
36
+ *
37
+ * @param finder - The finder name to validate
38
+ * @param operation - The operation name (for error messages)
39
+ * @throws Error if finder name is invalid
40
+ */
41
+ export declare const validateFinderName: (finder: string | undefined, operation: string) => void;
42
+ /**
43
+ * Validates that an action name is valid.
44
+ *
45
+ * @param action - The action name to validate
46
+ * @param operation - The operation name (for error messages)
47
+ * @throws Error if action name is invalid
48
+ */
49
+ export declare const validateActionName: (action: string | undefined, operation: string) => void;
50
+ /**
51
+ * Validates that a facet name is valid.
52
+ *
53
+ * @param facet - The facet name to validate
54
+ * @param operation - The operation name (for error messages)
55
+ * @throws Error if facet name is invalid
56
+ */
57
+ export declare const validateFacetName: (facet: string | undefined, operation: string) => void;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Validation module
3
+ *
4
+ * Provides centralized validation functions for:
5
+ * - Location arrays (LocKeyArray validation against Coordinate hierarchy)
6
+ * - Keys (PriKey, ComKey validation)
7
+ * - Items (Item key type validation)
8
+ * - Queries (ItemQuery validation)
9
+ * - Operation parameters (OperationParams validation)
10
+ */
11
+ export type { ValidationOptions, ValidationResult } from './types';
12
+ export { validateLocations, isValidLocations } from './LocationValidator';
13
+ export { validateKey, validatePriKey, validateComKey } from './KeyValidator';
14
+ export { validatePK, validateKeys } from './ItemValidator';
15
+ export { validateQuery, validateOperationParams, validateFinderName, validateActionName, validateFacetName } from './QueryValidator';
@@ -0,0 +1,501 @@
1
+ // src/logger.ts
2
+ import Logging from "@fjell/logging";
3
+ var LibLogger = Logging.getLogger("@fjell/core");
4
+ var logger_default = LibLogger;
5
+
6
+ // src/validation/LocationValidator.ts
7
+ var logger = logger_default.get("validation", "LocationValidator");
8
+ var validateLocations = (locations, coordinate, operation) => {
9
+ if (!locations || locations.length === 0) {
10
+ return;
11
+ }
12
+ const keyTypeArray = coordinate.kta;
13
+ const expectedLocationTypes = keyTypeArray.slice(1);
14
+ const actualLocationTypes = locations.map((loc) => loc.kt);
15
+ logger.debug(`Validating locations for ${operation}`, {
16
+ expected: expectedLocationTypes,
17
+ actual: actualLocationTypes,
18
+ coordinate: keyTypeArray
19
+ });
20
+ if (actualLocationTypes.length > expectedLocationTypes.length) {
21
+ logger.error("Location key array has too many elements", {
22
+ expected: expectedLocationTypes.length,
23
+ actual: actualLocationTypes.length,
24
+ expectedTypes: expectedLocationTypes,
25
+ actualTypes: actualLocationTypes,
26
+ coordinate,
27
+ operation
28
+ });
29
+ throw new Error(
30
+ `Invalid location key array for ${operation}: Expected at most ${expectedLocationTypes.length} location keys (hierarchy: [${expectedLocationTypes.join(", ")}]), but received ${actualLocationTypes.length} (types: [${actualLocationTypes.join(", ")}])`
31
+ );
32
+ }
33
+ for (let i = 0; i < actualLocationTypes.length; i++) {
34
+ if (expectedLocationTypes[i] !== actualLocationTypes[i]) {
35
+ logger.error("Location key array order mismatch", {
36
+ position: i,
37
+ expected: expectedLocationTypes[i],
38
+ actual: actualLocationTypes[i],
39
+ expectedHierarchy: expectedLocationTypes,
40
+ actualOrder: actualLocationTypes,
41
+ coordinate,
42
+ operation
43
+ });
44
+ throw new Error(
45
+ `Invalid location key array order for ${operation}: At position ${i}, expected key type "${expectedLocationTypes[i]}" but received "${actualLocationTypes[i]}". Location keys must be ordered according to the hierarchy: [${expectedLocationTypes.join(", ")}]. Received order: [${actualLocationTypes.join(", ")}]`
46
+ );
47
+ }
48
+ }
49
+ logger.debug(`Location key array validation passed for ${operation}`, { locations });
50
+ };
51
+ var isValidLocations = (locations, coordinate, operation) => {
52
+ try {
53
+ validateLocations(locations, coordinate, operation);
54
+ return { valid: true };
55
+ } catch (error) {
56
+ return {
57
+ valid: false,
58
+ error: error instanceof Error ? error.message : String(error)
59
+ };
60
+ }
61
+ };
62
+
63
+ // src/key/KUtils.ts
64
+ var logger2 = logger_default.get("KUtils");
65
+ var isComKey = (key) => {
66
+ logger2.trace("isComKey", { key });
67
+ return key !== void 0 && (key.pk !== void 0 && key.kt !== void 0) && (key.loc !== void 0 && Array.isArray(key.loc));
68
+ };
69
+ var isPriKey = (key) => {
70
+ logger2.trace("isPriKey", { key });
71
+ return key !== void 0 && (key.pk !== void 0 && key.kt !== void 0) && key.loc === void 0;
72
+ };
73
+ var toKeyTypeArray = (ik) => {
74
+ logger2.trace("toKeyTypeArray", { ik });
75
+ if (isComKey(ik)) {
76
+ const ck = ik;
77
+ return [ck.kt, ...ck.loc.map((l) => l.kt)];
78
+ } else {
79
+ return [ik.kt];
80
+ }
81
+ };
82
+
83
+ // src/validation/KeyValidator.ts
84
+ var logger3 = logger_default.get("validation", "KeyValidator");
85
+ var validateLocationKeyOrder = (key, coordinate, operation) => {
86
+ const keyTypeArray = coordinate.kta;
87
+ const expectedLocationTypes = keyTypeArray.slice(1);
88
+ const actualLocationTypes = key.loc.map((loc) => loc.kt);
89
+ if (expectedLocationTypes.length !== actualLocationTypes.length) {
90
+ logger3.error("Location key array length mismatch", {
91
+ expected: expectedLocationTypes.length,
92
+ actual: actualLocationTypes.length,
93
+ key,
94
+ coordinate,
95
+ operation
96
+ });
97
+ const expectedOrder = expectedLocationTypes.map(
98
+ (kt, i) => ` [${i}] { kt: '${kt}', lk: <value> }`
99
+ ).join("\n");
100
+ const actualOrder = key.loc.map(
101
+ (loc, i) => ` [${i}] { kt: '${loc.kt}', lk: ${JSON.stringify(loc.lk)} }`
102
+ ).join("\n");
103
+ throw new Error(
104
+ `Location key array length mismatch for ${operation} operation.
105
+
106
+ Expected ${expectedLocationTypes.length} location keys but received ${actualLocationTypes.length}.
107
+
108
+ Expected location key order for '${keyTypeArray[0]}':
109
+ ${expectedOrder}
110
+
111
+ Received location key order:
112
+ ${actualOrder}`
113
+ );
114
+ }
115
+ for (let i = 0; i < expectedLocationTypes.length; i++) {
116
+ if (expectedLocationTypes[i] !== actualLocationTypes[i]) {
117
+ logger3.error("Location key array order mismatch", {
118
+ position: i,
119
+ expected: expectedLocationTypes[i],
120
+ actual: actualLocationTypes[i],
121
+ key,
122
+ coordinate,
123
+ operation
124
+ });
125
+ const expectedOrder = expectedLocationTypes.map(
126
+ (kt, i2) => ` [${i2}] { kt: '${kt}', lk: <value> }`
127
+ ).join("\n");
128
+ const actualOrder = key.loc.map(
129
+ (loc, i2) => ` [${i2}] { kt: '${loc.kt}', lk: ${JSON.stringify(loc.lk)} }`
130
+ ).join("\n");
131
+ throw new Error(
132
+ `Location key array order mismatch for ${operation} operation.
133
+
134
+ At position ${i}, expected key type "${expectedLocationTypes[i]}" but received "${actualLocationTypes[i]}".
135
+
136
+ Expected location key order for '${keyTypeArray[0]}':
137
+ ${expectedOrder}
138
+
139
+ Received location key order:
140
+ ${actualOrder}
141
+
142
+ Tip: Location keys must be ordered according to the hierarchy: [${expectedLocationTypes.join(", ")}]`
143
+ );
144
+ }
145
+ }
146
+ };
147
+ var validateKey = (key, coordinate, operation) => {
148
+ logger3.debug(`Validating key for ${operation}`, { key, coordinate: coordinate.kta });
149
+ if (!key || key === null) {
150
+ throw new Error(
151
+ `Invalid key structure for ${operation} operation.
152
+
153
+ The provided key is null or undefined.
154
+
155
+ Valid key formats:
156
+ PriKey: { kt: string, pk: string|number }
157
+ ComKey: { kt: string, pk: string|number, loc: Array<{ kt: string, lk: string|number }> }`
158
+ );
159
+ }
160
+ const isCompositeLibrary = coordinate.kta.length > 1;
161
+ const keyIsComposite = isComKey(key);
162
+ const keyIsPrimary = isPriKey(key);
163
+ if (isCompositeLibrary && !keyIsComposite) {
164
+ logger3.error(`Composite library received primary key in ${operation}`, { key, coordinate });
165
+ const keyTypeArray = coordinate.kta;
166
+ throw new Error(
167
+ `Invalid key type for ${operation} operation.
168
+
169
+ This is a composite item library. You must provide a ComKey with location keys.
170
+
171
+ Expected: ComKey with format:
172
+ {
173
+ kt: '${keyTypeArray[0]}',
174
+ pk: string|number,
175
+ loc: [
176
+ ` + keyTypeArray.slice(1).map((kt) => ` { kt: '${kt}', lk: string|number }`).join(",\n") + `
177
+ ]
178
+ }
179
+
180
+ Received: PriKey with format:
181
+ ${JSON.stringify(key, null, 2)}
182
+
183
+ Example correct usage:
184
+ library.operations.${operation}({
185
+ kt: '${keyTypeArray[0]}',
186
+ pk: 'item-id',
187
+ loc: [${keyTypeArray.slice(1).map((kt) => `{ kt: '${kt}', lk: 'parent-id' }`).join(", ")}]
188
+ })`
189
+ );
190
+ }
191
+ if (!isCompositeLibrary && keyIsComposite) {
192
+ logger3.error(`Primary library received composite key in ${operation}`, { key, coordinate });
193
+ const keyTypeArray = coordinate.kta;
194
+ throw new Error(
195
+ `Invalid key type for ${operation} operation.
196
+
197
+ This is a primary item library. You should provide a PriKey without location keys.
198
+
199
+ Expected: PriKey with format:
200
+ { kt: '${keyTypeArray[0]}', pk: string|number }
201
+
202
+ Received: ComKey with format:
203
+ ${JSON.stringify(key, null, 2)}
204
+
205
+ Example correct usage:
206
+ library.operations.${operation}({ kt: '${keyTypeArray[0]}', pk: 'item-id' })`
207
+ );
208
+ }
209
+ if (!keyIsPrimary && !keyIsComposite) {
210
+ logger3.error(`Invalid key structure in ${operation}`, { key, coordinate });
211
+ throw new Error(
212
+ `Invalid key structure for ${operation} operation.
213
+
214
+ The provided key does not match PriKey or ComKey format.
215
+
216
+ Received:
217
+ ${JSON.stringify(key, null, 2)}
218
+
219
+ Valid key formats:
220
+ PriKey: { kt: string, pk: string|number }
221
+ ComKey: { kt: string, pk: string|number, loc: Array<{ kt: string, lk: string|number }> }`
222
+ );
223
+ }
224
+ const expectedKeyType = coordinate.kta[0];
225
+ if (key.kt !== expectedKeyType) {
226
+ logger3.error(`Key type mismatch in ${operation}`, {
227
+ expected: expectedKeyType,
228
+ received: key.kt,
229
+ key,
230
+ coordinate
231
+ });
232
+ throw new Error(
233
+ `Invalid key type for ${operation} operation.
234
+
235
+ Expected key type: '${expectedKeyType}'
236
+ Received key type: '${key.kt}'
237
+
238
+ Example correct usage:
239
+ library.operations.${operation}({ kt: '${expectedKeyType}', pk: 'item-id', ... })`
240
+ );
241
+ }
242
+ if (keyIsComposite) {
243
+ const comKey = key;
244
+ if (comKey.loc.length === 0) {
245
+ logger3.debug(`Empty loc array detected in ${operation} - will search across all locations`, { key });
246
+ } else {
247
+ validateLocationKeyOrder(comKey, coordinate, operation);
248
+ }
249
+ }
250
+ logger3.debug(`Key validation passed for ${operation}`, { key });
251
+ };
252
+ var validatePriKey = (key, expectedType, operation) => {
253
+ if (!key || key === null) {
254
+ throw new Error(`[${operation}] PriKey is undefined or null`);
255
+ }
256
+ if (!key.kt) {
257
+ throw new Error(`[${operation}] PriKey is missing 'kt' field`);
258
+ }
259
+ if (key.kt !== expectedType) {
260
+ throw new Error(
261
+ `[${operation}] PriKey has incorrect type.
262
+ Expected: '${expectedType}'
263
+ Received: '${key.kt}'`
264
+ );
265
+ }
266
+ if (typeof key.pk === "undefined" || key.pk === null) {
267
+ throw new Error(`[${operation}] PriKey is missing 'pk' field`);
268
+ }
269
+ };
270
+ var validateComKey = (key, coordinate, operation) => {
271
+ if (!key || key === null) {
272
+ throw new Error(`[${operation}] ComKey is undefined or null`);
273
+ }
274
+ if (!key.kt) {
275
+ throw new Error(`[${operation}] ComKey is missing 'kt' field`);
276
+ }
277
+ if (key.kt !== coordinate.kta[0]) {
278
+ throw new Error(
279
+ `[${operation}] ComKey has incorrect type.
280
+ Expected: '${coordinate.kta[0]}'
281
+ Received: '${key.kt}'`
282
+ );
283
+ }
284
+ if (typeof key.pk === "undefined" || key.pk === null) {
285
+ throw new Error(`[${operation}] ComKey is missing 'pk' field`);
286
+ }
287
+ if (!Array.isArray(key.loc)) {
288
+ throw new Error(`[${operation}] ComKey is missing or invalid 'loc' field (must be an array)`);
289
+ }
290
+ if (key.loc.length > 0) {
291
+ validateLocationKeyOrder(key, coordinate, operation);
292
+ }
293
+ };
294
+
295
+ // src/validation/ItemValidator.ts
296
+ var logger4 = logger_default.get("validation", "ItemValidator");
297
+ var validatePKForItem = (item, pkType) => {
298
+ if (!item) {
299
+ logger4.error("Validating PK, Item is undefined", { item });
300
+ throw new Error("Validating PK, Item is undefined");
301
+ }
302
+ if (!item.key) {
303
+ logger4.error("Validating PK, Item does not have a key", { item });
304
+ throw new Error("Validating PK, Item does not have a key");
305
+ }
306
+ const keyTypeArray = toKeyTypeArray(item.key);
307
+ if (keyTypeArray[0] !== pkType) {
308
+ logger4.error("Key Type Array Mismatch", { keyTypeArray, pkType });
309
+ throw new Error(`Item does not have the correct primary key type. Expected ${pkType}, got ${keyTypeArray[0]}`);
310
+ }
311
+ return item;
312
+ };
313
+ var validatePK = (input, pkType) => {
314
+ logger4.trace("Checking Return Type", { input });
315
+ if (Array.isArray(input)) {
316
+ return input.map((item) => validatePKForItem(item, pkType));
317
+ }
318
+ return validatePKForItem(input, pkType);
319
+ };
320
+ var validateKeys = (item, keyTypes) => {
321
+ logger4.trace("Checking Return Type", { item });
322
+ if (!item) {
323
+ throw new Error("validating keys, item is undefined");
324
+ }
325
+ if (!item.key) {
326
+ throw new Error("validating keys, item does not have a key: " + JSON.stringify(item));
327
+ }
328
+ const keyTypeArray = toKeyTypeArray(item.key);
329
+ if (keyTypeArray.length !== keyTypes.length) {
330
+ throw new Error(`Item does not have the correct number of keys. Expected ${keyTypes.length}, but got ${keyTypeArray.length}`);
331
+ }
332
+ const match = JSON.stringify(keyTypeArray) === JSON.stringify(keyTypes);
333
+ if (!match) {
334
+ logger4.error("Key Type Array Mismatch", { keyTypeArray, thisKeyTypes: keyTypes });
335
+ throw new Error(`Item does not have the correct key types. Expected [${keyTypes.join(", ")}], but got [${keyTypeArray.join(", ")}]`);
336
+ }
337
+ return item;
338
+ };
339
+
340
+ // src/validation/QueryValidator.ts
341
+ var logger5 = logger_default.get("validation", "QueryValidator");
342
+ var validateQuery = (query, operation) => {
343
+ if (typeof query === "undefined" || query === null) {
344
+ return;
345
+ }
346
+ if (typeof query !== "object") {
347
+ logger5.error(`Invalid query type for ${operation}`, { query, type: typeof query });
348
+ throw new Error(
349
+ `[${operation}] Invalid query parameter.
350
+
351
+ Expected: object or undefined
352
+ Received: ${typeof query}
353
+
354
+ Example valid queries:
355
+ {}
356
+ { filter: { status: 'active' } }
357
+ { limit: 10, sort: { field: 'name', order: 'asc' } }`
358
+ );
359
+ }
360
+ if (Array.isArray(query)) {
361
+ logger5.error(`Query cannot be an array for ${operation}`, { query });
362
+ throw new Error(
363
+ `[${operation}] Invalid query parameter.
364
+
365
+ Query cannot be an array.
366
+ Received: ${JSON.stringify(query)}`
367
+ );
368
+ }
369
+ logger5.debug(`Query validation passed for ${operation}`, { query });
370
+ };
371
+ var validateOperationParams = (params, operation) => {
372
+ if (typeof params === "undefined") {
373
+ return;
374
+ }
375
+ if (params === null) {
376
+ logger5.error(`Params cannot be null for ${operation}`, { params });
377
+ throw new Error(
378
+ `[${operation}] Invalid operation parameters.
379
+
380
+ Parameters cannot be null.
381
+ Expected: object or undefined
382
+ Received: null
383
+
384
+ Example valid parameters:
385
+ {}
386
+ { email: 'user@example.com' }
387
+ { status: 'active', limit: 10 }`
388
+ );
389
+ }
390
+ if (typeof params !== "object") {
391
+ logger5.error(`Invalid params type for ${operation}`, { params, type: typeof params });
392
+ throw new Error(
393
+ `[${operation}] Invalid operation parameters.
394
+
395
+ Expected: object or undefined
396
+ Received: ${typeof params}
397
+
398
+ Example valid parameters:
399
+ {}
400
+ { email: 'user@example.com' }
401
+ { status: 'active', limit: 10 }`
402
+ );
403
+ }
404
+ if (Array.isArray(params)) {
405
+ logger5.error(`Params cannot be an array for ${operation}`, { params });
406
+ throw new Error(
407
+ `[${operation}] Invalid operation parameters.
408
+
409
+ Parameters cannot be an array.
410
+ Received: ${JSON.stringify(params)}`
411
+ );
412
+ }
413
+ for (const [key, value] of Object.entries(params)) {
414
+ const valueType = typeof value;
415
+ const isValidType = valueType === "string" || valueType === "number" || valueType === "boolean" || value instanceof Date || Array.isArray(value) && value.every(
416
+ (v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean" || v instanceof Date
417
+ );
418
+ if (!isValidType) {
419
+ logger5.error(`Invalid param value type for ${operation}`, { key, value, valueType });
420
+ throw new Error(
421
+ `[${operation}] Invalid value type for parameter "${key}".
422
+
423
+ Allowed types: string, number, boolean, Date, or arrays of these types
424
+ Received: ${valueType}
425
+ Value: ${JSON.stringify(value)}`
426
+ );
427
+ }
428
+ }
429
+ logger5.debug(`Operation params validation passed for ${operation}`, { params });
430
+ };
431
+ var validateFinderName = (finder, operation) => {
432
+ if (!finder || typeof finder !== "string") {
433
+ logger5.error(`Invalid finder name for ${operation}`, { finder, type: typeof finder });
434
+ throw new Error(
435
+ `[${operation}] Finder name must be a non-empty string.
436
+
437
+ Received: ${JSON.stringify(finder)}`
438
+ );
439
+ }
440
+ if (finder.trim().length === 0) {
441
+ logger5.error(`Empty finder name for ${operation}`, { finder });
442
+ throw new Error(
443
+ `[${operation}] Finder name cannot be empty or whitespace only.
444
+
445
+ Received: "${finder}"`
446
+ );
447
+ }
448
+ logger5.debug(`Finder name validation passed for ${operation}`, { finder });
449
+ };
450
+ var validateActionName = (action, operation) => {
451
+ if (!action || typeof action !== "string") {
452
+ logger5.error(`Invalid action name for ${operation}`, { action, type: typeof action });
453
+ throw new Error(
454
+ `[${operation}] Action name must be a non-empty string.
455
+
456
+ Received: ${JSON.stringify(action)}`
457
+ );
458
+ }
459
+ if (action.trim().length === 0) {
460
+ logger5.error(`Empty action name for ${operation}`, { action });
461
+ throw new Error(
462
+ `[${operation}] Action name cannot be empty or whitespace only.
463
+
464
+ Received: "${action}"`
465
+ );
466
+ }
467
+ logger5.debug(`Action name validation passed for ${operation}`, { action });
468
+ };
469
+ var validateFacetName = (facet, operation) => {
470
+ if (!facet || typeof facet !== "string") {
471
+ logger5.error(`Invalid facet name for ${operation}`, { facet, type: typeof facet });
472
+ throw new Error(
473
+ `[${operation}] Facet name must be a non-empty string.
474
+
475
+ Received: ${JSON.stringify(facet)}`
476
+ );
477
+ }
478
+ if (facet.trim().length === 0) {
479
+ logger5.error(`Empty facet name for ${operation}`, { facet });
480
+ throw new Error(
481
+ `[${operation}] Facet name cannot be empty or whitespace only.
482
+
483
+ Received: "${facet}"`
484
+ );
485
+ }
486
+ logger5.debug(`Facet name validation passed for ${operation}`, { facet });
487
+ };
488
+ export {
489
+ isValidLocations,
490
+ validateActionName,
491
+ validateComKey,
492
+ validateFacetName,
493
+ validateFinderName,
494
+ validateKey,
495
+ validateKeys,
496
+ validateLocations,
497
+ validateOperationParams,
498
+ validatePK,
499
+ validatePriKey,
500
+ validateQuery
501
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Shared validation types and options
3
+ */
4
+ /**
5
+ * Options for validation functions
6
+ */
7
+ export interface ValidationOptions {
8
+ /**
9
+ * Custom error message prefix
10
+ */
11
+ errorPrefix?: string;
12
+ /**
13
+ * Whether to allow undefined (treated as empty array for location validation)
14
+ */
15
+ allowUndefined?: boolean;
16
+ /**
17
+ * Whether to throw errors or return validation results
18
+ * @default true
19
+ */
20
+ throwOnError?: boolean;
21
+ }
22
+ /**
23
+ * Result of a validation operation (for non-throwing mode)
24
+ */
25
+ export interface ValidationResult {
26
+ /**
27
+ * Whether validation passed
28
+ */
29
+ valid: boolean;
30
+ /**
31
+ * Error message if validation failed
32
+ */
33
+ error?: string;
34
+ /**
35
+ * Detailed validation context
36
+ */
37
+ context?: Record<string, unknown>;
38
+ }
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.48",
4
+ "version": "4.4.50",
5
5
  "keywords": [
6
6
  "core",
7
7
  "fjell"
@@ -15,6 +15,11 @@
15
15
  "import": "./dist/index.js",
16
16
  "default": "./dist/index.js"
17
17
  },
18
+ "./validation": {
19
+ "types": "./dist/validation/index.d.ts",
20
+ "import": "./dist/validation/index.js",
21
+ "default": "./dist/validation/index.js"
22
+ },
18
23
  "./browser": {
19
24
  "types": "./dist/index.d.ts",
20
25
  "import": "./dist/index.js"
@@ -36,7 +41,7 @@
36
41
  "docs:test": "cd docs && npm run test"
37
42
  },
38
43
  "dependencies": {
39
- "@fjell/logging": "^4.4.47",
44
+ "@fjell/logging": "^4.4.49",
40
45
  "deepmerge": "^4.3.1",
41
46
  "luxon": "^3.7.1"
42
47
  },
@@ -0,0 +1,35 @@
1
+ import type { ItemTypeArray } from "./keys";
2
+ import LibLogger from "./logger";
3
+
4
+ const logger = LibLogger.get("Coordinate");
5
+
6
+ export interface Coordinate<
7
+ S extends string,
8
+ L1 extends string = never,
9
+ L2 extends string = never,
10
+ L3 extends string = never,
11
+ L4 extends string = never,
12
+ L5 extends string = never,
13
+ > {
14
+ kta: ItemTypeArray<S, L1, L2, L3, L4, L5>;
15
+ scopes: string[];
16
+ toString: () => string;
17
+ }
18
+
19
+ export const createCoordinate = <
20
+ S extends string,
21
+ L1 extends string = never,
22
+ L2 extends string = never,
23
+ L3 extends string = never,
24
+ L4 extends string = never,
25
+ L5 extends string = never,
26
+ >(kta: ItemTypeArray<S, L1, L2, L3, L4, L5> | S, scopes: string[] = []): Coordinate<S, L1, L2, L3, L4, L5> => {
27
+ const ktArray = Array.isArray(kta) ? kta : [kta];
28
+ const toString = () => {
29
+ logger.debug("toString", { kta, scopes });
30
+ return `${ktArray.join(', ')} - ${scopes.join(', ')}`;
31
+ }
32
+ logger.debug("createCoordinate", { kta: ktArray, scopes, toString });
33
+ return { kta: ktArray as ItemTypeArray<S, L1, L2, L3, L4, L5>, scopes, toString };
34
+ }
35
+