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
|
@@ -0,0 +1,960 @@
|
|
|
1
|
+
export function generateTypes(): string {
|
|
2
|
+
return `
|
|
3
|
+
import { betterAuth } from "better-auth";
|
|
4
|
+
import { auth } from "./auth.config";
|
|
5
|
+
import {
|
|
6
|
+
and,
|
|
7
|
+
eq,
|
|
8
|
+
ne,
|
|
9
|
+
inArray,
|
|
10
|
+
notInArray,
|
|
11
|
+
gt,
|
|
12
|
+
gte,
|
|
13
|
+
lt,
|
|
14
|
+
lte,
|
|
15
|
+
isNull,
|
|
16
|
+
isNotNull,
|
|
17
|
+
getTableColumns,
|
|
18
|
+
sql,
|
|
19
|
+
type InferInsertModel,
|
|
20
|
+
type InferSelectModel,
|
|
21
|
+
type SQL,
|
|
22
|
+
} from "drizzle-orm";
|
|
23
|
+
|
|
24
|
+
export type WorkerEnv = {
|
|
25
|
+
Bindings: Record<string, unknown>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type RegisterHandlersOptions = {
|
|
29
|
+
databaseBinding: string;
|
|
30
|
+
kvBinding?: string;
|
|
31
|
+
schedulerBinding?: string;
|
|
32
|
+
r2Binding?: string;
|
|
33
|
+
realtimeBinding?: string;
|
|
34
|
+
realtimeObjectName?: string;
|
|
35
|
+
realtimeSubscribePath?: string;
|
|
36
|
+
realtimeWebsocketPath?: string;
|
|
37
|
+
realtimeProtocol?: string;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type StorageMethod =
|
|
41
|
+
| "download"
|
|
42
|
+
| "get"
|
|
43
|
+
| "delete"
|
|
44
|
+
| "list"
|
|
45
|
+
| "put"
|
|
46
|
+
| "preview";
|
|
47
|
+
|
|
48
|
+
export type StorageAuthorizationArgs = {
|
|
49
|
+
path: string;
|
|
50
|
+
method: StorageMethod;
|
|
51
|
+
headers?: Headers;
|
|
52
|
+
query?: Record<string, string>;
|
|
53
|
+
contentType?: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type StorageHandler = (
|
|
57
|
+
ctx: AppflareContext,
|
|
58
|
+
args: StorageAuthorizationArgs,
|
|
59
|
+
) => boolean | Promise<boolean>;
|
|
60
|
+
|
|
61
|
+
export type RegisteredStorageHandler = {
|
|
62
|
+
kind: "storage-manager";
|
|
63
|
+
definition: {
|
|
64
|
+
handler: StorageHandler;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export function storageManager(definition: {
|
|
69
|
+
handler: StorageHandler;
|
|
70
|
+
}): RegisteredStorageHandler {
|
|
71
|
+
return {
|
|
72
|
+
kind: "storage-manager",
|
|
73
|
+
definition,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const storageHandlers: StorageHandler[] = [];
|
|
78
|
+
|
|
79
|
+
export function setStorageHandlers(handlers: StorageHandler[]): void {
|
|
80
|
+
storageHandlers.length = 0;
|
|
81
|
+
storageHandlers.push(...handlers);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function isStorageAllowed(
|
|
85
|
+
ctx: AppflareContext,
|
|
86
|
+
args: StorageAuthorizationArgs,
|
|
87
|
+
): Promise<boolean> {
|
|
88
|
+
if (storageHandlers.length === 0) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const handler of storageHandlers) {
|
|
93
|
+
const allowed = await handler(ctx, args);
|
|
94
|
+
if (allowed) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const mergedSchema = {
|
|
103
|
+
...authSchema,
|
|
104
|
+
...schema,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export const createDb = (database: D1Database) =>
|
|
108
|
+
drizzle(database, { schema: mergedSchema });
|
|
109
|
+
|
|
110
|
+
export type AppflareDb = ReturnType<typeof createDb>;
|
|
111
|
+
|
|
112
|
+
type QueryTables = AppflareDb["query"];
|
|
113
|
+
type TableName = Extract<keyof QueryTables, string>;
|
|
114
|
+
type TableQuery<TName extends TableName> = QueryTables[TName];
|
|
115
|
+
type TableFindManyArgs<TName extends TableName> = TableQuery<TName> extends {
|
|
116
|
+
findMany: (args?: infer TArgs) => Promise<unknown>;
|
|
117
|
+
}
|
|
118
|
+
? TArgs
|
|
119
|
+
: never;
|
|
120
|
+
type TableFindManyResult<TName extends TableName> = TableQuery<TName> extends {
|
|
121
|
+
findMany: (args?: unknown) => infer TResult;
|
|
122
|
+
}
|
|
123
|
+
? TResult
|
|
124
|
+
: Promise<Array<TableModel<TName>>>;
|
|
125
|
+
type TableFindFirstArgs<TName extends TableName> = TableQuery<TName> extends {
|
|
126
|
+
findFirst: (args?: infer TArgs) => Promise<unknown>;
|
|
127
|
+
}
|
|
128
|
+
? TArgs
|
|
129
|
+
: never;
|
|
130
|
+
type TableFindFirstResult<TName extends TableName> = TableQuery<TName> extends {
|
|
131
|
+
findFirst: (args?: unknown) => infer TResult;
|
|
132
|
+
}
|
|
133
|
+
? TResult
|
|
134
|
+
: Promise<TableModel<TName> | null>;
|
|
135
|
+
type TableModel<TName extends TableName> = InferSelectModel<
|
|
136
|
+
(typeof mergedSchema)[TName]
|
|
137
|
+
>;
|
|
138
|
+
type TableInsertModel<TName extends TableName> = InferInsertModel<
|
|
139
|
+
(typeof mergedSchema)[TName]
|
|
140
|
+
>;
|
|
141
|
+
|
|
142
|
+
type Primitive = string | number | boolean | Date;
|
|
143
|
+
type NonNil<T> = Exclude<T, null | undefined>;
|
|
144
|
+
type Friendly<T> = T extends Date ? Date | number : T;
|
|
145
|
+
type Comparable<T> = Friendly<Extract<NonNil<T>, Primitive>>;
|
|
146
|
+
type RegexOperand<T> = T extends string ? string : never;
|
|
147
|
+
|
|
148
|
+
type GeoPoint = {
|
|
149
|
+
latitude: number;
|
|
150
|
+
longitude: number;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
type GeoCoordinates = {
|
|
154
|
+
type?: "Point";
|
|
155
|
+
coordinates: [number, number] | readonly [number, number];
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
type GeoWithinOperand = {
|
|
159
|
+
$geometry: GeoPoint | GeoCoordinates;
|
|
160
|
+
latitudeField?: string;
|
|
161
|
+
longitudeField?: string;
|
|
162
|
+
$minDistance?: number;
|
|
163
|
+
$maxDistance?: number;
|
|
164
|
+
gt?: number;
|
|
165
|
+
gte?: number;
|
|
166
|
+
lt?: number;
|
|
167
|
+
lte?: number;
|
|
168
|
+
$gt?: number;
|
|
169
|
+
$gte?: number;
|
|
170
|
+
$lt?: number;
|
|
171
|
+
$lte?: number;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
type FieldOperators<T> = {
|
|
175
|
+
eq?: Friendly<NonNil<T>>;
|
|
176
|
+
$eq?: Friendly<NonNil<T>>;
|
|
177
|
+
ne?: Friendly<NonNil<T>>;
|
|
178
|
+
$ne?: Friendly<NonNil<T>>;
|
|
179
|
+
in?: ReadonlyArray<Friendly<NonNil<T>>>;
|
|
180
|
+
$in?: ReadonlyArray<Friendly<NonNil<T>>>;
|
|
181
|
+
nin?: ReadonlyArray<Friendly<NonNil<T>>>;
|
|
182
|
+
$nin?: ReadonlyArray<Friendly<NonNil<T>>>;
|
|
183
|
+
gt?: Comparable<T>;
|
|
184
|
+
$gt?: Comparable<T>;
|
|
185
|
+
gte?: Comparable<T>;
|
|
186
|
+
$gte?: Comparable<T>;
|
|
187
|
+
lt?: Comparable<T>;
|
|
188
|
+
$lt?: Comparable<T>;
|
|
189
|
+
lte?: Comparable<T>;
|
|
190
|
+
$lte?: Comparable<T>;
|
|
191
|
+
exists?: boolean;
|
|
192
|
+
regex?: RegexOperand<T>;
|
|
193
|
+
$options?: string;
|
|
194
|
+
geoWithin?: GeoWithinOperand;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
type WhereFieldValue<T> = Friendly<NonNil<T>> | FieldOperators<T>;
|
|
198
|
+
|
|
199
|
+
export type WhereInput<TModel extends Record<string, unknown>> = {
|
|
200
|
+
[K in keyof TModel]?: WhereFieldValue<TModel[K]>;
|
|
201
|
+
} & {
|
|
202
|
+
geoWithin?: GeoWithinOperand;
|
|
203
|
+
$geoWithin?: GeoWithinOperand;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export type QueryFindManyArgs<TName extends TableName> = Omit<
|
|
207
|
+
NonNullable<TableFindManyArgs<TName>>,
|
|
208
|
+
"where"
|
|
209
|
+
> & {
|
|
210
|
+
where?: WhereInput<TableModel<TName>>;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export type QueryFindFirstArgs<TName extends TableName> = Omit<
|
|
214
|
+
NonNullable<TableFindFirstArgs<TName>>,
|
|
215
|
+
"where"
|
|
216
|
+
> & {
|
|
217
|
+
where?: WhereInput<TableModel<TName>>;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
export type QueryInsertArgs<TName extends TableName> = {
|
|
221
|
+
values: TableInsertModel<TName> | Array<TableInsertModel<TName>>;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
export type QueryUpdateArgs<TName extends TableName> = {
|
|
225
|
+
set: Partial<TableInsertModel<TName>>;
|
|
226
|
+
where?: WhereInput<TableModel<TName>>;
|
|
227
|
+
limit?: number;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
export type QueryDeleteArgs<TName extends TableName> = {
|
|
231
|
+
where?: WhereInput<TableModel<TName>>;
|
|
232
|
+
limit?: number;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
export type QueryUpsertArgs<TName extends TableName> = {
|
|
236
|
+
values: TableInsertModel<TName> | Array<TableInsertModel<TName>>;
|
|
237
|
+
target?:
|
|
238
|
+
| Extract<keyof TableInsertModel<TName>, string>
|
|
239
|
+
| Array<Extract<keyof TableInsertModel<TName>, string>>;
|
|
240
|
+
set?: Partial<TableInsertModel<TName>>;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
export type QueryTableApi<TName extends TableName> = {
|
|
244
|
+
findMany: (
|
|
245
|
+
args?: QueryFindManyArgs<TName>,
|
|
246
|
+
) => TableFindManyResult<TName>;
|
|
247
|
+
findFirst: (
|
|
248
|
+
args?: QueryFindFirstArgs<TName>,
|
|
249
|
+
) => TableFindFirstResult<TName>;
|
|
250
|
+
insert: (args: QueryInsertArgs<TName>) => Promise<Array<TableModel<TName>>>;
|
|
251
|
+
update: (args: QueryUpdateArgs<TName>) => Promise<Array<TableModel<TName>>>;
|
|
252
|
+
upsert: (args: QueryUpsertArgs<TName>) => Promise<Array<TableModel<TName>>>;
|
|
253
|
+
delete: (args?: QueryDeleteArgs<TName>) => Promise<Array<TableModel<TName>>>;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
export type AppflareQueryDb = {
|
|
257
|
+
[TName in TableName]: QueryTableApi<TName>;
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
export type DbMutationKind = "insert" | "update" | "upsert" | "delete";
|
|
261
|
+
|
|
262
|
+
export type DbMutationEvent = {
|
|
263
|
+
kind: DbMutationKind;
|
|
264
|
+
table: string;
|
|
265
|
+
args: Record<string, unknown>;
|
|
266
|
+
rows: unknown[];
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
export type QueryDbOptions = {
|
|
270
|
+
onMutation?: (event: DbMutationEvent) => void;
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
274
|
+
return typeof value === "object" && value !== null;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function readOperatorValue(record: Record<string, unknown>, key: string): unknown {
|
|
278
|
+
if (record[key] !== undefined) {
|
|
279
|
+
return record[key];
|
|
280
|
+
}
|
|
281
|
+
const prefixed = "$" + key;
|
|
282
|
+
return record[prefixed];
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function normalizeGeoPoint(
|
|
286
|
+
value: GeoPoint | GeoCoordinates,
|
|
287
|
+
): { latitude: number; longitude: number } | null {
|
|
288
|
+
if ((value as GeoPoint).latitude !== undefined) {
|
|
289
|
+
const point = value as GeoPoint;
|
|
290
|
+
if (
|
|
291
|
+
typeof point.latitude === "number" &&
|
|
292
|
+
typeof point.longitude === "number"
|
|
293
|
+
) {
|
|
294
|
+
return {
|
|
295
|
+
latitude: point.latitude,
|
|
296
|
+
longitude: point.longitude,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const coordinates = (value as GeoCoordinates).coordinates;
|
|
303
|
+
if (!Array.isArray(coordinates) || coordinates.length < 2) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const [longitude, latitude] = coordinates;
|
|
308
|
+
if (typeof latitude !== "number" || typeof longitude !== "number") {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
latitude,
|
|
314
|
+
longitude,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function createGeoDistanceFilter(
|
|
319
|
+
operand: GeoWithinOperand,
|
|
320
|
+
fields: Record<string, unknown>,
|
|
321
|
+
fallbackLatitudeField?: string,
|
|
322
|
+
): SQL | undefined {
|
|
323
|
+
const geometry = normalizeGeoPoint(operand.$geometry);
|
|
324
|
+
if (!geometry) {
|
|
325
|
+
return undefined;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const latitudeField = operand.latitudeField ?? fallbackLatitudeField;
|
|
329
|
+
const longitudeField = operand.longitudeField;
|
|
330
|
+
if (!latitudeField || !longitudeField) {
|
|
331
|
+
return undefined;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const latitudeColumn = fields[latitudeField];
|
|
335
|
+
if (!latitudeColumn) {
|
|
336
|
+
return undefined;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const longitudeColumn = fields[longitudeField];
|
|
340
|
+
if (!longitudeColumn) {
|
|
341
|
+
return undefined;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const distanceExpression = sql<number>\`(
|
|
345
|
+
6371000 * acos(
|
|
346
|
+
cos(radians(\${geometry.latitude})) * cos(radians(\${latitudeColumn}))
|
|
347
|
+
* cos(radians(\${longitudeColumn}) - radians(\${geometry.longitude}))
|
|
348
|
+
+ sin(radians(\${geometry.latitude})) * sin(radians(\${latitudeColumn}))
|
|
349
|
+
)
|
|
350
|
+
)\`;
|
|
351
|
+
|
|
352
|
+
const minDistance =
|
|
353
|
+
operand.$minDistance ??
|
|
354
|
+
operand.gte ??
|
|
355
|
+
operand.$gte ??
|
|
356
|
+
operand.gt ??
|
|
357
|
+
operand.$gt;
|
|
358
|
+
const maxDistance =
|
|
359
|
+
operand.$maxDistance ??
|
|
360
|
+
operand.lte ??
|
|
361
|
+
operand.$lte ??
|
|
362
|
+
operand.lt ??
|
|
363
|
+
operand.$lt;
|
|
364
|
+
|
|
365
|
+
const filters: SQL[] = [];
|
|
366
|
+
if (typeof minDistance === "number") {
|
|
367
|
+
filters.push(sql\`\${distanceExpression} >= \${minDistance}\`);
|
|
368
|
+
}
|
|
369
|
+
if (typeof maxDistance === "number") {
|
|
370
|
+
filters.push(sql\`\${distanceExpression} <= \${maxDistance}\`);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (filters.length === 0) {
|
|
374
|
+
return undefined;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (filters.length === 1) {
|
|
378
|
+
return filters[0];
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return and(...filters);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function buildFieldFilter(
|
|
385
|
+
fieldName: string,
|
|
386
|
+
field: unknown,
|
|
387
|
+
value: unknown,
|
|
388
|
+
fields: Record<string, unknown>,
|
|
389
|
+
): SQL | undefined {
|
|
390
|
+
if (!isRecord(value) || value instanceof Date || Array.isArray(value)) {
|
|
391
|
+
return eq(field as never, value as never);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const filters: SQL[] = [];
|
|
395
|
+
const eqValue = readOperatorValue(value, "eq");
|
|
396
|
+
if (eqValue !== undefined) {
|
|
397
|
+
filters.push(eq(field as never, eqValue as never));
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const neValue = readOperatorValue(value, "ne");
|
|
401
|
+
if (neValue !== undefined) {
|
|
402
|
+
filters.push(ne(field as never, neValue as never));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const inValue = readOperatorValue(value, "in");
|
|
406
|
+
if (Array.isArray(inValue) && inValue.length > 0) {
|
|
407
|
+
filters.push(inArray(field as never, inValue as never));
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const ninValue = readOperatorValue(value, "nin");
|
|
411
|
+
if (Array.isArray(ninValue) && ninValue.length > 0) {
|
|
412
|
+
filters.push(notInArray(field as never, ninValue as never));
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const gtValue = readOperatorValue(value, "gt");
|
|
416
|
+
if (gtValue !== undefined) {
|
|
417
|
+
filters.push(gt(field as never, gtValue as never));
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const gteValue = readOperatorValue(value, "gte");
|
|
421
|
+
if (gteValue !== undefined) {
|
|
422
|
+
filters.push(gte(field as never, gteValue as never));
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const ltValue = readOperatorValue(value, "lt");
|
|
426
|
+
if (ltValue !== undefined) {
|
|
427
|
+
filters.push(lt(field as never, ltValue as never));
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const lteValue = readOperatorValue(value, "lte");
|
|
431
|
+
if (lteValue !== undefined) {
|
|
432
|
+
filters.push(lte(field as never, lteValue as never));
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (typeof value.exists === "boolean") {
|
|
436
|
+
filters.push(value.exists ? isNotNull(field as never) : isNull(field as never));
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (typeof value.regex === "string") {
|
|
440
|
+
const caseInsensitive = typeof value.$options === "string" && value.$options.includes("i");
|
|
441
|
+
const pattern = "%" + value.regex + "%";
|
|
442
|
+
if (caseInsensitive) {
|
|
443
|
+
filters.push(
|
|
444
|
+
sql\`lower(\${field as never}) like lower(\${pattern})\`,
|
|
445
|
+
);
|
|
446
|
+
} else {
|
|
447
|
+
filters.push(sql\`\${field as never} like \${pattern}\`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (isRecord(value.geoWithin)) {
|
|
452
|
+
const geoFilter = createGeoDistanceFilter(
|
|
453
|
+
value.geoWithin as GeoWithinOperand,
|
|
454
|
+
fields,
|
|
455
|
+
fieldName,
|
|
456
|
+
);
|
|
457
|
+
if (geoFilter) {
|
|
458
|
+
filters.push(geoFilter);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (filters.length === 0) {
|
|
463
|
+
return eq(field as never, value as never);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (filters.length === 1) {
|
|
467
|
+
return filters[0];
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return and(...filters);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function buildWhereFilter(
|
|
474
|
+
table: unknown,
|
|
475
|
+
where?: Record<string, unknown>,
|
|
476
|
+
): SQL | undefined {
|
|
477
|
+
if (!table || !where || Object.keys(where).length === 0) {
|
|
478
|
+
return undefined;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const fields = getTableColumns(table as never) as Record<string, unknown>;
|
|
482
|
+
const filters: SQL[] = [];
|
|
483
|
+
|
|
484
|
+
const topLevelGeoWithin = isRecord(where.geoWithin)
|
|
485
|
+
? (where.geoWithin as GeoWithinOperand)
|
|
486
|
+
: isRecord(where.$geoWithin)
|
|
487
|
+
? (where.$geoWithin as GeoWithinOperand)
|
|
488
|
+
: undefined;
|
|
489
|
+
|
|
490
|
+
if (topLevelGeoWithin) {
|
|
491
|
+
const geoFilter = createGeoDistanceFilter(topLevelGeoWithin, fields);
|
|
492
|
+
if (geoFilter) {
|
|
493
|
+
filters.push(geoFilter);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
for (const [fieldName, fieldValue] of Object.entries(where)) {
|
|
498
|
+
if (fieldName === "geoWithin" || fieldName === "$geoWithin") {
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (fieldValue === undefined) {
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const field = fields[fieldName];
|
|
507
|
+
if (!field) {
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const filter = buildFieldFilter(fieldName, field, fieldValue, fields);
|
|
512
|
+
if (filter) {
|
|
513
|
+
filters.push(filter);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (filters.length === 0) {
|
|
518
|
+
return undefined;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (filters.length === 1) {
|
|
522
|
+
return filters[0];
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return and(...filters);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function inferConflictTarget(table: unknown): string[] {
|
|
529
|
+
if (!table) {
|
|
530
|
+
return [];
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const columns = getTableColumns(table as never) as Record<string, unknown>;
|
|
534
|
+
if (columns.id) {
|
|
535
|
+
return ["id"];
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return [];
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
export function createQueryDb(
|
|
542
|
+
$db: AppflareDb,
|
|
543
|
+
options?: QueryDbOptions,
|
|
544
|
+
): AppflareQueryDb {
|
|
545
|
+
const cache = new Map<string, unknown>();
|
|
546
|
+
const onMutation = options?.onMutation;
|
|
547
|
+
const queryDb = $db.query as Record<
|
|
548
|
+
string,
|
|
549
|
+
{
|
|
550
|
+
findMany: (args?: unknown) => Promise<unknown>;
|
|
551
|
+
findFirst: (args?: unknown) => Promise<unknown>;
|
|
552
|
+
}
|
|
553
|
+
>;
|
|
554
|
+
|
|
555
|
+
return new Proxy({} as AppflareQueryDb, {
|
|
556
|
+
get: (_target, property) => {
|
|
557
|
+
const tableName = String(property);
|
|
558
|
+
if (cache.has(tableName)) {
|
|
559
|
+
return cache.get(tableName);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const queryTable = queryDb[tableName];
|
|
563
|
+
if (!queryTable) {
|
|
564
|
+
return undefined;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const table = (mergedSchema as Record<string, unknown>)[tableName];
|
|
568
|
+
|
|
569
|
+
const emitMutation = (
|
|
570
|
+
kind: DbMutationKind,
|
|
571
|
+
args: Record<string, unknown>,
|
|
572
|
+
rows: unknown[],
|
|
573
|
+
): void => {
|
|
574
|
+
onMutation?.({
|
|
575
|
+
kind,
|
|
576
|
+
table: tableName,
|
|
577
|
+
args,
|
|
578
|
+
rows,
|
|
579
|
+
});
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
const tableApi = {
|
|
583
|
+
findMany: (args?: Record<string, unknown>) => {
|
|
584
|
+
const where = isRecord(args?.where)
|
|
585
|
+
? (args?.where as Record<string, unknown>)
|
|
586
|
+
: undefined;
|
|
587
|
+
const whereFilter = buildWhereFilter(table, where);
|
|
588
|
+
if (!whereFilter) {
|
|
589
|
+
return queryTable.findMany(args);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return queryTable.findMany({
|
|
593
|
+
...(args ?? {}),
|
|
594
|
+
where: () => whereFilter,
|
|
595
|
+
});
|
|
596
|
+
},
|
|
597
|
+
findFirst: (args?: Record<string, unknown>) => {
|
|
598
|
+
const where = isRecord(args?.where)
|
|
599
|
+
? (args?.where as Record<string, unknown>)
|
|
600
|
+
: undefined;
|
|
601
|
+
const whereFilter = buildWhereFilter(table, where);
|
|
602
|
+
if (!whereFilter) {
|
|
603
|
+
return queryTable.findFirst(args);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
return queryTable.findFirst({
|
|
607
|
+
...(args ?? {}),
|
|
608
|
+
where: () => whereFilter,
|
|
609
|
+
});
|
|
610
|
+
},
|
|
611
|
+
insert: async (args: QueryInsertArgs<TableName>) => {
|
|
612
|
+
let insertQuery: any = ($db as any)
|
|
613
|
+
.insert(table as any)
|
|
614
|
+
.values(args.values as any);
|
|
615
|
+
|
|
616
|
+
if (typeof insertQuery.returning === "function") {
|
|
617
|
+
insertQuery = insertQuery.returning();
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
const rows = (await insertQuery) as Array<TableModel<TableName>>;
|
|
621
|
+
emitMutation(
|
|
622
|
+
"insert",
|
|
623
|
+
{ values: args.values as unknown as Record<string, unknown> },
|
|
624
|
+
rows,
|
|
625
|
+
);
|
|
626
|
+
return rows;
|
|
627
|
+
},
|
|
628
|
+
update: async (args: QueryUpdateArgs<TableName>) => {
|
|
629
|
+
const whereFilter = buildWhereFilter(
|
|
630
|
+
table,
|
|
631
|
+
args.where as Record<string, unknown> | undefined,
|
|
632
|
+
);
|
|
633
|
+
let updateQuery: any = ($db as any)
|
|
634
|
+
.update(table as any)
|
|
635
|
+
.set(args.set as any);
|
|
636
|
+
|
|
637
|
+
if (whereFilter) {
|
|
638
|
+
updateQuery = updateQuery.where(whereFilter);
|
|
639
|
+
}
|
|
640
|
+
if (typeof args.limit === "number" && typeof updateQuery.limit === "function") {
|
|
641
|
+
updateQuery = updateQuery.limit(args.limit);
|
|
642
|
+
}
|
|
643
|
+
if (typeof updateQuery.returning === "function") {
|
|
644
|
+
updateQuery = updateQuery.returning();
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const rows = (await updateQuery) as Array<TableModel<TableName>>;
|
|
648
|
+
emitMutation(
|
|
649
|
+
"update",
|
|
650
|
+
{
|
|
651
|
+
set: args.set as unknown as Record<string, unknown>,
|
|
652
|
+
where: (args.where ?? undefined) as unknown as Record<string, unknown>,
|
|
653
|
+
limit: args.limit,
|
|
654
|
+
},
|
|
655
|
+
rows,
|
|
656
|
+
);
|
|
657
|
+
return rows;
|
|
658
|
+
},
|
|
659
|
+
upsert: async (args: QueryUpsertArgs<TableName>) => {
|
|
660
|
+
const valuesArray = Array.isArray(args.values)
|
|
661
|
+
? args.values
|
|
662
|
+
: [args.values];
|
|
663
|
+
const values = Array.isArray(args.values)
|
|
664
|
+
? args.values
|
|
665
|
+
: args.values;
|
|
666
|
+
|
|
667
|
+
const targets = args.target
|
|
668
|
+
? Array.isArray(args.target)
|
|
669
|
+
? args.target
|
|
670
|
+
: [args.target]
|
|
671
|
+
: inferConflictTarget(table);
|
|
672
|
+
|
|
673
|
+
if (targets.length === 0) {
|
|
674
|
+
throw new Error(
|
|
675
|
+
"Unable to infer conflict target for table " + tableName + ". Provide target explicitly.",
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
const tableColumns = getTableColumns(table as never) as Record<string, unknown>;
|
|
680
|
+
const targetColumns = targets
|
|
681
|
+
.map((target) => tableColumns[target])
|
|
682
|
+
.filter(Boolean);
|
|
683
|
+
|
|
684
|
+
if (targetColumns.length === 0) {
|
|
685
|
+
throw new Error(
|
|
686
|
+
"Invalid conflict target for table " + tableName + ".",
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
const setPayload = args.set ?? valuesArray[0] ?? {};
|
|
691
|
+
let upsertQuery: any = ($db as any)
|
|
692
|
+
.insert(table as any)
|
|
693
|
+
.values(values as any)
|
|
694
|
+
.onConflictDoUpdate({
|
|
695
|
+
target: targetColumns as any,
|
|
696
|
+
set: setPayload as any,
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
if (typeof upsertQuery.returning === "function") {
|
|
700
|
+
upsertQuery = upsertQuery.returning();
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
const rows = (await upsertQuery) as Array<TableModel<TableName>>;
|
|
704
|
+
emitMutation(
|
|
705
|
+
"upsert",
|
|
706
|
+
{
|
|
707
|
+
values: args.values as unknown as Record<string, unknown>,
|
|
708
|
+
target: targets as unknown as Record<string, unknown>,
|
|
709
|
+
set: (args.set ?? undefined) as unknown as Record<string, unknown>,
|
|
710
|
+
},
|
|
711
|
+
rows,
|
|
712
|
+
);
|
|
713
|
+
return rows;
|
|
714
|
+
},
|
|
715
|
+
delete: async (args?: QueryDeleteArgs<TableName>) => {
|
|
716
|
+
const whereFilter = buildWhereFilter(
|
|
717
|
+
table,
|
|
718
|
+
args?.where as Record<string, unknown> | undefined,
|
|
719
|
+
);
|
|
720
|
+
let deleteQuery: any = ($db as any).delete(table as any);
|
|
721
|
+
|
|
722
|
+
if (whereFilter) {
|
|
723
|
+
deleteQuery = deleteQuery.where(whereFilter);
|
|
724
|
+
}
|
|
725
|
+
if (typeof args?.limit === "number" && typeof deleteQuery.limit === "function") {
|
|
726
|
+
deleteQuery = deleteQuery.limit(args.limit);
|
|
727
|
+
}
|
|
728
|
+
if (typeof deleteQuery.returning === "function") {
|
|
729
|
+
deleteQuery = deleteQuery.returning();
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
const rows = (await deleteQuery) as Array<TableModel<TableName>>;
|
|
733
|
+
emitMutation(
|
|
734
|
+
"delete",
|
|
735
|
+
{
|
|
736
|
+
where: (args?.where ?? undefined) as unknown as Record<string, unknown>,
|
|
737
|
+
limit: args?.limit,
|
|
738
|
+
},
|
|
739
|
+
rows,
|
|
740
|
+
);
|
|
741
|
+
return rows;
|
|
742
|
+
},
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
cache.set(tableName, tableApi);
|
|
746
|
+
return tableApi;
|
|
747
|
+
},
|
|
748
|
+
}) as AppflareQueryDb;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
export class AppflareHandledError extends Error {
|
|
752
|
+
public readonly status: number;
|
|
753
|
+
public readonly payload: unknown;
|
|
754
|
+
|
|
755
|
+
public constructor(status: number, payload: unknown) {
|
|
756
|
+
super(typeof payload === "object" ? "Handled error" : String(payload));
|
|
757
|
+
this.status = status;
|
|
758
|
+
this.payload = payload;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
type AuthSession = typeof auth.$Infer.Session;
|
|
765
|
+
type User = AuthSession['user']
|
|
766
|
+
type Session = AuthSession['session']
|
|
767
|
+
|
|
768
|
+
export type StoragePutArgs = {
|
|
769
|
+
path: string;
|
|
770
|
+
body: ReadableStream | ArrayBuffer | ArrayBufferView | string | Blob;
|
|
771
|
+
contentType?: string;
|
|
772
|
+
httpMetadata?: Record<string, unknown>;
|
|
773
|
+
customMetadata?: Record<string, string>;
|
|
774
|
+
};
|
|
775
|
+
|
|
776
|
+
export type StorageGetArgs = {
|
|
777
|
+
path: string;
|
|
778
|
+
method?: StorageMethod;
|
|
779
|
+
onlyIf?: unknown;
|
|
780
|
+
range?: unknown;
|
|
781
|
+
includeBody?: boolean;
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
export type StorageDeleteArgs = {
|
|
785
|
+
path: string;
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
export type StorageListArgs = {
|
|
789
|
+
prefix?: string;
|
|
790
|
+
cursor?: string;
|
|
791
|
+
limit?: number;
|
|
792
|
+
delimiter?: string;
|
|
793
|
+
include?: Array<"httpMetadata" | "customMetadata">;
|
|
794
|
+
method?: StorageMethod;
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
export type StorageSignedUrlArgs = {
|
|
798
|
+
path: string;
|
|
799
|
+
method?: "GET" | "PUT" | "DELETE";
|
|
800
|
+
expiresIn?: number;
|
|
801
|
+
contentType?: string;
|
|
802
|
+
downloadAsAttachment?: boolean;
|
|
803
|
+
fileName?: string;
|
|
804
|
+
};
|
|
805
|
+
|
|
806
|
+
export type AppflareStorage = {
|
|
807
|
+
put: (args: StoragePutArgs) => Promise<unknown>;
|
|
808
|
+
get: (args: StorageGetArgs) => Promise<unknown | null>;
|
|
809
|
+
delete: (args: StorageDeleteArgs) => Promise<void>;
|
|
810
|
+
list: (args?: StorageListArgs) => Promise<unknown>;
|
|
811
|
+
signedUrl: (args: StorageSignedUrlArgs) => Promise<string>;
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
export type AppflareContext = {
|
|
815
|
+
$db: AppflareDb;
|
|
816
|
+
db: AppflareQueryDb;
|
|
817
|
+
mutationEvents: DbMutationEvent[];
|
|
818
|
+
user: User;
|
|
819
|
+
session: Session;
|
|
820
|
+
context: Context<WorkerEnv>;
|
|
821
|
+
scheduler: Scheduler;
|
|
822
|
+
storage: AppflareStorage;
|
|
823
|
+
error: (status: number, message: string, details?: unknown) => never;
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
type InferOperationArgs<TShape extends ZodRawShape> = z.output<z.ZodObject<TShape>>;
|
|
827
|
+
|
|
828
|
+
export type SchedulerEnqueueOptions = {
|
|
829
|
+
delaySeconds?: number;
|
|
830
|
+
};
|
|
831
|
+
|
|
832
|
+
declare global {
|
|
833
|
+
interface AppflareSchedulerHandlerMap {}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
export type SchedulerTaskName = keyof AppflareSchedulerHandlerMap extends never
|
|
837
|
+
? string
|
|
838
|
+
: Extract<keyof AppflareSchedulerHandlerMap, string>;
|
|
839
|
+
|
|
840
|
+
export type SchedulerTaskPayload<TTask extends SchedulerTaskName> =
|
|
841
|
+
TTask extends keyof AppflareSchedulerHandlerMap
|
|
842
|
+
? AppflareSchedulerHandlerMap[TTask]
|
|
843
|
+
: unknown;
|
|
844
|
+
|
|
845
|
+
export type SchedulerEnqueue = {
|
|
846
|
+
<TTask extends SchedulerTaskName>(
|
|
847
|
+
task: TTask,
|
|
848
|
+
...args: undefined extends SchedulerTaskPayload<TTask>
|
|
849
|
+
? [
|
|
850
|
+
payload?: SchedulerTaskPayload<TTask>,
|
|
851
|
+
options?: SchedulerEnqueueOptions,
|
|
852
|
+
]
|
|
853
|
+
: [
|
|
854
|
+
payload: SchedulerTaskPayload<TTask>,
|
|
855
|
+
options?: SchedulerEnqueueOptions,
|
|
856
|
+
]
|
|
857
|
+
): Promise<void>;
|
|
858
|
+
};
|
|
859
|
+
|
|
860
|
+
export type Scheduler = {
|
|
861
|
+
enqueue: SchedulerEnqueue;
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
type OperationDefinition<
|
|
865
|
+
TShape extends ZodRawShape,
|
|
866
|
+
TResult,
|
|
867
|
+
> = {
|
|
868
|
+
args: TShape;
|
|
869
|
+
authRequired: boolean;
|
|
870
|
+
middleware?: (
|
|
871
|
+
ctx: AppflareContext,
|
|
872
|
+
index: InferOperationArgs<TShape>,
|
|
873
|
+
request: Request,
|
|
874
|
+
) => void | Promise<void>;
|
|
875
|
+
handler: (
|
|
876
|
+
ctx: AppflareContext,
|
|
877
|
+
args: InferOperationArgs<TShape>,
|
|
878
|
+
) => TResult | Promise<TResult>;
|
|
879
|
+
};
|
|
880
|
+
|
|
881
|
+
type OperationDefinitionInput<
|
|
882
|
+
TShape extends ZodRawShape,
|
|
883
|
+
TResult,
|
|
884
|
+
> = Omit<OperationDefinition<TShape, TResult>, "authRequired"> & {
|
|
885
|
+
authRequired?: boolean;
|
|
886
|
+
};
|
|
887
|
+
|
|
888
|
+
export type RegisteredOperation<
|
|
889
|
+
TShape extends ZodRawShape,
|
|
890
|
+
TResult,
|
|
891
|
+
> = {
|
|
892
|
+
kind: "query" | "mutation";
|
|
893
|
+
definition: OperationDefinition<TShape, TResult>;
|
|
894
|
+
};
|
|
895
|
+
|
|
896
|
+
type SchedulerDefinition<TShape extends ZodRawShape | undefined> = {
|
|
897
|
+
args?: TShape;
|
|
898
|
+
handler: (
|
|
899
|
+
ctx: AppflareContext,
|
|
900
|
+
args: TShape extends ZodRawShape ? InferOperationArgs<TShape> : undefined,
|
|
901
|
+
) => void | Promise<void>;
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
export type RegisteredScheduler<TShape extends ZodRawShape | undefined> = {
|
|
905
|
+
kind: "scheduler";
|
|
906
|
+
definition: SchedulerDefinition<TShape>;
|
|
907
|
+
};
|
|
908
|
+
|
|
909
|
+
type CronDefinition = {
|
|
910
|
+
cronTrigger: string | string[];
|
|
911
|
+
handler: (ctx: AppflareContext) => void | Promise<void>;
|
|
912
|
+
};
|
|
913
|
+
|
|
914
|
+
export type RegisteredCron = {
|
|
915
|
+
kind: "cron";
|
|
916
|
+
definition: CronDefinition;
|
|
917
|
+
};
|
|
918
|
+
|
|
919
|
+
export function query<TShape extends ZodRawShape, TResult>(
|
|
920
|
+
definition: OperationDefinitionInput<TShape, TResult>,
|
|
921
|
+
): RegisteredOperation<TShape, TResult> {
|
|
922
|
+
return {
|
|
923
|
+
kind: "query",
|
|
924
|
+
definition: {
|
|
925
|
+
authRequired: false,
|
|
926
|
+
...definition,
|
|
927
|
+
},
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
export function mutation<TShape extends ZodRawShape, TResult>(
|
|
932
|
+
definition: OperationDefinitionInput<TShape, TResult>,
|
|
933
|
+
): RegisteredOperation<TShape, TResult> {
|
|
934
|
+
return {
|
|
935
|
+
kind: "mutation",
|
|
936
|
+
definition: {
|
|
937
|
+
authRequired: false,
|
|
938
|
+
...definition,
|
|
939
|
+
},
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
export function scheduler<TShape extends ZodRawShape | undefined = undefined>(
|
|
944
|
+
definition: SchedulerDefinition<TShape>,
|
|
945
|
+
): RegisteredScheduler<TShape> {
|
|
946
|
+
return {
|
|
947
|
+
kind: "scheduler",
|
|
948
|
+
definition,
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
export function cron(definition: CronDefinition): RegisteredCron {
|
|
953
|
+
return {
|
|
954
|
+
kind: "cron",
|
|
955
|
+
definition,
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
`;
|
|
960
|
+
}
|