schemock 0.0.4-alpha.7 → 0.0.4-alpha.8

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
@@ -1148,6 +1148,56 @@ const user = await api.user.get('123');
1148
1148
  await api.post.create({ title: 'Hello', authorId: '123' });
1149
1149
  ```
1150
1150
 
1151
+ ### Using with React Hooks (SchemockProvider)
1152
+
1153
+ To use the configured client with generated React hooks, wrap your app with `SchemockProvider`:
1154
+
1155
+ ```tsx
1156
+ import { SchemockProvider, createClient, useUsers, useCreateUser } from './generated';
1157
+
1158
+ // 1. Create configured client
1159
+ const api = createClient({
1160
+ onRequest: (ctx) => {
1161
+ const token = localStorage.getItem('authToken');
1162
+ if (token) {
1163
+ ctx.headers.Authorization = `Bearer ${token}`;
1164
+ }
1165
+ return ctx;
1166
+ },
1167
+ onError: (error) => {
1168
+ if (error.status === 401) window.location.href = '/login';
1169
+ }
1170
+ });
1171
+
1172
+ // 2. Wrap your app
1173
+ function App() {
1174
+ return (
1175
+ <SchemockProvider client={api}>
1176
+ <UserList />
1177
+ </SchemockProvider>
1178
+ );
1179
+ }
1180
+
1181
+ // 3. Hooks automatically use the configured client
1182
+ function UserList() {
1183
+ const { data, isLoading } = useUsers();
1184
+ const createUser = useCreateUser();
1185
+
1186
+ if (isLoading) return <div>Loading...</div>;
1187
+
1188
+ return (
1189
+ <div>
1190
+ {data?.data.map(user => <div key={user.id}>{user.name}</div>)}
1191
+ <button onClick={() => createUser.mutate({ name: 'New User' })}>
1192
+ Add User
1193
+ </button>
1194
+ </div>
1195
+ );
1196
+ }
1197
+ ```
1198
+
1199
+ Without `SchemockProvider`, hooks use the default unconfigured client (no auth).
1200
+
1151
1201
  ### Creating Mock JWT Tokens
1152
1202
 
1153
1203
  For testing different user contexts:
package/dist/cli/index.js CHANGED
@@ -4285,7 +4285,7 @@ function generateHooks(schemas) {
4285
4285
  code.comment("GENERATED BY SCHEMOCK - DO NOT EDIT");
4286
4286
  code.line("import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';");
4287
4287
  code.line("import { useMemo } from 'react';");
4288
- code.line("import { api } from './client';");
4288
+ code.line("import { useSchemockClient } from './provider';");
4289
4289
  code.line("import type * as Types from './types';");
4290
4290
  code.line();
4291
4291
  generateStableKeyHelper(code);
@@ -4362,6 +4362,7 @@ function generateEntityHooks(code, schema) {
4362
4362
  code.line("enabled?: boolean;");
4363
4363
  }, "}) {");
4364
4364
  code.indent();
4365
+ code.line("const api = useSchemockClient();");
4365
4366
  code.comment("Use stable query key to prevent unnecessary refetches");
4366
4367
  code.line(`const queryKey = useStableQueryKey('${pluralName}', options);`);
4367
4368
  code.block("return useQuery({", () => {
@@ -4380,6 +4381,7 @@ function generateEntityHooks(code, schema) {
4380
4381
  code.line("enabled?: boolean;");
4381
4382
  }, "}) {");
4382
4383
  code.indent();
4384
+ code.line("const api = useSchemockClient();");
4383
4385
  if (hasRelations) {
4384
4386
  code.comment("Use stable query key for includes array");
4385
4387
  code.line(`const queryKey = useStableQueryKey('${pluralName}', id, options?.include);`);
@@ -4407,6 +4409,7 @@ function generateEntityHooks(code, schema) {
4407
4409
  }
4408
4410
  code.docComment(`Create a new ${pascalName}`);
4409
4411
  code.block(`export function useCreate${pascalName}() {`, () => {
4412
+ code.line("const api = useSchemockClient();");
4410
4413
  code.line("const queryClient = useQueryClient();");
4411
4414
  code.block("return useMutation({", () => {
4412
4415
  code.line(`mutationFn: (data: Types.${pascalName}Create) => api.${name}.create(data),`);
@@ -4418,6 +4421,7 @@ function generateEntityHooks(code, schema) {
4418
4421
  code.line();
4419
4422
  code.docComment(`Update an existing ${pascalName}`);
4420
4423
  code.block(`export function useUpdate${pascalName}() {`, () => {
4424
+ code.line("const api = useSchemockClient();");
4421
4425
  code.line("const queryClient = useQueryClient();");
4422
4426
  code.block("return useMutation({", () => {
4423
4427
  code.line(`mutationFn: ({ id, data }: { id: string; data: Types.${pascalName}Update }) =>`);
@@ -4431,6 +4435,7 @@ function generateEntityHooks(code, schema) {
4431
4435
  code.line();
4432
4436
  code.docComment(`Delete a ${pascalName}`);
4433
4437
  code.block(`export function useDelete${pascalName}() {`, () => {
4438
+ code.line("const api = useSchemockClient();");
4434
4439
  code.line("const queryClient = useQueryClient();");
4435
4440
  code.block("return useMutation({", () => {
4436
4441
  code.line(`mutationFn: (id: string) => api.${name}.delete(id),`);
@@ -4442,6 +4447,113 @@ function generateEntityHooks(code, schema) {
4442
4447
  code.line();
4443
4448
  }
4444
4449
 
4450
+ // src/cli/generators/provider.ts
4451
+ function generateProvider() {
4452
+ const code = new CodeBuilder();
4453
+ code.comment("GENERATED BY SCHEMOCK - DO NOT EDIT");
4454
+ code.line("import { createContext, useContext, type ReactNode } from 'react';");
4455
+ code.line("import { type ApiClient, api as defaultApi } from './client';");
4456
+ code.line();
4457
+ code.multiDocComment([
4458
+ "React Context for the Schemock API client.",
4459
+ "",
4460
+ "This context provides the API client to all hooks in the component tree.",
4461
+ "By default, it uses the unconfigured `api` client."
4462
+ ]);
4463
+ code.line("const SchemockContext = createContext<ApiClient>(defaultApi);");
4464
+ code.line();
4465
+ code.multiDocComment([
4466
+ "Props for the SchemockProvider component."
4467
+ ]);
4468
+ code.block("export interface SchemockProviderProps {", () => {
4469
+ code.docComment("Optional configured API client. If not provided, uses the default unconfigured client.");
4470
+ code.line("client?: ApiClient;");
4471
+ code.docComment("Child components that will have access to the API client.");
4472
+ code.line("children: ReactNode;");
4473
+ });
4474
+ code.line();
4475
+ code.multiDocComment([
4476
+ "Provider component for injecting a configured API client into hooks.",
4477
+ "",
4478
+ "Use this to provide an auth-configured client to all Schemock hooks.",
4479
+ "",
4480
+ "@example",
4481
+ "```tsx",
4482
+ "import { SchemockProvider, createClient } from './generated';",
4483
+ "",
4484
+ "// Create a client with auth interceptors",
4485
+ "const api = createClient({",
4486
+ " onRequest: (ctx) => {",
4487
+ ' const token = localStorage.getItem("authToken");',
4488
+ " if (token) {",
4489
+ " ctx.headers.Authorization = `Bearer ${token}`;",
4490
+ " }",
4491
+ " return ctx;",
4492
+ " },",
4493
+ " onError: (error) => {",
4494
+ " if (error.status === 401) {",
4495
+ ' window.location.href = "/login";',
4496
+ " }",
4497
+ " }",
4498
+ "});",
4499
+ "",
4500
+ "// Wrap your app with the provider",
4501
+ "function App() {",
4502
+ " return (",
4503
+ " <SchemockProvider client={api}>",
4504
+ " <MyComponent />",
4505
+ " </SchemockProvider>",
4506
+ " );",
4507
+ "}",
4508
+ "",
4509
+ "// Now all hooks automatically use the configured client",
4510
+ "function MyComponent() {",
4511
+ " const { data } = useUsers(); // Uses auth-configured client",
4512
+ " return <div>{data?.data.map(u => u.name)}</div>;",
4513
+ "}",
4514
+ "```"
4515
+ ]);
4516
+ code.block("export function SchemockProvider({ client, children }: SchemockProviderProps): JSX.Element {", () => {
4517
+ code.line("return (");
4518
+ code.line(" <SchemockContext.Provider value={client ?? defaultApi}>");
4519
+ code.line(" {children}");
4520
+ code.line(" </SchemockContext.Provider>");
4521
+ code.line(");");
4522
+ });
4523
+ code.line();
4524
+ code.multiDocComment([
4525
+ "Hook to access the API client from context.",
4526
+ "",
4527
+ "This is used internally by generated hooks to access the configured client.",
4528
+ "You can also use it directly if you need to call API methods outside of hooks.",
4529
+ "",
4530
+ "@returns The API client from context (or default if no provider)",
4531
+ "",
4532
+ "@example",
4533
+ "```tsx",
4534
+ "import { useSchemockClient } from './generated';",
4535
+ "",
4536
+ "function MyComponent() {",
4537
+ " const api = useSchemockClient();",
4538
+ "",
4539
+ " const handleClick = async () => {",
4540
+ " // Direct API call using the configured client",
4541
+ ' await api.user.create({ name: "John" });',
4542
+ " };",
4543
+ "",
4544
+ " return <button onClick={handleClick}>Create User</button>;",
4545
+ "}",
4546
+ "```"
4547
+ ]);
4548
+ code.block("export function useSchemockClient(): ApiClient {", () => {
4549
+ code.line("return useContext(SchemockContext);");
4550
+ });
4551
+ code.line();
4552
+ code.comment("Re-export client types for convenience");
4553
+ code.line("export type { ApiClient } from './client';");
4554
+ return code.toString();
4555
+ }
4556
+
4445
4557
  // src/cli/generators/form-schemas.ts
4446
4558
  function generateFormSchemas(schemas) {
4447
4559
  const code = new CodeBuilder();
@@ -6506,6 +6618,9 @@ async function generateClientTarget(target, schemas, endpoints, config, options)
6506
6618
  console.log(" \u26A0\uFE0F GraphQL target not yet implemented");
6507
6619
  break;
6508
6620
  }
6621
+ const providerCode = generateProvider();
6622
+ await writeOutput3(path.join(outputDir, "provider.ts"), providerCode, options.dryRun);
6623
+ files.push("provider.ts");
6509
6624
  const hooksCode = generateHooks(targetSchemas);
6510
6625
  await writeOutput3(path.join(outputDir, "hooks.ts"), hooksCode, options.dryRun);
6511
6626
  files.push("hooks.ts");
@@ -6632,7 +6747,8 @@ function generateClientIndex(targetType, hasEndpoints = false) {
6632
6747
  "",
6633
6748
  "export * from './types';",
6634
6749
  "export * from './hooks';",
6635
- "export { api } from './client';"
6750
+ "export * from './provider';",
6751
+ "export { api, createClient } from './client';"
6636
6752
  ];
6637
6753
  if (targetType === "mock") {
6638
6754
  lines.push("export { db } from './db';");
@@ -6801,6 +6917,10 @@ async function generate(options) {
6801
6917
  default:
6802
6918
  throw new Error(`Unknown adapter: ${adapter}`);
6803
6919
  }
6920
+ console.log("\n\u{1F3A3} Generating React Context provider...");
6921
+ const providerCode = generateProvider();
6922
+ await writeOutput4(path.join(outputDir, "provider.ts"), providerCode, options.dryRun);
6923
+ console.log(" \u2713 provider.ts (SchemockProvider + useSchemockClient)");
6804
6924
  console.log("\n\u269B\uFE0F Generating React hooks...");
6805
6925
  const hooksCode = generateHooks(analyzed);
6806
6926
  await writeOutput4(path.join(outputDir, "hooks.ts"), hooksCode, options.dryRun);
@@ -6892,7 +7012,8 @@ function generateIndex(adapter, hasEndpoints = false) {
6892
7012
  "",
6893
7013
  "export * from './types';",
6894
7014
  "export * from './hooks';",
6895
- "export { api } from './client';"
7015
+ "export * from './provider';",
7016
+ "export { api, createClient } from './client';"
6896
7017
  ];
6897
7018
  if (adapter === "mock") {
6898
7019
  lines.push("export { db } from './db';");