@atscript/moost-db 0.1.164 → 0.1.165
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/index.cjs +91 -25
- package/dist/index.d.cts +16 -2
- package/dist/index.d.mts +17 -3
- package/dist/index.mjs +92 -26
- package/package.json +17 -17
package/dist/index.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_db_space_registry = require("./db-space-registry-D5gnC_-6.cjs");
|
|
3
3
|
let _atscript_typescript_utils = require("@atscript/typescript/utils");
|
|
4
|
+
let _atscript_db = require("@atscript/db");
|
|
4
5
|
let _moostjs_event_http = require("@moostjs/event-http");
|
|
5
6
|
let moost = require("moost");
|
|
6
7
|
let _uniqu_url = require("@uniqu/url");
|
|
7
|
-
let _atscript_db = require("@atscript/db");
|
|
8
8
|
let _wooksjs_event_core = require("@wooksjs/event-core");
|
|
9
9
|
let _wooksjs_http_body = require("@wooksjs/http-body");
|
|
10
10
|
let _uniqu_core = require("@uniqu/core");
|
|
@@ -14,12 +14,14 @@ let _atscript_db_memory = require("@atscript/db-memory");
|
|
|
14
14
|
* The structured error envelope every `moost-db` rejection uses (since
|
|
15
15
|
* 0.1.128 all of them do): `{ message, statusCode, errors: [{ path, message }] }`
|
|
16
16
|
* — the same shape the validation interceptor renders for `ValidatorError` /
|
|
17
|
-
* `DbError`, so clients parse one format.
|
|
17
|
+
* `DbError`, so clients parse one format. A `DbError` adds its `code` and
|
|
18
|
+
* each entry's `code` / `params` (since 0.1.165).
|
|
18
19
|
*/
|
|
19
|
-
function errorEnvelope(statusCode, message, errors) {
|
|
20
|
+
function errorEnvelope(statusCode, message, errors, code) {
|
|
20
21
|
return new _moostjs_event_http.HttpError(statusCode, {
|
|
21
22
|
message,
|
|
22
23
|
statusCode,
|
|
24
|
+
...code === void 0 ? {} : { code },
|
|
23
25
|
errors
|
|
24
26
|
});
|
|
25
27
|
}
|
|
@@ -95,7 +97,15 @@ const dbErrorCodeToStatus = {
|
|
|
95
97
|
function transformValidationError(error, reply) {
|
|
96
98
|
const validation = validatorErrorToHttp(error);
|
|
97
99
|
if (validation) reply(validation);
|
|
98
|
-
else if (error instanceof _atscript_db.DbError)
|
|
100
|
+
else if (error instanceof _atscript_db.DbError) {
|
|
101
|
+
const errors = error.errors.map(({ path, message, code, params }) => ({
|
|
102
|
+
path,
|
|
103
|
+
message,
|
|
104
|
+
...code === void 0 ? {} : { code },
|
|
105
|
+
...params === void 0 ? {} : { params }
|
|
106
|
+
}));
|
|
107
|
+
reply(errorEnvelope(dbErrorCodeToStatus[error.code] ?? 400, error.message, errors, error.code));
|
|
108
|
+
}
|
|
99
109
|
}
|
|
100
110
|
const validationErrorTransform = () => (0, moost.defineInterceptor)({ error: transformValidationError }, moost.TInterceptorPriority.BEFORE_ALL);
|
|
101
111
|
const UseValidationErrorTransform = () => (0, moost.Intercept)(validationErrorTransform());
|
|
@@ -191,7 +201,9 @@ var SelectControlDto = class {
|
|
|
191
201
|
(0, _atscript_typescript_utils.throwFeatureDisabled)("JSON Schema", "jsonSchema", "emit.jsonSchema");
|
|
192
202
|
}
|
|
193
203
|
};
|
|
194
|
-
(0, _atscript_typescript_utils
|
|
204
|
+
(0, _atscript_typescript_utils.$ak)("db.index.fulltext", "db.index.plain", "db.index.unique", "db.view.joins", "expect.pattern");
|
|
205
|
+
(0, _atscript_typescript_utils.$nr)("db.agg.avg", "db.agg.count", "db.agg.countDistinct", "db.agg.max", "db.agg.min", "db.agg.sum", "db.alias", "db.amount.currency.ref", "db.column", "db.column.collate", "db.column.derived", "db.column.dimension", "db.column.filterable", "db.column.measure", "db.column.precision", "db.column.renamed", "db.column.searchable", "db.column.sortable", "db.column.version", "db.column.version.exempt", "db.compute", "db.default", "db.default.increment", "db.default.now", "db.default.uuid", "db.depth.limit", "db.encrypted", "db.http.path", "db.ignore", "db.index.fulltext", "db.index.geo", "db.index.plain", "db.index.unique", "db.json", "db.onUpdate.inc", "db.onUpdate.now", "db.patch.strategy", "db.rel.FK", "db.rel.filter", "db.rel.filterable", "db.rel.from", "db.rel.onDelete", "db.rel.onUpdate", "db.rel.to", "db.rel.via", "db.schema", "db.search.filter", "db.search.vector", "db.search.vector.threshold", "db.sort.nulls", "db.space", "db.sync.method", "db.table", "db.table.filterable", "db.table.preferredId.uniqueIndex", "db.table.renamed", "db.table.sortable", "db.unit.ref", "db.view", "db.view.filter", "db.view.for", "db.view.having", "db.view.joins", "db.view.materialized", "db.view.renamed", "db.writeOnly", "meta.default", "meta.id", "meta.readonly", "meta.required");
|
|
206
|
+
(0, _atscript_typescript_utils.defineAnnotatedType)("object", QueryControlsDto).prop("$skip", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$limit", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$count", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("boolean").tags("boolean").optional().$type).prop("$sort", (0, _atscript_typescript_utils.defineAnnotatedType)().ref(SortControlDto).optional().$type).prop("$nulls", (0, _atscript_typescript_utils.defineAnnotatedType)().ref(NullsControlDto).optional().$type).prop("$select", (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().ref(SelectControlDto).$type).item((0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).$type).optional().$type).prop("$search", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$index", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$fuzzy", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$vector", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$threshold", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$with", (0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().ref(WithRelationDto).$type).optional().$type).prop("$actions", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("boolean").tags("boolean").optional().$type);
|
|
195
207
|
(0, _atscript_typescript_utils.defineAnnotatedType)("object", PagesControlsDto).prop("$page", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").annotate("expect.pattern", {
|
|
196
208
|
pattern: "^\\d+$",
|
|
197
209
|
flags: "u",
|
|
@@ -200,9 +212,9 @@ var SelectControlDto = class {
|
|
|
200
212
|
pattern: "^\\d+$",
|
|
201
213
|
flags: "u",
|
|
202
214
|
message: "Expected positive number"
|
|
203
|
-
}, true).optional().$type).prop("$sort", (0, _atscript_typescript_utils.defineAnnotatedType)().
|
|
204
|
-
(0, _atscript_typescript_utils.defineAnnotatedType)("object", GetOneControlsDto).prop("$select", (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().
|
|
205
|
-
(0, _atscript_typescript_utils.defineAnnotatedType)("object", GeoControlsDto).prop("$center", (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).item((0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).$type).item((0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("number").$type).$type).optional().$type).prop("$maxDistance", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("number").optional().$type).prop("$minDistance", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("number").optional().$type).prop("$index", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$select", (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().
|
|
215
|
+
}, true).optional().$type).prop("$sort", (0, _atscript_typescript_utils.defineAnnotatedType)().ref(SortControlDto).optional().$type).prop("$nulls", (0, _atscript_typescript_utils.defineAnnotatedType)().ref(NullsControlDto).optional().$type).prop("$select", (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().ref(SelectControlDto).$type).item((0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).$type).optional().$type).prop("$search", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$index", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$fuzzy", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$vector", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$threshold", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$with", (0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().ref(WithRelationDto).$type).optional().$type).prop("$actions", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("boolean").tags("boolean").optional().$type);
|
|
216
|
+
(0, _atscript_typescript_utils.defineAnnotatedType)("object", GetOneControlsDto).prop("$select", (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().ref(SelectControlDto).$type).item((0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).$type).optional().$type).prop("$with", (0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().ref(WithRelationDto).$type).optional().$type).prop("$actions", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("boolean").tags("boolean").optional().$type);
|
|
217
|
+
(0, _atscript_typescript_utils.defineAnnotatedType)("object", GeoControlsDto).prop("$center", (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).item((0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).$type).item((0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("number").$type).$type).optional().$type).prop("$maxDistance", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("number").optional().$type).prop("$minDistance", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("number").optional().$type).prop("$index", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$select", (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().ref(SelectControlDto).$type).item((0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).$type).optional().$type).prop("$skip", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$limit", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$page", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").annotate("expect.pattern", {
|
|
206
218
|
pattern: "^\\d+$",
|
|
207
219
|
flags: "u",
|
|
208
220
|
message: "Expected positive number"
|
|
@@ -210,10 +222,10 @@ var SelectControlDto = class {
|
|
|
210
222
|
pattern: "^\\d+$",
|
|
211
223
|
flags: "u",
|
|
212
224
|
message: "Expected positive number"
|
|
213
|
-
}, true).optional().$type).prop("$with", (0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().
|
|
214
|
-
(0, _atscript_typescript_utils.defineAnnotatedType)("object", WithRelationDto).prop("name", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).prop("filter", (0, _atscript_typescript_utils.defineAnnotatedType)().
|
|
215
|
-
(0, _atscript_typescript_utils.defineAnnotatedType)("object", WithRelationControlsDto).prop("$skip", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$limit", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$sort", (0, _atscript_typescript_utils.defineAnnotatedType)().
|
|
216
|
-
(0, _atscript_typescript_utils.defineAnnotatedType)("object", WithFilterDto).propPattern(/./, (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).item((0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("number").$type).item((0, _atscript_typescript_utils.defineAnnotatedType)().designType("boolean").tags("boolean").$type).item((0, _atscript_typescript_utils.defineAnnotatedType)().designType("null").tags("null").$type).item((0, _atscript_typescript_utils.defineAnnotatedType)().
|
|
225
|
+
}, true).optional().$type).prop("$with", (0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().ref(WithRelationDto).$type).optional().$type).prop("$actions", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("boolean").tags("boolean").optional().$type);
|
|
226
|
+
(0, _atscript_typescript_utils.defineAnnotatedType)("object", WithRelationDto).prop("name", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).prop("filter", (0, _atscript_typescript_utils.defineAnnotatedType)().ref(WithFilterDto).optional().$type).prop("controls", (0, _atscript_typescript_utils.defineAnnotatedType)().ref(WithRelationControlsDto).optional().$type).prop("insights", (0, _atscript_typescript_utils.defineAnnotatedType)().ref(WithFilterDto).optional().$type);
|
|
227
|
+
(0, _atscript_typescript_utils.defineAnnotatedType)("object", WithRelationControlsDto).prop("$skip", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$limit", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$sort", (0, _atscript_typescript_utils.defineAnnotatedType)().ref(SortControlDto).optional().$type).prop("$nulls", (0, _atscript_typescript_utils.defineAnnotatedType)().ref(NullsControlDto).optional().$type).prop("$select", (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().ref(SelectControlDto).$type).item((0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).$type).optional().$type).prop("$with", (0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().ref(WithRelationDto).$type).optional().$type);
|
|
228
|
+
(0, _atscript_typescript_utils.defineAnnotatedType)("object", WithFilterDto).propPattern(/./, (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).item((0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("number").$type).item((0, _atscript_typescript_utils.defineAnnotatedType)().designType("boolean").tags("boolean").$type).item((0, _atscript_typescript_utils.defineAnnotatedType)().designType("null").tags("null").$type).item((0, _atscript_typescript_utils.defineAnnotatedType)().ref(WithFilterDto).$type).item((0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().ref(WithFilterDto).$type).$type).$type);
|
|
217
229
|
(0, _atscript_typescript_utils.defineAnnotatedType)("object", SortControlDto).propPattern(/./, (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").value(1).$type).item((0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").value(-1).$type).$type);
|
|
218
230
|
(0, _atscript_typescript_utils.defineAnnotatedType)("object", NullsControlDto).propPattern(/./, (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").value("first").$type).item((0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").value("last").$type).$type);
|
|
219
231
|
(0, _atscript_typescript_utils.defineAnnotatedType)("object", SelectControlDto).propPattern(/./, (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").value(1).$type).item((0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").value(0).$type).$type);
|
|
@@ -2179,15 +2191,19 @@ function walk(node, def, shallow) {
|
|
|
2179
2191
|
const sProp = sType.props[name];
|
|
2180
2192
|
if (!sProp) continue;
|
|
2181
2193
|
if ((0, _atscript_db.isNavRelation)(prop)) continue;
|
|
2194
|
+
const member = !prop.ref && sProp.ref === void 0 ? (0, _atscript_db.soleUnionMember)(prop) : void 0;
|
|
2195
|
+
if (member?.ref?.field) {
|
|
2196
|
+
const terminal = resolveTerminalRef({
|
|
2197
|
+
...prop,
|
|
2198
|
+
ref: member.ref
|
|
2199
|
+
});
|
|
2200
|
+
if (terminal) applyTerminal(sProp, terminal, shallow, true);
|
|
2201
|
+
}
|
|
2182
2202
|
if (prop.ref?.field && sProp.ref && !("type" in sProp.ref.type)) {
|
|
2183
2203
|
const terminal = resolveTerminalRef(prop);
|
|
2184
2204
|
if (terminal) {
|
|
2185
2205
|
const direct = prop.ref.type();
|
|
2186
|
-
|
|
2187
|
-
field: terminal.field,
|
|
2188
|
-
type: shallow(terminal.type)
|
|
2189
|
-
};
|
|
2190
|
-
if (terminal.fk && sProp.metadata["db.rel.FK"] === void 0) sProp.metadata["db.rel.FK"] = true;
|
|
2206
|
+
applyTerminal(sProp, terminal, shallow, terminal.type !== direct || terminal.field !== prop.ref.field);
|
|
2191
2207
|
}
|
|
2192
2208
|
}
|
|
2193
2209
|
walk(sProp, prop, shallow);
|
|
@@ -2200,6 +2216,17 @@ function walk(node, def, shallow) {
|
|
|
2200
2216
|
if (sType.kind === "array" && sType.of && of) walk(sType.of, of, shallow);
|
|
2201
2217
|
}
|
|
2202
2218
|
}
|
|
2219
|
+
/**
|
|
2220
|
+
* Points a serialized prop at its terminal field (when `repoint`) and
|
|
2221
|
+
* inherits the chain's FK marker.
|
|
2222
|
+
*/
|
|
2223
|
+
function applyTerminal(sProp, terminal, shallow, repoint) {
|
|
2224
|
+
if (repoint) sProp.ref = {
|
|
2225
|
+
field: terminal.field,
|
|
2226
|
+
type: shallow(terminal.type)
|
|
2227
|
+
};
|
|
2228
|
+
if (terminal.fk && sProp.metadata["db.rel.FK"] === void 0) sProp.metadata["db.rel.FK"] = true;
|
|
2229
|
+
}
|
|
2203
2230
|
//#endregion
|
|
2204
2231
|
//#region src/meta/meta-cache.ts
|
|
2205
2232
|
/**
|
|
@@ -2996,11 +3023,13 @@ let AsReadableController = _AsReadableController = class AsReadableController {
|
|
|
2996
3023
|
* an unhandled 500. Quote such values: `?name='json-w1'`.
|
|
2997
3024
|
*/
|
|
2998
3025
|
parseUrlOr400(queryString) {
|
|
3026
|
+
let parsed;
|
|
2999
3027
|
try {
|
|
3000
|
-
|
|
3028
|
+
parsed = (0, _uniqu_url.parseUrl)(queryString);
|
|
3001
3029
|
} catch (error) {
|
|
3002
3030
|
throw badRequest("", `Malformed query string: ${error instanceof Error ? error.message : String(error)}`);
|
|
3003
3031
|
}
|
|
3032
|
+
return nestParsedWith(parsed);
|
|
3004
3033
|
}
|
|
3005
3034
|
/**
|
|
3006
3035
|
* Parse a URL keeping only `$*` control keywords; report whether any
|
|
@@ -3312,6 +3341,38 @@ function applyMetaHeaders(response, caching) {
|
|
|
3312
3341
|
}
|
|
3313
3342
|
response.setHeader(varyKey, tokens.join(", "));
|
|
3314
3343
|
}
|
|
3344
|
+
/**
|
|
3345
|
+
* A dotted `$with` (`$with=a.b(…)`) rewritten into its nested form
|
|
3346
|
+
* (`$with=a($with=b(…))`) right after parsing (since 0.1.165), so every
|
|
3347
|
+
* per-level step — relation-name checks, write-only vetoes, seals, row-scope
|
|
3348
|
+
* overlays — sees one relation per level. The intermediate levels get the
|
|
3349
|
+
* `$with` insight their nested form records. Unchanged when no entry is dotted.
|
|
3350
|
+
*/
|
|
3351
|
+
function nestParsedWith(parsed) {
|
|
3352
|
+
const controls = parsed.controls;
|
|
3353
|
+
const withRels = controls?.$with;
|
|
3354
|
+
if (!Array.isArray(withRels) || withRels.length === 0) return parsed;
|
|
3355
|
+
const nested = (0, _atscript_db.nestDottedWith)(withRels);
|
|
3356
|
+
if (nested === withRels) return parsed;
|
|
3357
|
+
const insights = parsed.insights;
|
|
3358
|
+
if (insights) for (const path of insights.keys()) {
|
|
3359
|
+
if (!insights.get(path)?.has("$with")) continue;
|
|
3360
|
+
const segments = path.split(".");
|
|
3361
|
+
for (let i = 1; i < segments.length; i++) {
|
|
3362
|
+
const prefix = segments.slice(0, i).join(".");
|
|
3363
|
+
let ops = insights.get(prefix);
|
|
3364
|
+
if (!ops) insights.set(prefix, ops = /* @__PURE__ */ new Set());
|
|
3365
|
+
ops.add("$with");
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
return {
|
|
3369
|
+
...parsed,
|
|
3370
|
+
controls: {
|
|
3371
|
+
...controls,
|
|
3372
|
+
$with: nested
|
|
3373
|
+
}
|
|
3374
|
+
};
|
|
3375
|
+
}
|
|
3315
3376
|
//#endregion
|
|
3316
3377
|
//#region src/actions/verdict.ts
|
|
3317
3378
|
/** Assert that a `disabled` predicate returned an array of the expected length; throws HTTP 500 otherwise. */
|
|
@@ -5509,12 +5570,16 @@ let AsDbReadableController = _AsDbReadableController = class AsDbReadableControl
|
|
|
5509
5570
|
return this._indexFieldPathsCache ??= [...this._searchIndexFieldPaths(), ...this._geoIndexFieldPaths()];
|
|
5510
5571
|
}
|
|
5511
5572
|
_searchIndexFieldPaths() {
|
|
5512
|
-
const out = (typeof this.readable.getSearchIndexes === "function" ? this.readable.getSearchIndexes() : []).map((info) =>
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5573
|
+
const out = (typeof this.readable.getSearchIndexes === "function" ? this.readable.getSearchIndexes() : []).map((info) => {
|
|
5574
|
+
const entry = {
|
|
5575
|
+
name: info.name,
|
|
5576
|
+
type: info.type === "vector" ? "vector" : "text",
|
|
5577
|
+
fields: info.fields ?? this._invertibleFields,
|
|
5578
|
+
isDefault: info.isDefault === true
|
|
5579
|
+
};
|
|
5580
|
+
if (info.aliases?.length) entry.aliases = info.aliases;
|
|
5581
|
+
return entry;
|
|
5582
|
+
});
|
|
5518
5583
|
for (const type of ["text", "vector"]) {
|
|
5519
5584
|
const ofType = out.filter((entry) => entry.type === type);
|
|
5520
5585
|
if (ofType.some((entry) => entry.isDefault)) continue;
|
|
@@ -5603,7 +5668,8 @@ let AsDbReadableController = _AsDbReadableController = class AsDbReadableControl
|
|
|
5603
5668
|
}
|
|
5604
5669
|
if (!this.readable.isSearchable()) return void 0;
|
|
5605
5670
|
if (name) {
|
|
5606
|
-
const
|
|
5671
|
+
const text = this.indexFieldPaths().filter((e) => e.type === "text");
|
|
5672
|
+
const entry = text.find((e) => e.name === name) ?? text.find((e) => e.aliases?.includes(name) === true);
|
|
5607
5673
|
if (!entry || !this._indexVisible(entry)) return badRequest("$index", (0, _atscript_db.searchIndexNotFoundMessage)(name));
|
|
5608
5674
|
return;
|
|
5609
5675
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1125,6 +1125,12 @@ interface TDbDecorateContext {
|
|
|
1125
1125
|
interface TDbIndexFieldPaths {
|
|
1126
1126
|
/** The name a request addresses the index by (`$index` for text and geo, `$vector` for vector). */
|
|
1127
1127
|
name: string;
|
|
1128
|
+
/**
|
|
1129
|
+
* Other names that address the same index (the adapter's deprecated
|
|
1130
|
+
* `TSearchIndexInfo.aliases`); omitted when there are none.
|
|
1131
|
+
* @since 0.1.165
|
|
1132
|
+
*/
|
|
1133
|
+
aliases?: readonly string[];
|
|
1128
1134
|
type: "text" | "vector" | "geo";
|
|
1129
1135
|
/**
|
|
1130
1136
|
* Logical field paths the index reads. An index whose coverage cannot be
|
|
@@ -3128,14 +3134,22 @@ declare function getDbEndpoint(target: object, method: string | symbol): TDbRequ
|
|
|
3128
3134
|
interface THttpErrorEntry {
|
|
3129
3135
|
path: string;
|
|
3130
3136
|
message: string;
|
|
3137
|
+
/**
|
|
3138
|
+
* Machine-readable reason of the entry (since 0.1.165) — a `DbError`
|
|
3139
|
+
* entry's code (`UNIQUE`, `FK_MISSING`, `FK_RESTRICT`).
|
|
3140
|
+
*/
|
|
3141
|
+
code?: string;
|
|
3142
|
+
/** Values the message names, for translating it (since 0.1.165). */
|
|
3143
|
+
params?: Record<string, unknown>;
|
|
3131
3144
|
}
|
|
3132
3145
|
/**
|
|
3133
3146
|
* The structured error envelope every `moost-db` rejection uses (since
|
|
3134
3147
|
* 0.1.128 all of them do): `{ message, statusCode, errors: [{ path, message }] }`
|
|
3135
3148
|
* — the same shape the validation interceptor renders for `ValidatorError` /
|
|
3136
|
-
* `DbError`, so clients parse one format.
|
|
3149
|
+
* `DbError`, so clients parse one format. A `DbError` adds its `code` and
|
|
3150
|
+
* each entry's `code` / `params` (since 0.1.165).
|
|
3137
3151
|
*/
|
|
3138
|
-
declare function errorEnvelope(statusCode: number, message: string, errors: THttpErrorEntry[]): HttpError;
|
|
3152
|
+
declare function errorEnvelope(statusCode: number, message: string, errors: THttpErrorEntry[], code?: string): HttpError;
|
|
3139
3153
|
/**
|
|
3140
3154
|
* A 400 with a single `errors` entry: `path` names the offending logical
|
|
3141
3155
|
* path / body position, `message` the reason, `top` the envelope's top-level
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { a as resolveDbSpace, i as provideDbSpace, n as clearDbSpaces, r as closeDbSpaces, t as DEFAULT_DB_SPACE } from "./db-space-registry-Cvd8s9ql.mjs";
|
|
2
2
|
import { TAtscriptAnnotatedType, TAtscriptDataType, TSerializeOptions, TSerializedAnnotatedType, TValidatorOptions, Validator } from "@atscript/typescript/utils";
|
|
3
|
+
import { AggregateFn, AtscriptDbReadable, AtscriptDbTable, BucketUnit, FilterExpr, FlatOf, NullsPlacement, TCrudOp, TCrudOp as TCrudOp$1, TCrudPermissions, TCrudPermissions as TCrudPermissions$1, TDbActionInfo, TDbActionInfo as TDbActionInfo$1, TDbActionIntent, TDbActionIntent as TDbActionIntent$1, TDbActionLevel, TDbActionLevel as TDbActionLevel$1, TDbActionProcessor, TDbActionTargetSummary, TDbActionTargetSummary as TDbActionTargetSummary$1, TDbAvailableActions, TDbAvailableActions as TDbAvailableActions$1, TDbFieldMeta, TDbRemoveGuardContext, TDbRemoveGuardContext as TDbRemoveGuardContext$1, TDbWriteAction, TDbWriteAction as TDbWriteAction$1, TDbWriteCheckContext, TDbWriteCheckContext as TDbWriteCheckContext$1, TDbWriteGuardContext, TDbWriteGuardContext as TDbWriteGuardContext$1, TFilterPredicate, TIdResolveOptions, TIdentification, TMetaResponse, TQueryPathOp, TQueryPathOp as TQueryPathOp$1, TQueryPathRefs, TQueryPathSource, TTransactionOptions, Uniquery, UniqueryControls, collectQueryPaths } from "@atscript/db";
|
|
3
4
|
import { HttpError, MoostHttp } from "@moostjs/event-http";
|
|
4
5
|
import { Mate, Moost, TConsoleBase, TMateParamMeta, TMoostMetadata } from "moost";
|
|
5
6
|
import { parseUrl } from "@uniqu/url";
|
|
6
|
-
import { AggregateFn, AtscriptDbReadable, AtscriptDbTable, BucketUnit, FilterExpr, FlatOf, NullsPlacement, TCrudOp, TCrudOp as TCrudOp$1, TCrudPermissions, TCrudPermissions as TCrudPermissions$1, TDbActionInfo, TDbActionInfo as TDbActionInfo$1, TDbActionIntent, TDbActionIntent as TDbActionIntent$1, TDbActionLevel, TDbActionLevel as TDbActionLevel$1, TDbActionProcessor, TDbActionTargetSummary, TDbActionTargetSummary as TDbActionTargetSummary$1, TDbAvailableActions, TDbAvailableActions as TDbAvailableActions$1, TDbFieldMeta, TDbRemoveGuardContext, TDbRemoveGuardContext as TDbRemoveGuardContext$1, TDbWriteAction, TDbWriteAction as TDbWriteAction$1, TDbWriteCheckContext, TDbWriteCheckContext as TDbWriteCheckContext$1, TDbWriteGuardContext, TDbWriteGuardContext as TDbWriteGuardContext$1, TFilterPredicate, TIdResolveOptions, TIdentification, TMetaResponse, TQueryPathOp, TQueryPathOp as TQueryPathOp$1, TQueryPathRefs, TQueryPathSource, TTransactionOptions, Uniquery, UniqueryControls, collectQueryPaths } from "@atscript/db";
|
|
7
7
|
import { EventContext } from "@wooksjs/event-core";
|
|
8
8
|
|
|
9
9
|
//#region src/meta/meta-cache.d.ts
|
|
@@ -1125,6 +1125,12 @@ interface TDbDecorateContext {
|
|
|
1125
1125
|
interface TDbIndexFieldPaths {
|
|
1126
1126
|
/** The name a request addresses the index by (`$index` for text and geo, `$vector` for vector). */
|
|
1127
1127
|
name: string;
|
|
1128
|
+
/**
|
|
1129
|
+
* Other names that address the same index (the adapter's deprecated
|
|
1130
|
+
* `TSearchIndexInfo.aliases`); omitted when there are none.
|
|
1131
|
+
* @since 0.1.165
|
|
1132
|
+
*/
|
|
1133
|
+
aliases?: readonly string[];
|
|
1128
1134
|
type: "text" | "vector" | "geo";
|
|
1129
1135
|
/**
|
|
1130
1136
|
* Logical field paths the index reads. An index whose coverage cannot be
|
|
@@ -3128,14 +3134,22 @@ declare function getDbEndpoint(target: object, method: string | symbol): TDbRequ
|
|
|
3128
3134
|
interface THttpErrorEntry {
|
|
3129
3135
|
path: string;
|
|
3130
3136
|
message: string;
|
|
3137
|
+
/**
|
|
3138
|
+
* Machine-readable reason of the entry (since 0.1.165) — a `DbError`
|
|
3139
|
+
* entry's code (`UNIQUE`, `FK_MISSING`, `FK_RESTRICT`).
|
|
3140
|
+
*/
|
|
3141
|
+
code?: string;
|
|
3142
|
+
/** Values the message names, for translating it (since 0.1.165). */
|
|
3143
|
+
params?: Record<string, unknown>;
|
|
3131
3144
|
}
|
|
3132
3145
|
/**
|
|
3133
3146
|
* The structured error envelope every `moost-db` rejection uses (since
|
|
3134
3147
|
* 0.1.128 all of them do): `{ message, statusCode, errors: [{ path, message }] }`
|
|
3135
3148
|
* — the same shape the validation interceptor renders for `ValidatorError` /
|
|
3136
|
-
* `DbError`, so clients parse one format.
|
|
3149
|
+
* `DbError`, so clients parse one format. A `DbError` adds its `code` and
|
|
3150
|
+
* each entry's `code` / `params` (since 0.1.165).
|
|
3137
3151
|
*/
|
|
3138
|
-
declare function errorEnvelope(statusCode: number, message: string, errors: THttpErrorEntry[]): HttpError;
|
|
3152
|
+
declare function errorEnvelope(statusCode: number, message: string, errors: THttpErrorEntry[], code?: string): HttpError;
|
|
3139
3153
|
/**
|
|
3140
3154
|
* A 400 with a single `errors` entry: `path` names the offending logical
|
|
3141
3155
|
* path / body position, `message` the reason, `top` the envelope's top-level
|
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { a as resolveDbSpace, i as provideDbSpace, n as clearDbSpaces, r as closeDbSpaces, t as DEFAULT_DB_SPACE } from "./db-space-registry-Cdb0FGCn.mjs";
|
|
2
|
-
import { ValidatorError, defineAnnotatedType, isAnnotatedType, serializeAnnotatedType, throwFeatureDisabled } from "@atscript/typescript/utils";
|
|
2
|
+
import { $ak, $nr, ValidatorError, defineAnnotatedType, isAnnotatedType, serializeAnnotatedType, throwFeatureDisabled } from "@atscript/typescript/utils";
|
|
3
|
+
import { ADAPTER_FILTER_REASON, ALL_AGGREGATE_FNS, DbError, ENCRYPTED_REASON, acceptedOperatorsHint, aliasTargetOf, andFilters, bucketSourceVerdict, canFilterLeaf, checkHavingKeys, classifyQueryPath, collectQueryPaths, collectQueryPaths as collectQueryPaths$1, containsRelationFilter, findAncestorInSet, geoIndexNotFoundMessage, groupSourceVerdict, hasRelationOp, isEmptyObject, isJsonValueField, isNavRelation, isPlainObject, isRelationOp, narrowerFilterOps, nestDottedWith, normalizeComputedSelect, numericOperandProblem, reconcileCas, resolveDesignType, searchIndexNotFoundMessage, searchMemberKind, selfOrAncestor, soleUnionMember, unsupportedOperatorMessage, vectorIndexNotFoundMessage } from "@atscript/db";
|
|
3
4
|
import { Body, Delete, Get, HttpError, MoostHttp, Patch, Post, Put, Query, Url, httpKind, prerenderJson } from "@moostjs/event-http";
|
|
4
5
|
import { ApplyDecorators, Controller, Inherit, Inject, Intercept, Moost, Optional, Param, Pipe, Provide, Resolve, TInterceptorPriority, TPipePriority, current, defineBeforeInterceptor, defineInterceptor, definePipeFn, getMoostMate, useControllerContext, withControllerContext } from "moost";
|
|
5
6
|
import { parseUrl } from "@uniqu/url";
|
|
6
|
-
import { ADAPTER_FILTER_REASON, ALL_AGGREGATE_FNS, DbError, ENCRYPTED_REASON, acceptedOperatorsHint, aliasTargetOf, andFilters, bucketSourceVerdict, canFilterLeaf, checkHavingKeys, classifyQueryPath, collectQueryPaths, collectQueryPaths as collectQueryPaths$1, containsRelationFilter, findAncestorInSet, geoIndexNotFoundMessage, groupSourceVerdict, hasRelationOp, isEmptyObject, isJsonValueField, isNavRelation, isPlainObject, isRelationOp, narrowerFilterOps, normalizeComputedSelect, numericOperandProblem, reconcileCas, resolveDesignType, searchIndexNotFoundMessage, searchMemberKind, selfOrAncestor, unsupportedOperatorMessage, vectorIndexNotFoundMessage } from "@atscript/db";
|
|
7
7
|
import { cached, current as current$1, defineWook, key, routeParamsKey, useRouteParams } from "@wooksjs/event-core";
|
|
8
8
|
import { useBody } from "@wooksjs/http-body";
|
|
9
9
|
import { BUCKET_UNITS } from "@uniqu/core";
|
|
@@ -13,12 +13,14 @@ import { buildMemoryPredicate, projectRow, sortRows } from "@atscript/db-memory"
|
|
|
13
13
|
* The structured error envelope every `moost-db` rejection uses (since
|
|
14
14
|
* 0.1.128 all of them do): `{ message, statusCode, errors: [{ path, message }] }`
|
|
15
15
|
* — the same shape the validation interceptor renders for `ValidatorError` /
|
|
16
|
-
* `DbError`, so clients parse one format.
|
|
16
|
+
* `DbError`, so clients parse one format. A `DbError` adds its `code` and
|
|
17
|
+
* each entry's `code` / `params` (since 0.1.165).
|
|
17
18
|
*/
|
|
18
|
-
function errorEnvelope(statusCode, message, errors) {
|
|
19
|
+
function errorEnvelope(statusCode, message, errors, code) {
|
|
19
20
|
return new HttpError(statusCode, {
|
|
20
21
|
message,
|
|
21
22
|
statusCode,
|
|
23
|
+
...code === void 0 ? {} : { code },
|
|
22
24
|
errors
|
|
23
25
|
});
|
|
24
26
|
}
|
|
@@ -94,7 +96,15 @@ const dbErrorCodeToStatus = {
|
|
|
94
96
|
function transformValidationError(error, reply) {
|
|
95
97
|
const validation = validatorErrorToHttp(error);
|
|
96
98
|
if (validation) reply(validation);
|
|
97
|
-
else if (error instanceof DbError)
|
|
99
|
+
else if (error instanceof DbError) {
|
|
100
|
+
const errors = error.errors.map(({ path, message, code, params }) => ({
|
|
101
|
+
path,
|
|
102
|
+
message,
|
|
103
|
+
...code === void 0 ? {} : { code },
|
|
104
|
+
...params === void 0 ? {} : { params }
|
|
105
|
+
}));
|
|
106
|
+
reply(errorEnvelope(dbErrorCodeToStatus[error.code] ?? 400, error.message, errors, error.code));
|
|
107
|
+
}
|
|
98
108
|
}
|
|
99
109
|
const validationErrorTransform = () => defineInterceptor({ error: transformValidationError }, TInterceptorPriority.BEFORE_ALL);
|
|
100
110
|
const UseValidationErrorTransform = () => Intercept(validationErrorTransform());
|
|
@@ -190,7 +200,9 @@ var SelectControlDto = class {
|
|
|
190
200
|
throwFeatureDisabled("JSON Schema", "jsonSchema", "emit.jsonSchema");
|
|
191
201
|
}
|
|
192
202
|
};
|
|
193
|
-
|
|
203
|
+
$ak("db.index.fulltext", "db.index.plain", "db.index.unique", "db.view.joins", "expect.pattern");
|
|
204
|
+
$nr("db.agg.avg", "db.agg.count", "db.agg.countDistinct", "db.agg.max", "db.agg.min", "db.agg.sum", "db.alias", "db.amount.currency.ref", "db.column", "db.column.collate", "db.column.derived", "db.column.dimension", "db.column.filterable", "db.column.measure", "db.column.precision", "db.column.renamed", "db.column.searchable", "db.column.sortable", "db.column.version", "db.column.version.exempt", "db.compute", "db.default", "db.default.increment", "db.default.now", "db.default.uuid", "db.depth.limit", "db.encrypted", "db.http.path", "db.ignore", "db.index.fulltext", "db.index.geo", "db.index.plain", "db.index.unique", "db.json", "db.onUpdate.inc", "db.onUpdate.now", "db.patch.strategy", "db.rel.FK", "db.rel.filter", "db.rel.filterable", "db.rel.from", "db.rel.onDelete", "db.rel.onUpdate", "db.rel.to", "db.rel.via", "db.schema", "db.search.filter", "db.search.vector", "db.search.vector.threshold", "db.sort.nulls", "db.space", "db.sync.method", "db.table", "db.table.filterable", "db.table.preferredId.uniqueIndex", "db.table.renamed", "db.table.sortable", "db.unit.ref", "db.view", "db.view.filter", "db.view.for", "db.view.having", "db.view.joins", "db.view.materialized", "db.view.renamed", "db.writeOnly", "meta.default", "meta.id", "meta.readonly", "meta.required");
|
|
205
|
+
defineAnnotatedType("object", QueryControlsDto).prop("$skip", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$limit", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$count", defineAnnotatedType().designType("boolean").tags("boolean").optional().$type).prop("$sort", defineAnnotatedType().ref(SortControlDto).optional().$type).prop("$nulls", defineAnnotatedType().ref(NullsControlDto).optional().$type).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().ref(SelectControlDto).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).$type).optional().$type).prop("$search", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$index", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$fuzzy", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$vector", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$threshold", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$with", defineAnnotatedType("array").of(defineAnnotatedType().ref(WithRelationDto).$type).optional().$type).prop("$actions", defineAnnotatedType().designType("boolean").tags("boolean").optional().$type);
|
|
194
206
|
defineAnnotatedType("object", PagesControlsDto).prop("$page", defineAnnotatedType().designType("string").tags("string").annotate("expect.pattern", {
|
|
195
207
|
pattern: "^\\d+$",
|
|
196
208
|
flags: "u",
|
|
@@ -199,9 +211,9 @@ defineAnnotatedType("object", PagesControlsDto).prop("$page", defineAnnotatedTyp
|
|
|
199
211
|
pattern: "^\\d+$",
|
|
200
212
|
flags: "u",
|
|
201
213
|
message: "Expected positive number"
|
|
202
|
-
}, true).optional().$type).prop("$sort", defineAnnotatedType().
|
|
203
|
-
defineAnnotatedType("object", GetOneControlsDto).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().
|
|
204
|
-
defineAnnotatedType("object", GeoControlsDto).prop("$center", defineAnnotatedType("union").item(defineAnnotatedType().designType("string").tags("string").$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("number").tags("number").$type).$type).optional().$type).prop("$maxDistance", defineAnnotatedType().designType("number").tags("number").optional().$type).prop("$minDistance", defineAnnotatedType().designType("number").tags("number").optional().$type).prop("$index", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().
|
|
214
|
+
}, true).optional().$type).prop("$sort", defineAnnotatedType().ref(SortControlDto).optional().$type).prop("$nulls", defineAnnotatedType().ref(NullsControlDto).optional().$type).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().ref(SelectControlDto).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).$type).optional().$type).prop("$search", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$index", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$fuzzy", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$vector", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$threshold", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$with", defineAnnotatedType("array").of(defineAnnotatedType().ref(WithRelationDto).$type).optional().$type).prop("$actions", defineAnnotatedType().designType("boolean").tags("boolean").optional().$type);
|
|
215
|
+
defineAnnotatedType("object", GetOneControlsDto).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().ref(SelectControlDto).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).$type).optional().$type).prop("$with", defineAnnotatedType("array").of(defineAnnotatedType().ref(WithRelationDto).$type).optional().$type).prop("$actions", defineAnnotatedType().designType("boolean").tags("boolean").optional().$type);
|
|
216
|
+
defineAnnotatedType("object", GeoControlsDto).prop("$center", defineAnnotatedType("union").item(defineAnnotatedType().designType("string").tags("string").$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("number").tags("number").$type).$type).optional().$type).prop("$maxDistance", defineAnnotatedType().designType("number").tags("number").optional().$type).prop("$minDistance", defineAnnotatedType().designType("number").tags("number").optional().$type).prop("$index", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().ref(SelectControlDto).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).$type).optional().$type).prop("$skip", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$limit", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$page", defineAnnotatedType().designType("string").tags("string").annotate("expect.pattern", {
|
|
205
217
|
pattern: "^\\d+$",
|
|
206
218
|
flags: "u",
|
|
207
219
|
message: "Expected positive number"
|
|
@@ -209,10 +221,10 @@ defineAnnotatedType("object", GeoControlsDto).prop("$center", defineAnnotatedTyp
|
|
|
209
221
|
pattern: "^\\d+$",
|
|
210
222
|
flags: "u",
|
|
211
223
|
message: "Expected positive number"
|
|
212
|
-
}, true).optional().$type).prop("$with", defineAnnotatedType("array").of(defineAnnotatedType().
|
|
213
|
-
defineAnnotatedType("object", WithRelationDto).prop("name", defineAnnotatedType().designType("string").tags("string").$type).prop("filter", defineAnnotatedType().
|
|
214
|
-
defineAnnotatedType("object", WithRelationControlsDto).prop("$skip", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$limit", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$sort", defineAnnotatedType().
|
|
215
|
-
defineAnnotatedType("object", WithFilterDto).propPattern(/./, defineAnnotatedType("union").item(defineAnnotatedType().designType("string").tags("string").$type).item(defineAnnotatedType().designType("number").tags("number").$type).item(defineAnnotatedType().designType("boolean").tags("boolean").$type).item(defineAnnotatedType().designType("null").tags("null").$type).item(defineAnnotatedType().
|
|
224
|
+
}, true).optional().$type).prop("$with", defineAnnotatedType("array").of(defineAnnotatedType().ref(WithRelationDto).$type).optional().$type).prop("$actions", defineAnnotatedType().designType("boolean").tags("boolean").optional().$type);
|
|
225
|
+
defineAnnotatedType("object", WithRelationDto).prop("name", defineAnnotatedType().designType("string").tags("string").$type).prop("filter", defineAnnotatedType().ref(WithFilterDto).optional().$type).prop("controls", defineAnnotatedType().ref(WithRelationControlsDto).optional().$type).prop("insights", defineAnnotatedType().ref(WithFilterDto).optional().$type);
|
|
226
|
+
defineAnnotatedType("object", WithRelationControlsDto).prop("$skip", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$limit", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$sort", defineAnnotatedType().ref(SortControlDto).optional().$type).prop("$nulls", defineAnnotatedType().ref(NullsControlDto).optional().$type).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().ref(SelectControlDto).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).$type).optional().$type).prop("$with", defineAnnotatedType("array").of(defineAnnotatedType().ref(WithRelationDto).$type).optional().$type);
|
|
227
|
+
defineAnnotatedType("object", WithFilterDto).propPattern(/./, defineAnnotatedType("union").item(defineAnnotatedType().designType("string").tags("string").$type).item(defineAnnotatedType().designType("number").tags("number").$type).item(defineAnnotatedType().designType("boolean").tags("boolean").$type).item(defineAnnotatedType().designType("null").tags("null").$type).item(defineAnnotatedType().ref(WithFilterDto).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().ref(WithFilterDto).$type).$type).$type);
|
|
216
228
|
defineAnnotatedType("object", SortControlDto).propPattern(/./, defineAnnotatedType("union").item(defineAnnotatedType().designType("number").value(1).$type).item(defineAnnotatedType().designType("number").value(-1).$type).$type);
|
|
217
229
|
defineAnnotatedType("object", NullsControlDto).propPattern(/./, defineAnnotatedType("union").item(defineAnnotatedType().designType("string").value("first").$type).item(defineAnnotatedType().designType("string").value("last").$type).$type);
|
|
218
230
|
defineAnnotatedType("object", SelectControlDto).propPattern(/./, defineAnnotatedType("union").item(defineAnnotatedType().designType("number").value(1).$type).item(defineAnnotatedType().designType("number").value(0).$type).$type);
|
|
@@ -2178,15 +2190,19 @@ function walk(node, def, shallow) {
|
|
|
2178
2190
|
const sProp = sType.props[name];
|
|
2179
2191
|
if (!sProp) continue;
|
|
2180
2192
|
if (isNavRelation(prop)) continue;
|
|
2193
|
+
const member = !prop.ref && sProp.ref === void 0 ? soleUnionMember(prop) : void 0;
|
|
2194
|
+
if (member?.ref?.field) {
|
|
2195
|
+
const terminal = resolveTerminalRef({
|
|
2196
|
+
...prop,
|
|
2197
|
+
ref: member.ref
|
|
2198
|
+
});
|
|
2199
|
+
if (terminal) applyTerminal(sProp, terminal, shallow, true);
|
|
2200
|
+
}
|
|
2181
2201
|
if (prop.ref?.field && sProp.ref && !("type" in sProp.ref.type)) {
|
|
2182
2202
|
const terminal = resolveTerminalRef(prop);
|
|
2183
2203
|
if (terminal) {
|
|
2184
2204
|
const direct = prop.ref.type();
|
|
2185
|
-
|
|
2186
|
-
field: terminal.field,
|
|
2187
|
-
type: shallow(terminal.type)
|
|
2188
|
-
};
|
|
2189
|
-
if (terminal.fk && sProp.metadata["db.rel.FK"] === void 0) sProp.metadata["db.rel.FK"] = true;
|
|
2205
|
+
applyTerminal(sProp, terminal, shallow, terminal.type !== direct || terminal.field !== prop.ref.field);
|
|
2190
2206
|
}
|
|
2191
2207
|
}
|
|
2192
2208
|
walk(sProp, prop, shallow);
|
|
@@ -2199,6 +2215,17 @@ function walk(node, def, shallow) {
|
|
|
2199
2215
|
if (sType.kind === "array" && sType.of && of) walk(sType.of, of, shallow);
|
|
2200
2216
|
}
|
|
2201
2217
|
}
|
|
2218
|
+
/**
|
|
2219
|
+
* Points a serialized prop at its terminal field (when `repoint`) and
|
|
2220
|
+
* inherits the chain's FK marker.
|
|
2221
|
+
*/
|
|
2222
|
+
function applyTerminal(sProp, terminal, shallow, repoint) {
|
|
2223
|
+
if (repoint) sProp.ref = {
|
|
2224
|
+
field: terminal.field,
|
|
2225
|
+
type: shallow(terminal.type)
|
|
2226
|
+
};
|
|
2227
|
+
if (terminal.fk && sProp.metadata["db.rel.FK"] === void 0) sProp.metadata["db.rel.FK"] = true;
|
|
2228
|
+
}
|
|
2202
2229
|
//#endregion
|
|
2203
2230
|
//#region src/meta/meta-cache.ts
|
|
2204
2231
|
/**
|
|
@@ -2995,11 +3022,13 @@ let AsReadableController = _AsReadableController = class AsReadableController {
|
|
|
2995
3022
|
* an unhandled 500. Quote such values: `?name='json-w1'`.
|
|
2996
3023
|
*/
|
|
2997
3024
|
parseUrlOr400(queryString) {
|
|
3025
|
+
let parsed;
|
|
2998
3026
|
try {
|
|
2999
|
-
|
|
3027
|
+
parsed = parseUrl(queryString);
|
|
3000
3028
|
} catch (error) {
|
|
3001
3029
|
throw badRequest("", `Malformed query string: ${error instanceof Error ? error.message : String(error)}`);
|
|
3002
3030
|
}
|
|
3031
|
+
return nestParsedWith(parsed);
|
|
3003
3032
|
}
|
|
3004
3033
|
/**
|
|
3005
3034
|
* Parse a URL keeping only `$*` control keywords; report whether any
|
|
@@ -3311,6 +3340,38 @@ function applyMetaHeaders(response, caching) {
|
|
|
3311
3340
|
}
|
|
3312
3341
|
response.setHeader(varyKey, tokens.join(", "));
|
|
3313
3342
|
}
|
|
3343
|
+
/**
|
|
3344
|
+
* A dotted `$with` (`$with=a.b(…)`) rewritten into its nested form
|
|
3345
|
+
* (`$with=a($with=b(…))`) right after parsing (since 0.1.165), so every
|
|
3346
|
+
* per-level step — relation-name checks, write-only vetoes, seals, row-scope
|
|
3347
|
+
* overlays — sees one relation per level. The intermediate levels get the
|
|
3348
|
+
* `$with` insight their nested form records. Unchanged when no entry is dotted.
|
|
3349
|
+
*/
|
|
3350
|
+
function nestParsedWith(parsed) {
|
|
3351
|
+
const controls = parsed.controls;
|
|
3352
|
+
const withRels = controls?.$with;
|
|
3353
|
+
if (!Array.isArray(withRels) || withRels.length === 0) return parsed;
|
|
3354
|
+
const nested = nestDottedWith(withRels);
|
|
3355
|
+
if (nested === withRels) return parsed;
|
|
3356
|
+
const insights = parsed.insights;
|
|
3357
|
+
if (insights) for (const path of insights.keys()) {
|
|
3358
|
+
if (!insights.get(path)?.has("$with")) continue;
|
|
3359
|
+
const segments = path.split(".");
|
|
3360
|
+
for (let i = 1; i < segments.length; i++) {
|
|
3361
|
+
const prefix = segments.slice(0, i).join(".");
|
|
3362
|
+
let ops = insights.get(prefix);
|
|
3363
|
+
if (!ops) insights.set(prefix, ops = /* @__PURE__ */ new Set());
|
|
3364
|
+
ops.add("$with");
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
return {
|
|
3368
|
+
...parsed,
|
|
3369
|
+
controls: {
|
|
3370
|
+
...controls,
|
|
3371
|
+
$with: nested
|
|
3372
|
+
}
|
|
3373
|
+
};
|
|
3374
|
+
}
|
|
3314
3375
|
//#endregion
|
|
3315
3376
|
//#region src/actions/verdict.ts
|
|
3316
3377
|
/** Assert that a `disabled` predicate returned an array of the expected length; throws HTTP 500 otherwise. */
|
|
@@ -5506,12 +5567,16 @@ let AsDbReadableController = _AsDbReadableController = class AsDbReadableControl
|
|
|
5506
5567
|
return this._indexFieldPathsCache ??= [...this._searchIndexFieldPaths(), ...this._geoIndexFieldPaths()];
|
|
5507
5568
|
}
|
|
5508
5569
|
_searchIndexFieldPaths() {
|
|
5509
|
-
const out = (typeof this.readable.getSearchIndexes === "function" ? this.readable.getSearchIndexes() : []).map((info) =>
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5570
|
+
const out = (typeof this.readable.getSearchIndexes === "function" ? this.readable.getSearchIndexes() : []).map((info) => {
|
|
5571
|
+
const entry = {
|
|
5572
|
+
name: info.name,
|
|
5573
|
+
type: info.type === "vector" ? "vector" : "text",
|
|
5574
|
+
fields: info.fields ?? this._invertibleFields,
|
|
5575
|
+
isDefault: info.isDefault === true
|
|
5576
|
+
};
|
|
5577
|
+
if (info.aliases?.length) entry.aliases = info.aliases;
|
|
5578
|
+
return entry;
|
|
5579
|
+
});
|
|
5515
5580
|
for (const type of ["text", "vector"]) {
|
|
5516
5581
|
const ofType = out.filter((entry) => entry.type === type);
|
|
5517
5582
|
if (ofType.some((entry) => entry.isDefault)) continue;
|
|
@@ -5600,7 +5665,8 @@ let AsDbReadableController = _AsDbReadableController = class AsDbReadableControl
|
|
|
5600
5665
|
}
|
|
5601
5666
|
if (!this.readable.isSearchable()) return void 0;
|
|
5602
5667
|
if (name) {
|
|
5603
|
-
const
|
|
5668
|
+
const text = this.indexFieldPaths().filter((e) => e.type === "text");
|
|
5669
|
+
const entry = text.find((e) => e.name === name) ?? text.find((e) => e.aliases?.includes(name) === true);
|
|
5604
5670
|
if (!entry || !this._indexVisible(entry)) return badRequest("$index", searchIndexNotFoundMessage(name));
|
|
5605
5671
|
return;
|
|
5606
5672
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/moost-db",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.165",
|
|
4
4
|
"description": "Generic database controller for Moost with Atscript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"annotations",
|
|
@@ -44,29 +44,29 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@uniqu/url": "^0.1.15",
|
|
47
|
-
"@atscript/db-memory": "^0.1.
|
|
47
|
+
"@atscript/db-memory": "^0.1.165"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@atscript/core": "^0.1
|
|
51
|
-
"@atscript/typescript": "^0.1
|
|
52
|
-
"@moostjs/event-http": "^0.
|
|
50
|
+
"@atscript/core": "^0.2.1",
|
|
51
|
+
"@atscript/typescript": "^0.2.1",
|
|
52
|
+
"@moostjs/event-http": "^0.7.0",
|
|
53
53
|
"@uniqu/core": "^0.1.15",
|
|
54
|
-
"@wooksjs/event-core": "^0.
|
|
55
|
-
"@wooksjs/event-http": "^0.
|
|
56
|
-
"@wooksjs/http-body": "^0.
|
|
54
|
+
"@wooksjs/event-core": "^0.8.0",
|
|
55
|
+
"@wooksjs/event-http": "^0.8.0",
|
|
56
|
+
"@wooksjs/http-body": "^0.8.0",
|
|
57
57
|
"better-sqlite3": "^12.6.2",
|
|
58
|
-
"moost": "^0.
|
|
59
|
-
"unplugin-atscript": "^0.1
|
|
60
|
-
"@atscript/db-sqlite": "^0.1.
|
|
58
|
+
"moost": "^0.7.0",
|
|
59
|
+
"unplugin-atscript": "^0.2.1",
|
|
60
|
+
"@atscript/db-sqlite": "^0.1.165"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
|
-
"@atscript/typescript": "^0.1
|
|
64
|
-
"@moostjs/event-http": "^0.
|
|
63
|
+
"@atscript/typescript": "^0.2.1",
|
|
64
|
+
"@moostjs/event-http": "^0.7.0",
|
|
65
65
|
"@uniqu/core": "^0.1.15",
|
|
66
|
-
"@wooksjs/event-core": "^0.
|
|
67
|
-
"@wooksjs/http-body": "^0.
|
|
68
|
-
"moost": "^0.
|
|
69
|
-
"@atscript/db": "^0.1.
|
|
66
|
+
"@wooksjs/event-core": "^0.8.0",
|
|
67
|
+
"@wooksjs/http-body": "^0.8.0",
|
|
68
|
+
"moost": "^0.7.0",
|
|
69
|
+
"@atscript/db": "^0.1.165"
|
|
70
70
|
},
|
|
71
71
|
"scripts": {
|
|
72
72
|
"build": "vp pack",
|