appflare 0.0.28 → 0.1.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.
Files changed (141) hide show
  1. package/cli/commands/index.ts +140 -0
  2. package/cli/generate.ts +149 -0
  3. package/cli/index.ts +56 -447
  4. package/cli/load-config.ts +182 -0
  5. package/cli/schema-compiler.ts +657 -0
  6. package/cli/templates/auth/README.md +156 -0
  7. package/cli/templates/auth/config.ts +61 -0
  8. package/cli/templates/auth/route-config.ts +18 -0
  9. package/cli/templates/auth/route-handler.ts +18 -0
  10. package/cli/templates/auth/route-request-utils.ts +55 -0
  11. package/cli/templates/auth/route.ts +14 -0
  12. package/cli/templates/core/README.md +266 -0
  13. package/cli/templates/core/app-creation.ts +19 -0
  14. package/cli/templates/core/client/appflare.ts +37 -0
  15. package/cli/templates/core/client/index.ts +6 -0
  16. package/cli/templates/core/client/storage.ts +100 -0
  17. package/cli/templates/core/client/types.ts +54 -0
  18. package/cli/templates/core/client-modules/appflare.ts +112 -0
  19. package/cli/templates/core/client-modules/handlers/index.ts +740 -0
  20. package/cli/templates/core/client-modules/handlers.ts +1 -0
  21. package/cli/templates/core/client-modules/index.ts +7 -0
  22. package/cli/templates/core/client-modules/storage.ts +180 -0
  23. package/cli/templates/core/client-modules/types.ts +145 -0
  24. package/cli/templates/core/client.ts +39 -0
  25. package/cli/templates/core/drizzle.ts +15 -0
  26. package/cli/templates/core/export.ts +14 -0
  27. package/cli/templates/core/handlers-route.ts +23 -0
  28. package/cli/templates/core/handlers.ts +1 -0
  29. package/cli/templates/core/imports.ts +8 -0
  30. package/cli/templates/core/server.ts +38 -0
  31. package/cli/templates/core/types.ts +6 -0
  32. package/cli/templates/core/wrangler.ts +109 -0
  33. package/cli/templates/handlers/README.md +265 -0
  34. package/cli/templates/handlers/auth.ts +36 -0
  35. package/cli/templates/handlers/execution.ts +39 -0
  36. package/cli/templates/handlers/generators/context/context-creation.ts +80 -0
  37. package/cli/templates/handlers/generators/context/error-helpers.ts +11 -0
  38. package/cli/templates/handlers/generators/context/scheduler.ts +24 -0
  39. package/cli/templates/handlers/generators/context/storage-api.ts +112 -0
  40. package/cli/templates/handlers/generators/context/storage-helpers.ts +59 -0
  41. package/cli/templates/handlers/generators/context/types.ts +18 -0
  42. package/cli/templates/handlers/generators/context.ts +43 -0
  43. package/cli/templates/handlers/generators/execution.ts +15 -0
  44. package/cli/templates/handlers/generators/handlers.ts +13 -0
  45. package/cli/templates/handlers/index.ts +43 -0
  46. package/cli/templates/handlers/operations.ts +116 -0
  47. package/cli/templates/handlers/registration.ts +1114 -0
  48. package/cli/templates/handlers/types.ts +960 -0
  49. package/cli/templates/handlers/utils.ts +48 -0
  50. package/cli/types.ts +108 -0
  51. package/cli/utils/handler-discovery.ts +366 -0
  52. package/cli/utils/json-utils.ts +24 -0
  53. package/cli/utils/path-utils.ts +19 -0
  54. package/cli/utils/schema-discovery.ts +390 -0
  55. package/index.ts +27 -4
  56. package/package.json +23 -20
  57. package/react/index.ts +5 -3
  58. package/react/use-infinite-query.ts +190 -0
  59. package/react/use-mutation.ts +54 -0
  60. package/react/use-query.ts +158 -0
  61. package/schema.ts +262 -0
  62. package/tsconfig.json +2 -4
  63. package/cli/README.md +0 -108
  64. package/cli/core/build.ts +0 -187
  65. package/cli/core/config.ts +0 -92
  66. package/cli/core/discover-handlers.ts +0 -143
  67. package/cli/core/handlers.ts +0 -7
  68. package/cli/core/index.ts +0 -205
  69. package/cli/generators/generate-api-client/client.ts +0 -163
  70. package/cli/generators/generate-api-client/extract-configuration.ts +0 -121
  71. package/cli/generators/generate-api-client/index.ts +0 -973
  72. package/cli/generators/generate-api-client/types.ts +0 -164
  73. package/cli/generators/generate-api-client/utils.ts +0 -22
  74. package/cli/generators/generate-api-client.ts +0 -1
  75. package/cli/generators/generate-cloudflare-worker/helpers.ts +0 -24
  76. package/cli/generators/generate-cloudflare-worker/index.ts +0 -2
  77. package/cli/generators/generate-cloudflare-worker/worker.ts +0 -148
  78. package/cli/generators/generate-cloudflare-worker/wrangler.ts +0 -108
  79. package/cli/generators/generate-cloudflare-worker.ts +0 -4
  80. package/cli/generators/generate-cron-handlers/cron-handlers-block.ts +0 -2
  81. package/cli/generators/generate-cron-handlers/handler-entries.ts +0 -29
  82. package/cli/generators/generate-cron-handlers/index.ts +0 -61
  83. package/cli/generators/generate-cron-handlers/runtime-block.ts +0 -49
  84. package/cli/generators/generate-cron-handlers/type-helpers-block.ts +0 -60
  85. package/cli/generators/generate-db-handlers/index.ts +0 -33
  86. package/cli/generators/generate-db-handlers/prepare.ts +0 -24
  87. package/cli/generators/generate-db-handlers/templates.ts +0 -189
  88. package/cli/generators/generate-db-handlers.ts +0 -1
  89. package/cli/generators/generate-hono-server/auth.ts +0 -97
  90. package/cli/generators/generate-hono-server/imports.ts +0 -55
  91. package/cli/generators/generate-hono-server/index.ts +0 -52
  92. package/cli/generators/generate-hono-server/routes.ts +0 -115
  93. package/cli/generators/generate-hono-server/template.ts +0 -371
  94. package/cli/generators/generate-hono-server.ts +0 -1
  95. package/cli/generators/generate-scheduler-handlers/constants.ts +0 -8
  96. package/cli/generators/generate-scheduler-handlers/handler-entries.ts +0 -22
  97. package/cli/generators/generate-scheduler-handlers/index.ts +0 -51
  98. package/cli/generators/generate-scheduler-handlers/runtime-block.ts +0 -68
  99. package/cli/generators/generate-scheduler-handlers/scheduler-handlers-block.ts +0 -2
  100. package/cli/generators/generate-scheduler-handlers/type-helpers-block.ts +0 -68
  101. package/cli/generators/generate-scheduler-handlers.ts +0 -1
  102. package/cli/generators/generate-websocket-durable-object/auth.ts +0 -30
  103. package/cli/generators/generate-websocket-durable-object/imports.ts +0 -55
  104. package/cli/generators/generate-websocket-durable-object/index.ts +0 -41
  105. package/cli/generators/generate-websocket-durable-object/query-handlers.ts +0 -18
  106. package/cli/generators/generate-websocket-durable-object/template.ts +0 -714
  107. package/cli/generators/generate-websocket-durable-object.ts +0 -1
  108. package/cli/schema/schema-static-types.ts +0 -702
  109. package/cli/schema/schema.ts +0 -151
  110. package/cli/utils/tsc.ts +0 -54
  111. package/cli/utils/utils.ts +0 -190
  112. package/cli/utils/zod-utils.ts +0 -121
  113. package/lib/README.md +0 -50
  114. package/lib/db.ts +0 -19
  115. package/lib/location.ts +0 -110
  116. package/lib/values.ts +0 -27
  117. package/react/README.md +0 -67
  118. package/react/hooks/useMutation.ts +0 -89
  119. package/react/hooks/usePaginatedQuery.ts +0 -213
  120. package/react/hooks/useQuery.ts +0 -106
  121. package/react/shared/queryShared.ts +0 -174
  122. package/server/README.md +0 -218
  123. package/server/auth.ts +0 -107
  124. package/server/database/builders.ts +0 -83
  125. package/server/database/context.ts +0 -327
  126. package/server/database/populate.ts +0 -234
  127. package/server/database/query-builder.ts +0 -161
  128. package/server/database/query-utils.ts +0 -25
  129. package/server/db.ts +0 -2
  130. package/server/storage/auth.ts +0 -16
  131. package/server/storage/bucket.ts +0 -22
  132. package/server/storage/context.ts +0 -34
  133. package/server/storage/index.ts +0 -38
  134. package/server/storage/operations.ts +0 -149
  135. package/server/storage/route-handler.ts +0 -60
  136. package/server/storage/types.ts +0 -55
  137. package/server/storage/utils.ts +0 -47
  138. package/server/storage.ts +0 -6
  139. package/server/types/schema-refs.ts +0 -66
  140. package/server/types/types.ts +0 -633
  141. package/server/utils/id-utils.ts +0 -230
@@ -0,0 +1,390 @@
1
+ import { readdir } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ import type { LoadedAppflareConfig } from "../types";
4
+
5
+ export type DiscoveredColumn = {
6
+ name: string;
7
+ expression: string;
8
+ type: "string" | "number" | "boolean" | "unknown";
9
+ optional: boolean;
10
+ primaryKey: boolean;
11
+ autoIncrement: boolean;
12
+ };
13
+
14
+ export type DiscoveredTable = {
15
+ exportName: string;
16
+ tableName: string;
17
+ columns: DiscoveredColumn[];
18
+ };
19
+
20
+ export type DiscoveredSchema = {
21
+ schemaPath: string;
22
+ tables: DiscoveredTable[];
23
+ };
24
+
25
+ function splitTopLevel(value: string): string[] {
26
+ const result: string[] = [];
27
+ let current = "";
28
+ let depthParen = 0;
29
+ let depthBrace = 0;
30
+ let depthBracket = 0;
31
+ let inSingleQuote = false;
32
+ let inDoubleQuote = false;
33
+ let inTemplateString = false;
34
+ let escaped = false;
35
+
36
+ for (let i = 0; i < value.length; i += 1) {
37
+ const char = value[i];
38
+
39
+ if (escaped) {
40
+ current += char;
41
+ escaped = false;
42
+ continue;
43
+ }
44
+
45
+ if (char === "\\") {
46
+ current += char;
47
+ escaped = true;
48
+ continue;
49
+ }
50
+
51
+ if (!inDoubleQuote && !inTemplateString && char === "'") {
52
+ inSingleQuote = !inSingleQuote;
53
+ current += char;
54
+ continue;
55
+ }
56
+
57
+ if (!inSingleQuote && !inTemplateString && char === '"') {
58
+ inDoubleQuote = !inDoubleQuote;
59
+ current += char;
60
+ continue;
61
+ }
62
+
63
+ if (!inSingleQuote && !inDoubleQuote && char === "`") {
64
+ inTemplateString = !inTemplateString;
65
+ current += char;
66
+ continue;
67
+ }
68
+
69
+ if (inSingleQuote || inDoubleQuote || inTemplateString) {
70
+ current += char;
71
+ continue;
72
+ }
73
+
74
+ if (char === "(") {
75
+ depthParen += 1;
76
+ } else if (char === ")") {
77
+ depthParen -= 1;
78
+ } else if (char === "{") {
79
+ depthBrace += 1;
80
+ } else if (char === "}") {
81
+ depthBrace -= 1;
82
+ } else if (char === "[") {
83
+ depthBracket += 1;
84
+ } else if (char === "]") {
85
+ depthBracket -= 1;
86
+ }
87
+
88
+ if (
89
+ char === "," &&
90
+ depthParen === 0 &&
91
+ depthBrace === 0 &&
92
+ depthBracket === 0
93
+ ) {
94
+ const segment = current.trim();
95
+ if (segment.length > 0) {
96
+ result.push(segment);
97
+ }
98
+ current = "";
99
+ continue;
100
+ }
101
+
102
+ current += char;
103
+ }
104
+
105
+ const trailing = current.trim();
106
+ if (trailing.length > 0) {
107
+ result.push(trailing);
108
+ }
109
+
110
+ return result;
111
+ }
112
+
113
+ function getTopLevelColonIndex(entry: string): number {
114
+ let depthParen = 0;
115
+ let depthBrace = 0;
116
+ let depthBracket = 0;
117
+ let inSingleQuote = false;
118
+ let inDoubleQuote = false;
119
+ let inTemplateString = false;
120
+ let escaped = false;
121
+
122
+ for (let i = 0; i < entry.length; i += 1) {
123
+ const char = entry[i];
124
+
125
+ if (escaped) {
126
+ escaped = false;
127
+ continue;
128
+ }
129
+
130
+ if (char === "\\") {
131
+ escaped = true;
132
+ continue;
133
+ }
134
+
135
+ if (!inDoubleQuote && !inTemplateString && char === "'") {
136
+ inSingleQuote = !inSingleQuote;
137
+ continue;
138
+ }
139
+
140
+ if (!inSingleQuote && !inTemplateString && char === '"') {
141
+ inDoubleQuote = !inDoubleQuote;
142
+ continue;
143
+ }
144
+
145
+ if (!inSingleQuote && !inDoubleQuote && char === "`") {
146
+ inTemplateString = !inTemplateString;
147
+ continue;
148
+ }
149
+
150
+ if (inSingleQuote || inDoubleQuote || inTemplateString) {
151
+ continue;
152
+ }
153
+
154
+ if (char === "(") {
155
+ depthParen += 1;
156
+ continue;
157
+ }
158
+ if (char === ")") {
159
+ depthParen -= 1;
160
+ continue;
161
+ }
162
+ if (char === "{") {
163
+ depthBrace += 1;
164
+ continue;
165
+ }
166
+ if (char === "}") {
167
+ depthBrace -= 1;
168
+ continue;
169
+ }
170
+ if (char === "[") {
171
+ depthBracket += 1;
172
+ continue;
173
+ }
174
+ if (char === "]") {
175
+ depthBracket -= 1;
176
+ continue;
177
+ }
178
+
179
+ if (
180
+ char === ":" &&
181
+ depthParen === 0 &&
182
+ depthBrace === 0 &&
183
+ depthBracket === 0
184
+ ) {
185
+ return i;
186
+ }
187
+ }
188
+
189
+ return -1;
190
+ }
191
+
192
+ function inferColumnType(expression: string): DiscoveredColumn["type"] {
193
+ const lowered = expression.toLowerCase();
194
+ if (/\.(int|integer|real|numeric|decimal|float|double)\s*\(/.test(lowered)) {
195
+ return "number";
196
+ }
197
+ if (/\.(text|varchar|char)\s*\(/.test(lowered)) {
198
+ return "string";
199
+ }
200
+ if (/\.(boolean|bool)\s*\(/.test(lowered)) {
201
+ return "boolean";
202
+ }
203
+ return "unknown";
204
+ }
205
+
206
+ function discoverColumns(rawColumns: string): DiscoveredColumn[] {
207
+ const entries = splitTopLevel(rawColumns);
208
+ const columns: DiscoveredColumn[] = [];
209
+
210
+ for (const entry of entries) {
211
+ const colonIndex = getTopLevelColonIndex(entry);
212
+ if (colonIndex === -1) {
213
+ continue;
214
+ }
215
+
216
+ const key = entry
217
+ .slice(0, colonIndex)
218
+ .trim()
219
+ .replace(/^['"]|['"]$/g, "");
220
+ if (!key) {
221
+ continue;
222
+ }
223
+
224
+ const expression = entry.slice(colonIndex + 1).trim();
225
+ const primaryKey = /\.primarykey\s*\(/i.test(expression);
226
+ const autoIncrement = /autoincrement\s*:\s*true/i.test(expression);
227
+ const hasDefault = /\.default\s*\(/i.test(expression);
228
+ const notNull = /\.notnull\s*\(/i.test(expression);
229
+ const optional = !notNull || hasDefault || autoIncrement;
230
+
231
+ columns.push({
232
+ name: key,
233
+ expression,
234
+ type: inferColumnType(expression),
235
+ optional,
236
+ primaryKey,
237
+ autoIncrement,
238
+ });
239
+ }
240
+
241
+ return columns;
242
+ }
243
+
244
+ function discoverTables(schemaSource: string): DiscoveredTable[] {
245
+ const tablePattern =
246
+ /export\s+const\s+(\w+)\s*=\s*table\s*\(\s*["'`]([^"'`]+)["'`]/g;
247
+ const tables: DiscoveredTable[] = [];
248
+
249
+ const findMatchingBrace = (source: string, startIndex: number): number => {
250
+ let depth = 0;
251
+ let inSingleQuote = false;
252
+ let inDoubleQuote = false;
253
+ let inTemplateString = false;
254
+ let escaped = false;
255
+
256
+ for (let i = startIndex; i < source.length; i += 1) {
257
+ const char = source[i];
258
+
259
+ if (escaped) {
260
+ escaped = false;
261
+ continue;
262
+ }
263
+
264
+ if (char === "\\") {
265
+ escaped = true;
266
+ continue;
267
+ }
268
+
269
+ if (!inDoubleQuote && !inTemplateString && char === "'") {
270
+ inSingleQuote = !inSingleQuote;
271
+ continue;
272
+ }
273
+
274
+ if (!inSingleQuote && !inTemplateString && char === '"') {
275
+ inDoubleQuote = !inDoubleQuote;
276
+ continue;
277
+ }
278
+
279
+ if (!inSingleQuote && !inDoubleQuote && char === "`") {
280
+ inTemplateString = !inTemplateString;
281
+ continue;
282
+ }
283
+
284
+ if (inSingleQuote || inDoubleQuote || inTemplateString) {
285
+ continue;
286
+ }
287
+
288
+ if (char === "{") {
289
+ depth += 1;
290
+ continue;
291
+ }
292
+
293
+ if (char === "}") {
294
+ depth -= 1;
295
+ if (depth === 0) {
296
+ return i;
297
+ }
298
+ }
299
+ }
300
+
301
+ return -1;
302
+ };
303
+
304
+ let match = tablePattern.exec(schemaSource);
305
+ while (match) {
306
+ const exportName = match[1];
307
+ const tableName = match[2];
308
+ const objectStart = schemaSource.indexOf("{", tablePattern.lastIndex);
309
+ if (objectStart === -1) {
310
+ match = tablePattern.exec(schemaSource);
311
+ continue;
312
+ }
313
+
314
+ const objectEnd = findMatchingBrace(schemaSource, objectStart);
315
+ if (objectEnd === -1) {
316
+ match = tablePattern.exec(schemaSource);
317
+ continue;
318
+ }
319
+
320
+ const rawColumns = schemaSource.slice(objectStart + 1, objectEnd);
321
+ tables.push({
322
+ exportName,
323
+ tableName,
324
+ columns: discoverColumns(rawColumns),
325
+ });
326
+ match = tablePattern.exec(schemaSource);
327
+ }
328
+
329
+ return tables;
330
+ }
331
+
332
+ async function findSchemaCandidates(dirPath: string): Promise<string[]> {
333
+ const entries = await readdir(dirPath, { withFileTypes: true });
334
+ const discovered: string[] = [];
335
+
336
+ for (const entry of entries) {
337
+ if (entry.name.startsWith(".")) {
338
+ continue;
339
+ }
340
+ if (entry.name === "node_modules" || entry.name === "_generated") {
341
+ continue;
342
+ }
343
+
344
+ const entryPath = resolve(dirPath, entry.name);
345
+ if (entry.isDirectory()) {
346
+ discovered.push(...(await findSchemaCandidates(entryPath)));
347
+ continue;
348
+ }
349
+
350
+ if (entry.isFile() && entry.name === "schema.ts") {
351
+ discovered.push(entryPath);
352
+ }
353
+ }
354
+
355
+ return discovered;
356
+ }
357
+
358
+ export async function discoverSchema(
359
+ loadedConfig: LoadedAppflareConfig,
360
+ prioritySchemaPaths: string[] = [],
361
+ ): Promise<DiscoveredSchema> {
362
+ const scanCandidates = await findSchemaCandidates(
363
+ loadedConfig.scanDirAbs,
364
+ ).catch(() => []);
365
+
366
+ const fallbackCandidate = resolve(loadedConfig.configDir, "schema.ts");
367
+ const candidates = [
368
+ ...prioritySchemaPaths,
369
+ ...(scanCandidates.length ? scanCandidates : [fallbackCandidate]),
370
+ ];
371
+
372
+ for (const schemaPath of candidates) {
373
+ const file = Bun.file(schemaPath);
374
+ if (!(await file.exists())) {
375
+ continue;
376
+ }
377
+ const source = await file.text();
378
+ const tables = discoverTables(source);
379
+ if (tables.length > 0) {
380
+ return {
381
+ schemaPath,
382
+ tables,
383
+ };
384
+ }
385
+ }
386
+
387
+ throw new Error(
388
+ `Unable to discover schema.ts under scanDir (${loadedConfig.scanDirAbs}) or fallback (${fallbackCandidate}).`,
389
+ );
390
+ }
package/index.ts CHANGED
@@ -1,4 +1,27 @@
1
- export * from "./lib/db";
2
- export * from "./lib/values";
3
- export * from "./lib/location";
4
- export { AppflareConfig } from "./cli/utils/utils";
1
+ export type {
2
+ AppflareConfig,
3
+ AppflareDatabaseBinding,
4
+ AppflareKVNamespace,
5
+ AppflareR2Bucket,
6
+ LoadedAppflareConfig,
7
+ NormalizedAppflareConfig,
8
+ } from "./cli/types";
9
+ export { loadConfig } from "./cli/load-config";
10
+ export { generateArtifacts } from "./cli/generate";
11
+ export {
12
+ schema,
13
+ table,
14
+ v,
15
+ isSchemaDefinition,
16
+ type ColumnDefinition,
17
+ type RelationDefinition,
18
+ type SchemaDefinition,
19
+ type TableDefinition,
20
+ } from "./schema";
21
+ export { useQuery } from "./react";
22
+ export { useInfiniteQuery } from "./react";
23
+ export { useMutation } from "./react";
24
+ export type {
25
+ UseAppflareQueryOptions,
26
+ UseAppflareInfiniteQueryOptions,
27
+ } from "./react";
package/package.json CHANGED
@@ -1,38 +1,41 @@
1
1
  {
2
2
  "name": "appflare",
3
- "version": "0.0.28",
3
+ "version": "0.1.0",
4
4
  "bin": {
5
5
  "appflare": "./cli/index.ts"
6
6
  },
7
7
  "exports": {
8
8
  ".": "./index.ts",
9
- "./db": "./lib/db.ts",
10
- "./values": "./lib/values.ts",
11
- "./location": "./lib/location.ts",
12
- "./*": "./*.ts",
13
- "./react/*": "./react/*.ts",
14
9
  "./react": "./react/index.ts",
15
- "./server/db": "./server/db.ts",
16
- "./server/storage": "./server/storage.ts",
17
- "./server/auth": "./server/auth.ts"
10
+ "./*": "./*.ts"
18
11
  },
19
12
  "scripts": {},
13
+ "peerDependencies": {
14
+ "@tanstack/react-query": "^5.90.21",
15
+ "react": ">=18"
16
+ },
20
17
  "devDependencies": {
21
- "@cloudflare/workers-types": "^4.20241112.0",
22
- "@types/react": "^18.3.12",
23
- "typescript": "^5.9.3",
24
- "@tanstack/react-query": "^5.90.12",
25
- "react": "^18.3.1"
18
+ "@cloudflare/workers-types": "^4.20260219.0",
19
+ "@types/react": "^19.2.0",
20
+ "@tanstack/react-query": "^5.90.21",
21
+ "@types/bun": "^1.3.9",
22
+ "@types/node": "^25.3.0",
23
+ "npm-check-updates": "^19.4.0",
24
+ "typescript": "^5.9.3"
26
25
  },
27
26
  "dependencies": {
28
- "@hono/standard-validator": "^0.2.1",
27
+ "@better-auth/cli": "^1.4.18",
28
+ "@hono/standard-validator": "^0.2.2",
29
29
  "better-auth": "^1.4.18",
30
+ "better-auth-cloudflare": "^0.2.9",
30
31
  "better-fetch": "^1.1.2",
32
+ "bun": "^1.3.9",
31
33
  "chokidar": "^5.0.0",
32
- "cloudflare-do-mongo": "^0.1.2",
33
- "commander": "^14.0.1",
34
- "hono": "^4.6.8",
35
- "mongodb": "^7.0.0",
36
- "zod": "^4.1.13"
34
+ "commander": "^14.0.3",
35
+ "drizzle-kit": "^0.31.9",
36
+ "drizzle-orm": "^0.45.1",
37
+ "hono": "^4.12.0",
38
+ "wrangler": "^4.67.0",
39
+ "zod": "^4.3.6"
37
40
  }
38
41
  }
package/react/index.ts CHANGED
@@ -1,3 +1,5 @@
1
- export * from "./hooks/useMutation";
2
- export * from "./hooks/usePaginatedQuery";
3
- export * from "./hooks/useQuery";
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";
@@ -0,0 +1,190 @@
1
+ import {
2
+ type InfiniteData,
3
+ type QueryKey,
4
+ type UseInfiniteQueryOptions,
5
+ type UseInfiniteQueryResult,
6
+ useInfiniteQuery as useTanstackInfiniteQuery,
7
+ useQueryClient,
8
+ } from "@tanstack/react-query";
9
+ import { useEffect, useMemo } from "react";
10
+
11
+ type AppflareRequestErrorLike = {
12
+ message: string;
13
+ status?: number;
14
+ };
15
+
16
+ type AppflareRequestResultLike<TData> = {
17
+ data: TData | null;
18
+ error: AppflareRequestErrorLike | null;
19
+ };
20
+
21
+ type AppflareRealtimeSubscriptionLike = {
22
+ remove: () => void;
23
+ };
24
+
25
+ type AppflareRealtimeQueryUpdateLike<TData> = {
26
+ event: "query:update";
27
+ payload: {
28
+ queryName: string;
29
+ signature: string;
30
+ data: TData;
31
+ };
32
+ };
33
+
34
+ type AppflareQueryLike<TArgs, TData> = {
35
+ run: (
36
+ args: TArgs,
37
+ options?: any,
38
+ ) => Promise<AppflareRequestResultLike<TData>>;
39
+ subscribe?: (options: {
40
+ args?: TArgs;
41
+ authToken?: string;
42
+ requestOptions?: any;
43
+ signal?: AbortSignal;
44
+ onChange: (
45
+ data: TData,
46
+ update: AppflareRealtimeQueryUpdateLike<TData>,
47
+ ) => void;
48
+ onError?: (error: unknown) => void;
49
+ }) => AppflareRealtimeSubscriptionLike;
50
+ };
51
+
52
+ export type UseAppflareInfiniteQueryOptions<
53
+ TArgs,
54
+ TData,
55
+ TPageParam,
56
+ TSelected = InfiniteData<TData, TPageParam>,
57
+ TKey extends QueryKey = QueryKey,
58
+ > = {
59
+ realtime?: {
60
+ enabled?: boolean;
61
+ authToken?: string;
62
+ requestOptions?: any;
63
+ onChange?: (
64
+ data: TData,
65
+ update: AppflareRealtimeQueryUpdateLike<TData>,
66
+ ) => void;
67
+ onError?: (error: unknown) => void;
68
+ };
69
+ requestOptions?: any;
70
+ pageParamToArgs?: (baseArgs: TArgs, pageParam: TPageParam) => TArgs;
71
+ queryOptions?: Omit<
72
+ UseInfiniteQueryOptions<TData, Error, TSelected, TKey, TPageParam>,
73
+ "queryFn" | "queryKey"
74
+ > & {
75
+ queryKey?: TKey;
76
+ };
77
+ };
78
+
79
+ function toError(error: AppflareRequestErrorLike): Error {
80
+ const next = new Error(error.message);
81
+ if (error.status !== undefined) {
82
+ (next as Error & { status?: number }).status = error.status;
83
+ }
84
+ return next;
85
+ }
86
+
87
+ export function useInfiniteQuery<
88
+ TArgs,
89
+ TData,
90
+ TPageParam = unknown,
91
+ TSelected = InfiniteData<TData, TPageParam>,
92
+ TKey extends QueryKey = QueryKey,
93
+ >(
94
+ query: AppflareQueryLike<TArgs, TData>,
95
+ args: TArgs,
96
+ options?: UseAppflareInfiniteQueryOptions<
97
+ TArgs,
98
+ TData,
99
+ TPageParam,
100
+ TSelected,
101
+ TKey
102
+ >,
103
+ ): UseInfiniteQueryResult<TSelected, Error> {
104
+ const queryClient = useQueryClient();
105
+ const resolvedQueryKey = useMemo(() => {
106
+ return (
107
+ options?.queryOptions?.queryKey ??
108
+ (["appflare", "infinite-query", query, args] as unknown as TKey)
109
+ );
110
+ }, [args, options?.queryOptions?.queryKey, query]);
111
+
112
+ const result = useTanstackInfiniteQuery<
113
+ TData,
114
+ Error,
115
+ TSelected,
116
+ TKey,
117
+ TPageParam
118
+ >({
119
+ ...(options?.queryOptions as Omit<
120
+ UseInfiniteQueryOptions<TData, Error, TSelected, TKey, TPageParam>,
121
+ "queryFn" | "queryKey"
122
+ >),
123
+ queryKey: resolvedQueryKey,
124
+ queryFn: async ({ pageParam }) => {
125
+ const nextArgs =
126
+ pageParam === undefined || !options?.pageParamToArgs
127
+ ? args
128
+ : options.pageParamToArgs(args, pageParam as TPageParam);
129
+ const response = await query.run(nextArgs, options?.requestOptions);
130
+ if (response.error) {
131
+ throw toError(response.error);
132
+ }
133
+ return response.data as TData;
134
+ },
135
+ });
136
+
137
+ useEffect(() => {
138
+ if (options?.realtime?.enabled === false || !query.subscribe) {
139
+ return;
140
+ }
141
+
142
+ const controller = new AbortController();
143
+ const subscription = query.subscribe({
144
+ args,
145
+ authToken: options.realtime?.authToken,
146
+ requestOptions: options.realtime?.requestOptions,
147
+ signal: controller.signal,
148
+ onChange: (data, update) => {
149
+ queryClient.setQueryData<InfiniteData<TData, TPageParam>>(
150
+ resolvedQueryKey,
151
+ (previous) => {
152
+ if (!previous || previous.pages.length === 0) {
153
+ return previous;
154
+ }
155
+
156
+ return {
157
+ ...previous,
158
+ pages: [data as TData, ...previous.pages.slice(1)],
159
+ };
160
+ },
161
+ );
162
+ options.realtime?.onChange?.(
163
+ data as TData,
164
+ update as AppflareRealtimeQueryUpdateLike<TData>,
165
+ );
166
+ },
167
+ onError: (error) => {
168
+ options.realtime?.onError?.(error);
169
+ },
170
+ });
171
+
172
+ return () => {
173
+ controller.abort();
174
+ subscription.remove();
175
+ };
176
+ }, [
177
+ args,
178
+ options?.realtime?.authToken,
179
+ options?.realtime?.enabled,
180
+ options?.realtime?.onChange,
181
+ options?.realtime?.onError,
182
+ options?.realtime?.requestOptions,
183
+ query,
184
+ query.subscribe,
185
+ queryClient,
186
+ resolvedQueryKey,
187
+ ]);
188
+
189
+ return result;
190
+ }