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,54 @@
1
+ import {
2
+ type UseMutationOptions,
3
+ type UseMutationResult,
4
+ useMutation as useTanstackMutation,
5
+ } from "@tanstack/react-query";
6
+
7
+ type AppflareRequestErrorLike = {
8
+ message: string;
9
+ status?: number;
10
+ };
11
+
12
+ type AppflareRequestResultLike<TData> = {
13
+ data: TData | null;
14
+ error: AppflareRequestErrorLike | null;
15
+ };
16
+
17
+ type AppflareMutationLike<TArgs, TData> = {
18
+ run: (
19
+ args: TArgs,
20
+ options?: any,
21
+ ) => Promise<AppflareRequestResultLike<TData>>;
22
+ };
23
+
24
+ function toError(error: AppflareRequestErrorLike): Error {
25
+ const next = new Error(error.message);
26
+ if (error.status !== undefined) {
27
+ (next as Error & { status?: number }).status = error.status;
28
+ }
29
+ return next;
30
+ }
31
+
32
+ export function useMutation<TArgs, TData, TContext = unknown>(
33
+ mutation: AppflareMutationLike<TArgs, TData>,
34
+ args: TArgs,
35
+ mutationOptions?: Omit<
36
+ UseMutationOptions<TData, Error, void, TContext>,
37
+ "mutationFn"
38
+ >,
39
+ ): UseMutationResult<TData, Error, void, TContext> {
40
+ return useTanstackMutation<TData, Error, void, TContext>({
41
+ ...(mutationOptions as Omit<
42
+ UseMutationOptions<TData, Error, void, TContext>,
43
+ "mutationFn"
44
+ >),
45
+ mutationFn: async () => {
46
+ const response = await mutation.run(args);
47
+ if (response.error) {
48
+ throw toError(response.error);
49
+ }
50
+
51
+ return response.data as TData;
52
+ },
53
+ });
54
+ }
@@ -0,0 +1,158 @@
1
+ import {
2
+ type QueryKey,
3
+ type UseQueryOptions,
4
+ type UseQueryResult,
5
+ useQuery as useTanstackQuery,
6
+ useQueryClient,
7
+ } from "@tanstack/react-query";
8
+ import { useEffect, useMemo } from "react";
9
+
10
+ type AppflareRequestErrorLike = {
11
+ message: string;
12
+ status?: number;
13
+ };
14
+
15
+ type AppflareRequestResultLike<TData> = {
16
+ data: TData | null;
17
+ error: AppflareRequestErrorLike | null;
18
+ };
19
+
20
+ type AppflareRealtimeSubscriptionLike = {
21
+ remove: () => void;
22
+ };
23
+
24
+ type AppflareRealtimeQueryUpdateLike<TData> = {
25
+ event: "query:update";
26
+ payload: {
27
+ queryName: string;
28
+ signature: string;
29
+ data: TData;
30
+ };
31
+ };
32
+
33
+ type AppflareQueryLike<TArgs, TData> = {
34
+ run: (
35
+ args: TArgs,
36
+ options?: any,
37
+ ) => Promise<AppflareRequestResultLike<TData>>;
38
+ subscribe?: (options: {
39
+ args?: TArgs;
40
+ authToken?: string;
41
+ requestOptions?: any;
42
+ signal?: AbortSignal;
43
+ onChange: (
44
+ data: TData,
45
+ update: AppflareRealtimeQueryUpdateLike<TData>,
46
+ ) => void;
47
+ onError?: (error: unknown) => void;
48
+ }) => AppflareRealtimeSubscriptionLike;
49
+ };
50
+
51
+ export type UseAppflareQueryOptions<
52
+ TArgs,
53
+ TData,
54
+ TSelected = TData,
55
+ TKey extends QueryKey = QueryKey,
56
+ > = {
57
+ realtime?: {
58
+ enabled?: boolean;
59
+ authToken?: string;
60
+ requestOptions?: any;
61
+ onChange?: (
62
+ data: TData,
63
+ update: AppflareRealtimeQueryUpdateLike<TData>,
64
+ ) => void;
65
+ onError?: (error: unknown) => void;
66
+ };
67
+ requestOptions?: any;
68
+ queryOptions?: Omit<
69
+ UseQueryOptions<TData, Error, TSelected, TKey>,
70
+ "queryFn" | "queryKey"
71
+ > & {
72
+ queryKey?: TKey;
73
+ };
74
+ };
75
+
76
+ function toError(error: AppflareRequestErrorLike): Error {
77
+ const next = new Error(error.message);
78
+ if (error.status !== undefined) {
79
+ (next as Error & { status?: number }).status = error.status;
80
+ }
81
+ return next;
82
+ }
83
+
84
+ export function useQuery<
85
+ TArgs,
86
+ TData,
87
+ TSelected = TData,
88
+ TKey extends QueryKey = QueryKey,
89
+ >(
90
+ query: AppflareQueryLike<TArgs, TData>,
91
+ args: TArgs,
92
+ options?: UseAppflareQueryOptions<TArgs, TData, TSelected, TKey>,
93
+ ): UseQueryResult<TSelected, Error> {
94
+ const queryClient = useQueryClient();
95
+ const resolvedQueryKey = useMemo(() => {
96
+ return (
97
+ options?.queryOptions?.queryKey ??
98
+ (["appflare", "query", query, args] as unknown as TKey)
99
+ );
100
+ }, [args, options?.queryOptions?.queryKey, query]);
101
+
102
+ const result = useTanstackQuery<TData, Error, TSelected, TKey>({
103
+ ...(options?.queryOptions as Omit<
104
+ UseQueryOptions<TData, Error, TSelected, TKey>,
105
+ "queryFn" | "queryKey"
106
+ >),
107
+ queryKey: resolvedQueryKey,
108
+ queryFn: async () => {
109
+ const response = await query.run(args, options?.requestOptions);
110
+ if (response.error) {
111
+ throw toError(response.error);
112
+ }
113
+ return response.data as TData;
114
+ },
115
+ });
116
+
117
+ useEffect(() => {
118
+ if (options?.realtime?.enabled === false || !query.subscribe) {
119
+ return;
120
+ }
121
+
122
+ const controller = new AbortController();
123
+ const subscription = query.subscribe({
124
+ args,
125
+ authToken: options.realtime?.authToken,
126
+ requestOptions: options.realtime?.requestOptions,
127
+ signal: controller.signal,
128
+ onChange: (data, update) => {
129
+ queryClient.setQueryData<TData>(resolvedQueryKey, data as TData);
130
+ options.realtime?.onChange?.(
131
+ data as TData,
132
+ update as AppflareRealtimeQueryUpdateLike<TData>,
133
+ );
134
+ },
135
+ onError: (error) => {
136
+ options.realtime?.onError?.(error);
137
+ },
138
+ });
139
+
140
+ return () => {
141
+ controller.abort();
142
+ subscription.remove();
143
+ };
144
+ }, [
145
+ args,
146
+ options?.realtime?.authToken,
147
+ options?.realtime?.enabled,
148
+ options?.realtime?.onChange,
149
+ options?.realtime?.onError,
150
+ options?.realtime?.requestOptions,
151
+ query,
152
+ query.subscribe,
153
+ queryClient,
154
+ resolvedQueryKey,
155
+ ]);
156
+
157
+ return result;
158
+ }
package/schema.ts ADDED
@@ -0,0 +1,262 @@
1
+ export type ColumnType = "int" | "string" | "boolean" | "date";
2
+
3
+ export type ColumnBuilderOptions = {
4
+ sqlName?: string;
5
+ length?: number;
6
+ };
7
+
8
+ export type PrimaryKeyOptions = {
9
+ autoIncrement?: boolean;
10
+ };
11
+
12
+ export type RelationOneOptions = {
13
+ referenceField?: string;
14
+ field?: string;
15
+ fkType?: ColumnType;
16
+ sqlName?: string;
17
+ notNull?: boolean;
18
+ };
19
+
20
+ export type RelationManyOptions = {
21
+ referenceField?: string;
22
+ field?: string;
23
+ fkType?: ColumnType;
24
+ sqlName?: string;
25
+ notNull?: boolean;
26
+ };
27
+
28
+ export type ColumnReference = {
29
+ table: string;
30
+ column: string;
31
+ };
32
+
33
+ export type ColumnDefinition = {
34
+ kind: "column";
35
+ type: ColumnType;
36
+ sqlName?: string;
37
+ length?: number;
38
+ notNull?: boolean;
39
+ primaryKey?: boolean;
40
+ autoIncrement?: boolean;
41
+ unique?: true | { name?: string };
42
+ index?: true | { name?: string };
43
+ sqlDefault?: unknown;
44
+ runtimeDefaultFn?: () => unknown;
45
+ references?: ColumnReference;
46
+ };
47
+
48
+ export type OneRelationDefinition = {
49
+ kind: "relation";
50
+ relation: "one";
51
+ targetTable: string;
52
+ field?: string;
53
+ referenceField?: string;
54
+ fkType?: ColumnType;
55
+ sqlName?: string;
56
+ notNull?: boolean;
57
+ };
58
+
59
+ export type ManyRelationDefinition = {
60
+ kind: "relation";
61
+ relation: "many";
62
+ targetTable: string;
63
+ field?: string;
64
+ referenceField?: string;
65
+ fkType?: ColumnType;
66
+ sqlName?: string;
67
+ notNull?: boolean;
68
+ };
69
+
70
+ export type RelationDefinition = OneRelationDefinition | ManyRelationDefinition;
71
+
72
+ export type TableOptions = {
73
+ sqlName?: string;
74
+ };
75
+
76
+ export type TableShape = Record<string, ColumnBuilder | RelationDefinition>;
77
+
78
+ export type TableDefinition = {
79
+ kind: "table";
80
+ sqlName?: string;
81
+ columns: Record<string, ColumnDefinition>;
82
+ relations: Record<string, RelationDefinition>;
83
+ };
84
+
85
+ export type SchemaDefinition = {
86
+ kind: "schema";
87
+ tables: Record<string, TableDefinition>;
88
+ };
89
+
90
+ export class ColumnBuilder {
91
+ private definition: ColumnDefinition;
92
+
93
+ public constructor(
94
+ typeOrDefinition: ColumnType | ColumnDefinition,
95
+ options: ColumnBuilderOptions = {},
96
+ ) {
97
+ if (typeof typeOrDefinition === "string") {
98
+ this.definition = {
99
+ kind: "column",
100
+ type: typeOrDefinition,
101
+ sqlName: options.sqlName,
102
+ length: options.length,
103
+ };
104
+ return;
105
+ }
106
+
107
+ this.definition = {
108
+ ...typeOrDefinition,
109
+ };
110
+ }
111
+
112
+ private with(patch: Partial<ColumnDefinition>): ColumnBuilder {
113
+ return new ColumnBuilder({
114
+ ...this.definition,
115
+ ...patch,
116
+ });
117
+ }
118
+
119
+ public sql(name: string): ColumnBuilder {
120
+ return this.with({ sqlName: name });
121
+ }
122
+
123
+ public notNull(): ColumnBuilder {
124
+ return this.with({ notNull: true });
125
+ }
126
+
127
+ public primaryKey(options: PrimaryKeyOptions = {}): ColumnBuilder {
128
+ return this.with({
129
+ primaryKey: true,
130
+ autoIncrement: options.autoIncrement ?? this.definition.autoIncrement,
131
+ });
132
+ }
133
+
134
+ public unique(name?: string): ColumnBuilder {
135
+ return this.with({ unique: name ? { name } : true });
136
+ }
137
+
138
+ public index(name?: string): ColumnBuilder {
139
+ return this.with({ index: name ? { name } : true });
140
+ }
141
+
142
+ public default(value: unknown): ColumnBuilder {
143
+ return this.with({ sqlDefault: value });
144
+ }
145
+
146
+ public defaultFn(fn: () => unknown): ColumnBuilder {
147
+ return this.with({ runtimeDefaultFn: fn });
148
+ }
149
+
150
+ public references(table: string, column = "id"): ColumnBuilder {
151
+ return this.with({ references: { table, column } });
152
+ }
153
+
154
+ public toDefinition(): ColumnDefinition {
155
+ return { ...this.definition };
156
+ }
157
+ }
158
+
159
+ export function table(
160
+ shape: TableShape,
161
+ options: TableOptions = {},
162
+ ): TableDefinition {
163
+ const columns: Record<string, ColumnDefinition> = {};
164
+ const relations: Record<string, RelationDefinition> = {};
165
+
166
+ for (const [fieldName, value] of Object.entries(shape)) {
167
+ if (value instanceof ColumnBuilder) {
168
+ columns[fieldName] = value.toDefinition();
169
+ continue;
170
+ }
171
+
172
+ if (value.kind === "relation") {
173
+ relations[fieldName] = value;
174
+ continue;
175
+ }
176
+
177
+ throw new Error(
178
+ `Invalid table field '${fieldName}'. Use column builders or relation helpers.`,
179
+ );
180
+ }
181
+
182
+ return {
183
+ kind: "table",
184
+ sqlName: options.sqlName,
185
+ columns,
186
+ relations,
187
+ };
188
+ }
189
+
190
+ export function schema(
191
+ tables: Record<string, TableDefinition>,
192
+ ): SchemaDefinition {
193
+ return {
194
+ kind: "schema",
195
+ tables,
196
+ };
197
+ }
198
+
199
+ export function isSchemaDefinition(value: unknown): value is SchemaDefinition {
200
+ if (typeof value !== "object" || value === null) {
201
+ return false;
202
+ }
203
+ const candidate = value as { kind?: string; tables?: unknown };
204
+ return candidate.kind === "schema" && typeof candidate.tables === "object";
205
+ }
206
+
207
+ export const v = {
208
+ int: (options: ColumnBuilderOptions = {}) =>
209
+ new ColumnBuilder("int", options),
210
+ string: (options: ColumnBuilderOptions = {}) =>
211
+ new ColumnBuilder("string", options),
212
+ boolean: (options: ColumnBuilderOptions = {}) =>
213
+ new ColumnBuilder("boolean", options),
214
+ date: (options: ColumnBuilderOptions = {}) =>
215
+ new ColumnBuilder("date", options),
216
+ one: (
217
+ targetTable: string,
218
+ fieldOrOptions?: string | RelationOneOptions,
219
+ options: RelationOneOptions = {},
220
+ ): OneRelationDefinition => {
221
+ const field =
222
+ typeof fieldOrOptions === "string" ? fieldOrOptions : undefined;
223
+ const mergedOptions =
224
+ typeof fieldOrOptions === "string"
225
+ ? options
226
+ : (fieldOrOptions ?? options);
227
+
228
+ return {
229
+ kind: "relation",
230
+ relation: "one",
231
+ targetTable,
232
+ field: mergedOptions.field ?? field,
233
+ referenceField: mergedOptions.referenceField,
234
+ fkType: mergedOptions.fkType,
235
+ sqlName: mergedOptions.sqlName,
236
+ notNull: mergedOptions.notNull,
237
+ };
238
+ },
239
+ many: (
240
+ targetTable: string,
241
+ fieldOrOptions?: string | RelationManyOptions,
242
+ options: RelationManyOptions = {},
243
+ ): ManyRelationDefinition => {
244
+ const field =
245
+ typeof fieldOrOptions === "string" ? fieldOrOptions : undefined;
246
+ const mergedOptions =
247
+ typeof fieldOrOptions === "string"
248
+ ? options
249
+ : (fieldOrOptions ?? options);
250
+
251
+ return {
252
+ kind: "relation",
253
+ relation: "many",
254
+ targetTable,
255
+ field: mergedOptions.field ?? field,
256
+ referenceField: mergedOptions.referenceField,
257
+ fkType: mergedOptions.fkType,
258
+ sqlName: mergedOptions.sqlName,
259
+ notNull: mergedOptions.notNull,
260
+ };
261
+ },
262
+ };
package/tsconfig.json CHANGED
@@ -1,8 +1,6 @@
1
1
  {
2
+ "extends": "../../tsconfig.json",
2
3
  "compilerOptions": {
3
- "target": "es2022",
4
- "module": "es2022",
5
- "moduleResolution": "bundler",
6
- "types": ["@cloudflare/workers-types/2023-07-01", "@types/node"]
4
+ "rootDir": "."
7
5
  }
8
6
  }
package/cli/README.md DELETED
@@ -1,108 +0,0 @@
1
- # Appflare CLI
2
-
3
- This folder contains the build toolchain that turns an Appflare project (schema + query/mutation handlers) into a fully generated API surface (typed client, Hono server, websocket durable object, and optional JS/.d.ts emit). The CLI is Bun-based and is intended to run inside a project that exports `appflare.config.ts`.
4
-
5
- ## Command surface
6
-
7
- - **build**: entrypoint defined in [packages/appflare/cli/index.ts](packages/appflare/cli/index.ts). Generates all artifacts into the configured `outDir` and optionally emits compiled output.
8
- - `-c, --config <path>`: path to the config file (defaults to `appflare.config.ts`).
9
- - `--emit`: after generation, run `bunx tsc` with a temporary tsconfig to emit JS and .d.ts into `outDir/dist`.
10
- - `-w, --watch`: keep the process alive, watch for file changes (excluding `outDir`/`node_modules`/build artifacts), and rerun the build. Recomputes watched paths when the config changes.
11
-
12
- ### Config shape
13
-
14
- `appflare.config.ts` must default-export an object with:
15
-
16
- ```ts
17
- export default {
18
- dir: "./app", // Root folder containing your handlers
19
- schema: "./schema.ts", // Path to the Zod schema file
20
- outDir: "./_generated", // Where generated files are written
21
- auth: {
22
- // Optional: Better Auth config forwarded to the generated server
23
- enabled: false,
24
- basePath: "/auth",
25
- options: {},
26
- },
27
- };
28
- ```
29
-
30
- The loader in [packages/appflare/cli/core/config.ts](packages/appflare/cli/core/config.ts) validates presence and types of `dir`, `schema`, and `outDir`, lightly checks optional `auth` (base path, enabled flag, options object), and resolves paths relative to the config file location.
31
-
32
- ## Build pipeline
33
-
34
- The build orchestrator in [packages/appflare/cli/core/build.ts](packages/appflare/cli/core/build.ts) performs the following steps when `build` runs:
35
-
36
- 1. Resolve absolute paths for `dir`, `schema`, and `outDir`; ensure `dir` and `schema` exist.
37
- 2. Create `outDir/src` and `outDir/server` if missing.
38
- 3. Generate typed schema helpers into `outDir/src/schema-types.ts` using [packages/appflare/cli/schema/schema.ts](packages/appflare/cli/schema/schema.ts). This produces table doc interfaces, `TableNames`, `Id`, query helpers, and convenience types from [packages/appflare/cli/schema/schema-static-types.ts](packages/appflare/cli/schema/schema-static-types.ts).
39
- 4. Generate built-in CRUD handlers for every table into `outDir/src/handlers/<table>.ts` plus an index via [packages/appflare/cli/generators/generate-db-handlers.ts](packages/appflare/cli/generators/generate-db-handlers.ts). These include `find*`, `findOne*`, `insert*`, `update*`, and `delete*` operations backed by the generated schema types.
40
- 5. Discover user-defined handlers under `dir` with [packages/appflare/cli/core/discover-handlers.ts](packages/appflare/cli/core/discover-handlers.ts):
41
- - Recurses through `.ts` files (excluding `node_modules`, `.git`, `dist`, `build`, and the configured `outDir`).
42
- - Recognizes handlers declared as `export const <name> = query(` or `export const <name> = mutation(`.
43
- - Skips the schema file and the config file; deduplicates by kind/file/name.
44
- 6. Generate a typed client at `outDir/src/api.ts` via [packages/appflare/cli/generators/generate-api-client.ts](packages/appflare/cli/generators/generate-api-client.ts):
45
- - Produces `createAppflareApi()` with `queries` and `mutations` collections keyed by `<file>/<handler>`.
46
- - Each handler function wraps `fetch` (default `better-fetch`) and carries metadata: Zod schema, websocket helper, and route path.
47
- - Realtime helpers build websocket URLs for subscriptions, normalizing `ws`/`wss` bases and providing hooks (`onOpen`, `onMessage`, `onData`, etc.).
48
- 7. Generate a Hono server at `outDir/server/server.ts` with [packages/appflare/cli/generators/generate-hono-server.ts](packages/appflare/cli/generators/generate-hono-server.ts):
49
- - Routes: `GET /queries/<file>/<name>` and `POST /mutations/<file>/<name>`.
50
- - Uses `@hono/standard-validator` + Zod arg schemas and wraps Mongo via `createMongoDbContext` from `appflare/server/db`.
51
- - Supports optional mutation notifications for realtime (custom notifier or Durable Object hook).
52
- 8. Generate a websocket Durable Object shim at `outDir/server/websocket-hibernation-server.ts` via [packages/appflare/cli/generators/generate-websocket-durable-object.ts](packages/appflare/cli/generators/generate-websocket-durable-object.ts):
53
- - Implements `WebSocketHibernationServer` to handle subscriptions at `/ws` and mutation notifications at `/notify`.
54
- - Selects a default query handler per table (or a specific handler) and re-fetches data on mutation notifications, emitting `data` messages to subscribers.
55
- 9. If `--emit` is set, remove any previous `outDir/dist`, write a temporary tsconfig (includes generated schema types and handlers only), and run `bunx tsc` to emit JS + .d.ts into `outDir/dist` (logic in [packages/appflare/cli/utils/tsc.ts](packages/appflare/cli/utils/tsc.ts)).
56
-
57
- ## Handler authoring guidelines
58
-
59
- - Handlers must be exported as `query({ args, handler })` or `mutation({ args, handler })` objects.
60
- - Filenames become the first route/path segment and grouping key in the client (`<file>/<handler>`).
61
- - Arguments are validated with Zod; the client infers optional vs required keys and provides typed `args` for both client and server.
62
- - The discovery step ignores `.d.ts` files and anything outside the configured `dir`.
63
-
64
- ## Generated layout (relative to `outDir`)
65
-
66
- ```
67
- src/
68
- schema-types.ts # Typed schema exports, helpers, and Zod-powered validator types
69
- handlers/ # Auto CRUD handlers per table + index
70
- <table>.ts
71
- index.ts
72
- api.ts # Typed queries/mutations client with realtime helpers
73
- server/
74
- server.ts # Hono server that wires handlers + Mongo context
75
- websocket-hibernation-server.ts # Durable Object websocket bridge
76
- ```
77
-
78
- ## Realtime and Durable Object flow
79
-
80
- - Client websockets are created via `handler.websocket(args?, options?)`, building URLs against `realtime.baseUrl` (or handler override) and defaulting the `table` and `handler` params based on the handler’s file/name.
81
- - The Durable Object handles `/ws` upgrades, parses subscription params (`table`, `handler`, `where`, `orderBy`, `take`, `skip`, `select`, `include`, `args`), and caches subscriptions. On `/notify` payloads, it re-runs the relevant query or table fetch and pushes a `data` message to connected sockets.
82
- - Mutation notifications can be sent by the generated Hono server (if `realtime.notify` or `realtime.durableObject` is provided) so subscriptions stay in sync.
83
-
84
- ## Error handling and safeguards
85
-
86
- - Config, schema, and project directories are validated before generation; missing paths throw with readable errors.
87
- - Build de-duplicates discovered handlers to avoid duplicate route generation.
88
- - `--emit` uses a scoped tsconfig that only references generated files to prevent user code outside `rootDir` from breaking emit.
89
- - Generated files include `/* eslint-disable */` headers to avoid lint noise.
90
-
91
- ## Typical usage
92
-
93
- ```sh
94
- # From the repo root (config defaults to ./appflare.config.ts)
95
- bunx appflare build
96
-
97
- # Custom config location and emit compiled JS
98
- bunx appflare build --config ./config/appflare.config.ts --emit
99
- ```
100
-
101
- After running, import the generated client/server:
102
-
103
- ```ts
104
- import { createAppflareApi } from "./_generated/src/api";
105
- import server from "./_generated/server/server";
106
- ```
107
-
108
- Use the client in web/React or server contexts, and deploy the generated Hono server + Durable Object to your runtime of choice (e.g., Cloudflare Workers with Mongo).