@atscript/db 0.1.135 → 0.1.136
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-DKuf_v2L.d.cts → agg-CV7y8nC6.d.cts} +13 -3
- package/dist/{agg-BtlGeRfj.d.mts → agg-D5DHsAby.d.mts} +13 -3
- package/dist/agg.cjs +35 -3
- package/dist/agg.d.cts +2 -2
- package/dist/agg.d.mts +2 -2
- package/dist/agg.mjs +34 -1
- package/dist/aggregate-fns-CGBv3E8S.cjs +87 -0
- package/dist/aggregate-fns-CfsveE1w.mjs +58 -0
- package/dist/{buckets-D6PBXKRJ.d.cts → buckets-kKY0RKYN.d.cts} +60 -17
- package/dist/{buckets-GruoVxH5.d.mts → buckets-y4EzoX7D.d.mts} +60 -17
- package/dist/{db-space-9CH5neN7.d.mts → db-space-BX4K3vb6.d.mts} +126 -7
- package/dist/{db-space-BdtBNTeH.d.cts → db-space-ClG41vJh.d.cts} +126 -7
- package/dist/{db-view-Dm7MpUGB.cjs → db-view-BAzhEUxB.cjs} +391 -92
- package/dist/{db-view-Doh8vPg3.mjs → db-view-BrAtbaOx.mjs} +385 -92
- package/dist/index.cjs +17 -3
- package/dist/index.d.cts +12 -10
- package/dist/index.d.mts +12 -10
- package/dist/index.mjs +14 -4
- package/dist/{nested-writer-wk1EFUNY.cjs → nested-writer-BZNCuqI6.cjs} +16 -2
- package/dist/{nested-writer-CqL24ojl.mjs → nested-writer-FWD5oOYh.mjs} +11 -3
- package/dist/plugin.cjs +215 -75
- package/dist/plugin.mjs +215 -76
- package/dist/rel.cjs +2 -2
- package/dist/rel.d.cts +2 -20
- package/dist/rel.d.mts +2 -20
- package/dist/rel.mjs +2 -2
- package/dist/relation-helpers-BVjQflL_.d.cts +30 -0
- package/dist/relation-helpers-D4haLo1n.d.mts +30 -0
- package/dist/{relation-loader-BD4xANQJ.cjs → relation-loader-6ZB_5KFq.cjs} +1 -1
- package/dist/{relation-loader-B68R1LET.mjs → relation-loader-CTFaZpVa.mjs} +1 -1
- package/dist/shared.cjs +5 -1
- package/dist/shared.d.cts +16 -3
- package/dist/shared.d.mts +16 -3
- package/dist/shared.mjs +2 -2
- package/dist/sync.cjs +37 -10
- package/dist/sync.d.cts +24 -4
- package/dist/sync.d.mts +24 -4
- package/dist/sync.mjs +37 -10
- package/dist/{validation-utils-MWOP1Ts4.mjs → validation-utils-B4h-GW4d.mjs} +29 -7
- package/dist/{validation-utils-B7SXPkm7.cjs → validation-utils-Dg0hW6dn.cjs} +52 -6
- package/dist/{validator-CewfnGZj.d.mts → validator-Drb2N-YL.d.cts} +1 -1
- package/dist/{validator-CewfnGZj.d.cts → validator-Drb2N-YL.d.mts} +1 -1
- package/dist/validator.d.cts +1 -1
- package/dist/validator.d.mts +1 -1
- package/package.json +1 -1
- package/dist/agg-CvXDGnKi.mjs +0 -61
- package/dist/agg-EcIbAFnJ.cjs +0 -78
package/dist/sync.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_db_view = require("./db-view-
|
|
2
|
+
const require_db_view = require("./db-view-BAzhEUxB.cjs");
|
|
3
3
|
//#region src/schema/schema-hash.ts
|
|
4
4
|
/** Extracts sorted field snapshots from a readable's field descriptors. */
|
|
5
5
|
function extractFieldSnapshots(fields, typeMapper) {
|
|
@@ -66,20 +66,43 @@ function computeViewSnapshot(view) {
|
|
|
66
66
|
};
|
|
67
67
|
const plan = view.viewPlan;
|
|
68
68
|
const qualify = (ref) => view.resolveFieldRef(ref, (n) => n);
|
|
69
|
+
const canonical = (node) => JSON.stringify(canonicalizeQueryNode(node, qualify));
|
|
70
|
+
const columns = view.getViewColumnMappings().map((m) => {
|
|
71
|
+
const col = {
|
|
72
|
+
column: m.viewColumn,
|
|
73
|
+
sourceTable: m.sourceTable,
|
|
74
|
+
sourceColumn: m.sourceColumn
|
|
75
|
+
};
|
|
76
|
+
if (m.json) {
|
|
77
|
+
col.jsonPath = JSON.stringify(m.json.path);
|
|
78
|
+
col.jsonType = m.json.type;
|
|
79
|
+
}
|
|
80
|
+
if (m.aggFn) col.aggFn = m.aggFn;
|
|
81
|
+
if (m.aggField) col.aggField = m.aggField;
|
|
82
|
+
if (m.aggFilter) col.aggFilter = canonical(m.aggFilter);
|
|
83
|
+
return col;
|
|
84
|
+
}).toSorted((a, b) => a.column < b.column ? -1 : a.column > b.column ? 1 : 0);
|
|
69
85
|
const result = {
|
|
70
86
|
tableName: view.tableName,
|
|
71
87
|
viewType: plan.materialized ? "M" : "V",
|
|
72
88
|
entryTable: plan.entryTable,
|
|
73
|
-
joinTables: plan.joins.map((j) =>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
89
|
+
joinTables: plan.joins.map((j) => {
|
|
90
|
+
const join = {
|
|
91
|
+
targetTable: j.targetTable,
|
|
92
|
+
condition: canonical(j.condition)
|
|
93
|
+
};
|
|
94
|
+
if (j.kind === "left") join.kind = "left";
|
|
95
|
+
return join;
|
|
96
|
+
}),
|
|
97
|
+
columns
|
|
98
|
+
};
|
|
99
|
+
if (plan.filter) result.filterHash = fnv1a(canonical(plan.filter));
|
|
100
|
+
if (plan.having) result.havingHash = fnv1a(canonical(plan.having));
|
|
101
|
+
if (plan.materialized) result.materialized = true;
|
|
102
|
+
return {
|
|
103
|
+
...result,
|
|
78
104
|
fields
|
|
79
105
|
};
|
|
80
|
-
if (plan.filter) result.filterHash = fnv1a(JSON.stringify(canonicalizeQueryNode(plan.filter, qualify)));
|
|
81
|
-
if (plan.having) result.havingHash = fnv1a(JSON.stringify(canonicalizeQueryNode(plan.having, qualify)));
|
|
82
|
-
return result;
|
|
83
106
|
}
|
|
84
107
|
/**
|
|
85
108
|
* Converts a view predicate (join condition, `@db.view.filter`,
|
|
@@ -1747,7 +1770,11 @@ var SchemaSync = class {
|
|
|
1747
1770
|
if (d.externalTargets.get(fk.targetTable) === false) addRefusal(refusals, t.name, `FK ${t.name}.${fk.fields.join(",")} references "${fk.targetTable}" which is neither in the sync inventory nor present in the database`);
|
|
1748
1771
|
}
|
|
1749
1772
|
}
|
|
1750
|
-
for (const view of d.views)
|
|
1773
|
+
for (const view of d.views) {
|
|
1774
|
+
if (d.viewObjectKinds.get(view.tableName) === "table") addRefusal(refusals, view.tableName, `A physical table "${view.tableName}" exists where managed view "${view.tableName}" is declared — drop or rename it`);
|
|
1775
|
+
const fns = view.dbAdapter.aggregateFns();
|
|
1776
|
+
for (const col of view.getViewColumnMappings()) if (col.aggFn && !fns.has(col.aggFn)) addRefusal(refusals, view.tableName, `View "${view.tableName}" field "${col.viewPath}": aggregate "${col.aggFn}" is not supported by this adapter (aggregateFns())`);
|
|
1777
|
+
}
|
|
1751
1778
|
if (!safe) for (const r of d.removed) {
|
|
1752
1779
|
if (r.isView) continue;
|
|
1753
1780
|
const referencers = [];
|
package/dist/sync.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { D as TColumnDiff, Ft as AtscriptDbReadable, G as TDbObjectKind, I as TDbDefaultValue, R as TDbFieldMeta, Ut as TGenericLogger, X as TDbStorageType, at as TExistingTableOption, ht as TReferencingForeignKey, it as TExistingForeignKey, mt as TPrimaryKeyChange, nt as TEnsureTableOptions, rt as TExistingColumn, yt as TTableOptionDiff, z as TDbForeignKey } from "./buckets-kKY0RKYN.cjs";
|
|
2
|
+
import { i as AtscriptDbView, l as AtscriptQueryFieldRef$1, t as DbSpace, u as AtscriptQueryNode$1 } from "./db-space-ClG41vJh.cjs";
|
|
3
3
|
import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
|
|
4
4
|
|
|
5
5
|
//#region src/schema/sync-entry.d.ts
|
|
@@ -269,9 +269,27 @@ interface TViewJoinSnapshot {
|
|
|
269
269
|
targetTable: string;
|
|
270
270
|
/** Canonical JSON of the join condition (see {@link canonicalizeQueryNode}). */
|
|
271
271
|
condition: string;
|
|
272
|
-
/**
|
|
272
|
+
/** Emitted only for `"left"` — an inner join (the default) carries no key. @since 0.1.136 */
|
|
273
273
|
kind?: "inner" | "left";
|
|
274
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* One column of a managed view as stored in its snapshot — the PHYSICAL
|
|
277
|
+
* source it reads, so a source rename (`@db.column`, flattening, a moved
|
|
278
|
+
* JSON leaf) or an aggregate change recreates the view.
|
|
279
|
+
* @since 0.1.136
|
|
280
|
+
*/
|
|
281
|
+
interface TViewColumnSnapshot {
|
|
282
|
+
column: string;
|
|
283
|
+
sourceTable: string;
|
|
284
|
+
sourceColumn: string;
|
|
285
|
+
/** JSON array of the path segments inside a JSON source column. */
|
|
286
|
+
jsonPath?: string;
|
|
287
|
+
jsonType?: string;
|
|
288
|
+
aggFn?: string;
|
|
289
|
+
aggField?: string;
|
|
290
|
+
/** Canonical JSON of a conditional aggregate's predicate. */
|
|
291
|
+
aggFilter?: string;
|
|
292
|
+
}
|
|
275
293
|
interface TViewSnapshot {
|
|
276
294
|
tableName: string;
|
|
277
295
|
viewType: "V" | "M" | "E";
|
|
@@ -281,6 +299,8 @@ interface TViewSnapshot {
|
|
|
281
299
|
* join-less view (`[]`) serializes byte-identically to older snapshots.
|
|
282
300
|
*/
|
|
283
301
|
joinTables?: TViewJoinSnapshot[];
|
|
302
|
+
/** @since 0.1.136 — view columns and their physical sources, sorted by `column`. */
|
|
303
|
+
columns?: TViewColumnSnapshot[];
|
|
284
304
|
filterHash?: string;
|
|
285
305
|
/** @since 0.1.128 — hash of the canonical `@db.view.having` predicate. */
|
|
286
306
|
havingHash?: string;
|
|
@@ -649,4 +669,4 @@ declare function syncSchema(space: DbSpace, types: readonly TAtscriptAnnotatedTy
|
|
|
649
669
|
*/
|
|
650
670
|
declare function planSchema(space: DbSpace, types: readonly TAtscriptAnnotatedType[], opts?: Pick<TSyncOptions, "force" | "safe">): Promise<TSyncPlan>;
|
|
651
671
|
//#endregion
|
|
652
|
-
export { SchemaSync, SyncEntry, type TDbObjectKind, type TDependencyEdge, type TEnsureTableOptions, type TExistingForeignKey, type TFieldSnapshot, type TForeignKeyDiff, type TForeignKeySnapshot, type TPrimaryKeyChange, type TReferencingForeignKey, type TSyncColors, type TSyncEntryInit, type TSyncEntryStatus, type TSyncOptions, type TSyncPlan, type TSyncResult, type TSyncSkippedWork, type TTableSnapshot, type TViewJoinSnapshot, type TViewSnapshot, canonicalizeQueryNode, computeColumnDiff, computeForeignKeyDiff, computeSchemaHash, computeTableHash, computeTableOptionDiff, computeTableSnapshot, computeViewSnapshot, fkKey, hasForeignKeyChanges, planSchema, readStoredSnapshot, snapshotToExistingColumns, snapshotToExistingTableOptions, syncSchema, topoOrder };
|
|
672
|
+
export { SchemaSync, SyncEntry, type TDbObjectKind, type TDependencyEdge, type TEnsureTableOptions, type TExistingForeignKey, type TFieldSnapshot, type TForeignKeyDiff, type TForeignKeySnapshot, type TPrimaryKeyChange, type TReferencingForeignKey, type TSyncColors, type TSyncEntryInit, type TSyncEntryStatus, type TSyncOptions, type TSyncPlan, type TSyncResult, type TSyncSkippedWork, type TTableSnapshot, type TViewColumnSnapshot, type TViewJoinSnapshot, type TViewSnapshot, canonicalizeQueryNode, computeColumnDiff, computeForeignKeyDiff, computeSchemaHash, computeTableHash, computeTableOptionDiff, computeTableSnapshot, computeViewSnapshot, fkKey, hasForeignKeyChanges, planSchema, readStoredSnapshot, snapshotToExistingColumns, snapshotToExistingTableOptions, syncSchema, topoOrder };
|
package/dist/sync.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { D as TColumnDiff, Ft as AtscriptDbReadable, G as TDbObjectKind, I as TDbDefaultValue, R as TDbFieldMeta, Ut as TGenericLogger, X as TDbStorageType, at as TExistingTableOption, ht as TReferencingForeignKey, it as TExistingForeignKey, mt as TPrimaryKeyChange, nt as TEnsureTableOptions, rt as TExistingColumn, yt as TTableOptionDiff, z as TDbForeignKey } from "./buckets-y4EzoX7D.mjs";
|
|
2
|
+
import { i as AtscriptDbView, l as AtscriptQueryFieldRef$1, t as DbSpace, u as AtscriptQueryNode$1 } from "./db-space-BX4K3vb6.mjs";
|
|
3
3
|
import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
|
|
4
4
|
|
|
5
5
|
//#region src/schema/sync-entry.d.ts
|
|
@@ -269,9 +269,27 @@ interface TViewJoinSnapshot {
|
|
|
269
269
|
targetTable: string;
|
|
270
270
|
/** Canonical JSON of the join condition (see {@link canonicalizeQueryNode}). */
|
|
271
271
|
condition: string;
|
|
272
|
-
/**
|
|
272
|
+
/** Emitted only for `"left"` — an inner join (the default) carries no key. @since 0.1.136 */
|
|
273
273
|
kind?: "inner" | "left";
|
|
274
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* One column of a managed view as stored in its snapshot — the PHYSICAL
|
|
277
|
+
* source it reads, so a source rename (`@db.column`, flattening, a moved
|
|
278
|
+
* JSON leaf) or an aggregate change recreates the view.
|
|
279
|
+
* @since 0.1.136
|
|
280
|
+
*/
|
|
281
|
+
interface TViewColumnSnapshot {
|
|
282
|
+
column: string;
|
|
283
|
+
sourceTable: string;
|
|
284
|
+
sourceColumn: string;
|
|
285
|
+
/** JSON array of the path segments inside a JSON source column. */
|
|
286
|
+
jsonPath?: string;
|
|
287
|
+
jsonType?: string;
|
|
288
|
+
aggFn?: string;
|
|
289
|
+
aggField?: string;
|
|
290
|
+
/** Canonical JSON of a conditional aggregate's predicate. */
|
|
291
|
+
aggFilter?: string;
|
|
292
|
+
}
|
|
275
293
|
interface TViewSnapshot {
|
|
276
294
|
tableName: string;
|
|
277
295
|
viewType: "V" | "M" | "E";
|
|
@@ -281,6 +299,8 @@ interface TViewSnapshot {
|
|
|
281
299
|
* join-less view (`[]`) serializes byte-identically to older snapshots.
|
|
282
300
|
*/
|
|
283
301
|
joinTables?: TViewJoinSnapshot[];
|
|
302
|
+
/** @since 0.1.136 — view columns and their physical sources, sorted by `column`. */
|
|
303
|
+
columns?: TViewColumnSnapshot[];
|
|
284
304
|
filterHash?: string;
|
|
285
305
|
/** @since 0.1.128 — hash of the canonical `@db.view.having` predicate. */
|
|
286
306
|
havingHash?: string;
|
|
@@ -649,4 +669,4 @@ declare function syncSchema(space: DbSpace, types: readonly TAtscriptAnnotatedTy
|
|
|
649
669
|
*/
|
|
650
670
|
declare function planSchema(space: DbSpace, types: readonly TAtscriptAnnotatedType[], opts?: Pick<TSyncOptions, "force" | "safe">): Promise<TSyncPlan>;
|
|
651
671
|
//#endregion
|
|
652
|
-
export { SchemaSync, SyncEntry, type TDbObjectKind, type TDependencyEdge, type TEnsureTableOptions, type TExistingForeignKey, type TFieldSnapshot, type TForeignKeyDiff, type TForeignKeySnapshot, type TPrimaryKeyChange, type TReferencingForeignKey, type TSyncColors, type TSyncEntryInit, type TSyncEntryStatus, type TSyncOptions, type TSyncPlan, type TSyncResult, type TSyncSkippedWork, type TTableSnapshot, type TViewJoinSnapshot, type TViewSnapshot, canonicalizeQueryNode, computeColumnDiff, computeForeignKeyDiff, computeSchemaHash, computeTableHash, computeTableOptionDiff, computeTableSnapshot, computeViewSnapshot, fkKey, hasForeignKeyChanges, planSchema, readStoredSnapshot, snapshotToExistingColumns, snapshotToExistingTableOptions, syncSchema, topoOrder };
|
|
672
|
+
export { SchemaSync, SyncEntry, type TDbObjectKind, type TDependencyEdge, type TEnsureTableOptions, type TExistingForeignKey, type TFieldSnapshot, type TForeignKeyDiff, type TForeignKeySnapshot, type TPrimaryKeyChange, type TReferencingForeignKey, type TSyncColors, type TSyncEntryInit, type TSyncEntryStatus, type TSyncOptions, type TSyncPlan, type TSyncResult, type TSyncSkippedWork, type TTableSnapshot, type TViewColumnSnapshot, type TViewJoinSnapshot, type TViewSnapshot, canonicalizeQueryNode, computeColumnDiff, computeForeignKeyDiff, computeSchemaHash, computeTableHash, computeTableOptionDiff, computeTableSnapshot, computeViewSnapshot, fkKey, hasForeignKeyChanges, planSchema, readStoredSnapshot, snapshotToExistingColumns, snapshotToExistingTableOptions, syncSchema, topoOrder };
|
package/dist/sync.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { W as NoopLogger } from "./db-view-BrAtbaOx.mjs";
|
|
2
2
|
//#region src/schema/schema-hash.ts
|
|
3
3
|
/** Extracts sorted field snapshots from a readable's field descriptors. */
|
|
4
4
|
function extractFieldSnapshots(fields, typeMapper) {
|
|
@@ -65,20 +65,43 @@ function computeViewSnapshot(view) {
|
|
|
65
65
|
};
|
|
66
66
|
const plan = view.viewPlan;
|
|
67
67
|
const qualify = (ref) => view.resolveFieldRef(ref, (n) => n);
|
|
68
|
+
const canonical = (node) => JSON.stringify(canonicalizeQueryNode(node, qualify));
|
|
69
|
+
const columns = view.getViewColumnMappings().map((m) => {
|
|
70
|
+
const col = {
|
|
71
|
+
column: m.viewColumn,
|
|
72
|
+
sourceTable: m.sourceTable,
|
|
73
|
+
sourceColumn: m.sourceColumn
|
|
74
|
+
};
|
|
75
|
+
if (m.json) {
|
|
76
|
+
col.jsonPath = JSON.stringify(m.json.path);
|
|
77
|
+
col.jsonType = m.json.type;
|
|
78
|
+
}
|
|
79
|
+
if (m.aggFn) col.aggFn = m.aggFn;
|
|
80
|
+
if (m.aggField) col.aggField = m.aggField;
|
|
81
|
+
if (m.aggFilter) col.aggFilter = canonical(m.aggFilter);
|
|
82
|
+
return col;
|
|
83
|
+
}).toSorted((a, b) => a.column < b.column ? -1 : a.column > b.column ? 1 : 0);
|
|
68
84
|
const result = {
|
|
69
85
|
tableName: view.tableName,
|
|
70
86
|
viewType: plan.materialized ? "M" : "V",
|
|
71
87
|
entryTable: plan.entryTable,
|
|
72
|
-
joinTables: plan.joins.map((j) =>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
88
|
+
joinTables: plan.joins.map((j) => {
|
|
89
|
+
const join = {
|
|
90
|
+
targetTable: j.targetTable,
|
|
91
|
+
condition: canonical(j.condition)
|
|
92
|
+
};
|
|
93
|
+
if (j.kind === "left") join.kind = "left";
|
|
94
|
+
return join;
|
|
95
|
+
}),
|
|
96
|
+
columns
|
|
97
|
+
};
|
|
98
|
+
if (plan.filter) result.filterHash = fnv1a(canonical(plan.filter));
|
|
99
|
+
if (plan.having) result.havingHash = fnv1a(canonical(plan.having));
|
|
100
|
+
if (plan.materialized) result.materialized = true;
|
|
101
|
+
return {
|
|
102
|
+
...result,
|
|
77
103
|
fields
|
|
78
104
|
};
|
|
79
|
-
if (plan.filter) result.filterHash = fnv1a(JSON.stringify(canonicalizeQueryNode(plan.filter, qualify)));
|
|
80
|
-
if (plan.having) result.havingHash = fnv1a(JSON.stringify(canonicalizeQueryNode(plan.having, qualify)));
|
|
81
|
-
return result;
|
|
82
105
|
}
|
|
83
106
|
/**
|
|
84
107
|
* Converts a view predicate (join condition, `@db.view.filter`,
|
|
@@ -1746,7 +1769,11 @@ var SchemaSync = class {
|
|
|
1746
1769
|
if (d.externalTargets.get(fk.targetTable) === false) addRefusal(refusals, t.name, `FK ${t.name}.${fk.fields.join(",")} references "${fk.targetTable}" which is neither in the sync inventory nor present in the database`);
|
|
1747
1770
|
}
|
|
1748
1771
|
}
|
|
1749
|
-
for (const view of d.views)
|
|
1772
|
+
for (const view of d.views) {
|
|
1773
|
+
if (d.viewObjectKinds.get(view.tableName) === "table") addRefusal(refusals, view.tableName, `A physical table "${view.tableName}" exists where managed view "${view.tableName}" is declared — drop or rename it`);
|
|
1774
|
+
const fns = view.dbAdapter.aggregateFns();
|
|
1775
|
+
for (const col of view.getViewColumnMappings()) if (col.aggFn && !fns.has(col.aggFn)) addRefusal(refusals, view.tableName, `View "${view.tableName}" field "${col.viewPath}": aggregate "${col.aggFn}" is not supported by this adapter (aggregateFns())`);
|
|
1776
|
+
}
|
|
1750
1777
|
if (!safe) for (const r of d.removed) {
|
|
1751
1778
|
if (r.isView) continue;
|
|
1752
1779
|
const referencers = [];
|
|
@@ -11,14 +11,17 @@ function validateExclusiveWith(token, selfName, others) {
|
|
|
11
11
|
});
|
|
12
12
|
return errors;
|
|
13
13
|
}
|
|
14
|
+
/** Primitive base type (`string`, `number`, `decimal`, …) of a resolved definition, if it is a primitive. */
|
|
15
|
+
function primitiveBaseType(def) {
|
|
16
|
+
if (!def || !isPrimitive(def)) return void 0;
|
|
17
|
+
const ct = def.config.type;
|
|
18
|
+
return typeof ct === "object" ? ct.kind === "final" ? ct.value : ct.kind : ct;
|
|
19
|
+
}
|
|
14
20
|
/** Resolves a field/prop's primitive base type, or `undefined` if it isn't a ref-to-primitive. */
|
|
15
21
|
function getPrimitiveBaseType(node, doc) {
|
|
16
22
|
const def = node.getDefinition();
|
|
17
23
|
if (!def || !isRef(def)) return void 0;
|
|
18
|
-
|
|
19
|
-
if (!unwound || !isPrimitive(unwound.def)) return void 0;
|
|
20
|
-
const ct = unwound.def.config.type;
|
|
21
|
-
return typeof ct === "object" ? ct.kind === "final" ? ct.value : ct.kind : ct;
|
|
24
|
+
return primitiveBaseType(doc.unwindType(def.id, def.chain)?.def);
|
|
22
25
|
}
|
|
23
26
|
/** Asserts `args[0]` names a sibling property whose primitive base type is `string`. */
|
|
24
27
|
function validateSiblingStringField(token, args, doc, selfName) {
|
|
@@ -253,6 +256,24 @@ const viewAnnotationNames = [
|
|
|
253
256
|
"db.view.filter",
|
|
254
257
|
"db.view.materialized"
|
|
255
258
|
];
|
|
259
|
+
/** The `@db.view.joins` annotations of a view interface, in declaration order. */
|
|
260
|
+
function viewJoins(owner) {
|
|
261
|
+
return owner.annotations?.filter((a) => a.name === "db.view.joins") ?? [];
|
|
262
|
+
}
|
|
263
|
+
/** The target type names of `@db.view.joins` annotations. */
|
|
264
|
+
function joinTargets(joins) {
|
|
265
|
+
return joins.map((a) => a.args[0]?.text).filter((t) => !!t);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* The type names a view predicate (`@db.view.filter`, a conditional
|
|
269
|
+
* `@db.agg.*`) may reference: the `@db.view.for` entry table, then every
|
|
270
|
+
* `@db.view.joins` target.
|
|
271
|
+
*/
|
|
272
|
+
function viewScopeTypes(owner) {
|
|
273
|
+
const entry = getAnnotationAlias(owner, "db.view.for");
|
|
274
|
+
const targets = joinTargets(viewJoins(owner));
|
|
275
|
+
return entry ? [entry, ...targets] : targets;
|
|
276
|
+
}
|
|
256
277
|
/**
|
|
257
278
|
* Check if a node has any @db.view.* annotation.
|
|
258
279
|
*/
|
|
@@ -266,8 +287,9 @@ function hasAnyViewAnnotation(node) {
|
|
|
266
287
|
* @param allowedTypes - Type names allowed as qualified refs
|
|
267
288
|
* @param unqualifiedTarget - Type name for resolving unqualified refs, or null to disallow them
|
|
268
289
|
* @param doc - The document for type lookups
|
|
290
|
+
* @param scopeHint - Replaces the default "expected 'A' or 'B'" tail of the out-of-scope message
|
|
269
291
|
*/
|
|
270
|
-
function validateQueryScope(queryToken, allowedTypes, unqualifiedTarget, doc) {
|
|
292
|
+
function validateQueryScope(queryToken, allowedTypes, unqualifiedTarget, doc, scopeHint) {
|
|
271
293
|
const errors = [];
|
|
272
294
|
const queryNode = queryToken.queryNode;
|
|
273
295
|
if (!queryNode) return errors;
|
|
@@ -275,7 +297,7 @@ function validateQueryScope(queryToken, allowedTypes, unqualifiedTarget, doc) {
|
|
|
275
297
|
if (ref.typeRef) {
|
|
276
298
|
const typeName = ref.typeRef.text;
|
|
277
299
|
if (!allowedTypes.includes(typeName)) errors.push({
|
|
278
|
-
message: `Query references '${typeName}' which is not in scope — expected ${allowedTypes.map((t) => `'${t}'`).join(" or ")}`,
|
|
300
|
+
message: `Query references '${typeName}' which is not in scope — ${scopeHint ?? `expected ${allowedTypes.map((t) => `'${t}'`).join(" or ")}`}`,
|
|
279
301
|
severity: 1,
|
|
280
302
|
range: ref.typeRef.range
|
|
281
303
|
});
|
|
@@ -310,4 +332,4 @@ function validateQueryScope(queryToken, allowedTypes, unqualifiedTarget, doc) {
|
|
|
310
332
|
return errors;
|
|
311
333
|
}
|
|
312
334
|
//#endregion
|
|
313
|
-
export {
|
|
335
|
+
export { validateSiblingStringField as _, validateRefArgument as a, getAnnotationAlias as c, getParentStruct as d, getParentTypeName as f, validateFieldBaseType as g, validateExclusiveWith as h, validateQueryScope as i, getDbTableOwner as l, refActionAnnotation as m, hasAnyViewAnnotation as n, viewJoins as o, primitiveBaseType as p, joinTargets as r, viewScopeTypes as s, findFKFieldsPointingTo as t, getNavTargetTypeName as u };
|
|
@@ -11,14 +11,17 @@ function validateExclusiveWith(token, selfName, others) {
|
|
|
11
11
|
});
|
|
12
12
|
return errors;
|
|
13
13
|
}
|
|
14
|
+
/** Primitive base type (`string`, `number`, `decimal`, …) of a resolved definition, if it is a primitive. */
|
|
15
|
+
function primitiveBaseType(def) {
|
|
16
|
+
if (!def || !(0, _atscript_core.isPrimitive)(def)) return void 0;
|
|
17
|
+
const ct = def.config.type;
|
|
18
|
+
return typeof ct === "object" ? ct.kind === "final" ? ct.value : ct.kind : ct;
|
|
19
|
+
}
|
|
14
20
|
/** Resolves a field/prop's primitive base type, or `undefined` if it isn't a ref-to-primitive. */
|
|
15
21
|
function getPrimitiveBaseType(node, doc) {
|
|
16
22
|
const def = node.getDefinition();
|
|
17
23
|
if (!def || !(0, _atscript_core.isRef)(def)) return void 0;
|
|
18
|
-
|
|
19
|
-
if (!unwound || !(0, _atscript_core.isPrimitive)(unwound.def)) return void 0;
|
|
20
|
-
const ct = unwound.def.config.type;
|
|
21
|
-
return typeof ct === "object" ? ct.kind === "final" ? ct.value : ct.kind : ct;
|
|
24
|
+
return primitiveBaseType(doc.unwindType(def.id, def.chain)?.def);
|
|
22
25
|
}
|
|
23
26
|
/** Asserts `args[0]` names a sibling property whose primitive base type is `string`. */
|
|
24
27
|
function validateSiblingStringField(token, args, doc, selfName) {
|
|
@@ -253,6 +256,24 @@ const viewAnnotationNames = [
|
|
|
253
256
|
"db.view.filter",
|
|
254
257
|
"db.view.materialized"
|
|
255
258
|
];
|
|
259
|
+
/** The `@db.view.joins` annotations of a view interface, in declaration order. */
|
|
260
|
+
function viewJoins(owner) {
|
|
261
|
+
return owner.annotations?.filter((a) => a.name === "db.view.joins") ?? [];
|
|
262
|
+
}
|
|
263
|
+
/** The target type names of `@db.view.joins` annotations. */
|
|
264
|
+
function joinTargets(joins) {
|
|
265
|
+
return joins.map((a) => a.args[0]?.text).filter((t) => !!t);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* The type names a view predicate (`@db.view.filter`, a conditional
|
|
269
|
+
* `@db.agg.*`) may reference: the `@db.view.for` entry table, then every
|
|
270
|
+
* `@db.view.joins` target.
|
|
271
|
+
*/
|
|
272
|
+
function viewScopeTypes(owner) {
|
|
273
|
+
const entry = getAnnotationAlias(owner, "db.view.for");
|
|
274
|
+
const targets = joinTargets(viewJoins(owner));
|
|
275
|
+
return entry ? [entry, ...targets] : targets;
|
|
276
|
+
}
|
|
256
277
|
/**
|
|
257
278
|
* Check if a node has any @db.view.* annotation.
|
|
258
279
|
*/
|
|
@@ -266,8 +287,9 @@ function hasAnyViewAnnotation(node) {
|
|
|
266
287
|
* @param allowedTypes - Type names allowed as qualified refs
|
|
267
288
|
* @param unqualifiedTarget - Type name for resolving unqualified refs, or null to disallow them
|
|
268
289
|
* @param doc - The document for type lookups
|
|
290
|
+
* @param scopeHint - Replaces the default "expected 'A' or 'B'" tail of the out-of-scope message
|
|
269
291
|
*/
|
|
270
|
-
function validateQueryScope(queryToken, allowedTypes, unqualifiedTarget, doc) {
|
|
292
|
+
function validateQueryScope(queryToken, allowedTypes, unqualifiedTarget, doc, scopeHint) {
|
|
271
293
|
const errors = [];
|
|
272
294
|
const queryNode = queryToken.queryNode;
|
|
273
295
|
if (!queryNode) return errors;
|
|
@@ -275,7 +297,7 @@ function validateQueryScope(queryToken, allowedTypes, unqualifiedTarget, doc) {
|
|
|
275
297
|
if (ref.typeRef) {
|
|
276
298
|
const typeName = ref.typeRef.text;
|
|
277
299
|
if (!allowedTypes.includes(typeName)) errors.push({
|
|
278
|
-
message: `Query references '${typeName}' which is not in scope — expected ${allowedTypes.map((t) => `'${t}'`).join(" or ")}`,
|
|
300
|
+
message: `Query references '${typeName}' which is not in scope — ${scopeHint ?? `expected ${allowedTypes.map((t) => `'${t}'`).join(" or ")}`}`,
|
|
279
301
|
severity: 1,
|
|
280
302
|
range: ref.typeRef.range
|
|
281
303
|
});
|
|
@@ -352,6 +374,18 @@ Object.defineProperty(exports, "hasAnyViewAnnotation", {
|
|
|
352
374
|
return hasAnyViewAnnotation;
|
|
353
375
|
}
|
|
354
376
|
});
|
|
377
|
+
Object.defineProperty(exports, "joinTargets", {
|
|
378
|
+
enumerable: true,
|
|
379
|
+
get: function() {
|
|
380
|
+
return joinTargets;
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
Object.defineProperty(exports, "primitiveBaseType", {
|
|
384
|
+
enumerable: true,
|
|
385
|
+
get: function() {
|
|
386
|
+
return primitiveBaseType;
|
|
387
|
+
}
|
|
388
|
+
});
|
|
355
389
|
Object.defineProperty(exports, "refActionAnnotation", {
|
|
356
390
|
enumerable: true,
|
|
357
391
|
get: function() {
|
|
@@ -388,3 +422,15 @@ Object.defineProperty(exports, "validateSiblingStringField", {
|
|
|
388
422
|
return validateSiblingStringField;
|
|
389
423
|
}
|
|
390
424
|
});
|
|
425
|
+
Object.defineProperty(exports, "viewJoins", {
|
|
426
|
+
enumerable: true,
|
|
427
|
+
get: function() {
|
|
428
|
+
return viewJoins;
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
Object.defineProperty(exports, "viewScopeTypes", {
|
|
432
|
+
enumerable: true,
|
|
433
|
+
get: function() {
|
|
434
|
+
return viewScopeTypes;
|
|
435
|
+
}
|
|
436
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TAtscriptAnnotatedType, TAtscriptTypeArray, TAtscriptTypeObject, TValidatorPlugin, Validator } from "@atscript/typescript/utils";
|
|
2
2
|
|
|
3
3
|
//#region src/db-error.d.ts
|
|
4
|
-
type DbErrorCode = "CONFLICT" | "FK_VIOLATION" | "NOT_FOUND" | "CASCADE_CYCLE" | "INVALID_QUERY" | "DEPTH_EXCEEDED" | "VERSION_COLUMN_WRITE" | "CAS_EXHAUSTED" | "CAS_MISMATCH" | "ENC_CONFIG_MISSING" | "ENC_KEY_INVALID" | "ENC_NOT_ENCRYPTED" | "ENC_DECRYPT_FAILED" | "ENC_FIELD_FILTER" | "ENC_FIELD_SORT" | "ENC_FIELD_AGG" | "ENC_FIELD_PATCH_OP" | "GEO_INDEX_MISSING" | "GEO_NOT_SUPPORTED" | "FILTER_TYPE_MISMATCH" /** The adapter's `calendarBucketUnits()` lacks the requested unit (moost-db: 400). */ | "BUCKET_NOT_SUPPORTED" /** The engine cannot resolve the bucket's time zone — a store-configuration condition (moost-db: 501). */ | "BUCKET_TZ_UNAVAILABLE" | "TX_WAIT_TIMEOUT";
|
|
4
|
+
type DbErrorCode = "CONFLICT" | "FK_VIOLATION" | "NOT_FOUND" | "CASCADE_CYCLE" | "INVALID_QUERY" | "DEPTH_EXCEEDED" | "VERSION_COLUMN_WRITE" | "CAS_EXHAUSTED" | "CAS_MISMATCH" | "ENC_CONFIG_MISSING" | "ENC_KEY_INVALID" | "ENC_NOT_ENCRYPTED" | "ENC_DECRYPT_FAILED" | "ENC_FIELD_FILTER" | "ENC_FIELD_SORT" | "ENC_FIELD_AGG" | "ENC_FIELD_PATCH_OP" | "GEO_INDEX_MISSING" | "GEO_NOT_SUPPORTED" | "FILTER_TYPE_MISMATCH" /** The adapter's `calendarBucketUnits()` lacks the requested unit (moost-db: 400). */ | "BUCKET_NOT_SUPPORTED" /** The engine cannot resolve the bucket's time zone — a store-configuration condition (moost-db: 501). */ | "BUCKET_TZ_UNAVAILABLE" /** A known aggregate function the adapter's `aggregateFns()` lacks (moost-db: 400). @since 0.1.136 */ | "AGG_FN_NOT_SUPPORTED" | "TX_WAIT_TIMEOUT";
|
|
5
5
|
declare class DbError extends Error {
|
|
6
6
|
readonly code: DbErrorCode;
|
|
7
7
|
readonly errors: Array<{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TAtscriptAnnotatedType, TAtscriptTypeArray, TAtscriptTypeObject, TValidatorPlugin, Validator } from "@atscript/typescript/utils";
|
|
2
2
|
|
|
3
3
|
//#region src/db-error.d.ts
|
|
4
|
-
type DbErrorCode = "CONFLICT" | "FK_VIOLATION" | "NOT_FOUND" | "CASCADE_CYCLE" | "INVALID_QUERY" | "DEPTH_EXCEEDED" | "VERSION_COLUMN_WRITE" | "CAS_EXHAUSTED" | "CAS_MISMATCH" | "ENC_CONFIG_MISSING" | "ENC_KEY_INVALID" | "ENC_NOT_ENCRYPTED" | "ENC_DECRYPT_FAILED" | "ENC_FIELD_FILTER" | "ENC_FIELD_SORT" | "ENC_FIELD_AGG" | "ENC_FIELD_PATCH_OP" | "GEO_INDEX_MISSING" | "GEO_NOT_SUPPORTED" | "FILTER_TYPE_MISMATCH" /** The adapter's `calendarBucketUnits()` lacks the requested unit (moost-db: 400). */ | "BUCKET_NOT_SUPPORTED" /** The engine cannot resolve the bucket's time zone — a store-configuration condition (moost-db: 501). */ | "BUCKET_TZ_UNAVAILABLE" | "TX_WAIT_TIMEOUT";
|
|
4
|
+
type DbErrorCode = "CONFLICT" | "FK_VIOLATION" | "NOT_FOUND" | "CASCADE_CYCLE" | "INVALID_QUERY" | "DEPTH_EXCEEDED" | "VERSION_COLUMN_WRITE" | "CAS_EXHAUSTED" | "CAS_MISMATCH" | "ENC_CONFIG_MISSING" | "ENC_KEY_INVALID" | "ENC_NOT_ENCRYPTED" | "ENC_DECRYPT_FAILED" | "ENC_FIELD_FILTER" | "ENC_FIELD_SORT" | "ENC_FIELD_AGG" | "ENC_FIELD_PATCH_OP" | "GEO_INDEX_MISSING" | "GEO_NOT_SUPPORTED" | "FILTER_TYPE_MISMATCH" /** The adapter's `calendarBucketUnits()` lacks the requested unit (moost-db: 400). */ | "BUCKET_NOT_SUPPORTED" /** The engine cannot resolve the bucket's time zone — a store-configuration condition (moost-db: 501). */ | "BUCKET_TZ_UNAVAILABLE" /** A known aggregate function the adapter's `aggregateFns()` lacks (moost-db: 400). @since 0.1.136 */ | "AGG_FN_NOT_SUPPORTED" | "TX_WAIT_TIMEOUT";
|
|
5
5
|
declare class DbError extends Error {
|
|
6
6
|
readonly code: DbErrorCode;
|
|
7
7
|
readonly errors: Array<{
|
package/dist/validator.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { a as $mul, c as $update, d as TDbFieldOp, g as separateCas, h as reconcileCas, i as $insert, l as $upsert, m as isDbFieldOp, n as $dec, o as $remove, r as $inc, s as $replace, t as $cas, u as TDbCas } from "./ops-AqhV7s9o.cjs";
|
|
2
|
-
import { a as buildValidationContext, c as isNavRelation, d as TArrayPatch, f as TDbPatch, g as DbError, i as buildPatchPartial, l as isEmptyObject, n as ValidatorMode, o as dbPlugin, p as getKeyProps, r as buildDbValidator, s as forceNavNonOptional, t as ValidationContext, u as isPlainObject } from "./validator-
|
|
2
|
+
import { a as buildValidationContext, c as isNavRelation, d as TArrayPatch, f as TDbPatch, g as DbError, i as buildPatchPartial, l as isEmptyObject, n as ValidatorMode, o as dbPlugin, p as getKeyProps, r as buildDbValidator, s as forceNavNonOptional, t as ValidationContext, u as isPlainObject } from "./validator-Drb2N-YL.cjs";
|
|
3
3
|
import { n as createDbValidatorPlugin, t as DbValidationContext } from "./db-validator-plugin-BWy60OvG.cjs";
|
|
4
4
|
export { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, DbError, type DbValidationContext, type TArrayPatch, type TDbCas, type TDbFieldOp, type TDbPatch, ValidationContext, ValidatorMode, buildDbValidator, buildPatchPartial, buildValidationContext, createDbValidatorPlugin, dbPlugin, forceNavNonOptional, getKeyProps, isDbFieldOp, isEmptyObject, isNavRelation, isPlainObject, reconcileCas, separateCas };
|
package/dist/validator.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { a as $mul, c as $update, d as TDbFieldOp, g as separateCas, h as reconcileCas, i as $insert, l as $upsert, m as isDbFieldOp, n as $dec, o as $remove, r as $inc, s as $replace, t as $cas, u as TDbCas } from "./ops-AqhV7s9o.mjs";
|
|
2
|
-
import { a as buildValidationContext, c as isNavRelation, d as TArrayPatch, f as TDbPatch, g as DbError, i as buildPatchPartial, l as isEmptyObject, n as ValidatorMode, o as dbPlugin, p as getKeyProps, r as buildDbValidator, s as forceNavNonOptional, t as ValidationContext, u as isPlainObject } from "./validator-
|
|
2
|
+
import { a as buildValidationContext, c as isNavRelation, d as TArrayPatch, f as TDbPatch, g as DbError, i as buildPatchPartial, l as isEmptyObject, n as ValidatorMode, o as dbPlugin, p as getKeyProps, r as buildDbValidator, s as forceNavNonOptional, t as ValidationContext, u as isPlainObject } from "./validator-Drb2N-YL.mjs";
|
|
3
3
|
import { n as createDbValidatorPlugin, t as DbValidationContext } from "./db-validator-plugin-BWy60OvG.mjs";
|
|
4
4
|
export { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, DbError, type DbValidationContext, type TArrayPatch, type TDbCas, type TDbFieldOp, type TDbPatch, ValidationContext, ValidatorMode, buildDbValidator, buildPatchPartial, buildValidationContext, createDbValidatorPlugin, dbPlugin, forceNavNonOptional, getKeyProps, isDbFieldOp, isEmptyObject, isNavRelation, isPlainObject, reconcileCas, separateCas };
|
package/package.json
CHANGED
package/dist/agg-CvXDGnKi.mjs
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { r as DbError } from "./db-error-D5uilS_A.mjs";
|
|
2
|
-
import { isAggregateExpr as isAggregateExpr$1, isBucketExpr as isBucketExpr$1, resolveAlias } from "@uniqu/core";
|
|
3
|
-
//#region src/query/aggregate-fns.ts
|
|
4
|
-
/**
|
|
5
|
-
* The only `$fn` names a `$select` aggregate may use (since 0.1.135). Any
|
|
6
|
-
* other name is rejected with `INVALID_QUERY` (HTTP 400 through moost-db)
|
|
7
|
-
* before a query reaches an adapter, so no adapter ever renders an unchecked
|
|
8
|
-
* function name into SQL or a pipeline.
|
|
9
|
-
*/
|
|
10
|
-
const SUPPORTED_AGGREGATE_FNS = [
|
|
11
|
-
"sum",
|
|
12
|
-
"count",
|
|
13
|
-
"avg",
|
|
14
|
-
"min",
|
|
15
|
-
"max"
|
|
16
|
-
];
|
|
17
|
-
const SUPPORTED_LIST = `${SUPPORTED_AGGREGATE_FNS.slice(0, -1).join(", ")} or ${SUPPORTED_AGGREGATE_FNS.at(-1)}`;
|
|
18
|
-
/**
|
|
19
|
-
* Throws `DbError` `INVALID_QUERY` (uniqu's wording) unless `fn` is in
|
|
20
|
-
* `SUPPORTED_AGGREGATE_FNS` — for adapters to re-assert before rendering.
|
|
21
|
-
*/
|
|
22
|
-
function assertAggregateFn(fn, path = "$select") {
|
|
23
|
-
if (!SUPPORTED_AGGREGATE_FNS.includes(fn)) throw new DbError("INVALID_QUERY", [{
|
|
24
|
-
path,
|
|
25
|
-
message: `Unknown aggregate function "${String(fn)}" — use ${SUPPORTED_LIST}`
|
|
26
|
-
}]);
|
|
27
|
-
}
|
|
28
|
-
//#endregion
|
|
29
|
-
//#region src/agg.ts
|
|
30
|
-
/**
|
|
31
|
-
* Reads the `$search` / `$index` controls of an aggregate query.
|
|
32
|
-
*
|
|
33
|
-
* Search and grouping are orthogonal: `$search` narrows the ROWS, `$groupBy`
|
|
34
|
-
* shapes what is left. Every adapter must therefore apply the search predicate
|
|
35
|
-
* BEFORE grouping, so the groups — and `$count`, which counts them — only ever
|
|
36
|
-
* describe rows that matched. An adapter that silently ignored the term would
|
|
37
|
-
* report totals for rows the same search excludes from the leaf list.
|
|
38
|
-
*
|
|
39
|
-
* Two rules the leaf search path applies and the grouped path must NOT:
|
|
40
|
-
* - **No implicit relevance ordering.** Relevance is a property of a row, not
|
|
41
|
-
* of a group; after `$group` there is no per-row score left to sort on.
|
|
42
|
-
* Grouped results order by `$sort` or not at all.
|
|
43
|
-
* - **No implicit row cap.** The leaf runners cap an unbounded search at 1000
|
|
44
|
-
* rows; applying that before `$group` would silently truncate group counts.
|
|
45
|
-
*
|
|
46
|
-
* Returns `undefined` when there is nothing to search for, so callers fall
|
|
47
|
-
* straight through to the plain grouped path.
|
|
48
|
-
*/
|
|
49
|
-
function resolveAggregateSearch(controls) {
|
|
50
|
-
const raw = controls?.$search;
|
|
51
|
-
if (typeof raw !== "string") return;
|
|
52
|
-
const text = raw.trim();
|
|
53
|
-
if (!text) return;
|
|
54
|
-
const index = controls?.$index;
|
|
55
|
-
return {
|
|
56
|
-
text,
|
|
57
|
-
indexName: typeof index === "string" && index ? index : void 0
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
//#endregion
|
|
61
|
-
export { SUPPORTED_AGGREGATE_FNS as a, resolveAlias as i, isBucketExpr$1 as n, assertAggregateFn as o, resolveAggregateSearch as r, isAggregateExpr$1 as t };
|
package/dist/agg-EcIbAFnJ.cjs
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
const require_db_error = require("./db-error-DTkkeu5b.cjs");
|
|
2
|
-
require("@uniqu/core");
|
|
3
|
-
//#region src/query/aggregate-fns.ts
|
|
4
|
-
/**
|
|
5
|
-
* The only `$fn` names a `$select` aggregate may use (since 0.1.135). Any
|
|
6
|
-
* other name is rejected with `INVALID_QUERY` (HTTP 400 through moost-db)
|
|
7
|
-
* before a query reaches an adapter, so no adapter ever renders an unchecked
|
|
8
|
-
* function name into SQL or a pipeline.
|
|
9
|
-
*/
|
|
10
|
-
const SUPPORTED_AGGREGATE_FNS = [
|
|
11
|
-
"sum",
|
|
12
|
-
"count",
|
|
13
|
-
"avg",
|
|
14
|
-
"min",
|
|
15
|
-
"max"
|
|
16
|
-
];
|
|
17
|
-
const SUPPORTED_LIST = `${SUPPORTED_AGGREGATE_FNS.slice(0, -1).join(", ")} or ${SUPPORTED_AGGREGATE_FNS.at(-1)}`;
|
|
18
|
-
/**
|
|
19
|
-
* Throws `DbError` `INVALID_QUERY` (uniqu's wording) unless `fn` is in
|
|
20
|
-
* `SUPPORTED_AGGREGATE_FNS` — for adapters to re-assert before rendering.
|
|
21
|
-
*/
|
|
22
|
-
function assertAggregateFn(fn, path = "$select") {
|
|
23
|
-
if (!SUPPORTED_AGGREGATE_FNS.includes(fn)) throw new require_db_error.DbError("INVALID_QUERY", [{
|
|
24
|
-
path,
|
|
25
|
-
message: `Unknown aggregate function "${String(fn)}" — use ${SUPPORTED_LIST}`
|
|
26
|
-
}]);
|
|
27
|
-
}
|
|
28
|
-
//#endregion
|
|
29
|
-
//#region src/agg.ts
|
|
30
|
-
/**
|
|
31
|
-
* Reads the `$search` / `$index` controls of an aggregate query.
|
|
32
|
-
*
|
|
33
|
-
* Search and grouping are orthogonal: `$search` narrows the ROWS, `$groupBy`
|
|
34
|
-
* shapes what is left. Every adapter must therefore apply the search predicate
|
|
35
|
-
* BEFORE grouping, so the groups — and `$count`, which counts them — only ever
|
|
36
|
-
* describe rows that matched. An adapter that silently ignored the term would
|
|
37
|
-
* report totals for rows the same search excludes from the leaf list.
|
|
38
|
-
*
|
|
39
|
-
* Two rules the leaf search path applies and the grouped path must NOT:
|
|
40
|
-
* - **No implicit relevance ordering.** Relevance is a property of a row, not
|
|
41
|
-
* of a group; after `$group` there is no per-row score left to sort on.
|
|
42
|
-
* Grouped results order by `$sort` or not at all.
|
|
43
|
-
* - **No implicit row cap.** The leaf runners cap an unbounded search at 1000
|
|
44
|
-
* rows; applying that before `$group` would silently truncate group counts.
|
|
45
|
-
*
|
|
46
|
-
* Returns `undefined` when there is nothing to search for, so callers fall
|
|
47
|
-
* straight through to the plain grouped path.
|
|
48
|
-
*/
|
|
49
|
-
function resolveAggregateSearch(controls) {
|
|
50
|
-
const raw = controls?.$search;
|
|
51
|
-
if (typeof raw !== "string") return;
|
|
52
|
-
const text = raw.trim();
|
|
53
|
-
if (!text) return;
|
|
54
|
-
const index = controls?.$index;
|
|
55
|
-
return {
|
|
56
|
-
text,
|
|
57
|
-
indexName: typeof index === "string" && index ? index : void 0
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
//#endregion
|
|
61
|
-
Object.defineProperty(exports, "SUPPORTED_AGGREGATE_FNS", {
|
|
62
|
-
enumerable: true,
|
|
63
|
-
get: function() {
|
|
64
|
-
return SUPPORTED_AGGREGATE_FNS;
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
Object.defineProperty(exports, "assertAggregateFn", {
|
|
68
|
-
enumerable: true,
|
|
69
|
-
get: function() {
|
|
70
|
-
return assertAggregateFn;
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
Object.defineProperty(exports, "resolveAggregateSearch", {
|
|
74
|
-
enumerable: true,
|
|
75
|
-
get: function() {
|
|
76
|
-
return resolveAggregateSearch;
|
|
77
|
-
}
|
|
78
|
-
});
|