orchid-orm 1.78.4 → 1.78.5

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.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { Adapter, AfterCommitStandaloneHook, AfterHook, BrandColumnsShape, Column, ColumnSchemaConfig, ColumnsShape, ComputedOptionsConfig, ComputedOptionsFactory, CreateData, CreateManyMethodsNames, CreateMethodsNames, CreateSelf, DbSharedOptions, DbSqlMethod, DbTableOptionScopes, DefaultColumnTypes, DefaultSchemaConfig, DeleteMethodsNames, EmptyObject, FromArg, FromResult, Grant, IsQuery, IsolationLevel, MaybeArray, MergeQuery, PickQuerySelectableRelations, QueryAfterHook, QueryBeforeActionHook, QueryBeforeHook, QueryData, QueryHasWhere, QueryOrExpression, QueryReturnType, QuerySchema, QueryScopes, QueryTake, RawSqlBase, RecordUnknown, RelationConfigBase, Rls, RlsPolicy, SelectAsFnReturnType, SelectResultObj, SelectableFromShape, StorageOptions, TableData, TableDataFn, TableDataItem, TransactionOptions, UpdateData, WhereArg } from "pqb/internal";
2
- import { Db, Query } from "pqb";
1
+ import { Adapter, AfterCommitStandaloneHook, AfterHook, BrandColumnsShape, Column, ColumnSchemaConfig, ColumnsShape, ComputedOptionsConfig, ComputedOptionsFactory, CreateData, CreateManyMethodsNames, CreateMethodsNames, CreateSelf, DbSharedOptions, DbSqlMethod, DbTableOptionScopes, DefaultColumnTypes, DefaultSchemaConfig, DeleteMethodsNames, EmptyObject, FromArg, FromResult, Grant, IsQuery, IsolationLevel, MaybeArray, MergeQuery, PickQuerySelectableRelations, QueryAfterHook, QueryBeforeActionHook, QueryBeforeHook, QueryCatch, QueryData, QueryHasWhere, QueryOrExpression, QueryReturnType, QuerySchema, QueryScopes, QueryThen, QueryThenShallowSimplify, RawSqlBase, RecordUnknown, RelationConfigBase, Rls, RlsPolicy, SelectAsFnReturnType, SelectResultObj, SelectableFromShape, StorageOptions, TableData, TableDataFn, TableDataItem, TransactionOptions, UpdateData, WhereArg } from "pqb/internal";
2
+ import { Db, Expression, Query } from "pqb";
3
3
  export * from "pqb";
4
4
  interface BelongsToDataForCreate<Name extends string, FK extends string, Required, Q extends Query> {
5
5
  columns: FK;
@@ -943,10 +943,35 @@ declare function isInTransaction(this: {
943
943
  declare function afterCommit(this: {
944
944
  $qb: Db;
945
945
  }, hook: AfterCommitStandaloneHook): void;
946
+ type OrmSelectArg = Record<string, () => SelectAsFnReturnType>;
947
+ interface OrmSelectMethods {
948
+ /**
949
+ * Use `$select` to select independent query and expression results in a single SQL query.
950
+ * Each value is a callback returning a query or SQL expression. The helper returns a
951
+ * single-result query, so it can be awaited directly or further composed.
952
+ */
953
+ $select<Obj extends OrmSelectArg>(selection: Obj): {
954
+ then: QueryThenShallowSimplify<ColumnsShape.Output<SelectResultObj<Db, Obj>['result']>>;
955
+ catch: QueryCatch;
956
+ };
957
+ }
958
+ type RequiredPreparedParams<Params extends object> = { [K in keyof Params]-?: Exclude<Params[K], undefined>; };
959
+ type PreparedQueryParams<Params extends object> = { [K in keyof Params]: Expression<Column.Pick.QueryColumnOfType<Params[K]>>; };
960
+ type PreparedQueryResult<Q extends Query> = Q['then'] extends QueryThen<infer Result> ? Promise<Result> : never;
961
+ type PreparedQueryExecutor<Params extends object, Q extends Query> = keyof Params extends never ? () => PreparedQueryResult<Q> : (params: Params) => PreparedQueryResult<Q>;
962
+ interface OrmPrepareMethods {
963
+ /**
964
+ * Creates a query executor that builds SQL once on its first execution and
965
+ * reuses it as a driver prepared statement afterwards.
966
+ *
967
+ * Values closed over by `callback` are static: they are captured when the
968
+ * prepared query is defined. Use `Params` for values that change per call.
969
+ */
970
+ $prepare<Params extends object = {}, Q extends Query = Query>(callback: (params: PreparedQueryParams<Params>) => Q, ...invalidParams: Params extends RequiredPreparedParams<Params> ? [] : [message: 'Prepared statement parameters must be required and cannot include undefined']): PreparedQueryExecutor<Params, Q>;
971
+ }
946
972
  interface FromQuery extends Query {
947
973
  returnType: 'all';
948
974
  }
949
- type OrmSelectArg = Record<string, () => SelectAsFnReturnType>;
950
975
  interface OrchidORMQueryHelper<Q extends Query, Args extends unknown[], Result> {
951
976
  <T extends Query>(q: T & {
952
977
  table: Q['table'];
@@ -1031,7 +1056,7 @@ declare const defineRls: <T extends Rls.TableConfig>(rls: T) => T;
1031
1056
  * Identity helper for table-local grant configuration.
1032
1057
  */
1033
1058
  declare const setGrants: <const T extends readonly Grant.TableClassGrant[]>(grants: T) => T;
1034
- interface OrchidORMMethods {
1059
+ interface OrchidORMMethods extends OrmPrepareMethods, OrmSelectMethods {
1035
1060
  /**
1036
1061
  * @see import('pqb').QueryTransaction.prototype.transaction
1037
1062
  */
@@ -1116,21 +1141,6 @@ interface OrchidORMMethods {
1116
1141
  * @param args - SQL template literal, or an object { raw: string, values?: unknown[] }
1117
1142
  */
1118
1143
  $queryArrays: Db['queryArrays'];
1119
- /**
1120
- * Use `$select` to select independent query and expression results in a single SQL query.
1121
- * Each value is a callback returning a query or SQL expression. The helper returns a
1122
- * single-result query, so it can be awaited directly or further composed.
1123
- *
1124
- * ```ts
1125
- * const result = await db.$select({
1126
- * userCount: () => db.user.count(),
1127
- * one: () => sql<number>`1::int`,
1128
- * });
1129
- *
1130
- * // { userCount: number; one: number }
1131
- * ```
1132
- */
1133
- $select<Obj extends OrmSelectArg>(selection: Obj): QueryTake<SelectResultObj<Db, Obj>>;
1134
1144
  /**
1135
1145
  * See {@link FromMethods.from}
1136
1146
  */
package/dist/index.js CHANGED
@@ -2616,6 +2616,81 @@ function isInTransaction() {
2616
2616
  function afterCommit(hook) {
2617
2617
  this.$qb.afterCommit(hook);
2618
2618
  }
2619
+ const ormSelect = (qb) => (async (selection) => {
2620
+ const q = qb.select(selection);
2621
+ const [{ selectAs }] = q.q.select;
2622
+ const values = Object.entries(selectAs);
2623
+ if (values.every(([, val]) => val instanceof pqb_internal.ValExpression)) return Object.fromEntries(values.map(([key, expr]) => [key, expr.value]));
2624
+ const valExpressions = {};
2625
+ for (const [key, value] of values) if (value instanceof pqb_internal.ValExpression) {
2626
+ valExpressions[key] = value.value;
2627
+ delete selectAs[key];
2628
+ }
2629
+ const result = await q.take();
2630
+ return Object.assign(result, valExpressions);
2631
+ });
2632
+ var PreparedParamExpression = class extends pqb.Expression {
2633
+ constructor(key) {
2634
+ super();
2635
+ this.key = key;
2636
+ this.result = { value: pqb_internal.emptyObject };
2637
+ this.q = { expr: this };
2638
+ }
2639
+ makeSQL(ctx) {
2640
+ ctx.values.push(this);
2641
+ return `$${ctx.values.length}`;
2642
+ }
2643
+ };
2644
+ const prepareSqlItem = (sql, params) => {
2645
+ const values = sql.values?.map((value) => {
2646
+ return value instanceof PreparedParamExpression ? params[value.key] : value;
2647
+ });
2648
+ return {
2649
+ ...sql,
2650
+ values,
2651
+ prepare: true
2652
+ };
2653
+ };
2654
+ const prepareSql = (sql, params) => {
2655
+ if ("text" in sql) return prepareSqlItem(sql, params);
2656
+ return {
2657
+ ...sql,
2658
+ batch: sql.batch.map((item) => prepareSqlItem(item, params))
2659
+ };
2660
+ };
2661
+ const createPreparedParams = () => {
2662
+ return new Proxy(Object.create(null), { get(expressions, key) {
2663
+ if (typeof key !== "string") return;
2664
+ let expression = expressions[key];
2665
+ if (!expression) {
2666
+ expression = new PreparedParamExpression(key);
2667
+ expressions[key] = expression;
2668
+ }
2669
+ return expression;
2670
+ } });
2671
+ };
2672
+ const ormPrepare = () => {
2673
+ return ((callback) => {
2674
+ const query = callback(createPreparedParams());
2675
+ const emptyParams = {};
2676
+ let prepared;
2677
+ return (params) => {
2678
+ let cached;
2679
+ if (prepared) cached = prepared;
2680
+ else {
2681
+ const sql = query.toSQL(true);
2682
+ cached = {
2683
+ query: Object.create(query),
2684
+ sql
2685
+ };
2686
+ prepared = cached;
2687
+ }
2688
+ const executionQuery = Object.create(cached.query);
2689
+ Object.defineProperty(executionQuery, "toSQL", { value: () => prepareSql(cached.sql, params ?? emptyParams) });
2690
+ return executionQuery.then();
2691
+ };
2692
+ });
2693
+ };
2619
2694
  /**
2620
2695
  * Identity helper for table row-level security configuration.
2621
2696
  */
@@ -2765,7 +2840,8 @@ const privateOrchidORMWithAdapter = ({ log, logger, autoPreparedStatements, noPr
2765
2840
  $withRecursive: qb.withRecursive.bind(qb),
2766
2841
  $withSql: qb.withSql.bind(qb),
2767
2842
  $from: qb.from.bind(qb),
2768
- $select: ((selection) => qb.select(selection).take()),
2843
+ $select: ormSelect(qb),
2844
+ $prepare: ormPrepare(),
2769
2845
  $close: adapter.close.bind(adapter),
2770
2846
  $withOptions: qb.withOptions.bind(qb)
2771
2847
  };