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.
@@ -4,11 +4,205 @@ export function generateQueryHelperFunctionsSection(): string {
4
4
  }
5
5
 
6
6
  function readOperatorValue(record: Record<string, unknown>, key: string): unknown {
7
- if (record[key] !== undefined) {
8
- return record[key];
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
- const prefixed = "$" + key;
11
- return record[prefixed];
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.$minDistance ??
287
+ operand.minDistance ??
94
288
  operand.gte ??
95
- operand.$gte ??
96
- operand.gt ??
97
- operand.$gt;
289
+ operand.gt;
98
290
  const maxDistance =
99
- operand.$maxDistance ??
291
+ operand.maxDistance ??
100
292
  operand.lte ??
101
- operand.$lte ??
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
- : isRecord(where.$geoWithin)
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" || 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
  }