appflare 0.2.36 → 0.2.37
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/Documentation.md +8 -8
- package/cli/commands/index.ts +1 -1
- package/cli/generate.ts +24 -0
- package/cli/index.ts +1 -1
- package/cli/schema-compiler.ts +180 -2
- package/cli/templates/core/README.md +1 -1
- package/cli/templates/handlers/generators/handlers.ts +2 -2
- package/cli/templates/handlers/generators/registration/modules/realtime/utils.ts +4 -10
- package/cli/templates/handlers/generators/types/context.ts +8 -2
- package/cli/templates/handlers/generators/types/query-definitions/filter-and-where-types.ts +6 -16
- package/cli/templates/handlers/generators/types/query-definitions/query-helper-functions.ts +216 -17
- package/cli/templates/handlers/index.ts +2 -1
- package/cli/templates/handlers/types.ts +2 -2
- package/dist/cli/index.js +502 -301
- package/dist/cli/index.mjs +502 -301
- package/dist/index.d.mts +81 -2
- package/dist/index.d.ts +81 -2
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/schema.ts +229 -3
|
@@ -4,11 +4,205 @@ export function generateQueryHelperFunctionsSection(): string {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
function readOperatorValue(record: Record<string, unknown>, key: string): unknown {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
return record[key];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function getJsonColumnsMap(): Record<string, Record<string, { shape: unknown }>> {
|
|
11
|
+
const rawValue = (mergedSchema as Record<string, unknown>)[
|
|
12
|
+
"__appflareJsonColumns"
|
|
13
|
+
];
|
|
14
|
+
if (!isRecord(rawValue)) {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
const map: Record<string, Record<string, { shape: unknown }>> = {};
|
|
18
|
+
for (const [tableName, tableValue] of Object.entries(rawValue)) {
|
|
19
|
+
if (isRecord(tableValue)) {
|
|
20
|
+
map[tableName] = tableValue as Record<string, { shape: unknown }>;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return map;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const jsonColumnsMap = getJsonColumnsMap();
|
|
27
|
+
|
|
28
|
+
function getJsonColumnShape(
|
|
29
|
+
tableName: string | undefined,
|
|
30
|
+
fieldName: string,
|
|
31
|
+
): { kind: string } | null {
|
|
32
|
+
if (!tableName) return null;
|
|
33
|
+
const tableCols = jsonColumnsMap[tableName];
|
|
34
|
+
if (!tableCols) return null;
|
|
35
|
+
const col = tableCols[fieldName];
|
|
36
|
+
if (!col || !isRecord(col.shape)) return null;
|
|
37
|
+
return col.shape as { kind: string };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function buildJsonFieldFilter(
|
|
41
|
+
fieldName: string,
|
|
42
|
+
field: unknown,
|
|
43
|
+
value: unknown,
|
|
44
|
+
tableName: string | undefined,
|
|
45
|
+
): SQL | undefined {
|
|
46
|
+
const shape = getJsonColumnShape(tableName, fieldName);
|
|
47
|
+
if (!shape) return undefined;
|
|
48
|
+
|
|
49
|
+
if (shape.kind === "array") {
|
|
50
|
+
return buildJsonArrayFilter(fieldName, field, value);
|
|
51
|
+
}
|
|
52
|
+
if (shape.kind === "object") {
|
|
53
|
+
return buildJsonObjectFilter(fieldName, field, value);
|
|
9
54
|
}
|
|
10
|
-
|
|
11
|
-
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function buildJsonArrayFilter(
|
|
59
|
+
fieldName: string,
|
|
60
|
+
field: unknown,
|
|
61
|
+
value: unknown,
|
|
62
|
+
): SQL | undefined {
|
|
63
|
+
if (!isRecord(value) || value instanceof Date || Array.isArray(value)) {
|
|
64
|
+
return sql\`json_extract(\${field as never}, '$') = json(\${JSON.stringify(value)})\`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const filters: SQL[] = [];
|
|
68
|
+
|
|
69
|
+
const includesValue = readOperatorValue(value, "includes");
|
|
70
|
+
if (Array.isArray(includesValue) && includesValue.length > 0) {
|
|
71
|
+
const conditions = includesValue.map((v) =>
|
|
72
|
+
sql\`EXISTS (SELECT 1 FROM json_each(\${field as never}) j WHERE j.value = \${v})\`,
|
|
73
|
+
);
|
|
74
|
+
filters.push(and(...conditions));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const includesAnyValue = readOperatorValue(value, "includesAny");
|
|
78
|
+
if (Array.isArray(includesAnyValue) && includesAnyValue.length > 0) {
|
|
79
|
+
const conditions = includesAnyValue.map((v) =>
|
|
80
|
+
sql\`EXISTS (SELECT 1 FROM json_each(\${field as never}) j WHERE j.value = \${v})\`,
|
|
81
|
+
);
|
|
82
|
+
filters.push(sql\`(\${sql.join(conditions, sql\` OR \`)})\`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const lengthValue = readOperatorValue(value, "length");
|
|
86
|
+
if (lengthValue !== undefined) {
|
|
87
|
+
filters.push(sql\`json_array_length(\${field as never}) = \${lengthValue}\`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const eqValue = readOperatorValue(value, "eq");
|
|
91
|
+
if (eqValue !== undefined) {
|
|
92
|
+
filters.push(sql\`json_extract(\${field as never}, '$') = json(\${JSON.stringify(eqValue)})\`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const neValue = readOperatorValue(value, "ne");
|
|
96
|
+
if (neValue !== undefined) {
|
|
97
|
+
filters.push(sql\`json_extract(\${field as never}, '$') != json(\${JSON.stringify(neValue)})\`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (filters.length === 0) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
if (filters.length === 1) return filters[0];
|
|
104
|
+
return and(...filters);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function buildJsonObjectFilter(
|
|
108
|
+
fieldName: string,
|
|
109
|
+
field: unknown,
|
|
110
|
+
value: unknown,
|
|
111
|
+
): SQL | undefined {
|
|
112
|
+
if (!isRecord(value) || value instanceof Date || Array.isArray(value)) {
|
|
113
|
+
return sql\`json_extract(\${field as never}, '$') = json(\${JSON.stringify(value)})\`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const filters: SQL[] = [];
|
|
117
|
+
|
|
118
|
+
for (const [key, opValue] of Object.entries(value)) {
|
|
119
|
+
if (key === "eq") {
|
|
120
|
+
filters.push(sql\`json_extract(\${field as never}, '$') = json(\${JSON.stringify(opValue)})\`);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (key === "ne") {
|
|
124
|
+
filters.push(sql\`json_extract(\${field as never}, '$') != json(\${JSON.stringify(opValue)})\`);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (key === "exists") {
|
|
128
|
+
filters.push(
|
|
129
|
+
opValue
|
|
130
|
+
? sql\`json_extract(\${field as never}, '$') IS NOT NULL\`
|
|
131
|
+
: sql\`json_extract(\${field as never}, '$') IS NULL\`,
|
|
132
|
+
);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const nestedFilters = buildJsonObjectPathFilter(field, key, opValue);
|
|
137
|
+
if (nestedFilters) {
|
|
138
|
+
filters.push(nestedFilters);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (filters.length === 0) return undefined;
|
|
143
|
+
if (filters.length === 1) return filters[0];
|
|
144
|
+
return and(...filters);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function buildJsonObjectPathFilter(
|
|
148
|
+
field: unknown,
|
|
149
|
+
path: string,
|
|
150
|
+
opValue: unknown,
|
|
151
|
+
): SQL | undefined {
|
|
152
|
+
const jsonPath = '$.' + path;
|
|
153
|
+
if (!isRecord(opValue) || opValue instanceof Date || Array.isArray(opValue)) {
|
|
154
|
+
return sql\`json_extract(\${field as never}, \${jsonPath}) = \${opValue}\`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const filters: SQL[] = [];
|
|
158
|
+
|
|
159
|
+
const eqVal = readOperatorValue(opValue, "eq");
|
|
160
|
+
if (eqVal !== undefined) {
|
|
161
|
+
filters.push(sql\`json_extract(\${field as never}, \${jsonPath}) = \${eqVal}\`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const neVal = readOperatorValue(opValue, "ne");
|
|
165
|
+
if (neVal !== undefined) {
|
|
166
|
+
filters.push(sql\`json_extract(\${field as never}, \${jsonPath}) != \${neVal}\`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const gtVal = readOperatorValue(opValue, "gt");
|
|
170
|
+
if (gtVal !== undefined) {
|
|
171
|
+
filters.push(sql\`json_extract(\${field as never}, \${jsonPath}) > \${gtVal}\`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const gteVal = readOperatorValue(opValue, "gte");
|
|
175
|
+
if (gteVal !== undefined) {
|
|
176
|
+
filters.push(sql\`json_extract(\${field as never}, \${jsonPath}) >= \${gteVal}\`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const ltVal = readOperatorValue(opValue, "lt");
|
|
180
|
+
if (ltVal !== undefined) {
|
|
181
|
+
filters.push(sql\`json_extract(\${field as never}, \${jsonPath}) < \${ltVal}\`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const lteVal = readOperatorValue(opValue, "lte");
|
|
185
|
+
if (lteVal !== undefined) {
|
|
186
|
+
filters.push(sql\`json_extract(\${field as never}, \${jsonPath}) <= \${lteVal}\`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const inVal = readOperatorValue(opValue, "in");
|
|
190
|
+
if (Array.isArray(inVal) && inVal.length > 0) {
|
|
191
|
+
filters.push(sql\`json_extract(\${field as never}, \${jsonPath}) IN (\${sql.join(inVal.map((v) => sql\`\${v}\`), sql\`, \`)})\`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const existsVal = readOperatorValue(opValue, "exists");
|
|
195
|
+
if (typeof existsVal === "boolean") {
|
|
196
|
+
filters.push(
|
|
197
|
+
existsVal
|
|
198
|
+
? sql\`json_extract(\${field as never}, \${jsonPath}) IS NOT NULL\`
|
|
199
|
+
: sql\`json_extract(\${field as never}, \${jsonPath}) IS NULL\`,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (filters.length === 0) return undefined;
|
|
204
|
+
if (filters.length === 1) return filters[0];
|
|
205
|
+
return and(...filters);
|
|
12
206
|
}
|
|
13
207
|
|
|
14
208
|
function normalizeGeoPoint(
|
|
@@ -90,17 +284,13 @@ function createGeoDistanceFilter(
|
|
|
90
284
|
)\`;
|
|
91
285
|
|
|
92
286
|
const minDistance =
|
|
93
|
-
operand
|
|
287
|
+
operand.minDistance ??
|
|
94
288
|
operand.gte ??
|
|
95
|
-
operand
|
|
96
|
-
operand.gt ??
|
|
97
|
-
operand.$gt;
|
|
289
|
+
operand.gt;
|
|
98
290
|
const maxDistance =
|
|
99
|
-
operand
|
|
291
|
+
operand.maxDistance ??
|
|
100
292
|
operand.lte ??
|
|
101
|
-
operand
|
|
102
|
-
operand.lt ??
|
|
103
|
-
operand.$lt;
|
|
293
|
+
operand.lt;
|
|
104
294
|
|
|
105
295
|
const filters: SQL[] = [];
|
|
106
296
|
if (typeof minDistance === "number") {
|
|
@@ -126,7 +316,18 @@ function buildFieldFilter(
|
|
|
126
316
|
field: unknown,
|
|
127
317
|
value: unknown,
|
|
128
318
|
fields: Record<string, unknown>,
|
|
319
|
+
tableName?: string,
|
|
129
320
|
): SQL | undefined {
|
|
321
|
+
const jsonFilter = buildJsonFieldFilter(fieldName, field, value, tableName);
|
|
322
|
+
if (jsonFilter) return jsonFilter;
|
|
323
|
+
|
|
324
|
+
const isArrayColumn = isRecord(value) && !Array.isArray(value) && !(value instanceof Date) &&
|
|
325
|
+
("includes" in value || "includesAny" in value || "length" in value);
|
|
326
|
+
if (isArrayColumn) {
|
|
327
|
+
const arrayFilter = buildJsonArrayFilter(fieldName, field, value);
|
|
328
|
+
if (arrayFilter) return arrayFilter;
|
|
329
|
+
}
|
|
330
|
+
|
|
130
331
|
if (!isRecord(value) || value instanceof Date || Array.isArray(value)) {
|
|
131
332
|
return eq(field as never, value as never);
|
|
132
333
|
}
|
|
@@ -237,9 +438,7 @@ function buildWhereFilterFromFields(
|
|
|
237
438
|
|
|
238
439
|
const topLevelGeoWithin = isRecord(where.geoWithin)
|
|
239
440
|
? (where.geoWithin as GeoWithinOperand)
|
|
240
|
-
:
|
|
241
|
-
? (where.$geoWithin as GeoWithinOperand)
|
|
242
|
-
: undefined;
|
|
441
|
+
: undefined;
|
|
243
442
|
|
|
244
443
|
if (topLevelGeoWithin) {
|
|
245
444
|
const geoFilter = createGeoDistanceFilter(topLevelGeoWithin, fields, tableName);
|
|
@@ -249,7 +448,7 @@ function buildWhereFilterFromFields(
|
|
|
249
448
|
}
|
|
250
449
|
|
|
251
450
|
for (const [fieldName, fieldValue] of Object.entries(where)) {
|
|
252
|
-
if (fieldName === "geoWithin"
|
|
451
|
+
if (fieldName === "geoWithin") {
|
|
253
452
|
continue;
|
|
254
453
|
}
|
|
255
454
|
|
|
@@ -277,7 +476,7 @@ function buildWhereFilterFromFields(
|
|
|
277
476
|
continue;
|
|
278
477
|
}
|
|
279
478
|
|
|
280
|
-
const filter = buildFieldFilter(fieldName, field, fieldValue, fields);
|
|
479
|
+
const filter = buildFieldFilter(fieldName, field, fieldValue, fields, tableName);
|
|
281
480
|
if (filter) {
|
|
282
481
|
filters.push(filter);
|
|
283
482
|
}
|
|
@@ -13,8 +13,9 @@ export function generateHandlersArtifacts(
|
|
|
13
13
|
schemaImportPath: string,
|
|
14
14
|
operations: DiscoveredHandlerOperation[],
|
|
15
15
|
defaultR2Binding?: string,
|
|
16
|
+
roles: string[] = [],
|
|
16
17
|
): GeneratedHandlerArtifact[] {
|
|
17
|
-
const handlersSource = generateHandlersSource(schemaImportPath);
|
|
18
|
+
const handlersSource = generateHandlersSource(schemaImportPath, roles);
|
|
18
19
|
|
|
19
20
|
const contextSource = generateContextSource(defaultR2Binding);
|
|
20
21
|
|
|
@@ -4,12 +4,12 @@ import { generateTypesQueryRuntimeSection } from "./generators/types/query-runti
|
|
|
4
4
|
import { generateTypesContextSection } from "./generators/types/context";
|
|
5
5
|
import { generateTypesOperationsSection } from "./generators/types/operations";
|
|
6
6
|
|
|
7
|
-
export function generateTypes(): string {
|
|
7
|
+
export function generateTypes(roles: string[] = []): string {
|
|
8
8
|
return [
|
|
9
9
|
generateTypesCoreSection(),
|
|
10
10
|
generateTypesQueryDefinitionsSection(),
|
|
11
11
|
generateTypesQueryRuntimeSection(),
|
|
12
|
-
generateTypesContextSection(),
|
|
12
|
+
generateTypesContextSection(roles),
|
|
13
13
|
generateTypesOperationsSection(),
|
|
14
14
|
].join("\n\n");
|
|
15
15
|
}
|