@fjell/core 4.4.42 → 4.4.44

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/index.js CHANGED
@@ -413,7 +413,7 @@ var AItemService = class {
413
413
 
414
414
  // src/item/ItemQuery.ts
415
415
  var isCondition = (condition) => {
416
- return (typeof condition.column === "string" && (Array.isArray(condition.value) && condition.value.every((item) => typeof item === "string")) || Array.isArray(condition.value) && condition.value.every((item) => typeof item === "number") || typeof condition.value === "string" || typeof condition.value === "number" || typeof condition.value === "boolean" || condition.value instanceof Date) && (condition.operator ? typeof condition.operator === "string" : true);
416
+ return (typeof condition.column === "string" && (Array.isArray(condition.value) && condition.value.every((item) => typeof item === "string")) || Array.isArray(condition.value) && condition.value.every((item) => typeof item === "number") || typeof condition.value === "string" || typeof condition.value === "number" || typeof condition.value === "boolean" || condition.value instanceof Date || condition.value === null) && (condition.operator ? typeof condition.operator === "string" : true);
417
417
  };
418
418
 
419
419
  // src/item/IQFactory.ts
@@ -477,7 +477,7 @@ var IQFactory = class _IQFactory {
477
477
  this.query.refs = {};
478
478
  }
479
479
  const refName = name || kt;
480
- this.query.refs[refName] = cPK(pk, kt);
480
+ this.query.refs[refName] = { key: cPK(pk, kt) };
481
481
  return this;
482
482
  }
483
483
  condition(column, value, operator = "==") {
@@ -596,7 +596,10 @@ var paramsToQuery = (params) => {
596
596
  var isRefQueryMatch = (refKey, queryRef, references) => {
597
597
  logger3.trace("doesRefMatch", { queryRef, references });
598
598
  logger3.debug("Comparing Ref", { refKey, itemRef: references[refKey], queryRef });
599
- return isItemKeyEqual(queryRef, references[refKey]);
599
+ if (!references[refKey]) {
600
+ return false;
601
+ }
602
+ return isItemKeyEqual(queryRef, references[refKey].key);
600
603
  };
601
604
  var isCompoundConditionQueryMatch = (queryCondition, item) => {
602
605
  if (queryCondition.compoundType === "AND") {
@@ -617,6 +620,17 @@ var isConditionQueryMatch = (queryCondition, item) => {
617
620
  return false;
618
621
  }
619
622
  logger3.debug("Comparing Condition", { propKey, itemProp: item[propKey], queryCondition });
623
+ if (queryCondition.value === null) {
624
+ if (queryCondition.operator === "==") {
625
+ return item[propKey] === null;
626
+ } else if (queryCondition.operator === "!=") {
627
+ return item[propKey] !== null;
628
+ } else {
629
+ throw new Error(
630
+ `Operator ${queryCondition.operator} cannot be used with null value. Use '==' for null checks or '!=' for not-null checks.`
631
+ );
632
+ }
633
+ }
620
634
  let result = false;
621
635
  switch (queryCondition.operator) {
622
636
  case "==":
@@ -655,6 +669,9 @@ var isConditionQueryMatch = (queryCondition, item) => {
655
669
  var isAggQueryMatch = (aggKey, aggQuery, agg) => {
656
670
  const aggItem = agg.item;
657
671
  logger3.debug("Comparing Agg", { aggKey, aggItem, aggQuery });
672
+ if (!aggItem) {
673
+ return false;
674
+ }
658
675
  return isQueryMatch(aggItem, aggQuery);
659
676
  };
660
677
  var isEventQueryMatch = (eventKey, eventQuery, item) => {
@@ -684,7 +701,7 @@ var isQueryMatch = (item, query) => {
684
701
  if (query.refs && item.refs) {
685
702
  for (const key in query.refs) {
686
703
  const queryRef = query.refs[key];
687
- if (!isRefQueryMatch(key, queryRef, item.refs)) return false;
704
+ if (!isRefQueryMatch(key, queryRef.key, item.refs)) return false;
688
705
  }
689
706
  } else if (query.refs && !item.refs) {
690
707
  logger3.debug("Query contains refs but item does not have refs", { query, item });
@@ -703,10 +720,20 @@ var isQueryMatch = (item, query) => {
703
720
  if (query.aggs && item.aggs) {
704
721
  for (const key in query.aggs) {
705
722
  const aggQuery = query.aggs[key];
706
- if (item.aggs[key] && !isAggQueryMatch(key, aggQuery, item.aggs[key])) return false;
723
+ if (item.aggs[key]) {
724
+ let hasMatch = false;
725
+ for (const aggRecord of item.aggs[key]) {
726
+ if (isAggQueryMatch(key, aggQuery, aggRecord)) {
727
+ hasMatch = true;
728
+ break;
729
+ }
730
+ }
731
+ if (!hasMatch) return false;
732
+ } else {
733
+ return false;
734
+ }
707
735
  }
708
- }
709
- if (query.aggs && !item.aggs) {
736
+ } else if (query.aggs && !item.aggs) {
710
737
  logger3.debug("Query contains aggs but item does not have aggs", { query, item });
711
738
  return false;
712
739
  }
@@ -747,11 +774,11 @@ var abbrevQuery = (query) => {
747
774
  return abbrev.join(" ");
748
775
  };
749
776
  var abbrevRef = (key, ref) => {
750
- if (isPriKey(ref)) {
751
- const priKey = ref;
777
+ if (isPriKey(ref.key)) {
778
+ const priKey = ref.key;
752
779
  return `R(${key},${priKey.kt},${priKey.pk})`;
753
780
  } else {
754
- const comKey = ref;
781
+ const comKey = ref.key;
755
782
  return `R(${key},${JSON.stringify(comKey)})`;
756
783
  }
757
784
  };
@@ -9,7 +9,7 @@ export declare class IQFactory {
9
9
  limit(limit: number): this;
10
10
  offset(offset: number): this;
11
11
  pk(kt: string, pk: string, name?: string): this;
12
- condition(column: string, value: string[] | string | number[] | number | boolean | Date, operator?: ConditionOperator): this;
12
+ condition(column: string, value: string[] | string | number[] | number | boolean | Date | null, operator?: ConditionOperator): this;
13
13
  static all(): IQFactory;
14
14
  static orderBy(field: string, direction?: OrderDirection): IQFactory;
15
15
  static agg(name: string, query: ItemQuery): IQFactory;
@@ -17,7 +17,7 @@ export declare class IQFactory {
17
17
  static limit(limit: number): IQFactory;
18
18
  static offset(offset: number): IQFactory;
19
19
  static pk(kt: string, pk: string, name?: string): IQFactory;
20
- static condition(column: string, value: string[] | string | number[] | number | boolean | Date, operator?: ConditionOperator): IQFactory;
20
+ static condition(column: string, value: string[] | string | number[] | number | boolean | Date | null, operator?: ConditionOperator): IQFactory;
21
21
  static conditions(conditions: Condition[], compoundType?: CompoundType): IQFactory;
22
22
  toQuery(): ItemQuery;
23
23
  }
@@ -1,5 +1,4 @@
1
- import { Item } from "../items";
2
- import { ComKey, PriKey } from "../keys";
1
+ import { Item, Reference } from "../items";
3
2
  import { CompoundCondition, Condition, ItemQuery, QueryParams } from "./ItemQuery";
4
3
  /**
5
4
  * When we query or search, we're sending a GET request. This converts everything in ItemQuery into a flat
@@ -29,7 +28,7 @@ export declare const queryToParams: (query: ItemQuery) => QueryParams;
29
28
  export declare const paramsToQuery: (params: QueryParams) => ItemQuery;
30
29
  export declare const isQueryMatch: <S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never>(item: Item<S, L1, L2, L3, L4, L5>, query: ItemQuery) => boolean;
31
30
  export declare const abbrevQuery: (query: ItemQuery | null | undefined) => string;
32
- export declare const abbrevRef: <S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never>(key: string, ref: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>) => string;
31
+ export declare const abbrevRef: <S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never>(key: string, ref: Reference<S, L1, L2, L3, L4, L5>) => string;
33
32
  export declare const abbrevAgg: (key: string, agg: ItemQuery) => string;
34
33
  export declare const abbrevCompoundCondition: (compoundCondition: CompoundCondition) => string;
35
34
  export declare const abbrevCondition: (condition: Condition | CompoundCondition) => string;
@@ -10,7 +10,7 @@ export type ConditionOperator = '==' | '!=' | '>' | '>=' | '<' | '<=' | 'in' | '
10
10
  */
11
11
  export type Condition = {
12
12
  column: string;
13
- value: string[] | string | number[] | number | boolean | Date;
13
+ value: string[] | string | number[] | number | boolean | Date | null;
14
14
  operator: ConditionOperator;
15
15
  };
16
16
  /**
package/dist/items.d.ts CHANGED
@@ -29,16 +29,20 @@ export interface Deletable extends Partial<Evented> {
29
29
  deleted: ItemEvent;
30
30
  }
31
31
  export type ManagedEvents = Timestamped & Deletable;
32
- export type References = Record<string, ComKey<string, string | never, string | never, string | never, string | never, string | never> | PriKey<string>>;
33
- export type ReferenceItem<S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> = {
32
+ export type Reference<S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> = {
34
33
  key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>;
35
- item: Item<S, L1, L2, L3, L4, L5>;
34
+ item?: Item<S, L1, L2, L3, L4, L5>;
35
+ };
36
+ export type ReferenceItem<S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> = Reference<S, L1, L2, L3, L4, L5>;
37
+ export type References = Record<string, Reference<any, any, any, any, any, any>>;
38
+ export type Aggregation<S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> = {
39
+ key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>;
40
+ item?: Item<S, L1, L2, L3, L4, L5>;
36
41
  };
37
- export type ReferenceItems = Record<string, ReferenceItem<string, string | never, string | never, string | never, string | never, string | never>>;
38
42
  export interface Item<S extends string = never, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> extends Identified<S, L1, L2, L3, L4, L5> {
39
43
  events: ManagedEvents & Evented;
40
- aggs?: ReferenceItems;
41
- refs?: References;
44
+ aggs?: Record<string, Aggregation<any, any, any, any, any, any>[]>;
45
+ refs?: Record<string, Reference<any, any, any, any, any, any>>;
42
46
  [key: string]: any;
43
47
  }
44
48
  /**
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.42",
4
+ "version": "4.4.44",
5
5
  "keywords": [
6
6
  "core",
7
7
  "fjell"
@@ -82,13 +82,13 @@ export class IQFactory {
82
82
  this.query.refs = {};
83
83
  }
84
84
  const refName = name || kt;
85
- this.query.refs[refName] = cPK<string>(pk, kt);
85
+ this.query.refs[refName] = { key: cPK<string>(pk, kt) };
86
86
  return this;
87
87
  }
88
88
 
89
89
  public condition(
90
90
  column: string,
91
- value: string[] | string | number[] | number | boolean | Date,
91
+ value: string[] | string | number[] | number | boolean | Date | null,
92
92
  operator: ConditionOperator = '==',
93
93
  ) {
94
94
  const condition: Condition = { column, value, operator };
@@ -145,7 +145,7 @@ export class IQFactory {
145
145
 
146
146
  public static condition(
147
147
  column: string,
148
- value: string[] | string | number[] | number | boolean | Date,
148
+ value: string[] | string | number[] | number | boolean | Date | null,
149
149
  operator: ConditionOperator = '=='
150
150
  ) {
151
151
  const iqFactory = new IQFactory();
@@ -1,4 +1,4 @@
1
- import { Item, ReferenceItem, References } from "../items";
1
+ import { Item, Reference, ReferenceItem, References } from "../items";
2
2
  import { isItemKeyEqual, isPriKey } from "../key/KUtils";
3
3
  import { ComKey, PriKey } from "../keys";
4
4
  import LibLogger from "../logger";
@@ -103,7 +103,10 @@ const isRefQueryMatch =
103
103
  ): boolean => {
104
104
  logger.trace('doesRefMatch', { queryRef, references });
105
105
  logger.debug('Comparing Ref', { refKey, itemRef: references[refKey], queryRef });
106
- return isItemKeyEqual(queryRef, references[refKey]);
106
+ if (!references[refKey]) {
107
+ return false;
108
+ }
109
+ return isItemKeyEqual(queryRef, references[refKey].key);
107
110
  }
108
111
 
109
112
  const isCompoundConditionQueryMatch = <
@@ -155,6 +158,20 @@ const isConditionQueryMatch = <
155
158
  return false;
156
159
  }
157
160
  logger.debug('Comparing Condition', { propKey, itemProp: item[propKey], queryCondition });
161
+
162
+ // Handle null values - only == and != make sense with null
163
+ if (queryCondition.value === null) {
164
+ if (queryCondition.operator === '==') {
165
+ return item[propKey] === null;
166
+ } else if (queryCondition.operator === '!=') {
167
+ return item[propKey] !== null;
168
+ } else {
169
+ throw new Error(
170
+ `Operator ${queryCondition.operator} cannot be used with null value. Use '==' for null checks or '!=' for not-null checks.`
171
+ );
172
+ }
173
+ }
174
+
158
175
  let result = false;
159
176
  switch (queryCondition.operator) {
160
177
  case '==':
@@ -207,6 +224,9 @@ const isAggQueryMatch = <
207
224
  const aggItem = agg.item;
208
225
  logger.debug('Comparing Agg', { aggKey, aggItem, aggQuery });
209
226
  // Fancy, right? This is a recursive call to isQueryMatch
227
+ if (!aggItem) {
228
+ return false;
229
+ }
210
230
  return isQueryMatch(aggItem, aggQuery);
211
231
  }
212
232
 
@@ -257,7 +277,7 @@ export const isQueryMatch = <
257
277
  if (query.refs && item.refs) {
258
278
  for (const key in query.refs) {
259
279
  const queryRef = query.refs[key];
260
- if (!isRefQueryMatch(key, queryRef, item.refs)) return false;
280
+ if (!isRefQueryMatch(key, queryRef.key, item.refs)) return false;
261
281
  }
262
282
  } else if (query.refs && !item.refs) {
263
283
  logger.debug('Query contains refs but item does not have refs', { query, item });
@@ -279,9 +299,21 @@ export const isQueryMatch = <
279
299
  if (query.aggs && item.aggs) {
280
300
  for (const key in query.aggs) {
281
301
  const aggQuery = query.aggs[key];
282
- if (item.aggs[key] && !isAggQueryMatch(key, aggQuery, item.aggs[key])) return false;
302
+ // aggs is a record where each key maps to an array of aggregations
303
+ if (item.aggs[key]) {
304
+ let hasMatch = false;
305
+ for (const aggRecord of item.aggs[key]) {
306
+ if (isAggQueryMatch(key, aggQuery, aggRecord as ReferenceItem<any, any, any, any, any, any>)) {
307
+ hasMatch = true;
308
+ break;
309
+ }
310
+ }
311
+ if (!hasMatch) return false;
312
+ } else {
313
+ return false;
314
+ }
283
315
  }
284
- } if (query.aggs && !item.aggs) {
316
+ } else if (query.aggs && !item.aggs) {
285
317
  logger.debug('Query contains aggs but item does not have aggs', { query, item });
286
318
  return false;
287
319
  }
@@ -332,12 +364,12 @@ export const abbrevRef = <
332
364
  L3 extends string = never,
333
365
  L4 extends string = never,
334
366
  L5 extends string = never
335
- >(key: string, ref: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): string => {
336
- if (isPriKey(ref)) {
337
- const priKey = ref as PriKey<S>;
367
+ >(key: string, ref: Reference<S, L1, L2, L3, L4, L5>): string => {
368
+ if (isPriKey(ref.key)) {
369
+ const priKey = ref.key as PriKey<S>;
338
370
  return `R(${key},${priKey.kt},${priKey.pk})`;
339
371
  } else {
340
- const comKey = ref as ComKey<S, L1, L2, L3, L4, L5>;
372
+ const comKey = ref.key as ComKey<S, L1, L2, L3, L4, L5>;
341
373
  return `R(${key},${JSON.stringify(comKey)})`;
342
374
  }
343
375
  }
@@ -14,7 +14,7 @@ export type ConditionOperator =
14
14
  */
15
15
  export type Condition = {
16
16
  column: string,
17
- value: string[] | string | number[] | number | boolean | Date,
17
+ value: string[] | string | number[] | number | boolean | Date | null,
18
18
  operator: ConditionOperator,
19
19
  };
20
20
 
@@ -55,7 +55,8 @@ export const isCondition = (condition: any): condition is Condition => {
55
55
  typeof condition.value === 'string' ||
56
56
  typeof condition.value === 'number' ||
57
57
  typeof condition.value === 'boolean' ||
58
- condition.value instanceof Date
58
+ condition.value instanceof Date ||
59
+ condition.value === null
59
60
  ) && (condition.operator ? typeof condition.operator === 'string' : true);
60
61
  }
61
62
 
package/src/items.ts CHANGED
@@ -46,23 +46,32 @@ export interface Deletable extends Partial<Evented> {
46
46
 
47
47
  export type ManagedEvents = Timestamped & Deletable;
48
48
 
49
- export type References = Record<
50
- string,
51
- ComKey<string, string | never, string | never, string | never, string | never, string | never> |
52
- PriKey<string>
53
- >;
49
+ export type Reference<S extends string,
50
+ L1 extends string = never,
51
+ L2 extends string = never,
52
+ L3 extends string = never,
53
+ L4 extends string = never,
54
+ L5 extends string = never
55
+ > = { key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, item?: Item<S, L1, L2, L3, L4, L5> };
54
56
 
55
- export type ReferenceItem<
56
- S extends string,
57
+ export type ReferenceItem<S extends string,
57
58
  L1 extends string = never,
58
59
  L2 extends string = never,
59
60
  L3 extends string = never,
60
61
  L4 extends string = never,
61
62
  L5 extends string = never
62
- > = { key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, item: Item<S, L1, L2, L3, L4, L5> };
63
+ > = Reference<S, L1, L2, L3, L4, L5>;
64
+
65
+ export type References = Record<string, Reference<any, any, any, any, any, any>>;
63
66
 
64
- export type ReferenceItems = Record<
65
- string, ReferenceItem<string, string | never, string | never, string | never, string | never, string | never>>;
67
+ export type Aggregation<
68
+ S extends string,
69
+ L1 extends string = never,
70
+ L2 extends string = never,
71
+ L3 extends string = never,
72
+ L4 extends string = never,
73
+ L5 extends string = never
74
+ > = { key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, item?: Item<S, L1, L2, L3, L4, L5> };
66
75
 
67
76
  export interface Item<S extends string = never,
68
77
  L1 extends string = never,
@@ -72,8 +81,8 @@ export interface Item<S extends string = never,
72
81
  L5 extends string = never> extends Identified<S, L1, L2, L3, L4, L5> {
73
82
  events: ManagedEvents & Evented;
74
83
  // TODO: This is a bit of a hack to get around the fact that we don't want to pass all these generics
75
- aggs?: ReferenceItems;
76
- refs?: References;
84
+ aggs?: Record<string, Aggregation<any, any, any, any, any, any>[]>;
85
+ refs?: Record<string, Reference<any, any, any, any, any, any>>;
77
86
  [key: string]: any
78
87
  }
79
88