@fjell/core 4.4.63 → 4.4.64

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.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, UpdateOptions } from './operations/Operations';
18
+ export type { Operations, OperationParams, AffectedKeys, CreateOptions, UpdateOptions, AllOptions, PaginationMetadata, AllOperationResult } 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
@@ -1826,35 +1826,49 @@ function createOneWrapper(coordinate, implementation, options = {}) {
1826
1826
 
1827
1827
  // src/operations/wrappers/createAllWrapper.ts
1828
1828
  var logger10 = logger_default.get("operations", "wrappers", "all");
1829
- function createAllWrapper(coordinate, implementation, options = {}) {
1830
- const operationName = options.operationName || "all";
1831
- return async (query, locations) => {
1832
- if (options.debug) {
1833
- logger10.debug(`[${operationName}] Called with:`, { query, locations });
1829
+ function createAllWrapper(coordinate, implementation, wrapperOptions = {}) {
1830
+ const operationName = wrapperOptions.operationName || "all";
1831
+ return async (query, locations, allOptions) => {
1832
+ if (wrapperOptions.debug) {
1833
+ logger10.debug(`[${operationName}] Called with:`, { query, locations, allOptions });
1834
1834
  }
1835
- if (!options.skipValidation) {
1835
+ if (!wrapperOptions.skipValidation) {
1836
1836
  validateQuery(query, operationName);
1837
1837
  validateLocations(locations, coordinate, operationName);
1838
+ if (allOptions && "limit" in allOptions && allOptions.limit != null) {
1839
+ if (!Number.isInteger(allOptions.limit) || allOptions.limit < 1) {
1840
+ throw new Error(`[${operationName}] limit must be a positive integer, got: ${allOptions.limit}`);
1841
+ }
1842
+ }
1843
+ if (allOptions && "offset" in allOptions && allOptions.offset != null) {
1844
+ if (!Number.isInteger(allOptions.offset) || allOptions.offset < 0) {
1845
+ throw new Error(`[${operationName}] offset must be a non-negative integer, got: ${allOptions.offset}`);
1846
+ }
1847
+ }
1838
1848
  }
1839
1849
  const normalizedQuery = query ?? {};
1840
1850
  const normalizedLocations = locations ?? [];
1841
1851
  try {
1842
- const result = await implementation(normalizedQuery, normalizedLocations);
1843
- if (options.debug) {
1844
- logger10.debug(`[${operationName}] Result: ${result.length} items`);
1852
+ const result = await implementation(normalizedQuery, normalizedLocations, allOptions);
1853
+ if (wrapperOptions.debug) {
1854
+ logger10.debug(`[${operationName}] Result: ${result.items.length} items, total: ${result.metadata.total}`);
1845
1855
  }
1846
- if (!options.skipValidation) {
1847
- return validatePK(result, coordinate.kta[0]);
1856
+ if (!wrapperOptions.skipValidation) {
1857
+ const validatedItems = validatePK(result.items, coordinate.kta[0]);
1858
+ return {
1859
+ items: validatedItems,
1860
+ metadata: result.metadata
1861
+ };
1848
1862
  }
1849
1863
  return result;
1850
1864
  } catch (error) {
1851
- if (options.onError) {
1865
+ if (wrapperOptions.onError) {
1852
1866
  const context = {
1853
1867
  operationName,
1854
- params: [query, locations],
1868
+ params: [query, locations, allOptions],
1855
1869
  coordinate
1856
1870
  };
1857
- throw options.onError(error, context);
1871
+ throw wrapperOptions.onError(error, context);
1858
1872
  }
1859
1873
  throw new Error(
1860
1874
  `[${operationName}] Operation failed: ${error.message}`,
@@ -19,6 +19,124 @@ 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 the all() operation to control pagination.
24
+ *
25
+ * When provided, these options take precedence over any limit/offset
26
+ * specified in the ItemQuery.
27
+ *
28
+ * @public
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * // Fetch first 50 items
33
+ * const result = await operations.all(query, [], { limit: 50, offset: 0 });
34
+ *
35
+ * // Fetch next page
36
+ * const nextPage = await operations.all(query, [], { limit: 50, offset: 50 });
37
+ * ```
38
+ */
39
+ export interface AllOptions {
40
+ /**
41
+ * Maximum number of items to return.
42
+ *
43
+ * - Must be a positive integer (>= 1)
44
+ * - When not provided, returns all matching items
45
+ * - Takes precedence over query.limit when both are specified
46
+ */
47
+ limit?: number;
48
+ /**
49
+ * Number of items to skip before returning results.
50
+ *
51
+ * - Must be a non-negative integer (>= 0)
52
+ * - Defaults to 0 when not provided
53
+ * - Takes precedence over query.offset when both are specified
54
+ */
55
+ offset?: number;
56
+ }
57
+ /**
58
+ * Metadata about the pagination state for an all() operation.
59
+ *
60
+ * This metadata enables proper pagination UI and logic by providing
61
+ * the total count of matching items before limit/offset are applied.
62
+ *
63
+ * @public
64
+ */
65
+ export interface PaginationMetadata {
66
+ /**
67
+ * Total count of items matching the query BEFORE limit/offset applied.
68
+ *
69
+ * This represents the complete result set size and is used to:
70
+ * - Display "Showing X of Y results"
71
+ * - Calculate total pages
72
+ * - Determine if more items exist
73
+ */
74
+ total: number;
75
+ /**
76
+ * Number of items actually returned in this response.
77
+ *
78
+ * This equals `items.length` and is provided for convenience.
79
+ * When offset + returned < total, more items exist.
80
+ */
81
+ returned: number;
82
+ /**
83
+ * The limit that was applied, if any.
84
+ *
85
+ * - Undefined when no limit was applied
86
+ * - Reflects the effective limit (options.limit ?? query.limit)
87
+ */
88
+ limit?: number;
89
+ /**
90
+ * The offset that was applied.
91
+ *
92
+ * - 0 when no offset was applied
93
+ * - Reflects the effective offset (options.offset ?? query.offset ?? 0)
94
+ */
95
+ offset: number;
96
+ /**
97
+ * Convenience field indicating whether more items exist beyond this page.
98
+ *
99
+ * Calculated as: `offset + returned < total`
100
+ *
101
+ * Useful for:
102
+ * - "Load More" buttons
103
+ * - Infinite scroll implementations
104
+ * - "Next Page" button state
105
+ */
106
+ hasMore: boolean;
107
+ }
108
+ /**
109
+ * Result structure for the all() operation with pagination support.
110
+ *
111
+ * This structure provides both the items and metadata needed for
112
+ * implementing proper pagination in applications.
113
+ *
114
+ * @template T - The item type being returned
115
+ *
116
+ * @public
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * const result = await operations.all(query, [], { limit: 50, offset: 0 });
121
+ *
122
+ * console.log(`Showing ${result.metadata.returned} of ${result.metadata.total}`);
123
+ * // "Showing 50 of 1234"
124
+ *
125
+ * if (result.metadata.hasMore) {
126
+ * // Load next page
127
+ * }
128
+ * ```
129
+ */
130
+ export interface AllOperationResult<T> {
131
+ /**
132
+ * Array of items matching the query, with limit/offset applied.
133
+ */
134
+ items: T[];
135
+ /**
136
+ * Pagination metadata for the result set.
137
+ */
138
+ metadata: PaginationMetadata;
139
+ }
22
140
  /**
23
141
  * Options for update operations across all Fjell libraries.
24
142
  *
@@ -97,25 +215,38 @@ export interface UpdateOptions {
97
215
  */
98
216
  export interface Operations<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> {
99
217
  /**
100
- * Retrieves all items matching the query.
218
+ * Retrieves all items matching the query with optional pagination.
101
219
  *
102
- * @param query - Optional query to filter items
220
+ * @param query - Optional query to filter items (may include limit/offset for backwards compatibility)
103
221
  * @param locations - Optional location hierarchy to scope the query
104
- * @returns Array of items matching the query
222
+ * @param options - Optional pagination options (takes precedence over query limit/offset)
223
+ * @returns Result containing items and pagination metadata
105
224
  *
106
- * @example
225
+ * @example Get all items
107
226
  * ```typescript
108
- * // Get all users
109
- * const users = await operations.all();
227
+ * const result = await operations.all();
228
+ * // result.items = all items
229
+ * // result.metadata.total = items.length
230
+ * ```
110
231
  *
111
- * // Get users with filter
112
- * const activeUsers = await operations.all({ filter: { status: 'active' } });
232
+ * @example Get paginated items
233
+ * ```typescript
234
+ * const result = await operations.all({}, [], { limit: 50, offset: 0 });
235
+ * // result.items = first 50 items
236
+ * // result.metadata.total = total matching count
237
+ * // result.metadata.hasMore = true if more exist
238
+ * ```
113
239
  *
114
- * // Get items in specific location
115
- * const comments = await operations.all({}, [{kt: 'post', lk: 'post-123'}]);
240
+ * @example Get items in specific location with pagination
241
+ * ```typescript
242
+ * const result = await operations.all(
243
+ * {},
244
+ * [{kt: 'post', lk: 'post-123'}],
245
+ * { limit: 20, offset: 0 }
246
+ * );
116
247
  * ```
117
248
  */
118
- all(query?: ItemQuery, locations?: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<V[]>;
249
+ all(query?: ItemQuery, locations?: LocKeyArray<L1, L2, L3, L4, L5> | [], options?: AllOptions): Promise<AllOperationResult<V>>;
119
250
  /**
120
251
  * Retrieves the first item matching the query.
121
252
  *
@@ -1,7 +1,7 @@
1
1
  import { Item } from "../items";
2
2
  import { ComKey, LocKeyArray } from "../keys";
3
3
  import { ItemQuery } from "../item/ItemQuery";
4
- import { AffectedKeys, OperationParams, Operations } from "./Operations";
4
+ import { AffectedKeys, AllOperationResult, AllOptions, OperationParams, Operations } from "./Operations";
5
5
  /**
6
6
  * Contained Operations interface - specialized for contained (hierarchical) items only.
7
7
  *
@@ -47,7 +47,7 @@ import { AffectedKeys, OperationParams, Operations } from "./Operations";
47
47
  * ```
48
48
  */
49
49
  export interface ContainedOperations<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> extends Omit<Operations<V, S, L1, L2, L3, L4, L5>, 'get' | 'update' | 'remove' | 'upsert' | 'create' | 'action' | 'facet' | 'allAction' | 'allFacet' | 'all' | 'one' | 'find' | 'findOne'> {
50
- all(query: ItemQuery | undefined, locations: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<V[]>;
50
+ all(query: ItemQuery | undefined, locations: LocKeyArray<L1, L2, L3, L4, L5> | [], allOptions?: AllOptions): Promise<AllOperationResult<V>>;
51
51
  one(query: ItemQuery | undefined, locations: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<V | null>;
52
52
  find(finder: string, params: OperationParams | undefined, locations: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<V[]>;
53
53
  findOne(finder: string, params: OperationParams | undefined, locations: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<V | null>;
@@ -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, UpdateOptions } from "./Operations";
4
+ import { AffectedKeys, AllOperationResult, AllOptions, CreateOptions, OperationParams, UpdateOptions } from "./Operations";
5
5
  /**
6
6
  * Get method signature - retrieves single item by key
7
7
  */
@@ -33,10 +33,34 @@ export interface UpsertMethod<V extends Item<S, L1, L2, L3, L4, L5>, S extends s
33
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
- * All method signature - retrieves all items matching query
36
+ * All method signature - retrieves all items matching query with optional pagination.
37
+ *
38
+ * @param query - Optional query to filter items (may include limit/offset for backwards compatibility)
39
+ * @param locations - Optional location hierarchy to scope the query
40
+ * @param options - Optional pagination options (takes precedence over query limit/offset)
41
+ * @returns Result containing items and pagination metadata
42
+ *
43
+ * @example Without options (backwards compatible)
44
+ * ```typescript
45
+ * const result = await operations.all({ compoundCondition: {...} });
46
+ * // result.items = [...all matching items...]
47
+ * // result.metadata.total = result.items.length
48
+ * ```
49
+ *
50
+ * @example With options (new pattern)
51
+ * ```typescript
52
+ * const result = await operations.all(
53
+ * { compoundCondition: {...} },
54
+ * [],
55
+ * { limit: 50, offset: 0 }
56
+ * );
57
+ * // result.items = [...first 50 items...]
58
+ * // result.metadata.total = total matching count
59
+ * // result.metadata.hasMore = true if more items exist
60
+ * ```
37
61
  */
38
62
  export interface AllMethod<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> {
39
- (query?: ItemQuery, locations?: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<V[]>;
63
+ (query?: ItemQuery, locations?: LocKeyArray<L1, L2, L3, L4, L5> | [], options?: AllOptions): Promise<AllOperationResult<V>>;
40
64
  }
41
65
  /**
42
66
  * One method signature - retrieves first item matching query
@@ -1,7 +1,7 @@
1
1
  import { Item } from "../items";
2
2
  import { PriKey } from "../keys";
3
3
  import { ItemQuery } from "../item/ItemQuery";
4
- import { AffectedKeys, OperationParams, Operations } from "./Operations";
4
+ import { AffectedKeys, AllOperationResult, AllOptions, OperationParams, Operations } from "./Operations";
5
5
  /**
6
6
  * Primary Operations interface - specialized for primary (top-level) items only.
7
7
  *
@@ -39,7 +39,7 @@ import { AffectedKeys, OperationParams, Operations } from "./Operations";
39
39
  * ```
40
40
  */
41
41
  export interface PrimaryOperations<V extends Item<S>, S extends string> extends Omit<Operations<V, S>, 'all' | 'one' | 'create' | 'get' | 'update' | 'upsert' | 'remove' | 'find' | 'findOne' | 'action' | 'allAction' | 'facet' | 'allFacet'> {
42
- all(query?: ItemQuery): Promise<V[]>;
42
+ all(query?: ItemQuery, locations?: [], allOptions?: AllOptions): Promise<AllOperationResult<V>>;
43
43
  one(query?: ItemQuery): Promise<V | null>;
44
44
  find(finder: string, params?: OperationParams): Promise<V[]>;
45
45
  findOne(finder: string, params?: OperationParams): Promise<V | null>;
@@ -19,10 +19,10 @@ import type { WrapperOptions } from "./types";
19
19
  * ```typescript
20
20
  * const all = createAllWrapper(
21
21
  * coordinate,
22
- * async (query, locations) => {
23
- * return await database.findAll(query, locations);
22
+ * async (query, locations, options) => {
23
+ * return await database.findAll(query, locations, options);
24
24
  * }
25
25
  * );
26
26
  * ```
27
27
  */
28
- export declare function createAllWrapper<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>(coordinate: Coordinate<S, L1, L2, L3, L4, L5>, implementation: AllMethod<V, S, L1, L2, L3, L4, L5>, options?: WrapperOptions): AllMethod<V, S, L1, L2, L3, L4, L5>;
28
+ export declare function createAllWrapper<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>(coordinate: Coordinate<S, L1, L2, L3, L4, L5>, implementation: AllMethod<V, S, L1, L2, L3, L4, L5>, wrapperOptions?: WrapperOptions): AllMethod<V, S, L1, L2, L3, L4, L5>;
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.63",
4
+ "version": "4.4.64",
5
5
  "keywords": [
6
6
  "core",
7
7
  "fjell"
@@ -41,14 +41,14 @@
41
41
  "docs:test": "cd docs && npm run test"
42
42
  },
43
43
  "dependencies": {
44
- "@fjell/logging": "^4.4.58",
44
+ "@fjell/logging": "^4.4.59",
45
45
  "deepmerge": "^4.3.1",
46
46
  "luxon": "^3.7.2"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@eslint/eslintrc": "^3.3.1",
50
50
  "@eslint/js": "^9.39.1",
51
- "@fjell/common-config": "^1.1.30",
51
+ "@fjell/common-config": "^1.1.31",
52
52
  "@swc/core": "^1.15.2",
53
53
  "@tsconfig/recommended": "^1.0.13",
54
54
  "@types/luxon": "^3.7.1",