kitcn 0.23.0 → 0.25.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/dist/aggregate/index.d.ts +11 -1
- package/dist/aggregate/index.js +1 -1
- package/dist/auth/generated/index.d.ts +1 -1
- package/dist/auth/generated/index.js +1 -1
- package/dist/auth/http/index.d.ts +1 -1
- package/dist/auth/index.d.ts +4 -4
- package/dist/auth/index.js +10 -9
- package/dist/auth/nextjs/index.d.ts +2 -2
- package/dist/auth/nextjs/index.js +3 -3
- package/dist/auth/start/server/index.d.ts +1 -1
- package/dist/auth/start/server/index.js +1 -1
- package/dist/{builder-CsxVc5xC.js → builder-1Vx-tEnS.js} +3 -3
- package/dist/{caller-factory-CWa0ELLD.js → caller-factory-Dd3H7j3V.js} +3 -3
- package/dist/{where-clause-compiler-B_H3oio5.d.ts → capabilities-Bem7xvGK.d.ts} +2958 -4145
- package/dist/cli.mjs +155 -60
- package/dist/{convex-plugin-D8B0oCFq.js → convex-plugin-DfOhBU9g.js} +1 -1
- package/dist/{create-schema-orm-B3f2Kc8O.js → create-schema-orm-Bu2JCiBS.js} +1 -1
- package/dist/crpc/index.d.ts +3 -3
- package/dist/crpc/index.js +4 -4
- package/dist/{customFunctions-DxEEO4Dq.js → customFunctions-BbhgdRGl.js} +2 -2
- package/dist/definitions-D9vhJ6OV.js +215 -0
- package/dist/extensions-DzjCJXYl.js +55 -0
- package/dist/filter-expression-Dydt8wS0.js +402 -0
- package/dist/{generated-contract-disabled-B-nmd7Ne.d.ts → generated-contract-disabled-BEc4d98x.d.ts} +2 -2
- package/dist/{http-types-BoSDAh4Y.d.ts → http-types-90mapMP9.d.ts} +33 -3
- package/dist/id-Bibg34Yb.js +37 -0
- package/dist/{local-env-yFKub75x.mjs → local-env-Du24tdbp.mjs} +2243 -2113
- package/dist/{middleware-Br5zubu7.d.ts → middleware-CE_MC6Gi.d.ts} +1 -1
- package/dist/{middleware-DUd1Sj39.js → middleware-cJDRHTRY.js} +1 -1
- package/dist/orm/aggregate-index/index.d.ts +20 -0
- package/dist/orm/aggregate-index/index.js +2136 -0
- package/dist/orm/index.d.ts +5 -4
- package/dist/orm/index.js +126 -4292
- package/dist/orm/migrations/index.d.ts +3 -0
- package/dist/orm/migrations/index.js +506 -0
- package/dist/plugins/index.d.ts +1 -1
- package/dist/plugins/index.js +1 -1
- package/dist/{procedure-caller-DQxnLBS_.js → procedure-caller-DIzXfX2E.js} +2 -2
- package/dist/{procedure-name-C55TynK3.d.ts → procedure-name-B09-cf1-.d.ts} +3 -3
- package/dist/{query-context-BzihIpnM.js → query-context-DOm5Xm3H.js} +1 -402
- package/dist/{query-options-C_eBSIXG.js → query-options-DUiOj01a.js} +33 -1
- package/dist/ratelimit/index.d.ts +47 -11
- package/dist/ratelimit/index.js +256 -102
- package/dist/react/index.d.ts +9 -6
- package/dist/react/index.js +36 -17
- package/dist/rsc/index.d.ts +6 -9
- package/dist/rsc/index.js +7 -11
- package/dist/runtime-DfGsZtx3.js +1074 -0
- package/dist/schema-Besl-rpv.js +1411 -0
- package/dist/server/index.d.ts +4 -4
- package/dist/server/index.js +4 -4
- package/dist/solid/index.d.ts +9 -6
- package/dist/solid/index.js +36 -17
- package/dist/{runtime-6DJxpDfb.js → table-rmJm5Qio.js} +3 -1095
- package/dist/{token-xlpENMVn.js → token-DcV_0fkF.js} +1 -1
- package/dist/{types-C0Xl7P8K.d.ts → types-In-ZZOsd.d.ts} +1 -1
- package/dist/{validators-C7LelqTN.js → validators-Dmbhsd3U.js} +1 -1
- package/dist/watcher.mjs +1 -1
- package/dist/where-clause-compiler-mohWFiG0.d.ts +2042 -0
- package/package.json +3 -2
- package/skills/kitcn/SKILL.md +19 -18
- package/skills/kitcn/references/features/aggregates.md +21 -0
- package/skills/kitcn/references/features/auth-organizations.md +3 -3
- package/skills/kitcn/references/features/auth-polar.md +2 -1
- package/skills/kitcn/references/features/auth.md +8 -1
- package/skills/kitcn/references/features/http.md +26 -1
- package/skills/kitcn/references/features/ratelimit.md +11 -0
- package/skills/kitcn/references/features/react.md +1 -1
- package/skills/kitcn/references/setup/auth.md +52 -42
- package/skills/kitcn/references/setup/index.md +1 -1
- package/skills/kitcn/references/setup/server.md +37 -6
- /package/dist/{api-entry-N3nBOlI2.js → api-entry-CkDpGYVg.js} +0 -0
- /package/dist/{context-utils-BBUtBqjN.d.ts → context-utils-DwZ3Cam1.d.ts} +0 -0
- /package/dist/{create-schema-BXrKE2YY.js → create-schema-BRFjdgRr.js} +0 -0
- /package/dist/{error-Bvo7YEhk.js → error-CMLeCadS.js} +0 -0
- /package/dist/{generated-contract-disabled-BXaz7JCE.js → generated-contract-disabled-CZa0iyV0.js} +0 -0
- /package/dist/{meta-utils-D9K4fICl.js → meta-utils-D2lqN2Lg.js} +0 -0
- /package/dist/{query-context-CNo9ffvI.d.ts → query-context-DJONf8X5.d.ts} +0 -0
- /package/dist/{token-B9Bjcqug.d.ts → token-kQaqFby4.d.ts} +0 -0
- /package/dist/{transformer-yZuBWo8v.js → transformer-D8wO-kEj.js} +0 -0
- /package/dist/{types-BCl8gfGy.d.ts → types-Bf3XQex5.d.ts} +0 -0
- /package/dist/{types-DF2cg_w0.d.ts → types-MHYXboyy.d.ts} +0 -0
- /package/dist/{types-CnTpHR1F.d.ts → types-eNzQmbqM.d.ts} +0 -0
- /package/dist/{upstream-BR6sBLg3.js → upstream-BCgGZX6q.js} +0 -0
- /package/dist/{validators-BhsByJeg.d.ts → validators-V4eM2-Jg.d.ts} +0 -0
|
@@ -1,406 +1,5 @@
|
|
|
1
1
|
import { compareValues, convexToJson, jsonToConvex } from "convex/values";
|
|
2
2
|
|
|
3
|
-
//#region src/orm/filter-expression.ts
|
|
4
|
-
/**
|
|
5
|
-
* Unique symbol for FilterExpression brand
|
|
6
|
-
* Prevents structural typing - only expressions created by factory functions are valid
|
|
7
|
-
*/
|
|
8
|
-
const FilterExpressionBrand = Symbol("FilterExpression");
|
|
9
|
-
/**
|
|
10
|
-
* Create a field reference
|
|
11
|
-
* Used internally by operator functions
|
|
12
|
-
*/
|
|
13
|
-
function fieldRef(fieldName) {
|
|
14
|
-
return {
|
|
15
|
-
__brand: "FieldReference",
|
|
16
|
-
fieldName
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Type guard for FieldReference
|
|
21
|
-
*/
|
|
22
|
-
function isFieldReference(value) {
|
|
23
|
-
return value && typeof value === "object" && value.__brand === "FieldReference";
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* The pattern is the same for every row of a scan while the value is not, so
|
|
27
|
-
* only the value genuinely has to be split per call. A small bounded cache
|
|
28
|
-
* keeps the split pattern across rows without unbounded growth on
|
|
29
|
-
* caller-supplied patterns; a query with a handful of LIKE filters still hits
|
|
30
|
-
* on every row.
|
|
31
|
-
*/
|
|
32
|
-
const LIKE_PATTERN_CACHE_MAX = 16;
|
|
33
|
-
const likePatternCache = /* @__PURE__ */ new Map();
|
|
34
|
-
function likePatternCodePoints(pattern, caseInsensitive) {
|
|
35
|
-
const key = caseInsensitive ? `i:${pattern}` : `s:${pattern}`;
|
|
36
|
-
const cached = likePatternCache.get(key);
|
|
37
|
-
if (cached) return cached;
|
|
38
|
-
const source = Array.from(caseInsensitive ? pattern.toLowerCase() : pattern);
|
|
39
|
-
if (likePatternCache.size >= LIKE_PATTERN_CACHE_MAX) for (const oldest of likePatternCache.keys()) {
|
|
40
|
-
likePatternCache.delete(oldest);
|
|
41
|
-
break;
|
|
42
|
-
}
|
|
43
|
-
likePatternCache.set(key, source);
|
|
44
|
-
return source;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* SQL `LIKE` semantics: `%` matches any run of characters, `_` matches exactly
|
|
48
|
-
* one, everything else is literal. Wildcards work anywhere in the pattern, not
|
|
49
|
-
* only at the ends.
|
|
50
|
-
*
|
|
51
|
-
* Matched with a single-backtrack scan rather than a compiled regex. A pattern
|
|
52
|
-
* is caller data — anything interpolated into `` `%${query}%` `` reaches here —
|
|
53
|
-
* and translating `%` to a regex quantifier makes `'%%%%%z'` backtrack
|
|
54
|
-
* exponentially, which would burn the whole Convex function CPU budget on one
|
|
55
|
-
* row. This scan remembers only the most recent `%`, so the worst case is
|
|
56
|
-
* O(value * pattern) with no catastrophic case at all.
|
|
57
|
-
*/
|
|
58
|
-
function matchLikePattern(value, pattern, caseInsensitive) {
|
|
59
|
-
const target = Array.from(caseInsensitive ? value.toLowerCase() : value);
|
|
60
|
-
const source = likePatternCodePoints(pattern, caseInsensitive);
|
|
61
|
-
let valueIndex = 0;
|
|
62
|
-
let patternIndex = 0;
|
|
63
|
-
let wildcardPatternIndex = -1;
|
|
64
|
-
let wildcardValueIndex = 0;
|
|
65
|
-
while (valueIndex < target.length) {
|
|
66
|
-
const patternChar = patternIndex < source.length ? source[patternIndex] : void 0;
|
|
67
|
-
if (patternChar === "_" || patternChar === target[valueIndex]) {
|
|
68
|
-
valueIndex += 1;
|
|
69
|
-
patternIndex += 1;
|
|
70
|
-
} else if (patternChar === "%") {
|
|
71
|
-
wildcardPatternIndex = patternIndex;
|
|
72
|
-
wildcardValueIndex = valueIndex;
|
|
73
|
-
patternIndex += 1;
|
|
74
|
-
} else if (wildcardPatternIndex === -1) return false;
|
|
75
|
-
else {
|
|
76
|
-
wildcardValueIndex += 1;
|
|
77
|
-
valueIndex = wildcardValueIndex;
|
|
78
|
-
patternIndex = wildcardPatternIndex + 1;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
while (patternIndex < source.length && source[patternIndex] === "%") patternIndex += 1;
|
|
82
|
-
return patternIndex === source.length;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Structural equality matching Convex's own `q.eq`. Arrays, objects and bytes
|
|
86
|
-
* are compared by content: reference identity would make every `eq`/`in` filter
|
|
87
|
-
* on a `custom(v.array(...))` / `custom(v.object(...))` column match nothing.
|
|
88
|
-
*/
|
|
89
|
-
function filterValuesEqual(a, b) {
|
|
90
|
-
if (a === b) return true;
|
|
91
|
-
if (a === null || b === null || typeof a !== "object" || typeof b !== "object") return false;
|
|
92
|
-
if (Array.isArray(a) || Array.isArray(b)) {
|
|
93
|
-
if (!(Array.isArray(a) && Array.isArray(b)) || a.length !== b.length) return false;
|
|
94
|
-
return a.every((item, index) => filterValuesEqual(item, b[index]));
|
|
95
|
-
}
|
|
96
|
-
if (a instanceof ArrayBuffer || b instanceof ArrayBuffer) {
|
|
97
|
-
if (!(a instanceof ArrayBuffer && b instanceof ArrayBuffer) || a.byteLength !== b.byteLength) return false;
|
|
98
|
-
const left = new Uint8Array(a);
|
|
99
|
-
const right = new Uint8Array(b);
|
|
100
|
-
return left.every((byte, index) => byte === right[index]);
|
|
101
|
-
}
|
|
102
|
-
const aKeys = Object.keys(a);
|
|
103
|
-
const bKeys = Object.keys(b);
|
|
104
|
-
if (aKeys.length !== bKeys.length) return false;
|
|
105
|
-
return aKeys.every((key) => Object.hasOwn(b, key) && filterValuesEqual(a[key], b[key]));
|
|
106
|
-
}
|
|
107
|
-
/** `inArray`/`notInArray` membership using {@link filterValuesEqual}. */
|
|
108
|
-
function filterValueInList(value, list) {
|
|
109
|
-
return list.some((candidate) => filterValuesEqual(candidate, value));
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Create a column wrapper
|
|
113
|
-
* Used internally by query builder's _createColumnProxies
|
|
114
|
-
*/
|
|
115
|
-
function column(builder, columnName) {
|
|
116
|
-
return {
|
|
117
|
-
builder,
|
|
118
|
-
columnName
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
function resolveColumn(col) {
|
|
122
|
-
if (col && typeof col === "object" && "columnName" in col) return col;
|
|
123
|
-
if (isFieldReference(col)) return {
|
|
124
|
-
builder: col,
|
|
125
|
-
columnName: col.fieldName
|
|
126
|
-
};
|
|
127
|
-
const builder = col;
|
|
128
|
-
const columnName = builder?.config?.name;
|
|
129
|
-
if (!columnName) throw new Error("Column builder is missing a column name");
|
|
130
|
-
return column(builder, columnName);
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Internal binary expression implementation
|
|
134
|
-
* Private class - only accessible via factory functions
|
|
135
|
-
*/
|
|
136
|
-
var BinaryExpressionImpl = class {
|
|
137
|
-
[FilterExpressionBrand] = true;
|
|
138
|
-
type = "binary";
|
|
139
|
-
constructor(operator, operands) {
|
|
140
|
-
this.operator = operator;
|
|
141
|
-
this.operands = operands;
|
|
142
|
-
}
|
|
143
|
-
accept(visitor) {
|
|
144
|
-
return visitor.visitBinary(this);
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
/**
|
|
148
|
-
* Internal logical expression implementation
|
|
149
|
-
* Private class - only accessible via factory functions
|
|
150
|
-
*/
|
|
151
|
-
var LogicalExpressionImpl = class {
|
|
152
|
-
[FilterExpressionBrand] = true;
|
|
153
|
-
type = "logical";
|
|
154
|
-
constructor(operator, operands) {
|
|
155
|
-
this.operator = operator;
|
|
156
|
-
this.operands = operands;
|
|
157
|
-
}
|
|
158
|
-
accept(visitor) {
|
|
159
|
-
return visitor.visitLogical(this);
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
/**
|
|
163
|
-
* Internal unary expression implementation
|
|
164
|
-
* Private class - only accessible via factory functions
|
|
165
|
-
*/
|
|
166
|
-
var UnaryExpressionImpl = class {
|
|
167
|
-
[FilterExpressionBrand] = true;
|
|
168
|
-
type = "unary";
|
|
169
|
-
constructor(operator, operands) {
|
|
170
|
-
this.operator = operator;
|
|
171
|
-
this.operands = operands;
|
|
172
|
-
}
|
|
173
|
-
accept(visitor) {
|
|
174
|
-
return visitor.visitUnary(this);
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
/**
|
|
178
|
-
* Equality operator: field == value
|
|
179
|
-
*
|
|
180
|
-
* @example
|
|
181
|
-
* const filter = eq(cols.name, 'Alice');
|
|
182
|
-
*/
|
|
183
|
-
function eq(col, value) {
|
|
184
|
-
return new BinaryExpressionImpl("eq", [fieldRef(resolveColumn(col).columnName), value]);
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Not equal operator: field != value
|
|
188
|
-
*/
|
|
189
|
-
function ne(col, value) {
|
|
190
|
-
return new BinaryExpressionImpl("ne", [fieldRef(resolveColumn(col).columnName), value]);
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Greater than operator: field > value
|
|
194
|
-
*/
|
|
195
|
-
function gt(col, value) {
|
|
196
|
-
return new BinaryExpressionImpl("gt", [fieldRef(resolveColumn(col).columnName), value]);
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Greater than or equal operator: field >= value
|
|
200
|
-
*/
|
|
201
|
-
function gte(col, value) {
|
|
202
|
-
return new BinaryExpressionImpl("gte", [fieldRef(resolveColumn(col).columnName), value]);
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Less than operator: field < value
|
|
206
|
-
*/
|
|
207
|
-
function lt(col, value) {
|
|
208
|
-
return new BinaryExpressionImpl("lt", [fieldRef(resolveColumn(col).columnName), value]);
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Less than or equal operator: field <= value
|
|
212
|
-
*/
|
|
213
|
-
function lte(col, value) {
|
|
214
|
-
return new BinaryExpressionImpl("lte", [fieldRef(resolveColumn(col).columnName), value]);
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Between operator: field BETWEEN min AND max (inclusive)
|
|
218
|
-
*
|
|
219
|
-
* Sugar for and(gte(field, min), lte(field, max)).
|
|
220
|
-
*/
|
|
221
|
-
function between(col, min, max) {
|
|
222
|
-
return and(gte(col, min), lte(col, max));
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Not between operator: field < min OR field > max
|
|
226
|
-
*
|
|
227
|
-
* Sugar for or(lt(field, min), gt(field, max)).
|
|
228
|
-
*/
|
|
229
|
-
function notBetween(col, min, max) {
|
|
230
|
-
return or(lt(col, min), gt(col, max));
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* LIKE operator: SQL-style pattern matching with % wildcards
|
|
234
|
-
* Note: Implemented as post-filter (Convex has no native LIKE)
|
|
235
|
-
*
|
|
236
|
-
* @example
|
|
237
|
-
* const users = await db.query.users.findMany({
|
|
238
|
-
* where: like(users.name, '%alice%'),
|
|
239
|
-
* });
|
|
240
|
-
*/
|
|
241
|
-
function like(col, pattern) {
|
|
242
|
-
return new BinaryExpressionImpl("like", [fieldRef(resolveColumn(col).columnName), pattern]);
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* ILIKE operator: Case-insensitive LIKE
|
|
246
|
-
* Note: Implemented as post-filter (Convex has no native LIKE)
|
|
247
|
-
*
|
|
248
|
-
* @example
|
|
249
|
-
* const users = await db.query.users.findMany({
|
|
250
|
-
* where: ilike(users.name, '%ALICE%'),
|
|
251
|
-
* });
|
|
252
|
-
*/
|
|
253
|
-
function ilike(col, pattern) {
|
|
254
|
-
return new BinaryExpressionImpl("ilike", [fieldRef(resolveColumn(col).columnName), pattern]);
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* NOT LIKE operator: Negated LIKE pattern
|
|
258
|
-
*/
|
|
259
|
-
function notLike(col, pattern) {
|
|
260
|
-
return new BinaryExpressionImpl("notLike", [fieldRef(resolveColumn(col).columnName), pattern]);
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* NOT ILIKE operator: Negated case-insensitive LIKE
|
|
264
|
-
*/
|
|
265
|
-
function notIlike(col, pattern) {
|
|
266
|
-
return new BinaryExpressionImpl("notIlike", [fieldRef(resolveColumn(col).columnName), pattern]);
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* startsWith operator: Check if string starts with prefix
|
|
270
|
-
* Optimized for prefix matching
|
|
271
|
-
*
|
|
272
|
-
* @example
|
|
273
|
-
* const users = await db.query.users.findMany({
|
|
274
|
-
* where: startsWith(users.email, 'admin@'),
|
|
275
|
-
* });
|
|
276
|
-
*/
|
|
277
|
-
function startsWith(col, prefix) {
|
|
278
|
-
return new BinaryExpressionImpl("startsWith", [fieldRef(resolveColumn(col).columnName), prefix]);
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* endsWith operator: Check if string ends with suffix
|
|
282
|
-
*
|
|
283
|
-
* @example
|
|
284
|
-
* const users = await db.query.users.findMany({
|
|
285
|
-
* where: endsWith(users.email, '@example.com'),
|
|
286
|
-
* });
|
|
287
|
-
*/
|
|
288
|
-
function endsWith(col, suffix) {
|
|
289
|
-
return new BinaryExpressionImpl("endsWith", [fieldRef(resolveColumn(col).columnName), suffix]);
|
|
290
|
-
}
|
|
291
|
-
/**
|
|
292
|
-
* contains operator: Check if string contains substring
|
|
293
|
-
* Can use search index for optimization when available
|
|
294
|
-
*
|
|
295
|
-
* @example
|
|
296
|
-
* const posts = await db.query.posts.findMany({
|
|
297
|
-
* where: contains(posts.title, 'javascript'),
|
|
298
|
-
* });
|
|
299
|
-
*/
|
|
300
|
-
function contains(col, substring) {
|
|
301
|
-
return new BinaryExpressionImpl("contains", [fieldRef(resolveColumn(col).columnName), substring]);
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Logical AND: all expressions must be true
|
|
305
|
-
* Filters out undefined expressions (following Drizzle pattern)
|
|
306
|
-
*
|
|
307
|
-
* @example
|
|
308
|
-
* const filter = and(
|
|
309
|
-
* eq(fieldRef('age'), 25),
|
|
310
|
-
* eq(fieldRef('name'), 'Alice')
|
|
311
|
-
* );
|
|
312
|
-
*/
|
|
313
|
-
function and(...expressions) {
|
|
314
|
-
const defined = expressions.filter((expr) => expr !== void 0);
|
|
315
|
-
if (defined.length === 0) return;
|
|
316
|
-
if (defined.length === 1) return defined[0];
|
|
317
|
-
return new LogicalExpressionImpl("and", defined);
|
|
318
|
-
}
|
|
319
|
-
/**
|
|
320
|
-
* Logical OR: at least one expression must be true
|
|
321
|
-
* Filters out undefined expressions (following Drizzle pattern)
|
|
322
|
-
*
|
|
323
|
-
* @example
|
|
324
|
-
* const filter = or(
|
|
325
|
-
* eq(fieldRef('status'), 'active'),
|
|
326
|
-
* eq(fieldRef('status'), 'pending')
|
|
327
|
-
* );
|
|
328
|
-
*/
|
|
329
|
-
function or(...expressions) {
|
|
330
|
-
const defined = expressions.filter((expr) => expr !== void 0);
|
|
331
|
-
if (defined.length === 0) return;
|
|
332
|
-
if (defined.length === 1) return defined[0];
|
|
333
|
-
return new LogicalExpressionImpl("or", defined);
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Logical NOT: negates expression
|
|
337
|
-
*
|
|
338
|
-
* @example
|
|
339
|
-
* const filter = not(eq(fieldRef('isDeleted'), true));
|
|
340
|
-
*/
|
|
341
|
-
function not(expression) {
|
|
342
|
-
return new UnaryExpressionImpl("not", [expression]);
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Array membership operator: field IN array
|
|
346
|
-
*
|
|
347
|
-
* @example
|
|
348
|
-
* const filter = inArray(cols.status, ['active', 'pending']);
|
|
349
|
-
*/
|
|
350
|
-
function inArray(col, values) {
|
|
351
|
-
return new BinaryExpressionImpl("inArray", [fieldRef(resolveColumn(col).columnName), values]);
|
|
352
|
-
}
|
|
353
|
-
/**
|
|
354
|
-
* Array exclusion operator: field NOT IN array
|
|
355
|
-
* Validates array is non-empty at construction time
|
|
356
|
-
*
|
|
357
|
-
* @example
|
|
358
|
-
* const filter = notInArray(cols.role, ['admin', 'moderator']);
|
|
359
|
-
*/
|
|
360
|
-
function notInArray(col, values) {
|
|
361
|
-
if (!Array.isArray(values) || values.length === 0) throw new Error("notInArray requires a non-empty array of values");
|
|
362
|
-
return new BinaryExpressionImpl("notInArray", [fieldRef(resolveColumn(col).columnName), values]);
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Array contains operator: field @> array
|
|
366
|
-
*/
|
|
367
|
-
function arrayContains(col, values) {
|
|
368
|
-
return new BinaryExpressionImpl("arrayContains", [fieldRef(resolveColumn(col).columnName), values]);
|
|
369
|
-
}
|
|
370
|
-
/**
|
|
371
|
-
* Array contained operator: field <@ array
|
|
372
|
-
*/
|
|
373
|
-
function arrayContained(col, values) {
|
|
374
|
-
return new BinaryExpressionImpl("arrayContained", [fieldRef(resolveColumn(col).columnName), values]);
|
|
375
|
-
}
|
|
376
|
-
/**
|
|
377
|
-
* Array overlaps operator: field && array
|
|
378
|
-
*/
|
|
379
|
-
function arrayOverlaps(col, values) {
|
|
380
|
-
return new BinaryExpressionImpl("arrayOverlaps", [fieldRef(resolveColumn(col).columnName), values]);
|
|
381
|
-
}
|
|
382
|
-
/**
|
|
383
|
-
* Null check operator: field IS NULL
|
|
384
|
-
* Type validation: Only works with nullable fields
|
|
385
|
-
*
|
|
386
|
-
* @example
|
|
387
|
-
* const filter = isNull(cols.deletedAt);
|
|
388
|
-
*/
|
|
389
|
-
function isNull(col) {
|
|
390
|
-
return new UnaryExpressionImpl("isNull", [fieldRef(resolveColumn(col).columnName)]);
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* Not null check operator: field IS NOT NULL
|
|
394
|
-
* Type validation: Only works with nullable fields
|
|
395
|
-
*
|
|
396
|
-
* @example
|
|
397
|
-
* const filter = isNotNull(cols.deletedAt);
|
|
398
|
-
*/
|
|
399
|
-
function isNotNull(col) {
|
|
400
|
-
return new UnaryExpressionImpl("isNotNull", [fieldRef(resolveColumn(col).columnName)]);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
//#endregion
|
|
404
3
|
//#region src/orm/unset-token.ts
|
|
405
4
|
const unsetToken = Symbol.for("kitcn/orm/unsetToken");
|
|
406
5
|
function isUnsetToken(value) {
|
|
@@ -1743,4 +1342,4 @@ async function getByIdWithOrmQueryFallback(ctx, tableName, id) {
|
|
|
1743
1342
|
}
|
|
1744
1343
|
|
|
1745
1344
|
//#endregion
|
|
1746
|
-
export {
|
|
1345
|
+
export { indexKeyWithinBounds as a, streamIndexRange as c, getIndexFields as i, isUnsetToken as l, EmptyStream as n, mergedStream as o, QueryStream as r, stream as s, getByIdWithOrmQueryFallback as t, unsetToken as u };
|
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
import { getFunctionName } from "convex/server";
|
|
2
2
|
|
|
3
3
|
//#region src/crpc/http-types.ts
|
|
4
|
+
/**
|
|
5
|
+
* Build the exact cache key for an HTTP route.
|
|
6
|
+
*
|
|
7
|
+
* Missing args normalize to `{}` so the RSC prefetch, the browser observer, and
|
|
8
|
+
* a hand-written `getQueryData` call all hash to the same key. Every producer
|
|
9
|
+
* of an `httpQuery` cache key goes through here.
|
|
10
|
+
*/
|
|
11
|
+
function buildHttpQueryKey(routeKey, args) {
|
|
12
|
+
return [
|
|
13
|
+
"httpQuery",
|
|
14
|
+
routeKey,
|
|
15
|
+
args ?? {}
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Build the route-wide prefix key.
|
|
20
|
+
*
|
|
21
|
+
* Filters match on prefix, so this matches every args variant of the route.
|
|
22
|
+
* Not a cache key: nothing is ever stored under it.
|
|
23
|
+
*/
|
|
24
|
+
function buildHttpQueryPrefixKey(routeKey) {
|
|
25
|
+
return ["httpQuery", routeKey];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Freshness window for `crpc.http.*` routes, in milliseconds.
|
|
29
|
+
*
|
|
30
|
+
* HTTP routes are a pull model with no push channel, so hydrated data needs a
|
|
31
|
+
* real freshness window or the browser refetches it on mount. The RSC
|
|
32
|
+
* QueryClient reads the same constant, so the server dedupe window and the
|
|
33
|
+
* client freshness window cannot drift apart.
|
|
34
|
+
*/
|
|
35
|
+
const HTTP_DEFAULT_STALE_TIME = 3e4;
|
|
4
36
|
/** HTTP client error */
|
|
5
37
|
var HttpClientError = class extends Error {
|
|
6
38
|
name = "HttpClientError";
|
|
@@ -244,4 +276,4 @@ function convexInfiniteQueryOptions(funcRef, args, opts = {}, meta) {
|
|
|
244
276
|
}
|
|
245
277
|
|
|
246
278
|
//#endregion
|
|
247
|
-
export { buildSearchParams as a,
|
|
279
|
+
export { buildSearchParams as a, HTTP_DEFAULT_STALE_TIME as c, buildHttpQueryPrefixKey as d, isHttpClientError as f, RESERVED_KEYS as i, HttpClientError as l, convexInfiniteQueryOptions as n, executeHttpRequest as o, convexQuery as r, replaceUrlParam as s, convexAction as t, buildHttpQueryKey as u };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { o as MiddlewareBuilder } from "../types-
|
|
2
|
-
import { t as Plugin } from "../middleware-
|
|
1
|
+
import { o as MiddlewareBuilder } from "../types-eNzQmbqM.js";
|
|
2
|
+
import { t as Plugin } from "../middleware-CE_MC6Gi.js";
|
|
3
3
|
import * as convex_server0 from "convex/server";
|
|
4
4
|
|
|
5
5
|
//#region src/ratelimit/duration.d.ts
|
|
@@ -127,6 +127,7 @@ type ConvexQueryBuilder = {
|
|
|
127
127
|
withIndex: (name: any, cb: any) => {
|
|
128
128
|
unique: () => Promise<any>;
|
|
129
129
|
collect: () => Promise<any[]>;
|
|
130
|
+
take: (limit: number) => Promise<any[]>;
|
|
130
131
|
};
|
|
131
132
|
};
|
|
132
133
|
type ConvexRatelimitDbReader = {
|
|
@@ -176,17 +177,36 @@ declare function calculateRatelimit(state: RatelimitState | null, algorithm: Res
|
|
|
176
177
|
*/
|
|
177
178
|
declare function snapshotToState(snapshot: RatelimitSnapshot): RatelimitState;
|
|
178
179
|
//#endregion
|
|
180
|
+
//#region src/ratelimit/maintenance.d.ts
|
|
181
|
+
type CleanupRatelimitStateOptions = {
|
|
182
|
+
before: number;
|
|
183
|
+
limit?: number;
|
|
184
|
+
};
|
|
185
|
+
type CleanupRatelimitStateResult = {
|
|
186
|
+
deleted: number;
|
|
187
|
+
hasMore: boolean;
|
|
188
|
+
};
|
|
189
|
+
/** Deletes one on-demand batch of state older than the caller-owned cutoff. */
|
|
190
|
+
declare function cleanupRatelimitState(db: ConvexRatelimitDbWriter, options: CleanupRatelimitStateOptions): Promise<CleanupRatelimitStateResult>;
|
|
191
|
+
//#endregion
|
|
179
192
|
//#region src/ratelimit/plugin.d.ts
|
|
180
193
|
type MaybePromise<T> = T | Promise<T>;
|
|
181
194
|
type RatelimitBuckets = Record<string, Record<string, ResolvedAlgorithm>>;
|
|
182
195
|
type BucketName<TBuckets extends RatelimitBuckets> = Extract<keyof TBuckets, string>;
|
|
183
196
|
type TierName<TBuckets extends RatelimitBuckets> = Extract<keyof TBuckets[BucketName<TBuckets>], string>;
|
|
184
|
-
|
|
197
|
+
/** What is known before request signals are resolved. */
|
|
198
|
+
type RatelimitRequestArgs<TCtx, TMeta extends object, TUser, TBuckets extends RatelimitBuckets> = {
|
|
185
199
|
ctx: TCtx;
|
|
186
200
|
meta: TMeta;
|
|
187
201
|
user: TUser;
|
|
188
202
|
bucket: BucketName<TBuckets>;
|
|
189
203
|
tier: TierName<TBuckets>;
|
|
204
|
+
};
|
|
205
|
+
/** Signals are resolved once per request and reused as the identifier input. */
|
|
206
|
+
type RatelimitIdentifierArgs<TCtx, TMeta extends object, TUser, TBuckets extends RatelimitBuckets> = RatelimitRequestArgs<TCtx, TMeta, TUser, TBuckets> & {
|
|
207
|
+
signals: LimitRequest | undefined;
|
|
208
|
+
};
|
|
209
|
+
type RatelimitResolvedArgs<TCtx, TMeta extends object, TUser, TBuckets extends RatelimitBuckets> = RatelimitIdentifierArgs<TCtx, TMeta, TUser, TBuckets> & {
|
|
190
210
|
identifier: string;
|
|
191
211
|
};
|
|
192
212
|
type RatelimitPluginOptions<TCtx = unknown, TMeta extends object = object, TUser = unknown, TBuckets extends RatelimitBuckets = RatelimitBuckets> = {
|
|
@@ -199,18 +219,34 @@ type RatelimitPluginOptions<TCtx = unknown, TMeta extends object = object, TUser
|
|
|
199
219
|
ctx: TCtx;
|
|
200
220
|
meta: TMeta;
|
|
201
221
|
}) => MaybePromise<TUser>;
|
|
202
|
-
getIdentifier: (args: {
|
|
203
|
-
ctx: TCtx;
|
|
204
|
-
meta: TMeta;
|
|
205
|
-
user: TUser;
|
|
206
|
-
bucket: BucketName<TBuckets>;
|
|
207
|
-
}) => MaybePromise<string>;
|
|
208
222
|
getTier: (user: TUser) => MaybePromise<TierName<TBuckets>>;
|
|
209
|
-
|
|
223
|
+
/**
|
|
224
|
+
* Resolved once per request, before `getIdentifier`, and reused for the limit
|
|
225
|
+
* call. Read request metadata here rather than in `getIdentifier`, so a
|
|
226
|
+
* guarded mutation pays at most one `ctx.meta.getRequestMetadata()` syscall.
|
|
227
|
+
*/
|
|
228
|
+
getSignals: (args: RatelimitRequestArgs<TCtx, TMeta, TUser, TBuckets>) => MaybePromise<LimitRequest | undefined>;
|
|
229
|
+
/**
|
|
230
|
+
* The rate-limit partition key. Everything sharing a value shares one budget
|
|
231
|
+
* and one `ratelimitState` document, so key unauthenticated traffic by
|
|
232
|
+
* `signals.ip` rather than a constant.
|
|
233
|
+
*/
|
|
234
|
+
getIdentifier: (args: RatelimitIdentifierArgs<TCtx, TMeta, TUser, TBuckets>) => MaybePromise<string>;
|
|
210
235
|
prefix?: string | ((args: RatelimitResolvedArgs<TCtx, TMeta, TUser, TBuckets>) => MaybePromise<string>);
|
|
211
236
|
failureMode?: 'closed' | 'open';
|
|
212
237
|
enableProtection?: boolean;
|
|
213
238
|
denyListThreshold?: number;
|
|
239
|
+
denyList?: RatelimitConfig['denyList'];
|
|
240
|
+
dynamicLimits?: boolean;
|
|
241
|
+
timeout?: number;
|
|
242
|
+
/**
|
|
243
|
+
* Block cache backing store. The plugin builds one `Ratelimit` per guarded
|
|
244
|
+
* mutation, so the default per-instance cache never outlives a request. Pass
|
|
245
|
+
* a longer-lived `Map` only if you accept that a stale block can deny a
|
|
246
|
+
* request a refilled shard could have served, and that a mutation replayed
|
|
247
|
+
* after an OCC conflict can observe entries its first attempt wrote.
|
|
248
|
+
*/
|
|
249
|
+
ephemeralCache?: RatelimitConfig['ephemeralCache'];
|
|
214
250
|
message?: string | ((args: RatelimitResolvedArgs<TCtx, TMeta, TUser, TBuckets>) => MaybePromise<string>);
|
|
215
251
|
};
|
|
216
252
|
type AnyRatelimitPluginOptions = RatelimitPluginOptions<any, any, any, any>;
|
|
@@ -285,4 +321,4 @@ declare const HOUR: number;
|
|
|
285
321
|
declare const DAY: number;
|
|
286
322
|
declare const WEEK: number;
|
|
287
323
|
//#endregion
|
|
288
|
-
export { type CheckRequest, type ConvexQueryBuilder, type ConvexRatelimitDbReader, type ConvexRatelimitDbWriter, DAY, type Duration, type DurationString, type DurationUnit, type DynamicLimitResponse, type EvaluationResult, type FixedWindowAlgorithm, HOUR, type HookAPIOptions, type HookCheckValue, type LimitRequest, MINUTE, RATE_LIMIT_DYNAMIC_TABLE, RATE_LIMIT_HIT_TABLE, RATE_LIMIT_STATE_TABLE, Ratelimit, type RatelimitConfig, RatelimitPlugin, type RatelimitPluginOptions, type RatelimitReason, type RatelimitResponse, type RatelimitRow, type RatelimitShardState, type RatelimitSnapshot, type RatelimitState, type RatelimitStoredState, type RemainingResponse, type ResolvedAlgorithm, SECOND, type SlidingWindowAlgorithm, type TokenBucketAlgorithm, WEEK, applyDynamicLimit, calculateRatelimit, fixedWindow, slidingWindow, snapshotToState, toMs, tokenBucket };
|
|
324
|
+
export { type CheckRequest, type CleanupRatelimitStateOptions, type CleanupRatelimitStateResult, type ConvexQueryBuilder, type ConvexRatelimitDbReader, type ConvexRatelimitDbWriter, DAY, type Duration, type DurationString, type DurationUnit, type DynamicLimitResponse, type EvaluationResult, type FixedWindowAlgorithm, HOUR, type HookAPIOptions, type HookCheckValue, type LimitRequest, MINUTE, RATE_LIMIT_DYNAMIC_TABLE, RATE_LIMIT_HIT_TABLE, RATE_LIMIT_STATE_TABLE, Ratelimit, type RatelimitConfig, RatelimitPlugin, type RatelimitPluginOptions, type RatelimitReason, type RatelimitResponse, type RatelimitRow, type RatelimitShardState, type RatelimitSnapshot, type RatelimitState, type RatelimitStoredState, type RemainingResponse, type ResolvedAlgorithm, SECOND, type SlidingWindowAlgorithm, type TokenBucketAlgorithm, WEEK, applyDynamicLimit, calculateRatelimit, cleanupRatelimitState, fixedWindow, slidingWindow, snapshotToState, toMs, tokenBucket };
|