appflare 0.2.48 → 0.2.49

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 (139) hide show
  1. package/Documentation.md +898 -898
  2. package/cli/commands/index.ts +247 -247
  3. package/cli/generate.ts +360 -360
  4. package/cli/index.ts +120 -120
  5. package/cli/load-config.ts +184 -184
  6. package/cli/schema-compiler.ts +1366 -1366
  7. package/cli/templates/auth/README.md +156 -156
  8. package/cli/templates/auth/config.ts +61 -61
  9. package/cli/templates/auth/route-config.ts +1 -1
  10. package/cli/templates/auth/route-handler.ts +1 -1
  11. package/cli/templates/auth/route-request-utils.ts +5 -5
  12. package/cli/templates/auth/route.config.ts +18 -18
  13. package/cli/templates/auth/route.handler.ts +18 -18
  14. package/cli/templates/auth/route.request-utils.ts +55 -55
  15. package/cli/templates/auth/route.ts +14 -14
  16. package/cli/templates/core/README.md +266 -266
  17. package/cli/templates/core/app-creation.ts +19 -19
  18. package/cli/templates/core/client/appflare.ts +112 -112
  19. package/cli/templates/core/client/handlers/index.ts +763 -763
  20. package/cli/templates/core/client/handlers.ts +1 -1
  21. package/cli/templates/core/client/index.ts +7 -7
  22. package/cli/templates/core/client/storage.ts +195 -195
  23. package/cli/templates/core/client/types.ts +187 -187
  24. package/cli/templates/core/client-modules/appflare.ts +1 -1
  25. package/cli/templates/core/client-modules/handlers.ts +1 -1
  26. package/cli/templates/core/client-modules/index.ts +1 -1
  27. package/cli/templates/core/client-modules/storage.ts +1 -1
  28. package/cli/templates/core/client-modules/types.ts +1 -1
  29. package/cli/templates/core/client.artifacts.ts +39 -39
  30. package/cli/templates/core/client.ts +4 -4
  31. package/cli/templates/core/drizzle.ts +15 -15
  32. package/cli/templates/core/export.ts +14 -14
  33. package/cli/templates/core/handlers.route.ts +24 -24
  34. package/cli/templates/core/handlers.ts +1 -1
  35. package/cli/templates/core/imports.ts +9 -9
  36. package/cli/templates/core/server.ts +38 -38
  37. package/cli/templates/core/types.ts +6 -6
  38. package/cli/templates/core/wrangler.ts +109 -109
  39. package/cli/templates/dashboard/builders/functions/index.ts +17 -17
  40. package/cli/templates/dashboard/builders/functions/render-page/header.ts +20 -20
  41. package/cli/templates/dashboard/builders/functions/render-page/index.ts +33 -33
  42. package/cli/templates/dashboard/builders/functions/render-page/request-panel.ts +271 -271
  43. package/cli/templates/dashboard/builders/functions/render-page/result-panel.ts +85 -85
  44. package/cli/templates/dashboard/builders/functions/render-page/scripts.ts +703 -703
  45. package/cli/templates/dashboard/builders/functions/tree-builder.ts +47 -47
  46. package/cli/templates/dashboard/builders/navigation.ts +155 -155
  47. package/cli/templates/dashboard/builders/storage/index.ts +13 -13
  48. package/cli/templates/dashboard/builders/storage/routes/create-directory-route.ts +29 -29
  49. package/cli/templates/dashboard/builders/storage/routes/delete-route.ts +18 -18
  50. package/cli/templates/dashboard/builders/storage/routes/download-route.ts +23 -23
  51. package/cli/templates/dashboard/builders/storage/routes/index.ts +22 -22
  52. package/cli/templates/dashboard/builders/storage/routes/list-route.ts +25 -25
  53. package/cli/templates/dashboard/builders/storage/routes/preview-route.ts +21 -21
  54. package/cli/templates/dashboard/builders/storage/routes/upload-route.ts +21 -21
  55. package/cli/templates/dashboard/builders/storage/runtime/helpers.ts +72 -72
  56. package/cli/templates/dashboard/builders/storage/runtime/storage-page.ts +130 -130
  57. package/cli/templates/dashboard/builders/table-routes/common/drawer-panel.ts +27 -27
  58. package/cli/templates/dashboard/builders/table-routes/common/pagination.ts +30 -30
  59. package/cli/templates/dashboard/builders/table-routes/common/search-bar.ts +23 -23
  60. package/cli/templates/dashboard/builders/table-routes/fragments.ts +257 -217
  61. package/cli/templates/dashboard/builders/table-routes/helpers.ts +45 -45
  62. package/cli/templates/dashboard/builders/table-routes/index.ts +8 -8
  63. package/cli/templates/dashboard/builders/table-routes/table/actions-cell.ts +71 -71
  64. package/cli/templates/dashboard/builders/table-routes/table/get-route.ts +291 -291
  65. package/cli/templates/dashboard/builders/table-routes/table/index.ts +80 -80
  66. package/cli/templates/dashboard/builders/table-routes/table/post-routes.ts +163 -163
  67. package/cli/templates/dashboard/builders/table-routes/table-route.ts +7 -7
  68. package/cli/templates/dashboard/builders/table-routes/users/get-route.ts +69 -69
  69. package/cli/templates/dashboard/builders/table-routes/users/html/modals.ts +57 -57
  70. package/cli/templates/dashboard/builders/table-routes/users/html/page.ts +27 -27
  71. package/cli/templates/dashboard/builders/table-routes/users/html/table.ts +128 -128
  72. package/cli/templates/dashboard/builders/table-routes/users/index.ts +32 -32
  73. package/cli/templates/dashboard/builders/table-routes/users/post-routes.ts +150 -150
  74. package/cli/templates/dashboard/builders/table-routes/users/redirect.ts +14 -14
  75. package/cli/templates/dashboard/builders/table-routes/users-route.ts +10 -10
  76. package/cli/templates/dashboard/components/dashboard-home.ts +23 -23
  77. package/cli/templates/dashboard/components/layout.ts +420 -420
  78. package/cli/templates/dashboard/components/login-page.ts +65 -65
  79. package/cli/templates/dashboard/index.ts +61 -61
  80. package/cli/templates/dashboard/types.ts +9 -9
  81. package/cli/templates/handlers/README.md +353 -353
  82. package/cli/templates/handlers/auth.ts +37 -37
  83. package/cli/templates/handlers/execution.ts +44 -42
  84. package/cli/templates/handlers/generators/context/context-creation.ts +101 -101
  85. package/cli/templates/handlers/generators/context/error-helpers.ts +11 -11
  86. package/cli/templates/handlers/generators/context/scheduler.ts +24 -24
  87. package/cli/templates/handlers/generators/context/storage-api.ts +82 -82
  88. package/cli/templates/handlers/generators/context/storage-helpers.ts +59 -59
  89. package/cli/templates/handlers/generators/context/types.ts +40 -40
  90. package/cli/templates/handlers/generators/context.ts +43 -43
  91. package/cli/templates/handlers/generators/execution.ts +15 -15
  92. package/cli/templates/handlers/generators/handlers.ts +14 -14
  93. package/cli/templates/handlers/generators/registration/modules/cron.ts +35 -35
  94. package/cli/templates/handlers/generators/registration/modules/realtime/auth.ts +75 -75
  95. package/cli/templates/handlers/generators/registration/modules/realtime/durable-object.ts +144 -144
  96. package/cli/templates/handlers/generators/registration/modules/realtime/index.ts +14 -14
  97. package/cli/templates/handlers/generators/registration/modules/realtime/publisher.ts +102 -102
  98. package/cli/templates/handlers/generators/registration/modules/realtime/routes.ts +164 -164
  99. package/cli/templates/handlers/generators/registration/modules/realtime/types.ts +30 -30
  100. package/cli/templates/handlers/generators/registration/modules/realtime/utils.ts +510 -510
  101. package/cli/templates/handlers/generators/registration/modules/scheduler.ts +65 -65
  102. package/cli/templates/handlers/generators/registration/modules/storage.ts +199 -199
  103. package/cli/templates/handlers/generators/registration/sections.ts +210 -210
  104. package/cli/templates/handlers/generators/types/context.ts +121 -121
  105. package/cli/templates/handlers/generators/types/core.ts +108 -108
  106. package/cli/templates/handlers/generators/types/operations.ts +135 -135
  107. package/cli/templates/handlers/generators/types/query-definitions/filter-and-where-types.ts +291 -291
  108. package/cli/templates/handlers/generators/types/query-definitions/query-api-types.ts +135 -135
  109. package/cli/templates/handlers/generators/types/query-definitions/query-helper-functions.ts +1382 -1382
  110. package/cli/templates/handlers/generators/types/query-definitions/schema-and-table-types.ts +278 -278
  111. package/cli/templates/handlers/generators/types/query-definitions.ts +13 -13
  112. package/cli/templates/handlers/generators/types/query-runtime/handled-error.ts +13 -13
  113. package/cli/templates/handlers/generators/types/query-runtime/runtime-aggregate-and-footer.ts +174 -174
  114. package/cli/templates/handlers/generators/types/query-runtime/runtime-read.ts +156 -156
  115. package/cli/templates/handlers/generators/types/query-runtime/runtime-setup.ts +45 -45
  116. package/cli/templates/handlers/generators/types/query-runtime/runtime-write.ts +958 -958
  117. package/cli/templates/handlers/generators/types/query-runtime.ts +15 -15
  118. package/cli/templates/handlers/index.ts +47 -47
  119. package/cli/templates/handlers/operations.ts +116 -116
  120. package/cli/templates/handlers/registration.ts +91 -91
  121. package/cli/templates/handlers/types.ts +17 -17
  122. package/cli/templates/handlers/utils.ts +48 -48
  123. package/cli/types.ts +110 -110
  124. package/cli/utils/handler-discovery.ts +501 -501
  125. package/cli/utils/json-utils.ts +24 -24
  126. package/cli/utils/path-utils.ts +19 -19
  127. package/cli/utils/schema-discovery.ts +402 -399
  128. package/dist/cli/index.js +77 -55
  129. package/dist/cli/index.mjs +77 -55
  130. package/index.ts +18 -18
  131. package/package.json +58 -58
  132. package/react/index.ts +5 -5
  133. package/react/use-infinite-query.ts +255 -255
  134. package/react/use-mutation.ts +89 -89
  135. package/react/use-query.ts +210 -210
  136. package/schema.ts +641 -641
  137. package/test-better-auth-hash.ts +2 -2
  138. package/tsconfig.json +6 -6
  139. package/tsup.config.ts +82 -82
package/package.json CHANGED
@@ -1,58 +1,58 @@
1
- {
2
- "name": "appflare",
3
- "version": "0.2.48",
4
- "main": "./dist/index.js",
5
- "module": "./dist/index.mjs",
6
- "types": "./dist/index.d.ts",
7
- "bin": {
8
- "appflare": "./dist/cli/index.js"
9
- },
10
- "exports": {
11
- ".": {
12
- "types": "./dist/index.d.ts",
13
- "import": "./dist/index.mjs",
14
- "require": "./dist/index.js"
15
- },
16
- "./react": {
17
- "types": "./dist/react/index.d.ts",
18
- "import": "./dist/react/index.mjs",
19
- "require": "./dist/react/index.js"
20
- },
21
- "./*": "./dist/*.js"
22
- },
23
- "scripts": {
24
- "build": "tsup",
25
- "dev": "tsup --watch",
26
- "lint": "tsc --noEmit",
27
- "prepublishOnly": "bun run build"
28
- },
29
- "peerDependencies": {
30
- "@tanstack/react-query": "^5.90.21",
31
- "react": ">=18"
32
- },
33
- "devDependencies": {
34
- "@cloudflare/workers-types": "^4.20260219.0",
35
- "@types/react": "^19.2.0",
36
- "@tanstack/react-query": "^5.90.21",
37
- "@types/bun": "^1.3.9",
38
- "@types/node": "^25.3.0",
39
- "npm-check-updates": "^19.4.0",
40
- "tsup": "^8.3.6"
41
- },
42
- "dependencies": {
43
- "@better-auth/cli": "^1.4.21",
44
- "@hono/standard-validator": "0.2.2",
45
- "better-auth": "^1.5.5",
46
- "better-auth-cloudflare": "0.2.9",
47
- "better-fetch": "1.1.2",
48
- "bun": "1.3.9",
49
- "chokidar": "5.0.0",
50
- "commander": "14.0.3",
51
- "drizzle-kit": "0.31.9",
52
- "drizzle-orm": "0.45.1",
53
- "hono": "4.12.0",
54
- "typescript": "6.0.3",
55
- "wrangler": "4.67.0",
56
- "zod": "4.3.6"
57
- }
58
- }
1
+ {
2
+ "name": "appflare",
3
+ "version": "0.2.49",
4
+ "main": "./dist/index.js",
5
+ "module": "./dist/index.mjs",
6
+ "types": "./dist/index.d.ts",
7
+ "bin": {
8
+ "appflare": "./dist/cli/index.js"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.mjs",
14
+ "require": "./dist/index.js"
15
+ },
16
+ "./react": {
17
+ "types": "./dist/react/index.d.ts",
18
+ "import": "./dist/react/index.mjs",
19
+ "require": "./dist/react/index.js"
20
+ },
21
+ "./*": "./dist/*.js"
22
+ },
23
+ "scripts": {
24
+ "build": "tsup",
25
+ "dev": "tsup --watch",
26
+ "lint": "tsc --noEmit",
27
+ "prepublishOnly": "bun run build"
28
+ },
29
+ "peerDependencies": {
30
+ "@tanstack/react-query": "^5.90.21",
31
+ "react": ">=18"
32
+ },
33
+ "devDependencies": {
34
+ "@cloudflare/workers-types": "^4.20260219.0",
35
+ "@types/react": "^19.2.0",
36
+ "@tanstack/react-query": "^5.90.21",
37
+ "@types/bun": "^1.3.9",
38
+ "@types/node": "^25.3.0",
39
+ "npm-check-updates": "^19.4.0",
40
+ "tsup": "^8.3.6"
41
+ },
42
+ "dependencies": {
43
+ "@better-auth/cli": "^1.4.21",
44
+ "@hono/standard-validator": "0.2.2",
45
+ "better-auth": "^1.5.5",
46
+ "better-auth-cloudflare": "0.2.9",
47
+ "better-fetch": "1.1.2",
48
+ "bun": "1.3.9",
49
+ "chokidar": "5.0.0",
50
+ "commander": "14.0.3",
51
+ "drizzle-kit": "0.31.9",
52
+ "drizzle-orm": "0.45.1",
53
+ "hono": "4.12.0",
54
+ "typescript": "6.0.3",
55
+ "wrangler": "4.67.0",
56
+ "zod": "4.3.6"
57
+ }
58
+ }
package/react/index.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { useQuery } from "./use-query";
2
- export type { UseAppflareQueryOptions } from "./use-query";
3
- export { useInfiniteQuery } from "./use-infinite-query";
4
- export type { UseAppflareInfiniteQueryOptions } from "./use-infinite-query";
5
- export { useMutation } from "./use-mutation";
1
+ export { useQuery } from "./use-query";
2
+ export type { UseAppflareQueryOptions } from "./use-query";
3
+ export { useInfiniteQuery } from "./use-infinite-query";
4
+ export type { UseAppflareInfiniteQueryOptions } from "./use-infinite-query";
5
+ export { useMutation } from "./use-mutation";
@@ -1,255 +1,255 @@
1
- import {
2
- hashKey,
3
- type InfiniteData,
4
- type QueryKey,
5
- type UseInfiniteQueryOptions,
6
- type UseInfiniteQueryResult,
7
- useInfiniteQuery as useTanstackInfiniteQuery,
8
- useQueryClient,
9
- } from "@tanstack/react-query";
10
- import { useEffect, useMemo, useRef } from "react";
11
-
12
- type AppflareRequestErrorLike = {
13
- message: string;
14
- status?: number;
15
- };
16
-
17
- type AppflareRequestResultLike<TData> = {
18
- data: TData | null;
19
- error: AppflareRequestErrorLike | null;
20
- };
21
-
22
- type AppflareRealtimeSubscriptionLike = {
23
- remove: () => void;
24
- };
25
-
26
- type AppflareRealtimeQueryUpdateLike<TData> = {
27
- event: "query:update";
28
- payload: {
29
- queryName: string;
30
- signature: string;
31
- data: TData;
32
- };
33
- };
34
-
35
- type AppflareRequiredInputKeys<TInput extends Record<string, unknown>> = {
36
- [K in keyof TInput]-?: undefined extends TInput[K] ? never : K;
37
- }[keyof TInput];
38
-
39
- type AppflareHasRequiredInputKeys<TInput extends Record<string, unknown>> = [
40
- AppflareRequiredInputKeys<TInput>,
41
- ] extends [never]
42
- ? false
43
- : true;
44
-
45
- type AppflareRouteRunArgs<TInput extends Record<string, unknown>> =
46
- AppflareHasRequiredInputKeys<TInput> extends true
47
- ? [args: TInput, options?: any]
48
- : [args?: TInput, options?: any];
49
-
50
- type AppflareQueryLike<TArgs extends Record<string, unknown>, TData> = {
51
- run: (
52
- ...params: AppflareRouteRunArgs<TArgs>
53
- ) => Promise<AppflareRequestResultLike<TData>>;
54
- subscribe?: (options: {
55
- args?: TArgs;
56
- authToken?: string;
57
- requestOptions?: any;
58
- signal?: AbortSignal;
59
- onChange: (
60
- data: TData,
61
- update: AppflareRealtimeQueryUpdateLike<TData>,
62
- ) => void;
63
- onError?: (error: unknown) => void;
64
- }) => AppflareRealtimeSubscriptionLike;
65
- queryKey?: (args?: TArgs) => QueryKey;
66
- };
67
-
68
- export type UseAppflareInfiniteQueryOptions<
69
- TArgs extends Record<string, unknown>,
70
- TData,
71
- TPageParam,
72
- TSelected = InfiniteData<TData, TPageParam>,
73
- TKey extends QueryKey = QueryKey,
74
- > = {
75
- realtime?: {
76
- enabled?: boolean;
77
- authToken?: string;
78
- requestOptions?: any;
79
- onChange?: (
80
- data: TData,
81
- update: AppflareRealtimeQueryUpdateLike<TData>,
82
- ) => void;
83
- onError?: (error: unknown) => void;
84
- };
85
- requestOptions?: any;
86
- pageParamToArgs?: (baseArgs: TArgs, pageParam: TPageParam) => TArgs;
87
- queryOptions?: Omit<
88
- UseInfiniteQueryOptions<TData, Error, TSelected, TKey, TPageParam>,
89
- "queryFn" | "queryKey"
90
- > & {
91
- queryKey?: TKey;
92
- };
93
- };
94
-
95
- type UseInfiniteQueryCallParams<
96
- TArgs extends Record<string, unknown>,
97
- TData,
98
- TPageParam,
99
- TSelected,
100
- TKey extends QueryKey,
101
- > =
102
- AppflareHasRequiredInputKeys<TArgs> extends true
103
- ? [
104
- args: TArgs,
105
- options?: UseAppflareInfiniteQueryOptions<
106
- TArgs,
107
- TData,
108
- TPageParam,
109
- TSelected,
110
- TKey
111
- >,
112
- ]
113
- : [
114
- args?: TArgs,
115
- options?: UseAppflareInfiniteQueryOptions<
116
- TArgs,
117
- TData,
118
- TPageParam,
119
- TSelected,
120
- TKey
121
- >,
122
- ];
123
-
124
- function toError(error: AppflareRequestErrorLike): Error {
125
- const next = new Error(error.message);
126
- if (error.status !== undefined) {
127
- (next as Error & { status?: number }).status = error.status;
128
- }
129
- return next;
130
- }
131
-
132
- export function useInfiniteQuery<
133
- TArgs extends Record<string, unknown>,
134
- TData,
135
- TPageParam = unknown,
136
- TSelected = InfiniteData<TData, TPageParam>,
137
- TKey extends QueryKey = QueryKey,
138
- >(
139
- query: AppflareQueryLike<TArgs, TData>,
140
- ...params: UseInfiniteQueryCallParams<
141
- TArgs,
142
- TData,
143
- TPageParam,
144
- TSelected,
145
- TKey
146
- >
147
- ): UseInfiniteQueryResult<TSelected, Error> {
148
- const args = (params[0] ?? {}) as TArgs;
149
- const options = params[1];
150
- const queryClient = useQueryClient();
151
- const realtimeOnChangeRef = useRef(options?.realtime?.onChange);
152
- const realtimeOnErrorRef = useRef(options?.realtime?.onError);
153
-
154
- useEffect(() => {
155
- realtimeOnChangeRef.current = options?.realtime?.onChange;
156
- realtimeOnErrorRef.current = options?.realtime?.onError;
157
- }, [options?.realtime?.onChange, options?.realtime?.onError]);
158
-
159
- const resolvedQueryKey = useMemo(() => {
160
- return (
161
- options?.queryOptions?.queryKey ??
162
- (query.queryKey
163
- ? query.queryKey(args)
164
- : (["appflare", "infinite-query", query, args] as unknown as TKey))
165
- ) as TKey
166
- }, [args, options?.queryOptions?.queryKey, query]);
167
-
168
- const resolvedQueryKeyHash = useMemo(
169
- () => hashKey(resolvedQueryKey as QueryKey),
170
- [resolvedQueryKey],
171
- );
172
-
173
- const realtimeArgsHash = useMemo(() => hashKey([args] as QueryKey), [args]);
174
-
175
- const realtimeRequestOptionsHash = useMemo(
176
- () => hashKey([options?.realtime?.requestOptions] as QueryKey),
177
- [options?.realtime?.requestOptions],
178
- );
179
-
180
- const result = useTanstackInfiniteQuery<
181
- TData,
182
- Error,
183
- TSelected,
184
- TKey,
185
- TPageParam
186
- >({
187
- ...(options?.queryOptions as Omit<
188
- UseInfiniteQueryOptions<TData, Error, TSelected, TKey, TPageParam>,
189
- "queryFn" | "queryKey"
190
- >),
191
- queryKey: resolvedQueryKey,
192
- queryFn: async ({ pageParam }) => {
193
- const nextArgs =
194
- pageParam === undefined || !options?.pageParamToArgs
195
- ? args
196
- : options.pageParamToArgs(args, pageParam as TPageParam);
197
- const response = await query.run(nextArgs, options?.requestOptions);
198
- if (response.error) {
199
- throw toError(response.error);
200
- }
201
- return response.data as TData;
202
- },
203
- });
204
-
205
- useEffect(() => {
206
- if (!options?.realtime?.enabled || !query.subscribe) {
207
- return;
208
- }
209
-
210
- const controller = new AbortController();
211
- const subscription = query.subscribe({
212
- args,
213
- authToken: options?.realtime?.authToken,
214
- requestOptions: options?.realtime?.requestOptions,
215
- signal: controller.signal,
216
- onChange: (data, update) => {
217
- queryClient.setQueryData<InfiniteData<TData, TPageParam>>(
218
- resolvedQueryKey,
219
- (previous) => {
220
- if (!previous || previous.pages.length === 0) {
221
- return previous;
222
- }
223
-
224
- return {
225
- ...previous,
226
- pages: [data as TData, ...previous.pages.slice(1)],
227
- };
228
- },
229
- );
230
- realtimeOnChangeRef.current?.(
231
- data as TData,
232
- update as AppflareRealtimeQueryUpdateLike<TData>,
233
- );
234
- },
235
- onError: (error) => {
236
- realtimeOnErrorRef.current?.(error);
237
- },
238
- });
239
-
240
- return () => {
241
- controller.abort();
242
- subscription.remove();
243
- };
244
- }, [
245
- realtimeArgsHash,
246
- options?.realtime?.authToken,
247
- options?.realtime?.enabled,
248
- realtimeRequestOptionsHash,
249
- query.subscribe,
250
- queryClient,
251
- resolvedQueryKeyHash,
252
- ]);
253
-
254
- return result;
255
- }
1
+ import {
2
+ hashKey,
3
+ type InfiniteData,
4
+ type QueryKey,
5
+ type UseInfiniteQueryOptions,
6
+ type UseInfiniteQueryResult,
7
+ useInfiniteQuery as useTanstackInfiniteQuery,
8
+ useQueryClient,
9
+ } from "@tanstack/react-query";
10
+ import { useEffect, useMemo, useRef } from "react";
11
+
12
+ type AppflareRequestErrorLike = {
13
+ message: string;
14
+ status?: number;
15
+ };
16
+
17
+ type AppflareRequestResultLike<TData> = {
18
+ data: TData | null;
19
+ error: AppflareRequestErrorLike | null;
20
+ };
21
+
22
+ type AppflareRealtimeSubscriptionLike = {
23
+ remove: () => void;
24
+ };
25
+
26
+ type AppflareRealtimeQueryUpdateLike<TData> = {
27
+ event: "query:update";
28
+ payload: {
29
+ queryName: string;
30
+ signature: string;
31
+ data: TData;
32
+ };
33
+ };
34
+
35
+ type AppflareRequiredInputKeys<TInput extends Record<string, unknown>> = {
36
+ [K in keyof TInput]-?: undefined extends TInput[K] ? never : K;
37
+ }[keyof TInput];
38
+
39
+ type AppflareHasRequiredInputKeys<TInput extends Record<string, unknown>> = [
40
+ AppflareRequiredInputKeys<TInput>,
41
+ ] extends [never]
42
+ ? false
43
+ : true;
44
+
45
+ type AppflareRouteRunArgs<TInput extends Record<string, unknown>> =
46
+ AppflareHasRequiredInputKeys<TInput> extends true
47
+ ? [args: TInput, options?: any]
48
+ : [args?: TInput, options?: any];
49
+
50
+ type AppflareQueryLike<TArgs extends Record<string, unknown>, TData> = {
51
+ run: (
52
+ ...params: AppflareRouteRunArgs<TArgs>
53
+ ) => Promise<AppflareRequestResultLike<TData>>;
54
+ subscribe?: (options: {
55
+ args?: TArgs;
56
+ authToken?: string;
57
+ requestOptions?: any;
58
+ signal?: AbortSignal;
59
+ onChange: (
60
+ data: TData,
61
+ update: AppflareRealtimeQueryUpdateLike<TData>,
62
+ ) => void;
63
+ onError?: (error: unknown) => void;
64
+ }) => AppflareRealtimeSubscriptionLike;
65
+ queryKey?: (args?: TArgs) => QueryKey;
66
+ };
67
+
68
+ export type UseAppflareInfiniteQueryOptions<
69
+ TArgs extends Record<string, unknown>,
70
+ TData,
71
+ TPageParam,
72
+ TSelected = InfiniteData<TData, TPageParam>,
73
+ TKey extends QueryKey = QueryKey,
74
+ > = {
75
+ realtime?: {
76
+ enabled?: boolean;
77
+ authToken?: string;
78
+ requestOptions?: any;
79
+ onChange?: (
80
+ data: TData,
81
+ update: AppflareRealtimeQueryUpdateLike<TData>,
82
+ ) => void;
83
+ onError?: (error: unknown) => void;
84
+ };
85
+ requestOptions?: any;
86
+ pageParamToArgs?: (baseArgs: TArgs, pageParam: TPageParam) => TArgs;
87
+ queryOptions?: Omit<
88
+ UseInfiniteQueryOptions<TData, Error, TSelected, TKey, TPageParam>,
89
+ "queryFn" | "queryKey"
90
+ > & {
91
+ queryKey?: TKey;
92
+ };
93
+ };
94
+
95
+ type UseInfiniteQueryCallParams<
96
+ TArgs extends Record<string, unknown>,
97
+ TData,
98
+ TPageParam,
99
+ TSelected,
100
+ TKey extends QueryKey,
101
+ > =
102
+ AppflareHasRequiredInputKeys<TArgs> extends true
103
+ ? [
104
+ args: TArgs,
105
+ options?: UseAppflareInfiniteQueryOptions<
106
+ TArgs,
107
+ TData,
108
+ TPageParam,
109
+ TSelected,
110
+ TKey
111
+ >,
112
+ ]
113
+ : [
114
+ args?: TArgs,
115
+ options?: UseAppflareInfiniteQueryOptions<
116
+ TArgs,
117
+ TData,
118
+ TPageParam,
119
+ TSelected,
120
+ TKey
121
+ >,
122
+ ];
123
+
124
+ function toError(error: AppflareRequestErrorLike): Error {
125
+ const next = new Error(error.message);
126
+ if (error.status !== undefined) {
127
+ (next as Error & { status?: number }).status = error.status;
128
+ }
129
+ return next;
130
+ }
131
+
132
+ export function useInfiniteQuery<
133
+ TArgs extends Record<string, unknown>,
134
+ TData,
135
+ TPageParam = unknown,
136
+ TSelected = InfiniteData<TData, TPageParam>,
137
+ TKey extends QueryKey = QueryKey,
138
+ >(
139
+ query: AppflareQueryLike<TArgs, TData>,
140
+ ...params: UseInfiniteQueryCallParams<
141
+ TArgs,
142
+ TData,
143
+ TPageParam,
144
+ TSelected,
145
+ TKey
146
+ >
147
+ ): UseInfiniteQueryResult<TSelected, Error> {
148
+ const args = (params[0] ?? {}) as TArgs;
149
+ const options = params[1];
150
+ const queryClient = useQueryClient();
151
+ const realtimeOnChangeRef = useRef(options?.realtime?.onChange);
152
+ const realtimeOnErrorRef = useRef(options?.realtime?.onError);
153
+
154
+ useEffect(() => {
155
+ realtimeOnChangeRef.current = options?.realtime?.onChange;
156
+ realtimeOnErrorRef.current = options?.realtime?.onError;
157
+ }, [options?.realtime?.onChange, options?.realtime?.onError]);
158
+
159
+ const resolvedQueryKey = useMemo(() => {
160
+ return (
161
+ options?.queryOptions?.queryKey ??
162
+ (query.queryKey
163
+ ? query.queryKey(args)
164
+ : (["appflare", "infinite-query", query, args] as unknown as TKey))
165
+ ) as TKey
166
+ }, [args, options?.queryOptions?.queryKey, query]);
167
+
168
+ const resolvedQueryKeyHash = useMemo(
169
+ () => hashKey(resolvedQueryKey as QueryKey),
170
+ [resolvedQueryKey],
171
+ );
172
+
173
+ const realtimeArgsHash = useMemo(() => hashKey([args] as QueryKey), [args]);
174
+
175
+ const realtimeRequestOptionsHash = useMemo(
176
+ () => hashKey([options?.realtime?.requestOptions] as QueryKey),
177
+ [options?.realtime?.requestOptions],
178
+ );
179
+
180
+ const result = useTanstackInfiniteQuery<
181
+ TData,
182
+ Error,
183
+ TSelected,
184
+ TKey,
185
+ TPageParam
186
+ >({
187
+ ...(options?.queryOptions as Omit<
188
+ UseInfiniteQueryOptions<TData, Error, TSelected, TKey, TPageParam>,
189
+ "queryFn" | "queryKey"
190
+ >),
191
+ queryKey: resolvedQueryKey,
192
+ queryFn: async ({ pageParam }) => {
193
+ const nextArgs =
194
+ pageParam === undefined || !options?.pageParamToArgs
195
+ ? args
196
+ : options.pageParamToArgs(args, pageParam as TPageParam);
197
+ const response = await query.run(nextArgs, options?.requestOptions);
198
+ if (response.error) {
199
+ throw toError(response.error);
200
+ }
201
+ return response.data as TData;
202
+ },
203
+ });
204
+
205
+ useEffect(() => {
206
+ if (!options?.realtime?.enabled || !query.subscribe) {
207
+ return;
208
+ }
209
+
210
+ const controller = new AbortController();
211
+ const subscription = query.subscribe({
212
+ args,
213
+ authToken: options?.realtime?.authToken,
214
+ requestOptions: options?.realtime?.requestOptions,
215
+ signal: controller.signal,
216
+ onChange: (data, update) => {
217
+ queryClient.setQueryData<InfiniteData<TData, TPageParam>>(
218
+ resolvedQueryKey,
219
+ (previous) => {
220
+ if (!previous || previous.pages.length === 0) {
221
+ return previous;
222
+ }
223
+
224
+ return {
225
+ ...previous,
226
+ pages: [data as TData, ...previous.pages.slice(1)],
227
+ };
228
+ },
229
+ );
230
+ realtimeOnChangeRef.current?.(
231
+ data as TData,
232
+ update as AppflareRealtimeQueryUpdateLike<TData>,
233
+ );
234
+ },
235
+ onError: (error) => {
236
+ realtimeOnErrorRef.current?.(error);
237
+ },
238
+ });
239
+
240
+ return () => {
241
+ controller.abort();
242
+ subscription.remove();
243
+ };
244
+ }, [
245
+ realtimeArgsHash,
246
+ options?.realtime?.authToken,
247
+ options?.realtime?.enabled,
248
+ realtimeRequestOptionsHash,
249
+ query.subscribe,
250
+ queryClient,
251
+ resolvedQueryKeyHash,
252
+ ]);
253
+
254
+ return result;
255
+ }