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.
- package/cli/commands/index.ts +140 -0
- package/cli/generate.ts +149 -0
- package/cli/index.ts +56 -447
- package/cli/load-config.ts +182 -0
- package/cli/schema-compiler.ts +657 -0
- package/cli/templates/auth/README.md +156 -0
- package/cli/templates/auth/config.ts +61 -0
- package/cli/templates/auth/route-config.ts +18 -0
- package/cli/templates/auth/route-handler.ts +18 -0
- package/cli/templates/auth/route-request-utils.ts +55 -0
- package/cli/templates/auth/route.ts +14 -0
- package/cli/templates/core/README.md +266 -0
- package/cli/templates/core/app-creation.ts +19 -0
- package/cli/templates/core/client/appflare.ts +37 -0
- package/cli/templates/core/client/index.ts +6 -0
- package/cli/templates/core/client/storage.ts +100 -0
- package/cli/templates/core/client/types.ts +54 -0
- package/cli/templates/core/client-modules/appflare.ts +112 -0
- package/cli/templates/core/client-modules/handlers/index.ts +740 -0
- package/cli/templates/core/client-modules/handlers.ts +1 -0
- package/cli/templates/core/client-modules/index.ts +7 -0
- package/cli/templates/core/client-modules/storage.ts +180 -0
- package/cli/templates/core/client-modules/types.ts +145 -0
- package/cli/templates/core/client.ts +39 -0
- package/cli/templates/core/drizzle.ts +15 -0
- package/cli/templates/core/export.ts +14 -0
- package/cli/templates/core/handlers-route.ts +23 -0
- package/cli/templates/core/handlers.ts +1 -0
- package/cli/templates/core/imports.ts +8 -0
- package/cli/templates/core/server.ts +38 -0
- package/cli/templates/core/types.ts +6 -0
- package/cli/templates/core/wrangler.ts +109 -0
- package/cli/templates/handlers/README.md +265 -0
- package/cli/templates/handlers/auth.ts +36 -0
- package/cli/templates/handlers/execution.ts +39 -0
- package/cli/templates/handlers/generators/context/context-creation.ts +80 -0
- package/cli/templates/handlers/generators/context/error-helpers.ts +11 -0
- package/cli/templates/handlers/generators/context/scheduler.ts +24 -0
- package/cli/templates/handlers/generators/context/storage-api.ts +112 -0
- package/cli/templates/handlers/generators/context/storage-helpers.ts +59 -0
- package/cli/templates/handlers/generators/context/types.ts +18 -0
- package/cli/templates/handlers/generators/context.ts +43 -0
- package/cli/templates/handlers/generators/execution.ts +15 -0
- package/cli/templates/handlers/generators/handlers.ts +13 -0
- package/cli/templates/handlers/index.ts +43 -0
- package/cli/templates/handlers/operations.ts +116 -0
- package/cli/templates/handlers/registration.ts +1114 -0
- package/cli/templates/handlers/types.ts +960 -0
- package/cli/templates/handlers/utils.ts +48 -0
- package/cli/types.ts +108 -0
- package/cli/utils/handler-discovery.ts +366 -0
- package/cli/utils/json-utils.ts +24 -0
- package/cli/utils/path-utils.ts +19 -0
- package/cli/utils/schema-discovery.ts +390 -0
- package/index.ts +27 -4
- package/package.json +23 -20
- package/react/index.ts +5 -3
- package/react/use-infinite-query.ts +190 -0
- package/react/use-mutation.ts +54 -0
- package/react/use-query.ts +158 -0
- package/schema.ts +262 -0
- package/tsconfig.json +2 -4
- package/cli/README.md +0 -108
- package/cli/core/build.ts +0 -187
- package/cli/core/config.ts +0 -92
- package/cli/core/discover-handlers.ts +0 -143
- package/cli/core/handlers.ts +0 -7
- package/cli/core/index.ts +0 -205
- package/cli/generators/generate-api-client/client.ts +0 -163
- package/cli/generators/generate-api-client/extract-configuration.ts +0 -121
- package/cli/generators/generate-api-client/index.ts +0 -973
- package/cli/generators/generate-api-client/types.ts +0 -164
- package/cli/generators/generate-api-client/utils.ts +0 -22
- package/cli/generators/generate-api-client.ts +0 -1
- package/cli/generators/generate-cloudflare-worker/helpers.ts +0 -24
- package/cli/generators/generate-cloudflare-worker/index.ts +0 -2
- package/cli/generators/generate-cloudflare-worker/worker.ts +0 -148
- package/cli/generators/generate-cloudflare-worker/wrangler.ts +0 -108
- package/cli/generators/generate-cloudflare-worker.ts +0 -4
- package/cli/generators/generate-cron-handlers/cron-handlers-block.ts +0 -2
- package/cli/generators/generate-cron-handlers/handler-entries.ts +0 -29
- package/cli/generators/generate-cron-handlers/index.ts +0 -61
- package/cli/generators/generate-cron-handlers/runtime-block.ts +0 -49
- package/cli/generators/generate-cron-handlers/type-helpers-block.ts +0 -60
- package/cli/generators/generate-db-handlers/index.ts +0 -33
- package/cli/generators/generate-db-handlers/prepare.ts +0 -24
- package/cli/generators/generate-db-handlers/templates.ts +0 -189
- package/cli/generators/generate-db-handlers.ts +0 -1
- package/cli/generators/generate-hono-server/auth.ts +0 -97
- package/cli/generators/generate-hono-server/imports.ts +0 -55
- package/cli/generators/generate-hono-server/index.ts +0 -52
- package/cli/generators/generate-hono-server/routes.ts +0 -115
- package/cli/generators/generate-hono-server/template.ts +0 -371
- package/cli/generators/generate-hono-server.ts +0 -1
- package/cli/generators/generate-scheduler-handlers/constants.ts +0 -8
- package/cli/generators/generate-scheduler-handlers/handler-entries.ts +0 -22
- package/cli/generators/generate-scheduler-handlers/index.ts +0 -51
- package/cli/generators/generate-scheduler-handlers/runtime-block.ts +0 -68
- package/cli/generators/generate-scheduler-handlers/scheduler-handlers-block.ts +0 -2
- package/cli/generators/generate-scheduler-handlers/type-helpers-block.ts +0 -68
- package/cli/generators/generate-scheduler-handlers.ts +0 -1
- package/cli/generators/generate-websocket-durable-object/auth.ts +0 -30
- package/cli/generators/generate-websocket-durable-object/imports.ts +0 -55
- package/cli/generators/generate-websocket-durable-object/index.ts +0 -41
- package/cli/generators/generate-websocket-durable-object/query-handlers.ts +0 -18
- package/cli/generators/generate-websocket-durable-object/template.ts +0 -714
- package/cli/generators/generate-websocket-durable-object.ts +0 -1
- package/cli/schema/schema-static-types.ts +0 -702
- package/cli/schema/schema.ts +0 -151
- package/cli/utils/tsc.ts +0 -54
- package/cli/utils/utils.ts +0 -190
- package/cli/utils/zod-utils.ts +0 -121
- package/lib/README.md +0 -50
- package/lib/db.ts +0 -19
- package/lib/location.ts +0 -110
- package/lib/values.ts +0 -27
- package/react/README.md +0 -67
- package/react/hooks/useMutation.ts +0 -89
- package/react/hooks/usePaginatedQuery.ts +0 -213
- package/react/hooks/useQuery.ts +0 -106
- package/react/shared/queryShared.ts +0 -174
- package/server/README.md +0 -218
- package/server/auth.ts +0 -107
- package/server/database/builders.ts +0 -83
- package/server/database/context.ts +0 -327
- package/server/database/populate.ts +0 -234
- package/server/database/query-builder.ts +0 -161
- package/server/database/query-utils.ts +0 -25
- package/server/db.ts +0 -2
- package/server/storage/auth.ts +0 -16
- package/server/storage/bucket.ts +0 -22
- package/server/storage/context.ts +0 -34
- package/server/storage/index.ts +0 -38
- package/server/storage/operations.ts +0 -149
- package/server/storage/route-handler.ts +0 -60
- package/server/storage/types.ts +0 -55
- package/server/storage/utils.ts +0 -47
- package/server/storage.ts +0 -6
- package/server/types/schema-refs.ts +0 -66
- package/server/types/types.ts +0 -633
- package/server/utils/id-utils.ts +0 -230
|
@@ -1,973 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DiscoveredHandler,
|
|
3
|
-
groupBy,
|
|
4
|
-
pascalCase,
|
|
5
|
-
toImportPathFromGeneratedSrc,
|
|
6
|
-
} from "../../utils/utils";
|
|
7
|
-
import {
|
|
8
|
-
generateMutationsClientLines,
|
|
9
|
-
generateQueriesClientLines,
|
|
10
|
-
} from "./client";
|
|
11
|
-
import {
|
|
12
|
-
generateMutationsTypeLines,
|
|
13
|
-
generateQueriesTypeLines,
|
|
14
|
-
generateTypeBlocks,
|
|
15
|
-
generateInternalTypeLines,
|
|
16
|
-
} from "./types";
|
|
17
|
-
import { renderObjectKey } from "./utils";
|
|
18
|
-
import { extractClientConfig } from "./extract-configuration";
|
|
19
|
-
|
|
20
|
-
const HEADER_TEMPLATE = `/* eslint-disable */
|
|
21
|
-
/**
|
|
22
|
-
* This file is auto-generated by appflare/handler-build.ts.
|
|
23
|
-
* Do not edit directly.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
import fetch from "better-fetch";
|
|
27
|
-
import { z } from "zod";
|
|
28
|
-
import type { BetterAuthClientOptions } from "better-auth/client";
|
|
29
|
-
import { createAuthClient } from "better-auth/client";
|
|
30
|
-
|
|
31
|
-
import type {
|
|
32
|
-
AnyValidator,
|
|
33
|
-
InferQueryArgs,
|
|
34
|
-
MutationDefinition,
|
|
35
|
-
QueryArgsShape,
|
|
36
|
-
QueryDefinition,
|
|
37
|
-
InternalMutationContext,
|
|
38
|
-
InternalMutationDefinition,
|
|
39
|
-
InternalQueryContext,
|
|
40
|
-
InternalQueryDefinition,
|
|
41
|
-
} from "./schema-types";
|
|
42
|
-
{{configImport}}
|
|
43
|
-
`;
|
|
44
|
-
|
|
45
|
-
const TYPE_DEFINITIONS_TEMPLATE = `
|
|
46
|
-
type AnyArgsShape = Record<string, AnyValidator>;
|
|
47
|
-
|
|
48
|
-
type AnyHandlerDefinition = QueryDefinition<AnyArgsShape, unknown> | MutationDefinition<AnyArgsShape, unknown>;
|
|
49
|
-
|
|
50
|
-
type Simplify<T> = { [K in keyof T]: T[K] };
|
|
51
|
-
|
|
52
|
-
type OptionalKeys<TArgs extends QueryArgsShape> = {
|
|
53
|
-
[K in keyof TArgs]: TArgs[K] extends z.ZodOptional<any> | z.ZodDefault<any> ? K : never;
|
|
54
|
-
}[keyof TArgs];
|
|
55
|
-
|
|
56
|
-
type RequiredKeys<TArgs extends QueryArgsShape> = Exclude<keyof TArgs, OptionalKeys<TArgs>>;
|
|
57
|
-
|
|
58
|
-
type HandlerArgsFromShape<TArgs extends QueryArgsShape> = Simplify<
|
|
59
|
-
Partial<Pick<InferQueryArgs<TArgs>, OptionalKeys<TArgs>>> &
|
|
60
|
-
Pick<InferQueryArgs<TArgs>, RequiredKeys<TArgs>>
|
|
61
|
-
>;
|
|
62
|
-
|
|
63
|
-
type HandlerArgs<THandler extends AnyHandlerDefinition> =
|
|
64
|
-
THandler extends { args: infer TArgs extends QueryArgsShape }
|
|
65
|
-
? HandlerArgsFromShape<TArgs>
|
|
66
|
-
: never;
|
|
67
|
-
|
|
68
|
-
type HandlerResult<THandler extends AnyHandlerDefinition> = THandler extends {
|
|
69
|
-
handler: (...args: any[]) => Promise<infer TResult>;
|
|
70
|
-
}
|
|
71
|
-
? TResult
|
|
72
|
-
: never;
|
|
73
|
-
|
|
74
|
-
type HandlerInvoker<TArgs, TResult> = (args: TArgs, init?: RequestInit) => Promise<TResult>;
|
|
75
|
-
|
|
76
|
-
type HandlerArgsShape<THandler extends AnyHandlerDefinition> =
|
|
77
|
-
THandler extends { args: infer TArgs extends QueryArgsShape }
|
|
78
|
-
? TArgs
|
|
79
|
-
: never;
|
|
80
|
-
|
|
81
|
-
type HandlerSchemaFromShape<TArgs extends QueryArgsShape> = z.ZodObject<{
|
|
82
|
-
[K in keyof TArgs]: z.ZodType<InferQueryArgs<TArgs>[K]>;
|
|
83
|
-
}>;
|
|
84
|
-
|
|
85
|
-
type HandlerSchema<THandler extends AnyHandlerDefinition> = HandlerSchemaFromShape<
|
|
86
|
-
HandlerArgsShape<THandler>
|
|
87
|
-
>;
|
|
88
|
-
|
|
89
|
-
type WebSocketHeaders = Record<string, string>;
|
|
90
|
-
|
|
91
|
-
type WebSocketFactoryOptions = { headers?: WebSocketHeaders };
|
|
92
|
-
|
|
93
|
-
type WebSocketFactory = (
|
|
94
|
-
url: string,
|
|
95
|
-
protocols?: string | string[],
|
|
96
|
-
options?: WebSocketFactoryOptions
|
|
97
|
-
) => WebSocket;
|
|
98
|
-
|
|
99
|
-
export type RealtimeMessage<TResult> = {
|
|
100
|
-
type?: string;
|
|
101
|
-
data?: TResult[];
|
|
102
|
-
table?: string;
|
|
103
|
-
where?: unknown;
|
|
104
|
-
[key: string]: unknown;
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
export type HandlerWebsocketOptions<TResult> = {
|
|
108
|
-
baseUrl?: string;
|
|
109
|
-
table?: string;
|
|
110
|
-
handler?: { file: string; name: string };
|
|
111
|
-
handlerFile?: string;
|
|
112
|
-
handlerName?: string;
|
|
113
|
-
where?: Record<string, unknown>;
|
|
114
|
-
orderBy?: Record<string, unknown>;
|
|
115
|
-
take?: number;
|
|
116
|
-
skip?: number;
|
|
117
|
-
path?: string;
|
|
118
|
-
protocols?: string | string[];
|
|
119
|
-
headers?: WebSocketHeaders;
|
|
120
|
-
signal?: AbortSignal;
|
|
121
|
-
websocketImpl?: WebSocketFactory;
|
|
122
|
-
onOpen?: (event: any) => void;
|
|
123
|
-
onClose?: (event: any) => void;
|
|
124
|
-
onError?: (event: any) => void;
|
|
125
|
-
onMessage?: (message: RealtimeMessage<TResult>, raw: any) => void;
|
|
126
|
-
onData?: (data: TResult[], message: RealtimeMessage<TResult>) => void;
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
type HandlerMetadata<THandler extends AnyHandlerDefinition> = {
|
|
130
|
-
schema: HandlerSchema<THandler>;
|
|
131
|
-
websocket: HandlerWebsocket<
|
|
132
|
-
HandlerArgs<THandler>,
|
|
133
|
-
HandlerResult<THandler>
|
|
134
|
-
>;
|
|
135
|
-
path: string;
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
export type HandlerWebsocket<TArgs, TResult> = (
|
|
139
|
-
args?: TArgs,
|
|
140
|
-
options?: HandlerWebsocketOptions<TResult>
|
|
141
|
-
) => WebSocket;
|
|
142
|
-
|
|
143
|
-
export type AppflareHandler<THandler extends AnyHandlerDefinition> = HandlerInvoker<
|
|
144
|
-
HandlerArgs<THandler>,
|
|
145
|
-
HandlerResult<THandler>
|
|
146
|
-
> &
|
|
147
|
-
HandlerMetadata<THandler>;
|
|
148
|
-
|
|
149
|
-
export type StoragePutResult = {
|
|
150
|
-
key: string;
|
|
151
|
-
size: number;
|
|
152
|
-
contentType: string;
|
|
153
|
-
cacheControl: string;
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
export type StorageDeleteResult = {
|
|
157
|
-
key: string;
|
|
158
|
-
deleted: boolean;
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
export type StorageManagerClient = {
|
|
162
|
-
url: (path: string) => string;
|
|
163
|
-
get: (path: string, init?: RequestInit) => Promise<Response>;
|
|
164
|
-
head: (path: string, init?: RequestInit) => Promise<Response>;
|
|
165
|
-
put: (
|
|
166
|
-
path: string,
|
|
167
|
-
body: BodyInit,
|
|
168
|
-
init?: RequestInit
|
|
169
|
-
) => Promise<StoragePutResult>;
|
|
170
|
-
post: (
|
|
171
|
-
path: string,
|
|
172
|
-
body: BodyInit,
|
|
173
|
-
init?: RequestInit
|
|
174
|
-
) => Promise<StoragePutResult>;
|
|
175
|
-
delete: (path: string, init?: RequestInit) => Promise<StorageDeleteResult>;
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
export type StorageManagerOptions = {
|
|
179
|
-
basePath?: string;
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
type RequestExecutor = (
|
|
183
|
-
input: RequestInfo | URL,
|
|
184
|
-
init?: RequestInit
|
|
185
|
-
) => Promise<Response>;
|
|
186
|
-
|
|
187
|
-
const defaultFetcher: RequestExecutor = (input, init) => fetch(input, init);
|
|
188
|
-
|
|
189
|
-
const defaultWebSocketFactory: WebSocketFactory = (
|
|
190
|
-
url,
|
|
191
|
-
protocols,
|
|
192
|
-
_options
|
|
193
|
-
) => {
|
|
194
|
-
if (typeof WebSocket === "undefined") {
|
|
195
|
-
throw new Error(
|
|
196
|
-
"WebSocket is not available in this environment. Provide options.realtime.websocketImpl to create websockets."
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
return new WebSocket(url, protocols);
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
type ResolvedRealtimeConfig = {
|
|
203
|
-
baseUrl?: string;
|
|
204
|
-
path: string;
|
|
205
|
-
headers?: WebSocketHeaders;
|
|
206
|
-
websocketImpl?: WebSocketFactory;
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
type RealtimeConfig = {
|
|
210
|
-
baseUrl?: string;
|
|
211
|
-
path?: string;
|
|
212
|
-
headers?: WebSocketHeaders;
|
|
213
|
-
websocketImpl?: WebSocketFactory;
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
`;
|
|
217
|
-
|
|
218
|
-
const INTERNAL_TEMPLATE = `
|
|
219
|
-
export type InternalQueries = {{internalQueriesTypeDef}};
|
|
220
|
-
|
|
221
|
-
export type InternalMutations = {{internalMutationsTypeDef}};
|
|
222
|
-
|
|
223
|
-
export type InternalHandlers = InternalQueries & InternalMutations;
|
|
224
|
-
|
|
225
|
-
export const internal: InternalHandlers = {{internalInit}};
|
|
226
|
-
|
|
227
|
-
const __internalQueries = [
|
|
228
|
-
{{internalQueriesMeta}}
|
|
229
|
-
];
|
|
230
|
-
|
|
231
|
-
const __internalMutations = [
|
|
232
|
-
{{internalMutationsMeta}}
|
|
233
|
-
];
|
|
234
|
-
|
|
235
|
-
type BoundInternalHandlers = {
|
|
236
|
-
[F in keyof InternalHandlers]: {
|
|
237
|
-
[N in keyof InternalHandlers[F]]: InternalHandlers[F][N] extends InternalQueryDefinition<
|
|
238
|
-
infer TArgs,
|
|
239
|
-
infer TResult
|
|
240
|
-
>
|
|
241
|
-
? (args: HandlerArgsFromShape<TArgs>) => Promise<TResult>
|
|
242
|
-
: InternalHandlers[F][N] extends InternalMutationDefinition<
|
|
243
|
-
infer TArgs,
|
|
244
|
-
infer TResult
|
|
245
|
-
>
|
|
246
|
-
? (args: HandlerArgsFromShape<TArgs>) => Promise<TResult>
|
|
247
|
-
: never;
|
|
248
|
-
};
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
export type InternalCaller = {
|
|
252
|
-
internal: BoundInternalHandlers;
|
|
253
|
-
runQuery: <TArgs extends QueryArgsShape, TResult>(
|
|
254
|
-
handler: InternalQueryDefinition<TArgs, TResult>,
|
|
255
|
-
args: HandlerArgsFromShape<TArgs>
|
|
256
|
-
) => Promise<TResult>;
|
|
257
|
-
runMutation: <TArgs extends QueryArgsShape, TResult>(
|
|
258
|
-
handler: InternalMutationDefinition<TArgs, TResult>,
|
|
259
|
-
args: HandlerArgsFromShape<TArgs>
|
|
260
|
-
) => Promise<TResult>;
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
export function createInternalCaller(
|
|
264
|
-
ctx: InternalQueryContext | InternalMutationContext
|
|
265
|
-
): InternalCaller {
|
|
266
|
-
const bound: Record<string, Record<string, any>> = {};
|
|
267
|
-
for (const entry of __internalQueries) {
|
|
268
|
-
(bound[entry.file] ||= {})[entry.name] = (args: any) =>
|
|
269
|
-
runInternalQuery(ctx as any, entry.handler as any, args as any);
|
|
270
|
-
}
|
|
271
|
-
for (const entry of __internalMutations) {
|
|
272
|
-
(bound[entry.file] ||= {})[entry.name] = (args: any) =>
|
|
273
|
-
runInternalMutation(ctx as any, entry.handler as any, args as any);
|
|
274
|
-
}
|
|
275
|
-
return {
|
|
276
|
-
internal: bound as BoundInternalHandlers,
|
|
277
|
-
runQuery: (handler, args) =>
|
|
278
|
-
runInternalQuery(ctx as any, handler as any, args as any),
|
|
279
|
-
runMutation: (handler, args) =>
|
|
280
|
-
runInternalMutation(ctx as any, handler as any, args as any),
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
`;
|
|
284
|
-
|
|
285
|
-
const CLIENT_TYPES_TEMPLATE = `
|
|
286
|
-
export type QueriesClient = {{queriesTypeDef}};
|
|
287
|
-
|
|
288
|
-
export type MutationsClient = {{mutationsTypeDef}};
|
|
289
|
-
|
|
290
|
-
{{authClientTypeDefinitions}}
|
|
291
|
-
|
|
292
|
-
export type AppflareApiClient = {
|
|
293
|
-
queries: QueriesClient;
|
|
294
|
-
mutations: MutationsClient;
|
|
295
|
-
storage: StorageManagerClient;
|
|
296
|
-
auth?: AppflareAuthClient;
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
export type AppflareApiOptions = {
|
|
300
|
-
baseUrl?: string;
|
|
301
|
-
fetcher?: RequestExecutor;
|
|
302
|
-
realtime?: RealtimeConfig;
|
|
303
|
-
storage?: StorageManagerOptions;
|
|
304
|
-
auth?: (BetterAuthClientOptions & { baseURL?: string });
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
export function createAppflareApi(options: AppflareApiOptions = {}) {
|
|
308
|
-
const baseUrl = normalizeBaseUrl(options.baseUrl);
|
|
309
|
-
const request = options.fetcher ?? defaultFetcher;
|
|
310
|
-
const realtime = resolveRealtimeConfig(baseUrl, options.realtime);
|
|
311
|
-
const queries: QueriesClient = {{queriesInit}};
|
|
312
|
-
const mutations: MutationsClient = {{mutationsInit}};
|
|
313
|
-
const storage = createStorageManagerClient(baseUrl, request, options.storage);
|
|
314
|
-
const authBasePath = normalizeAuthBasePath({{authBasePath}}) ?? "/auth";
|
|
315
|
-
{{authClientInit}}
|
|
316
|
-
return { queries, mutations, storage, auth };
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
`;
|
|
320
|
-
|
|
321
|
-
const UTILITY_FUNCTIONS_TEMPLATE_PART1 = `
|
|
322
|
-
function withHandlerMetadata<THandler extends AnyHandlerDefinition>(
|
|
323
|
-
invoke: HandlerInvoker<HandlerArgs<THandler>, HandlerResult<THandler>>,
|
|
324
|
-
meta: HandlerMetadata<THandler>
|
|
325
|
-
): AppflareHandler<THandler> {
|
|
326
|
-
const fn = invoke as AppflareHandler<THandler>;
|
|
327
|
-
fn.schema = meta.schema;
|
|
328
|
-
fn.websocket = meta.websocket;
|
|
329
|
-
fn.path = meta.path;
|
|
330
|
-
return fn;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
function resolveRealtimeConfig(
|
|
334
|
-
baseUrl: string,
|
|
335
|
-
realtime?: RealtimeConfig
|
|
336
|
-
): ResolvedRealtimeConfig {
|
|
337
|
-
return {
|
|
338
|
-
baseUrl: normalizeWsBaseUrl(realtime?.baseUrl ?? baseUrl),
|
|
339
|
-
path: realtime?.path ?? "/ws",
|
|
340
|
-
headers: realtime?.headers,
|
|
341
|
-
websocketImpl: realtime?.websocketImpl ?? defaultWebSocketFactory,
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
function createHandlerSchema<TArgs extends QueryArgsShape>(
|
|
346
|
-
args: TArgs
|
|
347
|
-
): HandlerSchemaFromShape<TArgs> {
|
|
348
|
-
return z.object(args as any as Record<string, z.ZodTypeAny>) as HandlerSchemaFromShape<TArgs>;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
function parseHandlerArgs<THandler extends AnyHandlerDefinition>(
|
|
352
|
-
handler: THandler,
|
|
353
|
-
args: HandlerArgs<THandler>
|
|
354
|
-
): HandlerArgs<THandler> {
|
|
355
|
-
const schema = createHandlerSchema(handler.args as any);
|
|
356
|
-
return schema.parse(args ?? ({} as HandlerArgs<THandler>)) as HandlerArgs<THandler>;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
export async function runInternalQuery<
|
|
360
|
-
TArgs extends QueryArgsShape,
|
|
361
|
-
TResult,
|
|
362
|
-
>(
|
|
363
|
-
ctx: InternalQueryContext,
|
|
364
|
-
handler: InternalQueryDefinition<TArgs, TResult>,
|
|
365
|
-
args: HandlerArgsFromShape<TArgs>
|
|
366
|
-
): Promise<TResult> {
|
|
367
|
-
const parsed = parseHandlerArgs(handler as any, args as any);
|
|
368
|
-
if (handler.middleware) {
|
|
369
|
-
const middlewareResult = await handler.middleware(
|
|
370
|
-
ctx as any,
|
|
371
|
-
parsed as any
|
|
372
|
-
);
|
|
373
|
-
if (typeof middlewareResult !== "undefined") {
|
|
374
|
-
return middlewareResult as TResult;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
return handler.handler(ctx as any, parsed as any);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
export async function runInternalMutation<
|
|
381
|
-
TArgs extends QueryArgsShape,
|
|
382
|
-
TResult,
|
|
383
|
-
>(
|
|
384
|
-
ctx: InternalMutationContext,
|
|
385
|
-
handler: InternalMutationDefinition<TArgs, TResult>,
|
|
386
|
-
args: HandlerArgsFromShape<TArgs>
|
|
387
|
-
): Promise<TResult> {
|
|
388
|
-
const parsed = parseHandlerArgs(handler as any, args as any);
|
|
389
|
-
if (handler.middleware) {
|
|
390
|
-
const middlewareResult = await handler.middleware(
|
|
391
|
-
ctx as any,
|
|
392
|
-
parsed as any
|
|
393
|
-
);
|
|
394
|
-
if (typeof middlewareResult !== "undefined") {
|
|
395
|
-
return middlewareResult as TResult;
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
return handler.handler(ctx as any, parsed as any);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
`;
|
|
402
|
-
|
|
403
|
-
const UTILITY_FUNCTIONS_TEMPLATE_PART2 = `
|
|
404
|
-
function createHandlerWebsocket<TArgs, TResult>(
|
|
405
|
-
realtime: ResolvedRealtimeConfig,
|
|
406
|
-
defaults: { defaultTable: string; defaultHandler: { file: string; name: string } }
|
|
407
|
-
): HandlerWebsocket<TArgs, TResult> {
|
|
408
|
-
return (args, options) => {
|
|
409
|
-
const baseUrl = normalizeWsBaseUrl(options?.baseUrl ?? realtime.baseUrl);
|
|
410
|
-
if (!baseUrl) {
|
|
411
|
-
throw new Error(
|
|
412
|
-
"Missing realtime baseUrl. Provide createAppflareApi({ realtime: { baseUrl } }) or handler websocket options.baseUrl."
|
|
413
|
-
);
|
|
414
|
-
}
|
|
415
|
-
const params = new URLSearchParams();
|
|
416
|
-
const tableParam = options?.table ?? defaults.defaultTable;
|
|
417
|
-
const normalizedTable = tableParam.endsWith("s")
|
|
418
|
-
? tableParam
|
|
419
|
-
: tableParam + "s";
|
|
420
|
-
params.set("table", normalizedTable);
|
|
421
|
-
|
|
422
|
-
const handlerRef =
|
|
423
|
-
options?.handler ??
|
|
424
|
-
(options?.handlerFile && options?.handlerName
|
|
425
|
-
? { file: options.handlerFile, name: options.handlerName }
|
|
426
|
-
: defaults.defaultHandler);
|
|
427
|
-
if (handlerRef) {
|
|
428
|
-
params.set("handler", JSON.stringify(handlerRef));
|
|
429
|
-
}
|
|
430
|
-
const where = options?.where ?? (isPlainObject(args) ? (args as Record<string, unknown>) : undefined);
|
|
431
|
-
if (where && Object.keys(where).length > 0) {
|
|
432
|
-
params.set("where", JSON.stringify(where));
|
|
433
|
-
}
|
|
434
|
-
if (options?.orderBy) params.set("orderBy", JSON.stringify(options.orderBy));
|
|
435
|
-
if (options?.take !== undefined) params.set("take", String(options.take));
|
|
436
|
-
if (options?.skip !== undefined) params.set("skip", String(options.skip));
|
|
437
|
-
|
|
438
|
-
const path = options?.path ?? realtime.path;
|
|
439
|
-
const url = buildRealtimeUrl(baseUrl, path, params);
|
|
440
|
-
const websocketFactory = options?.websocketImpl ?? realtime.websocketImpl ?? defaultWebSocketFactory;
|
|
441
|
-
const socket = websocketFactory(url, options?.protocols, {
|
|
442
|
-
headers: options?.headers ?? realtime.headers,
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
if (options?.onOpen) socket.addEventListener("open", options.onOpen as any);
|
|
446
|
-
if (options?.onClose) socket.addEventListener("close", options.onClose as any);
|
|
447
|
-
if (options?.onError) socket.addEventListener("error", options.onError as any);
|
|
448
|
-
|
|
449
|
-
const onMessage = (event: any) => {
|
|
450
|
-
const message = parseRealtimeMessage<TResult>(event?.data ?? event);
|
|
451
|
-
if (options?.onMessage) options.onMessage(message, event);
|
|
452
|
-
if (message?.type === "data" && Array.isArray((message as any).data)) {
|
|
453
|
-
options?.onData?.((message as any).data as TResult[], message);
|
|
454
|
-
}
|
|
455
|
-
};
|
|
456
|
-
|
|
457
|
-
socket.addEventListener("message", onMessage as any);
|
|
458
|
-
|
|
459
|
-
if (options?.signal) {
|
|
460
|
-
const abortHandler = () => socket.close(1000, "aborted");
|
|
461
|
-
if (options.signal.aborted) abortHandler();
|
|
462
|
-
else options.signal.addEventListener("abort", abortHandler, { once: true });
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
return socket;
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
|
-
`;
|
|
469
|
-
|
|
470
|
-
const UTILITY_FUNCTIONS_TEMPLATE_PART3 = `
|
|
471
|
-
function normalizeAuthBasePath(basePath?: string | null): string | undefined {
|
|
472
|
-
if (!basePath) return undefined;
|
|
473
|
-
const prefixed = basePath.startsWith("/") ? basePath : \`/\${basePath}\`;
|
|
474
|
-
return prefixed.replace(/\\/+$/, "") || "/auth";
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
function normalizeBaseUrl(baseUrl?: string): string {
|
|
478
|
-
if (!baseUrl) {
|
|
479
|
-
return "";
|
|
480
|
-
}
|
|
481
|
-
return baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
function normalizeWsBaseUrl(baseUrl?: string): string | undefined {
|
|
485
|
-
if (!baseUrl) return undefined;
|
|
486
|
-
if (baseUrl.startsWith("ws://") || baseUrl.startsWith("wss://")) {
|
|
487
|
-
return baseUrl.replace(/\\/+$/, "");
|
|
488
|
-
}
|
|
489
|
-
if (baseUrl.startsWith("https://")) return baseUrl.replace(/^https/, "wss").replace(/\\/$/, "");
|
|
490
|
-
if (baseUrl.startsWith("http://")) return baseUrl.replace(/^http/, "ws").replace(/\\/$/, "");
|
|
491
|
-
return baseUrl.replace(/\\/$/, "");
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
function buildUrl(baseUrl: string, path: string): string {
|
|
495
|
-
if (!baseUrl) {
|
|
496
|
-
return path;
|
|
497
|
-
}
|
|
498
|
-
const normalizedPath = path.startsWith("/") ? path : "/" + path;
|
|
499
|
-
return baseUrl + normalizedPath;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
function buildRealtimeUrl(
|
|
503
|
-
baseUrl: string,
|
|
504
|
-
path: string,
|
|
505
|
-
params: URLSearchParams
|
|
506
|
-
): string {
|
|
507
|
-
const normalizedBase = baseUrl.replace(/\\/+$/, "");
|
|
508
|
-
const normalizedPath = path.startsWith("/") ? path : "/" + path;
|
|
509
|
-
const query = params.toString();
|
|
510
|
-
return query ? normalizedBase + normalizedPath + "?" + query : normalizedBase + normalizedPath;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
function buildQueryUrl(
|
|
514
|
-
baseUrl: string,
|
|
515
|
-
path: string,
|
|
516
|
-
params: Record<string, unknown> | undefined
|
|
517
|
-
): string {
|
|
518
|
-
const url = buildUrl(baseUrl, path);
|
|
519
|
-
const query = serializeQueryParams(params);
|
|
520
|
-
return query ? url + "?" + query : url;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
function serializeQueryParams(
|
|
524
|
-
params: Record<string, unknown> | undefined
|
|
525
|
-
): string {
|
|
526
|
-
if (!params) {
|
|
527
|
-
return "";
|
|
528
|
-
}
|
|
529
|
-
const searchParams = new URLSearchParams();
|
|
530
|
-
for (const [key, value] of Object.entries(params)) {
|
|
531
|
-
if (value === undefined || value === null) {
|
|
532
|
-
continue;
|
|
533
|
-
}
|
|
534
|
-
if (Array.isArray(value)) {
|
|
535
|
-
for (const entry of value) {
|
|
536
|
-
searchParams.append(key, serializeQueryValue(entry));
|
|
537
|
-
}
|
|
538
|
-
continue;
|
|
539
|
-
}
|
|
540
|
-
searchParams.append(key, serializeQueryValue(value));
|
|
541
|
-
}
|
|
542
|
-
return searchParams.toString();
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
function serializeQueryValue(value: unknown): string {
|
|
546
|
-
if (value instanceof Date) {
|
|
547
|
-
return value.toISOString();
|
|
548
|
-
}
|
|
549
|
-
if (typeof value === "object") {
|
|
550
|
-
return JSON.stringify(value);
|
|
551
|
-
}
|
|
552
|
-
return String(value);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
function normalizeStorageBasePath(basePath?: string): string {
|
|
556
|
-
if (!basePath) return "/storage";
|
|
557
|
-
const prefixed = basePath.startsWith("/") ? basePath : \`/\${basePath}\`;
|
|
558
|
-
const trimmed = prefixed.replace(/\\/+$/, "");
|
|
559
|
-
return trimmed || "/storage";
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
function buildStoragePath(basePath: string, path: string): string {
|
|
563
|
-
const trimmedBase = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
|
|
564
|
-
const normalizedPath = path.startsWith("/") ? path : \`/\${path}\`;
|
|
565
|
-
if (
|
|
566
|
-
normalizedPath === trimmedBase ||
|
|
567
|
-
normalizedPath.startsWith(\`\${trimmedBase}/\`)
|
|
568
|
-
) {
|
|
569
|
-
return normalizedPath;
|
|
570
|
-
}
|
|
571
|
-
return \`\${trimmedBase}\${normalizedPath}\`;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
function createStorageManagerClient(
|
|
575
|
-
baseUrl: string,
|
|
576
|
-
request: RequestExecutor,
|
|
577
|
-
options?: StorageManagerOptions
|
|
578
|
-
): StorageManagerClient {
|
|
579
|
-
const basePath = normalizeStorageBasePath(options?.basePath);
|
|
580
|
-
const toUrl = (path: string) => buildUrl(baseUrl, buildStoragePath(basePath, path));
|
|
581
|
-
|
|
582
|
-
const sendJson = async <T>(
|
|
583
|
-
method: string,
|
|
584
|
-
path: string,
|
|
585
|
-
init?: RequestInit
|
|
586
|
-
): Promise<T> => {
|
|
587
|
-
const response = await request(toUrl(path), { ...(init ?? {}), method });
|
|
588
|
-
return parseJson<T>(response);
|
|
589
|
-
};
|
|
590
|
-
|
|
591
|
-
return {
|
|
592
|
-
url: toUrl,
|
|
593
|
-
get: (path, init) => request(toUrl(path), { ...(init ?? {}), method: "GET" }),
|
|
594
|
-
head: (path, init) => request(toUrl(path), { ...(init ?? {}), method: "HEAD" }),
|
|
595
|
-
put: (path, body, init) =>
|
|
596
|
-
sendJson<StoragePutResult>("PUT", path, { ...(init ?? {}), body }),
|
|
597
|
-
post: (path, body, init) =>
|
|
598
|
-
sendJson<StoragePutResult>("POST", path, { ...(init ?? {}), body }),
|
|
599
|
-
delete: (path, init) =>
|
|
600
|
-
sendJson<StorageDeleteResult>("DELETE", path, { ...(init ?? {}) }),
|
|
601
|
-
};
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
605
|
-
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
function parseRealtimeMessage<TResult>(value: unknown): RealtimeMessage<TResult> {
|
|
609
|
-
if (typeof value === "string") {
|
|
610
|
-
try {
|
|
611
|
-
return JSON.parse(value) as RealtimeMessage<TResult>;
|
|
612
|
-
} catch {
|
|
613
|
-
return { type: "message", raw: value } as any;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
return value as RealtimeMessage<TResult>;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
function ensureJsonHeaders(headers?: HeadersInit): HeadersInit {
|
|
620
|
-
if (!headers) {
|
|
621
|
-
return { "content-type": "application/json" };
|
|
622
|
-
}
|
|
623
|
-
if (typeof Headers !== "undefined" && headers instanceof Headers) {
|
|
624
|
-
const next = new Headers(headers);
|
|
625
|
-
if (!next.has("content-type")) {
|
|
626
|
-
next.set("content-type", "application/json");
|
|
627
|
-
}
|
|
628
|
-
return next;
|
|
629
|
-
}
|
|
630
|
-
if (Array.isArray(headers)) {
|
|
631
|
-
const entries = headers.slice();
|
|
632
|
-
const hasContentType = entries.some(
|
|
633
|
-
([key]) => key.toLowerCase() === "content-type"
|
|
634
|
-
);
|
|
635
|
-
if (!hasContentType) {
|
|
636
|
-
entries.push(["content-type", "application/json"]);
|
|
637
|
-
}
|
|
638
|
-
return entries;
|
|
639
|
-
}
|
|
640
|
-
if (typeof headers === "object") {
|
|
641
|
-
const record = { ...(headers as Record<string, string>) };
|
|
642
|
-
if (!hasHeader(record, "content-type")) {
|
|
643
|
-
record["content-type"] = "application/json";
|
|
644
|
-
}
|
|
645
|
-
return record;
|
|
646
|
-
}
|
|
647
|
-
return { "content-type": "application/json" };
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
function hasHeader(record: Record<string, string>, name: string): boolean {
|
|
651
|
-
const needle = name.toLowerCase();
|
|
652
|
-
return Object.keys(record).some((key) => key.toLowerCase() === needle);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
async function parseJson<TResult>(response: Response): Promise<TResult> {
|
|
656
|
-
if (!response.ok) {
|
|
657
|
-
throw new Error("Request failed with status " + response.status);
|
|
658
|
-
}
|
|
659
|
-
return (await response.json()) as TResult;
|
|
660
|
-
}
|
|
661
|
-
`;
|
|
662
|
-
|
|
663
|
-
const UTILITY_FUNCTIONS_TEMPLATE =
|
|
664
|
-
UTILITY_FUNCTIONS_TEMPLATE_PART1 +
|
|
665
|
-
UTILITY_FUNCTIONS_TEMPLATE_PART2 +
|
|
666
|
-
UTILITY_FUNCTIONS_TEMPLATE_PART3;
|
|
667
|
-
|
|
668
|
-
function generateImports(params: {
|
|
669
|
-
handlers: DiscoveredHandler[];
|
|
670
|
-
outDirAbs: string;
|
|
671
|
-
}): { importLines: string[]; importAliasBySource: Map<string, string> } {
|
|
672
|
-
const handlerImportsGrouped = groupBy(
|
|
673
|
-
params.handlers,
|
|
674
|
-
(h) => h.sourceFileAbs,
|
|
675
|
-
);
|
|
676
|
-
|
|
677
|
-
const importLines: string[] = [];
|
|
678
|
-
const importAliasBySource = new Map<string, string>();
|
|
679
|
-
for (const [fileAbs, list] of Array.from(handlerImportsGrouped.entries())) {
|
|
680
|
-
const alias = `__appflare_${pascalCase(list[0].routePath)}`;
|
|
681
|
-
importAliasBySource.set(fileAbs, alias);
|
|
682
|
-
const importPath = toImportPathFromGeneratedSrc(params.outDirAbs, fileAbs);
|
|
683
|
-
importLines.push(
|
|
684
|
-
`import * as ${alias} from ${JSON.stringify(importPath)};`,
|
|
685
|
-
);
|
|
686
|
-
}
|
|
687
|
-
return { importLines, importAliasBySource };
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
function generateGroupedHandlers(handlers: DiscoveredHandler[]): {
|
|
691
|
-
queriesByFile: Map<string, DiscoveredHandler[]>;
|
|
692
|
-
mutationsByFile: Map<string, DiscoveredHandler[]>;
|
|
693
|
-
internalQueriesByFile: Map<string, DiscoveredHandler[]>;
|
|
694
|
-
internalMutationsByFile: Map<string, DiscoveredHandler[]>;
|
|
695
|
-
} {
|
|
696
|
-
const queries = handlers.filter((h) => h.kind === "query");
|
|
697
|
-
const mutations = handlers.filter((h) => h.kind === "mutation");
|
|
698
|
-
const internalQueries = handlers.filter((h) => h.kind === "internalQuery");
|
|
699
|
-
const internalMutations = handlers.filter(
|
|
700
|
-
(h) => h.kind === "internalMutation",
|
|
701
|
-
);
|
|
702
|
-
|
|
703
|
-
const queriesByFile = groupBy(queries, (h) => h.routePath);
|
|
704
|
-
const mutationsByFile = groupBy(mutations, (h) => h.routePath);
|
|
705
|
-
const internalQueriesByFile = groupBy(internalQueries, (h) => h.routePath);
|
|
706
|
-
const internalMutationsByFile = groupBy(
|
|
707
|
-
internalMutations,
|
|
708
|
-
(h) => h.routePath,
|
|
709
|
-
);
|
|
710
|
-
|
|
711
|
-
return {
|
|
712
|
-
queriesByFile,
|
|
713
|
-
mutationsByFile,
|
|
714
|
-
internalQueriesByFile,
|
|
715
|
-
internalMutationsByFile,
|
|
716
|
-
};
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
function generateTypeDefs(
|
|
720
|
-
queriesByFile: Map<string, DiscoveredHandler[]>,
|
|
721
|
-
mutationsByFile: Map<string, DiscoveredHandler[]>,
|
|
722
|
-
internalQueriesByFile: Map<string, DiscoveredHandler[]>,
|
|
723
|
-
internalMutationsByFile: Map<string, DiscoveredHandler[]>,
|
|
724
|
-
importAliasBySource: Map<string, string>,
|
|
725
|
-
): {
|
|
726
|
-
queriesTypeDef: string;
|
|
727
|
-
mutationsTypeDef: string;
|
|
728
|
-
internalQueriesTypeDef: string;
|
|
729
|
-
internalMutationsTypeDef: string;
|
|
730
|
-
} {
|
|
731
|
-
const queriesTypeLines = generateQueriesTypeLines(queriesByFile);
|
|
732
|
-
const mutationsTypeLines = generateMutationsTypeLines(mutationsByFile);
|
|
733
|
-
const internalQueriesTypeLines = generateInternalTypeLines(
|
|
734
|
-
internalQueriesByFile,
|
|
735
|
-
importAliasBySource,
|
|
736
|
-
);
|
|
737
|
-
const internalMutationsTypeLines = generateInternalTypeLines(
|
|
738
|
-
internalMutationsByFile,
|
|
739
|
-
importAliasBySource,
|
|
740
|
-
);
|
|
741
|
-
|
|
742
|
-
const queriesTypeDef =
|
|
743
|
-
queriesByFile.size === 0 ? "{}" : `{\n${queriesTypeLines}\n}`;
|
|
744
|
-
const mutationsTypeDef =
|
|
745
|
-
mutationsByFile.size === 0 ? "{}" : `{\n${mutationsTypeLines}\n}`;
|
|
746
|
-
const internalQueriesTypeDef =
|
|
747
|
-
internalQueriesByFile.size === 0
|
|
748
|
-
? "{}"
|
|
749
|
-
: `{\n${internalQueriesTypeLines}\n}`;
|
|
750
|
-
const internalMutationsTypeDef =
|
|
751
|
-
internalMutationsByFile.size === 0
|
|
752
|
-
? "{}"
|
|
753
|
-
: `{\n${internalMutationsTypeLines}\n}`;
|
|
754
|
-
|
|
755
|
-
return {
|
|
756
|
-
queriesTypeDef,
|
|
757
|
-
mutationsTypeDef,
|
|
758
|
-
internalQueriesTypeDef,
|
|
759
|
-
internalMutationsTypeDef,
|
|
760
|
-
};
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
function generateClientInits(
|
|
764
|
-
queriesByFile: Map<string, DiscoveredHandler[]>,
|
|
765
|
-
mutationsByFile: Map<string, DiscoveredHandler[]>,
|
|
766
|
-
importAliasBySource: Map<string, string>,
|
|
767
|
-
): { queriesInit: string; mutationsInit: string } {
|
|
768
|
-
const queriesClientLines = generateQueriesClientLines(
|
|
769
|
-
queriesByFile,
|
|
770
|
-
importAliasBySource,
|
|
771
|
-
);
|
|
772
|
-
const mutationsClientLines = generateMutationsClientLines(
|
|
773
|
-
mutationsByFile,
|
|
774
|
-
importAliasBySource,
|
|
775
|
-
);
|
|
776
|
-
|
|
777
|
-
const queriesInit =
|
|
778
|
-
queriesByFile.size === 0 ? "{}" : `{\n${queriesClientLines}\n\t}`;
|
|
779
|
-
const mutationsInit =
|
|
780
|
-
mutationsByFile.size === 0 ? "{}" : `{\n${mutationsClientLines}\n\t}`;
|
|
781
|
-
|
|
782
|
-
return { queriesInit, mutationsInit };
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
function generateInternalInit(
|
|
786
|
-
internalByFile: Map<string, DiscoveredHandler[]>,
|
|
787
|
-
importAliasBySource: Map<string, string>,
|
|
788
|
-
): string {
|
|
789
|
-
if (internalByFile.size === 0) return "{}";
|
|
790
|
-
const lines: string[] = [];
|
|
791
|
-
for (const [fileName, list] of Array.from(internalByFile.entries()).sort(
|
|
792
|
-
(a, b) => a[0].localeCompare(b[0]),
|
|
793
|
-
)) {
|
|
794
|
-
const fileKey = renderObjectKey(fileName);
|
|
795
|
-
const inner = list
|
|
796
|
-
.slice()
|
|
797
|
-
.sort((a, b) => a.name.localeCompare(b.name))
|
|
798
|
-
.map((h) => {
|
|
799
|
-
const alias = importAliasBySource.get(h.sourceFileAbs)!;
|
|
800
|
-
return `\t\t${h.name}: ${alias}.${h.name},`;
|
|
801
|
-
})
|
|
802
|
-
.join("\n");
|
|
803
|
-
lines.push(`\t${fileKey}: {\n${inner}\n\t}`);
|
|
804
|
-
}
|
|
805
|
-
return `{
|
|
806
|
-
${lines.join("\n")}
|
|
807
|
-
}`;
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
function generateInternalMeta(
|
|
811
|
-
internalByFile: Map<string, DiscoveredHandler[]>,
|
|
812
|
-
importAliasBySource: Map<string, string>,
|
|
813
|
-
): string {
|
|
814
|
-
if (internalByFile.size === 0) return "";
|
|
815
|
-
const lines: string[] = [];
|
|
816
|
-
for (const [fileName, list] of Array.from(internalByFile.entries()).sort(
|
|
817
|
-
(a, b) => a[0].localeCompare(b[0]),
|
|
818
|
-
)) {
|
|
819
|
-
for (const h of list.slice().sort((a, b) => a.name.localeCompare(b.name))) {
|
|
820
|
-
const alias = importAliasBySource.get(h.sourceFileAbs)!;
|
|
821
|
-
lines.push(
|
|
822
|
-
`{ file: ${JSON.stringify(fileName)}, name: ${JSON.stringify(
|
|
823
|
-
h.name,
|
|
824
|
-
)}, handler: ${alias}.${h.name} },`,
|
|
825
|
-
);
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
return lines.join("\n");
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
export function generateApiClient(params: {
|
|
832
|
-
handlers: DiscoveredHandler[];
|
|
833
|
-
outDirAbs: string;
|
|
834
|
-
authBasePath?: string;
|
|
835
|
-
authEnabled?: boolean;
|
|
836
|
-
configPathAbs?: string;
|
|
837
|
-
}): { apiTs: string; clientConfigTs: string | null } {
|
|
838
|
-
const { importLines, importAliasBySource } = generateImports(params);
|
|
839
|
-
const {
|
|
840
|
-
queriesByFile,
|
|
841
|
-
mutationsByFile,
|
|
842
|
-
internalQueriesByFile,
|
|
843
|
-
internalMutationsByFile,
|
|
844
|
-
} = generateGroupedHandlers(params.handlers);
|
|
845
|
-
const {
|
|
846
|
-
queriesTypeDef,
|
|
847
|
-
mutationsTypeDef,
|
|
848
|
-
internalQueriesTypeDef,
|
|
849
|
-
internalMutationsTypeDef,
|
|
850
|
-
} = generateTypeDefs(
|
|
851
|
-
queriesByFile,
|
|
852
|
-
mutationsByFile,
|
|
853
|
-
internalQueriesByFile,
|
|
854
|
-
internalMutationsByFile,
|
|
855
|
-
importAliasBySource,
|
|
856
|
-
);
|
|
857
|
-
const { queriesInit, mutationsInit } = generateClientInits(
|
|
858
|
-
queriesByFile,
|
|
859
|
-
mutationsByFile,
|
|
860
|
-
importAliasBySource,
|
|
861
|
-
);
|
|
862
|
-
const internalHandlersCombined = new Map<string, DiscoveredHandler[]>();
|
|
863
|
-
for (const [file, list] of Array.from(internalQueriesByFile.entries())) {
|
|
864
|
-
internalHandlersCombined.set(file, list.slice());
|
|
865
|
-
}
|
|
866
|
-
for (const [file, list] of Array.from(internalMutationsByFile.entries())) {
|
|
867
|
-
const existing = internalHandlersCombined.get(file) ?? [];
|
|
868
|
-
internalHandlersCombined.set(file, existing.concat(list));
|
|
869
|
-
}
|
|
870
|
-
const internalInit = generateInternalInit(
|
|
871
|
-
internalHandlersCombined,
|
|
872
|
-
importAliasBySource,
|
|
873
|
-
);
|
|
874
|
-
const internalQueriesMeta = generateInternalMeta(
|
|
875
|
-
internalQueriesByFile,
|
|
876
|
-
importAliasBySource,
|
|
877
|
-
);
|
|
878
|
-
const internalMutationsMeta = generateInternalMeta(
|
|
879
|
-
internalMutationsByFile,
|
|
880
|
-
importAliasBySource,
|
|
881
|
-
);
|
|
882
|
-
|
|
883
|
-
const authBasePathLiteral = JSON.stringify(params.authBasePath ?? "/auth");
|
|
884
|
-
|
|
885
|
-
// Generate config import and auth client options handling
|
|
886
|
-
let configImport = "";
|
|
887
|
-
let authClientTypeDefinitions =
|
|
888
|
-
"type AppflareAuthClient = ReturnType<typeof createAuthClient>;";
|
|
889
|
-
let authClientInit = ` const auth = createAuthClient({
|
|
890
|
-
...(options.auth ?? {}),
|
|
891
|
-
baseURL:
|
|
892
|
-
(options.auth as any)?.baseURL ??
|
|
893
|
-
buildUrl(baseUrl, authBasePath),
|
|
894
|
-
});`;
|
|
895
|
-
|
|
896
|
-
const clientConfigTs =
|
|
897
|
-
params.authEnabled && params.configPathAbs
|
|
898
|
-
? extractClientConfig(params.configPathAbs)
|
|
899
|
-
: null;
|
|
900
|
-
|
|
901
|
-
if (params.authEnabled && params.configPathAbs) {
|
|
902
|
-
if (clientConfigTs) {
|
|
903
|
-
configImport = `\nimport { clientOptions } from "./client.config";`;
|
|
904
|
-
|
|
905
|
-
// Use a factory function pattern to properly infer the client type from clientOptions
|
|
906
|
-
authClientTypeDefinitions = `const __getAppflareAuthClientOptions = () => (clientOptions ?? {}) as const;
|
|
907
|
-
type AppflareAuthClientOptions = ReturnType<typeof __getAppflareAuthClientOptions>;
|
|
908
|
-
const __createTypedAuthClient = (baseURL: string) => createAuthClient({
|
|
909
|
-
...__getAppflareAuthClientOptions(),
|
|
910
|
-
baseURL,
|
|
911
|
-
});
|
|
912
|
-
type AppflareAuthClient = ReturnType<typeof __createTypedAuthClient>;`;
|
|
913
|
-
|
|
914
|
-
authClientInit = ` const auth = createAuthClient({
|
|
915
|
-
...__getAppflareAuthClientOptions(),
|
|
916
|
-
...(options.auth ?? {}),
|
|
917
|
-
baseURL:
|
|
918
|
-
(options.auth as any)?.baseURL ??
|
|
919
|
-
buildUrl(baseUrl, authBasePath),
|
|
920
|
-
});`;
|
|
921
|
-
} else {
|
|
922
|
-
const configImportPath = toImportPathFromGeneratedSrc(
|
|
923
|
-
params.outDirAbs,
|
|
924
|
-
params.configPathAbs,
|
|
925
|
-
);
|
|
926
|
-
configImport = `\nimport __appflareConfig from ${JSON.stringify(configImportPath)};`;
|
|
927
|
-
|
|
928
|
-
// Use a factory function pattern to properly infer the client type from clientOptions
|
|
929
|
-
authClientTypeDefinitions = `const __getAppflareAuthClientOptions = () => (__appflareConfig.auth?.clientOptions ?? {}) as const;
|
|
930
|
-
type AppflareAuthClientOptions = ReturnType<typeof __getAppflareAuthClientOptions>;
|
|
931
|
-
const __createTypedAuthClient = (baseURL: string) => createAuthClient({
|
|
932
|
-
...__getAppflareAuthClientOptions(),
|
|
933
|
-
baseURL,
|
|
934
|
-
});
|
|
935
|
-
type AppflareAuthClient = ReturnType<typeof __createTypedAuthClient>;`;
|
|
936
|
-
|
|
937
|
-
authClientInit = ` const auth = createAuthClient({
|
|
938
|
-
...__getAppflareAuthClientOptions(),
|
|
939
|
-
...(options.auth ?? {}),
|
|
940
|
-
baseURL:
|
|
941
|
-
(options.auth as any)?.baseURL ??
|
|
942
|
-
buildUrl(baseUrl, authBasePath),
|
|
943
|
-
});`;
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
const typeBlocks = generateTypeBlocks(params.handlers, importAliasBySource);
|
|
948
|
-
|
|
949
|
-
return {
|
|
950
|
-
apiTs:
|
|
951
|
-
HEADER_TEMPLATE.replace("{{configImport}}", configImport) +
|
|
952
|
-
importLines.join("\n") +
|
|
953
|
-
TYPE_DEFINITIONS_TEMPLATE +
|
|
954
|
-
typeBlocks.join("\n\n") +
|
|
955
|
-
INTERNAL_TEMPLATE.replace(
|
|
956
|
-
"{{internalQueriesTypeDef}}",
|
|
957
|
-
internalQueriesTypeDef,
|
|
958
|
-
)
|
|
959
|
-
.replace("{{internalMutationsTypeDef}}", internalMutationsTypeDef)
|
|
960
|
-
.replace("{{internalInit}}", internalInit)
|
|
961
|
-
.replace("{{internalQueriesMeta}}", internalQueriesMeta)
|
|
962
|
-
.replace("{{internalMutationsMeta}}", internalMutationsMeta) +
|
|
963
|
-
CLIENT_TYPES_TEMPLATE.replace("{{queriesTypeDef}}", queriesTypeDef)
|
|
964
|
-
.replace("{{mutationsTypeDef}}", mutationsTypeDef)
|
|
965
|
-
.replace("{{queriesInit}}", queriesInit)
|
|
966
|
-
.replace("{{mutationsInit}}", mutationsInit)
|
|
967
|
-
.replace("{{authBasePath}}", authBasePathLiteral)
|
|
968
|
-
.replace("{{authClientTypeDefinitions}}", authClientTypeDefinitions)
|
|
969
|
-
.replace("{{authClientInit}}", authClientInit) +
|
|
970
|
-
UTILITY_FUNCTIONS_TEMPLATE,
|
|
971
|
-
clientConfigTs,
|
|
972
|
-
};
|
|
973
|
-
}
|