@nkzw/fate 0.0.7 → 0.1.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/lib/cli.mjs CHANGED
@@ -7,11 +7,11 @@ import { styleText } from "node:util";
7
7
  const isDataViewField = (field) => Boolean(field) && typeof field === "object" && "fields" in field;
8
8
  /**
9
9
  * Builds the schema object used by the CLI generator from your data views and
10
- * list resolver configs.
10
+ * root resolver configs.
11
11
  */
12
- const createSchema = (dataViews, lists) => {
12
+ const createSchema = (dataViews, roots) => {
13
13
  const canonicalViews = /* @__PURE__ */ new Map();
14
- const entities = {};
14
+ const rootSchema = {};
15
15
  const fateTypes = /* @__PURE__ */ new Map();
16
16
  const processing = /* @__PURE__ */ new Set();
17
17
  const ensureType = (view) => {
@@ -36,20 +36,25 @@ const createSchema = (dataViews, lists) => {
36
36
  const typeName = view.typeName;
37
37
  if (!typeName) throw new Error("Data view is missing a type name.");
38
38
  if (!canonicalViews.has(typeName)) canonicalViews.set(typeName, view);
39
- entities[typeName.toLowerCase()] = { type: typeName };
40
39
  }
41
- for (const [name, list] of Object.entries(lists)) {
42
- const config = "fields" in list ? { view: list } : list;
43
- const typeName = ensureType(config.view);
44
- entities[typeName.toLowerCase()] = {
45
- list: name,
46
- listProcedure: config.procedure,
47
- type: typeName
40
+ for (const view of dataViews) ensureType(view);
41
+ for (const [name, root$1] of Object.entries(roots)) {
42
+ const config = "fields" in root$1 ? { view: root$1 } : root$1;
43
+ const view = config.view;
44
+ const type = ensureType(view);
45
+ if (!view.typeName) throw new Error(`Root "${name}" is missing a data view.`);
46
+ const router = config.router ?? view.typeName[0]?.toLowerCase() + view.typeName.slice(1);
47
+ if (!router) throw new Error(`Root "${name}" is missing a router name.`);
48
+ rootSchema[name] = {
49
+ kind: view.kind === "list" ? "list" : "query",
50
+ procedure: config.procedure ?? (view.kind === "list" ? "list" : name),
51
+ router,
52
+ type
48
53
  };
49
54
  }
50
55
  for (const view of dataViews) ensureType(view);
51
56
  return {
52
- entities,
57
+ roots: rootSchema,
53
58
  types: Array.from(fateTypes.values())
54
59
  };
55
60
  };
@@ -89,21 +94,29 @@ const formatTypes = (types) => {
89
94
  const indentBlock = (value, spaces) => value.split("\n").map((line) => line.length ? `${" ".repeat(spaces)}${line}` : line).join("\n");
90
95
  const generate = async () => {
91
96
  console.log(styleText("bold", `Generating fate client…\n`));
92
- const { appRouter, Lists, ...dataViews } = await import(moduleName);
93
- const { entities, types } = createSchema(Object.values(dataViews), Lists);
97
+ const { appRouter, Root, ...dataViews } = await import(moduleName);
98
+ const { roots, types } = createSchema(Object.values(dataViews), Root ?? {});
94
99
  const routerRecord = appRouter._def?.record ?? {};
95
100
  const mutationEntries = [];
96
101
  const byIdEntries = [];
97
102
  const listEntries = [];
103
+ const queryEntries = [];
104
+ const rootsByRouter = /* @__PURE__ */ new Map();
105
+ for (const entry of Object.entries(roots)) {
106
+ const list = rootsByRouter.get(entry[1].router) ?? [];
107
+ list.push(entry);
108
+ rootsByRouter.set(entry[1].router, list);
109
+ }
98
110
  for (const [router, procedures] of Object.entries(routerRecord)) {
99
- const entity = entities[router];
100
- if (!entity) continue;
111
+ const routerRoots = rootsByRouter.get(router);
112
+ if (!routerRoots?.length) continue;
113
+ const entityType = routerRoots[0][1].type;
101
114
  for (const [procedureName, procedure] of Object.entries(procedures)) {
102
115
  const type = procedure?._def?.type;
103
116
  if (!type) continue;
104
117
  if (type === "mutation") {
105
118
  mutationEntries.push({
106
- entityType: entity.type,
119
+ entityType,
107
120
  name: `${router}.${procedureName}`,
108
121
  procedure: procedureName,
109
122
  router
@@ -112,22 +125,53 @@ const generate = async () => {
112
125
  }
113
126
  if (procedureName === "byId" && type === "query") {
114
127
  byIdEntries.push({
115
- entityType: entity.type,
128
+ entityType,
116
129
  router
117
130
  });
118
131
  continue;
119
132
  }
120
- const listProcedure = entity.listProcedure ?? "list";
121
- if (procedureName === listProcedure && type === "query" && entity.list) listEntries.push({
122
- list: entity.list,
123
- procedure: listProcedure,
124
- router
125
- });
133
+ for (const [queryName, root$1] of routerRoots) {
134
+ if (root$1.kind !== "query") continue;
135
+ if (procedureName === root$1.procedure && type === "query") queryEntries.push({
136
+ name: queryName,
137
+ procedure: root$1.procedure,
138
+ router,
139
+ type: root$1.type
140
+ });
141
+ }
142
+ for (const [listName, root$1] of routerRoots) {
143
+ if (root$1.kind !== "list") continue;
144
+ if (procedureName === root$1.procedure && type === "query") listEntries.push({
145
+ list: listName,
146
+ procedure: root$1.procedure,
147
+ router,
148
+ type: root$1.type
149
+ });
150
+ }
126
151
  }
127
152
  }
128
153
  mutationEntries.sort((a, b) => a.name.localeCompare(b.name));
129
154
  byIdEntries.sort((a, b) => a.entityType.localeCompare(b.entityType));
155
+ queryEntries.sort((a, b) => a.name.localeCompare(b.name));
130
156
  listEntries.sort((a, b) => a.list.localeCompare(b.list));
157
+ const rootEntries = [
158
+ ...byIdEntries.map(({ entityType, router }) => ({
159
+ name: router,
160
+ type: entityType,
161
+ value: `'${router}': clientRoot<RouterOutputs['${router}']['byId'], '${entityType}'>('${entityType}'),`
162
+ })),
163
+ ...queryEntries.map(({ name, procedure, router, type }) => ({
164
+ name,
165
+ type,
166
+ value: `'${name}': clientRoot<RouterOutputs['${router}']['${procedure}'], '${type}'>('${type}'),`
167
+ })),
168
+ ...listEntries.map(({ list, procedure, router, type }) => ({
169
+ name: list,
170
+ type,
171
+ value: `'${list}': clientRoot<RouterOutputs['${router}']['${procedure}'], '${type}'>('${type}'),`
172
+ }))
173
+ ];
174
+ rootEntries.sort((a, b) => a.name.localeCompare(b.name));
131
175
  const viewTypes = Array.from(["AppRouter", ...new Set(mutationEntries.map((entry) => entry.entityType))]).sort();
132
176
  const mutationResolverLines = mutationEntries.map(({ name, procedure, router }) => `'${name}': (client: TRPCClientType) => client.${router}.${procedure}.mutate,`);
133
177
  const mutationConfigLines = mutationEntries.map(({ entityType, name, procedure, router }) => `'${name}': mutation<
@@ -146,17 +190,21 @@ const generate = async () => {
146
190
  select,
147
191
  }),`);
148
192
  const listLines = listEntries.map(({ list, procedure, router }) => `${list}: (client: TRPCClientType) => client.${router}.${procedure}.query,`);
193
+ const queryLines = queryEntries.map(({ name, procedure, router }) => `${name}: (client: TRPCClientType) => client.${router}.${procedure}.query,`);
149
194
  const typeImports = `import type { ${viewTypes.join(", ")} } from '${moduleName}';`;
150
195
  const typesBlock = indentBlock(formatTypes(types), 6);
151
196
  const mutationResolverBlock = indentBlock(mutationResolverLines.join("\n"), 4);
152
197
  const mutationConfigBlock = indentBlock(mutationConfigLines.join("\n"), 2);
153
198
  const byIdBlock = indentBlock(byIdLines.join("\n"), 8);
199
+ const rootsBlock = indentBlock(rootEntries.map((entry) => entry.value).join("\n"), 2);
154
200
  const listsBlockContent = listLines.join("\n");
201
+ const listsBlock = listLines.length ? ` lists: {\n${indentBlock(listsBlockContent, 8)}\n },\n` : "";
202
+ const queriesBlockContent = queryLines.join("\n");
155
203
  const source = `// @generated by \`pnpm fate generate\`
156
204
  ${typeImports}
157
205
  import { createTRPCProxyClient } from '@trpc/client';
158
206
  import { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
159
- import { createClient, createTRPCTransport, mutation } from 'react-fate';
207
+ import { clientRoot, createClient, createTRPCTransport, mutation } from 'react-fate';
160
208
 
161
209
  type TRPCClientType = ReturnType<typeof createTRPCProxyClient<AppRouter>>;
162
210
  type RouterInputs = inferRouterInputs<AppRouter>;
@@ -166,10 +214,16 @@ const mutations = {
166
214
  ${mutationConfigBlock}
167
215
  } as const;
168
216
 
217
+ const roots = {
218
+ ${rootsBlock}
219
+ } as const;
220
+
169
221
  type GeneratedClientMutations = typeof mutations;
222
+ type GeneratedClientRoots = typeof roots;
170
223
 
171
224
  declare module 'react-fate' {
172
225
  interface ClientMutations extends GeneratedClientMutations {}
226
+ interface ClientRoots extends GeneratedClientRoots {}
173
227
  }
174
228
 
175
229
  export const createFateClient = (options: {
@@ -181,14 +235,15 @@ export const createFateClient = (options: {
181
235
  ${mutationResolverBlock}
182
236
  } as const;
183
237
 
184
- return createClient({
238
+ return createClient<[GeneratedClientRoots, GeneratedClientMutations]>({
185
239
  mutations,
240
+ roots,
186
241
  transport: createTRPCTransport<AppRouter, typeof trpcMutations>({
187
242
  byId: {
188
243
  ${byIdBlock}
189
244
  },
190
245
  client: trpcClient,
191
- ${listLines.length ? ` lists: {\n${indentBlock(listsBlockContent, 8)}\n },\n` : ""} mutations: trpcMutations,
246
+ ${queryLines.length ? ` queries: {\n${indentBlock(queriesBlockContent, 8)}\n },\n` : ""}${listsBlock} mutations: trpcMutations,
192
247
  }),
193
248
  types: ${typesBlock.trimStart()},
194
249
  });