workers-qb 1.10.2 → 1.11.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/README.md CHANGED
@@ -54,6 +54,7 @@ workers-qb is a lightweight query builder designed specifically for Cloudflare W
54
54
  - CRUD operations (insert/update/select/delete)
55
55
  - Bulk inserts
56
56
  - JOIN queries
57
+ - Subqueries
57
58
  - Modular SELECT queries
58
59
  - ON CONFLICT handling
59
60
  - UPSERT support
package/dist/index.d.mts CHANGED
@@ -40,6 +40,42 @@ declare class QueryWithExtra<GenericResultWrapper, Result = any, IsAsync extends
40
40
  }
41
41
  declare function trimQuery(query: string): string;
42
42
 
43
+ interface SelectExecuteOptions {
44
+ lazy?: boolean;
45
+ }
46
+ declare class SelectBuilder<GenericResultWrapper, GenericResult = DefaultReturnObject, IsAsync extends boolean = true> {
47
+ _debugger: boolean;
48
+ _options: Partial<SelectAll>;
49
+ _fetchAll: (params: SelectAll) => QueryWithExtra<GenericResultWrapper, any, IsAsync>;
50
+ _fetchOne: (params: SelectOne) => QueryWithExtra<GenericResultWrapper, any, IsAsync>;
51
+ constructor(options: Partial<SelectAll>, fetchAll: (params: SelectAll) => QueryWithExtra<GenericResultWrapper, any, IsAsync>, fetchOne: (params: SelectOne) => QueryWithExtra<GenericResultWrapper, any, IsAsync>);
52
+ setDebugger(state: boolean): void;
53
+ tableName(tableName: SelectAll['tableName']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
54
+ fields(fields: SelectAll['fields']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
55
+ where(conditions: string | Array<string>, params?: Primitive | Primitive[]): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
56
+ whereIn<T extends string | Array<string>, P extends T extends Array<string> ? Primitive[][] : Primitive[]>(fields: T, values: P): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
57
+ join(join: SelectAll['join']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
58
+ groupBy(groupBy: SelectAll['groupBy']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
59
+ having(conditions: string | Array<string>, params?: Primitive | Primitive[]): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
60
+ orderBy(orderBy: SelectAll['orderBy']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
61
+ offset(offset: SelectAll['offset']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
62
+ limit(limit: SelectAll['limit']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
63
+ _parseArray(fieldName: string, option: any, value: any): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
64
+ getQueryAll<P extends SelectExecuteOptions = SelectExecuteOptions>(options?: P): Query<ArrayResult<GenericResultWrapper, GenericResult, IsAsync, P extends {
65
+ lazy: true;
66
+ } ? true : false>, IsAsync>;
67
+ getQueryOne(): Query<OneResult<GenericResultWrapper, GenericResult>, IsAsync>;
68
+ execute<P extends SelectExecuteOptions = SelectExecuteOptions>(options?: P): ArrayResult<GenericResultWrapper, GenericResult, IsAsync, P extends {
69
+ lazy: true;
70
+ } ? true : false>;
71
+ all<P extends SelectExecuteOptions = SelectExecuteOptions>(options?: P): ArrayResult<GenericResultWrapper, GenericResult, IsAsync, P extends {
72
+ lazy: true;
73
+ } ? true : false>;
74
+ one(): MaybeAsync<IsAsync, OneResult<GenericResultWrapper, GenericResult>>;
75
+ count(): MaybeAsync<IsAsync, CountResult<GenericResultWrapper>>;
76
+ getOptions(): SelectAll;
77
+ }
78
+
43
79
  type OmitIndexSignature<ObjectType> = {
44
80
  [KeyType in keyof ObjectType as {} extends Record<KeyType, unknown> ? never : KeyType]: ObjectType[KeyType];
45
81
  };
@@ -54,7 +90,7 @@ type SimpleMerge<Destination, Source> = {
54
90
  } & Source;
55
91
  type Merge<Destination, Source> = Simplify<SimpleMerge<PickIndexSignature<Destination>, PickIndexSignature<Source>> & SimpleMerge<OmitIndexSignature<Destination>, OmitIndexSignature<Source>>>;
56
92
 
57
- type Primitive = null | string | number | boolean | bigint | Raw;
93
+ type Primitive = null | string | number | boolean | bigint | Raw | SelectAll | SelectBuilder<any, any, any>;
58
94
  type QueryLoggerMeta = {
59
95
  duration?: number;
60
96
  };
@@ -69,7 +105,7 @@ type Where = {
69
105
  } | string | Array<string>;
70
106
  type Join = {
71
107
  type?: string | JoinTypes;
72
- table: string | SelectAll;
108
+ table: string | SelectAll | SelectBuilder<any, any, any>;
73
109
  on: string;
74
110
  alias?: string;
75
111
  };
@@ -79,9 +115,11 @@ type SelectOne = {
79
115
  where?: Where;
80
116
  join?: Join | Array<Join>;
81
117
  groupBy?: string | Array<string>;
82
- having?: string | Array<string>;
118
+ having?: Where;
83
119
  orderBy?: string | Array<string> | Record<string, string | OrderTypes>;
84
120
  offset?: number;
121
+ subQueryPlaceholders?: Record<string, SelectAll>;
122
+ subQueryTokenNextId?: number;
85
123
  };
86
124
  type RawQuery = {
87
125
  query: string;
@@ -191,41 +229,6 @@ declare function defaultLogger(query: RawQuery, meta: QueryLoggerMeta): any;
191
229
  declare function asyncLoggerWrapper<Async extends boolean = true>(query: Query<any, Async> | Query<any, Async>[], loggerFunction: CallableFunction | undefined, innerFunction: () => any): Promise<any>;
192
230
  declare function syncLoggerWrapper<Async extends boolean = false>(query: Query<any, Async> | Query<any, Async>[], loggerFunction: CallableFunction | undefined, innerFunction: () => any): any;
193
231
 
194
- interface SelectExecuteOptions {
195
- lazy?: boolean;
196
- }
197
- declare class SelectBuilder<GenericResultWrapper, GenericResult = DefaultReturnObject, IsAsync extends boolean = true> {
198
- _debugger: boolean;
199
- _options: Partial<SelectAll>;
200
- _fetchAll: (params: SelectAll) => QueryWithExtra<GenericResultWrapper, any, IsAsync>;
201
- _fetchOne: (params: SelectOne) => QueryWithExtra<GenericResultWrapper, any, IsAsync>;
202
- constructor(options: Partial<SelectAll>, fetchAll: (params: SelectAll) => QueryWithExtra<GenericResultWrapper, any, IsAsync>, fetchOne: (params: SelectOne) => QueryWithExtra<GenericResultWrapper, any, IsAsync>);
203
- setDebugger(state: boolean): void;
204
- tableName(tableName: SelectAll['tableName']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
205
- fields(fields: SelectAll['fields']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
206
- where(conditions: string | Array<string>, params?: Primitive | Primitive[]): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
207
- whereIn<T extends string | Array<string>, P extends T extends Array<string> ? Primitive[][] : Primitive[]>(fields: T, values: P): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
208
- join(join: SelectAll['join']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
209
- groupBy(groupBy: SelectAll['groupBy']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
210
- having(having: SelectAll['having']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
211
- orderBy(orderBy: SelectAll['orderBy']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
212
- offset(offset: SelectAll['offset']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
213
- limit(limit: SelectAll['limit']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
214
- _parseArray(fieldName: string, option: any, value: any): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
215
- getQueryAll<P extends SelectExecuteOptions = SelectExecuteOptions>(options?: P): Query<ArrayResult<GenericResultWrapper, GenericResult, IsAsync, P extends {
216
- lazy: true;
217
- } ? true : false>, IsAsync>;
218
- getQueryOne(): Query<OneResult<GenericResultWrapper, GenericResult>, IsAsync>;
219
- execute<P extends SelectExecuteOptions = SelectExecuteOptions>(options?: P): ArrayResult<GenericResultWrapper, GenericResult, IsAsync, P extends {
220
- lazy: true;
221
- } ? true : false>;
222
- all<P extends SelectExecuteOptions = SelectExecuteOptions>(options?: P): ArrayResult<GenericResultWrapper, GenericResult, IsAsync, P extends {
223
- lazy: true;
224
- } ? true : false>;
225
- one(): MaybeAsync<IsAsync, OneResult<GenericResultWrapper, GenericResult>>;
226
- count(): MaybeAsync<IsAsync, CountResult<GenericResultWrapper>>;
227
- }
228
-
229
232
  declare class QueryBuilder<GenericResultWrapper, IsAsync extends boolean = true> {
230
233
  protected options: QueryBuilderOptions<IsAsync>;
231
234
  loggerWrapper: typeof asyncLoggerWrapper;
@@ -263,12 +266,25 @@ declare class QueryBuilder<GenericResultWrapper, IsAsync extends boolean = true>
263
266
  protected _insert(params: Insert): string;
264
267
  protected _update(params: Update): string;
265
268
  protected _delete(params: Delete): string;
266
- protected _select(params: SelectAll): string;
269
+ protected _select(params: SelectAll, queryArgs?: any[]): string;
267
270
  protected _fields(value?: string | Array<string>): string;
268
- protected _where(value?: Where): string;
269
- protected _join(value?: Join | Array<Join>): string;
271
+ protected _where(value: Where | undefined, context?: {
272
+ subQueryPlaceholders?: Record<string, SelectAll>;
273
+ queryArgs: any[];
274
+ toSQLCompiler?: (params: SelectAll, queryArgs: any[]) => string;
275
+ }): string;
276
+ protected _join(value: Join | Array<Join> | undefined, context: {
277
+ subQueryPlaceholders?: Record<string, SelectAll>;
278
+ queryArgs: any[];
279
+ toSQLCompiler: (params: SelectAll, queryArgs: any[]) => string;
280
+ }): string;
270
281
  protected _groupBy(value?: string | Array<string>): string;
271
- protected _having(value?: string | Array<string>): string;
282
+ protected _having(value: Where | undefined, // Using Where type as Having structure is similar for conditions/params
283
+ context: {
284
+ subQueryPlaceholders?: Record<string, SelectAll>;
285
+ queryArgs: any[];
286
+ toSQLCompiler?: (params: SelectAll, queryArgs: any[]) => string;
287
+ }): string;
272
288
  protected _orderBy(value?: string | Array<string> | Record<string, string | OrderTypes>): string;
273
289
  protected _limit(value?: number): string;
274
290
  protected _offset(value?: number): string;
package/dist/index.d.ts CHANGED
@@ -40,6 +40,42 @@ declare class QueryWithExtra<GenericResultWrapper, Result = any, IsAsync extends
40
40
  }
41
41
  declare function trimQuery(query: string): string;
42
42
 
43
+ interface SelectExecuteOptions {
44
+ lazy?: boolean;
45
+ }
46
+ declare class SelectBuilder<GenericResultWrapper, GenericResult = DefaultReturnObject, IsAsync extends boolean = true> {
47
+ _debugger: boolean;
48
+ _options: Partial<SelectAll>;
49
+ _fetchAll: (params: SelectAll) => QueryWithExtra<GenericResultWrapper, any, IsAsync>;
50
+ _fetchOne: (params: SelectOne) => QueryWithExtra<GenericResultWrapper, any, IsAsync>;
51
+ constructor(options: Partial<SelectAll>, fetchAll: (params: SelectAll) => QueryWithExtra<GenericResultWrapper, any, IsAsync>, fetchOne: (params: SelectOne) => QueryWithExtra<GenericResultWrapper, any, IsAsync>);
52
+ setDebugger(state: boolean): void;
53
+ tableName(tableName: SelectAll['tableName']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
54
+ fields(fields: SelectAll['fields']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
55
+ where(conditions: string | Array<string>, params?: Primitive | Primitive[]): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
56
+ whereIn<T extends string | Array<string>, P extends T extends Array<string> ? Primitive[][] : Primitive[]>(fields: T, values: P): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
57
+ join(join: SelectAll['join']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
58
+ groupBy(groupBy: SelectAll['groupBy']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
59
+ having(conditions: string | Array<string>, params?: Primitive | Primitive[]): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
60
+ orderBy(orderBy: SelectAll['orderBy']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
61
+ offset(offset: SelectAll['offset']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
62
+ limit(limit: SelectAll['limit']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
63
+ _parseArray(fieldName: string, option: any, value: any): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
64
+ getQueryAll<P extends SelectExecuteOptions = SelectExecuteOptions>(options?: P): Query<ArrayResult<GenericResultWrapper, GenericResult, IsAsync, P extends {
65
+ lazy: true;
66
+ } ? true : false>, IsAsync>;
67
+ getQueryOne(): Query<OneResult<GenericResultWrapper, GenericResult>, IsAsync>;
68
+ execute<P extends SelectExecuteOptions = SelectExecuteOptions>(options?: P): ArrayResult<GenericResultWrapper, GenericResult, IsAsync, P extends {
69
+ lazy: true;
70
+ } ? true : false>;
71
+ all<P extends SelectExecuteOptions = SelectExecuteOptions>(options?: P): ArrayResult<GenericResultWrapper, GenericResult, IsAsync, P extends {
72
+ lazy: true;
73
+ } ? true : false>;
74
+ one(): MaybeAsync<IsAsync, OneResult<GenericResultWrapper, GenericResult>>;
75
+ count(): MaybeAsync<IsAsync, CountResult<GenericResultWrapper>>;
76
+ getOptions(): SelectAll;
77
+ }
78
+
43
79
  type OmitIndexSignature<ObjectType> = {
44
80
  [KeyType in keyof ObjectType as {} extends Record<KeyType, unknown> ? never : KeyType]: ObjectType[KeyType];
45
81
  };
@@ -54,7 +90,7 @@ type SimpleMerge<Destination, Source> = {
54
90
  } & Source;
55
91
  type Merge<Destination, Source> = Simplify<SimpleMerge<PickIndexSignature<Destination>, PickIndexSignature<Source>> & SimpleMerge<OmitIndexSignature<Destination>, OmitIndexSignature<Source>>>;
56
92
 
57
- type Primitive = null | string | number | boolean | bigint | Raw;
93
+ type Primitive = null | string | number | boolean | bigint | Raw | SelectAll | SelectBuilder<any, any, any>;
58
94
  type QueryLoggerMeta = {
59
95
  duration?: number;
60
96
  };
@@ -69,7 +105,7 @@ type Where = {
69
105
  } | string | Array<string>;
70
106
  type Join = {
71
107
  type?: string | JoinTypes;
72
- table: string | SelectAll;
108
+ table: string | SelectAll | SelectBuilder<any, any, any>;
73
109
  on: string;
74
110
  alias?: string;
75
111
  };
@@ -79,9 +115,11 @@ type SelectOne = {
79
115
  where?: Where;
80
116
  join?: Join | Array<Join>;
81
117
  groupBy?: string | Array<string>;
82
- having?: string | Array<string>;
118
+ having?: Where;
83
119
  orderBy?: string | Array<string> | Record<string, string | OrderTypes>;
84
120
  offset?: number;
121
+ subQueryPlaceholders?: Record<string, SelectAll>;
122
+ subQueryTokenNextId?: number;
85
123
  };
86
124
  type RawQuery = {
87
125
  query: string;
@@ -191,41 +229,6 @@ declare function defaultLogger(query: RawQuery, meta: QueryLoggerMeta): any;
191
229
  declare function asyncLoggerWrapper<Async extends boolean = true>(query: Query<any, Async> | Query<any, Async>[], loggerFunction: CallableFunction | undefined, innerFunction: () => any): Promise<any>;
192
230
  declare function syncLoggerWrapper<Async extends boolean = false>(query: Query<any, Async> | Query<any, Async>[], loggerFunction: CallableFunction | undefined, innerFunction: () => any): any;
193
231
 
194
- interface SelectExecuteOptions {
195
- lazy?: boolean;
196
- }
197
- declare class SelectBuilder<GenericResultWrapper, GenericResult = DefaultReturnObject, IsAsync extends boolean = true> {
198
- _debugger: boolean;
199
- _options: Partial<SelectAll>;
200
- _fetchAll: (params: SelectAll) => QueryWithExtra<GenericResultWrapper, any, IsAsync>;
201
- _fetchOne: (params: SelectOne) => QueryWithExtra<GenericResultWrapper, any, IsAsync>;
202
- constructor(options: Partial<SelectAll>, fetchAll: (params: SelectAll) => QueryWithExtra<GenericResultWrapper, any, IsAsync>, fetchOne: (params: SelectOne) => QueryWithExtra<GenericResultWrapper, any, IsAsync>);
203
- setDebugger(state: boolean): void;
204
- tableName(tableName: SelectAll['tableName']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
205
- fields(fields: SelectAll['fields']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
206
- where(conditions: string | Array<string>, params?: Primitive | Primitive[]): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
207
- whereIn<T extends string | Array<string>, P extends T extends Array<string> ? Primitive[][] : Primitive[]>(fields: T, values: P): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
208
- join(join: SelectAll['join']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
209
- groupBy(groupBy: SelectAll['groupBy']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
210
- having(having: SelectAll['having']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
211
- orderBy(orderBy: SelectAll['orderBy']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
212
- offset(offset: SelectAll['offset']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
213
- limit(limit: SelectAll['limit']): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
214
- _parseArray(fieldName: string, option: any, value: any): SelectBuilder<GenericResultWrapper, GenericResult, IsAsync>;
215
- getQueryAll<P extends SelectExecuteOptions = SelectExecuteOptions>(options?: P): Query<ArrayResult<GenericResultWrapper, GenericResult, IsAsync, P extends {
216
- lazy: true;
217
- } ? true : false>, IsAsync>;
218
- getQueryOne(): Query<OneResult<GenericResultWrapper, GenericResult>, IsAsync>;
219
- execute<P extends SelectExecuteOptions = SelectExecuteOptions>(options?: P): ArrayResult<GenericResultWrapper, GenericResult, IsAsync, P extends {
220
- lazy: true;
221
- } ? true : false>;
222
- all<P extends SelectExecuteOptions = SelectExecuteOptions>(options?: P): ArrayResult<GenericResultWrapper, GenericResult, IsAsync, P extends {
223
- lazy: true;
224
- } ? true : false>;
225
- one(): MaybeAsync<IsAsync, OneResult<GenericResultWrapper, GenericResult>>;
226
- count(): MaybeAsync<IsAsync, CountResult<GenericResultWrapper>>;
227
- }
228
-
229
232
  declare class QueryBuilder<GenericResultWrapper, IsAsync extends boolean = true> {
230
233
  protected options: QueryBuilderOptions<IsAsync>;
231
234
  loggerWrapper: typeof asyncLoggerWrapper;
@@ -263,12 +266,25 @@ declare class QueryBuilder<GenericResultWrapper, IsAsync extends boolean = true>
263
266
  protected _insert(params: Insert): string;
264
267
  protected _update(params: Update): string;
265
268
  protected _delete(params: Delete): string;
266
- protected _select(params: SelectAll): string;
269
+ protected _select(params: SelectAll, queryArgs?: any[]): string;
267
270
  protected _fields(value?: string | Array<string>): string;
268
- protected _where(value?: Where): string;
269
- protected _join(value?: Join | Array<Join>): string;
271
+ protected _where(value: Where | undefined, context?: {
272
+ subQueryPlaceholders?: Record<string, SelectAll>;
273
+ queryArgs: any[];
274
+ toSQLCompiler?: (params: SelectAll, queryArgs: any[]) => string;
275
+ }): string;
276
+ protected _join(value: Join | Array<Join> | undefined, context: {
277
+ subQueryPlaceholders?: Record<string, SelectAll>;
278
+ queryArgs: any[];
279
+ toSQLCompiler: (params: SelectAll, queryArgs: any[]) => string;
280
+ }): string;
270
281
  protected _groupBy(value?: string | Array<string>): string;
271
- protected _having(value?: string | Array<string>): string;
282
+ protected _having(value: Where | undefined, // Using Where type as Having structure is similar for conditions/params
283
+ context: {
284
+ subQueryPlaceholders?: Record<string, SelectAll>;
285
+ queryArgs: any[];
286
+ toSQLCompiler?: (params: SelectAll, queryArgs: any[]) => string;
287
+ }): string;
272
288
  protected _orderBy(value?: string | Array<string> | Record<string, string | OrderTypes>): string;
273
289
  protected _limit(value?: number): string;
274
290
  protected _offset(value?: number): string;
package/dist/index.js CHANGED
@@ -18,8 +18,8 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
20
  // src/index.ts
21
- var src_exports = {};
22
- __export(src_exports, {
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
23
  ConflictTypes: () => ConflictTypes,
24
24
  D1QB: () => D1QB,
25
25
  DOQB: () => DOQB,
@@ -38,7 +38,7 @@ __export(src_exports, {
38
38
  syncMigrationsBuilder: () => syncMigrationsBuilder,
39
39
  trimQuery: () => trimQuery
40
40
  });
41
- module.exports = __toCommonJS(src_exports);
41
+ module.exports = __toCommonJS(index_exports);
42
42
 
43
43
  // src/enums.ts
44
44
  var OrderTypes = /* @__PURE__ */ ((OrderTypes3) => {
@@ -135,25 +135,53 @@ var SelectBuilder = class _SelectBuilder {
135
135
  return this._parseArray("fields", this._options.fields, fields);
136
136
  }
137
137
  where(conditions, params) {
138
- if (!Array.isArray(conditions)) {
139
- conditions = [conditions];
140
- }
141
- if (params === void 0) params = [];
142
- if (!Array.isArray(params)) {
143
- params = [params];
144
- }
145
- if (this._options.where?.conditions) {
146
- conditions = this._options.where.conditions.concat(conditions);
138
+ const subQueryPlaceholders = this._options.subQueryPlaceholders ?? {};
139
+ let subQueryTokenNextId = this._options.subQueryTokenNextId ?? 0;
140
+ const existingConditions = this._options.where && typeof this._options.where === "object" && "conditions" in this._options.where ? this._options.where.conditions : [];
141
+ const existingParams = this._options.where && typeof this._options.where === "object" && "params" in this._options.where && this._options.where.params ? this._options.where.params : [];
142
+ const currentInputConditions = Array.isArray(conditions) ? conditions : [conditions];
143
+ const currentInputParams = params === void 0 ? [] : Array.isArray(params) ? params : [params];
144
+ const processedNewConditions = [];
145
+ const collectedPrimitiveParams = [];
146
+ let paramIndex = 0;
147
+ for (const conditionStr of currentInputConditions) {
148
+ if (!conditionStr.includes("?")) {
149
+ processedNewConditions.push(conditionStr);
150
+ continue;
151
+ }
152
+ const conditionParts = conditionStr.split("?");
153
+ let builtCondition = conditionParts[0] ?? "";
154
+ for (let j = 0; j < conditionParts.length - 1; j++) {
155
+ if (paramIndex >= currentInputParams.length) {
156
+ throw new Error('Mismatch between "?" placeholders and parameters in where clause.');
157
+ }
158
+ const currentParam = currentInputParams[paramIndex++];
159
+ const isSubQuery = typeof currentParam === "object" && currentParam !== null && ("tableName" in currentParam || "getOptions" in currentParam) && !currentParam.hasOwnProperty("_raw") || currentParam instanceof _SelectBuilder;
160
+ if (isSubQuery) {
161
+ const token = `__SUBQUERY_TOKEN_${subQueryTokenNextId++}__`;
162
+ subQueryPlaceholders[token] = currentParam instanceof _SelectBuilder ? currentParam.getOptions() : "getOptions" in currentParam && typeof currentParam.getOptions === "function" ? currentParam.getOptions() : currentParam;
163
+ builtCondition += token;
164
+ } else {
165
+ builtCondition += "?";
166
+ if (currentParam !== void 0) {
167
+ collectedPrimitiveParams.push(currentParam);
168
+ }
169
+ }
170
+ builtCondition += conditionParts[j + 1] ?? "";
171
+ }
172
+ processedNewConditions.push(builtCondition);
147
173
  }
148
- if (this._options.where?.params) {
149
- params = this._options.where.params.concat(params);
174
+ if (paramIndex < currentInputParams.length) {
175
+ throw new Error('Too many parameters provided for the given "?" placeholders in where clause.');
150
176
  }
151
177
  return new _SelectBuilder(
152
178
  {
153
179
  ...this._options,
180
+ subQueryPlaceholders,
181
+ subQueryTokenNextId,
154
182
  where: {
155
- conditions,
156
- params
183
+ conditions: existingConditions.concat(processedNewConditions),
184
+ params: existingParams.concat(collectedPrimitiveParams)
157
185
  }
158
186
  },
159
187
  this._fetchAll,
@@ -182,39 +210,81 @@ var SelectBuilder = class _SelectBuilder {
182
210
  const fieldLength = fields.length;
183
211
  whereInCondition = `(${fields.map((val) => val).reduce(seperateWithComma)}) IN (VALUES `;
184
212
  const valuesString = `(${[...new Array(fieldLength).keys()].map(() => "?").reduce(seperateWithComma)})`;
185
- whereInCondition += [...new Array(fieldLength).keys()].map(() => valuesString).reduce(seperateWithComma);
213
+ whereInCondition += [...new Array(values.length).keys()].map(() => valuesString).reduce(seperateWithComma);
186
214
  whereInCondition += ")";
187
215
  whereInParams = values.flat();
188
216
  }
189
- let conditions = [whereInCondition];
190
- let params = whereInParams;
191
- if (this._options.where?.conditions) {
192
- conditions = this._options.where?.conditions.concat(conditions);
217
+ return this.where(whereInCondition, whereInParams);
218
+ }
219
+ join(join) {
220
+ const joins = Array.isArray(join) ? join : [join];
221
+ const processedJoins = joins.map((j) => {
222
+ if (j && typeof j.table === "object") {
223
+ if (j.table instanceof _SelectBuilder) {
224
+ return { ...j, table: j.table.getOptions() };
225
+ }
226
+ }
227
+ return j;
228
+ });
229
+ return this._parseArray("join", this._options.join, processedJoins);
230
+ }
231
+ groupBy(groupBy) {
232
+ return this._parseArray("groupBy", this._options.groupBy, groupBy);
233
+ }
234
+ having(conditions, params) {
235
+ const subQueryPlaceholders = this._options.subQueryPlaceholders ?? {};
236
+ let subQueryTokenNextId = this._options.subQueryTokenNextId ?? 0;
237
+ const existingConditions = this._options.having && typeof this._options.having === "object" && "conditions" in this._options.having ? this._options.having.conditions : [];
238
+ const existingParams = this._options.having && typeof this._options.having === "object" && "params" in this._options.having && this._options.having.params ? this._options.having.params : [];
239
+ const currentInputConditions = Array.isArray(conditions) ? conditions : [conditions];
240
+ const currentInputParams = params === void 0 ? [] : Array.isArray(params) ? params : [params];
241
+ const processedNewConditions = [];
242
+ const collectedPrimitiveParams = [];
243
+ let paramIndex = 0;
244
+ for (const conditionStr of currentInputConditions) {
245
+ if (!conditionStr.includes("?")) {
246
+ processedNewConditions.push(conditionStr);
247
+ continue;
248
+ }
249
+ const conditionParts = conditionStr.split("?");
250
+ let builtCondition = conditionParts[0] ?? "";
251
+ for (let j = 0; j < conditionParts.length - 1; j++) {
252
+ if (paramIndex >= currentInputParams.length) {
253
+ throw new Error('Mismatch between "?" placeholders and parameters in having clause.');
254
+ }
255
+ const currentParam = currentInputParams[paramIndex++];
256
+ const isSubQuery = typeof currentParam === "object" && currentParam !== null && ("tableName" in currentParam || "getOptions" in currentParam) && !currentParam.hasOwnProperty("_raw") || currentParam instanceof _SelectBuilder;
257
+ if (isSubQuery) {
258
+ const token = `__SUBQUERY_TOKEN_${subQueryTokenNextId++}__`;
259
+ subQueryPlaceholders[token] = currentParam instanceof _SelectBuilder ? currentParam.getOptions() : "getOptions" in currentParam && typeof currentParam.getOptions === "function" ? currentParam.getOptions() : currentParam;
260
+ builtCondition += token;
261
+ } else {
262
+ builtCondition += "?";
263
+ if (currentParam !== void 0) {
264
+ collectedPrimitiveParams.push(currentParam);
265
+ }
266
+ }
267
+ builtCondition += conditionParts[j + 1] ?? "";
268
+ }
269
+ processedNewConditions.push(builtCondition);
193
270
  }
194
- if (this._options.where?.params) {
195
- params = this._options.where?.params.concat(params);
271
+ if (paramIndex < currentInputParams.length) {
272
+ throw new Error('Too many parameters provided for the given "?" placeholders in having clause.');
196
273
  }
197
274
  return new _SelectBuilder(
198
275
  {
199
276
  ...this._options,
200
- where: {
201
- conditions,
202
- params
277
+ subQueryPlaceholders,
278
+ subQueryTokenNextId,
279
+ having: {
280
+ conditions: existingConditions.concat(processedNewConditions),
281
+ params: existingParams.concat(collectedPrimitiveParams)
203
282
  }
204
283
  },
205
284
  this._fetchAll,
206
285
  this._fetchOne
207
286
  );
208
287
  }
209
- join(join) {
210
- return this._parseArray("join", this._options.join, join);
211
- }
212
- groupBy(groupBy) {
213
- return this._parseArray("groupBy", this._options.groupBy, groupBy);
214
- }
215
- having(having) {
216
- return this._parseArray("having", this._options.having, having);
217
- }
218
288
  orderBy(orderBy) {
219
289
  return this._parseArray("orderBy", this._options.orderBy, orderBy);
220
290
  }
@@ -284,6 +354,9 @@ var SelectBuilder = class _SelectBuilder {
284
354
  count() {
285
355
  return this._fetchOne(this._options).count();
286
356
  }
357
+ getOptions() {
358
+ return this._options;
359
+ }
287
360
  };
288
361
 
289
362
  // src/tools.ts
@@ -389,37 +462,56 @@ var QueryBuilder = class {
389
462
  );
390
463
  }
391
464
  fetchOne(params) {
465
+ const queryArgs = [];
466
+ const countQueryArgs = [];
467
+ const selectParamsForCount = {
468
+ ...params,
469
+ fields: "count(*) as total",
470
+ offset: void 0,
471
+ groupBy: void 0,
472
+ limit: 1
473
+ };
474
+ if (params.subQueryPlaceholders) {
475
+ selectParamsForCount.subQueryPlaceholders = params.subQueryPlaceholders;
476
+ }
477
+ const mainSql = this._select({ ...params, limit: 1 }, queryArgs);
478
+ const countSql = this._select(selectParamsForCount, countQueryArgs);
392
479
  return new QueryWithExtra(
393
480
  (q) => {
394
481
  return this.execute(q);
395
482
  },
396
- this._select({ ...params, limit: 1 }),
397
- this._select({
398
- ...params,
399
- fields: "count(*) as total",
400
- offset: void 0,
401
- groupBy: void 0,
402
- limit: 1
403
- }),
404
- typeof params.where === "object" && !Array.isArray(params.where) && params.where?.params ? Array.isArray(params.where?.params) ? params.where?.params : [params.where?.params] : void 0,
483
+ mainSql,
484
+ countSql,
485
+ queryArgs,
486
+ // Use the populated queryArgs from the main _select call
405
487
  "ONE" /* ONE */
406
488
  );
407
489
  }
408
490
  fetchAll(params) {
491
+ const queryArgs = [];
492
+ const countQueryArgs = [];
493
+ const mainQueryParams = { ...params, lazy: void 0 };
494
+ const countQueryParams = {
495
+ ...params,
496
+ fields: "count(*) as total",
497
+ offset: void 0,
498
+ groupBy: void 0,
499
+ limit: 1,
500
+ lazy: void 0
501
+ };
502
+ if (params.subQueryPlaceholders) {
503
+ countQueryParams.subQueryPlaceholders = params.subQueryPlaceholders;
504
+ }
505
+ const mainSql = this._select(mainQueryParams, queryArgs);
506
+ const countSql = this._select(countQueryParams, countQueryArgs);
409
507
  return new QueryWithExtra(
410
508
  (q) => {
411
509
  return params.lazy ? this.lazyExecute(q) : this.execute(q);
412
510
  },
413
- this._select({ ...params, lazy: void 0 }),
414
- this._select({
415
- ...params,
416
- fields: "count(*) as total",
417
- offset: void 0,
418
- groupBy: void 0,
419
- limit: 1,
420
- lazy: void 0
421
- }),
422
- typeof params.where === "object" && !Array.isArray(params.where) && params.where?.params ? Array.isArray(params.where?.params) ? params.where?.params : [params.where?.params] : void 0,
511
+ mainSql,
512
+ countSql,
513
+ queryArgs,
514
+ // Use the populated queryArgs from the main _select call
423
515
  "ALL" /* ALL */
424
516
  );
425
517
  }
@@ -577,39 +669,102 @@ var QueryBuilder = class {
577
669
  return `DELETE
578
670
  FROM ${params.tableName}` + this._where(params.where) + this._returning(params.returning) + this._orderBy(params.orderBy) + this._limit(params.limit) + this._offset(params.offset);
579
671
  }
580
- _select(params) {
672
+ _select(params, queryArgs) {
673
+ const isTopLevelCall = queryArgs === void 0;
674
+ if (isTopLevelCall) {
675
+ queryArgs = [];
676
+ }
677
+ const currentQueryArgs = queryArgs;
678
+ const context = {
679
+ subQueryPlaceholders: params.subQueryPlaceholders,
680
+ queryArgs: currentQueryArgs,
681
+ toSQLCompiler: this._select.bind(this)
682
+ };
581
683
  return `SELECT ${this._fields(params.fields)}
582
- FROM ${params.tableName}` + this._join(params.join) + this._where(params.where) + this._groupBy(params.groupBy) + this._having(params.having) + this._orderBy(params.orderBy) + this._limit(params.limit) + this._offset(params.offset);
684
+ FROM ${params.tableName}` + this._join(params.join, context) + this._where(params.where, context) + this._groupBy(params.groupBy) + this._having(params.having, context) + this._orderBy(params.orderBy) + this._limit(params.limit) + this._offset(params.offset);
583
685
  }
584
686
  _fields(value) {
585
687
  if (!value) return "*";
586
688
  if (typeof value === "string") return value;
587
689
  return value.join(", ");
588
690
  }
589
- _where(value) {
691
+ _where(value, context) {
590
692
  if (!value) return "";
591
- let conditions = value;
693
+ const currentContext = context ?? { queryArgs: [] };
694
+ let conditionStrings;
695
+ let primitiveParams = [];
592
696
  if (typeof value === "object" && !Array.isArray(value)) {
593
- conditions = value.conditions;
697
+ conditionStrings = Array.isArray(value.conditions) ? value.conditions : [value.conditions];
698
+ if (value.params) {
699
+ primitiveParams = Array.isArray(value.params) ? value.params : [value.params];
700
+ }
701
+ } else if (Array.isArray(value)) {
702
+ conditionStrings = value;
703
+ } else {
704
+ conditionStrings = [value];
594
705
  }
595
- if (typeof conditions === "string") return ` WHERE ${conditions.toString()}`;
596
- if (conditions.length === 1) return ` WHERE ${conditions[0].toString()}`;
597
- if (conditions.length > 1) {
598
- return ` WHERE (${conditions.join(") AND (")})`;
706
+ if (conditionStrings.length === 0) return "";
707
+ let primitiveParamIndex = 0;
708
+ const processedConditions = [];
709
+ for (const conditionStr of conditionStrings) {
710
+ const parts = conditionStr.split(/(__SUBQUERY_TOKEN_\d+__|\?)/g).filter(Boolean);
711
+ let builtCondition = "";
712
+ for (const part of parts) {
713
+ if (part === "?") {
714
+ if (primitiveParamIndex >= primitiveParams.length) {
715
+ throw new Error(
716
+ 'SQL generation error: Not enough primitive parameters for "?" placeholders in WHERE clause.'
717
+ );
718
+ }
719
+ currentContext.queryArgs.push(primitiveParams[primitiveParamIndex++]);
720
+ builtCondition += "?";
721
+ } else if (part.startsWith("__SUBQUERY_TOKEN_") && part.endsWith("__")) {
722
+ if (!currentContext.subQueryPlaceholders || !currentContext.toSQLCompiler) {
723
+ throw new Error("SQL generation error: Subquery context not provided for token processing.");
724
+ }
725
+ const subQueryParams = currentContext.subQueryPlaceholders[part];
726
+ if (!subQueryParams) {
727
+ throw new Error(`SQL generation error: Subquery token ${part} not found in placeholders.`);
728
+ }
729
+ const subQuerySql = currentContext.toSQLCompiler(subQueryParams, currentContext.queryArgs);
730
+ builtCondition += `(${subQuerySql})`;
731
+ } else {
732
+ builtCondition += part;
733
+ }
734
+ }
735
+ processedConditions.push(builtCondition);
599
736
  }
600
- return "";
737
+ if (primitiveParamIndex < primitiveParams.length && primitiveParams.length > 0) {
738
+ throw new Error(
739
+ 'SQL generation error: Too many primitive parameters provided for "?" placeholders in WHERE clause.'
740
+ );
741
+ }
742
+ if (processedConditions.length === 0) return "";
743
+ if (processedConditions.length === 1) {
744
+ return ` WHERE ${processedConditions[0]}`;
745
+ }
746
+ return ` WHERE (${processedConditions.join(") AND (")})`;
601
747
  }
602
- _join(value) {
748
+ _join(value, context) {
603
749
  if (!value) return "";
750
+ let joinArray;
604
751
  if (!Array.isArray(value)) {
605
- value = [value];
752
+ joinArray = [value];
753
+ } else {
754
+ joinArray = value;
606
755
  }
607
756
  const joinQuery = [];
608
- value.forEach((item) => {
757
+ joinArray.forEach((item) => {
609
758
  const type = item.type ? `${item.type} ` : "";
610
- joinQuery.push(
611
- `${type}JOIN ${typeof item.table === "string" ? item.table : `(${this._select(item.table)})`}${item.alias ? ` AS ${item.alias}` : ""} ON ${item.on}`
612
- );
759
+ let tableSql;
760
+ if (typeof item.table === "string") {
761
+ tableSql = item.table;
762
+ } else if (item.table instanceof SelectBuilder) {
763
+ tableSql = `(${context.toSQLCompiler(item.table.getOptions(), context.queryArgs)})`;
764
+ } else {
765
+ tableSql = `(${context.toSQLCompiler(item.table, context.queryArgs)})`;
766
+ }
767
+ joinQuery.push(`${type}JOIN ${tableSql}${item.alias ? ` AS ${item.alias}` : ""} ON ${item.on}`);
613
768
  });
614
769
  return " " + joinQuery.join(" ");
615
770
  }
@@ -618,10 +773,62 @@ var QueryBuilder = class {
618
773
  if (typeof value === "string") return ` GROUP BY ${value}`;
619
774
  return ` GROUP BY ${value.join(", ")}`;
620
775
  }
621
- _having(value) {
776
+ _having(value, context) {
622
777
  if (!value) return "";
623
- if (typeof value === "string") return ` HAVING ${value}`;
624
- return ` HAVING ${value.join(" AND ")}`;
778
+ const currentContext = context ?? { queryArgs: [] };
779
+ let conditionStrings;
780
+ let primitiveParams = [];
781
+ if (typeof value === "object" && !Array.isArray(value)) {
782
+ conditionStrings = Array.isArray(value.conditions) ? value.conditions : [value.conditions];
783
+ if (value.params) {
784
+ primitiveParams = Array.isArray(value.params) ? value.params : [value.params];
785
+ }
786
+ } else if (Array.isArray(value)) {
787
+ conditionStrings = value;
788
+ } else {
789
+ conditionStrings = [value];
790
+ }
791
+ if (conditionStrings.length === 0) return "";
792
+ let primitiveParamIndex = 0;
793
+ const processedConditions = [];
794
+ for (const conditionStr of conditionStrings) {
795
+ const parts = conditionStr.split(/(__SUBQUERY_TOKEN_\d+__|\?)/g).filter(Boolean);
796
+ let builtCondition = "";
797
+ for (const part of parts) {
798
+ if (part === "?") {
799
+ if (primitiveParamIndex >= primitiveParams.length) {
800
+ throw new Error(
801
+ 'SQL generation error: Not enough primitive parameters for "?" placeholders in HAVING clause.'
802
+ );
803
+ }
804
+ currentContext.queryArgs.push(primitiveParams[primitiveParamIndex++]);
805
+ builtCondition += "?";
806
+ } else if (part.startsWith("__SUBQUERY_TOKEN_") && part.endsWith("__")) {
807
+ if (!currentContext.subQueryPlaceholders || !currentContext.toSQLCompiler) {
808
+ throw new Error("SQL generation error: Subquery context not provided for token processing.");
809
+ }
810
+ const subQueryParams = currentContext.subQueryPlaceholders[part];
811
+ if (!subQueryParams) {
812
+ throw new Error(`SQL generation error: Subquery token ${part} not found in placeholders.`);
813
+ }
814
+ const subQuerySql = currentContext.toSQLCompiler(subQueryParams, currentContext.queryArgs);
815
+ builtCondition += `(${subQuerySql})`;
816
+ } else {
817
+ builtCondition += part;
818
+ }
819
+ }
820
+ processedConditions.push(builtCondition);
821
+ }
822
+ if (primitiveParamIndex < primitiveParams.length && primitiveParams.length > 0) {
823
+ throw new Error(
824
+ 'SQL generation error: Too many primitive parameters provided for "?" placeholders in HAVING clause.'
825
+ );
826
+ }
827
+ if (processedConditions.length === 0) return "";
828
+ if (processedConditions.length === 1) {
829
+ return ` HAVING ${processedConditions[0]}`;
830
+ }
831
+ return ` HAVING (${processedConditions.join(") AND (")})`;
625
832
  }
626
833
  _orderBy(value) {
627
834
  if (!value) return "";
package/dist/index.mjs CHANGED
@@ -93,25 +93,53 @@ var SelectBuilder = class _SelectBuilder {
93
93
  return this._parseArray("fields", this._options.fields, fields);
94
94
  }
95
95
  where(conditions, params) {
96
- if (!Array.isArray(conditions)) {
97
- conditions = [conditions];
98
- }
99
- if (params === void 0) params = [];
100
- if (!Array.isArray(params)) {
101
- params = [params];
102
- }
103
- if (this._options.where?.conditions) {
104
- conditions = this._options.where.conditions.concat(conditions);
96
+ const subQueryPlaceholders = this._options.subQueryPlaceholders ?? {};
97
+ let subQueryTokenNextId = this._options.subQueryTokenNextId ?? 0;
98
+ const existingConditions = this._options.where && typeof this._options.where === "object" && "conditions" in this._options.where ? this._options.where.conditions : [];
99
+ const existingParams = this._options.where && typeof this._options.where === "object" && "params" in this._options.where && this._options.where.params ? this._options.where.params : [];
100
+ const currentInputConditions = Array.isArray(conditions) ? conditions : [conditions];
101
+ const currentInputParams = params === void 0 ? [] : Array.isArray(params) ? params : [params];
102
+ const processedNewConditions = [];
103
+ const collectedPrimitiveParams = [];
104
+ let paramIndex = 0;
105
+ for (const conditionStr of currentInputConditions) {
106
+ if (!conditionStr.includes("?")) {
107
+ processedNewConditions.push(conditionStr);
108
+ continue;
109
+ }
110
+ const conditionParts = conditionStr.split("?");
111
+ let builtCondition = conditionParts[0] ?? "";
112
+ for (let j = 0; j < conditionParts.length - 1; j++) {
113
+ if (paramIndex >= currentInputParams.length) {
114
+ throw new Error('Mismatch between "?" placeholders and parameters in where clause.');
115
+ }
116
+ const currentParam = currentInputParams[paramIndex++];
117
+ const isSubQuery = typeof currentParam === "object" && currentParam !== null && ("tableName" in currentParam || "getOptions" in currentParam) && !currentParam.hasOwnProperty("_raw") || currentParam instanceof _SelectBuilder;
118
+ if (isSubQuery) {
119
+ const token = `__SUBQUERY_TOKEN_${subQueryTokenNextId++}__`;
120
+ subQueryPlaceholders[token] = currentParam instanceof _SelectBuilder ? currentParam.getOptions() : "getOptions" in currentParam && typeof currentParam.getOptions === "function" ? currentParam.getOptions() : currentParam;
121
+ builtCondition += token;
122
+ } else {
123
+ builtCondition += "?";
124
+ if (currentParam !== void 0) {
125
+ collectedPrimitiveParams.push(currentParam);
126
+ }
127
+ }
128
+ builtCondition += conditionParts[j + 1] ?? "";
129
+ }
130
+ processedNewConditions.push(builtCondition);
105
131
  }
106
- if (this._options.where?.params) {
107
- params = this._options.where.params.concat(params);
132
+ if (paramIndex < currentInputParams.length) {
133
+ throw new Error('Too many parameters provided for the given "?" placeholders in where clause.');
108
134
  }
109
135
  return new _SelectBuilder(
110
136
  {
111
137
  ...this._options,
138
+ subQueryPlaceholders,
139
+ subQueryTokenNextId,
112
140
  where: {
113
- conditions,
114
- params
141
+ conditions: existingConditions.concat(processedNewConditions),
142
+ params: existingParams.concat(collectedPrimitiveParams)
115
143
  }
116
144
  },
117
145
  this._fetchAll,
@@ -140,39 +168,81 @@ var SelectBuilder = class _SelectBuilder {
140
168
  const fieldLength = fields.length;
141
169
  whereInCondition = `(${fields.map((val) => val).reduce(seperateWithComma)}) IN (VALUES `;
142
170
  const valuesString = `(${[...new Array(fieldLength).keys()].map(() => "?").reduce(seperateWithComma)})`;
143
- whereInCondition += [...new Array(fieldLength).keys()].map(() => valuesString).reduce(seperateWithComma);
171
+ whereInCondition += [...new Array(values.length).keys()].map(() => valuesString).reduce(seperateWithComma);
144
172
  whereInCondition += ")";
145
173
  whereInParams = values.flat();
146
174
  }
147
- let conditions = [whereInCondition];
148
- let params = whereInParams;
149
- if (this._options.where?.conditions) {
150
- conditions = this._options.where?.conditions.concat(conditions);
175
+ return this.where(whereInCondition, whereInParams);
176
+ }
177
+ join(join) {
178
+ const joins = Array.isArray(join) ? join : [join];
179
+ const processedJoins = joins.map((j) => {
180
+ if (j && typeof j.table === "object") {
181
+ if (j.table instanceof _SelectBuilder) {
182
+ return { ...j, table: j.table.getOptions() };
183
+ }
184
+ }
185
+ return j;
186
+ });
187
+ return this._parseArray("join", this._options.join, processedJoins);
188
+ }
189
+ groupBy(groupBy) {
190
+ return this._parseArray("groupBy", this._options.groupBy, groupBy);
191
+ }
192
+ having(conditions, params) {
193
+ const subQueryPlaceholders = this._options.subQueryPlaceholders ?? {};
194
+ let subQueryTokenNextId = this._options.subQueryTokenNextId ?? 0;
195
+ const existingConditions = this._options.having && typeof this._options.having === "object" && "conditions" in this._options.having ? this._options.having.conditions : [];
196
+ const existingParams = this._options.having && typeof this._options.having === "object" && "params" in this._options.having && this._options.having.params ? this._options.having.params : [];
197
+ const currentInputConditions = Array.isArray(conditions) ? conditions : [conditions];
198
+ const currentInputParams = params === void 0 ? [] : Array.isArray(params) ? params : [params];
199
+ const processedNewConditions = [];
200
+ const collectedPrimitiveParams = [];
201
+ let paramIndex = 0;
202
+ for (const conditionStr of currentInputConditions) {
203
+ if (!conditionStr.includes("?")) {
204
+ processedNewConditions.push(conditionStr);
205
+ continue;
206
+ }
207
+ const conditionParts = conditionStr.split("?");
208
+ let builtCondition = conditionParts[0] ?? "";
209
+ for (let j = 0; j < conditionParts.length - 1; j++) {
210
+ if (paramIndex >= currentInputParams.length) {
211
+ throw new Error('Mismatch between "?" placeholders and parameters in having clause.');
212
+ }
213
+ const currentParam = currentInputParams[paramIndex++];
214
+ const isSubQuery = typeof currentParam === "object" && currentParam !== null && ("tableName" in currentParam || "getOptions" in currentParam) && !currentParam.hasOwnProperty("_raw") || currentParam instanceof _SelectBuilder;
215
+ if (isSubQuery) {
216
+ const token = `__SUBQUERY_TOKEN_${subQueryTokenNextId++}__`;
217
+ subQueryPlaceholders[token] = currentParam instanceof _SelectBuilder ? currentParam.getOptions() : "getOptions" in currentParam && typeof currentParam.getOptions === "function" ? currentParam.getOptions() : currentParam;
218
+ builtCondition += token;
219
+ } else {
220
+ builtCondition += "?";
221
+ if (currentParam !== void 0) {
222
+ collectedPrimitiveParams.push(currentParam);
223
+ }
224
+ }
225
+ builtCondition += conditionParts[j + 1] ?? "";
226
+ }
227
+ processedNewConditions.push(builtCondition);
151
228
  }
152
- if (this._options.where?.params) {
153
- params = this._options.where?.params.concat(params);
229
+ if (paramIndex < currentInputParams.length) {
230
+ throw new Error('Too many parameters provided for the given "?" placeholders in having clause.');
154
231
  }
155
232
  return new _SelectBuilder(
156
233
  {
157
234
  ...this._options,
158
- where: {
159
- conditions,
160
- params
235
+ subQueryPlaceholders,
236
+ subQueryTokenNextId,
237
+ having: {
238
+ conditions: existingConditions.concat(processedNewConditions),
239
+ params: existingParams.concat(collectedPrimitiveParams)
161
240
  }
162
241
  },
163
242
  this._fetchAll,
164
243
  this._fetchOne
165
244
  );
166
245
  }
167
- join(join) {
168
- return this._parseArray("join", this._options.join, join);
169
- }
170
- groupBy(groupBy) {
171
- return this._parseArray("groupBy", this._options.groupBy, groupBy);
172
- }
173
- having(having) {
174
- return this._parseArray("having", this._options.having, having);
175
- }
176
246
  orderBy(orderBy) {
177
247
  return this._parseArray("orderBy", this._options.orderBy, orderBy);
178
248
  }
@@ -242,6 +312,9 @@ var SelectBuilder = class _SelectBuilder {
242
312
  count() {
243
313
  return this._fetchOne(this._options).count();
244
314
  }
315
+ getOptions() {
316
+ return this._options;
317
+ }
245
318
  };
246
319
 
247
320
  // src/tools.ts
@@ -347,37 +420,56 @@ var QueryBuilder = class {
347
420
  );
348
421
  }
349
422
  fetchOne(params) {
423
+ const queryArgs = [];
424
+ const countQueryArgs = [];
425
+ const selectParamsForCount = {
426
+ ...params,
427
+ fields: "count(*) as total",
428
+ offset: void 0,
429
+ groupBy: void 0,
430
+ limit: 1
431
+ };
432
+ if (params.subQueryPlaceholders) {
433
+ selectParamsForCount.subQueryPlaceholders = params.subQueryPlaceholders;
434
+ }
435
+ const mainSql = this._select({ ...params, limit: 1 }, queryArgs);
436
+ const countSql = this._select(selectParamsForCount, countQueryArgs);
350
437
  return new QueryWithExtra(
351
438
  (q) => {
352
439
  return this.execute(q);
353
440
  },
354
- this._select({ ...params, limit: 1 }),
355
- this._select({
356
- ...params,
357
- fields: "count(*) as total",
358
- offset: void 0,
359
- groupBy: void 0,
360
- limit: 1
361
- }),
362
- typeof params.where === "object" && !Array.isArray(params.where) && params.where?.params ? Array.isArray(params.where?.params) ? params.where?.params : [params.where?.params] : void 0,
441
+ mainSql,
442
+ countSql,
443
+ queryArgs,
444
+ // Use the populated queryArgs from the main _select call
363
445
  "ONE" /* ONE */
364
446
  );
365
447
  }
366
448
  fetchAll(params) {
449
+ const queryArgs = [];
450
+ const countQueryArgs = [];
451
+ const mainQueryParams = { ...params, lazy: void 0 };
452
+ const countQueryParams = {
453
+ ...params,
454
+ fields: "count(*) as total",
455
+ offset: void 0,
456
+ groupBy: void 0,
457
+ limit: 1,
458
+ lazy: void 0
459
+ };
460
+ if (params.subQueryPlaceholders) {
461
+ countQueryParams.subQueryPlaceholders = params.subQueryPlaceholders;
462
+ }
463
+ const mainSql = this._select(mainQueryParams, queryArgs);
464
+ const countSql = this._select(countQueryParams, countQueryArgs);
367
465
  return new QueryWithExtra(
368
466
  (q) => {
369
467
  return params.lazy ? this.lazyExecute(q) : this.execute(q);
370
468
  },
371
- this._select({ ...params, lazy: void 0 }),
372
- this._select({
373
- ...params,
374
- fields: "count(*) as total",
375
- offset: void 0,
376
- groupBy: void 0,
377
- limit: 1,
378
- lazy: void 0
379
- }),
380
- typeof params.where === "object" && !Array.isArray(params.where) && params.where?.params ? Array.isArray(params.where?.params) ? params.where?.params : [params.where?.params] : void 0,
469
+ mainSql,
470
+ countSql,
471
+ queryArgs,
472
+ // Use the populated queryArgs from the main _select call
381
473
  "ALL" /* ALL */
382
474
  );
383
475
  }
@@ -535,39 +627,102 @@ var QueryBuilder = class {
535
627
  return `DELETE
536
628
  FROM ${params.tableName}` + this._where(params.where) + this._returning(params.returning) + this._orderBy(params.orderBy) + this._limit(params.limit) + this._offset(params.offset);
537
629
  }
538
- _select(params) {
630
+ _select(params, queryArgs) {
631
+ const isTopLevelCall = queryArgs === void 0;
632
+ if (isTopLevelCall) {
633
+ queryArgs = [];
634
+ }
635
+ const currentQueryArgs = queryArgs;
636
+ const context = {
637
+ subQueryPlaceholders: params.subQueryPlaceholders,
638
+ queryArgs: currentQueryArgs,
639
+ toSQLCompiler: this._select.bind(this)
640
+ };
539
641
  return `SELECT ${this._fields(params.fields)}
540
- FROM ${params.tableName}` + this._join(params.join) + this._where(params.where) + this._groupBy(params.groupBy) + this._having(params.having) + this._orderBy(params.orderBy) + this._limit(params.limit) + this._offset(params.offset);
642
+ FROM ${params.tableName}` + this._join(params.join, context) + this._where(params.where, context) + this._groupBy(params.groupBy) + this._having(params.having, context) + this._orderBy(params.orderBy) + this._limit(params.limit) + this._offset(params.offset);
541
643
  }
542
644
  _fields(value) {
543
645
  if (!value) return "*";
544
646
  if (typeof value === "string") return value;
545
647
  return value.join(", ");
546
648
  }
547
- _where(value) {
649
+ _where(value, context) {
548
650
  if (!value) return "";
549
- let conditions = value;
651
+ const currentContext = context ?? { queryArgs: [] };
652
+ let conditionStrings;
653
+ let primitiveParams = [];
550
654
  if (typeof value === "object" && !Array.isArray(value)) {
551
- conditions = value.conditions;
655
+ conditionStrings = Array.isArray(value.conditions) ? value.conditions : [value.conditions];
656
+ if (value.params) {
657
+ primitiveParams = Array.isArray(value.params) ? value.params : [value.params];
658
+ }
659
+ } else if (Array.isArray(value)) {
660
+ conditionStrings = value;
661
+ } else {
662
+ conditionStrings = [value];
552
663
  }
553
- if (typeof conditions === "string") return ` WHERE ${conditions.toString()}`;
554
- if (conditions.length === 1) return ` WHERE ${conditions[0].toString()}`;
555
- if (conditions.length > 1) {
556
- return ` WHERE (${conditions.join(") AND (")})`;
664
+ if (conditionStrings.length === 0) return "";
665
+ let primitiveParamIndex = 0;
666
+ const processedConditions = [];
667
+ for (const conditionStr of conditionStrings) {
668
+ const parts = conditionStr.split(/(__SUBQUERY_TOKEN_\d+__|\?)/g).filter(Boolean);
669
+ let builtCondition = "";
670
+ for (const part of parts) {
671
+ if (part === "?") {
672
+ if (primitiveParamIndex >= primitiveParams.length) {
673
+ throw new Error(
674
+ 'SQL generation error: Not enough primitive parameters for "?" placeholders in WHERE clause.'
675
+ );
676
+ }
677
+ currentContext.queryArgs.push(primitiveParams[primitiveParamIndex++]);
678
+ builtCondition += "?";
679
+ } else if (part.startsWith("__SUBQUERY_TOKEN_") && part.endsWith("__")) {
680
+ if (!currentContext.subQueryPlaceholders || !currentContext.toSQLCompiler) {
681
+ throw new Error("SQL generation error: Subquery context not provided for token processing.");
682
+ }
683
+ const subQueryParams = currentContext.subQueryPlaceholders[part];
684
+ if (!subQueryParams) {
685
+ throw new Error(`SQL generation error: Subquery token ${part} not found in placeholders.`);
686
+ }
687
+ const subQuerySql = currentContext.toSQLCompiler(subQueryParams, currentContext.queryArgs);
688
+ builtCondition += `(${subQuerySql})`;
689
+ } else {
690
+ builtCondition += part;
691
+ }
692
+ }
693
+ processedConditions.push(builtCondition);
557
694
  }
558
- return "";
695
+ if (primitiveParamIndex < primitiveParams.length && primitiveParams.length > 0) {
696
+ throw new Error(
697
+ 'SQL generation error: Too many primitive parameters provided for "?" placeholders in WHERE clause.'
698
+ );
699
+ }
700
+ if (processedConditions.length === 0) return "";
701
+ if (processedConditions.length === 1) {
702
+ return ` WHERE ${processedConditions[0]}`;
703
+ }
704
+ return ` WHERE (${processedConditions.join(") AND (")})`;
559
705
  }
560
- _join(value) {
706
+ _join(value, context) {
561
707
  if (!value) return "";
708
+ let joinArray;
562
709
  if (!Array.isArray(value)) {
563
- value = [value];
710
+ joinArray = [value];
711
+ } else {
712
+ joinArray = value;
564
713
  }
565
714
  const joinQuery = [];
566
- value.forEach((item) => {
715
+ joinArray.forEach((item) => {
567
716
  const type = item.type ? `${item.type} ` : "";
568
- joinQuery.push(
569
- `${type}JOIN ${typeof item.table === "string" ? item.table : `(${this._select(item.table)})`}${item.alias ? ` AS ${item.alias}` : ""} ON ${item.on}`
570
- );
717
+ let tableSql;
718
+ if (typeof item.table === "string") {
719
+ tableSql = item.table;
720
+ } else if (item.table instanceof SelectBuilder) {
721
+ tableSql = `(${context.toSQLCompiler(item.table.getOptions(), context.queryArgs)})`;
722
+ } else {
723
+ tableSql = `(${context.toSQLCompiler(item.table, context.queryArgs)})`;
724
+ }
725
+ joinQuery.push(`${type}JOIN ${tableSql}${item.alias ? ` AS ${item.alias}` : ""} ON ${item.on}`);
571
726
  });
572
727
  return " " + joinQuery.join(" ");
573
728
  }
@@ -576,10 +731,62 @@ var QueryBuilder = class {
576
731
  if (typeof value === "string") return ` GROUP BY ${value}`;
577
732
  return ` GROUP BY ${value.join(", ")}`;
578
733
  }
579
- _having(value) {
734
+ _having(value, context) {
580
735
  if (!value) return "";
581
- if (typeof value === "string") return ` HAVING ${value}`;
582
- return ` HAVING ${value.join(" AND ")}`;
736
+ const currentContext = context ?? { queryArgs: [] };
737
+ let conditionStrings;
738
+ let primitiveParams = [];
739
+ if (typeof value === "object" && !Array.isArray(value)) {
740
+ conditionStrings = Array.isArray(value.conditions) ? value.conditions : [value.conditions];
741
+ if (value.params) {
742
+ primitiveParams = Array.isArray(value.params) ? value.params : [value.params];
743
+ }
744
+ } else if (Array.isArray(value)) {
745
+ conditionStrings = value;
746
+ } else {
747
+ conditionStrings = [value];
748
+ }
749
+ if (conditionStrings.length === 0) return "";
750
+ let primitiveParamIndex = 0;
751
+ const processedConditions = [];
752
+ for (const conditionStr of conditionStrings) {
753
+ const parts = conditionStr.split(/(__SUBQUERY_TOKEN_\d+__|\?)/g).filter(Boolean);
754
+ let builtCondition = "";
755
+ for (const part of parts) {
756
+ if (part === "?") {
757
+ if (primitiveParamIndex >= primitiveParams.length) {
758
+ throw new Error(
759
+ 'SQL generation error: Not enough primitive parameters for "?" placeholders in HAVING clause.'
760
+ );
761
+ }
762
+ currentContext.queryArgs.push(primitiveParams[primitiveParamIndex++]);
763
+ builtCondition += "?";
764
+ } else if (part.startsWith("__SUBQUERY_TOKEN_") && part.endsWith("__")) {
765
+ if (!currentContext.subQueryPlaceholders || !currentContext.toSQLCompiler) {
766
+ throw new Error("SQL generation error: Subquery context not provided for token processing.");
767
+ }
768
+ const subQueryParams = currentContext.subQueryPlaceholders[part];
769
+ if (!subQueryParams) {
770
+ throw new Error(`SQL generation error: Subquery token ${part} not found in placeholders.`);
771
+ }
772
+ const subQuerySql = currentContext.toSQLCompiler(subQueryParams, currentContext.queryArgs);
773
+ builtCondition += `(${subQuerySql})`;
774
+ } else {
775
+ builtCondition += part;
776
+ }
777
+ }
778
+ processedConditions.push(builtCondition);
779
+ }
780
+ if (primitiveParamIndex < primitiveParams.length && primitiveParams.length > 0) {
781
+ throw new Error(
782
+ 'SQL generation error: Too many primitive parameters provided for "?" placeholders in HAVING clause.'
783
+ );
784
+ }
785
+ if (processedConditions.length === 0) return "";
786
+ if (processedConditions.length === 1) {
787
+ return ` HAVING ${processedConditions[0]}`;
788
+ }
789
+ return ` HAVING (${processedConditions.join(") AND (")})`;
583
790
  }
584
791
  _orderBy(value) {
585
792
  if (!value) return "";
package/package.json CHANGED
@@ -1,20 +1,15 @@
1
1
  {
2
2
  "name": "workers-qb",
3
- "version": "1.10.2",
3
+ "version": "1.11.0",
4
4
  "description": "Zero dependencies Query Builder for Cloudflare Workers",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
8
- "files": [
9
- "dist",
10
- "LICENSE",
11
- "README.md"
12
- ],
8
+ "files": ["dist", "LICENSE", "README.md"],
13
9
  "scripts": {
14
10
  "build": "tsup src/index.ts --format cjs,esm --dts",
15
11
  "lint": "npx @biomejs/biome check src/ tests/ || (npx @biomejs/biome check --write src/ tests/; exit 1)",
16
12
  "test": "vitest run --root tests",
17
- "prepare": "husky",
18
13
  "build-docs": "npm run docs:build && cp docs/_redirects docs/.vitepress/dist/_redirects",
19
14
  "docs:dev": "vitepress dev docs",
20
15
  "docs:build": "vitepress build docs",
@@ -61,13 +56,11 @@
61
56
  },
62
57
  "devDependencies": {
63
58
  "@biomejs/biome": "1.9.4",
64
- "@cloudflare/vitest-pool-workers": "^0.6.0",
65
- "@cloudflare/workers-types": "^4.20241106.0",
66
- "husky": "^9.1.6",
67
- "tsup": "^8.3.5",
68
- "typescript": "^5.6.3",
59
+ "@cloudflare/vitest-pool-workers": "^0.8.38",
60
+ "@cloudflare/workers-types": "^4.20250614.0",
61
+ "tsup": "^8.5.0",
62
+ "typescript": "^5.8.3",
69
63
  "vitepress": "^1.6.3",
70
- "vitest": "2.1.8",
71
- "wrangler": "^3.86.0"
64
+ "wrangler": "^4.20.0"
72
65
  }
73
66
  }