@danceroutine/tango-orm 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3,9 +3,9 @@ import { PostgresAdapter } from "./PostgresAdapter-CMiEpHya.js";
3
3
  import "./SqliteClient-CjOK9-ki.js";
4
4
  import { SqliteAdapter } from "./SqliteAdapter-CeqhyrPC.js";
5
5
  import { AdapterRegistry, connectDB, connection_exports, getDefaultAdapterRegistry } from "./connection-B_K2ZAf7.js";
6
- import { QBuilder, QueryCompiler, QueryResult, QuerySet, query_exports } from "./query-C6So1r6H.js";
6
+ import { QBuilder, QueryCompiler, QueryResult, QuerySet, query_exports } from "./query-FZJoSCg4.js";
7
7
  import { TangoRuntime, getTangoRuntime, initializeTangoRuntime, resetTangoRuntime } from "./defaultRuntime-BPK9kWEW.js";
8
- import { ModelManager } from "./registerModelObjects-BKMpfc4Z.js";
8
+ import { ModelManager } from "./registerModelObjects-C-1RbUHS.js";
9
9
  import { manager_exports } from "./manager-C6oJ2tAF.js";
10
10
  import { runtime_exports } from "./runtime-ByXbpVBS.js";
11
11
  import { UnitOfWork, atomic, transaction_exports } from "./transaction-Cs0Z9tbW.js";
@@ -1,3 +1,5 @@
1
+ import type { QNode } from '../query/domain/QNode';
2
+ import type { FilterInput } from '../query/domain/FilterInput';
1
3
  import type { QuerySet } from '../query/index';
2
4
  import type { TableMeta } from '../query/domain/index';
3
5
  /**
@@ -6,6 +8,23 @@ import type { TableMeta } from '../query/domain/index';
6
8
  export interface ManagerLike<TModelRow extends Record<string, unknown>, TSourceModel = unknown> {
7
9
  readonly meta: TableMeta;
8
10
  query(): QuerySet<TModelRow, TModelRow, TSourceModel>;
11
+ all(): QuerySet<TModelRow, TModelRow, TSourceModel>;
12
+ getOrCreate(args: {
13
+ where: FilterInput<TModelRow> | QNode<TModelRow>;
14
+ defaults?: Partial<TModelRow>;
15
+ }): Promise<{
16
+ record: TModelRow;
17
+ created: boolean;
18
+ }>;
19
+ updateOrCreate(args: {
20
+ where: FilterInput<TModelRow> | QNode<TModelRow>;
21
+ defaults?: Partial<TModelRow>;
22
+ update?: Partial<TModelRow>;
23
+ }): Promise<{
24
+ record: TModelRow;
25
+ created: boolean;
26
+ updated: boolean;
27
+ }>;
9
28
  findById(id: TModelRow[keyof TModelRow]): Promise<TModelRow | null>;
10
29
  getOrThrow(id: TModelRow[keyof TModelRow]): Promise<TModelRow>;
11
30
  create(input: Partial<TModelRow>): Promise<TModelRow>;
@@ -1,6 +1,7 @@
1
+ import type { QNode } from '../query/domain/QNode';
1
2
  import type { ModelWriteHooks } from '@danceroutine/tango-schema';
2
3
  import type { Model as SchemaModel } from '@danceroutine/tango-schema/domain';
3
- import type { TableMeta } from '../query/domain/index';
4
+ import type { FilterInput, TableMeta } from '../query/domain/index';
4
5
  import type { QuerySet } from '../query/index';
5
6
  import type { TangoRuntime } from '../runtime/TangoRuntime';
6
7
  import type { ManagerLike } from './ManagerLike';
@@ -39,7 +40,29 @@ export declare class ModelManager<TModelRow extends Record<string, unknown>, TSo
39
40
  */
40
41
  static isModelManager<TModelRow extends Record<string, unknown>>(value: unknown): value is ModelManager<TModelRow>;
41
42
  private static createTableMeta;
43
+ private static mergeCreatePayloadFromWhere;
44
+ private static countNonPkValues;
45
+ private static collectPlainFieldsFromQNode;
46
+ private static omitLookupKeysFromAtom;
47
+ private static mergeCompatiblePartials;
42
48
  query(): QuerySet<TModelRow, TModelRow, TSourceModel>;
49
+ all(): QuerySet<TModelRow, TModelRow, TSourceModel>;
50
+ getOrCreate(args: {
51
+ where: FilterInput<TModelRow> | QNode<TModelRow>;
52
+ defaults?: Partial<TModelRow>;
53
+ }): Promise<{
54
+ record: TModelRow;
55
+ created: boolean;
56
+ }>;
57
+ updateOrCreate(args: {
58
+ where: FilterInput<TModelRow> | QNode<TModelRow>;
59
+ defaults?: Partial<TModelRow>;
60
+ update?: Partial<TModelRow>;
61
+ }): Promise<{
62
+ record: TModelRow;
63
+ created: boolean;
64
+ updated: boolean;
65
+ }>;
43
66
  findById(id: TModelRow[keyof TModelRow]): Promise<TModelRow | null>;
44
67
  getOrThrow(id: TModelRow[keyof TModelRow]): Promise<TModelRow>;
45
68
  create(input: Partial<TModelRow>): Promise<TModelRow>;
@@ -1,8 +1,8 @@
1
1
  import "../PostgresClient-BQJZfEOT.js";
2
2
  import "../SqliteClient-CjOK9-ki.js";
3
- import "../query-C6So1r6H.js";
3
+ import "../query-FZJoSCg4.js";
4
4
  import "../defaultRuntime-BPK9kWEW.js";
5
- import { ModelManager, registerModelObjects } from "../registerModelObjects-BKMpfc4Z.js";
5
+ import { ModelManager, registerModelObjects } from "../registerModelObjects-C-1RbUHS.js";
6
6
  import "../manager-C6oJ2tAF.js";
7
7
 
8
8
  export { ModelManager, registerModelObjects };
@@ -1,5 +1,5 @@
1
1
  import { __export } from "./chunk-DLY2FNSh.js";
2
- import { ModelManager, registerModelObjects } from "./registerModelObjects-BKMpfc4Z.js";
2
+ import { ModelManager, registerModelObjects } from "./registerModelObjects-C-1RbUHS.js";
3
3
 
4
4
  //#region src/manager/index.ts
5
5
  var manager_exports = {};
@@ -68,6 +68,7 @@ export declare class QuerySet<TModel extends Record<string, unknown>, TBaseResul
68
68
  * Narrow an unknown value to `QuerySet`.
69
69
  */
70
70
  static isQuerySet<TModel extends Record<string, unknown>, TResult extends Record<string, unknown> = TModel>(value: unknown): value is QuerySet<TModel, TResult>;
71
+ private static invertOrderSpec;
71
72
  /**
72
73
  * Add a filter expression to the query.
73
74
  *
@@ -121,6 +122,7 @@ export declare class QuerySet<TModel extends Record<string, unknown>, TBaseResul
121
122
  * app-local registry current.
122
123
  */
123
124
  prefetchRelated<TTargetModel = undefined, const TRelationName extends RelationKeys<PrefetchRelatedRelations<TSourceModel, NoInfer<TTargetModel>>> | GeneratedPrefetchRelatedPathKeys<TSourceModel> = RelationKeys<PrefetchRelatedRelations<TSourceModel, NoInfer<TTargetModel>>> | GeneratedPrefetchRelatedPathKeys<TSourceModel>>(...rels: readonly TRelationName[]): QuerySet<TModel, TBaseResult, TSourceModel, THydrated & MaybeHydratedRelationMap<TSourceModel, PrefetchRelatedRelations<TSourceModel, NoInfer<TTargetModel>>, Extract<TRelationName, RelationKeys<PrefetchRelatedRelations<TSourceModel, NoInfer<TTargetModel>>>>, ManyRelationHydrationCardinality> & GeneratedHydratedRelationMap<TSourceModel, Extract<TRelationName, GeneratedPrefetchRelatedPathKeys<TSourceModel>>>>;
125
+ all(): QuerySet<TModel, TBaseResult, TSourceModel, THydrated>;
124
126
  /**
125
127
  * Execute the query and optionally shape each row.
126
128
  *
@@ -149,6 +151,15 @@ export declare class QuerySet<TModel extends Record<string, unknown>, TBaseResul
149
151
  fetchOne<Out>(shape: QueryShapeFunction<HydratedQueryResult<TBaseResult, THydrated>, Out>): Promise<Out | null>;
150
152
  fetchOne<Out>(shape: QueryShapeParser<HydratedQueryResult<TBaseResult, THydrated>, Out>): Promise<Out | null>;
151
153
  fetchOne<TShape extends QueryShape<HydratedQueryResult<TBaseResult, THydrated>> | undefined>(shape: TShape): Promise<HydratedQueryResult<TBaseResult, THydrated> | QueryShapeOutput<HydratedQueryResult<TBaseResult, THydrated>, NonNullable<TShape>> | null>;
154
+ first(): Promise<HydratedQueryResult<TBaseResult, THydrated> | null>;
155
+ first<Out>(shape: QueryShapeFunction<HydratedQueryResult<TBaseResult, THydrated>, Out>): Promise<Out | null>;
156
+ first<Out>(shape: QueryShapeParser<HydratedQueryResult<TBaseResult, THydrated>, Out>): Promise<Out | null>;
157
+ last(): Promise<HydratedQueryResult<TBaseResult, THydrated> | null>;
158
+ last<Out>(shape: QueryShapeFunction<HydratedQueryResult<TBaseResult, THydrated>, Out>): Promise<Out | null>;
159
+ last<Out>(shape: QueryShapeParser<HydratedQueryResult<TBaseResult, THydrated>, Out>): Promise<Out | null>;
160
+ get(q: FilterInput<TModel> | QNode<TModel>): Promise<HydratedQueryResult<TBaseResult, THydrated>>;
161
+ get<Out>(q: FilterInput<TModel> | QNode<TModel>, shape: QueryShapeFunction<HydratedQueryResult<TBaseResult, THydrated>, Out>): Promise<Out>;
162
+ get<Out>(q: FilterInput<TModel> | QNode<TModel>, shape: QueryShapeParser<HydratedQueryResult<TBaseResult, THydrated>, Out>): Promise<Out>;
152
163
  /**
153
164
  * Execute a `COUNT(*)` query for the current filtered state.
154
165
  */
@@ -157,6 +168,7 @@ export declare class QuerySet<TModel extends Record<string, unknown>, TBaseResul
157
168
  * Return whether at least one row matches the current query state.
158
169
  */
159
170
  exists(): Promise<boolean>;
171
+ private shapeFetchedRow;
160
172
  private getOrCreateEvaluationCache;
161
173
  private evaluateRows;
162
174
  private normalizeHydratedRowsForParserShape;
@@ -1,3 +1,3 @@
1
- import { QBuilder, QueryCompiler, QueryResult, QuerySet, compiler_exports, domain_exports } from "../query-C6So1r6H.js";
1
+ import { QBuilder, QueryCompiler, QueryResult, QuerySet, compiler_exports, domain_exports } from "../query-FZJoSCg4.js";
2
2
 
3
3
  export { QBuilder as Q, QBuilder, QueryCompiler, QueryResult, QuerySet, compiler_exports as compiler, domain_exports as domain };
@@ -0,0 +1,3 @@
1
+ import type { FilterInput } from '../domain/FilterInput';
2
+ import type { QNode } from '../domain/QNode';
3
+ export declare function isQNodeLike<T>(value: FilterInput<T> | QNode<T>): value is QNode<T>;
@@ -1,7 +1,16 @@
1
1
  import { __export } from "./chunk-DLY2FNSh.js";
2
- import { SqlSafetyEngine, getLogger, isError } from "@danceroutine/tango-core";
2
+ import { MultipleObjectsReturned, NotFoundError, SqlSafetyEngine, getLogger, isError } from "@danceroutine/tango-core";
3
3
  import { ModelRegistry } from "@danceroutine/tango-schema";
4
4
 
5
+ //#region src/query/domain/internal/InternalQNodeType.ts
6
+ const InternalQNodeType = {
7
+ ATOM: "atom",
8
+ AND: "and",
9
+ OR: "or",
10
+ NOT: "not"
11
+ };
12
+
13
+ //#endregion
5
14
  //#region src/query/domain/internal/InternalRelationKind.ts
6
15
  const InternalRelationKind = {
7
16
  HAS_MANY: "hasMany",
@@ -83,15 +92,6 @@ const InternalDialect = {
83
92
  SQLITE: "sqlite"
84
93
  };
85
94
 
86
- //#endregion
87
- //#region src/query/domain/internal/InternalQNodeType.ts
88
- const InternalQNodeType = {
89
- ATOM: "atom",
90
- AND: "and",
91
- OR: "or",
92
- NOT: "not"
93
- };
94
-
95
95
  //#endregion
96
96
  //#region src/query/domain/internal/InternalLookupType.ts
97
97
  const InternalLookupType = {
@@ -821,6 +821,19 @@ const InternalDirection = {
821
821
  DESC: "desc"
822
822
  };
823
823
 
824
+ //#endregion
825
+ //#region src/query/internal/isQNodeLike.ts
826
+ function isQNodeLike(value) {
827
+ if (typeof value !== "object" || value === null || "__tangoBrand" in value) return false;
828
+ switch (value.kind) {
829
+ case InternalQNodeType.ATOM: return "where" in value;
830
+ case InternalQNodeType.AND:
831
+ case InternalQNodeType.OR: return Array.isArray(value.nodes);
832
+ case InternalQNodeType.NOT: return "node" in value;
833
+ default: return false;
834
+ }
835
+ }
836
+
824
837
  //#endregion
825
838
  //#region src/query/QBuilder.ts
826
839
  var QBuilder = class QBuilder {
@@ -860,7 +873,7 @@ var QBuilder = class QBuilder {
860
873
  };
861
874
  }
862
875
  static wrapNode(input) {
863
- if (input.kind) return input;
876
+ if (isQNodeLike(input)) return input;
864
877
  return {
865
878
  kind: InternalQNodeType.ATOM,
866
879
  where: input
@@ -884,13 +897,20 @@ var QuerySet = class QuerySet {
884
897
  static isQuerySet(value) {
885
898
  return typeof value === "object" && value !== null && value.__tangoBrand === QuerySet.BRAND;
886
899
  }
900
+ static invertOrderSpec(order) {
901
+ if (!order?.length) return [];
902
+ return order.map((spec) => ({
903
+ by: spec.by,
904
+ dir: spec.dir === InternalDirection.ASC ? InternalDirection.DESC : InternalDirection.ASC
905
+ }));
906
+ }
887
907
  /**
888
908
  * Add a filter expression to the query.
889
909
  *
890
910
  * Multiple `filter()` calls are composed with `AND`.
891
911
  */
892
912
  filter(q) {
893
- const wrapped = q.kind ? q : {
913
+ const wrapped = isQNodeLike(q) ? q : {
894
914
  kind: InternalQNodeType.ATOM,
895
915
  where: q
896
916
  };
@@ -906,7 +926,7 @@ var QuerySet = class QuerySet {
906
926
  * Exclusions are translated to `NOT (...)` predicates.
907
927
  */
908
928
  exclude(q) {
909
- const wrapped = q.kind ? q : {
929
+ const wrapped = isQNodeLike(q) ? q : {
910
930
  kind: InternalQNodeType.ATOM,
911
931
  where: q
912
932
  };
@@ -989,6 +1009,9 @@ var QuerySet = class QuerySet {
989
1009
  prefetchRelated: [...rels]
990
1010
  });
991
1011
  }
1012
+ all() {
1013
+ return new QuerySet(this.executor, { ...this.state });
1014
+ }
992
1015
  async fetch(shape) {
993
1016
  const baseResult = await this.getOrCreateEvaluationCache();
994
1017
  if (!shape) return baseResult;
@@ -1012,6 +1035,36 @@ var QuerySet = class QuerySet {
1012
1035
  for (const row of result) return row;
1013
1036
  return null;
1014
1037
  }
1038
+ async first(shape) {
1039
+ return this.fetchOne(shape);
1040
+ }
1041
+ async last(shape) {
1042
+ if (this.state.limit !== undefined || this.state.offset !== undefined) {
1043
+ const page = await this.fetch();
1044
+ const row = page.at(-1);
1045
+ if (!row) return null;
1046
+ return this.shapeFetchedRow(row, shape);
1047
+ }
1048
+ const invertedOrder = QuerySet.invertOrderSpec(this.state.order);
1049
+ const effectiveOrder = invertedOrder.length > 0 ? invertedOrder : [{
1050
+ by: this.executor.meta.pk,
1051
+ dir: InternalDirection.DESC
1052
+ }];
1053
+ const qs = new QuerySet(this.executor, {
1054
+ ...this.state,
1055
+ order: effectiveOrder
1056
+ });
1057
+ return qs.limit(1).fetchOne(shape);
1058
+ }
1059
+ async get(q, shape) {
1060
+ const limited = this.filter(q).limit(2);
1061
+ const page = await limited.fetch();
1062
+ const rows = page.items;
1063
+ const count = rows.length;
1064
+ if (count === 0) throw new NotFoundError(`${this.executor.meta.table}: no matching record`);
1065
+ if (count > 1) throw new MultipleObjectsReturned(`${this.executor.meta.table}: more than one matching record`);
1066
+ return this.shapeFetchedRow(rows[0], shape);
1067
+ }
1015
1068
  /**
1016
1069
  * Execute a `COUNT(*)` query for the current filtered state.
1017
1070
  */
@@ -1029,6 +1082,12 @@ var QuerySet = class QuerySet {
1029
1082
  const count = await this.count();
1030
1083
  return count > 0;
1031
1084
  }
1085
+ shapeFetchedRow(row, shape) {
1086
+ if (!shape) return row;
1087
+ if (typeof shape === "function") return shape(row);
1088
+ const normalizedRow = this.normalizeHydratedRowsForParserShape([row])[0] ?? row;
1089
+ return shape.parse(normalizedRow);
1090
+ }
1032
1091
  getOrCreateEvaluationCache() {
1033
1092
  if (!this.evaluationCache) this.evaluationCache = this.evaluateRows().catch((error) => {
1034
1093
  this.evaluationCache = undefined;
@@ -1194,5 +1253,5 @@ __export(query_exports, {
1194
1253
  });
1195
1254
 
1196
1255
  //#endregion
1197
- export { OrmSqlSafetyAdapter, QBuilder, QueryCompiler, QueryResult, QuerySet, TableMetaFactory, compiler_exports, domain_exports, query_exports };
1198
- //# sourceMappingURL=query-C6So1r6H.js.map
1256
+ export { InternalQNodeType, OrmSqlSafetyAdapter, QBuilder, QueryCompiler, QueryResult, QuerySet, TableMetaFactory, compiler_exports, domain_exports, isQNodeLike, query_exports };
1257
+ //# sourceMappingURL=query-FZJoSCg4.js.map