sonamu 0.7.14 → 0.7.16

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.
Files changed (46) hide show
  1. package/dist/api/sonamu.d.ts.map +1 -1
  2. package/dist/api/sonamu.js +4 -3
  3. package/dist/database/base-model.d.ts +16 -6
  4. package/dist/database/base-model.d.ts.map +1 -1
  5. package/dist/database/base-model.js +44 -3
  6. package/dist/database/base-model.types.d.ts +29 -48
  7. package/dist/database/base-model.types.d.ts.map +1 -1
  8. package/dist/database/base-model.types.js +12 -2
  9. package/dist/database/puri.d.ts +2 -1
  10. package/dist/database/puri.d.ts.map +1 -1
  11. package/dist/database/puri.js +2 -1
  12. package/dist/database/puri.types.d.ts +3 -3
  13. package/dist/database/puri.types.d.ts.map +1 -1
  14. package/dist/database/puri.types.js +1 -1
  15. package/dist/entity/entity-manager.d.ts +8 -4
  16. package/dist/entity/entity-manager.d.ts.map +1 -1
  17. package/dist/entity/entity.d.ts +10 -1
  18. package/dist/entity/entity.d.ts.map +1 -1
  19. package/dist/entity/entity.js +84 -39
  20. package/dist/index.d.ts +1 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +2 -1
  23. package/dist/tasks/workflow-manager.d.ts +3 -3
  24. package/dist/tasks/workflow-manager.d.ts.map +1 -1
  25. package/dist/tasks/workflow-manager.js +15 -11
  26. package/dist/template/implementations/generated.template.d.ts.map +1 -1
  27. package/dist/template/implementations/generated.template.js +8 -6
  28. package/dist/types/types.d.ts +41 -11
  29. package/dist/types/types.d.ts.map +1 -1
  30. package/dist/types/types.js +27 -3
  31. package/dist/ui/api.d.ts.map +1 -1
  32. package/dist/ui/api.js +12 -3
  33. package/dist/ui-web/assets/{index-J9MCfjCd.js → index-BcbbB-BB.js} +42 -42
  34. package/dist/ui-web/index.html +1 -1
  35. package/package.json +3 -3
  36. package/src/api/sonamu.ts +3 -2
  37. package/src/database/base-model.ts +66 -11
  38. package/src/database/base-model.types.ts +79 -76
  39. package/src/database/puri.ts +5 -1
  40. package/src/database/puri.types.ts +3 -6
  41. package/src/entity/entity.ts +83 -34
  42. package/src/index.ts +1 -0
  43. package/src/tasks/workflow-manager.ts +16 -12
  44. package/src/template/implementations/generated.template.ts +17 -3
  45. package/src/types/types.ts +31 -2
  46. package/src/ui/api.ts +11 -2
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>{{projectName}}: Sonamu UI</title>
7
- <script type="module" crossorigin src="/sonamu-ui/assets/index-J9MCfjCd.js"></script>
7
+ <script type="module" crossorigin src="/sonamu-ui/assets/index-BcbbB-BB.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/sonamu-ui/assets/index-CpaB9P6g.css">
9
9
  </head>
10
10
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sonamu",
3
- "version": "0.7.14",
3
+ "version": "0.7.16",
4
4
  "description": "Sonamu — TypeScript Fullstack API Framework",
5
5
  "keywords": [
6
6
  "typescript",
@@ -81,10 +81,10 @@
81
81
  "tsicli": "^1.0.5",
82
82
  "vitest": "^4.0.10",
83
83
  "zod": "^4.1.12",
84
+ "@sonamu-kit/hmr-hook": "^0.4.1",
84
85
  "@sonamu-kit/hmr-runner": "^0.1.1",
85
- "@sonamu-kit/tasks": "^0.0.1",
86
86
  "@sonamu-kit/ts-loader": "^2.1.3",
87
- "@sonamu-kit/hmr-hook": "^0.4.1"
87
+ "@sonamu-kit/tasks": "^0.1.1"
88
88
  },
89
89
  "devDependencies": {
90
90
  "@biomejs/biome": "^2.3.7",
package/src/api/sonamu.ts CHANGED
@@ -595,7 +595,7 @@ class SonamuClass {
595
595
  private async initializeWorkflows(options: SonamuTaskOptions | undefined) {
596
596
  const { WorkflowManager } = await import("../tasks/workflow-manager");
597
597
  // NOTE: @sonamu-kit/tasks 안에선 knex config를 수정하기 때문에 connection이 아닌 config 째로 보냅니다.
598
- this._workflows = await WorkflowManager.create(DB.getDBConfig("w"), true);
598
+ this._workflows = new WorkflowManager(DB.getDBConfig("w"));
599
599
  if (!options) {
600
600
  return;
601
601
  }
@@ -608,7 +608,7 @@ class SonamuClass {
608
608
  };
609
609
 
610
610
  if (enableWorker) {
611
- await this.workflows.setupWorker({
611
+ this.workflows.setupWorker({
612
612
  ...defaultWorkerOptions,
613
613
  ...options.workerOptions,
614
614
  });
@@ -645,6 +645,7 @@ class SonamuClass {
645
645
  server
646
646
  .listen({ port, host })
647
647
  .then(async () => {
648
+ await this.workflows.startWorker();
648
649
  await options.lifecycle?.onStart?.(server);
649
650
  })
650
651
  .catch(async (err) => {
@@ -1,20 +1,18 @@
1
1
  /** biome-ignore-all lint/suspicious/noExplicitAny: Puri의 타입은 개별 모델에서 확정되므로 BaseModel에서는 any를 허용함 */
2
2
 
3
3
  import type { Knex } from "knex";
4
- import { group, isObject, omit, set } from "radashi";
4
+ import { cloneDeep, group, isObject, omit, set } from "radashi";
5
5
  import type { ListResult } from "..";
6
6
  import { Sonamu } from "../api";
7
+ import { EntityManager } from "../entity/entity-manager";
7
8
  import type { DatabaseSchemaExtend, SonamuQueryMode } from "../types/types";
8
9
  import { getJoinTables, getTableNamesFromWhere } from "../utils/sql-parser";
9
10
  import { chunk } from "../utils/utils";
10
- import type {
11
- EnhancerMap,
12
- ResolveSubsetIntersection,
13
- UnionExtractedTTables,
14
- } from "./base-model.types";
11
+ import type { EnhancerMap, ResolveSubsetIntersection } from "./base-model.types";
15
12
  import type { DBPreset } from "./db";
16
13
  import { DB } from "./db";
17
14
  import { Puri } from "./puri";
15
+ import type { UnionExtractedTTables } from "./puri.types";
18
16
  import type { InferAllSubsets, PuriLoaderQueries, PuriSubsetFn } from "./puri-subset.types";
19
17
  import { PuriWrapper } from "./puri-wrapper";
20
18
  import { UpsertBuilder } from "./upsert-builder";
@@ -137,7 +135,12 @@ export class BaseModelClass<
137
135
  * 타입 검증 및 추론을 도와줌
138
136
  */
139
137
  createEnhancers<T extends TSubsetKey>(
140
- enhancers: EnhancerMap<T, InferAllSubsets<TSubsetQueries, TLoaderQueries>, TSubsetMapping>,
138
+ enhancers: EnhancerMap<
139
+ T,
140
+ InferAllSubsets<TSubsetQueries, TLoaderQueries>,
141
+ TSubsetMapping,
142
+ TSubsetQueries
143
+ >,
141
144
  ) {
142
145
  return enhancers;
143
146
  }
@@ -169,7 +172,7 @@ export class BaseModelClass<
169
172
  };
170
173
  debug?: boolean;
171
174
  optimizeCountQuery?: boolean;
172
- } & EnhancerParam<TSubsetKey, TComputedResults, TSubsetMapping>,
175
+ } & EnhancerParam<TSubsetKey, TComputedResults, TSubsetMapping, TSubsetQueries>,
173
176
  ): Promise<ListResult<LP, TSubsetMapping[T]>> {
174
177
  const { subset, qb, params: queryParams, debug = false, optimizeCountQuery = false } = params;
175
178
 
@@ -191,10 +194,18 @@ export class BaseModelClass<
191
194
 
192
195
  // Enhancer 적용
193
196
  const enhancer = (params as any).enhancers?.[subset];
194
- const rows = (await Promise.all(
197
+ const enhancedRows = (await Promise.all(
195
198
  computedRows.map((row) => enhancer?.(row) ?? row),
196
199
  )) as TSubsetMapping[T][];
197
200
 
201
+ // Internal 필드 제거
202
+ const entity = EntityManager.get(this.modelName);
203
+ const internalFields = entity.subsetsInternal[subset] ?? [];
204
+ const rows =
205
+ internalFields.length > 0
206
+ ? enhancedRows.map((row) => this.omitInternalFields(row, internalFields))
207
+ : enhancedRows;
208
+
198
209
  if (queryParams.queryMode === "list") {
199
210
  // 리스트만 리턴
200
211
  return { rows } as ListResult<LP, TSubsetMapping[T]>;
@@ -204,6 +215,49 @@ export class BaseModelClass<
204
215
  }
205
216
  }
206
217
 
218
+ /**
219
+ * 객체에서 internal 필드 제거
220
+ * 중첩 필드(예: "user.email") 및 배열(예: "employees.salary")도 처리
221
+ */
222
+ omitInternalFields<T extends object>(row: T, fields: string[]): T {
223
+ const result = cloneDeep(row);
224
+ for (const field of fields) {
225
+ this.deleteField(result, field.split("."));
226
+ }
227
+ return result;
228
+ }
229
+
230
+ /**
231
+ * 중첩 필드 삭제 (배열 내 객체도 처리)
232
+ */
233
+ deleteField(obj: any, parts: string[]): void {
234
+ if (!obj || typeof obj !== "object") {
235
+ return;
236
+ }
237
+
238
+ if (parts.length === 1) {
239
+ if (Array.isArray(obj)) {
240
+ obj.forEach((item) => {
241
+ if (item && typeof item === "object") {
242
+ delete item[parts[0]];
243
+ }
244
+ });
245
+ } else {
246
+ delete obj[parts[0]];
247
+ }
248
+ return;
249
+ }
250
+
251
+ const [first, ...rest] = parts;
252
+ const next = obj[first];
253
+
254
+ if (Array.isArray(next)) {
255
+ next.map((item) => this.deleteField(item, rest));
256
+ } else if (next && typeof next === "object") {
257
+ this.deleteField(next, rest);
258
+ }
259
+ }
260
+
207
261
  /**
208
262
  * COUNT 쿼리 실행 (내부 메서드)
209
263
  */
@@ -401,9 +455,10 @@ type EnhancerParam<
401
455
  TSubsetKey extends string,
402
456
  TComputedResults extends Record<TSubsetKey, any>,
403
457
  TSubsetMapping extends Record<TSubsetKey, any>,
458
+ TSubsetQueries extends Record<TSubsetKey, PuriSubsetFn>,
404
459
  > = [RequiredEnhancerKeys<TSubsetKey, TComputedResults, TSubsetMapping>] extends [never]
405
- ? { enhancers?: EnhancerMap<TSubsetKey, TComputedResults, TSubsetMapping> }
406
- : { enhancers: EnhancerMap<TSubsetKey, TComputedResults, TSubsetMapping> };
460
+ ? { enhancers?: EnhancerMap<TSubsetKey, TComputedResults, TSubsetMapping, TSubsetQueries> }
461
+ : { enhancers: EnhancerMap<TSubsetKey, TComputedResults, TSubsetMapping, TSubsetQueries> };
407
462
 
408
463
  type RequiredEnhancerKeys<
409
464
  TSubsetKey extends string,
@@ -7,29 +7,11 @@
7
7
  * Enhancer, SubsetQuery 교집합 등 Model 계층에서 필요한 타입 정의.
8
8
  */
9
9
 
10
- import type { ListResult } from "..";
11
10
  import type { DatabaseSchemaExtend } from "../types/types";
12
11
  import type { Puri } from "./puri";
12
+ import type { ExtractTTables } from "./puri.types";
13
13
  import type { PuriSubsetFn } from "./puri-subset.types";
14
14
 
15
- // ============================================================================
16
- // Puri 테이블 추출 유틸리티
17
- // ============================================================================
18
-
19
- /**
20
- * Puri 인스턴스에서 TTables 타입 추출
21
- */
22
- export type ExtractPuriTables<T> = T extends Puri<any, infer TTables, any> ? TTables : never;
23
-
24
- /**
25
- * SubsetQueries에서 모든 TTables의 유니온 추출
26
- * getSubsetQueries의 qb 타입 정의에 사용
27
- */
28
- export type UnionExtractedTTables<
29
- TSubsetKey extends string,
30
- TSubsetQueries extends Record<TSubsetKey, PuriSubsetFn>,
31
- > = ExtractPuriTables<ReturnType<TSubsetQueries[TSubsetKey]>>;
32
-
33
15
  // ============================================================================
34
16
  // Subset 교집합 계산 (onSubset 메서드용)
35
17
  // ============================================================================
@@ -37,11 +19,12 @@ export type UnionExtractedTTables<
37
19
  /**
38
20
  * 두 Puri의 테이블 교집합을 가진 새로운 Puri 생성
39
21
  */
40
- type MergePuriTables<A, B, TA = ExtractPuriTables<A>, TB = ExtractPuriTables<B>> = Puri<
41
- DatabaseSchemaExtend,
42
- Pick<TA, Extract<keyof TA, keyof TB>>,
43
- any
44
- >;
22
+ type MergePuriTables<
23
+ A extends Puri<any, any, any>,
24
+ B extends Puri<any, any, any>,
25
+ TA = ExtractTTables<A>,
26
+ TB = ExtractTTables<B>,
27
+ > = Puri<DatabaseSchemaExtend, Pick<TA, Extract<keyof TA, keyof TB>>, any>;
45
28
 
46
29
  /**
47
30
  * 서브셋 키 배열을 순회하며 테이블 교집합 Puri 계산
@@ -62,24 +45,79 @@ export type ResolveSubsetIntersection<
62
45
  // Enhancer
63
46
  // ============================================================================
64
47
 
48
+ /**
49
+ * SubsetQueries의 Puri 반환 타입에서 TTables를 추출하고,
50
+ * TTables의 키 중 DatabaseSchemaExtend의 키와 일치하는 것이 메인 테이블.
51
+ * 해당 테이블의 BaseSchema에서 __virtual_query__ 키를 추출.
52
+ */
53
+ type ExtractMainTable<TSubsetQueries extends Record<string, PuriSubsetFn>> = Extract<
54
+ keyof ExtractTTables<ReturnType<TSubsetQueries[keyof TSubsetQueries]>>,
55
+ keyof DatabaseSchemaExtend
56
+ >;
57
+
58
+ type ExtractVirtualQueryKeys<TSubsetQueries extends Record<string, PuriSubsetFn>> =
59
+ ExtractMainTable<TSubsetQueries> extends infer TTable extends keyof DatabaseSchemaExtend
60
+ ? DatabaseSchemaExtend[TTable] extends { __virtual_query__: readonly (infer K)[] }
61
+ ? K
62
+ : never
63
+ : never;
64
+
65
+ /**
66
+ * TSubsetMapping에서 virtualQuery 키를 optional로 만든 타입
67
+ * Enhancer 필수 여부 판단 시 사용
68
+ */
69
+ type OmitVirtualQueryFromMapping<TMapping, TVirtualQueryKeys> = Omit<
70
+ TMapping,
71
+ TVirtualQueryKeys & keyof TMapping
72
+ > &
73
+ Partial<Pick<TMapping, TVirtualQueryKeys & keyof TMapping>>;
74
+
75
+ /**
76
+ * Computed가 Mapping에 호환되는지 판단 (virtualQuery 키 제외)
77
+ */
78
+ type IsEnhancerOptional<
79
+ TSubsetKey extends string,
80
+ TComputedResults extends Record<TSubsetKey, any>,
81
+ TSubsetMapping extends Record<TSubsetKey, any>,
82
+ TSubsetQueries extends Record<TSubsetKey, PuriSubsetFn>,
83
+ K extends TSubsetKey,
84
+ > = TComputedResults[K] extends OmitVirtualQueryFromMapping<
85
+ TSubsetMapping[K],
86
+ ExtractVirtualQueryKeys<TSubsetQueries>
87
+ >
88
+ ? true
89
+ : false;
90
+
65
91
  /**
66
92
  * 단일 Enhancer 함수 타입
67
- * computed 결과를 받아 최종 mapping 타입으로 변환
93
+ * computed 결과 + virtualQuery props를 받아 최종 mapping 타입으로 변환
68
94
  */
69
- export type EnhancerFn<TComputed, TMapping> = (row: TComputed) => TMapping | Promise<TMapping>;
95
+ type EnhancerFnWithVirtualQuery<TComputed, TMapping, TVirtualQueryKeys> = (
96
+ row: TComputed & Pick<TMapping, TVirtualQueryKeys & keyof TMapping>,
97
+ ) => TMapping | Promise<TMapping>;
70
98
 
71
99
  /**
72
100
  * Enhancer가 필수인 SubsetKey 추출
73
101
  *
74
102
  * ComputedResults[K]가 SubsetMapping[K]에 할당 불가능하면 해당 K는 필수
75
103
  * (즉, virtual 필드 등 추가 변환이 필요한 경우)
104
+ * 단, virtualQuery 키는 무시 (사용자가 appendSelect로 직접 추가)
76
105
  */
77
106
  export type RequiredEnhancerKeys<
78
107
  TSubsetKey extends string,
79
108
  TComputedResults extends Record<TSubsetKey, any>,
80
109
  TSubsetMapping extends Record<TSubsetKey, any>,
110
+ TSubsetQueries extends Record<TSubsetKey, PuriSubsetFn>,
81
111
  > = {
82
- [K in TSubsetKey]: TComputedResults[K] extends TSubsetMapping[K] ? never : K;
112
+ [K in TSubsetKey]: IsEnhancerOptional<
113
+ TSubsetKey,
114
+ TComputedResults,
115
+ TSubsetMapping,
116
+ TSubsetQueries,
117
+ K
118
+ > extends true
119
+ ? never
120
+ : K;
83
121
  }[TSubsetKey];
84
122
 
85
123
  /**
@@ -87,6 +125,7 @@ export type RequiredEnhancerKeys<
87
125
  *
88
126
  * - ComputedResults[K]가 SubsetMapping[K]에 assignable하면 → enhancer 선택적
89
127
  * - 그렇지 않으면 → enhancer 필수
128
+ * - 단, virtualQuery 키는 무시 (사용자가 appendSelect로 직접 추가)
90
129
  *
91
130
  * @example
92
131
  * // virtual 필드 employee_count가 있는 경우
@@ -98,59 +137,23 @@ export type EnhancerMap<
98
137
  TSubsetKey extends string,
99
138
  TComputedResults extends Record<TSubsetKey, any>,
100
139
  TSubsetMapping extends Record<TSubsetKey, any>,
140
+ TSubsetQueries extends Record<TSubsetKey, PuriSubsetFn>,
141
+ TRequiredKeys extends TSubsetKey = RequiredEnhancerKeys<
142
+ TSubsetKey,
143
+ TComputedResults,
144
+ TSubsetMapping,
145
+ TSubsetQueries
146
+ >,
101
147
  > = {
102
- // Computed가 Mapping에 호환되면 선택적
103
- [K in TSubsetKey as TComputedResults[K] extends TSubsetMapping[K] ? K : never]?: EnhancerFn<
148
+ [K in Exclude<TSubsetKey, TRequiredKeys>]?: EnhancerFnWithVirtualQuery<
104
149
  TComputedResults[K],
105
- TSubsetMapping[K]
150
+ TSubsetMapping[K],
151
+ ExtractVirtualQueryKeys<TSubsetQueries>
106
152
  >;
107
153
  } & {
108
- // 호환되지 않으면 필수
109
- [K in TSubsetKey as TComputedResults[K] extends TSubsetMapping[K] ? never : K]: EnhancerFn<
154
+ [K in TRequiredKeys]: EnhancerFnWithVirtualQuery<
110
155
  TComputedResults[K],
111
- TSubsetMapping[K]
156
+ TSubsetMapping[K],
157
+ ExtractVirtualQueryKeys<TSubsetQueries>
112
158
  >;
113
159
  };
114
-
115
- // ============================================================================
116
- // executeSubsetQuery
117
- // ============================================================================
118
-
119
- /**
120
- * executeSubsetQuery 기본 파라미터
121
- */
122
- export type ExecuteSubsetQueryBaseParams<TSubsetKey extends string> = {
123
- subset: TSubsetKey;
124
- qb: Puri<any, any, any>;
125
- params: {
126
- num?: number;
127
- page?: number;
128
- queryMode?: "list" | "count" | "both";
129
- };
130
- debug?: boolean;
131
- optimizeCountQuery?: boolean;
132
- };
133
-
134
- /**
135
- * executeSubsetQuery 파라미터 (Enhancer 포함)
136
- *
137
- * RequiredEnhancerKeys가 없으면 enhancers 선택적, 있으면 필수
138
- */
139
- export type ExecuteSubsetQueryParams<
140
- TSubsetKey extends string,
141
- TComputedResults extends Record<TSubsetKey, any>,
142
- TSubsetMapping extends Record<TSubsetKey, any>,
143
- T extends TSubsetKey,
144
- > = ExecuteSubsetQueryBaseParams<T> &
145
- ([RequiredEnhancerKeys<TSubsetKey, TComputedResults, TSubsetMapping>] extends [never]
146
- ? { enhancers?: EnhancerMap<TSubsetKey, TComputedResults, TSubsetMapping> }
147
- : { enhancers: EnhancerMap<TSubsetKey, TComputedResults, TSubsetMapping> });
148
-
149
- /**
150
- * executeSubsetQuery 반환 타입
151
- */
152
- export type ExecuteSubsetQueryResult<
153
- TSubsetMapping extends Record<string, any>,
154
- T extends string,
155
- LP extends { queryMode?: "list" | "count" | "both" },
156
- > = ListResult<LP, TSubsetMapping[T]>;
@@ -1205,12 +1205,14 @@ export class JoinClauseGroup<
1205
1205
  TResolved: 쿼리 실행 후 반환될 결과 타입
1206
1206
  TReturning: RETURNING 절에 사용될 타입
1207
1207
  */
1208
- export class ResolvedPuri<TResolved, TReturning> {
1208
+ export class ResolvedPuri<TResolved, TReturning> implements Promise<TResolved> {
1209
1209
  constructor(
1210
1210
  public knexQuery: Knex.QueryBuilder,
1211
1211
  private knex: Knex,
1212
1212
  ) {}
1213
1213
 
1214
+ [Symbol.toStringTag]: string = "Promise";
1215
+
1214
1216
  toQuery(): string {
1215
1217
  return this.knexQuery.toQuery();
1216
1218
  }
@@ -1227,11 +1229,13 @@ export class ResolvedPuri<TResolved, TReturning> {
1227
1229
  Naite.t("puri:executed-query", this.toQuery());
1228
1230
  return this.knexQuery.then(onfulfilled as any, onrejected);
1229
1231
  }
1232
+
1230
1233
  catch<TResult2 = never>(
1231
1234
  onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,
1232
1235
  ): Promise<TResolved | TResult2> {
1233
1236
  return this.knexQuery.catch(onrejected);
1234
1237
  }
1238
+
1235
1239
  finally(onfinally?: (() => void) | null): Promise<TResolved> {
1236
1240
  return this.knexQuery.finally(onfinally);
1237
1241
  }
@@ -3,7 +3,7 @@
3
3
  import type { QueryResult } from "pg";
4
4
  import type { DatabaseForeignKeys, DatabaseSchemaExtend } from "../types/types";
5
5
  import type { Puri } from "./puri";
6
- import type { PuriWrapper } from "./puri-wrapper";
6
+ import type { PuriSubsetFn } from "./puri-subset.types";
7
7
 
8
8
  // ============================================
9
9
  // 내부 타입 키 (메타데이터)
@@ -342,15 +342,12 @@ export type InsertData<T> = Omit<
342
342
  export type InsertResult = Pick<QueryResult<any>, "command" | "rowCount" | "rows" | "oid">;
343
343
 
344
344
  // SubsetQuery를 위한 타입 유틸리티
345
- type ExtractTTables<T extends Puri<any, any, any>> = T extends Puri<any, infer TTables, any>
345
+ export type ExtractTTables<T extends Puri<any, any, any>> = T extends Puri<any, infer TTables, any>
346
346
  ? TTables
347
347
  : never;
348
348
  export type UnionExtractedTTables<
349
349
  SubsetKey extends string,
350
- SubsetQueries extends Record<
351
- SubsetKey,
352
- (qbWrapper: PuriWrapper<DatabaseSchemaExtend>) => Puri<any, any, any>
353
- >,
350
+ SubsetQueries extends Record<SubsetKey, PuriSubsetFn>,
354
351
  > = {
355
352
  [K in SubsetKey]: ExtractTTables<ReturnType<SubsetQueries[K]>>;
356
353
  }[SubsetKey];