@querykitjs/core 1.2.0 → 2.0.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/README.en.md +36 -0
- package/README.md +36 -0
- package/dist/chunk-NHNDPIPM.js +54 -0
- package/dist/chunk-NHNDPIPM.js.map +1 -0
- package/dist/chunk-VD7XT52J.cjs +72 -0
- package/dist/chunk-VD7XT52J.cjs.map +1 -0
- package/dist/index.cjs +67 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -173
- package/dist/index.d.ts +23 -173
- package/dist/index.js +4 -26
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +357 -0
- package/dist/internal.cjs.map +1 -0
- package/dist/internal.d.cts +249 -0
- package/dist/internal.d.ts +249 -0
- package/dist/internal.js +327 -0
- package/dist/internal.js.map +1 -0
- package/dist/raw-Bx8c-TrC.d.cts +290 -0
- package/dist/raw-Bx8c-TrC.d.ts +290 -0
- package/package.json +32 -6
- package/skills/querykit-core/SKILL.md +160 -0
package/dist/internal.js
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { QueryKitError, resolveLimits, isRaw } from './chunk-NHNDPIPM.js';
|
|
2
|
+
export { isRaw } from './chunk-NHNDPIPM.js';
|
|
3
|
+
|
|
4
|
+
// src/internal/cursor.ts
|
|
5
|
+
function encodeCursor(value) {
|
|
6
|
+
let normalized = value instanceof Date ? value.toISOString() : value, json = JSON.stringify({ v: normalized });
|
|
7
|
+
return Buffer.from(json, "utf8").toString("base64url");
|
|
8
|
+
}
|
|
9
|
+
function decodeCursor(cursor) {
|
|
10
|
+
if (cursor)
|
|
11
|
+
try {
|
|
12
|
+
let json = Buffer.from(cursor, "base64url").toString("utf8");
|
|
13
|
+
return JSON.parse(json).v;
|
|
14
|
+
} catch {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
var isScalar = (value) => typeof value == "string" || typeof value == "boolean" || typeof value == "number" && Number.isFinite(value), scalarOf = (value) => {
|
|
19
|
+
let normalized = value instanceof Date ? value.toISOString() : typeof value == "object" && value !== null && typeof value.toJSON == "function" ? value.toJSON() : value;
|
|
20
|
+
return isScalar(normalized) ? normalized : void 0;
|
|
21
|
+
};
|
|
22
|
+
async function sign(body, secret) {
|
|
23
|
+
let encoder = new TextEncoder(), key = await crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]), signature = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
|
|
24
|
+
return Buffer.from(signature).toString("base64url");
|
|
25
|
+
}
|
|
26
|
+
function sameSignature(a, b) {
|
|
27
|
+
if (a.length !== b.length) return false;
|
|
28
|
+
let diff = 0;
|
|
29
|
+
for (let index = 0; index < a.length; index++) diff |= a.charCodeAt(index) ^ b.charCodeAt(index);
|
|
30
|
+
return diff === 0;
|
|
31
|
+
}
|
|
32
|
+
async function encodeCursorToken(payload, secret) {
|
|
33
|
+
let body = JSON.stringify({ v: 2, k: scalarOf(payload.k) ?? null, id: scalarOf(payload.id) ?? null }), signed = secret ? JSON.stringify({ ...JSON.parse(body), s: await sign(body, secret) }) : body;
|
|
34
|
+
return Buffer.from(signed, "utf8").toString("base64url");
|
|
35
|
+
}
|
|
36
|
+
async function decodeCursorToken(token, options) {
|
|
37
|
+
if (!token) return;
|
|
38
|
+
let refuse = () => {
|
|
39
|
+
throw new QueryKitError({ source: options.source, site: "cursorKey", key: options.key ?? "cursor", reason: "invalid-value" });
|
|
40
|
+
}, payload;
|
|
41
|
+
try {
|
|
42
|
+
payload = JSON.parse(Buffer.from(token, "base64url").toString("utf8"));
|
|
43
|
+
} catch {
|
|
44
|
+
return refuse();
|
|
45
|
+
}
|
|
46
|
+
if (typeof payload != "object" || payload === null || payload.v !== 2 || !isScalar(payload.k) || !isScalar(payload.id)) return refuse();
|
|
47
|
+
if (options.secret) {
|
|
48
|
+
let body = JSON.stringify({ v: 2, k: payload.k, id: payload.id });
|
|
49
|
+
if (typeof payload.s != "string" || !sameSignature(payload.s, await sign(body, options.secret))) return refuse();
|
|
50
|
+
}
|
|
51
|
+
return { k: payload.k, id: payload.id };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/internal/diagnostics.ts
|
|
55
|
+
function reportSkip(diag, info) {
|
|
56
|
+
if (!diag) return;
|
|
57
|
+
let full = { ...info, source: diag.source };
|
|
58
|
+
if (diag.strict) throw new QueryKitError(full);
|
|
59
|
+
diag.onSkipped?.(full);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/internal/context.ts
|
|
63
|
+
function loadAsyncLocalStorage() {
|
|
64
|
+
let asyncHooks = typeof process < "u" && typeof process.getBuiltinModule == "function" ? process.getBuiltinModule("node:async_hooks") : void 0;
|
|
65
|
+
if (!asyncHooks?.AsyncLocalStorage)
|
|
66
|
+
throw new Error("querykit: the repository context needs node:async_hooks (Node >= 22.12 or Bun) \u2014 a querykit adapter cannot run in this runtime");
|
|
67
|
+
return asyncHooks.AsyncLocalStorage;
|
|
68
|
+
}
|
|
69
|
+
function createContextStore() {
|
|
70
|
+
let AsyncLocalStorage = loadAsyncLocalStorage(), als = new AsyncLocalStorage();
|
|
71
|
+
return {
|
|
72
|
+
get: () => als.getStore(),
|
|
73
|
+
run: (patch, fn) => als.run({ ...als.getStore(), ...patch }, fn)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/internal/sort.ts
|
|
78
|
+
function normalizeSort(sort, diag) {
|
|
79
|
+
if (!sort) return [];
|
|
80
|
+
let items = [];
|
|
81
|
+
for (let item of sort)
|
|
82
|
+
item && typeof item.key == "string" && item.key ? items.push({ key: item.key, direction: item.direction ?? "asc" }) : reportSkip(diag, { site: "sort", key: "", reason: "invalid-value" });
|
|
83
|
+
return items;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/internal/pagination.ts
|
|
87
|
+
function clampPageSize(requested, fallback, max) {
|
|
88
|
+
return Math.min(max, Math.max(1, Math.trunc(requested ?? fallback)));
|
|
89
|
+
}
|
|
90
|
+
function clampPage(requested) {
|
|
91
|
+
return Math.max(1, Math.trunc(requested ?? 1));
|
|
92
|
+
}
|
|
93
|
+
function clampOffset(requested) {
|
|
94
|
+
return Math.max(0, Math.trunc(requested ?? 0));
|
|
95
|
+
}
|
|
96
|
+
function offsetMeta(page, perPage, totalItems) {
|
|
97
|
+
let totalPages = Math.ceil(totalItems / perPage);
|
|
98
|
+
return {
|
|
99
|
+
total_items: totalItems,
|
|
100
|
+
total_pages: totalPages,
|
|
101
|
+
current_page: page,
|
|
102
|
+
per_page: perPage,
|
|
103
|
+
has_next: page < totalPages,
|
|
104
|
+
has_prev: page > 1
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function offsetPageWithoutTotal(rows, page, perPage) {
|
|
108
|
+
let hasNext = rows.length > perPage;
|
|
109
|
+
return {
|
|
110
|
+
data: hasNext ? rows.slice(0, perPage) : [...rows],
|
|
111
|
+
meta: { total_items: null, total_pages: null, current_page: page, per_page: perPage, has_next: hasNext, has_prev: page > 1 }
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function infinitePage(rows, limit, offset) {
|
|
115
|
+
let hasMore = rows.length > limit, data = hasMore ? rows.slice(0, limit) : [...rows];
|
|
116
|
+
return {
|
|
117
|
+
data,
|
|
118
|
+
meta: { limit, offset, count: data.length, has_more: hasMore, next_offset: hasMore ? offset + limit : null }
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
async function cursorPage(rows, options) {
|
|
122
|
+
let { limit, direction, hasCursor, cursorField, idField, secret } = options, hasExtra = rows.length > limit, data = hasExtra ? rows.slice(0, limit) : [...rows];
|
|
123
|
+
direction === "backward" && (data = data.reverse());
|
|
124
|
+
let first = data[0], last = data[data.length - 1], hasNext = direction === "forward" ? hasExtra : hasCursor, hasPrev = direction === "forward" ? hasCursor : hasExtra;
|
|
125
|
+
return {
|
|
126
|
+
data,
|
|
127
|
+
meta: {
|
|
128
|
+
limit,
|
|
129
|
+
has_next: hasNext,
|
|
130
|
+
has_prev: hasPrev,
|
|
131
|
+
next_cursor: hasNext && last ? await encodeCursorToken({ k: last[cursorField], id: last[idField] }, secret) : null,
|
|
132
|
+
prev_cursor: hasPrev && first ? await encodeCursorToken({ k: first[cursorField], id: first[idField] }, secret) : null
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// src/internal/mutation.ts
|
|
138
|
+
var isPlainObject = (value) => typeof value == "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
139
|
+
function isAllRows(filter) {
|
|
140
|
+
return isPlainObject(filter) && filter.all === true && Object.keys(filter).length === 1;
|
|
141
|
+
}
|
|
142
|
+
function isEmptyFilter(filter) {
|
|
143
|
+
return filter == null || Array.isArray(filter) && filter.length === 0 || isPlainObject(filter) && Object.keys(filter).length === 0;
|
|
144
|
+
}
|
|
145
|
+
function refuseEmptyFilter(source, method) {
|
|
146
|
+
throw new QueryKitError({ source, site: "mutation", key: method, reason: "empty-filter" });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/internal/scope.ts
|
|
150
|
+
var isPlainObject2 = (value) => typeof value == "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype, isInScope = (value) => isPlainObject2(value) && Object.keys(value).length === 1 && Array.isArray(value.in);
|
|
151
|
+
function scopeConditions(scope) {
|
|
152
|
+
return Object.entries(scope ?? {}).map(
|
|
153
|
+
([key, value]) => isInScope(value) ? { key, operation: "in", value: [...value.in] } : { key, operation: "=", value }
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
function scopeDefaults(scope) {
|
|
157
|
+
return Object.fromEntries(Object.entries(scope ?? {}).filter(([, value]) => !isInScope(value)));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/internal/with.ts
|
|
161
|
+
var isPlainObject3 = (value) => typeof value == "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype, ruleOf = (relations, name) => {
|
|
162
|
+
let rule = relations && Object.prototype.hasOwnProperty.call(relations, name) ? relations[name] : void 0;
|
|
163
|
+
return isPlainObject3(rule) && (rule.columns === "*" || Array.isArray(rule.columns)) ? rule : void 0;
|
|
164
|
+
}, selectedKeys = (selection) => {
|
|
165
|
+
if (typeof selection == "string") return selection.split(/\s+/).filter(Boolean);
|
|
166
|
+
if (isPlainObject3(selection)) return Object.keys(selection).filter((key) => !!selection[key]);
|
|
167
|
+
};
|
|
168
|
+
function checkWith(withValue, relations, dialect, source, path = "") {
|
|
169
|
+
if (withValue == null) return;
|
|
170
|
+
let refuse = (key) => {
|
|
171
|
+
throw new QueryKitError({ source, site: "with", key: path + key, reason: "forbidden-key" });
|
|
172
|
+
};
|
|
173
|
+
if (!isPlainObject3(withValue)) return refuse("with");
|
|
174
|
+
let checked = {};
|
|
175
|
+
for (let [name, value] of Object.entries(withValue)) {
|
|
176
|
+
if (value === false || value === void 0) continue;
|
|
177
|
+
let rule = ruleOf(relations, name) ?? refuse(name), listed = rule.columns === "*" ? void 0 : new Set(rule.columns), selectListed = () => Object.fromEntries([...listed].map((column) => [column, true]));
|
|
178
|
+
if (value === true) {
|
|
179
|
+
checked[name] = listed ? { [dialect.columns]: selectListed() } : true;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
isPlainObject3(value) || refuse(name);
|
|
183
|
+
let options = value, out = {};
|
|
184
|
+
for (let [key, option] of Object.entries(options)) {
|
|
185
|
+
let at = `${name}.${key}`;
|
|
186
|
+
if (key === dialect.columns) {
|
|
187
|
+
let keys = selectedKeys(option) ?? refuse(at);
|
|
188
|
+
for (let column of keys) listed && (column.startsWith("-") || !listed.has(column)) && refuse(`${name}.${column}`);
|
|
189
|
+
out[key] = option;
|
|
190
|
+
} else if (key === dialect.nested)
|
|
191
|
+
out[key] = checkWith(option, rule.nested, dialect, source, `${name}.`);
|
|
192
|
+
else if (dialect.filters.includes(key))
|
|
193
|
+
checkFilterFields(option, rule, dialect, (field) => refuse(`${name}.${field}`)), out[key] = option;
|
|
194
|
+
else if (dialect.paging.includes(key))
|
|
195
|
+
out[key] = option;
|
|
196
|
+
else if (key === dialect.orderBy) {
|
|
197
|
+
for (let item of Array.isArray(option) ? option : [option])
|
|
198
|
+
for (let column of selectedKeys(item) ?? refuse(at)) listed && !listed.has(column) && refuse(`${name}.${column}`);
|
|
199
|
+
out[key] = option;
|
|
200
|
+
} else if (key === dialect.options) {
|
|
201
|
+
for (let sub of Object.keys(isPlainObject3(option) ? option : refuse(at))) dialect.paging.includes(sub) || refuse(`${at}.${sub}`);
|
|
202
|
+
out[key] = option;
|
|
203
|
+
} else
|
|
204
|
+
refuse(at);
|
|
205
|
+
}
|
|
206
|
+
listed && out[dialect.columns] === void 0 && (out[dialect.columns] = selectListed()), checked[name] = out;
|
|
207
|
+
}
|
|
208
|
+
return checked;
|
|
209
|
+
}
|
|
210
|
+
function checkFilterFields(filter, rule, dialect, refuse) {
|
|
211
|
+
isPlainObject3(filter) || refuse("filter");
|
|
212
|
+
for (let [key, value] of Object.entries(filter))
|
|
213
|
+
if (dialect.logical.includes(key))
|
|
214
|
+
for (let part of Array.isArray(value) ? value : [value]) checkFilterFields(part, rule, dialect, refuse);
|
|
215
|
+
else (key.startsWith("$") || !rule.filterable || rule.filterable[key] === void 0) && refuse(key);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/internal/limits.ts
|
|
219
|
+
var isRecord = (value) => typeof value == "object" && value !== null && !Array.isArray(value), formatLimit = (limit, max) => `${limit} (${max})`;
|
|
220
|
+
function findFilterLimitViolation(filter, limits) {
|
|
221
|
+
let hit = (limit, path) => ({ limit, max: limits[limit], path }), pending = [], push = (children, depth, path) => {
|
|
222
|
+
for (let index = children.length - 1; index >= 0; index--) pending.push([children[index], depth, [...path, index]]);
|
|
223
|
+
};
|
|
224
|
+
Array.isArray(filter) ? push(filter, 1, []) : filter !== void 0 && pending.push([filter, 1, []]);
|
|
225
|
+
let conditions = 0;
|
|
226
|
+
for (let next = pending.pop(); next; next = pending.pop()) {
|
|
227
|
+
let [node, depth, path] = next;
|
|
228
|
+
if (depth > limits.maxDepth) return hit("maxDepth", path);
|
|
229
|
+
if (!isRecord(node)) continue;
|
|
230
|
+
if ("key" in node) {
|
|
231
|
+
if (++conditions > limits.maxConditions) return hit("maxConditions", path);
|
|
232
|
+
let { value } = node, valuePath = [...path, "value"];
|
|
233
|
+
if (Array.isArray(value) && value.length > limits.maxInItems) return hit("maxInItems", valuePath);
|
|
234
|
+
let tooLong = (Array.isArray(value) ? value : [value]).findIndex((item) => typeof item == "string" && item.length > limits.maxStringLength);
|
|
235
|
+
if (tooLong >= 0) return hit("maxStringLength", Array.isArray(value) ? [...valuePath, tooLong] : valuePath);
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
let group = "and" in node ? "and" : "or" in node ? "or" : void 0;
|
|
239
|
+
if (group) {
|
|
240
|
+
let children = node[group];
|
|
241
|
+
Array.isArray(children) && push(children, depth + 1, [...path, group]);
|
|
242
|
+
} else "not" in node && pending.push([node.not, depth + 1, [...path, "not"]]);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
var LIKE_OPERATORS = /* @__PURE__ */ new Set(["like", "ilike", "notLike"]), isLikeOperator = (operation) => LIKE_OPERATORS.has(operation);
|
|
246
|
+
function likeWildcards(pattern) {
|
|
247
|
+
let count = 0, previous = "";
|
|
248
|
+
for (let index = 0; index < pattern.length; index++) {
|
|
249
|
+
let char = pattern[index];
|
|
250
|
+
if (char === "\\") {
|
|
251
|
+
index++, previous = "";
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
(char === "_" || char === "%" && previous !== "%") && count++, previous = char;
|
|
255
|
+
}
|
|
256
|
+
return count;
|
|
257
|
+
}
|
|
258
|
+
var offsetOf = (page, perPage) => ((page ?? 1) - 1) * (perPage ?? 20);
|
|
259
|
+
|
|
260
|
+
// src/internal/policy.ts
|
|
261
|
+
var DEFAULT_OPERATORS = ["=", "in"], OPERATOR_TOKEN = {
|
|
262
|
+
eq: "=",
|
|
263
|
+
ne: "!=",
|
|
264
|
+
gt: ">",
|
|
265
|
+
gte: ">=",
|
|
266
|
+
lt: "<",
|
|
267
|
+
lte: "<=",
|
|
268
|
+
contains: "%_%",
|
|
269
|
+
startsWith: "%_",
|
|
270
|
+
endsWith: "_%"
|
|
271
|
+
}, tokenOf = (operation) => OPERATOR_TOKEN[operation] ?? operation, isRecord2 = (value) => typeof value == "object" && value !== null && !Array.isArray(value);
|
|
272
|
+
function filterRules(options, canonical) {
|
|
273
|
+
let rules = /* @__PURE__ */ new Map();
|
|
274
|
+
if (options.filterable) {
|
|
275
|
+
for (let [key, operators] of Object.entries(options.filterable))
|
|
276
|
+
operators && rules.set(canonical(key), operators === "*" ? "*" : new Set(operators.map(tokenOf)));
|
|
277
|
+
return rules;
|
|
278
|
+
}
|
|
279
|
+
let fields = options.allowedColumns ?? (options.forcedColumns ? Object.keys(options.forcedColumns).filter((key) => options.forcedColumns[key]) : [options.primaryKey]), equality = new Set(DEFAULT_OPERATORS);
|
|
280
|
+
for (let field of fields) rules.set(canonical(field), equality);
|
|
281
|
+
return rules;
|
|
282
|
+
}
|
|
283
|
+
function resolvePolicy(options) {
|
|
284
|
+
let limits = resolveLimits(options.limits), canonical = (key) => options.fieldOf(key) ?? key, rules = filterRules(options, canonical), sortable = new Set((options.sortable ?? [options.primaryKey, ...options.createdAt ? [options.createdAt] : []]).map(canonical)), refuse = (site, key, reason, operation) => {
|
|
285
|
+
throw new QueryKitError({ source: options.source, site, key, reason, ...operation ? { operation } : {} });
|
|
286
|
+
}, checkCondition = (node) => {
|
|
287
|
+
let key = String(node.key), field = options.fieldOf(key);
|
|
288
|
+
if (field === void 0) return;
|
|
289
|
+
let operation = typeof node.operation == "string" ? node.operation : "=", rule = rules.get(field);
|
|
290
|
+
rule || refuse("filter", key, "forbidden-key", operation), rule !== "*" && !rule.has(tokenOf(operation)) && refuse("filter", key, "forbidden-operator", operation), isLikeOperator(operation) && typeof node.value == "string" && likeWildcards(node.value) > limits.maxLikeWildcards && refuse("filter", "maxLikeWildcards", "limit-exceeded", operation);
|
|
291
|
+
}, checkSortKey = (site, key) => {
|
|
292
|
+
let field = options.fieldOf(key);
|
|
293
|
+
field !== void 0 && !sortable.has(field) && refuse(site, key, "forbidden-key");
|
|
294
|
+
};
|
|
295
|
+
return {
|
|
296
|
+
limits,
|
|
297
|
+
checkFilter(filter) {
|
|
298
|
+
let violation = findFilterLimitViolation(filter, limits);
|
|
299
|
+
violation && refuse("filter", violation.limit, "limit-exceeded");
|
|
300
|
+
let pending = Array.isArray(filter) ? [...filter] : [filter];
|
|
301
|
+
for (; pending.length > 0; ) {
|
|
302
|
+
let node = pending.pop();
|
|
303
|
+
isRecord2(node) && ("key" in node ? checkCondition(node) : Array.isArray(node.and) ? pending.push(...node.and) : Array.isArray(node.or) ? pending.push(...node.or) : "not" in node ? pending.push(node.not) : !isRaw(node) && !options.isNative?.(node) && refuse("filter", Object.keys(node)[0] ?? "", "forbidden-key"));
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
checkSort(sort) {
|
|
307
|
+
if (sort) {
|
|
308
|
+
sort.length > limits.maxSortFields && refuse("sort", "maxSortFields", "limit-exceeded");
|
|
309
|
+
for (let item of sort) item && typeof item.key == "string" && item.key && checkSortKey("sort", item.key);
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
checkCursorKey(key) {
|
|
313
|
+
checkSortKey("cursorKey", key);
|
|
314
|
+
},
|
|
315
|
+
checkPage(page) {
|
|
316
|
+
("offset" in page ? page.offset : offsetOf(page.page, page.perPage)) > limits.maxOffset && refuse("page", "maxOffset", "limit-exceeded");
|
|
317
|
+
},
|
|
318
|
+
checkAggregateKey(key) {
|
|
319
|
+
let field = options.fieldOf(key);
|
|
320
|
+
field !== void 0 && !rules.has(field) && refuse("aggregate", key, "forbidden-key");
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export { checkWith, clampOffset, clampPage, clampPageSize, createContextStore, cursorPage, decodeCursor, decodeCursorToken, encodeCursor, encodeCursorToken, findFilterLimitViolation, formatLimit, infinitePage, isAllRows, isEmptyFilter, isLikeOperator, likeWildcards, normalizeSort, offsetMeta, offsetOf, offsetPageWithoutTotal, refuseEmptyFilter, reportSkip, resolvePolicy, scopeConditions, scopeDefaults };
|
|
326
|
+
//# sourceMappingURL=internal.js.map
|
|
327
|
+
//# sourceMappingURL=internal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/cursor.ts","../src/internal/diagnostics.ts","../src/internal/context.ts","../src/internal/sort.ts","../src/internal/pagination.ts","../src/internal/mutation.ts","../src/internal/scope.ts","../src/internal/with.ts","../src/internal/limits.ts","../src/internal/policy.ts"],"names":["isPlainObject","isRecord"],"mappings":"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","file":"internal.js","sourcesContent":["/**\n * Opaque cursor tokens for keyset pagination, shared by every adapter so a token\n * is interchangeable between them. A token is the base64url encoding of\n * `{ v: <cursor value> }`; dates travel as ISO strings.\n */\n\nimport { QueryKitError } from \"../errors\";\n\ninterface CursorPayload {\n v: unknown;\n}\n\nexport function encodeCursor(value: unknown): string {\n const normalized = value instanceof Date ? value.toISOString() : value;\n const json = JSON.stringify({ v: normalized } satisfies CursorPayload);\n return Buffer.from(json, \"utf8\").toString(\"base64url\");\n}\n\n/** The cursor value, or `undefined` for a missing or undecodable token. */\nexport function decodeCursor(cursor?: string | null): unknown | undefined {\n if (!cursor) return undefined;\n try {\n const json = Buffer.from(cursor, \"base64url\").toString(\"utf8\");\n const payload = JSON.parse(json) as CursorPayload;\n return payload.v;\n } catch {\n return undefined;\n }\n}\n\n/* ------------------------------- token v2 -------------------------------- */\n/* Task 012: `{ v: 2, k, id, s? }` — the cursor key's value and the primary key\n * (tie-break), both scalars; `s` is an HMAC-SHA256 signature when the registry\n * has a `cursorSecret`. Anything else is invalid-value on site cursorKey. */\n\nexport interface CursorToken {\n k: string | number | boolean;\n id: string | number | boolean;\n}\n\nconst isScalar = (value: unknown): value is CursorToken[\"k\"] =>\n typeof value === \"string\" || typeof value === \"boolean\" || (typeof value === \"number\" && Number.isFinite(value));\n\nconst scalarOf = (value: unknown): CursorToken[\"k\"] | undefined => {\n // A Date becomes its ISO string; a driver id (a Mongo ObjectId) its own JSON form (hex).\n const normalized =\n value instanceof Date\n ? value.toISOString()\n : typeof value === \"object\" && value !== null && typeof (value as { toJSON?: unknown }).toJSON === \"function\"\n ? (value as { toJSON: () => unknown }).toJSON()\n : value;\n return isScalar(normalized) ? normalized : undefined;\n};\n\nasync function sign(body: string, secret: string): Promise<string> {\n const encoder = new TextEncoder();\n const key = await crypto.subtle.importKey(\"raw\", encoder.encode(secret), { name: \"HMAC\", hash: \"SHA-256\" }, false, [\"sign\"]);\n const signature = await crypto.subtle.sign(\"HMAC\", key, encoder.encode(body));\n return Buffer.from(signature).toString(\"base64url\");\n}\n\n/** Constant-time compare, so a forged signature cannot be found byte by byte. */\nfunction sameSignature(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let index = 0; index < a.length; index++) diff |= a.charCodeAt(index) ^ b.charCodeAt(index);\n return diff === 0;\n}\n\n/** A v2 token for a row's cursor value and primary key; signed when `secret` is given. */\nexport async function encodeCursorToken(payload: { k: unknown; id: unknown }, secret?: string): Promise<string> {\n const body = JSON.stringify({ v: 2, k: scalarOf(payload.k) ?? null, id: scalarOf(payload.id) ?? null });\n const signed = secret ? JSON.stringify({ ...JSON.parse(body), s: await sign(body, secret) }) : body;\n return Buffer.from(signed, \"utf8\").toString(\"base64url\");\n}\n\n/**\n * The `{ k, id }` of a v2 token, `undefined` for no token. A v1 token, a\n * non-scalar `k` / `id`, a missing or wrong signature (with `secret`) or\n * anything undecodable throws `QueryKitError` — invalid-value, site cursorKey,\n * `key` the cursor key in use (the same key as an adapter's cast error).\n */\nexport async function decodeCursorToken(\n token: string | null | undefined,\n options: { source: string; secret?: string; key?: string },\n): Promise<CursorToken | undefined> {\n if (!token) return undefined;\n const refuse = (): never => {\n throw new QueryKitError({ source: options.source, site: \"cursorKey\", key: options.key ?? \"cursor\", reason: \"invalid-value\" });\n };\n let payload: Record<string, unknown>;\n try {\n payload = JSON.parse(Buffer.from(token, \"base64url\").toString(\"utf8\")) as Record<string, unknown>;\n } catch {\n return refuse();\n }\n if (typeof payload !== \"object\" || payload === null || payload.v !== 2 || !isScalar(payload.k) || !isScalar(payload.id)) return refuse();\n if (options.secret) {\n const body = JSON.stringify({ v: 2, k: payload.k, id: payload.id });\n if (typeof payload.s !== \"string\" || !sameSignature(payload.s, await sign(body, options.secret))) return refuse();\n }\n return { k: payload.k, id: payload.id };\n}\n","/**\n * Skip diagnostics shared by every adapter: a condition the adapter cannot apply\n * (unknown key, uncastable value) is reported through `onSkippedCondition`, or —\n * under `strict` — thrown as a `QueryKitError`, so all adapters fail the same way.\n */\nimport { QueryKitError } from \"../errors\";\nimport type { SkippedCondition } from \"../types\";\n\nexport interface Diagnostics {\n /** Table (drizzle), model (prisma, mongoose) — reported as `source`. */\n source: string;\n strict: boolean;\n onSkipped?: (info: SkippedCondition) => void;\n}\n\nexport function reportSkip(diag: Diagnostics | undefined, info: Omit<SkippedCondition, \"source\">): void {\n if (!diag) return;\n const full: SkippedCondition = { ...info, source: diag.source };\n if (diag.strict) throw new QueryKitError(full);\n diag.onSkipped?.(full);\n}\n","/**\n * Per-async-call repository context (the transaction executor or session), so a\n * repository called inside `registry.transaction(...)` joins the transaction\n * without threading it through every call. Each adapter supplies its own context\n * shape (`{ executor }` for SQL clients, `{ session }` for mongoose).\n *\n * `node:async_hooks` is loaded when a store is created (adapters, on the server),\n * not imported statically: the validators also import `@querykitjs/core/internal`\n * for the limit helpers, and a static import of a node built-in would stop a\n * browser bundle of `@querykitjs/zod` / `@querykitjs/class-validator` (task 016).\n */\nimport type { AsyncLocalStorage as AsyncLocalStorageType } from \"node:async_hooks\";\n\nexport interface ContextStore<TContext extends object> {\n get(): TContext | undefined;\n /** Run `fn` with `patch` merged over the current context. */\n run<T>(patch: TContext, fn: () => T): T;\n}\n\n/** Node ≥ 22.3 and Bun (`engines` ≥ 22.12); anywhere else the adapter cannot start. */\nfunction loadAsyncLocalStorage(): typeof AsyncLocalStorageType {\n const asyncHooks =\n typeof process !== \"undefined\" && typeof process.getBuiltinModule === \"function\" ? process.getBuiltinModule(\"node:async_hooks\") : undefined;\n if (!asyncHooks?.AsyncLocalStorage)\n throw new Error(\"querykit: the repository context needs node:async_hooks (Node >= 22.12 or Bun) — a querykit adapter cannot run in this runtime\");\n return asyncHooks.AsyncLocalStorage;\n}\n\nexport function createContextStore<TContext extends object>(): ContextStore<TContext> {\n const AsyncLocalStorage = loadAsyncLocalStorage();\n const als = new AsyncLocalStorage<TContext>();\n return {\n get: () => als.getStore(),\n run: (patch, fn) => als.run({ ...als.getStore(), ...patch }, fn),\n };\n}\n","import type { Sort, SortDirection } from \"../types\";\n\nimport { reportSkip, type Diagnostics } from \"./diagnostics\";\n\nexport interface NormalizedSortItem {\n key: string;\n direction: SortDirection;\n}\n\n/**\n * The wire `sort` array with a direction on every item. An item without a `key`\n * (`null`, `{}`) is dropped — sort comes from the client, and a malformed item\n * must not crash the query — and, with `diag`, reported as `invalid-value` on\n * `site: \"sort\"` (a `QueryKitError` under strict; task 021 B14).\n */\nexport function normalizeSort(sort?: Sort, diag?: Diagnostics): NormalizedSortItem[] {\n if (!sort) return [];\n const items: NormalizedSortItem[] = [];\n for (const item of sort) {\n if (item && typeof item.key === \"string\" && item.key) items.push({ key: item.key, direction: item.direction ?? \"asc\" });\n else reportSkip(diag, { site: \"sort\", key: \"\", reason: \"invalid-value\" });\n }\n return items;\n}\n","/**\n * Page-size clamping and the three pagination metas, computed once for every\n * adapter so `total_pages`, `has_more`, `next_cursor` … mean the same thing on\n * all of them. Adapters only fetch rows; these functions turn rows into a page.\n */\nimport type { CursorMeta, InfiniteMeta, OffsetMeta } from \"../types\";\n\nimport { encodeCursorToken } from \"./cursor\";\n\n/** A requested page size, defaulted, truncated, at least 1 and at most `max`. */\nexport function clampPageSize(requested: number | undefined, fallback: number, max: number): number {\n return Math.min(max, Math.max(1, Math.trunc(requested ?? fallback)));\n}\n\n/** A requested page number: an integer, at least 1. */\nexport function clampPage(requested: number | undefined): number {\n return Math.max(1, Math.trunc(requested ?? 1));\n}\n\n/** A requested offset: an integer, at least 0. */\nexport function clampOffset(requested: number | undefined): number {\n return Math.max(0, Math.trunc(requested ?? 0));\n}\n\nexport function offsetMeta(page: number, perPage: number, totalItems: number): OffsetMeta {\n const totalPages = Math.ceil(totalItems / perPage);\n return {\n total_items: totalItems,\n total_pages: totalPages,\n current_page: page,\n per_page: perPage,\n has_next: page < totalPages,\n has_prev: page > 1,\n };\n}\n\n/**\n * `withTotal: false` (task 013): rows fetched with `perPage + 1` and no count →\n * the page and its meta; the extra row only signals `has_next`, totals are `null`.\n */\nexport function offsetPageWithoutTotal<T>(rows: readonly T[], page: number, perPage: number): { data: T[]; meta: OffsetMeta } {\n const hasNext = rows.length > perPage;\n return {\n data: hasNext ? rows.slice(0, perPage) : [...rows],\n meta: { total_items: null, total_pages: null, current_page: page, per_page: perPage, has_next: hasNext, has_prev: page > 1 },\n };\n}\n\n/** Rows fetched with `limit + 1` → the page and its meta (the extra row only signals `has_more`). */\nexport function infinitePage<T>(rows: readonly T[], limit: number, offset: number): { data: T[]; meta: InfiniteMeta } {\n const hasMore = rows.length > limit;\n const data = hasMore ? rows.slice(0, limit) : [...rows];\n return {\n data,\n meta: { limit, offset, count: data.length, has_more: hasMore, next_offset: hasMore ? offset + limit : null },\n };\n}\n\nexport interface CursorPageOptions {\n limit: number;\n direction: \"forward\" | \"backward\";\n /** A cursor was supplied (the request continues from a previous page). */\n hasCursor: boolean;\n /** The row field the tokens are read from. */\n cursorField: string;\n /** The primary-key field — the tie-break value in every token (task 012). */\n idField: string;\n /** Signs the tokens (registry `cursorSecret`). */\n secret?: string;\n}\n\n/**\n * Rows fetched with `limit + 1` in query order → the page in display order and\n * its meta. A backward page is fetched reversed and flipped back here.\n */\nexport async function cursorPage<T extends Record<string, unknown>>(rows: readonly T[], options: CursorPageOptions): Promise<{ data: T[]; meta: CursorMeta }> {\n const { limit, direction, hasCursor, cursorField, idField, secret } = options;\n const hasExtra = rows.length > limit;\n let data = hasExtra ? rows.slice(0, limit) : [...rows];\n if (direction === \"backward\") data = data.reverse();\n\n const first = data[0];\n const last = data[data.length - 1];\n const hasNext = direction === \"forward\" ? hasExtra : hasCursor;\n const hasPrev = direction === \"forward\" ? hasCursor : hasExtra;\n\n return {\n data,\n meta: {\n limit,\n has_next: hasNext,\n has_prev: hasPrev,\n next_cursor: hasNext && last ? await encodeCursorToken({ k: last[cursorField], id: last[idField] }, secret) : null,\n prev_cursor: hasPrev && first ? await encodeCursorToken({ k: first[cursorField], id: first[idField] }, secret) : null,\n },\n };\n}\n","/**\n * Bulk-mutation guards shared by every adapter (task 013): a filterless\n * `updateWhere` / `deleteWhere` runs only on an explicit `{ all: true }`.\n */\nimport { QueryKitError } from \"../errors\";\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;\n\n/** `{ all: true }` — and nothing else — asks for every row (the scope still applies). */\nexport function isAllRows(filter: unknown): boolean {\n return isPlainObject(filter) && filter.all === true && Object.keys(filter).length === 1;\n}\n\n/** `undefined`, `null`, `[]` or `{}`: no condition at all. */\nexport function isEmptyFilter(filter: unknown): boolean {\n return (\n filter === undefined || filter === null || (Array.isArray(filter) && filter.length === 0) || (isPlainObject(filter) && Object.keys(filter).length === 0)\n );\n}\n\n/** A mutation without a condition — also one whose every condition was skipped. */\nexport function refuseEmptyFilter(source: string, method: \"updateWhere\" | \"deleteWhere\"): never {\n throw new QueryKitError({ source, site: \"mutation\", key: method, reason: \"empty-filter\" });\n}\n","/**\n * A repository `scope` (the server's RBAC / tenancy filter) split once for every\n * adapter (task 014): equality values and `{ in: [...] }` lists.\n */\n\n/** One scope condition, in the shape every adapter's `buildWhere` reads. */\nexport interface ScopeCondition {\n key: string;\n operation: \"=\" | \"in\";\n value: unknown;\n}\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;\n\n/** `{ in: [...] }` — and nothing else — is a list scope; any other value is equality (as before). */\nconst isInScope = (value: unknown): value is { in: readonly unknown[] } => isPlainObject(value) && Object.keys(value).length === 1 && Array.isArray(value.in);\n\n/** The scope as conditions: `{ k: v }` → `k = v`, `{ k: { in: [...] } }` → `k in [...]`. */\nexport function scopeConditions(scope: Record<string, unknown> | undefined): ScopeCondition[] {\n return Object.entries(scope ?? {}).map(([key, value]) =>\n isInScope(value) ? { key, operation: \"in\", value: [...value.in] } : { key, operation: \"=\", value },\n );\n}\n\n/** The values `create` / `upsert` fill in: the equality scopes only (a list has no single value). */\nexport function scopeDefaults(scope: Record<string, unknown> | undefined): Record<string, unknown> {\n return Object.fromEntries(Object.entries(scope ?? {}).filter(([, value]) => !isInScope(value)));\n}\n","/**\n * The client's `with` against the repository's `relations` allow-list (task 014, R21),\n * one walker for every adapter. Each adapter names its own option keys (a \"dialect\");\n * the rules are the same: an unlisted relation, column, filter field, nested relation\n * or option is `QueryKitError` `forbidden-key`, `site: \"with\"`, before the driver runs.\n */\nimport { QueryKitError } from \"../errors\";\nimport type { RelationRule, Relations } from \"../types\";\n\n/** How one adapter spells a relation's options. */\nexport interface WithDialect {\n /** Column selection: drizzle `columns`, prisma / mongoose `select`. */\n columns: string;\n /** Nested relations: drizzle `with`, prisma `include`, mongoose `populate`. */\n nested: string;\n /** Row filters: prisma `where`, mongoose `match`; drizzle none (its `where` is code, not JSON). */\n filters: readonly string[];\n /** Logical keys inside a filter, walked into: prisma `AND` / `OR` / `NOT`, mongoose `$and` / `$or` / `$nor`. */\n logical: readonly string[];\n /** Options that only page: drizzle `limit` / `offset`, prisma `take` / `skip`, mongoose `options.limit` / `options.skip`. */\n paging: readonly string[];\n /** A sort whose keys must be allowed columns: prisma `orderBy`. */\n orderBy?: string;\n /** An options bag whose keys must be `paging` keys: mongoose `options`. */\n options?: string;\n}\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;\n\n/** A rule that opens a relation: an object with `columns` (a list or `\"*\"`). A bare model (mongoose shorthand) does not. */\nconst ruleOf = (relations: Relations | undefined, name: string): RelationRule | undefined => {\n const rule = relations && Object.prototype.hasOwnProperty.call(relations, name) ? (relations as Record<string, unknown>)[name] : undefined;\n return isPlainObject(rule) && (rule.columns === \"*\" || Array.isArray(rule.columns)) ? (rule as unknown as RelationRule) : undefined;\n};\n\n/** Keys named by a selection: `{ a: true, b: 1 }` or mongoose's `\"a b -c\"`. */\nconst selectedKeys = (selection: unknown): string[] | undefined => {\n if (typeof selection === \"string\") return selection.split(/\\s+/).filter(Boolean);\n if (isPlainObject(selection)) return Object.keys(selection).filter(key => Boolean(selection[key]));\n return undefined;\n};\n\n/**\n * The checked `with`: every relation in `relations`, every selected column in its\n * `columns`, every filter field in its `filterable`, every nested relation in its\n * `nested`. A relation asked for with `true` (or without a selection) gets its\n * listed columns selected, so a column list is never widened to the full row.\n */\nexport function checkWith(\n withValue: unknown,\n relations: Relations | undefined,\n dialect: WithDialect,\n source: string,\n path = \"\",\n): Record<string, unknown> | undefined {\n if (withValue === undefined || withValue === null) return undefined;\n const refuse = (key: string): never => {\n throw new QueryKitError({ source, site: \"with\", key: path + key, reason: \"forbidden-key\" });\n };\n if (!isPlainObject(withValue)) return refuse(\"with\");\n\n const checked: Record<string, unknown> = {};\n for (const [name, value] of Object.entries(withValue)) {\n if (value === false || value === undefined) continue;\n const rule = ruleOf(relations, name) ?? refuse(name);\n const listed = rule.columns === \"*\" ? undefined : new Set(rule.columns);\n const selectListed = () => Object.fromEntries([...listed!].map(column => [column, true]));\n if (value === true) {\n checked[name] = listed ? { [dialect.columns]: selectListed() } : true;\n continue;\n }\n if (!isPlainObject(value)) refuse(name);\n const options = value as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const [key, option] of Object.entries(options)) {\n const at = `${name}.${key}`;\n if (key === dialect.columns) {\n const keys = selectedKeys(option) ?? refuse(at);\n // An exclusion (\"-secret\") selects every other column: only with \"*\".\n for (const column of keys) if (listed && (column.startsWith(\"-\") || !listed.has(column))) refuse(`${name}.${column}`);\n out[key] = option;\n } else if (key === dialect.nested) {\n out[key] = checkWith(option, rule.nested, dialect, source, `${name}.`);\n } else if (dialect.filters.includes(key)) {\n checkFilterFields(option, rule, dialect, (field: string) => refuse(`${name}.${field}`));\n out[key] = option;\n } else if (dialect.paging.includes(key)) {\n out[key] = option;\n } else if (key === dialect.orderBy) {\n for (const item of Array.isArray(option) ? option : [option]) {\n for (const column of selectedKeys(item) ?? refuse(at)) if (listed && !listed.has(column)) refuse(`${name}.${column}`);\n }\n out[key] = option;\n } else if (key === dialect.options) {\n for (const sub of Object.keys(isPlainObject(option) ? option : refuse(at))) if (!dialect.paging.includes(sub)) refuse(`${at}.${sub}`);\n out[key] = option;\n } else {\n refuse(at);\n }\n }\n if (listed && out[dialect.columns] === undefined) out[dialect.columns] = selectListed();\n checked[name] = out;\n }\n return checked;\n}\n\n/** Every field of a relation filter (through the logical keys) must be in `filterable`. */\nfunction checkFilterFields(filter: unknown, rule: RelationRule, dialect: WithDialect, refuse: (field: string) => never): void {\n if (!isPlainObject(filter)) refuse(\"filter\");\n for (const [key, value] of Object.entries(filter as Record<string, unknown>)) {\n if (dialect.logical.includes(key)) {\n for (const part of Array.isArray(value) ? value : [value]) checkFilterFields(part, rule, dialect, refuse);\n } else if (key.startsWith(\"$\") || !rule.filterable || rule.filterable[key] === undefined) {\n // `$where`, `$expr` and any field outside the allow-list.\n refuse(key);\n }\n }\n}\n","/**\n * The filter-limit walk shared by the validators (task 008). It runs on the raw,\n * unvalidated payload **before** any recursive schema sees it, so it is\n * iterative — no recursion at all, whatever the payload's depth — and a\n * 10 000-level `not` chain is rejected after `maxDepth + 1` steps.\n *\n * Only size is judged here. Shape errors (a missing `key`, a bad operator) are\n * the validator's job — a malformed node is skipped, not reported.\n */\nimport { DEFAULT_PER_PAGE, type QueryLimits } from \"../defaults\";\n\n/** Where in the filter a limit was crossed — object keys and array indexes, from the root. */\nexport type LimitPath = (string | number)[];\n\nexport interface LimitViolation {\n limit: keyof QueryLimits;\n max: number;\n path: LimitPath;\n}\n\n/** The limits the walk checks. */\nexport type FilterLimits = Pick<QueryLimits, \"maxDepth\" | \"maxConditions\" | \"maxInItems\" | \"maxStringLength\">;\n\n/** A node still to visit: the node, its level (the root is 1) and its path. */\ntype Pending = [node: unknown, depth: number, path: LimitPath];\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/** `maxInItems (200)` — the form every package's error text uses. */\nexport const formatLimit = (limit: keyof QueryLimits, max: number): string => `${limit} (${max})`;\n\n/**\n * The first limit the filter crosses, depth-first in document order, or\n * `undefined` when it is within all four. A flat array (implicit AND) puts its\n * conditions at level 1. Key order mirrors the validators: `key` first, then\n * `and`, `or`, `not`.\n */\nexport function findFilterLimitViolation(filter: unknown, limits: FilterLimits): LimitViolation | undefined {\n const hit = (limit: keyof FilterLimits, path: LimitPath): LimitViolation => ({ limit, max: limits[limit], path });\n const pending: Pending[] = [];\n /* Children go on the stack last-first, so they pop in document order. */\n const push = (children: readonly unknown[], depth: number, path: LimitPath) => {\n for (let index = children.length - 1; index >= 0; index--) pending.push([children[index], depth, [...path, index]]);\n };\n if (Array.isArray(filter)) push(filter, 1, []);\n else if (filter !== undefined) pending.push([filter, 1, []]);\n\n let conditions = 0;\n for (let next = pending.pop(); next; next = pending.pop()) {\n const [node, depth, path] = next;\n if (depth > limits.maxDepth) return hit(\"maxDepth\", path);\n if (!isRecord(node)) continue;\n\n if (\"key\" in node) {\n if (++conditions > limits.maxConditions) return hit(\"maxConditions\", path);\n const { value } = node;\n const valuePath = [...path, \"value\"];\n if (Array.isArray(value) && value.length > limits.maxInItems) return hit(\"maxInItems\", valuePath);\n const items: unknown[] = Array.isArray(value) ? value : [value];\n const tooLong = items.findIndex(item => typeof item === \"string\" && item.length > limits.maxStringLength);\n if (tooLong >= 0) return hit(\"maxStringLength\", Array.isArray(value) ? [...valuePath, tooLong] : valuePath);\n continue;\n }\n\n const group = \"and\" in node ? \"and\" : \"or\" in node ? \"or\" : undefined;\n if (group) {\n const children = node[group];\n if (Array.isArray(children)) push(children, depth + 1, [...path, group]);\n } else if (\"not\" in node) pending.push([node.not, depth + 1, [...path, \"not\"]]);\n }\n return undefined;\n}\n\nconst LIKE_OPERATORS: ReadonlySet<string> = new Set([\"like\", \"ilike\", \"notLike\"]);\n\n/** Whether an operator takes a SQL LIKE pattern. */\nexport const isLikeOperator = (operation: string): boolean => LIKE_OPERATORS.has(operation);\n\n/**\n * Unescaped wildcards in a LIKE pattern, a run of `%` counted once (`%%a%` ⇒ 2).\n * A backslash escapes the next character, as in the adapters' LIKE translation.\n */\nexport function likeWildcards(pattern: string): number {\n let count = 0;\n let previous = \"\";\n for (let index = 0; index < pattern.length; index++) {\n const char = pattern[index]!;\n if (char === \"\\\\\") {\n index++;\n previous = \"\";\n continue;\n }\n if (char === \"_\" || (char === \"%\" && previous !== \"%\")) count++;\n previous = char;\n }\n return count;\n}\n\n/** Rows an offset-paginated request skips: `(page - 1) × perPage`, with the core default page size. */\nexport const offsetOf = (page: number | undefined, perPage: number | undefined): number => ((page ?? 1) - 1) * (perPage ?? DEFAULT_PER_PAGE);\n","/**\n * The server-side request policy every adapter applies (task 009): which fields\n * a client may filter by and with which operators (`filterable`), which it may\n * sort and page by (`sortable`), and the request limits (`limits`, task 008).\n *\n * A plain object that is not a condition, a group, a branded `raw()` node or a\n * driver-native object (`isNative`) is refused as well (R19): it would otherwise\n * reach the driver as its own `where`, past `filterable`.\n *\n * A refused request never reaches the driver and is never dropped silently:\n * every check throws `QueryKitError`, whatever `strict` / `onSkippedCondition`\n * say. A key the model does not have is **not** the policy's business — it goes\n * on to the adapter's own `unknown-key` handling, unchanged.\n */\nimport { resolveLimits, type QueryLimits } from \"../defaults\";\nimport { QueryKitError } from \"../errors\";\nimport type { Filterable, SkipReason, SkipSite } from \"../types\";\nimport { isRaw } from \"../raw\";\nimport { findFilterLimitViolation, isLikeOperator, likeWildcards, offsetOf } from \"./limits\";\n\nexport interface PolicyOptions {\n /** Table / model name for the error. */\n source: string;\n /** Wire key → the model's canonical field name, or `undefined` when the model has no such field. */\n fieldOf: (key: string) => string | undefined;\n /** Canonical primary-key field — the last-resort safe default. */\n primaryKey: string;\n /** Canonical `createdAt` field when the model has one (default `sortable`). */\n createdAt?: string;\n filterable?: Filterable;\n sortable?: readonly string[];\n allowedColumns?: readonly string[];\n forcedColumns?: Readonly<Record<string, unknown>>;\n limits?: Partial<QueryLimits>;\n /**\n * A driver object that cannot come from JSON and stands for itself (drizzle `SQL`).\n * Anything else that is not a condition, a group or a {@link isRaw} node is refused (R19).\n */\n isNative?: (node: object) => boolean;\n}\n\nexport interface Policy {\n readonly limits: QueryLimits;\n /** Limits, then every condition's field and operator. */\n checkFilter(filter: unknown): void;\n /** Field count, then every sort field. */\n checkSort(sort: readonly { key?: unknown }[] | undefined): void;\n checkCursorKey(key: string): void;\n /** Offset pagination: the clamped `page` × `perPage`. Infinite: the clamped `offset`. */\n checkPage(page: { page: number; perPage: number } | { offset: number }): void;\n /** A `groupBy` / `sum` / `avg` / `min` / `max` field must be filterable. */\n checkAggregateKey(key: string): void;\n}\n\n/** The safe default when `filterable` is not given: equality only. */\nconst DEFAULT_OPERATORS = [\"=\", \"in\"] as const;\n\n/** Alias → the token it means, so `[\"=\"]` also admits `eq` and `[\"contains\"]` admits `%_%`. */\nconst OPERATOR_TOKEN: Readonly<Record<string, string>> = {\n eq: \"=\",\n ne: \"!=\",\n gt: \">\",\n gte: \">=\",\n lt: \"<\",\n lte: \"<=\",\n contains: \"%_%\",\n startsWith: \"%_\",\n endsWith: \"_%\",\n};\nconst tokenOf = (operation: string): string => OPERATOR_TOKEN[operation] ?? operation;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/** Field → admitted operator tokens, or `\"*\"`. */\ntype Rules = ReadonlyMap<string, ReadonlySet<string> | \"*\">;\n\nfunction filterRules(options: PolicyOptions, canonical: (key: string) => string): Rules {\n const rules = new Map<string, ReadonlySet<string> | \"*\">();\n if (options.filterable) {\n for (const [key, operators] of Object.entries(options.filterable)) {\n if (operators) rules.set(canonical(key), operators === \"*\" ? \"*\" : new Set(operators.map(tokenOf)));\n }\n return rules;\n }\n const fields =\n options.allowedColumns ?? (options.forcedColumns ? Object.keys(options.forcedColumns).filter(key => options.forcedColumns![key]) : [options.primaryKey]);\n const equality = new Set<string>(DEFAULT_OPERATORS);\n for (const field of fields) rules.set(canonical(field), equality);\n return rules;\n}\n\nexport function resolvePolicy(options: PolicyOptions): Policy {\n const limits = resolveLimits(options.limits);\n const canonical = (key: string) => options.fieldOf(key) ?? key;\n const rules = filterRules(options, canonical);\n const sortable = new Set((options.sortable ?? [options.primaryKey, ...(options.createdAt ? [options.createdAt] : [])]).map(canonical));\n\n const refuse = (site: SkipSite, key: string, reason: SkipReason, operation?: string): never => {\n throw new QueryKitError({ source: options.source, site, key, reason, ...(operation ? { operation: operation as never } : {}) });\n };\n\n const checkCondition = (node: Record<string, unknown>): void => {\n const key = String(node.key);\n const field = options.fieldOf(key);\n if (field === undefined) return; // not in the model — the adapter's unknown-key path\n const operation = typeof node.operation === \"string\" ? node.operation : \"=\";\n const rule = rules.get(field);\n if (!rule) refuse(\"filter\", key, \"forbidden-key\", operation);\n if (rule !== \"*\" && !rule!.has(tokenOf(operation))) refuse(\"filter\", key, \"forbidden-operator\", operation);\n if (isLikeOperator(operation) && typeof node.value === \"string\" && likeWildcards(node.value) > limits.maxLikeWildcards) {\n refuse(\"filter\", \"maxLikeWildcards\", \"limit-exceeded\", operation);\n }\n };\n\n const checkSortKey = (site: SkipSite, key: string): void => {\n const field = options.fieldOf(key);\n if (field !== undefined && !sortable.has(field)) refuse(site, key, \"forbidden-key\");\n };\n\n return {\n limits,\n\n checkFilter(filter) {\n const violation = findFilterLimitViolation(filter, limits);\n if (violation) refuse(\"filter\", violation.limit, \"limit-exceeded\");\n /* Bounded by maxDepth now, but kept iterative so `maxDepth: Infinity` cannot overflow the stack. */\n const pending: unknown[] = Array.isArray(filter) ? [...filter] : [filter];\n while (pending.length > 0) {\n const node = pending.pop();\n if (!isRecord(node)) continue;\n if (\"key\" in node) checkCondition(node);\n else if (Array.isArray(node.and)) pending.push(...node.and);\n else if (Array.isArray(node.or)) pending.push(...node.or);\n else if (\"not\" in node) pending.push(node.not);\n // R19: a JSON body cannot brand a node, so an unbranded driver object is refused.\n else if (!isRaw(node) && !options.isNative?.(node)) refuse(\"filter\", Object.keys(node)[0] ?? \"\", \"forbidden-key\");\n }\n },\n\n checkSort(sort) {\n if (!sort) return;\n if (sort.length > limits.maxSortFields) refuse(\"sort\", \"maxSortFields\", \"limit-exceeded\");\n for (const item of sort) if (item && typeof item.key === \"string\" && item.key) checkSortKey(\"sort\", item.key);\n },\n\n checkCursorKey(key) {\n checkSortKey(\"cursorKey\", key);\n },\n\n checkPage(page) {\n const offset = \"offset\" in page ? page.offset : offsetOf(page.page, page.perPage);\n if (offset > limits.maxOffset) refuse(\"page\", \"maxOffset\", \"limit-exceeded\");\n },\n\n checkAggregateKey(key) {\n const field = options.fieldOf(key);\n if (field !== undefined && !rules.has(field)) refuse(\"aggregate\", key, \"forbidden-key\");\n },\n };\n}\n"]}
|