awesome-key-factory 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bhaskar Sharma
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # awesome-key-factory
2
+
3
+ A type-safe key factory for managing react-query keys with full TypeScript support.
4
+
5
+ ## 📚 Documentation
6
+
7
+ - **[Documentation Site](https://bhaskar20.github.io/awesome-key-factory/)** - Beautiful, interactive documentation built with VitePress (recommended)
8
+ - **[Full Documentation](./DOCUMENTATION.md)** - Complete guide with all features and examples
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ yarn add awesome-key-factory
14
+ # or
15
+ npm install awesome-key-factory
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ### Basic Example
21
+
22
+ ```typescript
23
+ import { createKeyFactory } from 'awesome-key-factory';
24
+
25
+ const keys = createKeyFactory('baseKey', {
26
+ a: (params: {}) => ['key1', 'key2'],
27
+ b: {
28
+ c: ['key3', 'key4'],
29
+ },
30
+ });
31
+
32
+ // Access any level
33
+ keys.a({}) // => ['baseKey', 'a', 'key1', 'key2']
34
+ keys.b.c() // => ['baseKey', 'b', 'c', 'key3', 'key4']
35
+ keys.b() // => ['baseKey', 'b'] (any level can be called as a function)
36
+ ```
37
+
38
+ ### With Parameters
39
+
40
+ ```typescript
41
+ const queryKeys = createKeyFactory('app', {
42
+ users: {
43
+ all: () => [],
44
+ detail: (params: { id: string }) => [params.id],
45
+ posts: (params: { userId: string }) => [params.userId, 'posts'],
46
+ },
47
+ posts: {
48
+ all: () => [],
49
+ detail: (params: { id: string }) => [params.id],
50
+ },
51
+ });
52
+
53
+ // Usage in react-query
54
+ useQuery({
55
+ queryKey: queryKeys.users.detail({ id: '123' }),
56
+ queryFn: () => fetchUser('123'),
57
+ });
58
+
59
+ useQuery({
60
+ queryKey: queryKeys.users.posts({ userId: '456' }),
61
+ queryFn: () => fetchUserPosts('456'),
62
+ });
63
+ ```
64
+
65
+ ### Array Shorthand
66
+
67
+ You can use arrays as a shorthand for functions that return static keys:
68
+
69
+ ```typescript
70
+ const keys = createKeyFactory('shop', {
71
+ products: {
72
+ list: ['all'], // equivalent to () => ['all']
73
+ byId: (params: { id: string }) => [params.id],
74
+ },
75
+ });
76
+
77
+ keys.products.list() // => ['shop', 'products', 'list', 'all']
78
+ ```
79
+
80
+ ### Deep Nesting
81
+
82
+ ```typescript
83
+ const keys = createKeyFactory('api', {
84
+ v1: {
85
+ users: {
86
+ posts: {
87
+ comments: (params: { postId: string }) => [params.postId],
88
+ },
89
+ },
90
+ },
91
+ });
92
+
93
+ keys.v1.users.posts.comments({ postId: '123' })
94
+ // => ['api', 'v1', 'users', 'posts', 'comments', '123']
95
+
96
+ keys.v1.users.posts() // => ['api', 'v1', 'users', 'posts']
97
+ keys.v1.users() // => ['api', 'v1', 'users']
98
+ keys.v1() // => ['api', 'v1']
99
+ ```
100
+
101
+ ## Features
102
+
103
+ - ✅ **Fully TypeScript typed** - Get autocomplete and type safety for all your keys
104
+ - ✅ **Nested key access** - Access nested keys through the full path (`keys.b.c`)
105
+ - ✅ **Function parameters** - Pass typed parameters to your key functions
106
+ - ✅ **Array shorthand** - Use arrays for static key lists
107
+ - ✅ **Any level access** - Call any level in the hierarchy as a function to get its path
108
+
109
+ ## API
110
+
111
+ ### `createKeyFactory<BaseKey, Schema>(baseKey, schema)`
112
+
113
+ Creates a type-safe key factory.
114
+
115
+ **Parameters:**
116
+ - `baseKey` (string): The base key that will be prepended to all generated keys
117
+ - `schema` (object): An object defining the key structure with nested objects and functions
118
+
119
+ **Returns:** A factory object where each level can be accessed as a function
120
+
121
+ ## Documentation
122
+
123
+ For complete documentation, examples, and best practices, see:
124
+
125
+ - **[Documentation](https://bhaskar20.github.io/awesome-key-factory/)** - Beautiful, modern documentation site with search, dark mode, and more
126
+ - **[DOCUMENTATION.md](./DOCUMENTATION.md)** - Comprehensive markdown documentation
127
+
128
+ ## License
129
+
130
+ MIT
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Type helper to check if a function has no parameters
3
+ */
4
+ type HasNoParams<T> = T extends () => any ? true : false;
5
+ /**
6
+ * Type helper to extract the parameter type from a function
7
+ */
8
+ type ParamsOf<T> = T extends (params: infer P) => any ? P : T extends () => any ? Record<string, never> : never;
9
+ /**
10
+ * Type helper to create function signature with optional params when Record<string, never>
11
+ * Functions with no parameters can be called with 0 args or with an empty object
12
+ */
13
+ type FunctionWithParams<P, R, T> = HasNoParams<T> extends true ? (() => R) & ((params?: Record<string, never>) => R) : P extends Record<string, never> ? (params?: P) => R : (params: P) => R;
14
+ /**
15
+ * Type helper to extract the return type from a function
16
+ */
17
+ type ReturnOf<T> = T extends (...args: any[]) => infer R ? R : never;
18
+ /**
19
+ * Type helper to get the key array type from a function or array
20
+ */
21
+ type KeyArray<T> = T extends (...args: any[]) => infer R ? R extends readonly (infer K)[] ? K[] : never : T extends readonly (infer K)[] ? K[] : never;
22
+ /**
23
+ * Check if a type is an array
24
+ */
25
+ type IsArray<T> = T extends readonly (infer _)[] ? true : false;
26
+ /**
27
+ * Get the function type for a specific key in the schema
28
+ */
29
+ type GetKeyFunction<T, K extends string, BaseKey extends string, Path extends readonly string[] = []> = K extends keyof T ? T[K] extends (...args: any[]) => any ? FunctionWithParams<ParamsOf<T[K]>, [
30
+ ...Path,
31
+ BaseKey,
32
+ K,
33
+ ...KeyArray<ReturnOf<T[K]>>
34
+ ], T[K]> : IsArray<T[K]> extends true ? (params?: Record<string, never>) => [...Path, BaseKey, K, ...KeyArray<T[K]>] : T[K] extends Record<string, any> ? KeyFactory<T[K], BaseKey, [...Path, BaseKey, K]> & {
35
+ (): [...Path, BaseKey, K];
36
+ } : never : never;
37
+ /**
38
+ * Union of all string keys in a type
39
+ */
40
+ type StringKeys<T> = Extract<keyof T, string>;
41
+ /**
42
+ * Union of all function types for bracket notation access
43
+ */
44
+ type BracketAccess<T, BaseKey extends string, Path extends readonly string[] = []> = {
45
+ [K in StringKeys<T>]: GetKeyFunction<T, K, BaseKey, Path>;
46
+ }[StringKeys<T>];
47
+ /**
48
+ * Recursive type to transform the input schema into the output factory type
49
+ */
50
+ type KeyFactory<T, BaseKey extends string, Path extends readonly string[] = []> = {
51
+ [K in keyof T]: T[K] extends (...args: any[]) => any ? FunctionWithParams<ParamsOf<T[K]>, [
52
+ ...Path,
53
+ BaseKey,
54
+ K & string,
55
+ ...KeyArray<ReturnOf<T[K]>>
56
+ ], T[K]> : IsArray<T[K]> extends true ? (params?: Record<string, never>) => [...Path, BaseKey, K & string, ...KeyArray<T[K]>] : T[K] extends Record<string, any> ? KeyFactory<T[K], BaseKey, [...Path, BaseKey, K & string]> & {
57
+ (): [...Path, BaseKey, K & string];
58
+ } : never;
59
+ } & {
60
+ [K in StringKeys<T>]: GetKeyFunction<T, K, BaseKey, Path>;
61
+ } & {
62
+ [key: string]: BracketAccess<T, BaseKey, Path> | any;
63
+ };
64
+ /**
65
+ * Creates a type-safe key factory for react-query keys
66
+ *
67
+ * @param baseKey - The base key that will be prepended to all generated keys
68
+ * @param schema - An object defining the key structure with nested objects and functions
69
+ * @returns A factory object where each level can be accessed as a function
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * const keys = createKeyFactory("baseKey", {
74
+ * a: (params: {}) => ["key1", "key2"],
75
+ * b: {
76
+ * c: ["key3", "key4"]
77
+ * }
78
+ * });
79
+ *
80
+ * keys.a({}) // => ["baseKey", "a", "key1", "key2"]
81
+ * keys.b.c({}) // => ["baseKey", "b", "c", "key3", "key4"]
82
+ * keys.b() // => ["baseKey", "b"]
83
+ * ```
84
+ */
85
+ export declare function createKeyFactory<BaseKey extends string, Schema extends Record<string, any>>(baseKey: BaseKey, schema: Schema): KeyFactory<Schema, BaseKey>;
86
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createKeyFactory = createKeyFactory;
4
+ /**
5
+ * Creates a type-safe key factory for react-query keys
6
+ *
7
+ * @param baseKey - The base key that will be prepended to all generated keys
8
+ * @param schema - An object defining the key structure with nested objects and functions
9
+ * @returns A factory object where each level can be accessed as a function
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const keys = createKeyFactory("baseKey", {
14
+ * a: (params: {}) => ["key1", "key2"],
15
+ * b: {
16
+ * c: ["key3", "key4"]
17
+ * }
18
+ * });
19
+ *
20
+ * keys.a({}) // => ["baseKey", "a", "key1", "key2"]
21
+ * keys.b.c({}) // => ["baseKey", "b", "c", "key3", "key4"]
22
+ * keys.b() // => ["baseKey", "b"]
23
+ * ```
24
+ */
25
+ function createKeyFactory(baseKey, schema) {
26
+ function createFactory(currentSchema, currentBaseKey, currentPath = []) {
27
+ const factory = {};
28
+ // Add a function to get the current path
29
+ factory.__call = () => [...currentPath, currentBaseKey];
30
+ for (const key in currentSchema) {
31
+ const value = currentSchema[key];
32
+ if (typeof value === "function") {
33
+ // It's a function that returns keys
34
+ factory[key] = (params) => {
35
+ const keys = value(params);
36
+ return [...currentPath, currentBaseKey, key, ...keys];
37
+ };
38
+ }
39
+ else if (typeof value === "object" &&
40
+ value !== null &&
41
+ !Array.isArray(value)) {
42
+ // It's a nested object
43
+ const nestedFactory = createFactory(value, key, [
44
+ ...currentPath,
45
+ currentBaseKey,
46
+ ]);
47
+ // Also add a direct accessor that returns the path to this level
48
+ factory[key] = Object.assign(() => [...currentPath, currentBaseKey, key], nestedFactory);
49
+ }
50
+ else if (Array.isArray(value)) {
51
+ // It's an array of keys (shorthand for a function that returns the array)
52
+ factory[key] = (_params) => [
53
+ ...currentPath,
54
+ currentBaseKey,
55
+ key,
56
+ ...value,
57
+ ];
58
+ }
59
+ }
60
+ // Make the factory callable
61
+ return new Proxy(factory, {
62
+ get(target, prop) {
63
+ if (prop === "__call") {
64
+ return target.__call;
65
+ }
66
+ if (prop in target) {
67
+ return target[prop];
68
+ }
69
+ throw new Error(`Key "${String(prop)}" does not exist at this level. Access it through the proper hierarchy.`);
70
+ },
71
+ apply(target, _thisArg, _argumentsList) {
72
+ return target.__call();
73
+ },
74
+ });
75
+ }
76
+ return createFactory(schema, baseKey);
77
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,420 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const index_1 = require("./index");
4
+ describe("createKeyFactory", () => {
5
+ describe("basic functionality", () => {
6
+ it("should create keys with base key and function", () => {
7
+ const keys = (0, index_1.createKeyFactory)("baseKey", {
8
+ a: (_params) => ["key1", "key2"],
9
+ });
10
+ expect(keys.a({})).toEqual(["baseKey", "a", "key1", "key2"]);
11
+ });
12
+ it("should use params to create keys dynamically", () => {
13
+ const keys = (0, index_1.createKeyFactory)("baseKey", {
14
+ a: (params) => [params.id, params.status, "key1"],
15
+ });
16
+ expect(keys.a({ id: "123", status: "active" })).toEqual([
17
+ "baseKey",
18
+ "a",
19
+ "123",
20
+ "active",
21
+ "key1",
22
+ ]);
23
+ });
24
+ it("should create keys with nested objects", () => {
25
+ const keys = (0, index_1.createKeyFactory)("baseKey", {
26
+ b: {
27
+ c: ["key3", "key4"],
28
+ },
29
+ });
30
+ expect(keys.b.c()).toEqual(["baseKey", "b", "c", "key3", "key4"]);
31
+ });
32
+ it("should allow accessing intermediate levels", () => {
33
+ const keys = (0, index_1.createKeyFactory)("baseKey", {
34
+ b: {
35
+ c: ["key3", "key4"],
36
+ },
37
+ });
38
+ expect(keys.b()).toEqual(["baseKey", "b"]);
39
+ });
40
+ it("should handle the example from the requirements", () => {
41
+ const keys = (0, index_1.createKeyFactory)("baseKey", {
42
+ a: () => ["key1", "key2"],
43
+ b: {
44
+ c: () => ["key3", "key4"],
45
+ },
46
+ });
47
+ expect(keys.a({})).toEqual(["baseKey", "a", "key1", "key2"]);
48
+ expect(keys.b.c()).toEqual(["baseKey", "b", "c", "key3", "key4"]);
49
+ expect(() => keys.c()).toThrow();
50
+ expect(keys.b()).toEqual(["baseKey", "b"]);
51
+ });
52
+ });
53
+ describe("function parameters", () => {
54
+ it("should pass parameters to key functions", () => {
55
+ const keys = (0, index_1.createKeyFactory)("users", {
56
+ byId: (params) => [params.id, params.type],
57
+ });
58
+ expect(keys.byId({ id: "123", type: "admin" })).toEqual([
59
+ "users",
60
+ "byId",
61
+ "123",
62
+ "admin",
63
+ ]);
64
+ });
65
+ it("should handle empty parameters", () => {
66
+ const keys = (0, index_1.createKeyFactory)("posts", {
67
+ all: (_params) => [],
68
+ });
69
+ expect(keys.all({})).toEqual(["posts", "all"]);
70
+ });
71
+ it("should handle multiple parameters", () => {
72
+ const keys = (0, index_1.createKeyFactory)("users", {
73
+ posts: {
74
+ byId: (params) => [params.userId, params.postId],
75
+ },
76
+ });
77
+ expect(keys.posts.byId({ userId: "1", postId: "2" })).toEqual([
78
+ "users",
79
+ "posts",
80
+ "byId",
81
+ "1",
82
+ "2",
83
+ ]);
84
+ });
85
+ });
86
+ describe("nested structures", () => {
87
+ it("should handle deeply nested objects", () => {
88
+ const keys = (0, index_1.createKeyFactory)("app", {
89
+ users: {
90
+ posts: {
91
+ comments: ["all"],
92
+ },
93
+ },
94
+ });
95
+ expect(keys.users.posts.comments()).toEqual([
96
+ "app",
97
+ "users",
98
+ "posts",
99
+ "comments",
100
+ "all",
101
+ ]);
102
+ expect(keys.users.posts()).toEqual(["app", "users", "posts"]);
103
+ expect(keys.users()).toEqual(["app", "users"]);
104
+ });
105
+ it("should handle multiple nested levels with functions", () => {
106
+ const keys = (0, index_1.createKeyFactory)("api", {
107
+ v1: {
108
+ users: (params) => [params.id],
109
+ posts: {
110
+ list: (params) => [params.page.toString()],
111
+ detail: (params) => [params.id],
112
+ },
113
+ },
114
+ });
115
+ expect(keys.v1.users({ id: "123" })).toEqual([
116
+ "api",
117
+ "v1",
118
+ "users",
119
+ "123",
120
+ ]);
121
+ expect(keys.v1.posts.list({ page: 1 })).toEqual([
122
+ "api",
123
+ "v1",
124
+ "posts",
125
+ "list",
126
+ "1",
127
+ ]);
128
+ expect(keys.v1.posts.detail({ id: "456" })).toEqual([
129
+ "api",
130
+ "v1",
131
+ "posts",
132
+ "detail",
133
+ "456",
134
+ ]);
135
+ expect(keys.v1.posts()).toEqual(["api", "v1", "posts"]);
136
+ expect(keys.v1()).toEqual(["api", "v1"]);
137
+ });
138
+ });
139
+ describe("array shorthand", () => {
140
+ it("should handle array shorthand syntax", () => {
141
+ const keys = (0, index_1.createKeyFactory)("base", {
142
+ items: ["list"],
143
+ });
144
+ expect(keys.items()).toEqual(["base", "items", "list"]);
145
+ });
146
+ it("should handle multiple items in array", () => {
147
+ const keys = (0, index_1.createKeyFactory)("base", {
148
+ items: ["list", "all"],
149
+ });
150
+ expect(keys.items()).toEqual(["base", "items", "list", "all"]);
151
+ });
152
+ it("should handle empty arrays", () => {
153
+ const keys = (0, index_1.createKeyFactory)("base", {
154
+ items: [],
155
+ });
156
+ expect(keys.items()).toEqual(["base", "items"]);
157
+ });
158
+ it("should handle array shorthand in nested structures", () => {
159
+ const keys = (0, index_1.createKeyFactory)("app", {
160
+ users: {
161
+ list: ["all"],
162
+ },
163
+ });
164
+ expect(keys.users.list()).toEqual(["app", "users", "list", "all"]);
165
+ });
166
+ it("should handle array shorthand with numeric string values", () => {
167
+ const keys = (0, index_1.createKeyFactory)("base", {
168
+ items: ["1", "2", "3"],
169
+ });
170
+ expect(keys.items()).toEqual(["base", "items", "1", "2", "3"]);
171
+ });
172
+ it("should handle array shorthand in deeply nested structures", () => {
173
+ const keys = (0, index_1.createKeyFactory)("api", {
174
+ v1: {
175
+ users: {
176
+ posts: {
177
+ comments: ["all"],
178
+ },
179
+ },
180
+ },
181
+ });
182
+ expect(keys.v1.users.posts.comments()).toEqual([
183
+ "api",
184
+ "v1",
185
+ "users",
186
+ "posts",
187
+ "comments",
188
+ "all",
189
+ ]);
190
+ expect(keys.v1.users.posts()).toEqual(["api", "v1", "users", "posts"]);
191
+ expect(keys.v1.users()).toEqual(["api", "v1", "users"]);
192
+ expect(keys.v1()).toEqual(["api", "v1"]);
193
+ });
194
+ it("should handle array shorthand with special characters in array values", () => {
195
+ const keys = (0, index_1.createKeyFactory)("base", {
196
+ items: ["key-1", "key_2", "key.3"],
197
+ });
198
+ expect(keys.items()).toEqual(["base", "items", "key-1", "key_2", "key.3"]);
199
+ });
200
+ it("should handle multiple array shorthand keys at the same level", () => {
201
+ const keys = (0, index_1.createKeyFactory)("base", {
202
+ a: ["1"],
203
+ b: ["2"],
204
+ c: ["3"],
205
+ });
206
+ expect(keys.a()).toEqual(["base", "a", "1"]);
207
+ expect(keys.b()).toEqual(["base", "b", "2"]);
208
+ expect(keys.c()).toEqual(["base", "c", "3"]);
209
+ });
210
+ it("should handle array shorthand with bracket notation access", () => {
211
+ const keys = (0, index_1.createKeyFactory)("base", {
212
+ "key-1": ["value1"],
213
+ "key_2": ["value2"],
214
+ });
215
+ expect(keys["key-1"]()).toEqual(["base", "key-1", "value1"]);
216
+ expect(keys["key_2"]()).toEqual(["base", "key_2", "value2"]);
217
+ });
218
+ it("should handle array shorthand with long arrays", () => {
219
+ const keys = (0, index_1.createKeyFactory)("base", {
220
+ items: ["a", "b", "c", "d", "e", "f", "g"],
221
+ });
222
+ expect(keys.items()).toEqual([
223
+ "base",
224
+ "items",
225
+ "a",
226
+ "b",
227
+ "c",
228
+ "d",
229
+ "e",
230
+ "f",
231
+ "g",
232
+ ]);
233
+ });
234
+ it("should handle array shorthand with single character values", () => {
235
+ const keys = (0, index_1.createKeyFactory)("base", {
236
+ items: ["x", "y", "z"],
237
+ });
238
+ expect(keys.items()).toEqual(["base", "items", "x", "y", "z"]);
239
+ });
240
+ it("should handle array shorthand with empty string values", () => {
241
+ const keys = (0, index_1.createKeyFactory)("base", {
242
+ items: ["", "value"],
243
+ });
244
+ expect(keys.items()).toEqual(["base", "items", "", "value"]);
245
+ });
246
+ it("should handle array shorthand at root level with nested objects", () => {
247
+ const keys = (0, index_1.createKeyFactory)("app", {
248
+ root: ["level"],
249
+ nested: {
250
+ deep: ["value"],
251
+ },
252
+ });
253
+ expect(keys.root()).toEqual(["app", "root", "level"]);
254
+ expect(keys.nested.deep()).toEqual(["app", "nested", "deep", "value"]);
255
+ expect(keys.nested()).toEqual(["app", "nested"]);
256
+ });
257
+ it("should handle array shorthand with params parameter (should be optional)", () => {
258
+ const keys = (0, index_1.createKeyFactory)("base", {
259
+ items: ["list"],
260
+ });
261
+ // Array shorthand should accept optional params
262
+ expect(keys.items()).toEqual(["base", "items", "list"]);
263
+ expect(keys.items({})).toEqual(["base", "items", "list"]);
264
+ });
265
+ it("should handle array shorthand in multiple nested levels", () => {
266
+ const keys = (0, index_1.createKeyFactory)("shop", {
267
+ products: {
268
+ list: ["all"],
269
+ featured: ["featured"],
270
+ categories: {
271
+ electronics: ["items"],
272
+ },
273
+ },
274
+ cart: ["items"],
275
+ });
276
+ expect(keys.products.list()).toEqual(["shop", "products", "list", "all"]);
277
+ expect(keys.products.featured()).toEqual([
278
+ "shop",
279
+ "products",
280
+ "featured",
281
+ "featured",
282
+ ]);
283
+ expect(keys.products.categories.electronics()).toEqual([
284
+ "shop",
285
+ "products",
286
+ "categories",
287
+ "electronics",
288
+ "items",
289
+ ]);
290
+ expect(keys.cart()).toEqual(["shop", "cart", "items"]);
291
+ expect(keys.products.categories()).toEqual([
292
+ "shop",
293
+ "products",
294
+ "categories",
295
+ ]);
296
+ expect(keys.products()).toEqual(["shop", "products"]);
297
+ });
298
+ });
299
+ describe("complex real-world scenarios", () => {
300
+ it("should work with a typical react-query setup", () => {
301
+ const queryKeys = (0, index_1.createKeyFactory)("app", {
302
+ users: {
303
+ all: () => [],
304
+ lists: () => ["list"],
305
+ detail: (params) => [params.id],
306
+ posts: (params) => [params.userId, "posts"],
307
+ },
308
+ posts: {
309
+ all: () => [],
310
+ detail: (params) => [params.id],
311
+ comments: (params) => [params.postId, "comments"],
312
+ },
313
+ });
314
+ expect(queryKeys.users.all({})).toEqual(["app", "users", "all"]);
315
+ expect(queryKeys.users.lists({})).toEqual([
316
+ "app",
317
+ "users",
318
+ "lists",
319
+ "list",
320
+ ]);
321
+ expect(queryKeys.users.detail({ id: "123" })).toEqual([
322
+ "app",
323
+ "users",
324
+ "detail",
325
+ "123",
326
+ ]);
327
+ expect(queryKeys.users.posts({ userId: "456" })).toEqual([
328
+ "app",
329
+ "users",
330
+ "posts",
331
+ "456",
332
+ "posts",
333
+ ]);
334
+ expect(queryKeys.users()).toEqual(["app", "users"]);
335
+ expect(queryKeys.posts.all({})).toEqual(["app", "posts", "all"]);
336
+ expect(queryKeys.posts.detail({ id: "789" })).toEqual([
337
+ "app",
338
+ "posts",
339
+ "detail",
340
+ "789",
341
+ ]);
342
+ expect(queryKeys.posts.comments({ postId: "101" })).toEqual([
343
+ "app",
344
+ "posts",
345
+ "comments",
346
+ "101",
347
+ "comments",
348
+ ]);
349
+ expect(queryKeys.posts()).toEqual(["app", "posts"]);
350
+ });
351
+ it("should handle mixed function and array syntax", () => {
352
+ const keys = (0, index_1.createKeyFactory)("shop", {
353
+ products: {
354
+ list: ["all"],
355
+ byCategory: (params) => [params.category],
356
+ byId: (params) => [params.id],
357
+ },
358
+ cart: ["items"],
359
+ });
360
+ expect(keys.products.list()).toEqual(["shop", "products", "list", "all"]);
361
+ expect(keys.products.byCategory({ category: "electronics" })).toEqual([
362
+ "shop",
363
+ "products",
364
+ "byCategory",
365
+ "electronics",
366
+ ]);
367
+ expect(keys.products.byId({ id: "123" })).toEqual([
368
+ "shop",
369
+ "products",
370
+ "byId",
371
+ "123",
372
+ ]);
373
+ expect(keys.products()).toEqual(["shop", "products"]);
374
+ expect(keys.cart()).toEqual(["shop", "cart", "items"]);
375
+ });
376
+ });
377
+ describe("edge cases", () => {
378
+ it("should handle single level with no nesting", () => {
379
+ const keys = (0, index_1.createKeyFactory)("simple", {
380
+ item: () => ["single"],
381
+ });
382
+ expect(keys.item({})).toEqual(["simple", "item", "single"]);
383
+ });
384
+ it("should handle multiple keys at the same level", () => {
385
+ const keys = (0, index_1.createKeyFactory)("base", {
386
+ a: () => ["1"],
387
+ b: () => ["2"],
388
+ c: () => ["3"],
389
+ });
390
+ expect(keys.a({})).toEqual(["base", "a", "1"]);
391
+ expect(keys.b({})).toEqual(["base", "b", "2"]);
392
+ expect(keys.c({})).toEqual(["base", "c", "3"]);
393
+ });
394
+ it("should handle numeric and special characters in keys", () => {
395
+ const keys = (0, index_1.createKeyFactory)("base", {
396
+ "key-1": () => ["value"],
397
+ key_2: () => ["value2"],
398
+ });
399
+ expect(keys["key-1"]({})).toEqual(["base", "key-1", "value"]);
400
+ expect(keys["key_2"]({})).toEqual(["base", "key_2", "value2"]);
401
+ });
402
+ it("should support bracket notation with proper types for function parameters", () => {
403
+ const keys = (0, index_1.createKeyFactory)("base", {
404
+ "key-with-params": (params) => [params.id, params.type],
405
+ "another-key": () => ["static"],
406
+ });
407
+ // Bracket notation should work with proper types
408
+ const keyWithParams = keys["key-with-params"];
409
+ const anotherKey = keys["another-key"];
410
+ // TypeScript should know the parameter types
411
+ expect(keyWithParams({ id: "123", type: "admin" })).toEqual([
412
+ "base",
413
+ "key-with-params",
414
+ "123",
415
+ "admin",
416
+ ]);
417
+ expect(anotherKey({})).toEqual(["base", "another-key", "static"]);
418
+ });
419
+ });
420
+ });
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "awesome-key-factory",
3
+ "version": "1.0.0",
4
+ "description": "A type-safe key factory for managing react-query keys",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "test": "jest",
10
+ "prepublishOnly": "yarn build && yarn test",
11
+ "prepack": "yarn build",
12
+ "publish": "npm publish",
13
+ "publish:dry-run": "npm publish --dry-run",
14
+ "publish:public": "npm publish --access public",
15
+ "publish:beta": "npm publish --tag beta",
16
+ "publish:next": "npm publish --tag next",
17
+ "version:patch": "yarn version --patch --no-git-tag-version",
18
+ "version:minor": "yarn version --minor --no-git-tag-version",
19
+ "version:major": "yarn version --major --no-git-tag-version",
20
+ "release:patch": "yarn version:patch && yarn publish:public && git push && git push --tags",
21
+ "release:minor": "yarn version:minor && yarn publish:public && git push && git push --tags",
22
+ "release:major": "yarn version:major && yarn publish:public && git push && git push --tags",
23
+ "lint": "eslint . --ext .ts",
24
+ "lint:fix": "eslint . --ext .ts --fix",
25
+ "type-check": "tsc --noEmit",
26
+ "check": "yarn type-check && yarn lint",
27
+ "docs:dev": "cd vitepress-docs && yarn dev",
28
+ "docs:build": "cd vitepress-docs && yarn build",
29
+ "docs:preview": "cd vitepress-docs && yarn preview"
30
+ },
31
+ "keywords": [
32
+ "react-query",
33
+ "query-keys",
34
+ "typescript",
35
+ "type-safe"
36
+ ],
37
+ "author": "",
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/bhaskar20/awesome-key-factory.git"
42
+ },
43
+ "homepage": "https://bhaskar20.github.io/awesome-key-factory/",
44
+ "bugs": {
45
+ "url": "https://github.com/bhaskar20/awesome-key-factory/issues"
46
+ },
47
+ "devDependencies": {
48
+ "@astrojs/starlight": "^0.37.3",
49
+ "@types/jest": "^29.5.0",
50
+ "@types/node": "^20.0.0",
51
+ "@typescript-eslint/eslint-plugin": "^8.53.0",
52
+ "@typescript-eslint/parser": "^8.53.0",
53
+ "astro": "^5.16.10",
54
+ "eslint": "^9.39.2",
55
+ "eslint-config-prettier": "^10.1.8",
56
+ "jest": "^29.5.0",
57
+ "ts-jest": "^29.1.0",
58
+ "typescript": "^5.0.0",
59
+ "typescript-eslint": "^8.53.0"
60
+ },
61
+ "peerDependencies": {
62
+ "typescript": "^4.0.0 || ^5.0.0"
63
+ },
64
+ "files": [
65
+ "dist"
66
+ ],
67
+ "packageManager": "yarn@4.12.0+sha512.f45ab632439a67f8bc759bf32ead036a1f413287b9042726b7cc4818b7b49e14e9423ba49b18f9e06ea4941c1ad062385b1d8760a8d5091a1a31e5f6219afca8"
68
+ }