@atscript/moost-db 0.1.152 → 0.1.153
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 +69 -15
- package/dist/index.d.cts +24 -5
- package/dist/index.d.mts +24 -5
- package/dist/index.mjs +67 -13
- package/package.json +20 -20
package/dist/index.cjs
CHANGED
|
@@ -85,6 +85,9 @@ const dbErrorCodeToStatus = {
|
|
|
85
85
|
CAS_MISMATCH: 409,
|
|
86
86
|
TX_WAIT_TIMEOUT: 503,
|
|
87
87
|
SPACE_CLOSED: 503,
|
|
88
|
+
DEADLOCK: 503,
|
|
89
|
+
LOCK_TIMEOUT: 503,
|
|
90
|
+
SERIALIZATION_FAILURE: 503,
|
|
88
91
|
BUCKET_TZ_UNAVAILABLE: 501
|
|
89
92
|
};
|
|
90
93
|
function transformValidationError(error, reply) {
|
|
@@ -168,6 +171,15 @@ var SortControlDto = class {
|
|
|
168
171
|
(0, _atscript_typescript_utils.throwFeatureDisabled)("JSON Schema", "jsonSchema", "emit.jsonSchema");
|
|
169
172
|
}
|
|
170
173
|
};
|
|
174
|
+
var NullsControlDto = class {
|
|
175
|
+
static __is_atscript_annotated_type = true;
|
|
176
|
+
static type = {};
|
|
177
|
+
static metadata = /* @__PURE__ */ new Map();
|
|
178
|
+
static id = "NullsControlDto";
|
|
179
|
+
static toJsonSchema() {
|
|
180
|
+
(0, _atscript_typescript_utils.throwFeatureDisabled)("JSON Schema", "jsonSchema", "emit.jsonSchema");
|
|
181
|
+
}
|
|
182
|
+
};
|
|
171
183
|
var SelectControlDto = class {
|
|
172
184
|
static __is_atscript_annotated_type = true;
|
|
173
185
|
static type = {};
|
|
@@ -177,7 +189,7 @@ var SelectControlDto = class {
|
|
|
177
189
|
(0, _atscript_typescript_utils.throwFeatureDisabled)("JSON Schema", "jsonSchema", "emit.jsonSchema");
|
|
178
190
|
}
|
|
179
191
|
};
|
|
180
|
-
(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)().refTo(SortControlDto).optional().$type).prop("$select", (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().refTo(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)().refTo(WithRelationDto).$type).optional().$type).prop("$actions", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("boolean").tags("boolean").optional().$type);
|
|
192
|
+
(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)().refTo(SortControlDto).optional().$type).prop("$nulls", (0, _atscript_typescript_utils.defineAnnotatedType)().refTo(NullsControlDto).optional().$type).prop("$select", (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().refTo(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)().refTo(WithRelationDto).$type).optional().$type).prop("$actions", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("boolean").tags("boolean").optional().$type);
|
|
181
193
|
(0, _atscript_typescript_utils.defineAnnotatedType)("object", PagesControlsDto).prop("$page", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").annotate("expect.pattern", {
|
|
182
194
|
pattern: "^\\d+$",
|
|
183
195
|
flags: "u",
|
|
@@ -186,7 +198,7 @@ var SelectControlDto = class {
|
|
|
186
198
|
pattern: "^\\d+$",
|
|
187
199
|
flags: "u",
|
|
188
200
|
message: "Expected positive number"
|
|
189
|
-
}, true).optional().$type).prop("$sort", (0, _atscript_typescript_utils.defineAnnotatedType)().refTo(SortControlDto).optional().$type).prop("$select", (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().refTo(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)().refTo(WithRelationDto).$type).optional().$type).prop("$actions", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("boolean").tags("boolean").optional().$type);
|
|
201
|
+
}, true).optional().$type).prop("$sort", (0, _atscript_typescript_utils.defineAnnotatedType)().refTo(SortControlDto).optional().$type).prop("$nulls", (0, _atscript_typescript_utils.defineAnnotatedType)().refTo(NullsControlDto).optional().$type).prop("$select", (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().refTo(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)().refTo(WithRelationDto).$type).optional().$type).prop("$actions", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("boolean").tags("boolean").optional().$type);
|
|
190
202
|
(0, _atscript_typescript_utils.defineAnnotatedType)("object", GetOneControlsDto).prop("$select", (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().refTo(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)().refTo(WithRelationDto).$type).optional().$type).prop("$actions", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("boolean").tags("boolean").optional().$type);
|
|
191
203
|
(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)().refTo(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", {
|
|
192
204
|
pattern: "^\\d+$",
|
|
@@ -198,9 +210,10 @@ var SelectControlDto = class {
|
|
|
198
210
|
message: "Expected positive number"
|
|
199
211
|
}, true).optional().$type).prop("$with", (0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().refTo(WithRelationDto).$type).optional().$type).prop("$actions", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("boolean").tags("boolean").optional().$type);
|
|
200
212
|
(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)().refTo(WithFilterDto).optional().$type).prop("controls", (0, _atscript_typescript_utils.defineAnnotatedType)().refTo(WithRelationControlsDto).optional().$type).prop("insights", (0, _atscript_typescript_utils.defineAnnotatedType)().refTo(WithFilterDto).optional().$type);
|
|
201
|
-
(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)().refTo(SortControlDto).optional().$type).prop("$select", (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().refTo(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)().refTo(WithRelationDto).$type).optional().$type);
|
|
213
|
+
(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)().refTo(SortControlDto).optional().$type).prop("$nulls", (0, _atscript_typescript_utils.defineAnnotatedType)().refTo(NullsControlDto).optional().$type).prop("$select", (0, _atscript_typescript_utils.defineAnnotatedType)("union").item((0, _atscript_typescript_utils.defineAnnotatedType)().refTo(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)().refTo(WithRelationDto).$type).optional().$type);
|
|
202
214
|
(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)().refTo(WithFilterDto).$type).item((0, _atscript_typescript_utils.defineAnnotatedType)("array").of((0, _atscript_typescript_utils.defineAnnotatedType)().refTo(WithFilterDto).$type).$type).$type);
|
|
203
215
|
(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);
|
|
216
|
+
(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);
|
|
204
217
|
(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);
|
|
205
218
|
//#endregion
|
|
206
219
|
//#region src/mate.ts
|
|
@@ -2861,7 +2874,7 @@ let AsReadableController = _AsReadableController = class AsReadableController {
|
|
|
2861
2874
|
* keys the shared db validator plugin reads in db-client (`db.json`,
|
|
2862
2875
|
* `db.patch.strategy`, `db.default*`, `db.column.version`,
|
|
2863
2876
|
* `db.column.derived`) and the client-facing `db.http.path` /
|
|
2864
|
-
* `db.writeOnly`; strips every other `db.*` (table, column, index, etc.).
|
|
2877
|
+
* `db.writeOnly` / `db.sort.nulls`; strips every other `db.*` (table, column, index, etc.).
|
|
2865
2878
|
* Override in subclass to customise — keep the validator keys, or client
|
|
2866
2879
|
* preflight diverges from the server.
|
|
2867
2880
|
*/
|
|
@@ -2873,7 +2886,7 @@ let AsReadableController = _AsReadableController = class AsReadableController {
|
|
|
2873
2886
|
key,
|
|
2874
2887
|
value
|
|
2875
2888
|
};
|
|
2876
|
-
if (key === "db.json" || key === "db.patch.strategy" || key.startsWith("db.default") || key === "db.http.path" || key === "db.writeOnly" || key === "db.column.version" || key === "db.column.derived") return {
|
|
2889
|
+
if (key === "db.json" || key === "db.patch.strategy" || key.startsWith("db.default") || key === "db.http.path" || key === "db.writeOnly" || key === "db.column.version" || key === "db.column.derived" || key === "db.sort.nulls") return {
|
|
2877
2890
|
key,
|
|
2878
2891
|
value
|
|
2879
2892
|
};
|
|
@@ -3499,7 +3512,7 @@ function validateMultiId(body, source, maxIds = Infinity) {
|
|
|
3499
3512
|
}
|
|
3500
3513
|
function collectIdErrors(value, source, opts) {
|
|
3501
3514
|
const pathPrefix = opts.path ?? "";
|
|
3502
|
-
if (!isPlainObject$
|
|
3515
|
+
if (!isPlainObject$3(value)) return [{
|
|
3503
3516
|
path: pathPrefix,
|
|
3504
3517
|
message: "Expected JSON object for row identifier",
|
|
3505
3518
|
details: []
|
|
@@ -3550,7 +3563,7 @@ function describe(value) {
|
|
|
3550
3563
|
if (Array.isArray(value)) return "array";
|
|
3551
3564
|
return typeof value;
|
|
3552
3565
|
}
|
|
3553
|
-
function isPlainObject$
|
|
3566
|
+
function isPlainObject$3(value) {
|
|
3554
3567
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3555
3568
|
}
|
|
3556
3569
|
//#endregion
|
|
@@ -5338,7 +5351,10 @@ let AsDbReadableController = _AsDbReadableController = class AsDbReadableControl
|
|
|
5338
5351
|
if (sealed.size === 0) return void 0;
|
|
5339
5352
|
const refs = (0, _atscript_db.collectQueryPaths)({
|
|
5340
5353
|
filter: rel.filter,
|
|
5341
|
-
controls: {
|
|
5354
|
+
controls: {
|
|
5355
|
+
$sort: nested.$sort ?? rel.$sort,
|
|
5356
|
+
$nulls: nested.$nulls ?? rel.$nulls
|
|
5357
|
+
}
|
|
5342
5358
|
});
|
|
5343
5359
|
const filtered = refs.filter.find((ref) => (0, _atscript_db.selfOrAncestor)(ref.path, sealed) !== void 0)?.path;
|
|
5344
5360
|
if (filtered !== void 0) return writeOnlyError(`${path}.${filtered}`, "filter");
|
|
@@ -7053,7 +7069,29 @@ let AsDbReadableController = _AsDbReadableController = class AsDbReadableControl
|
|
|
7053
7069
|
})).data;
|
|
7054
7070
|
}
|
|
7055
7071
|
/**
|
|
7056
|
-
*
|
|
7072
|
+
* The `/pages` read without a `$sort` (since 0.1.153): ordered by the
|
|
7073
|
+
* primary key, ascending, so consecutive pages neither overlap nor skip
|
|
7074
|
+
* rows. A request with `$sort` keeps it (the core appends the primary key
|
|
7075
|
+
* as the tie-breaker); a source without a primary key (a view without
|
|
7076
|
+
* `@meta.id`) stays unordered. Only the plain read: text / vector search
|
|
7077
|
+
* keep their relevance order.
|
|
7078
|
+
*/
|
|
7079
|
+
_pagesOrder(q) {
|
|
7080
|
+
const controls = q.controls;
|
|
7081
|
+
const sort = controls?.$sort;
|
|
7082
|
+
const pk = this.readable.primaryKeys;
|
|
7083
|
+
if (sort && Object.keys(sort).length > 0 || pk.length === 0) return q;
|
|
7084
|
+
return {
|
|
7085
|
+
...q,
|
|
7086
|
+
controls: {
|
|
7087
|
+
...controls,
|
|
7088
|
+
$sort: Object.fromEntries(pk.map((f) => [f, 1]))
|
|
7089
|
+
}
|
|
7090
|
+
};
|
|
7091
|
+
}
|
|
7092
|
+
/**
|
|
7093
|
+
* **GET /pages** — returns paginated records with metadata. Without a
|
|
7094
|
+
* `$sort` the rows come in primary-key order (since 0.1.153).
|
|
7057
7095
|
*/
|
|
7058
7096
|
async pages(url) {
|
|
7059
7097
|
const { parsed, controls } = await this.parseRequest("pages", url);
|
|
@@ -7080,7 +7118,7 @@ let AsDbReadableController = _AsDbReadableController = class AsDbReadableControl
|
|
|
7080
7118
|
switch (strategy.kind) {
|
|
7081
7119
|
case "vector": return strategy.vectorField ? this.readable.vectorSearchWithCount(strategy.vectorField, strategy.vector, q) : this.readable.vectorSearchWithCount(strategy.vector, q);
|
|
7082
7120
|
case "search": return this.readable.searchWithCount(strategy.term, q, strategy.index);
|
|
7083
|
-
case "plain": return this.readable.findManyWithCount(q);
|
|
7121
|
+
case "plain": return this.readable.findManyWithCount(this._pagesOrder(q));
|
|
7084
7122
|
}
|
|
7085
7123
|
});
|
|
7086
7124
|
return {
|
|
@@ -7564,7 +7602,8 @@ let AsDbReadableController = _AsDbReadableController = class AsDbReadableControl
|
|
|
7564
7602
|
versionColumn: this.readable.versionColumn,
|
|
7565
7603
|
...capabilities.bucketUnits.length > 0 && { bucketUnits: [...capabilities.bucketUnits] },
|
|
7566
7604
|
aggregateFns: [...capabilities.aggregateFns],
|
|
7567
|
-
aggregateExpressions: capabilities.aggregateExpressions
|
|
7605
|
+
aggregateExpressions: capabilities.aggregateExpressions,
|
|
7606
|
+
...typeof this.readable.supportsNullsPlacement === "function" && this.readable.supportsNullsPlacement() && { nullsPlacement: true }
|
|
7568
7607
|
};
|
|
7569
7608
|
}
|
|
7570
7609
|
buildCrud() {
|
|
@@ -8164,7 +8203,22 @@ let AsValueHelpController = class AsValueHelpController extends AsReadableContro
|
|
|
8164
8203
|
return (await this.query(await this._scopedQuery(parsed.filter, controls))).data;
|
|
8165
8204
|
}
|
|
8166
8205
|
/**
|
|
8167
|
-
*
|
|
8206
|
+
* `/pages` controls without a `$sort` (since 0.1.153): ordered by the
|
|
8207
|
+
* primary key, ascending, so consecutive pages neither overlap nor skip
|
|
8208
|
+
* rows — like the DB controllers' `/pages`. A request with `$sort` or
|
|
8209
|
+
* `$search` keeps its order; a source without a primary key stays as is.
|
|
8210
|
+
*/
|
|
8211
|
+
_pagesOrder(controls) {
|
|
8212
|
+
const sort = controls.$sort;
|
|
8213
|
+
if ((Array.isArray(sort) ? sort.length > 0 : (0, _atscript_db.isPlainObject)(sort) ? !(0, _atscript_db.isEmptyObject)(sort) : !!sort) || controls.$search || !this.primaryKey) return controls;
|
|
8214
|
+
return {
|
|
8215
|
+
...controls,
|
|
8216
|
+
$sort: { [this.primaryKey]: 1 }
|
|
8217
|
+
};
|
|
8218
|
+
}
|
|
8219
|
+
/**
|
|
8220
|
+
* **GET /pages** — paginated row window plus total count. Without a
|
|
8221
|
+
* `$sort` the rows come in primary-key order (since 0.1.153).
|
|
8168
8222
|
*/
|
|
8169
8223
|
async runPages(url) {
|
|
8170
8224
|
const { parsed, controls } = await this.parseRequest("pages", url);
|
|
@@ -8173,11 +8227,11 @@ let AsValueHelpController = class AsValueHelpController extends AsReadableContro
|
|
|
8173
8227
|
const page = Math.max(Number(controls.$page || 1), 1);
|
|
8174
8228
|
const size = Math.max(Number(controls.$size || 10), 1);
|
|
8175
8229
|
const skip = (page - 1) * size;
|
|
8176
|
-
const result = await this.query(await this._scopedQuery(parsed.filter, {
|
|
8230
|
+
const result = await this.query(await this._scopedQuery(parsed.filter, this._pagesOrder({
|
|
8177
8231
|
...controls,
|
|
8178
8232
|
$skip: skip,
|
|
8179
8233
|
$limit: size
|
|
8180
|
-
}));
|
|
8234
|
+
})));
|
|
8181
8235
|
return {
|
|
8182
8236
|
data: result.data,
|
|
8183
8237
|
page,
|
|
@@ -8317,7 +8371,7 @@ let AsJsonValueHelpController = class AsJsonValueHelpController extends AsValueH
|
|
|
8317
8371
|
rows = rows.filter((row) => predicate(row));
|
|
8318
8372
|
}
|
|
8319
8373
|
const sort = this.normalizeSort(controls.controls.$sort);
|
|
8320
|
-
if (sort) rows = (0, _atscript_db_memory.sortRows)(rows, sort);
|
|
8374
|
+
if (sort) rows = (0, _atscript_db_memory.sortRows)(rows, sort, void 0, void 0, controls.controls.$nulls);
|
|
8321
8375
|
const total = rows.length;
|
|
8322
8376
|
const skip = Math.max(0, Number(controls.controls.$skip ?? 0));
|
|
8323
8377
|
const limit = Math.max(0, Number(controls.controls.$limit ?? total - skip));
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as resolveDbSpace, i as provideDbSpace, n as clearDbSpaces, r as closeDbSpaces, t as DEFAULT_DB_SPACE } from "./db-space-registry-fXB8LnGe.cjs";
|
|
2
2
|
import { TAtscriptAnnotatedType, TAtscriptDataType, TSerializeOptions, TSerializedAnnotatedType, TValidatorOptions, Validator } from "@atscript/typescript/utils";
|
|
3
|
-
import { AggregateFn, AtscriptDbReadable, AtscriptDbTable, BucketUnit, FilterExpr, FlatOf, 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, Uniquery, UniqueryControls, collectQueryPaths } from "@atscript/db";
|
|
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, Uniquery, UniqueryControls, collectQueryPaths } from "@atscript/db";
|
|
4
4
|
import { HttpError } from "@moostjs/event-http";
|
|
5
5
|
import { Mate, Moost, TConsoleBase, TMateParamMeta, TMoostMetadata } from "moost";
|
|
6
6
|
import { parseUrl } from "@uniqu/url";
|
|
@@ -243,7 +243,7 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
243
243
|
* keys the shared db validator plugin reads in db-client (`db.json`,
|
|
244
244
|
* `db.patch.strategy`, `db.default*`, `db.column.version`,
|
|
245
245
|
* `db.column.derived`) and the client-facing `db.http.path` /
|
|
246
|
-
* `db.writeOnly`; strips every other `db.*` (table, column, index, etc.).
|
|
246
|
+
* `db.writeOnly` / `db.sort.nulls`; strips every other `db.*` (table, column, index, etc.).
|
|
247
247
|
* Override in subclass to customise — keep the validator keys, or client
|
|
248
248
|
* preflight diverges from the server.
|
|
249
249
|
*/
|
|
@@ -2076,7 +2076,17 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
2076
2076
|
*/
|
|
2077
2077
|
query(url: string): Promise<DataType[] | number | HttpError>;
|
|
2078
2078
|
/**
|
|
2079
|
-
*
|
|
2079
|
+
* The `/pages` read without a `$sort` (since 0.1.153): ordered by the
|
|
2080
|
+
* primary key, ascending, so consecutive pages neither overlap nor skip
|
|
2081
|
+
* rows. A request with `$sort` keeps it (the core appends the primary key
|
|
2082
|
+
* as the tie-breaker); a source without a primary key (a view without
|
|
2083
|
+
* `@meta.id`) stays unordered. Only the plain read: text / vector search
|
|
2084
|
+
* keep their relevance order.
|
|
2085
|
+
*/
|
|
2086
|
+
private _pagesOrder;
|
|
2087
|
+
/**
|
|
2088
|
+
* **GET /pages** — returns paginated records with metadata. Without a
|
|
2089
|
+
* `$sort` the rows come in primary-key order (since 0.1.153).
|
|
2080
2090
|
*/
|
|
2081
2091
|
pages(url: string): Promise<{
|
|
2082
2092
|
data: DataType[];
|
|
@@ -2464,7 +2474,8 @@ interface ValueHelpQuery<T> {
|
|
|
2464
2474
|
$limit?: number;
|
|
2465
2475
|
$search?: string;
|
|
2466
2476
|
$select?: (keyof T | string)[];
|
|
2467
|
-
$sort?: unknown;
|
|
2477
|
+
$sort?: unknown; /** NULL placement per `$sort` key (URL `$sort=-name:last`; since 0.1.153). */
|
|
2478
|
+
$nulls?: Partial<Record<string, NullsPlacement>>;
|
|
2468
2479
|
[key: string]: unknown;
|
|
2469
2480
|
};
|
|
2470
2481
|
}
|
|
@@ -2577,7 +2588,15 @@ declare abstract class AsValueHelpController<T extends TAtscriptAnnotatedType =
|
|
|
2577
2588
|
*/
|
|
2578
2589
|
runQuery(url: string): Promise<DataType[] | HttpError>;
|
|
2579
2590
|
/**
|
|
2580
|
-
*
|
|
2591
|
+
* `/pages` controls without a `$sort` (since 0.1.153): ordered by the
|
|
2592
|
+
* primary key, ascending, so consecutive pages neither overlap nor skip
|
|
2593
|
+
* rows — like the DB controllers' `/pages`. A request with `$sort` or
|
|
2594
|
+
* `$search` keeps its order; a source without a primary key stays as is.
|
|
2595
|
+
*/
|
|
2596
|
+
private _pagesOrder;
|
|
2597
|
+
/**
|
|
2598
|
+
* **GET /pages** — paginated row window plus total count. Without a
|
|
2599
|
+
* `$sort` the rows come in primary-key order (since 0.1.153).
|
|
2581
2600
|
*/
|
|
2582
2601
|
runPages(url: string): Promise<{
|
|
2583
2602
|
data: DataType[];
|
package/dist/index.d.mts
CHANGED
|
@@ -3,7 +3,7 @@ import { TAtscriptAnnotatedType, TAtscriptDataType, TSerializeOptions, TSerializ
|
|
|
3
3
|
import { HttpError, MoostHttp } from "@moostjs/event-http";
|
|
4
4
|
import { Mate, Moost, TConsoleBase, TMateParamMeta, TMoostMetadata } from "moost";
|
|
5
5
|
import { parseUrl } from "@uniqu/url";
|
|
6
|
-
import { AggregateFn, AtscriptDbReadable, AtscriptDbTable, BucketUnit, FilterExpr, FlatOf, 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, Uniquery, UniqueryControls, collectQueryPaths } from "@atscript/db";
|
|
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, 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
|
|
@@ -243,7 +243,7 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
243
243
|
* keys the shared db validator plugin reads in db-client (`db.json`,
|
|
244
244
|
* `db.patch.strategy`, `db.default*`, `db.column.version`,
|
|
245
245
|
* `db.column.derived`) and the client-facing `db.http.path` /
|
|
246
|
-
* `db.writeOnly`; strips every other `db.*` (table, column, index, etc.).
|
|
246
|
+
* `db.writeOnly` / `db.sort.nulls`; strips every other `db.*` (table, column, index, etc.).
|
|
247
247
|
* Override in subclass to customise — keep the validator keys, or client
|
|
248
248
|
* preflight diverges from the server.
|
|
249
249
|
*/
|
|
@@ -2076,7 +2076,17 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
2076
2076
|
*/
|
|
2077
2077
|
query(url: string): Promise<DataType[] | number | HttpError>;
|
|
2078
2078
|
/**
|
|
2079
|
-
*
|
|
2079
|
+
* The `/pages` read without a `$sort` (since 0.1.153): ordered by the
|
|
2080
|
+
* primary key, ascending, so consecutive pages neither overlap nor skip
|
|
2081
|
+
* rows. A request with `$sort` keeps it (the core appends the primary key
|
|
2082
|
+
* as the tie-breaker); a source without a primary key (a view without
|
|
2083
|
+
* `@meta.id`) stays unordered. Only the plain read: text / vector search
|
|
2084
|
+
* keep their relevance order.
|
|
2085
|
+
*/
|
|
2086
|
+
private _pagesOrder;
|
|
2087
|
+
/**
|
|
2088
|
+
* **GET /pages** — returns paginated records with metadata. Without a
|
|
2089
|
+
* `$sort` the rows come in primary-key order (since 0.1.153).
|
|
2080
2090
|
*/
|
|
2081
2091
|
pages(url: string): Promise<{
|
|
2082
2092
|
data: DataType[];
|
|
@@ -2464,7 +2474,8 @@ interface ValueHelpQuery<T> {
|
|
|
2464
2474
|
$limit?: number;
|
|
2465
2475
|
$search?: string;
|
|
2466
2476
|
$select?: (keyof T | string)[];
|
|
2467
|
-
$sort?: unknown;
|
|
2477
|
+
$sort?: unknown; /** NULL placement per `$sort` key (URL `$sort=-name:last`; since 0.1.153). */
|
|
2478
|
+
$nulls?: Partial<Record<string, NullsPlacement>>;
|
|
2468
2479
|
[key: string]: unknown;
|
|
2469
2480
|
};
|
|
2470
2481
|
}
|
|
@@ -2577,7 +2588,15 @@ declare abstract class AsValueHelpController<T extends TAtscriptAnnotatedType =
|
|
|
2577
2588
|
*/
|
|
2578
2589
|
runQuery(url: string): Promise<DataType[] | HttpError>;
|
|
2579
2590
|
/**
|
|
2580
|
-
*
|
|
2591
|
+
* `/pages` controls without a `$sort` (since 0.1.153): ordered by the
|
|
2592
|
+
* primary key, ascending, so consecutive pages neither overlap nor skip
|
|
2593
|
+
* rows — like the DB controllers' `/pages`. A request with `$sort` or
|
|
2594
|
+
* `$search` keeps its order; a source without a primary key stays as is.
|
|
2595
|
+
*/
|
|
2596
|
+
private _pagesOrder;
|
|
2597
|
+
/**
|
|
2598
|
+
* **GET /pages** — paginated row window plus total count. Without a
|
|
2599
|
+
* `$sort` the rows come in primary-key order (since 0.1.153).
|
|
2581
2600
|
*/
|
|
2582
2601
|
runPages(url: string): Promise<{
|
|
2583
2602
|
data: DataType[];
|
package/dist/index.mjs
CHANGED
|
@@ -84,6 +84,9 @@ const dbErrorCodeToStatus = {
|
|
|
84
84
|
CAS_MISMATCH: 409,
|
|
85
85
|
TX_WAIT_TIMEOUT: 503,
|
|
86
86
|
SPACE_CLOSED: 503,
|
|
87
|
+
DEADLOCK: 503,
|
|
88
|
+
LOCK_TIMEOUT: 503,
|
|
89
|
+
SERIALIZATION_FAILURE: 503,
|
|
87
90
|
BUCKET_TZ_UNAVAILABLE: 501
|
|
88
91
|
};
|
|
89
92
|
function transformValidationError(error, reply) {
|
|
@@ -167,6 +170,15 @@ var SortControlDto = class {
|
|
|
167
170
|
throwFeatureDisabled("JSON Schema", "jsonSchema", "emit.jsonSchema");
|
|
168
171
|
}
|
|
169
172
|
};
|
|
173
|
+
var NullsControlDto = class {
|
|
174
|
+
static __is_atscript_annotated_type = true;
|
|
175
|
+
static type = {};
|
|
176
|
+
static metadata = /* @__PURE__ */ new Map();
|
|
177
|
+
static id = "NullsControlDto";
|
|
178
|
+
static toJsonSchema() {
|
|
179
|
+
throwFeatureDisabled("JSON Schema", "jsonSchema", "emit.jsonSchema");
|
|
180
|
+
}
|
|
181
|
+
};
|
|
170
182
|
var SelectControlDto = class {
|
|
171
183
|
static __is_atscript_annotated_type = true;
|
|
172
184
|
static type = {};
|
|
@@ -176,7 +188,7 @@ var SelectControlDto = class {
|
|
|
176
188
|
throwFeatureDisabled("JSON Schema", "jsonSchema", "emit.jsonSchema");
|
|
177
189
|
}
|
|
178
190
|
};
|
|
179
|
-
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().refTo(SortControlDto).optional().$type).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().refTo(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().refTo(WithRelationDto).$type).optional().$type).prop("$actions", defineAnnotatedType().designType("boolean").tags("boolean").optional().$type);
|
|
191
|
+
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().refTo(SortControlDto).optional().$type).prop("$nulls", defineAnnotatedType().refTo(NullsControlDto).optional().$type).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().refTo(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().refTo(WithRelationDto).$type).optional().$type).prop("$actions", defineAnnotatedType().designType("boolean").tags("boolean").optional().$type);
|
|
180
192
|
defineAnnotatedType("object", PagesControlsDto).prop("$page", defineAnnotatedType().designType("string").tags("string").annotate("expect.pattern", {
|
|
181
193
|
pattern: "^\\d+$",
|
|
182
194
|
flags: "u",
|
|
@@ -185,7 +197,7 @@ defineAnnotatedType("object", PagesControlsDto).prop("$page", defineAnnotatedTyp
|
|
|
185
197
|
pattern: "^\\d+$",
|
|
186
198
|
flags: "u",
|
|
187
199
|
message: "Expected positive number"
|
|
188
|
-
}, true).optional().$type).prop("$sort", defineAnnotatedType().refTo(SortControlDto).optional().$type).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().refTo(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().refTo(WithRelationDto).$type).optional().$type).prop("$actions", defineAnnotatedType().designType("boolean").tags("boolean").optional().$type);
|
|
200
|
+
}, true).optional().$type).prop("$sort", defineAnnotatedType().refTo(SortControlDto).optional().$type).prop("$nulls", defineAnnotatedType().refTo(NullsControlDto).optional().$type).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().refTo(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().refTo(WithRelationDto).$type).optional().$type).prop("$actions", defineAnnotatedType().designType("boolean").tags("boolean").optional().$type);
|
|
189
201
|
defineAnnotatedType("object", GetOneControlsDto).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().refTo(SelectControlDto).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).$type).optional().$type).prop("$with", defineAnnotatedType("array").of(defineAnnotatedType().refTo(WithRelationDto).$type).optional().$type).prop("$actions", defineAnnotatedType().designType("boolean").tags("boolean").optional().$type);
|
|
190
202
|
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().refTo(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", {
|
|
191
203
|
pattern: "^\\d+$",
|
|
@@ -197,9 +209,10 @@ defineAnnotatedType("object", GeoControlsDto).prop("$center", defineAnnotatedTyp
|
|
|
197
209
|
message: "Expected positive number"
|
|
198
210
|
}, true).optional().$type).prop("$with", defineAnnotatedType("array").of(defineAnnotatedType().refTo(WithRelationDto).$type).optional().$type).prop("$actions", defineAnnotatedType().designType("boolean").tags("boolean").optional().$type);
|
|
199
211
|
defineAnnotatedType("object", WithRelationDto).prop("name", defineAnnotatedType().designType("string").tags("string").$type).prop("filter", defineAnnotatedType().refTo(WithFilterDto).optional().$type).prop("controls", defineAnnotatedType().refTo(WithRelationControlsDto).optional().$type).prop("insights", defineAnnotatedType().refTo(WithFilterDto).optional().$type);
|
|
200
|
-
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().refTo(SortControlDto).optional().$type).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().refTo(SelectControlDto).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).$type).optional().$type).prop("$with", defineAnnotatedType("array").of(defineAnnotatedType().refTo(WithRelationDto).$type).optional().$type);
|
|
212
|
+
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().refTo(SortControlDto).optional().$type).prop("$nulls", defineAnnotatedType().refTo(NullsControlDto).optional().$type).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().refTo(SelectControlDto).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).$type).optional().$type).prop("$with", defineAnnotatedType("array").of(defineAnnotatedType().refTo(WithRelationDto).$type).optional().$type);
|
|
201
213
|
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().refTo(WithFilterDto).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().refTo(WithFilterDto).$type).$type).$type);
|
|
202
214
|
defineAnnotatedType("object", SortControlDto).propPattern(/./, defineAnnotatedType("union").item(defineAnnotatedType().designType("number").value(1).$type).item(defineAnnotatedType().designType("number").value(-1).$type).$type);
|
|
215
|
+
defineAnnotatedType("object", NullsControlDto).propPattern(/./, defineAnnotatedType("union").item(defineAnnotatedType().designType("string").value("first").$type).item(defineAnnotatedType().designType("string").value("last").$type).$type);
|
|
203
216
|
defineAnnotatedType("object", SelectControlDto).propPattern(/./, defineAnnotatedType("union").item(defineAnnotatedType().designType("number").value(1).$type).item(defineAnnotatedType().designType("number").value(0).$type).$type);
|
|
204
217
|
//#endregion
|
|
205
218
|
//#region src/mate.ts
|
|
@@ -2860,7 +2873,7 @@ let AsReadableController = _AsReadableController = class AsReadableController {
|
|
|
2860
2873
|
* keys the shared db validator plugin reads in db-client (`db.json`,
|
|
2861
2874
|
* `db.patch.strategy`, `db.default*`, `db.column.version`,
|
|
2862
2875
|
* `db.column.derived`) and the client-facing `db.http.path` /
|
|
2863
|
-
* `db.writeOnly`; strips every other `db.*` (table, column, index, etc.).
|
|
2876
|
+
* `db.writeOnly` / `db.sort.nulls`; strips every other `db.*` (table, column, index, etc.).
|
|
2864
2877
|
* Override in subclass to customise — keep the validator keys, or client
|
|
2865
2878
|
* preflight diverges from the server.
|
|
2866
2879
|
*/
|
|
@@ -2872,7 +2885,7 @@ let AsReadableController = _AsReadableController = class AsReadableController {
|
|
|
2872
2885
|
key,
|
|
2873
2886
|
value
|
|
2874
2887
|
};
|
|
2875
|
-
if (key === "db.json" || key === "db.patch.strategy" || key.startsWith("db.default") || key === "db.http.path" || key === "db.writeOnly" || key === "db.column.version" || key === "db.column.derived") return {
|
|
2888
|
+
if (key === "db.json" || key === "db.patch.strategy" || key.startsWith("db.default") || key === "db.http.path" || key === "db.writeOnly" || key === "db.column.version" || key === "db.column.derived" || key === "db.sort.nulls") return {
|
|
2876
2889
|
key,
|
|
2877
2890
|
value
|
|
2878
2891
|
};
|
|
@@ -5335,7 +5348,10 @@ let AsDbReadableController = _AsDbReadableController = class AsDbReadableControl
|
|
|
5335
5348
|
if (sealed.size === 0) return void 0;
|
|
5336
5349
|
const refs = collectQueryPaths$1({
|
|
5337
5350
|
filter: rel.filter,
|
|
5338
|
-
controls: {
|
|
5351
|
+
controls: {
|
|
5352
|
+
$sort: nested.$sort ?? rel.$sort,
|
|
5353
|
+
$nulls: nested.$nulls ?? rel.$nulls
|
|
5354
|
+
}
|
|
5339
5355
|
});
|
|
5340
5356
|
const filtered = refs.filter.find((ref) => selfOrAncestor(ref.path, sealed) !== void 0)?.path;
|
|
5341
5357
|
if (filtered !== void 0) return writeOnlyError(`${path}.${filtered}`, "filter");
|
|
@@ -7050,7 +7066,29 @@ let AsDbReadableController = _AsDbReadableController = class AsDbReadableControl
|
|
|
7050
7066
|
})).data;
|
|
7051
7067
|
}
|
|
7052
7068
|
/**
|
|
7053
|
-
*
|
|
7069
|
+
* The `/pages` read without a `$sort` (since 0.1.153): ordered by the
|
|
7070
|
+
* primary key, ascending, so consecutive pages neither overlap nor skip
|
|
7071
|
+
* rows. A request with `$sort` keeps it (the core appends the primary key
|
|
7072
|
+
* as the tie-breaker); a source without a primary key (a view without
|
|
7073
|
+
* `@meta.id`) stays unordered. Only the plain read: text / vector search
|
|
7074
|
+
* keep their relevance order.
|
|
7075
|
+
*/
|
|
7076
|
+
_pagesOrder(q) {
|
|
7077
|
+
const controls = q.controls;
|
|
7078
|
+
const sort = controls?.$sort;
|
|
7079
|
+
const pk = this.readable.primaryKeys;
|
|
7080
|
+
if (sort && Object.keys(sort).length > 0 || pk.length === 0) return q;
|
|
7081
|
+
return {
|
|
7082
|
+
...q,
|
|
7083
|
+
controls: {
|
|
7084
|
+
...controls,
|
|
7085
|
+
$sort: Object.fromEntries(pk.map((f) => [f, 1]))
|
|
7086
|
+
}
|
|
7087
|
+
};
|
|
7088
|
+
}
|
|
7089
|
+
/**
|
|
7090
|
+
* **GET /pages** — returns paginated records with metadata. Without a
|
|
7091
|
+
* `$sort` the rows come in primary-key order (since 0.1.153).
|
|
7054
7092
|
*/
|
|
7055
7093
|
async pages(url) {
|
|
7056
7094
|
const { parsed, controls } = await this.parseRequest("pages", url);
|
|
@@ -7077,7 +7115,7 @@ let AsDbReadableController = _AsDbReadableController = class AsDbReadableControl
|
|
|
7077
7115
|
switch (strategy.kind) {
|
|
7078
7116
|
case "vector": return strategy.vectorField ? this.readable.vectorSearchWithCount(strategy.vectorField, strategy.vector, q) : this.readable.vectorSearchWithCount(strategy.vector, q);
|
|
7079
7117
|
case "search": return this.readable.searchWithCount(strategy.term, q, strategy.index);
|
|
7080
|
-
case "plain": return this.readable.findManyWithCount(q);
|
|
7118
|
+
case "plain": return this.readable.findManyWithCount(this._pagesOrder(q));
|
|
7081
7119
|
}
|
|
7082
7120
|
});
|
|
7083
7121
|
return {
|
|
@@ -7561,7 +7599,8 @@ let AsDbReadableController = _AsDbReadableController = class AsDbReadableControl
|
|
|
7561
7599
|
versionColumn: this.readable.versionColumn,
|
|
7562
7600
|
...capabilities.bucketUnits.length > 0 && { bucketUnits: [...capabilities.bucketUnits] },
|
|
7563
7601
|
aggregateFns: [...capabilities.aggregateFns],
|
|
7564
|
-
aggregateExpressions: capabilities.aggregateExpressions
|
|
7602
|
+
aggregateExpressions: capabilities.aggregateExpressions,
|
|
7603
|
+
...typeof this.readable.supportsNullsPlacement === "function" && this.readable.supportsNullsPlacement() && { nullsPlacement: true }
|
|
7565
7604
|
};
|
|
7566
7605
|
}
|
|
7567
7606
|
buildCrud() {
|
|
@@ -8161,7 +8200,22 @@ let AsValueHelpController = class AsValueHelpController extends AsReadableContro
|
|
|
8161
8200
|
return (await this.query(await this._scopedQuery(parsed.filter, controls))).data;
|
|
8162
8201
|
}
|
|
8163
8202
|
/**
|
|
8164
|
-
*
|
|
8203
|
+
* `/pages` controls without a `$sort` (since 0.1.153): ordered by the
|
|
8204
|
+
* primary key, ascending, so consecutive pages neither overlap nor skip
|
|
8205
|
+
* rows — like the DB controllers' `/pages`. A request with `$sort` or
|
|
8206
|
+
* `$search` keeps its order; a source without a primary key stays as is.
|
|
8207
|
+
*/
|
|
8208
|
+
_pagesOrder(controls) {
|
|
8209
|
+
const sort = controls.$sort;
|
|
8210
|
+
if ((Array.isArray(sort) ? sort.length > 0 : isPlainObject(sort) ? !isEmptyObject(sort) : !!sort) || controls.$search || !this.primaryKey) return controls;
|
|
8211
|
+
return {
|
|
8212
|
+
...controls,
|
|
8213
|
+
$sort: { [this.primaryKey]: 1 }
|
|
8214
|
+
};
|
|
8215
|
+
}
|
|
8216
|
+
/**
|
|
8217
|
+
* **GET /pages** — paginated row window plus total count. Without a
|
|
8218
|
+
* `$sort` the rows come in primary-key order (since 0.1.153).
|
|
8165
8219
|
*/
|
|
8166
8220
|
async runPages(url) {
|
|
8167
8221
|
const { parsed, controls } = await this.parseRequest("pages", url);
|
|
@@ -8170,11 +8224,11 @@ let AsValueHelpController = class AsValueHelpController extends AsReadableContro
|
|
|
8170
8224
|
const page = Math.max(Number(controls.$page || 1), 1);
|
|
8171
8225
|
const size = Math.max(Number(controls.$size || 10), 1);
|
|
8172
8226
|
const skip = (page - 1) * size;
|
|
8173
|
-
const result = await this.query(await this._scopedQuery(parsed.filter, {
|
|
8227
|
+
const result = await this.query(await this._scopedQuery(parsed.filter, this._pagesOrder({
|
|
8174
8228
|
...controls,
|
|
8175
8229
|
$skip: skip,
|
|
8176
8230
|
$limit: size
|
|
8177
|
-
}));
|
|
8231
|
+
})));
|
|
8178
8232
|
return {
|
|
8179
8233
|
data: result.data,
|
|
8180
8234
|
page,
|
|
@@ -8314,7 +8368,7 @@ let AsJsonValueHelpController = class AsJsonValueHelpController extends AsValueH
|
|
|
8314
8368
|
rows = rows.filter((row) => predicate(row));
|
|
8315
8369
|
}
|
|
8316
8370
|
const sort = this.normalizeSort(controls.controls.$sort);
|
|
8317
|
-
if (sort) rows = sortRows(rows, sort);
|
|
8371
|
+
if (sort) rows = sortRows(rows, sort, void 0, void 0, controls.controls.$nulls);
|
|
8318
8372
|
const total = rows.length;
|
|
8319
8373
|
const skip = Math.max(0, Number(controls.controls.$skip ?? 0));
|
|
8320
8374
|
const limit = Math.max(0, Number(controls.controls.$limit ?? total - skip));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/moost-db",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.153",
|
|
4
4
|
"description": "Generic database controller for Moost with Atscript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"annotations",
|
|
@@ -43,30 +43,30 @@
|
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@uniqu/url": "^0.1.
|
|
47
|
-
"@atscript/db-memory": "^0.1.
|
|
46
|
+
"@uniqu/url": "^0.1.15",
|
|
47
|
+
"@atscript/db-memory": "^0.1.153"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@atscript/core": "^0.1.
|
|
51
|
-
"@atscript/typescript": "^0.1.
|
|
52
|
-
"@moostjs/event-http": "^0.6.
|
|
53
|
-
"@uniqu/core": "^0.1.
|
|
54
|
-
"@wooksjs/event-core": "^0.7.
|
|
55
|
-
"@wooksjs/event-http": "^0.7.
|
|
56
|
-
"@wooksjs/http-body": "^0.7.
|
|
50
|
+
"@atscript/core": "^0.1.103",
|
|
51
|
+
"@atscript/typescript": "^0.1.103",
|
|
52
|
+
"@moostjs/event-http": "^0.6.49",
|
|
53
|
+
"@uniqu/core": "^0.1.15",
|
|
54
|
+
"@wooksjs/event-core": "^0.7.28",
|
|
55
|
+
"@wooksjs/event-http": "^0.7.28",
|
|
56
|
+
"@wooksjs/http-body": "^0.7.28",
|
|
57
57
|
"better-sqlite3": "^12.6.2",
|
|
58
|
-
"moost": "^0.6.
|
|
59
|
-
"unplugin-atscript": "^0.1.
|
|
60
|
-
"@atscript/db-sqlite": "^0.1.
|
|
58
|
+
"moost": "^0.6.49",
|
|
59
|
+
"unplugin-atscript": "^0.1.103",
|
|
60
|
+
"@atscript/db-sqlite": "^0.1.153"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
|
-
"@atscript/typescript": "^0.1.
|
|
64
|
-
"@moostjs/event-http": "^0.6.
|
|
65
|
-
"@uniqu/core": "^0.1.
|
|
66
|
-
"@wooksjs/event-core": "^0.7.
|
|
67
|
-
"@wooksjs/http-body": "^0.7.
|
|
68
|
-
"moost": "^0.6.
|
|
69
|
-
"@atscript/db": "^0.1.
|
|
63
|
+
"@atscript/typescript": "^0.1.103",
|
|
64
|
+
"@moostjs/event-http": "^0.6.49",
|
|
65
|
+
"@uniqu/core": "^0.1.15",
|
|
66
|
+
"@wooksjs/event-core": "^0.7.28",
|
|
67
|
+
"@wooksjs/http-body": "^0.7.28",
|
|
68
|
+
"moost": "^0.6.49",
|
|
69
|
+
"@atscript/db": "^0.1.153"
|
|
70
70
|
},
|
|
71
71
|
"scripts": {
|
|
72
72
|
"postinstall": "asc -f dts",
|