@atscript/db 0.1.128 → 0.1.130
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/agg.cjs +31 -0
- package/dist/agg.d.cts +30 -1
- package/dist/agg.d.mts +30 -1
- package/dist/agg.mjs +31 -1
- package/dist/{db-error-DXwEzmYJ.cjs → db-error-C4JuLcvb.cjs} +27 -0
- package/dist/{db-error-BHPXOKzc.mjs → db-error-COrO58t5.mjs} +22 -1
- package/dist/{db-readable-CkbZn_z-.d.cts → db-readable-B7eYWS5q.d.cts} +10 -1
- package/dist/{db-readable-BTl4NzNN.d.mts → db-readable-Bn1bV_eC.d.mts} +10 -1
- package/dist/{db-space-BEB5Jl8M.d.mts → db-space-C2UCnGHd.d.cts} +28 -2
- package/dist/{db-space-BevOyVrC.d.cts → db-space-DdIPYD0Q.d.mts} +28 -2
- package/dist/{db-view-Ck0I-PMI.mjs → db-view-BkF2xEGd.mjs} +98 -8
- package/dist/{db-view-C8-MsjND.cjs → db-view-hkwQ-fHJ.cjs} +98 -8
- package/dist/index.cjs +4 -3
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +4 -4
- package/dist/{nested-writer-DI-HeTky.mjs → nested-writer-CkDo-ZfH.mjs} +1 -1
- package/dist/{nested-writer-DoDhl3X3.cjs → nested-writer-DxPhmWFz.cjs} +1 -1
- package/dist/ops.cjs +1 -1
- package/dist/ops.mjs +1 -1
- package/dist/rel.cjs +2 -2
- package/dist/rel.d.cts +1 -1
- package/dist/rel.d.mts +1 -1
- package/dist/rel.mjs +2 -2
- package/dist/{relation-loader-BnUgJsUG.cjs → relation-loader-C8GOpNYJ.cjs} +1 -1
- package/dist/{relation-loader-BmeOMj0b.mjs → relation-loader-CUGcxJ18.mjs} +1 -1
- package/dist/sync.cjs +280 -143
- package/dist/sync.d.cts +24 -6
- package/dist/sync.d.mts +24 -6
- package/dist/sync.mjs +280 -143
- package/dist/{validator-eKYWf3xf.mjs → validator-CeD_fqyW.mjs} +1 -1
- package/dist/{validator-BNUHCXIE.cjs → validator-lkCJKuoo.cjs} +1 -1
- package/dist/{validator-DuAmWc8L.d.cts → validator-wBARmD68.d.cts} +21 -2
- package/dist/{validator-DuAmWc8L.d.mts → validator-wBARmD68.d.mts} +21 -2
- package/dist/validator.cjs +2 -2
- package/dist/validator.d.cts +1 -1
- package/dist/validator.d.mts +1 -1
- package/dist/validator.mjs +2 -2
- package/package.json +1 -1
package/dist/agg.cjs
CHANGED
|
@@ -4,5 +4,36 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
4
4
|
function resolveAlias(expr) {
|
|
5
5
|
return expr.$as ?? `${expr.$fn}_${expr.$field}`;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Reads the `$search` / `$index` controls of an aggregate query.
|
|
9
|
+
*
|
|
10
|
+
* Search and grouping are orthogonal: `$search` narrows the ROWS, `$groupBy`
|
|
11
|
+
* shapes what is left. Every adapter must therefore apply the search predicate
|
|
12
|
+
* BEFORE grouping, so the groups — and `$count`, which counts them — only ever
|
|
13
|
+
* describe rows that matched. An adapter that silently ignored the term would
|
|
14
|
+
* report totals for rows the same search excludes from the leaf list.
|
|
15
|
+
*
|
|
16
|
+
* Two rules the leaf search path applies and the grouped path must NOT:
|
|
17
|
+
* - **No implicit relevance ordering.** Relevance is a property of a row, not
|
|
18
|
+
* of a group; after `$group` there is no per-row score left to sort on.
|
|
19
|
+
* Grouped results order by `$sort` or not at all.
|
|
20
|
+
* - **No implicit row cap.** The leaf runners cap an unbounded search at 1000
|
|
21
|
+
* rows; applying that before `$group` would silently truncate group counts.
|
|
22
|
+
*
|
|
23
|
+
* Returns `undefined` when there is nothing to search for, so callers fall
|
|
24
|
+
* straight through to the plain grouped path.
|
|
25
|
+
*/
|
|
26
|
+
function resolveAggregateSearch(controls) {
|
|
27
|
+
const raw = controls?.$search;
|
|
28
|
+
if (typeof raw !== "string") return;
|
|
29
|
+
const text = raw.trim();
|
|
30
|
+
if (!text) return;
|
|
31
|
+
const index = controls?.$index;
|
|
32
|
+
return {
|
|
33
|
+
text,
|
|
34
|
+
indexName: typeof index === "string" && index ? index : void 0
|
|
35
|
+
};
|
|
36
|
+
}
|
|
7
37
|
//#endregion
|
|
38
|
+
exports.resolveAggregateSearch = resolveAggregateSearch;
|
|
8
39
|
exports.resolveAlias = resolveAlias;
|
package/dist/agg.d.cts
CHANGED
|
@@ -3,5 +3,34 @@ import { AggregateControls, AggregateExpr, AggregateExpr as AggregateExpr$1, Agg
|
|
|
3
3
|
//#region src/agg.d.ts
|
|
4
4
|
/** Resolves output alias: $as if provided, otherwise `{fn}_{field}`. */
|
|
5
5
|
declare function resolveAlias(expr: AggregateExpr$1): string;
|
|
6
|
+
/** The text-search request a grouped query carries, once normalised. */
|
|
7
|
+
interface TAggregateSearch {
|
|
8
|
+
/** Non-empty, trimmed search term. */
|
|
9
|
+
text: string;
|
|
10
|
+
/** Named search index to target, when the caller picked one. */
|
|
11
|
+
indexName?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Reads the `$search` / `$index` controls of an aggregate query.
|
|
15
|
+
*
|
|
16
|
+
* Search and grouping are orthogonal: `$search` narrows the ROWS, `$groupBy`
|
|
17
|
+
* shapes what is left. Every adapter must therefore apply the search predicate
|
|
18
|
+
* BEFORE grouping, so the groups — and `$count`, which counts them — only ever
|
|
19
|
+
* describe rows that matched. An adapter that silently ignored the term would
|
|
20
|
+
* report totals for rows the same search excludes from the leaf list.
|
|
21
|
+
*
|
|
22
|
+
* Two rules the leaf search path applies and the grouped path must NOT:
|
|
23
|
+
* - **No implicit relevance ordering.** Relevance is a property of a row, not
|
|
24
|
+
* of a group; after `$group` there is no per-row score left to sort on.
|
|
25
|
+
* Grouped results order by `$sort` or not at all.
|
|
26
|
+
* - **No implicit row cap.** The leaf runners cap an unbounded search at 1000
|
|
27
|
+
* rows; applying that before `$group` would silently truncate group counts.
|
|
28
|
+
*
|
|
29
|
+
* Returns `undefined` when there is nothing to search for, so callers fall
|
|
30
|
+
* straight through to the plain grouped path.
|
|
31
|
+
*/
|
|
32
|
+
declare function resolveAggregateSearch(controls: {
|
|
33
|
+
[key: `$${string}`]: unknown;
|
|
34
|
+
} | undefined): TAggregateSearch | undefined;
|
|
6
35
|
//#endregion
|
|
7
|
-
export { type AggregateControls, type AggregateExpr, type AggregateFn, type AggregateQuery, type AggregateResult, resolveAlias };
|
|
36
|
+
export { type AggregateControls, type AggregateExpr, type AggregateFn, type AggregateQuery, type AggregateResult, resolveAggregateSearch, resolveAlias };
|
package/dist/agg.d.mts
CHANGED
|
@@ -3,5 +3,34 @@ import { AggregateControls, AggregateExpr, AggregateExpr as AggregateExpr$1, Agg
|
|
|
3
3
|
//#region src/agg.d.ts
|
|
4
4
|
/** Resolves output alias: $as if provided, otherwise `{fn}_{field}`. */
|
|
5
5
|
declare function resolveAlias(expr: AggregateExpr$1): string;
|
|
6
|
+
/** The text-search request a grouped query carries, once normalised. */
|
|
7
|
+
interface TAggregateSearch {
|
|
8
|
+
/** Non-empty, trimmed search term. */
|
|
9
|
+
text: string;
|
|
10
|
+
/** Named search index to target, when the caller picked one. */
|
|
11
|
+
indexName?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Reads the `$search` / `$index` controls of an aggregate query.
|
|
15
|
+
*
|
|
16
|
+
* Search and grouping are orthogonal: `$search` narrows the ROWS, `$groupBy`
|
|
17
|
+
* shapes what is left. Every adapter must therefore apply the search predicate
|
|
18
|
+
* BEFORE grouping, so the groups — and `$count`, which counts them — only ever
|
|
19
|
+
* describe rows that matched. An adapter that silently ignored the term would
|
|
20
|
+
* report totals for rows the same search excludes from the leaf list.
|
|
21
|
+
*
|
|
22
|
+
* Two rules the leaf search path applies and the grouped path must NOT:
|
|
23
|
+
* - **No implicit relevance ordering.** Relevance is a property of a row, not
|
|
24
|
+
* of a group; after `$group` there is no per-row score left to sort on.
|
|
25
|
+
* Grouped results order by `$sort` or not at all.
|
|
26
|
+
* - **No implicit row cap.** The leaf runners cap an unbounded search at 1000
|
|
27
|
+
* rows; applying that before `$group` would silently truncate group counts.
|
|
28
|
+
*
|
|
29
|
+
* Returns `undefined` when there is nothing to search for, so callers fall
|
|
30
|
+
* straight through to the plain grouped path.
|
|
31
|
+
*/
|
|
32
|
+
declare function resolveAggregateSearch(controls: {
|
|
33
|
+
[key: `$${string}`]: unknown;
|
|
34
|
+
} | undefined): TAggregateSearch | undefined;
|
|
6
35
|
//#endregion
|
|
7
|
-
export { type AggregateControls, type AggregateExpr, type AggregateFn, type AggregateQuery, type AggregateResult, resolveAlias };
|
|
36
|
+
export { type AggregateControls, type AggregateExpr, type AggregateFn, type AggregateQuery, type AggregateResult, resolveAggregateSearch, resolveAlias };
|
package/dist/agg.mjs
CHANGED
|
@@ -3,5 +3,35 @@
|
|
|
3
3
|
function resolveAlias(expr) {
|
|
4
4
|
return expr.$as ?? `${expr.$fn}_${expr.$field}`;
|
|
5
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* Reads the `$search` / `$index` controls of an aggregate query.
|
|
8
|
+
*
|
|
9
|
+
* Search and grouping are orthogonal: `$search` narrows the ROWS, `$groupBy`
|
|
10
|
+
* shapes what is left. Every adapter must therefore apply the search predicate
|
|
11
|
+
* BEFORE grouping, so the groups — and `$count`, which counts them — only ever
|
|
12
|
+
* describe rows that matched. An adapter that silently ignored the term would
|
|
13
|
+
* report totals for rows the same search excludes from the leaf list.
|
|
14
|
+
*
|
|
15
|
+
* Two rules the leaf search path applies and the grouped path must NOT:
|
|
16
|
+
* - **No implicit relevance ordering.** Relevance is a property of a row, not
|
|
17
|
+
* of a group; after `$group` there is no per-row score left to sort on.
|
|
18
|
+
* Grouped results order by `$sort` or not at all.
|
|
19
|
+
* - **No implicit row cap.** The leaf runners cap an unbounded search at 1000
|
|
20
|
+
* rows; applying that before `$group` would silently truncate group counts.
|
|
21
|
+
*
|
|
22
|
+
* Returns `undefined` when there is nothing to search for, so callers fall
|
|
23
|
+
* straight through to the plain grouped path.
|
|
24
|
+
*/
|
|
25
|
+
function resolveAggregateSearch(controls) {
|
|
26
|
+
const raw = controls?.$search;
|
|
27
|
+
if (typeof raw !== "string") return;
|
|
28
|
+
const text = raw.trim();
|
|
29
|
+
if (!text) return;
|
|
30
|
+
const index = controls?.$index;
|
|
31
|
+
return {
|
|
32
|
+
text,
|
|
33
|
+
indexName: typeof index === "string" && index ? index : void 0
|
|
34
|
+
};
|
|
35
|
+
}
|
|
6
36
|
//#endregion
|
|
7
|
-
export { resolveAlias };
|
|
37
|
+
export { resolveAggregateSearch, resolveAlias };
|
|
@@ -50,6 +50,27 @@ var CasExhaustedError = class extends DbError {
|
|
|
50
50
|
this.lastSeenVersion = lastSeenVersion;
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
|
+
/**
|
|
54
|
+
* Thrown by `AtscriptDbTable.touchMany` (`require: 'all'`, the default) when
|
|
55
|
+
* fewer rows than keys matched their expected version — at least one row is
|
|
56
|
+
* stale or missing. Nothing was written (the pre-count refused before the
|
|
57
|
+
* first statement, or the SQL transaction rolled every bump back). Surfaced
|
|
58
|
+
* as HTTP 409 by moost-db.
|
|
59
|
+
*/
|
|
60
|
+
var CasMismatchError = class extends DbError {
|
|
61
|
+
matched;
|
|
62
|
+
expected;
|
|
63
|
+
name = "CasMismatchError";
|
|
64
|
+
constructor(matched, expected) {
|
|
65
|
+
const message = `touchMany: ${matched} of ${expected} rows matched — stale or missing rows`;
|
|
66
|
+
super("CAS_MISMATCH", [{
|
|
67
|
+
path: "$cas",
|
|
68
|
+
message
|
|
69
|
+
}], message);
|
|
70
|
+
this.matched = matched;
|
|
71
|
+
this.expected = expected;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
53
74
|
//#endregion
|
|
54
75
|
Object.defineProperty(exports, "CasExhaustedError", {
|
|
55
76
|
enumerable: true,
|
|
@@ -57,6 +78,12 @@ Object.defineProperty(exports, "CasExhaustedError", {
|
|
|
57
78
|
return CasExhaustedError;
|
|
58
79
|
}
|
|
59
80
|
});
|
|
81
|
+
Object.defineProperty(exports, "CasMismatchError", {
|
|
82
|
+
enumerable: true,
|
|
83
|
+
get: function() {
|
|
84
|
+
return CasMismatchError;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
60
87
|
Object.defineProperty(exports, "DbError", {
|
|
61
88
|
enumerable: true,
|
|
62
89
|
get: function() {
|
|
@@ -50,5 +50,26 @@ var CasExhaustedError = class extends DbError {
|
|
|
50
50
|
this.lastSeenVersion = lastSeenVersion;
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
|
+
/**
|
|
54
|
+
* Thrown by `AtscriptDbTable.touchMany` (`require: 'all'`, the default) when
|
|
55
|
+
* fewer rows than keys matched their expected version — at least one row is
|
|
56
|
+
* stale or missing. Nothing was written (the pre-count refused before the
|
|
57
|
+
* first statement, or the SQL transaction rolled every bump back). Surfaced
|
|
58
|
+
* as HTTP 409 by moost-db.
|
|
59
|
+
*/
|
|
60
|
+
var CasMismatchError = class extends DbError {
|
|
61
|
+
matched;
|
|
62
|
+
expected;
|
|
63
|
+
name = "CasMismatchError";
|
|
64
|
+
constructor(matched, expected) {
|
|
65
|
+
const message = `touchMany: ${matched} of ${expected} rows matched — stale or missing rows`;
|
|
66
|
+
super("CAS_MISMATCH", [{
|
|
67
|
+
path: "$cas",
|
|
68
|
+
message
|
|
69
|
+
}], message);
|
|
70
|
+
this.matched = matched;
|
|
71
|
+
this.expected = expected;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
53
74
|
//#endregion
|
|
54
|
-
export {
|
|
75
|
+
export { DepthLimitExceededError as i, CasMismatchError as n, DbError as r, CasExhaustedError as t };
|
|
@@ -828,6 +828,15 @@ interface TDbRemoveGuardContext<Row = Record<string, unknown>> {
|
|
|
828
828
|
type TDbWriteGuard<Row = Record<string, unknown>> = (ctx: TDbWriteGuardContext<Row>) => void | Promise<void>;
|
|
829
829
|
/** A validated-stage delete guard — see {@link TDeleteOptions.guard}. */
|
|
830
830
|
type TDbRemoveGuard<Row = Record<string, unknown>> = (ctx: TDbRemoveGuardContext<Row>) => void | Promise<void>;
|
|
831
|
+
/** Options of `AtscriptDbTable.touchMany` (since 0.1.129). */
|
|
832
|
+
interface TTouchManyOptions {
|
|
833
|
+
/**
|
|
834
|
+
* `'all'` (default): every key must match its stored version — a stale or
|
|
835
|
+
* missing row throws `DbError("CAS_MISMATCH")` and no version moves.
|
|
836
|
+
* `'any'`: bump whatever matches and report the honest counts.
|
|
837
|
+
*/
|
|
838
|
+
require?: "all" | "any";
|
|
839
|
+
}
|
|
831
840
|
/** Options of `insertOne/Many`, `replaceOne` / `bulkReplace`, `updateOne` / `bulkUpdate`. */
|
|
832
841
|
interface TWriteOptions<Row = Record<string, unknown>> {
|
|
833
842
|
/** Nested-relation write recursion limit (default 3). */
|
|
@@ -2006,4 +2015,4 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
|
|
|
2006
2015
|
}, thisTableName: string, alias?: string): TDbForeignKey | undefined;
|
|
2007
2016
|
}
|
|
2008
2017
|
//#endregion
|
|
2009
|
-
export { TDbWriteAction as $, TCrudPermissions as A,
|
|
2018
|
+
export { TDbWriteAction as $, TCrudPermissions as A, findAncestorInSet as At, TDbForeignKey as B, NullableOptional as C, TWriteOptions as Ct, TCascadeTarget as D, UniqueryControls$1 as Dt, TCascadeResolver as E, Uniquery$1 as Et, TDbCollation as F, UniquSelect as Ft, TDbInsertResult as G, TDbIndexField as H, TDbDefaultFn as I, TDbRelation as J, TDbObjectKind as K, TDbDefaultValue as L, TDbActionIntent as M, isGeoPointType as Mt, TDbActionLevel as N, NoopLogger as Nt, TColumnDiff as O, WithRelation$1 as Ot, TDbActionProcessor as P, TGenericLogger as Pt, TDbUpdateResult as Q, TDbDeleteResult as R, NavPropsOf$1 as S, TValueFormatterPair as St, PrimaryKeyOf$1 as T, TypedWithRelation as Tt, TDbIndexType as U, TDbIndex as V, TDbInsertManyResult as W, TDbRemoveGuardContext as X, TDbRemoveGuard as Y, TDbStorageType as Z, DbQuery as _, TSearchIndexInfo as _t, TDbEncryptionOptions as a, TExistingForeignKey as at, FilterExpr$1 as b, TTableResolver as bt, BaseDbAdapter as c, TFkLookupResolver as ct, AggregateFn as d, TIdentification as dt, TDbWriteGuard as et, AggregateQuery$1 as f, TMetaResponse as ft, DbPatch as g, TRelationInfo as gt, DbControls as h, TReferencingForeignKey as ht, DbEncryption as i, TExistingColumn as it, TDbActionInfo as j, isGeoIndexableType as jt, TCrudOp as k, TableMetadata as kt, AggregateControls as l, TFkLookupTarget as lt, AtscriptDbWritable as m, TPrimaryKeyChange as mt, DbResponse as n, TDeleteOptions as nt, DocumentFieldMapper as o, TExistingTableOption as ot, AggregateResult as p, TMetadataOverrides as pt, TDbReferentialAction as q, resolveDesignType as r, TEnsureTableOptions as rt, FieldMappingStrategy as s, TFieldMeta as st, AtscriptDbReadable as t, TDbWriteGuardContext as tt, AggregateExpr$1 as u, TIdDescriptor as ut, DbRow as v, TSyncColumnResult as vt, OwnPropsOf$1 as w, TWriteTableResolver as wt, FlatOf$1 as x, TTouchManyOptions as xt, FieldOpsFor as y, TTableOptionDiff as yt, TDbFieldMeta as z };
|
|
@@ -828,6 +828,15 @@ interface TDbRemoveGuardContext<Row = Record<string, unknown>> {
|
|
|
828
828
|
type TDbWriteGuard<Row = Record<string, unknown>> = (ctx: TDbWriteGuardContext<Row>) => void | Promise<void>;
|
|
829
829
|
/** A validated-stage delete guard — see {@link TDeleteOptions.guard}. */
|
|
830
830
|
type TDbRemoveGuard<Row = Record<string, unknown>> = (ctx: TDbRemoveGuardContext<Row>) => void | Promise<void>;
|
|
831
|
+
/** Options of `AtscriptDbTable.touchMany` (since 0.1.129). */
|
|
832
|
+
interface TTouchManyOptions {
|
|
833
|
+
/**
|
|
834
|
+
* `'all'` (default): every key must match its stored version — a stale or
|
|
835
|
+
* missing row throws `DbError("CAS_MISMATCH")` and no version moves.
|
|
836
|
+
* `'any'`: bump whatever matches and report the honest counts.
|
|
837
|
+
*/
|
|
838
|
+
require?: "all" | "any";
|
|
839
|
+
}
|
|
831
840
|
/** Options of `insertOne/Many`, `replaceOne` / `bulkReplace`, `updateOne` / `bulkUpdate`. */
|
|
832
841
|
interface TWriteOptions<Row = Record<string, unknown>> {
|
|
833
842
|
/** Nested-relation write recursion limit (default 3). */
|
|
@@ -2006,4 +2015,4 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
|
|
|
2006
2015
|
}, thisTableName: string, alias?: string): TDbForeignKey | undefined;
|
|
2007
2016
|
}
|
|
2008
2017
|
//#endregion
|
|
2009
|
-
export { TDbWriteAction as $, TCrudPermissions as A,
|
|
2018
|
+
export { TDbWriteAction as $, TCrudPermissions as A, findAncestorInSet as At, TDbForeignKey as B, NullableOptional as C, TWriteOptions as Ct, TCascadeTarget as D, UniqueryControls$1 as Dt, TCascadeResolver as E, Uniquery$1 as Et, TDbCollation as F, UniquSelect as Ft, TDbInsertResult as G, TDbIndexField as H, TDbDefaultFn as I, TDbRelation as J, TDbObjectKind as K, TDbDefaultValue as L, TDbActionIntent as M, isGeoPointType as Mt, TDbActionLevel as N, NoopLogger as Nt, TColumnDiff as O, WithRelation$1 as Ot, TDbActionProcessor as P, TGenericLogger as Pt, TDbUpdateResult as Q, TDbDeleteResult as R, NavPropsOf$1 as S, TValueFormatterPair as St, PrimaryKeyOf$1 as T, TypedWithRelation as Tt, TDbIndexType as U, TDbIndex as V, TDbInsertManyResult as W, TDbRemoveGuardContext as X, TDbRemoveGuard as Y, TDbStorageType as Z, DbQuery as _, TSearchIndexInfo as _t, TDbEncryptionOptions as a, TExistingForeignKey as at, FilterExpr$1 as b, TTableResolver as bt, BaseDbAdapter as c, TFkLookupResolver as ct, AggregateFn as d, TIdentification as dt, TDbWriteGuard as et, AggregateQuery$1 as f, TMetaResponse as ft, DbPatch as g, TRelationInfo as gt, DbControls as h, TReferencingForeignKey as ht, DbEncryption as i, TExistingColumn as it, TDbActionInfo as j, isGeoIndexableType as jt, TCrudOp as k, TableMetadata as kt, AggregateControls as l, TFkLookupTarget as lt, AtscriptDbWritable as m, TPrimaryKeyChange as mt, DbResponse as n, TDeleteOptions as nt, DocumentFieldMapper as o, TExistingTableOption as ot, AggregateResult as p, TMetadataOverrides as pt, TDbReferentialAction as q, resolveDesignType as r, TEnsureTableOptions as rt, FieldMappingStrategy as s, TFieldMeta as st, AtscriptDbReadable as t, TDbWriteGuardContext as tt, AggregateExpr$1 as u, TIdDescriptor as ut, DbRow as v, TSyncColumnResult as vt, OwnPropsOf$1 as w, TWriteTableResolver as wt, FlatOf$1 as x, TTouchManyOptions as xt, FieldOpsFor as y, TTableOptionDiff as yt, TDbFieldMeta as z };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { C as NullableOptional, Ct as
|
|
2
|
-
import { AtscriptQueryComparison, AtscriptQueryFieldRef, AtscriptQueryFieldRef as AtscriptQueryFieldRef$1, AtscriptQueryNode, AtscriptQueryNode as AtscriptQueryNode$1, AtscriptRef, FlatOf, NavPropsOf, OwnPropsOf, PrimaryKeyOf, TAtscriptAnnotatedType, TAtscriptDataType, Validator } from "@atscript/typescript/utils";
|
|
1
|
+
import { C as NullableOptional, Ct as TWriteOptions, E as TCascadeResolver, G as TDbInsertResult, Pt as TGenericLogger, Q as TDbUpdateResult, R as TDbDeleteResult, W as TDbInsertManyResult, a as TDbEncryptionOptions, bt as TTableResolver, c as BaseDbAdapter, ct as TFkLookupResolver, g as DbPatch, ht as TReferencingForeignKey, i as DbEncryption, kt as TableMetadata, nt as TDeleteOptions, t as AtscriptDbReadable, v as DbRow, wt as TWriteTableResolver, xt as TTouchManyOptions } from "./db-readable-B7eYWS5q.cjs";
|
|
3
2
|
import { FilterExpr } from "@uniqu/core";
|
|
3
|
+
import { AtscriptQueryComparison, AtscriptQueryFieldRef, AtscriptQueryFieldRef as AtscriptQueryFieldRef$1, AtscriptQueryNode, AtscriptQueryNode as AtscriptQueryNode$1, AtscriptRef, FlatOf, NavPropsOf, OwnPropsOf, PrimaryKeyOf, TAtscriptAnnotatedType, TAtscriptDataType, Validator } from "@atscript/typescript/utils";
|
|
4
4
|
|
|
5
5
|
//#region src/strategies/integrity.d.ts
|
|
6
6
|
/**
|
|
@@ -103,6 +103,32 @@ declare class AtscriptDbTable<T extends TAtscriptAnnotatedType = TAtscriptAnnota
|
|
|
103
103
|
* present, `$cas` removed) — see {@link TWriteOptions}.
|
|
104
104
|
*/
|
|
105
105
|
bulkUpdate(payloads: Array<DbPatch<DataType>>, opts?: TWriteOptions<DataType>): Promise<TDbUpdateResult>;
|
|
106
|
+
/**
|
|
107
|
+
* Batch versioned touch (since 0.1.129): bumps the version of every listed
|
|
108
|
+
* row by exactly one, each row guarded by its own expected version. This is
|
|
109
|
+
* the batch fence `updateMany(orFilter, {})` used to be before 0.1.128 (an
|
|
110
|
+
* empty patch is a no-op since then and takes no lock).
|
|
111
|
+
*
|
|
112
|
+
* Each key carries the primary key field(s) (composite supported) plus the
|
|
113
|
+
* version column and NOTHING else — a touch has no payload. Unique indexes
|
|
114
|
+
* do not identify a touch key. `undefined`-valued properties are ignored,
|
|
115
|
+
* like in every write payload. Empty `keys` → `{ 0, 0 }` without a statement.
|
|
116
|
+
*
|
|
117
|
+
* `require: 'all'` (default): one count over the whole key set runs FIRST;
|
|
118
|
+
* a stale or missing row throws {@link CasMismatchError} before any write.
|
|
119
|
+
* The bumps then run as `updateMany(orFilter, {})` chunks of at most
|
|
120
|
+
* {@link TOUCH_MANY_CHUNK} keys inside one adapter transaction; a summed
|
|
121
|
+
* `matchedCount` short of `keys.length` (a row moved between the count and
|
|
122
|
+
* the bump) throws the same error — SQL engines roll every bump back. The
|
|
123
|
+
* pre-count is therefore a deliberate double check on SQL: it is what makes
|
|
124
|
+
* the guarantee hold on adapters whose `withTransaction` is a passthrough
|
|
125
|
+
* (the memory adapter, a Mongo standalone topology) — there it covers the
|
|
126
|
+
* common stale case and the residual race window is accepted.
|
|
127
|
+
* `require: 'any'`: no pre-count, the honest summed result is returned.
|
|
128
|
+
*
|
|
129
|
+
* No `guard`, no `onWrite`; not exposed over HTTP.
|
|
130
|
+
*/
|
|
131
|
+
touchMany(keys: Array<DbPatch<DataType>>, opts?: TTouchManyOptions): Promise<TDbUpdateResult>;
|
|
106
132
|
/**
|
|
107
133
|
* Deletes a single record by any type-compatible identifier — primary key
|
|
108
134
|
* or single-field unique index. Uses the same resolution logic as `findById`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { C as NullableOptional, Ct as
|
|
2
|
-
import { FilterExpr } from "@uniqu/core";
|
|
1
|
+
import { C as NullableOptional, Ct as TWriteOptions, E as TCascadeResolver, G as TDbInsertResult, Pt as TGenericLogger, Q as TDbUpdateResult, R as TDbDeleteResult, W as TDbInsertManyResult, a as TDbEncryptionOptions, bt as TTableResolver, c as BaseDbAdapter, ct as TFkLookupResolver, g as DbPatch, ht as TReferencingForeignKey, i as DbEncryption, kt as TableMetadata, nt as TDeleteOptions, t as AtscriptDbReadable, v as DbRow, wt as TWriteTableResolver, xt as TTouchManyOptions } from "./db-readable-Bn1bV_eC.mjs";
|
|
3
2
|
import { AtscriptQueryComparison, AtscriptQueryFieldRef, AtscriptQueryFieldRef as AtscriptQueryFieldRef$1, AtscriptQueryNode, AtscriptQueryNode as AtscriptQueryNode$1, AtscriptRef, FlatOf, NavPropsOf, OwnPropsOf, PrimaryKeyOf, TAtscriptAnnotatedType, TAtscriptDataType, Validator } from "@atscript/typescript/utils";
|
|
3
|
+
import { FilterExpr } from "@uniqu/core";
|
|
4
4
|
|
|
5
5
|
//#region src/strategies/integrity.d.ts
|
|
6
6
|
/**
|
|
@@ -103,6 +103,32 @@ declare class AtscriptDbTable<T extends TAtscriptAnnotatedType = TAtscriptAnnota
|
|
|
103
103
|
* present, `$cas` removed) — see {@link TWriteOptions}.
|
|
104
104
|
*/
|
|
105
105
|
bulkUpdate(payloads: Array<DbPatch<DataType>>, opts?: TWriteOptions<DataType>): Promise<TDbUpdateResult>;
|
|
106
|
+
/**
|
|
107
|
+
* Batch versioned touch (since 0.1.129): bumps the version of every listed
|
|
108
|
+
* row by exactly one, each row guarded by its own expected version. This is
|
|
109
|
+
* the batch fence `updateMany(orFilter, {})` used to be before 0.1.128 (an
|
|
110
|
+
* empty patch is a no-op since then and takes no lock).
|
|
111
|
+
*
|
|
112
|
+
* Each key carries the primary key field(s) (composite supported) plus the
|
|
113
|
+
* version column and NOTHING else — a touch has no payload. Unique indexes
|
|
114
|
+
* do not identify a touch key. `undefined`-valued properties are ignored,
|
|
115
|
+
* like in every write payload. Empty `keys` → `{ 0, 0 }` without a statement.
|
|
116
|
+
*
|
|
117
|
+
* `require: 'all'` (default): one count over the whole key set runs FIRST;
|
|
118
|
+
* a stale or missing row throws {@link CasMismatchError} before any write.
|
|
119
|
+
* The bumps then run as `updateMany(orFilter, {})` chunks of at most
|
|
120
|
+
* {@link TOUCH_MANY_CHUNK} keys inside one adapter transaction; a summed
|
|
121
|
+
* `matchedCount` short of `keys.length` (a row moved between the count and
|
|
122
|
+
* the bump) throws the same error — SQL engines roll every bump back. The
|
|
123
|
+
* pre-count is therefore a deliberate double check on SQL: it is what makes
|
|
124
|
+
* the guarantee hold on adapters whose `withTransaction` is a passthrough
|
|
125
|
+
* (the memory adapter, a Mongo standalone topology) — there it covers the
|
|
126
|
+
* common stale case and the residual race window is accepted.
|
|
127
|
+
* `require: 'any'`: no pre-count, the honest summed result is returned.
|
|
128
|
+
*
|
|
129
|
+
* No `guard`, no `onWrite`; not exposed over HTTP.
|
|
130
|
+
*/
|
|
131
|
+
touchMany(keys: Array<DbPatch<DataType>>, opts?: TTouchManyOptions): Promise<TDbUpdateResult>;
|
|
106
132
|
/**
|
|
107
133
|
* Deletes a single record by any type-compatible identifier — primary key
|
|
108
134
|
* or single-field unique index. Uses the same resolution logic as `findById`.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { n as DbError } from "./db-error-
|
|
2
|
-
import { a as forceNavNonOptional, c as getKeyProps, i as dbPlugin, l as isEmptyObject, n as buildPatchPartial, t as buildDbValidator, u as isPlainObject } from "./validator-
|
|
1
|
+
import { n as CasMismatchError, r as DbError } from "./db-error-COrO58t5.mjs";
|
|
2
|
+
import { a as forceNavNonOptional, c as getKeyProps, i as dbPlugin, l as isEmptyObject, n as buildPatchPartial, t as buildDbValidator, u as isPlainObject } from "./validator-CeD_fqyW.mjs";
|
|
3
3
|
import { resolveAlias } from "./agg.mjs";
|
|
4
|
-
import { a as batchPatchNestedTo, c as batchReplaceNestedTo, d as preValidateNestedFrom, f as validateBatch, g as findRemoteFK, h as findFKForRelation, i as batchPatchNestedFrom, l as batchReplaceNestedVia, m as remapDeleteFkViolation, n as batchInsertNestedTo, o as batchPatchNestedVia, p as enrichFkViolation, r as batchInsertNestedVia, s as batchReplaceNestedFrom, t as batchInsertNestedFrom, u as checkDepthOverflow } from "./nested-writer-
|
|
4
|
+
import { a as batchPatchNestedTo, c as batchReplaceNestedTo, d as preValidateNestedFrom, f as validateBatch, g as findRemoteFK, h as findFKForRelation, i as batchPatchNestedFrom, l as batchReplaceNestedVia, m as remapDeleteFkViolation, n as batchInsertNestedTo, o as batchPatchNestedVia, p as enrichFkViolation, r as batchInsertNestedVia, s as batchReplaceNestedFrom, t as batchInsertNestedFrom, u as checkDepthOverflow } from "./nested-writer-CkDo-ZfH.mjs";
|
|
5
5
|
import { separateCas, separateFieldOps } from "./ops.mjs";
|
|
6
6
|
import { flattenAnnotatedType, isAnnotatedType } from "@atscript/typescript/utils";
|
|
7
7
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -1027,13 +1027,11 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
1027
1027
|
return {
|
|
1028
1028
|
filter,
|
|
1029
1029
|
controls: {
|
|
1030
|
+
...controls,
|
|
1030
1031
|
$groupBy: groupBy,
|
|
1031
1032
|
$select: select ? new UniquSelect(select, meta.allPhysicalFields) : void 0,
|
|
1032
1033
|
$sort: sort,
|
|
1033
|
-
$having: having
|
|
1034
|
-
$skip: controls.$skip,
|
|
1035
|
-
$limit: controls.$limit,
|
|
1036
|
-
$count: controls.$count
|
|
1034
|
+
$having: having
|
|
1037
1035
|
},
|
|
1038
1036
|
insights: query.insights
|
|
1039
1037
|
};
|
|
@@ -2047,6 +2045,11 @@ var AtscriptDbReadable = class {
|
|
|
2047
2045
|
}]);
|
|
2048
2046
|
}
|
|
2049
2047
|
}
|
|
2048
|
+
const searchTerm = query.controls.$search;
|
|
2049
|
+
if (typeof searchTerm === "string" && searchTerm) {
|
|
2050
|
+
this._ensureSearchable();
|
|
2051
|
+
if (!searchTerm.trim()) return query.controls.$count ? [{ count: 0 }] : [];
|
|
2052
|
+
}
|
|
2050
2053
|
guardAggregate(this._meta, this.adapter, query);
|
|
2051
2054
|
const dbQuery = this._fieldMapper.translateAggregateQuery(query, this._meta);
|
|
2052
2055
|
return (await this.adapter.aggregate(dbQuery)).map((row) => this._fieldMapper.reconstructFromRead(row, this._meta));
|
|
@@ -2345,7 +2348,7 @@ var AtscriptDbReadable = class {
|
|
|
2345
2348
|
* Public entry point for relation loading. Used by adapters for nested $with delegation.
|
|
2346
2349
|
*/
|
|
2347
2350
|
async loadRelations(rows, withRelations) {
|
|
2348
|
-
const { loadRelationsImpl } = await import("./relation-loader-
|
|
2351
|
+
const { loadRelationsImpl } = await import("./relation-loader-CUGcxJ18.mjs").then((n) => n.n);
|
|
2349
2352
|
return loadRelationsImpl(rows, withRelations, this);
|
|
2350
2353
|
}
|
|
2351
2354
|
/**
|
|
@@ -3504,6 +3507,15 @@ function _translateOpsKeys(ops, meta) {
|
|
|
3504
3507
|
mul: ops.mul ? _translateOpsRecord(ops.mul, meta) : void 0
|
|
3505
3508
|
};
|
|
3506
3509
|
}
|
|
3510
|
+
/** Upper bound of keys per `touchMany` UPDATE statement (parameter-count safety). */
|
|
3511
|
+
const TOUCH_MANY_CHUNK = 500;
|
|
3512
|
+
/** `touchMany` input rejection — always `INVALID_QUERY`, path names the key. */
|
|
3513
|
+
function invalidTouchKey(path, message) {
|
|
3514
|
+
return new DbError("INVALID_QUERY", [{
|
|
3515
|
+
path,
|
|
3516
|
+
message
|
|
3517
|
+
}]);
|
|
3518
|
+
}
|
|
3507
3519
|
var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
3508
3520
|
_cascadeResolver;
|
|
3509
3521
|
_fkLookupResolver;
|
|
@@ -3783,6 +3795,84 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
3783
3795
|
}));
|
|
3784
3796
|
}
|
|
3785
3797
|
/**
|
|
3798
|
+
* Batch versioned touch (since 0.1.129): bumps the version of every listed
|
|
3799
|
+
* row by exactly one, each row guarded by its own expected version. This is
|
|
3800
|
+
* the batch fence `updateMany(orFilter, {})` used to be before 0.1.128 (an
|
|
3801
|
+
* empty patch is a no-op since then and takes no lock).
|
|
3802
|
+
*
|
|
3803
|
+
* Each key carries the primary key field(s) (composite supported) plus the
|
|
3804
|
+
* version column and NOTHING else — a touch has no payload. Unique indexes
|
|
3805
|
+
* do not identify a touch key. `undefined`-valued properties are ignored,
|
|
3806
|
+
* like in every write payload. Empty `keys` → `{ 0, 0 }` without a statement.
|
|
3807
|
+
*
|
|
3808
|
+
* `require: 'all'` (default): one count over the whole key set runs FIRST;
|
|
3809
|
+
* a stale or missing row throws {@link CasMismatchError} before any write.
|
|
3810
|
+
* The bumps then run as `updateMany(orFilter, {})` chunks of at most
|
|
3811
|
+
* {@link TOUCH_MANY_CHUNK} keys inside one adapter transaction; a summed
|
|
3812
|
+
* `matchedCount` short of `keys.length` (a row moved between the count and
|
|
3813
|
+
* the bump) throws the same error — SQL engines roll every bump back. The
|
|
3814
|
+
* pre-count is therefore a deliberate double check on SQL: it is what makes
|
|
3815
|
+
* the guarantee hold on adapters whose `withTransaction` is a passthrough
|
|
3816
|
+
* (the memory adapter, a Mongo standalone topology) — there it covers the
|
|
3817
|
+
* common stale case and the residual race window is accepted.
|
|
3818
|
+
* `require: 'any'`: no pre-count, the honest summed result is returned.
|
|
3819
|
+
*
|
|
3820
|
+
* No `guard`, no `onWrite`; not exposed over HTTP.
|
|
3821
|
+
*/
|
|
3822
|
+
async touchMany(keys, opts) {
|
|
3823
|
+
this._ensureBuilt();
|
|
3824
|
+
const versionField = this._meta.versionField;
|
|
3825
|
+
if (versionField === void 0) throw invalidTouchKey("", "touchMany requires @db.column.version");
|
|
3826
|
+
if (keys.length === 0) return {
|
|
3827
|
+
matchedCount: 0,
|
|
3828
|
+
modifiedCount: 0
|
|
3829
|
+
};
|
|
3830
|
+
const pkFields = this.primaryKeys;
|
|
3831
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3832
|
+
const pairs = [];
|
|
3833
|
+
for (const [i, key] of keys.entries()) {
|
|
3834
|
+
const pair = {};
|
|
3835
|
+
for (const pk of pkFields) {
|
|
3836
|
+
if (key[pk] === void 0) throw invalidTouchKey(`[${i}].${pk}`, `touchMany: each key must carry its "${pk}"`);
|
|
3837
|
+
pair[pk] = key[pk];
|
|
3838
|
+
}
|
|
3839
|
+
const version = key[versionField];
|
|
3840
|
+
if (typeof version !== "number" || !Number.isFinite(version)) throw invalidTouchKey(`[${i}].${versionField}`, `touchMany: each key must carry its expected "${versionField}" (number)`);
|
|
3841
|
+
for (const [prop, value] of Object.entries(key)) if (value !== void 0 && prop !== versionField && !(prop in pair)) throw invalidTouchKey(`[${i}].${prop}`, `touchMany: a touch carries no payload — keys hold the primary key and "${versionField}" only, got "${prop}"`);
|
|
3842
|
+
const identity = JSON.stringify(pair);
|
|
3843
|
+
if (seen.has(identity)) throw invalidTouchKey(`[${i}]`, `touchMany: duplicate key ${identity}`);
|
|
3844
|
+
seen.add(identity);
|
|
3845
|
+
pair[versionField] = version;
|
|
3846
|
+
pairs.push(pair);
|
|
3847
|
+
}
|
|
3848
|
+
const orFilter = { $or: pairs };
|
|
3849
|
+
this._guardMutationFilter(orFilter);
|
|
3850
|
+
const translated = this._fieldMapper.translateFilter(orFilter, this._meta).$or;
|
|
3851
|
+
const requireAll = (opts?.require ?? "all") === "all";
|
|
3852
|
+
if (requireAll) {
|
|
3853
|
+
const matched = await this.adapter.count({
|
|
3854
|
+
filter: { $or: translated },
|
|
3855
|
+
controls: {}
|
|
3856
|
+
});
|
|
3857
|
+
if (matched < keys.length) throw new CasMismatchError(matched, keys.length);
|
|
3858
|
+
}
|
|
3859
|
+
return this.adapter.withTransaction(async () => {
|
|
3860
|
+
let matchedCount = 0;
|
|
3861
|
+
let modifiedCount = 0;
|
|
3862
|
+
for (let start = 0; start < translated.length; start += TOUCH_MANY_CHUNK) {
|
|
3863
|
+
const chunk = translated.slice(start, start + TOUCH_MANY_CHUNK);
|
|
3864
|
+
const result = await this.adapter.updateMany({ $or: chunk }, {}, void 0);
|
|
3865
|
+
matchedCount += result.matchedCount;
|
|
3866
|
+
modifiedCount += result.modifiedCount;
|
|
3867
|
+
}
|
|
3868
|
+
if (requireAll && matchedCount !== keys.length) throw new CasMismatchError(matchedCount, keys.length);
|
|
3869
|
+
return {
|
|
3870
|
+
matchedCount,
|
|
3871
|
+
modifiedCount
|
|
3872
|
+
};
|
|
3873
|
+
});
|
|
3874
|
+
}
|
|
3875
|
+
/**
|
|
3786
3876
|
* Deletes a single record by any type-compatible identifier — primary key
|
|
3787
3877
|
* or single-field unique index. Uses the same resolution logic as `findById`.
|
|
3788
3878
|
*
|