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
@@ -1,151 +0,0 @@
1
- import { pathToFileURL } from "node:url";
2
- import { staticTypeDefinitions } from "./schema-static-types";
3
- import { pascalCase, toImportPathFromGeneratedSrc } from "../utils/utils";
4
- import { getZodObjectShape, renderField } from "../utils/zod-utils";
5
-
6
- function generateDocParts(
7
- tableNames: string[],
8
- schema: any,
9
- ): {
10
- docInterfaces: string[];
11
- docMapEntries: string[];
12
- tableIndexEntries: string[];
13
- } {
14
- const docInterfaces: string[] = [];
15
- const docMapEntries: string[] = [];
16
- const tableIndexEntries: string[] = [];
17
-
18
- for (const tableName of tableNames) {
19
- const validator = (schema as any)[tableName];
20
- const shape = getZodObjectShape(validator);
21
- const interfaceName = pascalCase(`${tableName}Doc`);
22
-
23
- const fields = Object.entries(shape);
24
- const lines: string[] = [];
25
- lines.push(`export interface ${interfaceName} {`);
26
- lines.push(`\t_id: Id<${JSON.stringify(tableName)}>;`);
27
- lines.push(`\t_creationTime: number;`);
28
- for (const [fieldName, fieldSchema] of fields) {
29
- const rendered = renderField(fieldName, fieldSchema);
30
- lines.push(`\t${rendered}`);
31
- }
32
- lines.push(`}`);
33
-
34
- docInterfaces.push(lines.join("\n"));
35
- docMapEntries.push(`\t${tableName}: ${interfaceName};`);
36
- tableIndexEntries.push(`\t${tableName}: [],`);
37
- }
38
-
39
- return { docInterfaces, docMapEntries, tableIndexEntries };
40
- }
41
-
42
- export async function getSchemaTableNames(
43
- schemaPathAbs: string,
44
- ): Promise<string[]> {
45
- const mod = await import(pathToFileURL(schemaPathAbs).href);
46
- const schema = mod?.default ?? mod;
47
- if (!schema || typeof schema !== "object") {
48
- throw new Error(
49
- `Invalid schema export in ${schemaPathAbs} (expected default export object)`,
50
- );
51
- }
52
- return Object.keys(schema).sort();
53
- }
54
-
55
- export async function generateSchemaTypes(params: {
56
- schemaPathAbs: string;
57
- configPathAbs?: string;
58
- outDirAbs?: string;
59
- }): Promise<string> {
60
- const mod = await import(pathToFileURL(params.schemaPathAbs).href);
61
- const schema = mod?.default ?? mod;
62
- if (!schema || typeof schema !== "object") {
63
- throw new Error(
64
- `Invalid schema export in ${params.schemaPathAbs} (expected default export object)`,
65
- );
66
- }
67
-
68
- const tableNames = Object.keys(schema).sort();
69
- if (tableNames.length === 0) {
70
- throw new Error(`Schema has no tables in ${params.schemaPathAbs}`);
71
- }
72
-
73
- const { docInterfaces, docMapEntries, tableIndexEntries } = generateDocParts(
74
- tableNames,
75
- schema,
76
- );
77
-
78
- const configImportPath =
79
- params.configPathAbs && params.outDirAbs
80
- ? toImportPathFromGeneratedSrc(params.outDirAbs, params.configPathAbs)
81
- : null;
82
-
83
- const authImports = configImportPath
84
- ? `import type appflareConfig from ${JSON.stringify(configImportPath)};
85
- import type { Auth, BetterAuthOptions,User,Session } from "better-auth";
86
- `
87
- : "";
88
-
89
- const authTypeDefs = configImportPath
90
- ? `type __AppflareAuthOptions = NonNullable<
91
- typeof appflareConfig["auth"]
92
- >["options"];
93
-
94
- type __AppflareAuthInstance = __AppflareAuthOptions extends BetterAuthOptions
95
- ? Auth<__AppflareAuthOptions>
96
- : Auth<BetterAuthOptions>;
97
-
98
- type __AppflareAuthSessionResult = __AppflareAuthOptions extends BetterAuthOptions
99
- ? Awaited<
100
- ReturnType<__AppflareAuthInstance["api"]["getSession"]>
101
- >
102
- : null;
103
-
104
-
105
- export type AppflareAuthSession = Session | null
106
- export type AppflareAuthUser = User | null
107
-
108
- export type AppflareAuthContext = {
109
- session: AppflareAuthSession
110
- user: AppflareAuthUser
111
- }
112
-
113
- `
114
- : `export type AppflareAuthSession = null;
115
- export type AppflareAuthUser = null;
116
- export type AppflareAuthContext = { session: null; user: null };
117
- `;
118
-
119
- return `/* eslint-disable */
120
- /**
121
- * This file is auto-generated by appflare/db-build.ts.
122
- * Do not edit directly.
123
- */
124
- ${authImports}
125
- type SchemaValidator<TValue> = {
126
- parse: (value: unknown) => TValue;
127
- };
128
-
129
- export type AnyValidator = SchemaValidator<unknown>;
130
-
131
- export type TableNames = ${tableNames.map((t) => JSON.stringify(t)).join(" | ")};
132
-
133
- export type Id<TableName extends TableNames> = string & { __table: TableName };
134
-
135
- ${docInterfaces.join("\n\n")}
136
-
137
- export interface TableDocMap {
138
- ${docMapEntries.join("\n")}
139
- }
140
-
141
- export type Doc<TableName extends TableNames> = TableDocMap[TableName];
142
-
143
- ${authTypeDefs}
144
-
145
- ${staticTypeDefinitions}
146
-
147
- export const tableIndexes = {
148
- ${tableIndexEntries.join("\n")}
149
- } as const;
150
- `;
151
- }
package/cli/utils/tsc.ts DELETED
@@ -1,54 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import { spawn } from "node:child_process";
4
- import { promises as fs } from "node:fs";
5
- import path from "node:path";
6
-
7
- export async function runTscEmit(tsconfigPathAbs: string): Promise<void> {
8
- await new Promise<void>((resolve, reject) => {
9
- const child = spawn("bunx", ["tsc", "-p", tsconfigPathAbs], {
10
- stdio: "inherit",
11
- });
12
- child.on("error", reject);
13
- child.on("exit", (code) => {
14
- if (code === 0) resolve();
15
- reject(new Error(`tsc exited with code ${code}`));
16
- });
17
- });
18
- }
19
-
20
- export async function writeEmitTsconfig(params: {
21
- configDirAbs: string;
22
- outDirAbs: string;
23
- }): Promise<string> {
24
- const outDirRel =
25
- path.relative(params.configDirAbs, params.outDirAbs).replace(/\\/g, "/") ||
26
- "./_generated";
27
- const tsconfigPathAbs = path.join(
28
- params.configDirAbs,
29
- ".appflare.tsconfig.emit.json"
30
- );
31
- const content = {
32
- compilerOptions: {
33
- noEmit: false,
34
- declaration: true,
35
- emitDeclarationOnly: false,
36
- outDir: `./${outDirRel}/dist`,
37
- rootDir: `.`,
38
- sourceMap: false,
39
- declarationMap: false,
40
- skipLibCheck: true,
41
- target: "ES2022",
42
- module: "ES2022",
43
- moduleResolution: "Bundler",
44
- types: ["node"],
45
- },
46
- include: [
47
- `./${outDirRel}/src/schema-types.ts`,
48
- `./${outDirRel}/src/schema.ts`,
49
- `./${outDirRel}/src/handlers/**/*`,
50
- ],
51
- };
52
- await fs.writeFile(tsconfigPathAbs, JSON.stringify(content, null, 2));
53
- return tsconfigPathAbs;
54
- }
@@ -1,190 +0,0 @@
1
- import { promises as fs } from "node:fs";
2
- import path from "node:path";
3
- import type { BetterAuthOptions } from "better-auth";
4
- import type { BetterAuthClientOptions } from "better-auth/client";
5
- import type { StorageManagerOptions } from "../../server/storage/types";
6
-
7
- export type AppflareAuthConfig<
8
- Options extends BetterAuthOptions,
9
- ClientOptions extends BetterAuthClientOptions,
10
- > = {
11
- enabled?: boolean;
12
- basePath?: string;
13
- /** Server-side Better Auth options. */
14
- options?: Options;
15
- /** Client-side Better Auth options used in the generated API client. */
16
- clientOptions?: ClientOptions;
17
- };
18
- export type AppflareStorageConfig<
19
- Env = unknown,
20
- Principal = unknown,
21
- > = StorageManagerOptions<Env, Principal> & {
22
- /** Optional KV binding name created in the generated worker. */
23
- kvBinding?: string;
24
- /** Optional KV namespace ID used in wrangler.json. */
25
- kvId?: string;
26
- };
27
-
28
- export type AppflareSchedulerConfig = {
29
- /** Set to false to disable scheduler generation. */
30
- enabled?: boolean;
31
- /** Optional Cloudflare Queue binding name. Defaults to APPFLARE_SCHEDULER_QUEUE. */
32
- queueBinding?: string;
33
- /** Optional Cloudflare Queue name. Defaults to a sanitized worker name with -scheduler suffix. */
34
- queueName?: string;
35
- };
36
-
37
- export type AppflareConfig<
38
- Options extends BetterAuthOptions = BetterAuthOptions,
39
- ClientOptions extends BetterAuthClientOptions = BetterAuthClientOptions,
40
- > = {
41
- dir: string;
42
- schema: string;
43
- outDir: string;
44
- /** Optional override for where wrangler.json is written. Resolved relative to the config file. */
45
- wranglerOutPath?: string;
46
- /** Optional override for wrangler.json main entrypoint. Defaults to ./server/index.ts in the generated worker. */
47
- wranglerMain?: string;
48
- /** Optional override for wrangler.json compatibility_date. Defaults to current date. */
49
- wranglerCompatibilityDate?: string;
50
- /** Optional CORS whitelist for generated Worker entrypoint. */
51
- corsOrigin?: string | string[];
52
- /** Optional wrangler.json overrides. */
53
- wrangler?: {
54
- name?: string;
55
- main?: string;
56
- compatibilityDate?: string;
57
- [key: string]: unknown;
58
- };
59
- auth?: AppflareAuthConfig<Options, ClientOptions>;
60
- storage?: AppflareStorageConfig;
61
- scheduler?: AppflareSchedulerConfig;
62
- };
63
-
64
- export type HandlerKind =
65
- | "query"
66
- | "mutation"
67
- | "internalQuery"
68
- | "internalMutation"
69
- | "scheduler"
70
- | "cron"
71
- | "http";
72
-
73
- export type DiscoveredHandler = {
74
- fileName: string;
75
- /** Path relative to project root without .ts extension, using forward slashes. */
76
- routePath: string;
77
- name: string;
78
- kind: HandlerKind;
79
- sourceFileAbs: string;
80
- cronTriggers?: string[];
81
- };
82
-
83
- export function pascalCase(value: string): string {
84
- return value
85
- .replace(/(^|[^A-Za-z0-9]+)([A-Za-z0-9])/g, (_, __, c: string) =>
86
- c.toUpperCase(),
87
- )
88
- .replace(/[^A-Za-z0-9]/g, "");
89
- }
90
-
91
- export function isValidIdentifier(value: string): boolean {
92
- return /^[A-Za-z_$][\w$]*$/.test(value);
93
- }
94
-
95
- export function groupBy<T, TKey>(
96
- items: T[],
97
- keyFn: (item: T) => TKey,
98
- ): Map<TKey, T[]> {
99
- const map = new Map<TKey, T[]>();
100
- for (const item of items) {
101
- const key = keyFn(item);
102
- if (map.has(key)) {
103
- map.get(key)!.push(item);
104
- } else {
105
- map.set(key, [item]);
106
- }
107
- }
108
- return map;
109
- }
110
-
111
- export function toImportPathFromGeneratedSrc(
112
- outDirAbs: string,
113
- fileAbs: string,
114
- ): string {
115
- const fromDir = path.join(outDirAbs, "src");
116
- let rel = path.relative(fromDir, fileAbs);
117
- rel = rel.replace(/\\/g, "/");
118
- rel = rel.replace(/\.ts$/, "");
119
- if (!rel.startsWith(".")) {
120
- rel = `./${rel}`;
121
- }
122
- return rel;
123
- }
124
-
125
- export function toImportPathFromGeneratedServer(
126
- outDirAbs: string,
127
- fileAbs: string,
128
- ): string {
129
- const fromDir = path.join(outDirAbs, "server");
130
- let rel = path.relative(fromDir, fileAbs);
131
- rel = rel.replace(/\\/g, "/");
132
- rel = rel.replace(/\.ts$/, "");
133
- if (!rel.startsWith(".")) {
134
- rel = `./${rel}`;
135
- }
136
- return rel;
137
- }
138
-
139
- export async function assertFileExists(
140
- fileAbs: string,
141
- message: string,
142
- ): Promise<void> {
143
- const ok = await fileExists(fileAbs);
144
- if (!ok) throw new Error(message);
145
- }
146
-
147
- export async function assertDirExists(
148
- dirAbs: string,
149
- message: string,
150
- ): Promise<void> {
151
- try {
152
- const stat = await fs.stat(dirAbs);
153
- if (!stat.isDirectory()) throw new Error(message);
154
- } catch {
155
- throw new Error(message);
156
- }
157
- }
158
-
159
- export async function fileExists(fileAbs: string): Promise<boolean> {
160
- try {
161
- const stat = await fs.stat(fileAbs);
162
- return stat.isFile();
163
- } catch {
164
- return false;
165
- }
166
- }
167
-
168
- export async function walkTsFiles(
169
- rootAbs: string,
170
- ignoreDirs: Set<string>,
171
- ): Promise<string[]> {
172
- const out: string[] = [];
173
- const entries = await fs.readdir(rootAbs, { withFileTypes: true });
174
- for (const entry of entries) {
175
- const abs = path.join(rootAbs, entry.name);
176
- if (entry.isDirectory()) {
177
- if (ignoreDirs.has(entry.name)) continue;
178
- out.push(...(await walkTsFiles(abs, ignoreDirs)));
179
- continue;
180
- }
181
- if (
182
- entry.isFile() &&
183
- entry.name.endsWith(".ts") &&
184
- !entry.name.endsWith(".d.ts")
185
- ) {
186
- out.push(abs);
187
- }
188
- }
189
- return out;
190
- }
@@ -1,121 +0,0 @@
1
- import { isValidIdentifier } from "./utils";
2
-
3
- function getZodObjectShape(schema: any): Record<string, any> {
4
- if (!schema || typeof schema !== "object") {
5
- throw new Error(`Schema table is not an object`);
6
- }
7
-
8
- const def = (schema as any)._def;
9
- if (def?.typeName === "ZodObject" || def?.type === "object") {
10
- const shape = def.shape;
11
- if (typeof shape === "function") {
12
- return shape();
13
- }
14
- if (shape && typeof shape === "object") {
15
- return shape;
16
- }
17
- }
18
-
19
- if (typeof (schema as any).shape === "function") {
20
- return (schema as any).shape;
21
- }
22
- if ((schema as any).shape && typeof (schema as any).shape === "object") {
23
- return (schema as any).shape;
24
- }
25
-
26
- throw new Error(`Table schema is not a Zod object`);
27
- }
28
-
29
- function renderField(fieldName: string, schema: any): string {
30
- const { tsType, optional } = renderType(schema);
31
- const safeKey = isValidIdentifier(fieldName)
32
- ? fieldName
33
- : JSON.stringify(fieldName);
34
- return `${safeKey}${optional ? "?" : ""}: ${tsType};`;
35
- }
36
-
37
- function renderType(schema: any): { tsType: string; optional: boolean } {
38
- const def = schema?._def;
39
- const typeName: string | undefined = def?.typeName ?? def?.type;
40
- const description: string | undefined =
41
- schema?.description ?? def?.description;
42
-
43
- if (description === "geo:point") {
44
- return { tsType: "GeoPoint", optional: false };
45
- }
46
-
47
- if (typeName === "ZodOptional" || typeName === "optional") {
48
- const inner = def?.innerType ?? def?.schema ?? schema?._def?.innerType;
49
- const rendered = renderType(inner);
50
- return { tsType: rendered.tsType, optional: true };
51
- }
52
- if (typeName === "ZodNullable" || typeName === "nullable") {
53
- const inner = def?.innerType ?? def?.schema;
54
- const rendered = renderType(inner);
55
- return { tsType: `${rendered.tsType} | null`, optional: false };
56
- }
57
- if (typeName === "ZodDefault" || typeName === "default") {
58
- const inner = def?.innerType ?? def?.schema;
59
- return renderType(inner);
60
- }
61
-
62
- if (typeName === "ZodString" || typeName === "string") {
63
- const description: string | undefined =
64
- schema?.description ?? def?.description;
65
- if (typeof description === "string" && description.startsWith("ref:")) {
66
- const refTableName = description.slice(4);
67
- return { tsType: `Id<${JSON.stringify(refTableName)}>`, optional: false };
68
- }
69
- return { tsType: "string", optional: false };
70
- }
71
- if (typeName === "ZodNumber" || typeName === "number") {
72
- return { tsType: "number", optional: false };
73
- }
74
- if (typeName === "ZodBoolean" || typeName === "boolean") {
75
- return { tsType: "boolean", optional: false };
76
- }
77
- if (typeName === "ZodDate" || typeName === "date") {
78
- return { tsType: "Date", optional: false };
79
- }
80
- if (typeName === "ZodArray" || typeName === "array") {
81
- const inner = def?.element ?? def?.innerType ?? def?.type;
82
- const rendered = renderType(inner);
83
- return { tsType: `Array<${rendered.tsType}>`, optional: false };
84
- }
85
- if (typeName === "ZodObject" || typeName === "object") {
86
- const shape = getZodObjectShape(schema);
87
- const entries = Object.entries(shape);
88
- if (entries.length === 0) {
89
- return { tsType: "Record<string, unknown>", optional: false };
90
- }
91
- const props = entries
92
- .map(([key, value]) => {
93
- const { tsType, optional } = renderType(value);
94
- const safeKey = isValidIdentifier(key) ? key : JSON.stringify(key);
95
- return `\t${safeKey}${optional ? "?" : ""}: ${tsType};`;
96
- })
97
- .join("\n");
98
- return { tsType: `{\n${props}\n}`, optional: false };
99
- }
100
- if (typeName === "ZodUnion" || typeName === "union") {
101
- const options: any[] = def?.options ?? def?.optionsMap ?? [];
102
- const parts = Array.isArray(options)
103
- ? options.map((o) => renderType(o).tsType)
104
- : ["unknown"];
105
- return { tsType: parts.join(" | ") || "unknown", optional: false };
106
- }
107
- if (typeName === "ZodLiteral" || typeName === "literal") {
108
- const value = def?.value;
109
- return { tsType: JSON.stringify(value), optional: false };
110
- }
111
- if (typeName === "ZodAny" || typeName === "any") {
112
- return { tsType: "any", optional: false };
113
- }
114
- if (typeName === "ZodUnknown" || typeName === "unknown") {
115
- return { tsType: "unknown", optional: false };
116
- }
117
-
118
- return { tsType: "unknown", optional: false };
119
- }
120
-
121
- export { getZodObjectShape, renderField, renderType };
package/lib/README.md DELETED
@@ -1,50 +0,0 @@
1
- # Appflare Library
2
-
3
- Docs for the shared library helpers in `lib/`
4
-
5
- ## What’s here
6
-
7
- - Schema helpers for defining tables and schemas with Zod
8
- - Value builders (`v.*`) for common field types and relations
9
- - React Query hooks with realtime-friendly subscriptions
10
-
11
- ## Library (`lib/`)
12
-
13
- **Schema builders** ([packages/appflare/lib/db.ts](packages/appflare/lib/db.ts))
14
-
15
- - `defineTable(shape)`: wrap a Zod shape in `z.object` to define a table.
16
- - `defineSchema(tables)`: collect a map of tables into a schema object.
17
-
18
- **Value helpers** ([packages/appflare/lib/values.ts](packages/appflare/lib/values.ts))
19
-
20
- - Primitives: `v.string()`, `v.number()`, `v.boolean()`, `v.date()`.
21
- - Relations/ids: `v.id(table)` creates a string with an ObjectId regex and a `ref:<table>` description for downstream typing.
22
- - Location: `v.point()`/`v.location()` yields a GeoJSON Point schema for geospatial queries.
23
- - Collections/objects: `v.array(item)`, `v.object(shape)`.
24
- - Modifiers: `v.optional(schema)`, `v.nullable(schema)`, `v.union(...schemas)`, `v.literal(value)`.
25
- - Misc: `v.buffer()` (string placeholder), `v.any()`, `v.unknown()`.
26
-
27
- **Location helpers** ([packages/appflare/lib/location.ts](packages/appflare/lib/location.ts))
28
-
29
- - Builders: `point(lng, lat)` returns a GeoJSON Point; `geo.point` is the same alias.
30
- - Queries: `geo.near("location", pointLngLat, { maxDistanceMeters })`, `geo.withinRadius("location", center, radiusMeters)`, `geo.withinBox(...)`, `geo.withinPolygon(...)`, `geo.intersects(...)` produce Mongo-ready filters you can pass to `where`.
31
-
32
- **Example: define a schema**
33
-
34
- ```ts
35
- import { defineSchema, defineTable } from "appflare/lib/db";
36
- import { v } from "appflare/lib/values";
37
-
38
- export default defineSchema({
39
- users: defineTable({
40
- name: v.string(),
41
- email: v.string(),
42
- age: v.number().optional(),
43
- orgId: v.id("orgs"),
44
- location: v.point(),
45
- }),
46
- orgs: defineTable({
47
- name: v.string(),
48
- }),
49
- });
50
- ```
package/lib/db.ts DELETED
@@ -1,19 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export type AppflareTable<TShape extends Record<string, z.ZodTypeAny>> =
4
- z.ZodObject<TShape>;
5
-
6
- export type AppflareSchema<TTables extends Record<string, AppflareTable<any>>> =
7
- TTables;
8
-
9
- export function defineTable<TShape extends Record<string, z.ZodTypeAny>>(
10
- shape: TShape
11
- ): AppflareTable<TShape> {
12
- return z.object(shape);
13
- }
14
-
15
- export function defineSchema<
16
- TTables extends Record<string, AppflareTable<any>>,
17
- >(tables: TTables): AppflareSchema<TTables> {
18
- return tables;
19
- }
package/lib/location.ts DELETED
@@ -1,110 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export type GeoCoordinates = readonly [number, number];
4
-
5
- export type GeoPoint = {
6
- type: "Point";
7
- coordinates: GeoCoordinates;
8
- };
9
-
10
- export const geoPointSchema = z
11
- .object({
12
- type: z.literal("Point"),
13
- coordinates: z.tuple([z.number(), z.number()]),
14
- })
15
- .describe("geo:point");
16
-
17
- export const EARTH_RADIUS_METERS = 6_378_100;
18
-
19
- const normalizePoint = (point: GeoPoint | GeoCoordinates): GeoPoint =>
20
- Array.isArray(point)
21
- ? { type: "Point", coordinates: [point[0], point[1]] }
22
- : (point as GeoPoint);
23
-
24
- export const point = (lng: number, lat: number): GeoPoint => ({
25
- type: "Point",
26
- coordinates: [lng, lat],
27
- });
28
-
29
- export type NearQueryOptions = {
30
- maxDistanceMeters?: number;
31
- minDistanceMeters?: number;
32
- };
33
-
34
- export const near = (
35
- field: string,
36
- geometry: GeoPoint | GeoCoordinates,
37
- options: NearQueryOptions = {}
38
- ): Record<string, unknown> => {
39
- const $near: Record<string, unknown> = {
40
- $geometry: normalizePoint(geometry),
41
- };
42
- if (options.maxDistanceMeters !== undefined) {
43
- $near.$maxDistance = options.maxDistanceMeters;
44
- }
45
- if (options.minDistanceMeters !== undefined) {
46
- $near.$minDistance = options.minDistanceMeters;
47
- }
48
- return { [field]: { $near } };
49
- };
50
-
51
- export const withinRadius = (
52
- field: string,
53
- center: GeoPoint | GeoCoordinates,
54
- radiusMeters: number
55
- ): Record<string, unknown> => ({
56
- [field]: {
57
- $geoWithin: {
58
- $centerSphere: [
59
- normalizePoint(center).coordinates,
60
- radiusMeters / EARTH_RADIUS_METERS,
61
- ],
62
- },
63
- },
64
- });
65
-
66
- export const withinBox = (
67
- field: string,
68
- southwest: GeoPoint | GeoCoordinates,
69
- northeast: GeoPoint | GeoCoordinates
70
- ): Record<string, unknown> => ({
71
- [field]: {
72
- $geoWithin: {
73
- $box: [
74
- normalizePoint(southwest).coordinates,
75
- normalizePoint(northeast).coordinates,
76
- ],
77
- },
78
- },
79
- });
80
-
81
- export const withinPolygon = (
82
- field: string,
83
- polygon: ReadonlyArray<GeoPoint | GeoCoordinates>
84
- ): Record<string, unknown> => ({
85
- [field]: {
86
- $geoWithin: {
87
- $polygon: polygon.map((p) => normalizePoint(p).coordinates),
88
- },
89
- },
90
- });
91
-
92
- export const intersects = (
93
- field: string,
94
- geometry: { type: string; coordinates: unknown }
95
- ): Record<string, unknown> => ({
96
- [field]: {
97
- $geoIntersects: {
98
- $geometry: geometry,
99
- },
100
- },
101
- });
102
-
103
- export const geo = {
104
- point,
105
- near,
106
- withinRadius,
107
- withinBox,
108
- withinPolygon,
109
- intersects,
110
- };