appflare 0.2.29 → 0.2.31

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 (140) hide show
  1. package/Documentation.md +758 -758
  2. package/cli/commands/index.ts +238 -238
  3. package/cli/generate.ts +178 -178
  4. package/cli/index.ts +120 -120
  5. package/cli/load-config.ts +184 -184
  6. package/cli/schema-compiler.ts +1183 -1183
  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 +748 -748
  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 +186 -186
  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 +171 -171
  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 +554 -554
  45. package/cli/templates/dashboard/builders/navigation.ts +122 -122
  46. package/cli/templates/dashboard/builders/storage/index.ts +13 -13
  47. package/cli/templates/dashboard/builders/storage/routes/create-directory-route.ts +29 -29
  48. package/cli/templates/dashboard/builders/storage/routes/delete-route.ts +18 -18
  49. package/cli/templates/dashboard/builders/storage/routes/download-route.ts +23 -23
  50. package/cli/templates/dashboard/builders/storage/routes/index.ts +22 -22
  51. package/cli/templates/dashboard/builders/storage/routes/list-route.ts +25 -25
  52. package/cli/templates/dashboard/builders/storage/routes/preview-route.ts +21 -21
  53. package/cli/templates/dashboard/builders/storage/routes/upload-route.ts +21 -21
  54. package/cli/templates/dashboard/builders/storage/runtime/helpers.ts +72 -72
  55. package/cli/templates/dashboard/builders/storage/runtime/storage-page.ts +130 -130
  56. package/cli/templates/dashboard/builders/table-routes/common/drawer-panel.ts +27 -27
  57. package/cli/templates/dashboard/builders/table-routes/common/pagination.ts +30 -30
  58. package/cli/templates/dashboard/builders/table-routes/common/search-bar.ts +23 -23
  59. package/cli/templates/dashboard/builders/table-routes/fragments.ts +217 -217
  60. package/cli/templates/dashboard/builders/table-routes/helpers.ts +45 -45
  61. package/cli/templates/dashboard/builders/table-routes/index.ts +8 -8
  62. package/cli/templates/dashboard/builders/table-routes/table/actions-cell.ts +71 -71
  63. package/cli/templates/dashboard/builders/table-routes/table/get-route.ts +291 -291
  64. package/cli/templates/dashboard/builders/table-routes/table/index.ts +80 -80
  65. package/cli/templates/dashboard/builders/table-routes/table/post-routes.ts +163 -163
  66. package/cli/templates/dashboard/builders/table-routes/table-route.ts +7 -7
  67. package/cli/templates/dashboard/builders/table-routes/users/get-route.ts +69 -69
  68. package/cli/templates/dashboard/builders/table-routes/users/html/modals.ts +57 -57
  69. package/cli/templates/dashboard/builders/table-routes/users/html/page.ts +27 -27
  70. package/cli/templates/dashboard/builders/table-routes/users/html/table.ts +128 -128
  71. package/cli/templates/dashboard/builders/table-routes/users/index.ts +32 -32
  72. package/cli/templates/dashboard/builders/table-routes/users/post-routes.ts +150 -150
  73. package/cli/templates/dashboard/builders/table-routes/users/redirect.ts +14 -14
  74. package/cli/templates/dashboard/builders/table-routes/users-route.ts +10 -10
  75. package/cli/templates/dashboard/components/dashboard-home.ts +23 -23
  76. package/cli/templates/dashboard/components/layout.ts +388 -388
  77. package/cli/templates/dashboard/components/login-page.ts +65 -65
  78. package/cli/templates/dashboard/index.ts +61 -61
  79. package/cli/templates/dashboard/types.ts +9 -9
  80. package/cli/templates/handlers/README.md +353 -353
  81. package/cli/templates/handlers/auth.ts +37 -37
  82. package/cli/templates/handlers/execution.ts +42 -42
  83. package/cli/templates/handlers/generators/context/context-creation.ts +101 -101
  84. package/cli/templates/handlers/generators/context/error-helpers.ts +11 -11
  85. package/cli/templates/handlers/generators/context/scheduler.ts +24 -24
  86. package/cli/templates/handlers/generators/context/storage-api.ts +82 -82
  87. package/cli/templates/handlers/generators/context/storage-helpers.ts +59 -59
  88. package/cli/templates/handlers/generators/context/types.ts +40 -40
  89. package/cli/templates/handlers/generators/context.ts +43 -43
  90. package/cli/templates/handlers/generators/execution.ts +15 -15
  91. package/cli/templates/handlers/generators/handlers.ts +13 -13
  92. package/cli/templates/handlers/generators/registration/modules/cron.ts +26 -26
  93. package/cli/templates/handlers/generators/registration/modules/realtime/auth.ts +75 -75
  94. package/cli/templates/handlers/generators/registration/modules/realtime/durable-object.ts +144 -144
  95. package/cli/templates/handlers/generators/registration/modules/realtime/index.ts +14 -14
  96. package/cli/templates/handlers/generators/registration/modules/realtime/publisher.ts +102 -102
  97. package/cli/templates/handlers/generators/registration/modules/realtime/routes.ts +164 -164
  98. package/cli/templates/handlers/generators/registration/modules/realtime/types.ts +30 -30
  99. package/cli/templates/handlers/generators/registration/modules/realtime/utils.ts +516 -516
  100. package/cli/templates/handlers/generators/registration/modules/scheduler.ts +56 -56
  101. package/cli/templates/handlers/generators/registration/modules/storage.ts +199 -199
  102. package/cli/templates/handlers/generators/registration/sections.ts +210 -210
  103. package/cli/templates/handlers/generators/types/context.ts +92 -92
  104. package/cli/templates/handlers/generators/types/core.ts +106 -106
  105. package/cli/templates/handlers/generators/types/operations.ts +135 -135
  106. package/cli/templates/handlers/generators/types/query-definitions/filter-and-where-types.ts +281 -259
  107. package/cli/templates/handlers/generators/types/query-definitions/query-api-types.ts +135 -135
  108. package/cli/templates/handlers/generators/types/query-definitions/query-helper-functions.ts +1103 -1031
  109. package/cli/templates/handlers/generators/types/query-definitions/schema-and-table-types.ts +278 -246
  110. package/cli/templates/handlers/generators/types/query-definitions.ts +13 -13
  111. package/cli/templates/handlers/generators/types/query-runtime/handled-error.ts +13 -13
  112. package/cli/templates/handlers/generators/types/query-runtime/runtime-aggregate-and-footer.ts +174 -174
  113. package/cli/templates/handlers/generators/types/query-runtime/runtime-read.ts +157 -121
  114. package/cli/templates/handlers/generators/types/query-runtime/runtime-setup.ts +45 -45
  115. package/cli/templates/handlers/generators/types/query-runtime/runtime-write.ts +697 -676
  116. package/cli/templates/handlers/generators/types/query-runtime.ts +15 -15
  117. package/cli/templates/handlers/index.ts +43 -43
  118. package/cli/templates/handlers/operations.ts +116 -116
  119. package/cli/templates/handlers/registration.ts +91 -91
  120. package/cli/templates/handlers/types.ts +15 -15
  121. package/cli/templates/handlers/utils.ts +48 -48
  122. package/cli/types.ts +110 -110
  123. package/cli/utils/handler-discovery.ts +466 -466
  124. package/cli/utils/json-utils.ts +24 -24
  125. package/cli/utils/path-utils.ts +19 -19
  126. package/cli/utils/schema-discovery.ts +399 -399
  127. package/dist/cli/index.d.mts +2 -0
  128. package/dist/cli/index.d.ts +2 -0
  129. package/dist/cli/index.js +301 -118
  130. package/dist/cli/index.mjs +301 -118
  131. package/index.ts +18 -18
  132. package/package.json +58 -58
  133. package/react/index.ts +5 -5
  134. package/react/use-infinite-query.ts +252 -252
  135. package/react/use-mutation.ts +89 -89
  136. package/react/use-query.ts +207 -207
  137. package/schema.ts +415 -415
  138. package/test-better-auth-hash.ts +2 -2
  139. package/tsconfig.json +6 -6
  140. package/tsup.config.ts +82 -82
package/index.ts CHANGED
@@ -1,18 +1,18 @@
1
- export type {
2
- AppflareConfig,
3
- AppflareDatabaseBinding,
4
- AppflareKVNamespace,
5
- AppflareR2Bucket,
6
- LoadedAppflareConfig,
7
- NormalizedAppflareConfig,
8
- } from "./cli/types";
9
- export {
10
- schema,
11
- table,
12
- v,
13
- isSchemaDefinition,
14
- type ColumnDefinition,
15
- type RelationDefinition,
16
- type SchemaDefinition,
17
- type TableDefinition,
18
- } from "./schema";
1
+ export type {
2
+ AppflareConfig,
3
+ AppflareDatabaseBinding,
4
+ AppflareKVNamespace,
5
+ AppflareR2Bucket,
6
+ LoadedAppflareConfig,
7
+ NormalizedAppflareConfig,
8
+ } from "./cli/types";
9
+ export {
10
+ schema,
11
+ table,
12
+ v,
13
+ isSchemaDefinition,
14
+ type ColumnDefinition,
15
+ type RelationDefinition,
16
+ type SchemaDefinition,
17
+ type TableDefinition,
18
+ } from "./schema";
package/package.json CHANGED
@@ -1,58 +1,58 @@
1
- {
2
- "name": "appflare",
3
- "version": "0.2.29",
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
- "better-auth": "^1.5.5",
45
- "@hono/standard-validator": "0.2.2",
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": "^5.9.3",
55
- "wrangler": "4.67.0",
56
- "zod": "4.3.6"
57
- }
58
- }
1
+ {
2
+ "name": "appflare",
3
+ "version": "0.2.31",
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
+ "better-auth": "^1.5.5",
45
+ "@hono/standard-validator": "0.2.2",
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": "^5.9.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,252 +1,252 @@
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
- };
66
-
67
- export type UseAppflareInfiniteQueryOptions<
68
- TArgs extends Record<string, unknown>,
69
- TData,
70
- TPageParam,
71
- TSelected = InfiniteData<TData, TPageParam>,
72
- TKey extends QueryKey = QueryKey,
73
- > = {
74
- realtime?: {
75
- enabled?: boolean;
76
- authToken?: string;
77
- requestOptions?: any;
78
- onChange?: (
79
- data: TData,
80
- update: AppflareRealtimeQueryUpdateLike<TData>,
81
- ) => void;
82
- onError?: (error: unknown) => void;
83
- };
84
- requestOptions?: any;
85
- pageParamToArgs?: (baseArgs: TArgs, pageParam: TPageParam) => TArgs;
86
- queryOptions?: Omit<
87
- UseInfiniteQueryOptions<TData, Error, TSelected, TKey, TPageParam>,
88
- "queryFn" | "queryKey"
89
- > & {
90
- queryKey?: TKey;
91
- };
92
- };
93
-
94
- type UseInfiniteQueryCallParams<
95
- TArgs extends Record<string, unknown>,
96
- TData,
97
- TPageParam,
98
- TSelected,
99
- TKey extends QueryKey,
100
- > =
101
- AppflareHasRequiredInputKeys<TArgs> extends true
102
- ? [
103
- args: TArgs,
104
- options?: UseAppflareInfiniteQueryOptions<
105
- TArgs,
106
- TData,
107
- TPageParam,
108
- TSelected,
109
- TKey
110
- >,
111
- ]
112
- : [
113
- args?: TArgs,
114
- options?: UseAppflareInfiniteQueryOptions<
115
- TArgs,
116
- TData,
117
- TPageParam,
118
- TSelected,
119
- TKey
120
- >,
121
- ];
122
-
123
- function toError(error: AppflareRequestErrorLike): Error {
124
- const next = new Error(error.message);
125
- if (error.status !== undefined) {
126
- (next as Error & { status?: number }).status = error.status;
127
- }
128
- return next;
129
- }
130
-
131
- export function useInfiniteQuery<
132
- TArgs extends Record<string, unknown>,
133
- TData,
134
- TPageParam = unknown,
135
- TSelected = InfiniteData<TData, TPageParam>,
136
- TKey extends QueryKey = QueryKey,
137
- >(
138
- query: AppflareQueryLike<TArgs, TData>,
139
- ...params: UseInfiniteQueryCallParams<
140
- TArgs,
141
- TData,
142
- TPageParam,
143
- TSelected,
144
- TKey
145
- >
146
- ): UseInfiniteQueryResult<TSelected, Error> {
147
- const args = (params[0] ?? {}) as TArgs;
148
- const options = params[1];
149
- const queryClient = useQueryClient();
150
- const realtimeOnChangeRef = useRef(options?.realtime?.onChange);
151
- const realtimeOnErrorRef = useRef(options?.realtime?.onError);
152
-
153
- useEffect(() => {
154
- realtimeOnChangeRef.current = options?.realtime?.onChange;
155
- realtimeOnErrorRef.current = options?.realtime?.onError;
156
- }, [options?.realtime?.onChange, options?.realtime?.onError]);
157
-
158
- const resolvedQueryKey = useMemo(() => {
159
- return (
160
- options?.queryOptions?.queryKey ??
161
- (["appflare", "infinite-query", query, args] as unknown as TKey)
162
- );
163
- }, [args, options?.queryOptions?.queryKey, query]);
164
-
165
- const resolvedQueryKeyHash = useMemo(
166
- () => hashKey(resolvedQueryKey as QueryKey),
167
- [resolvedQueryKey],
168
- );
169
-
170
- const realtimeArgsHash = useMemo(() => hashKey([args] as QueryKey), [args]);
171
-
172
- const realtimeRequestOptionsHash = useMemo(
173
- () => hashKey([options?.realtime?.requestOptions] as QueryKey),
174
- [options?.realtime?.requestOptions],
175
- );
176
-
177
- const result = useTanstackInfiniteQuery<
178
- TData,
179
- Error,
180
- TSelected,
181
- TKey,
182
- TPageParam
183
- >({
184
- ...(options?.queryOptions as Omit<
185
- UseInfiniteQueryOptions<TData, Error, TSelected, TKey, TPageParam>,
186
- "queryFn" | "queryKey"
187
- >),
188
- queryKey: resolvedQueryKey,
189
- queryFn: async ({ pageParam }) => {
190
- const nextArgs =
191
- pageParam === undefined || !options?.pageParamToArgs
192
- ? args
193
- : options.pageParamToArgs(args, pageParam as TPageParam);
194
- const response = await query.run(nextArgs, options?.requestOptions);
195
- if (response.error) {
196
- throw toError(response.error);
197
- }
198
- return response.data as TData;
199
- },
200
- });
201
-
202
- useEffect(() => {
203
- if (options?.realtime?.enabled === false || !query.subscribe) {
204
- return;
205
- }
206
-
207
- const controller = new AbortController();
208
- const subscription = query.subscribe({
209
- args,
210
- authToken: options.realtime?.authToken,
211
- requestOptions: options.realtime?.requestOptions,
212
- signal: controller.signal,
213
- onChange: (data, update) => {
214
- queryClient.setQueryData<InfiniteData<TData, TPageParam>>(
215
- resolvedQueryKey,
216
- (previous) => {
217
- if (!previous || previous.pages.length === 0) {
218
- return previous;
219
- }
220
-
221
- return {
222
- ...previous,
223
- pages: [data as TData, ...previous.pages.slice(1)],
224
- };
225
- },
226
- );
227
- realtimeOnChangeRef.current?.(
228
- data as TData,
229
- update as AppflareRealtimeQueryUpdateLike<TData>,
230
- );
231
- },
232
- onError: (error) => {
233
- realtimeOnErrorRef.current?.(error);
234
- },
235
- });
236
-
237
- return () => {
238
- controller.abort();
239
- subscription.remove();
240
- };
241
- }, [
242
- realtimeArgsHash,
243
- options?.realtime?.authToken,
244
- options?.realtime?.enabled,
245
- realtimeRequestOptionsHash,
246
- query.subscribe,
247
- queryClient,
248
- resolvedQueryKeyHash,
249
- ]);
250
-
251
- return result;
252
- }
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
+ };
66
+
67
+ export type UseAppflareInfiniteQueryOptions<
68
+ TArgs extends Record<string, unknown>,
69
+ TData,
70
+ TPageParam,
71
+ TSelected = InfiniteData<TData, TPageParam>,
72
+ TKey extends QueryKey = QueryKey,
73
+ > = {
74
+ realtime?: {
75
+ enabled?: boolean;
76
+ authToken?: string;
77
+ requestOptions?: any;
78
+ onChange?: (
79
+ data: TData,
80
+ update: AppflareRealtimeQueryUpdateLike<TData>,
81
+ ) => void;
82
+ onError?: (error: unknown) => void;
83
+ };
84
+ requestOptions?: any;
85
+ pageParamToArgs?: (baseArgs: TArgs, pageParam: TPageParam) => TArgs;
86
+ queryOptions?: Omit<
87
+ UseInfiniteQueryOptions<TData, Error, TSelected, TKey, TPageParam>,
88
+ "queryFn" | "queryKey"
89
+ > & {
90
+ queryKey?: TKey;
91
+ };
92
+ };
93
+
94
+ type UseInfiniteQueryCallParams<
95
+ TArgs extends Record<string, unknown>,
96
+ TData,
97
+ TPageParam,
98
+ TSelected,
99
+ TKey extends QueryKey,
100
+ > =
101
+ AppflareHasRequiredInputKeys<TArgs> extends true
102
+ ? [
103
+ args: TArgs,
104
+ options?: UseAppflareInfiniteQueryOptions<
105
+ TArgs,
106
+ TData,
107
+ TPageParam,
108
+ TSelected,
109
+ TKey
110
+ >,
111
+ ]
112
+ : [
113
+ args?: TArgs,
114
+ options?: UseAppflareInfiniteQueryOptions<
115
+ TArgs,
116
+ TData,
117
+ TPageParam,
118
+ TSelected,
119
+ TKey
120
+ >,
121
+ ];
122
+
123
+ function toError(error: AppflareRequestErrorLike): Error {
124
+ const next = new Error(error.message);
125
+ if (error.status !== undefined) {
126
+ (next as Error & { status?: number }).status = error.status;
127
+ }
128
+ return next;
129
+ }
130
+
131
+ export function useInfiniteQuery<
132
+ TArgs extends Record<string, unknown>,
133
+ TData,
134
+ TPageParam = unknown,
135
+ TSelected = InfiniteData<TData, TPageParam>,
136
+ TKey extends QueryKey = QueryKey,
137
+ >(
138
+ query: AppflareQueryLike<TArgs, TData>,
139
+ ...params: UseInfiniteQueryCallParams<
140
+ TArgs,
141
+ TData,
142
+ TPageParam,
143
+ TSelected,
144
+ TKey
145
+ >
146
+ ): UseInfiniteQueryResult<TSelected, Error> {
147
+ const args = (params[0] ?? {}) as TArgs;
148
+ const options = params[1];
149
+ const queryClient = useQueryClient();
150
+ const realtimeOnChangeRef = useRef(options?.realtime?.onChange);
151
+ const realtimeOnErrorRef = useRef(options?.realtime?.onError);
152
+
153
+ useEffect(() => {
154
+ realtimeOnChangeRef.current = options?.realtime?.onChange;
155
+ realtimeOnErrorRef.current = options?.realtime?.onError;
156
+ }, [options?.realtime?.onChange, options?.realtime?.onError]);
157
+
158
+ const resolvedQueryKey = useMemo(() => {
159
+ return (
160
+ options?.queryOptions?.queryKey ??
161
+ (["appflare", "infinite-query", query, args] as unknown as TKey)
162
+ );
163
+ }, [args, options?.queryOptions?.queryKey, query]);
164
+
165
+ const resolvedQueryKeyHash = useMemo(
166
+ () => hashKey(resolvedQueryKey as QueryKey),
167
+ [resolvedQueryKey],
168
+ );
169
+
170
+ const realtimeArgsHash = useMemo(() => hashKey([args] as QueryKey), [args]);
171
+
172
+ const realtimeRequestOptionsHash = useMemo(
173
+ () => hashKey([options?.realtime?.requestOptions] as QueryKey),
174
+ [options?.realtime?.requestOptions],
175
+ );
176
+
177
+ const result = useTanstackInfiniteQuery<
178
+ TData,
179
+ Error,
180
+ TSelected,
181
+ TKey,
182
+ TPageParam
183
+ >({
184
+ ...(options?.queryOptions as Omit<
185
+ UseInfiniteQueryOptions<TData, Error, TSelected, TKey, TPageParam>,
186
+ "queryFn" | "queryKey"
187
+ >),
188
+ queryKey: resolvedQueryKey,
189
+ queryFn: async ({ pageParam }) => {
190
+ const nextArgs =
191
+ pageParam === undefined || !options?.pageParamToArgs
192
+ ? args
193
+ : options.pageParamToArgs(args, pageParam as TPageParam);
194
+ const response = await query.run(nextArgs, options?.requestOptions);
195
+ if (response.error) {
196
+ throw toError(response.error);
197
+ }
198
+ return response.data as TData;
199
+ },
200
+ });
201
+
202
+ useEffect(() => {
203
+ if (options?.realtime?.enabled === false || !query.subscribe) {
204
+ return;
205
+ }
206
+
207
+ const controller = new AbortController();
208
+ const subscription = query.subscribe({
209
+ args,
210
+ authToken: options.realtime?.authToken,
211
+ requestOptions: options.realtime?.requestOptions,
212
+ signal: controller.signal,
213
+ onChange: (data, update) => {
214
+ queryClient.setQueryData<InfiniteData<TData, TPageParam>>(
215
+ resolvedQueryKey,
216
+ (previous) => {
217
+ if (!previous || previous.pages.length === 0) {
218
+ return previous;
219
+ }
220
+
221
+ return {
222
+ ...previous,
223
+ pages: [data as TData, ...previous.pages.slice(1)],
224
+ };
225
+ },
226
+ );
227
+ realtimeOnChangeRef.current?.(
228
+ data as TData,
229
+ update as AppflareRealtimeQueryUpdateLike<TData>,
230
+ );
231
+ },
232
+ onError: (error) => {
233
+ realtimeOnErrorRef.current?.(error);
234
+ },
235
+ });
236
+
237
+ return () => {
238
+ controller.abort();
239
+ subscription.remove();
240
+ };
241
+ }, [
242
+ realtimeArgsHash,
243
+ options?.realtime?.authToken,
244
+ options?.realtime?.enabled,
245
+ realtimeRequestOptionsHash,
246
+ query.subscribe,
247
+ queryClient,
248
+ resolvedQueryKeyHash,
249
+ ]);
250
+
251
+ return result;
252
+ }