@nkzw/fate 0.1.2 → 1.0.0-rc.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.
Files changed (170) hide show
  1. package/README.md +810 -129
  2. package/docs/api/classes/FateClient.md +832 -0
  3. package/docs/api/functions/clientRoot.md +27 -0
  4. package/docs/api/functions/createClient.md +21 -0
  5. package/docs/api/functions/createHTTPTransport.md +51 -0
  6. package/docs/api/functions/createTRPCTransport.md +46 -0
  7. package/docs/api/functions/getListEntries.md +15 -0
  8. package/docs/api/functions/getSelectionPlan.md +36 -0
  9. package/docs/api/functions/isRecord.md +15 -0
  10. package/docs/api/functions/isViewTag.md +17 -0
  11. package/docs/api/functions/liveConnectionTopic.md +19 -0
  12. package/docs/api/functions/liveEntityTopic.md +19 -0
  13. package/docs/api/functions/liveGlobalConnectionTopic.md +15 -0
  14. package/docs/api/functions/mutation.md +32 -0
  15. package/docs/api/functions/toEntityId.md +21 -0
  16. package/docs/api/functions/view.md +26 -0
  17. package/docs/api/index.md +81 -0
  18. package/docs/api/interfaces/FateThenable.md +31 -0
  19. package/docs/api/interfaces/Transport.md +199 -0
  20. package/docs/api/server/classes/FateRequestError.md +67 -0
  21. package/docs/api/server/drizzle/functions/createDrizzleFate.md +431 -0
  22. package/docs/api/server/drizzle/functions/createDrizzleSourceAdapter.md +21 -0
  23. package/docs/api/server/drizzle/functions/createDrizzleSourceRegistry.md +21 -0
  24. package/docs/api/server/drizzle/index.md +23 -0
  25. package/docs/api/server/drizzle/type-aliases/DrizzleManyToManyConfig.md +15 -0
  26. package/docs/api/server/drizzle/type-aliases/DrizzleManyToManyInput.md +5 -0
  27. package/docs/api/server/drizzle/type-aliases/DrizzleQueryExtra.md +21 -0
  28. package/docs/api/server/drizzle/type-aliases/DrizzleSourceAdapter.md +337 -0
  29. package/docs/api/server/drizzle/type-aliases/DrizzleViewConfig.md +29 -0
  30. package/docs/api/server/functions/bindSourceProcedures.md +32 -0
  31. package/docs/api/server/functions/computed.md +43 -0
  32. package/docs/api/server/functions/count.md +21 -0
  33. package/docs/api/server/functions/createFateFetchHandler.md +21 -0
  34. package/docs/api/server/functions/createFateServer.md +41 -0
  35. package/docs/api/server/functions/createHonoFateHandler.md +21 -0
  36. package/docs/api/server/functions/createLiveEventBus.md +17 -0
  37. package/docs/api/server/functions/createNestedSourcePlan.md +103 -0
  38. package/docs/api/server/functions/createResolver.md +74 -0
  39. package/docs/api/server/functions/createSourcePlan.md +95 -0
  40. package/docs/api/server/functions/createSourceProcedures.md +46 -0
  41. package/docs/api/server/functions/dataView.md +32 -0
  42. package/docs/api/server/functions/field.md +15 -0
  43. package/docs/api/server/functions/getNestedSelection.md +19 -0
  44. package/docs/api/server/functions/getScopedArgs.md +21 -0
  45. package/docs/api/server/functions/hasNestedSelection.md +19 -0
  46. package/docs/api/server/functions/list.md +28 -0
  47. package/docs/api/server/functions/refetchSourceById.md +55 -0
  48. package/docs/api/server/functions/resolveSourceById.md +55 -0
  49. package/docs/api/server/functions/resolveSourceByIds.md +55 -0
  50. package/docs/api/server/functions/resolveSourceConnection.md +67 -0
  51. package/docs/api/server/functions/resolver.md +42 -0
  52. package/docs/api/server/functions/toPrismaSelect.md +25 -0
  53. package/docs/api/server/functions/withConnection.md +24 -0
  54. package/docs/api/server/index.md +109 -0
  55. package/docs/api/server/prisma/functions/createPrismaFate.md +435 -0
  56. package/docs/api/server/prisma/functions/createPrismaSourceAdapter.md +21 -0
  57. package/docs/api/server/prisma/functions/createPrismaSourceRegistry.md +21 -0
  58. package/docs/api/server/prisma/functions/prismaConnectionArgs.md +57 -0
  59. package/docs/api/server/prisma/index.md +23 -0
  60. package/docs/api/server/prisma/type-aliases/PrismaDelegate.md +59 -0
  61. package/docs/api/server/prisma/type-aliases/PrismaQueryExtra.md +5 -0
  62. package/docs/api/server/prisma/type-aliases/PrismaSourceAdapter.md +343 -0
  63. package/docs/api/server/prisma/type-aliases/PrismaViewConfig.md +31 -0
  64. package/docs/api/server/type-aliases/ComputedField.md +55 -0
  65. package/docs/api/server/type-aliases/ComputedSelection.md +5 -0
  66. package/docs/api/server/type-aliases/ConnectionItem.md +29 -0
  67. package/docs/api/server/type-aliases/ConnectionPagination.md +39 -0
  68. package/docs/api/server/type-aliases/ConnectionResult.md +29 -0
  69. package/docs/api/server/type-aliases/CountSelection.md +29 -0
  70. package/docs/api/server/type-aliases/CountWhere.md +5 -0
  71. package/docs/api/server/type-aliases/DataViewListOptions.md +13 -0
  72. package/docs/api/server/type-aliases/DataViewOrderBy.md +5 -0
  73. package/docs/api/server/type-aliases/DataViewOrderDirection.md +5 -0
  74. package/docs/api/server/type-aliases/DataViewResult.md +11 -0
  75. package/docs/api/server/type-aliases/Entity.md +21 -0
  76. package/docs/api/server/type-aliases/FateServer.md +75 -0
  77. package/docs/api/server/type-aliases/FateServerManifest.md +5 -0
  78. package/docs/api/server/type-aliases/FieldSelection.md +21 -0
  79. package/docs/api/server/type-aliases/LiveConnectionEventType.md +5 -0
  80. package/docs/api/server/type-aliases/LiveConnectionSourceEvent.md +5 -0
  81. package/docs/api/server/type-aliases/LiveEventBus.md +5 -0
  82. package/docs/api/server/type-aliases/LiveEventType.md +5 -0
  83. package/docs/api/server/type-aliases/LiveSourceEvent.md +5 -0
  84. package/docs/api/server/type-aliases/NativeFateAPI.md +47 -0
  85. package/docs/api/server/type-aliases/OrderDirection.md +5 -0
  86. package/docs/api/server/type-aliases/SourceConfig.md +55 -0
  87. package/docs/api/server/type-aliases/SourceDefinition.md +55 -0
  88. package/docs/api/server/type-aliases/SourceOrder.md +5 -0
  89. package/docs/api/server/type-aliases/SourceOrderField.md +21 -0
  90. package/docs/api/server/type-aliases/SourcePlan.md +29 -0
  91. package/docs/api/server/type-aliases/SourcePlanNode.md +29 -0
  92. package/docs/api/server/type-aliases/SourceRegistry.md +11 -0
  93. package/docs/api/server/type-aliases/SourceRelation.md +15 -0
  94. package/docs/api/server/type-aliases/SourceRelationConfig.md +23 -0
  95. package/docs/api/server/variables/byIdInput.md +5 -0
  96. package/docs/api/server/variables/connectionArgs.md +7 -0
  97. package/docs/api/type-aliases/ConnectionMetadata.md +7 -0
  98. package/docs/api/type-aliases/ConnectionRef.md +13 -0
  99. package/docs/api/type-aliases/Entity.md +15 -0
  100. package/docs/api/type-aliases/EntityId.md +7 -0
  101. package/docs/api/type-aliases/FateLiveConnectionEvent.md +5 -0
  102. package/docs/api/type-aliases/FateLiveEvent.md +5 -0
  103. package/docs/api/type-aliases/FateMutations.md +7 -0
  104. package/docs/api/type-aliases/FateOperation.md +5 -0
  105. package/docs/api/type-aliases/FateProtocolRequest.md +5 -0
  106. package/docs/api/type-aliases/FateProtocolResponse.md +5 -0
  107. package/docs/api/type-aliases/FateRecord.md +7 -0
  108. package/docs/api/type-aliases/FateRoots.md +7 -0
  109. package/docs/api/type-aliases/InferFateAPI.md +11 -0
  110. package/docs/api/type-aliases/List.md +5 -0
  111. package/docs/api/type-aliases/ListEntry.md +5 -0
  112. package/docs/api/type-aliases/ListItem.md +13 -0
  113. package/docs/api/type-aliases/Mask.md +15 -0
  114. package/docs/api/type-aliases/MutationDefinition.md +21 -0
  115. package/docs/api/type-aliases/MutationEntity.md +13 -0
  116. package/docs/api/type-aliases/MutationIdentifier.md +19 -0
  117. package/docs/api/type-aliases/MutationInput.md +13 -0
  118. package/docs/api/type-aliases/MutationResult.md +13 -0
  119. package/docs/api/type-aliases/NodesItem.md +13 -0
  120. package/docs/api/type-aliases/Pagination.md +39 -0
  121. package/docs/api/type-aliases/Request.md +7 -0
  122. package/docs/api/type-aliases/RequestMode.md +7 -0
  123. package/docs/api/type-aliases/RequestOptions.md +7 -0
  124. package/docs/api/type-aliases/RequestResult.md +18 -0
  125. package/docs/api/type-aliases/Selection.md +13 -0
  126. package/docs/api/type-aliases/Snapshot.md +7 -0
  127. package/docs/api/type-aliases/TypeConfig.md +41 -0
  128. package/docs/api/type-aliases/View.md +17 -0
  129. package/docs/api/type-aliases/ViewData.md +18 -0
  130. package/docs/api/type-aliases/ViewEntity.md +13 -0
  131. package/docs/api/type-aliases/ViewEntityName.md +13 -0
  132. package/docs/api/type-aliases/ViewRef.md +13 -0
  133. package/docs/api/type-aliases/ViewSelection.md +13 -0
  134. package/docs/api/type-aliases/ViewSnapshot.md +18 -0
  135. package/docs/api/type-aliases/ViewTag.md +7 -0
  136. package/docs/api/variables/ConnectionTag.md +7 -0
  137. package/docs/guide/actions.md +263 -0
  138. package/docs/guide/core-concepts.md +22 -0
  139. package/docs/guide/getting-started.md +57 -0
  140. package/docs/guide/list-views.md +86 -0
  141. package/docs/guide/live-views.md +245 -0
  142. package/docs/guide/requests.md +130 -0
  143. package/docs/guide/server-integration.md +630 -0
  144. package/docs/guide/views.md +278 -0
  145. package/docs/guide/void-integration.md +167 -0
  146. package/docs/index.md +4 -0
  147. package/lib/cli.mjs +17 -250
  148. package/lib/clientStub.d.mts +6 -0
  149. package/lib/clientStub.mjs +6 -0
  150. package/lib/executor-BqwHdN3n.d.mts +468 -0
  151. package/lib/index.d.mts +64 -3
  152. package/lib/index.mjs +1965 -559
  153. package/lib/list-4wRNSVgI.d.mts +10 -0
  154. package/lib/list-C_mi6GnE.mjs +16 -0
  155. package/lib/list.d.mts +2 -0
  156. package/lib/list.mjs +2 -0
  157. package/lib/liveTopics-DzNtJaBD.mjs +197 -0
  158. package/lib/{record-DnhZuvUe.mjs → record-AeZJC9fd.mjs} +1 -2
  159. package/lib/record-ENh92gyb.d.mts +315 -0
  160. package/lib/server/drizzle.d.mts +304 -0
  161. package/lib/server/drizzle.mjs +688 -0
  162. package/lib/server/prisma.d.mts +309 -0
  163. package/lib/server/prisma.mjs +345 -0
  164. package/lib/server.d.mts +82 -184
  165. package/lib/server.mjs +870 -416
  166. package/lib/sourceRouter-Bwpkr062.mjs +972 -0
  167. package/lib/{types-Djlh7mMH.d.mts → types-Dz46PXr3.d.mts} +327 -197
  168. package/lib/vite.d.mts +17 -0
  169. package/lib/vite.mjs +592 -0
  170. package/package.json +44 -6
@@ -0,0 +1,688 @@
1
+ import { t as isRecord } from "../record-AeZJC9fd.mjs";
2
+ import { O as withConnection, S as isDataView, c as resolveSourceConnection, d as createSourceDefinitions, f as createSourcePlan, g as attachComputedState, i as createSourceRegistry, l as collectDataViewConfigs, o as resolveSourceById, p as getDataViewSourceConfig, s as resolveSourceByIds, t as bindSourceProcedures } from "../sourceRouter-Bwpkr062.mjs";
3
+ import { and, asc, createTableRelationsHelpers, desc, eq, extractTablesRelationalConfig, getTableColumns, gt, inArray, isTable, lt, normalizeRelation, or, sql } from "drizzle-orm";
4
+ //#region src/server/drizzle.ts
5
+ /**
6
+ * Fate's Drizzle integration.
7
+ *
8
+ * @example
9
+ * import { createDrizzleFate } from '@nkzw/fate/server/drizzle';
10
+ *
11
+ * @module @nkzw/fate/server/drizzle
12
+ */
13
+ const resolveSource = (source) => typeof source === "function" ? source() : source;
14
+ const isSourceDefinition = (target) => "view" in target && "id" in target;
15
+ const getColumn = (columns, field) => {
16
+ const column = columns[field];
17
+ if (!column) throw new Error(`No Drizzle column registered for field ${field}.`);
18
+ return column;
19
+ };
20
+ const paginationArgs = (args) => ({
21
+ after: typeof args?.after === "string" ? args.after : void 0,
22
+ before: typeof args?.before === "string" ? args.before : void 0,
23
+ first: typeof args?.first === "number" ? args.first : void 0,
24
+ last: typeof args?.last === "number" ? args.last : void 0
25
+ });
26
+ const hasPagination = (args) => {
27
+ const value = paginationArgs(args);
28
+ return value.after !== void 0 || value.before !== void 0 || value.first !== void 0 || value.last !== void 0;
29
+ };
30
+ const getConnectionDirection = (args) => args?.before !== void 0 || typeof args?.last === "number" ? "backward" : "forward";
31
+ const getConnectionSize = (fallback, args) => (typeof args?.first === "number" ? args.first : void 0) ?? (typeof args?.last === "number" ? args.last : void 0) ?? fallback;
32
+ const buildConnection = ({ cursor, direction, items, pageSize }) => {
33
+ const hasMore = items.length > pageSize;
34
+ const connectionItems = (direction === "forward" ? items.slice(0, pageSize) : items.slice(-pageSize)).map((node) => ({
35
+ cursor: node.id,
36
+ node
37
+ }));
38
+ const firstItem = connectionItems[0];
39
+ const lastItem = connectionItems.at(-1);
40
+ return {
41
+ items: connectionItems,
42
+ pagination: {
43
+ hasNext: direction === "backward" ? Boolean(cursor) : hasMore,
44
+ hasPrevious: direction === "backward" ? hasMore : Boolean(cursor),
45
+ nextCursor: lastItem?.cursor,
46
+ previousCursor: (direction === "backward" ? hasMore : Boolean(cursor)) ? firstItem?.cursor : void 0
47
+ }
48
+ };
49
+ };
50
+ const mapByField = (items, field) => new Map(items.map((item) => [item[field], item]));
51
+ const compactKeys = (values) => [...new Set(values.filter((value) => value !== null && value !== void 0))];
52
+ const reorderByIds = (ids, items) => {
53
+ const itemsById = mapByField(items, "id");
54
+ return ids.flatMap((id) => {
55
+ const item = itemsById.get(id);
56
+ return item ? [item] : [];
57
+ });
58
+ };
59
+ const addColumnField = (fields, field) => {
60
+ if (!field || field === "cursor") return;
61
+ fields.add(field);
62
+ };
63
+ const addComputedSelectionColumns = (fields, select) => {
64
+ if (!select) return;
65
+ for (const selection of Object.values(select)) if (selection.kind === "field") {
66
+ const [field] = selection.path.split(".");
67
+ addColumnField(fields, field);
68
+ }
69
+ };
70
+ const getRequiredFields = (node, extraFields = []) => {
71
+ const fields = new Set(extraFields);
72
+ addColumnField(fields, node.source.id);
73
+ for (const field of node.selectedFields) addColumnField(fields, field);
74
+ for (const order of node.orderBy) addColumnField(fields, order.field);
75
+ for (const computed of node.computeds.values()) addComputedSelectionColumns(fields, computed.select);
76
+ for (const [field] of node.relations) {
77
+ const sourceRelation = node.source.relations?.[field];
78
+ if (sourceRelation) addColumnField(fields, sourceRelation.localKey);
79
+ }
80
+ return fields;
81
+ };
82
+ const compareColumn = (direction, naturalDirection, column, value) => {
83
+ const forward = naturalDirection === "asc" ? gt(column, value) : lt(column, value);
84
+ const backward = naturalDirection === "asc" ? lt(column, value) : gt(column, value);
85
+ return direction === "forward" ? forward : backward;
86
+ };
87
+ const buildCursorWhere = ({ columns, cursorValues, direction, node }) => {
88
+ const branches = node.orderBy.map((entry, index) => {
89
+ const column = getColumn(columns, entry.field);
90
+ const compare = compareColumn(direction, entry.direction, column, cursorValues[entry.field]);
91
+ const equalities = node.orderBy.slice(0, index).map((previous) => eq(getColumn(columns, previous.field), cursorValues[previous.field]));
92
+ return equalities.length ? and(...equalities, compare) : compare;
93
+ });
94
+ return branches.length === 1 ? branches[0] : or(...branches);
95
+ };
96
+ const getQueryOrder = (direction, node, columns) => node.orderBy.map((entry) => {
97
+ const column = getColumn(columns, entry.field);
98
+ return direction === "forward" ? entry.direction === "asc" ? asc(column) : desc(column) : entry.direction === "asc" ? desc(column) : asc(column);
99
+ });
100
+ const isSQLWrapper = (value) => isRecord(value) && typeof value.getSQL === "function";
101
+ const whereFromCountSelection = (columns, where) => {
102
+ if (!where) return;
103
+ if (typeof where === "function") return where(columns);
104
+ if (isSQLWrapper(where)) return where;
105
+ if (Object.keys(where).length === 0) return;
106
+ const conditions = Object.entries(where).map(([field, value]) => eq(getColumn(columns, field), value));
107
+ return conditions.length === 1 ? conditions[0] : and(...conditions);
108
+ };
109
+ const toRegisteredConfig = (config) => ({
110
+ ...config,
111
+ columns: config.columns ?? getTableColumns(config.table)
112
+ });
113
+ const lowerFirst = (value) => `${value.slice(0, 1).toLowerCase()}${value.slice(1)}`;
114
+ const getColumnKey = (columns, column) => {
115
+ const entry = Object.entries(columns).find(([, candidate]) => candidate === column);
116
+ if (!entry) throw new Error(`Unable to resolve Drizzle column ${column.name}.`);
117
+ return entry[0];
118
+ };
119
+ const includesColumn = (columns, column) => Object.values(columns).includes(column);
120
+ const getDrizzleSchemaMetadata = ({ db, schema }) => {
121
+ if (schema) {
122
+ const extracted = extractTablesRelationalConfig(schema, createTableRelationsHelpers);
123
+ return {
124
+ fullSchema: schema,
125
+ schema: extracted.tables,
126
+ tableNamesMap: extracted.tableNamesMap
127
+ };
128
+ }
129
+ if (typeof db === "function") return;
130
+ const metadata = db._;
131
+ return metadata?.schema && metadata.fullSchema && metadata.tableNamesMap ? {
132
+ fullSchema: metadata.fullSchema,
133
+ schema: metadata.schema,
134
+ tableNamesMap: metadata.tableNamesMap
135
+ } : void 0;
136
+ };
137
+ const getTableConfig = ({ config, metadata }) => {
138
+ const entry = Object.entries(metadata.fullSchema).find(([, table]) => table === config.table);
139
+ if (!entry) throw new Error(`No Drizzle schema metadata found for ${config.view.typeName}.`);
140
+ const tableConfig = metadata.schema[entry[0]];
141
+ if (!tableConfig) throw new Error(`No Drizzle relational config found for ${config.view.typeName}.`);
142
+ return tableConfig;
143
+ };
144
+ const getTableForView = (view, metadata) => {
145
+ const directTable = metadata.fullSchema[lowerFirst(view.typeName)];
146
+ if (isTable(directTable)) return directTable;
147
+ const tableConfig = Object.values(metadata.schema).find((config) => config.tsName === lowerFirst(view.typeName) || config.dbName === view.typeName || lowerFirst(config.dbName) === lowerFirst(view.typeName));
148
+ if (tableConfig) {
149
+ const table = metadata.fullSchema[tableConfig.tsName];
150
+ if (isTable(table)) return table;
151
+ }
152
+ throw new Error(`No Drizzle table found for view ${view.typeName}.`);
153
+ };
154
+ const isDrizzleViewConfig = (value) => isRecord(value) && isDataView(value.view) && isTable(value.table);
155
+ const createDrizzleViewConfigs = ({ metadata, views }) => {
156
+ if (Array.isArray(views)) return views.map((view) => {
157
+ if (isDrizzleViewConfig(view)) return view;
158
+ if (!isDataView(view)) throw new Error(`Expected a data view or Drizzle view config.`);
159
+ if (!metadata) throw new Error(`Drizzle table for ${view.typeName} could not be inferred. Pass 'schema' to createDrizzleFate or use an explicit view config.`);
160
+ const config = getDataViewSourceConfig(view);
161
+ return {
162
+ ...config,
163
+ table: getTableForView(config.view, metadata)
164
+ };
165
+ });
166
+ if (!metadata) throw new Error(`Drizzle tables could not be inferred. Pass 'schema' to createDrizzleFate or use explicit view configs.`);
167
+ return collectDataViewConfigs(views).map((config) => ({
168
+ ...config,
169
+ table: getTableForView(config.view, metadata)
170
+ }));
171
+ };
172
+ const inferDirectRelation = ({ field, metadata, sourceConfig, targetConfig }) => {
173
+ const sourceTableConfig = getTableConfig({
174
+ config: sourceConfig,
175
+ metadata
176
+ });
177
+ const targetTableConfig = getTableConfig({
178
+ config: targetConfig,
179
+ metadata
180
+ });
181
+ const relation = sourceTableConfig.relations[field];
182
+ if (!relation) return null;
183
+ const normalized = normalizeRelation(metadata.schema, metadata.tableNamesMap, relation);
184
+ return {
185
+ foreignKey: getColumnKey(targetTableConfig.columns, normalized.references[0]),
186
+ localKey: getColumnKey(sourceTableConfig.columns, normalized.fields[0])
187
+ };
188
+ };
189
+ const inferManyToManyRelation = ({ field, metadata, sourceConfig, targetConfig }) => {
190
+ const sourceTableConfig = getTableConfig({
191
+ config: sourceConfig,
192
+ metadata
193
+ });
194
+ const targetTableConfig = getTableConfig({
195
+ config: targetConfig,
196
+ metadata
197
+ });
198
+ for (const joinTableConfig of Object.values(metadata.schema)) {
199
+ if (joinTableConfig === sourceTableConfig || joinTableConfig === targetTableConfig) continue;
200
+ let sourceJoin = null;
201
+ let targetJoin = null;
202
+ for (const relation of Object.values(joinTableConfig.relations)) {
203
+ let normalized;
204
+ try {
205
+ normalized = normalizeRelation(metadata.schema, metadata.tableNamesMap, relation);
206
+ } catch {
207
+ continue;
208
+ }
209
+ const fieldColumn = normalized.fields[0];
210
+ const referenceColumn = normalized.references[0];
211
+ if (!fieldColumn || !referenceColumn) continue;
212
+ if (includesColumn(sourceTableConfig.columns, referenceColumn)) sourceJoin = {
213
+ field: fieldColumn,
214
+ reference: referenceColumn
215
+ };
216
+ else if (includesColumn(targetTableConfig.columns, referenceColumn)) targetJoin = {
217
+ field: fieldColumn,
218
+ reference: referenceColumn
219
+ };
220
+ }
221
+ if (!sourceJoin || !targetJoin) continue;
222
+ const joinTable = metadata.fullSchema[joinTableConfig.tsName];
223
+ if (!isTable(joinTable)) continue;
224
+ sourceConfig.manyToMany = {
225
+ ...sourceConfig.manyToMany,
226
+ [field]: sourceConfig.manyToMany?.[field] ?? joinTable
227
+ };
228
+ return {
229
+ foreignKey: getColumnKey(targetTableConfig.columns, targetJoin.reference),
230
+ localKey: getColumnKey(sourceTableConfig.columns, sourceJoin.reference),
231
+ through: {
232
+ foreignKey: getColumnKey(joinTableConfig.columns, targetJoin.field),
233
+ localKey: getColumnKey(joinTableConfig.columns, sourceJoin.field)
234
+ }
235
+ };
236
+ }
237
+ return null;
238
+ };
239
+ const resolveManyToManyConfig = ({ field, source, sourceRelation, through: throughInput }) => {
240
+ const through = isTable(throughInput) ? { table: throughInput } : throughInput;
241
+ if (through.localColumn && through.foreignColumn) return {
242
+ foreignColumn: through.foreignColumn,
243
+ localColumn: through.localColumn,
244
+ table: through.table
245
+ };
246
+ if (!sourceRelation.through) throw new Error(`Drizzle many-to-many relation ${source.view.typeName}.${field} requires source relation 'through' keys or explicit join columns.`);
247
+ const throughColumns = through.columns ?? getTableColumns(through.table);
248
+ return {
249
+ foreignColumn: getColumn(throughColumns, sourceRelation.through.foreignKey),
250
+ localColumn: getColumn(throughColumns, sourceRelation.through.localKey),
251
+ table: through.table
252
+ };
253
+ };
254
+ function createDrizzleSourceAdapter({ db, schema, views }) {
255
+ const metadata = getDrizzleSchemaMetadata({
256
+ db,
257
+ schema
258
+ });
259
+ const viewConfigs = createDrizzleViewConfigs({
260
+ metadata,
261
+ views
262
+ }).map((config) => ({
263
+ ...config,
264
+ manyToMany: config.manyToMany ? { ...config.manyToMany } : void 0,
265
+ relations: config.relations ? { ...config.relations } : void 0
266
+ }));
267
+ const configsByView = new Map(viewConfigs.map((config) => [config.view, config]));
268
+ const configsByFields = new Map(viewConfigs.map((config) => [config.view.fields, config]));
269
+ const sourceDefinitions = createSourceDefinitions(viewConfigs, { resolveRelation: ({ config, field, kind, target }) => {
270
+ if (!metadata) return;
271
+ const targetConfig = configsByView.get(target.view) ?? configsByFields.get(target.view.fields);
272
+ if (!targetConfig) return;
273
+ return inferDirectRelation({
274
+ field,
275
+ metadata,
276
+ sourceConfig: config,
277
+ targetConfig
278
+ }) ?? (kind === "many" ? inferManyToManyRelation({
279
+ field,
280
+ metadata,
281
+ sourceConfig: config,
282
+ targetConfig
283
+ }) : void 0) ?? void 0;
284
+ } });
285
+ const sourcesByView = new Map(sourceDefinitions.map((source) => [source.view, source]));
286
+ const sourcesByFields = new Map(sourceDefinitions.map((source) => [source.view.fields, source]));
287
+ const sourceConfigs = new Map(viewConfigs.map((config, index) => [sourceDefinitions[index], {
288
+ ...toRegisteredConfig(config),
289
+ source: sourceDefinitions[index]
290
+ }]));
291
+ const getSource = (target) => {
292
+ if (isSourceDefinition(target)) return target;
293
+ const source = sourcesByView.get(target) ?? sourcesByFields.get(target.fields);
294
+ if (!source) throw new Error(`No source registered for view ${target.typeName}.`);
295
+ return source;
296
+ };
297
+ const getSourceConfig = (source) => {
298
+ const config = sourceConfigs.get(source);
299
+ if (!config) throw new Error(`No Drizzle table registered for source ${source.view.typeName}.`);
300
+ return config;
301
+ };
302
+ const getDb = (ctx) => {
303
+ if (typeof db === "function") return db(ctx);
304
+ return db;
305
+ };
306
+ const buildSelection = (node, extraFields = []) => {
307
+ const config = getSourceConfig(node.source);
308
+ const selection = {};
309
+ for (const field of getRequiredFields(node, extraFields)) selection[field] = getColumn(config.columns, field);
310
+ return selection;
311
+ };
312
+ const queryRows = async ({ ctx, extraFields, node, where }) => {
313
+ const config = getSourceConfig(node.source);
314
+ return await getDb(ctx).select(buildSelection(node, extraFields)).from(config.table).where(where).orderBy(...getQueryOrder("forward", node, config.columns));
315
+ };
316
+ const queryNodePage = async ({ baseWhere, ctx, cursor, direction, node, take }) => {
317
+ const config = getSourceConfig(node.source);
318
+ const currentDb = getDb(ctx);
319
+ let whereClause = baseWhere;
320
+ if (cursor) {
321
+ const cursorSelection = Object.fromEntries(node.orderBy.map((entry) => [entry.field, getColumn(config.columns, entry.field)]));
322
+ const [cursorRow] = await currentDb.select(cursorSelection).from(config.table).where(baseWhere ? and(baseWhere, eq(getColumn(config.columns, node.source.id), cursor)) : eq(getColumn(config.columns, node.source.id), cursor)).limit(1);
323
+ if (cursorRow) {
324
+ const cursorWhere = buildCursorWhere({
325
+ columns: config.columns,
326
+ cursorValues: cursorRow,
327
+ direction,
328
+ node
329
+ });
330
+ whereClause = whereClause ? and(whereClause, cursorWhere) : cursorWhere;
331
+ }
332
+ }
333
+ const rows = await currentDb.select(buildSelection(node)).from(config.table).where(whereClause).orderBy(...getQueryOrder(direction, node, config.columns)).limit(take);
334
+ return direction === "backward" ? rows.reverse() : rows;
335
+ };
336
+ const attachComputedCounts = async (items, node, ctx) => {
337
+ if (items.length === 0) return;
338
+ for (const [field, computed] of node.computeds) {
339
+ if (!computed.select) continue;
340
+ for (const [selectionName, selection] of Object.entries(computed.select)) {
341
+ if (selection.kind !== "count") continue;
342
+ const sourceRelation = node.source.relations?.[selection.relation];
343
+ if (!sourceRelation || sourceRelation.kind !== "many" && sourceRelation.kind !== "manyToMany") throw new Error(`Computed count ${node.source.view.typeName}.${field} requires a collection relation named ${selection.relation}.`);
344
+ const parentKeys = compactKeys(items.map((item) => item[sourceRelation.localKey]));
345
+ if (parentKeys.length === 0) continue;
346
+ const childConfig = getSourceConfig(resolveSource(sourceRelation.source));
347
+ let rows;
348
+ if (sourceRelation.kind === "manyToMany") {
349
+ const throughConfig = getSourceConfig(node.source).manyToMany?.[selection.relation];
350
+ if (!throughConfig) throw new Error(`No Drizzle many-to-many table registered for ${node.source.view.typeName}.${selection.relation}.`);
351
+ const through = resolveManyToManyConfig({
352
+ field: selection.relation,
353
+ source: node.source,
354
+ sourceRelation,
355
+ through: throughConfig
356
+ });
357
+ rows = await getDb(ctx).select({
358
+ count: sql`count(*)`.mapWith(Number),
359
+ parentKey: through.localColumn
360
+ }).from(through.table).innerJoin(childConfig.table, eq(through.foreignColumn, getColumn(childConfig.columns, sourceRelation.foreignKey))).where(and(inArray(through.localColumn, parentKeys), whereFromCountSelection(childConfig.columns, selection.where))).groupBy(through.localColumn);
361
+ } else {
362
+ const parentKeyColumn = getColumn(childConfig.columns, sourceRelation.foreignKey);
363
+ rows = await getDb(ctx).select({
364
+ count: sql`count(*)`.mapWith(Number),
365
+ parentKey: parentKeyColumn
366
+ }).from(childConfig.table).where(and(inArray(parentKeyColumn, parentKeys), whereFromCountSelection(childConfig.columns, selection.where))).groupBy(parentKeyColumn);
367
+ }
368
+ const counts = new Map(rows.map((row) => [row.parentKey, row.count]));
369
+ for (const item of items) attachComputedState(item, field, { [selectionName]: counts.get(item[sourceRelation.localKey]) ?? 0 });
370
+ }
371
+ }
372
+ };
373
+ const fetchManyRelation = async ({ ctx, items, relationNode, sourceRelation }) => {
374
+ const childConfig = getSourceConfig(resolveSource(sourceRelation.source));
375
+ const parentKeys = compactKeys(items.map((item) => item[sourceRelation.localKey]));
376
+ if (parentKeys.length === 0) return /* @__PURE__ */ new Map();
377
+ const hydrated = await hydrateRows(await queryRows({
378
+ ctx,
379
+ extraFields: [sourceRelation.foreignKey],
380
+ node: relationNode,
381
+ where: inArray(getColumn(childConfig.columns, sourceRelation.foreignKey), parentKeys)
382
+ }), relationNode, ctx);
383
+ const byParentKey = /* @__PURE__ */ new Map();
384
+ for (const item of hydrated) {
385
+ const key = item[sourceRelation.foreignKey];
386
+ const entries = byParentKey.get(key) ?? [];
387
+ entries.push(item);
388
+ byParentKey.set(key, entries);
389
+ }
390
+ return byParentKey;
391
+ };
392
+ const fetchManyConnection = async (parentKey, relationNode, sourceRelation, ctx) => {
393
+ const childConfig = getSourceConfig(resolveSource(sourceRelation.source));
394
+ const args = paginationArgs(relationNode.args);
395
+ const direction = getConnectionDirection(args);
396
+ const pageSize = getConnectionSize(20, args);
397
+ const cursor = direction === "forward" ? args.after : args.before;
398
+ if (parentKey === null || parentKey === void 0) return buildConnection({
399
+ cursor,
400
+ direction,
401
+ items: [],
402
+ pageSize
403
+ });
404
+ return buildConnection({
405
+ cursor,
406
+ direction,
407
+ items: await hydrateRows(await queryNodePage({
408
+ baseWhere: eq(getColumn(childConfig.columns, sourceRelation.foreignKey), parentKey),
409
+ ctx,
410
+ cursor,
411
+ direction,
412
+ node: relationNode,
413
+ take: pageSize + 1
414
+ }), relationNode, ctx),
415
+ pageSize
416
+ });
417
+ };
418
+ const queryManyToManyPage = async ({ ctx, cursor, direction, node, parentKey, sourceRelation, take, through }) => {
419
+ const childConfig = getSourceConfig(resolveSource(sourceRelation.source));
420
+ const currentDb = getDb(ctx);
421
+ let whereClause = eq(through.localColumn, parentKey);
422
+ if (cursor) {
423
+ const cursorSelection = Object.fromEntries(node.orderBy.map((entry) => [entry.field, getColumn(childConfig.columns, entry.field)]));
424
+ const [cursorRow] = await currentDb.select(cursorSelection).from(through.table).innerJoin(childConfig.table, eq(through.foreignColumn, getColumn(childConfig.columns, sourceRelation.foreignKey))).where(and(eq(through.localColumn, parentKey), eq(getColumn(childConfig.columns, node.source.id), cursor))).limit(1);
425
+ if (cursorRow) whereClause = and(whereClause, buildCursorWhere({
426
+ columns: childConfig.columns,
427
+ cursorValues: cursorRow,
428
+ direction,
429
+ node
430
+ }));
431
+ }
432
+ const rows = await currentDb.select({
433
+ ...buildSelection(node),
434
+ parentKey: through.localColumn
435
+ }).from(through.table).innerJoin(childConfig.table, eq(through.foreignColumn, getColumn(childConfig.columns, sourceRelation.foreignKey))).where(whereClause).orderBy(...getQueryOrder(direction, node, childConfig.columns)).limit(take);
436
+ return direction === "backward" ? rows.reverse() : rows;
437
+ };
438
+ const fetchManyToManyConnection = async (parentKey, relationNode, sourceRelation, through, ctx) => {
439
+ const args = paginationArgs(relationNode.args);
440
+ const direction = getConnectionDirection(args);
441
+ const pageSize = getConnectionSize(20, args);
442
+ const cursor = direction === "forward" ? args.after : args.before;
443
+ if (parentKey === null || parentKey === void 0) return buildConnection({
444
+ cursor,
445
+ direction,
446
+ items: [],
447
+ pageSize
448
+ });
449
+ return buildConnection({
450
+ cursor,
451
+ direction,
452
+ items: await hydrateRows(await queryManyToManyPage({
453
+ ctx,
454
+ cursor,
455
+ direction,
456
+ node: relationNode,
457
+ parentKey,
458
+ sourceRelation,
459
+ take: pageSize + 1,
460
+ through
461
+ }), relationNode, ctx),
462
+ pageSize
463
+ });
464
+ };
465
+ const fetchManyToManyRelation = async ({ ctx, items, node, relationField, relationNode, sourceRelation }) => {
466
+ const parentConfig = getSourceConfig(node.source);
467
+ const childConfig = getSourceConfig(resolveSource(sourceRelation.source));
468
+ const throughConfig = parentConfig.manyToMany?.[relationField];
469
+ if (!throughConfig) throw new Error(`No Drizzle many-to-many table registered for ${node.source.view.typeName}.${relationField}.`);
470
+ const through = resolveManyToManyConfig({
471
+ field: relationField,
472
+ source: node.source,
473
+ sourceRelation,
474
+ through: throughConfig
475
+ });
476
+ const parentKeys = compactKeys(items.map((item) => item[sourceRelation.localKey]));
477
+ if (parentKeys.length === 0) return /* @__PURE__ */ new Map();
478
+ const hydrated = await hydrateRows(await getDb(ctx).select({
479
+ ...buildSelection(relationNode),
480
+ parentKey: through.localColumn
481
+ }).from(through.table).innerJoin(childConfig.table, eq(through.foreignColumn, getColumn(childConfig.columns, sourceRelation.foreignKey))).where(inArray(through.localColumn, parentKeys)).orderBy(...getQueryOrder("forward", relationNode, childConfig.columns)), relationNode, ctx);
482
+ const byParentKey = /* @__PURE__ */ new Map();
483
+ for (const item of hydrated) {
484
+ const entries = byParentKey.get(item.parentKey) ?? [];
485
+ entries.push(item);
486
+ byParentKey.set(item.parentKey, entries);
487
+ }
488
+ return byParentKey;
489
+ };
490
+ const hydrateRows = async (rows, node, ctx) => {
491
+ const items = rows.map((row) => ({ ...row }));
492
+ await attachComputedCounts(items, node, ctx);
493
+ for (const [field, relationNode] of node.relations) {
494
+ const sourceRelation = node.source.relations?.[field];
495
+ if (!sourceRelation) continue;
496
+ if (sourceRelation.kind === "one") {
497
+ const childConfig = getSourceConfig(resolveSource(sourceRelation.source));
498
+ const childKeys = compactKeys(items.map((item) => item[sourceRelation.localKey]));
499
+ const childByKey = mapByField(await hydrateRows(childKeys.length ? await queryRows({
500
+ ctx,
501
+ node: relationNode,
502
+ where: inArray(getColumn(childConfig.columns, sourceRelation.foreignKey), childKeys)
503
+ }) : [], relationNode, ctx), sourceRelation.foreignKey);
504
+ for (const item of items) {
505
+ const localKey = item[sourceRelation.localKey];
506
+ item[field] = localKey !== null && localKey !== void 0 ? childByKey.get(localKey) ?? null : null;
507
+ }
508
+ continue;
509
+ }
510
+ if (sourceRelation.kind === "manyToMany") {
511
+ const throughConfig = getSourceConfig(node.source).manyToMany?.[field];
512
+ if (!throughConfig) throw new Error(`No Drizzle many-to-many table registered for ${node.source.view.typeName}.${field}.`);
513
+ const through = resolveManyToManyConfig({
514
+ field,
515
+ source: node.source,
516
+ sourceRelation,
517
+ through: throughConfig
518
+ });
519
+ if (hasPagination(relationNode.args)) {
520
+ const connections = await Promise.all(items.map(async (item) => [item[sourceRelation.localKey], await fetchManyToManyConnection(item[sourceRelation.localKey], relationNode, sourceRelation, through, ctx)]));
521
+ const connectionByParentKey = new Map(connections);
522
+ for (const item of items) item[field] = connectionByParentKey.get(item[sourceRelation.localKey]);
523
+ continue;
524
+ }
525
+ const byParentKey = await fetchManyToManyRelation({
526
+ ctx,
527
+ items,
528
+ node,
529
+ relationField: field,
530
+ relationNode,
531
+ sourceRelation
532
+ });
533
+ for (const item of items) item[field] = byParentKey.get(item[sourceRelation.localKey]) ?? [];
534
+ continue;
535
+ }
536
+ if (hasPagination(relationNode.args)) {
537
+ const connections = await Promise.all(items.map(async (item) => [item[sourceRelation.localKey], await fetchManyConnection(item[sourceRelation.localKey], relationNode, sourceRelation, ctx)]));
538
+ const connectionByParentKey = new Map(connections);
539
+ for (const item of items) item[field] = connectionByParentKey.get(item[sourceRelation.localKey]);
540
+ continue;
541
+ }
542
+ const byParentKey = await fetchManyRelation({
543
+ ctx,
544
+ items,
545
+ relationNode,
546
+ sourceRelation
547
+ });
548
+ for (const item of items) item[field] = byParentKey.get(item[sourceRelation.localKey]) ?? [];
549
+ }
550
+ return items;
551
+ };
552
+ const fetchByIds = async ({ ctx, extra, ids, plan }) => {
553
+ if (!ids.length) return [];
554
+ const config = getSourceConfig(plan.source);
555
+ return reorderByIds(ids, await hydrateRows(await queryRows({
556
+ ctx,
557
+ extraFields: extra?.extraFields,
558
+ node: plan.root,
559
+ where: inArray(getColumn(config.columns, plan.source.id), [...new Set(ids)])
560
+ }), plan.root, ctx));
561
+ };
562
+ const fetchById = async ({ ctx, extra, id, plan }) => (await fetchByIds({
563
+ ctx,
564
+ extra,
565
+ ids: [id],
566
+ plan
567
+ }))[0] ?? null;
568
+ const fetchConnection = async ({ ctx, cursor, direction, extra, plan, take }) => hydrateRows(await queryNodePage({
569
+ baseWhere: extra?.where,
570
+ ctx,
571
+ cursor,
572
+ direction,
573
+ node: plan.root,
574
+ take
575
+ }), plan.root, ctx);
576
+ const registry = createSourceRegistry(sourceDefinitions.map((source) => [source, {
577
+ byId: ({ ctx, extra, id, plan }) => fetchById({
578
+ ctx,
579
+ extra,
580
+ id,
581
+ plan
582
+ }),
583
+ byIds: ({ ctx, extra, ids, plan }) => fetchByIds({
584
+ ctx,
585
+ extra,
586
+ ids,
587
+ plan
588
+ }),
589
+ connection: ({ ctx, cursor, direction, extra, plan, take }) => fetchConnection({
590
+ ctx,
591
+ cursor,
592
+ direction,
593
+ extra,
594
+ plan,
595
+ take
596
+ })
597
+ }]));
598
+ return {
599
+ fetchById,
600
+ fetchByIds,
601
+ fetchConnection,
602
+ getSource,
603
+ registry,
604
+ resolveById: ({ ctx, extra, id, input, view }) => resolveSourceById({
605
+ ctx,
606
+ extra,
607
+ id,
608
+ input,
609
+ registry,
610
+ source: getSource(view)
611
+ }),
612
+ resolveByIds: ({ ctx, extra, ids, input, view }) => resolveSourceByIds({
613
+ ctx,
614
+ extra,
615
+ ids,
616
+ input,
617
+ registry,
618
+ source: getSource(view)
619
+ }),
620
+ resolveConnection: ({ ctx, cursor, direction, extra, input, skip, take, view }) => resolveSourceConnection({
621
+ ctx,
622
+ cursor,
623
+ direction,
624
+ extra,
625
+ input,
626
+ registry,
627
+ skip,
628
+ source: getSource(view),
629
+ take
630
+ })
631
+ };
632
+ }
633
+ const createDrizzleSourceRegistry = (options) => createDrizzleSourceAdapter(options).registry;
634
+ function createDrizzleFate({ procedure, ...options }) {
635
+ const adapter = createDrizzleSourceAdapter(options);
636
+ const connection = withConnection(procedure);
637
+ const procedures = bindSourceProcedures({
638
+ createConnectionProcedure: connection,
639
+ procedure,
640
+ registry: adapter.registry
641
+ });
642
+ return {
643
+ ...adapter,
644
+ connection,
645
+ createPlan: ({ args, ctx, select, view }) => createSourcePlan({
646
+ args,
647
+ ctx,
648
+ select,
649
+ source: adapter.getSource(view)
650
+ }),
651
+ procedures: (input) => {
652
+ const procedureInput = input;
653
+ return procedures(procedureInput && typeof procedureInput === "object" && "view" in procedureInput ? {
654
+ ...procedureInput,
655
+ source: adapter.getSource(procedureInput.view)
656
+ } : adapter.getSource(input));
657
+ },
658
+ resolveById: ({ ctx, extra, id, input, view }) => resolveSourceById({
659
+ ctx,
660
+ extra,
661
+ id,
662
+ input,
663
+ registry: adapter.registry,
664
+ source: adapter.getSource(view)
665
+ }),
666
+ resolveByIds: ({ ctx, extra, ids, input, view }) => resolveSourceByIds({
667
+ ctx,
668
+ extra,
669
+ ids,
670
+ input,
671
+ registry: adapter.registry,
672
+ source: adapter.getSource(view)
673
+ }),
674
+ resolveConnection: ({ ctx, cursor, direction, extra, input, skip, take, view }) => resolveSourceConnection({
675
+ ctx,
676
+ cursor,
677
+ direction,
678
+ extra,
679
+ input,
680
+ registry: adapter.registry,
681
+ skip,
682
+ source: adapter.getSource(view),
683
+ take
684
+ })
685
+ };
686
+ }
687
+ //#endregion
688
+ export { createDrizzleFate, createDrizzleSourceAdapter, createDrizzleSourceRegistry };