schemock 0.0.4-alpha.6 → 0.0.4-alpha.7

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/dist/cli/index.js CHANGED
@@ -1928,6 +1928,8 @@ function generateRLSError(code) {
1928
1928
  code.line(");");
1929
1929
  });
1930
1930
  code.line();
1931
+ }
1932
+ function generateNotFoundError(code) {
1931
1933
  code.comment("Helper to create not found error");
1932
1934
  code.block("function createNotFoundError(entity: string, id: string): ApiError {", () => {
1933
1935
  code.line("return new ApiError(");
@@ -2136,6 +2138,7 @@ function generateMockClient(schemas) {
2136
2138
  }
2137
2139
  code.line();
2138
2140
  generateRLSContextType(code);
2141
+ generateNotFoundError(code);
2139
2142
  if (schemasWithRLS) {
2140
2143
  generateRLSError(code);
2141
2144
  generateBypassCheck(code, allBypassConditions);
@@ -2388,154 +2391,154 @@ function generateEntityApiFactory(code, schema, allSchemas, hasRLS) {
2388
2391
  const hasJsonFields = jsonFields.length > 0;
2389
2392
  const jsonFieldsStr = jsonFields.map((f) => `'${f}'`).join(", ");
2390
2393
  code.block(`${name}: {`, () => {
2391
- code.block(
2392
- `list: (options?: Types.QueryOptions<Types.${pascalName}Filter, ${includeType}>) =>`,
2393
- () => {
2394
- code.line(`executeRequest('${name}.list', (ctx) => {`);
2395
- code.indent();
2396
- if (hasJsonFields) {
2397
- code.line(`let rawItems = db.${name}.getAll() as unknown as Record<string, unknown>[];`);
2398
- code.line(`let items = rawItems.map(row => parseRow<Types.${pascalName}>(row, [${jsonFieldsStr}]));`);
2399
- } else {
2400
- code.line(`let items = db.${name}.getAll() as unknown as Types.${pascalName}[];`);
2401
- }
2402
- code.line();
2403
- if (hasRLS) {
2404
- code.comment("Apply RLS filter");
2405
- code.line(`items = items.filter(item => rls${pascalName}Select(item as unknown as Record<string, unknown>, ctx));`);
2406
- code.line();
2407
- }
2408
- code.block("if (options?.where) {", () => {
2409
- code.line("items = applyFilter(items, options.where);");
2410
- });
2411
- code.line();
2412
- code.line("const total = items.length;");
2413
- code.line();
2414
- code.block("if (options?.orderBy) {", () => {
2415
- code.line("const [field, dir] = Object.entries(options.orderBy)[0];");
2416
- code.block("items = [...items].sort((a, b) => {", () => {
2417
- code.line("const aVal = (a as Record<string, unknown>)[field] as string | number | Date;");
2418
- code.line("const bVal = (b as Record<string, unknown>)[field] as string | number | Date;");
2419
- code.line("if (aVal < bVal) return dir === 'asc' ? -1 : 1;");
2420
- code.line("if (aVal > bVal) return dir === 'asc' ? 1 : -1;");
2421
- code.line("return 0;");
2422
- }, "});");
2423
- });
2424
- code.line();
2425
- code.line("const limit = options?.limit ?? 20;");
2426
- code.line("const offset = options?.offset ?? 0;");
2427
- code.line("items = items.slice(offset, offset + limit);");
2428
- code.line();
2429
- if (hasRelations) {
2430
- code.block("if (options?.include?.length) {", () => {
2431
- code.block("items = items.map(item => {", () => {
2432
- code.line("const result = { ...item } as Record<string, unknown>;");
2433
- for (const rel of relations) {
2434
- code.block(`if (options.include!.includes('${rel.name}')) {`, () => {
2435
- generateRelationLoad(code, schema, rel);
2436
- });
2437
- }
2438
- code.line(`return result as Types.${pascalName};`);
2439
- }, "});");
2440
- });
2441
- code.line();
2442
- }
2443
- code.line("return { data: items, meta: { total, limit, offset, hasMore: offset + limit < total } };");
2444
- code.dedent();
2445
- code.line("}),");
2446
- }
2394
+ code.line(
2395
+ `list: (options?: Types.QueryOptions<Types.${pascalName}Filter, ${includeType}>) =>`
2447
2396
  );
2397
+ code.indent();
2398
+ code.line(`executeRequest('${name}.list', (ctx) => {`);
2399
+ code.indent();
2400
+ if (hasJsonFields) {
2401
+ code.line(`let rawItems = db.${name}.getAll() as unknown as Record<string, unknown>[];`);
2402
+ code.line(`let items = rawItems.map(row => parseRow<Types.${pascalName}>(row, [${jsonFieldsStr}]));`);
2403
+ } else {
2404
+ code.line(`let items = db.${name}.getAll() as unknown as Types.${pascalName}[];`);
2405
+ }
2448
2406
  code.line();
2449
- code.block(
2450
- `get: (id: string, options?: { include?: ${includeType}[] }) =>`,
2451
- () => {
2452
- code.line(`executeRequest('${name}.get', (ctx) => {`);
2453
- code.indent();
2454
- code.line(`const rawItem = db.${name}.findFirst({ where: { id: { equals: id } } }) as unknown as Record<string, unknown> | null;`);
2455
- code.line(`if (!rawItem) throw createNotFoundError('${pascalName}', id);`);
2456
- if (hasJsonFields) {
2457
- code.line(`const item = parseRow<Types.${pascalName}>(rawItem, [${jsonFieldsStr}]);`);
2458
- } else {
2459
- code.line(`const item = rawItem as Types.${pascalName};`);
2460
- }
2461
- code.line();
2462
- if (hasRLS) {
2463
- code.comment("Apply RLS check");
2464
- code.block(`if (!rls${pascalName}Select(item as unknown as Record<string, unknown>, ctx)) {`, () => {
2465
- code.line(`throw createRLSError('select', '${pascalName}');`);
2466
- });
2467
- code.line();
2468
- }
2469
- if (hasRelations) {
2407
+ if (hasRLS) {
2408
+ code.comment("Apply RLS filter");
2409
+ code.line(`items = items.filter(item => rls${pascalName}Select(item as unknown as Record<string, unknown>, ctx));`);
2410
+ code.line();
2411
+ }
2412
+ code.block("if (options?.where) {", () => {
2413
+ code.line("items = applyFilter(items, options.where);");
2414
+ });
2415
+ code.line();
2416
+ code.line("const total = items.length;");
2417
+ code.line();
2418
+ code.block("if (options?.orderBy) {", () => {
2419
+ code.line("const [field, dir] = Object.entries(options.orderBy)[0];");
2420
+ code.block("items = [...items].sort((a, b) => {", () => {
2421
+ code.line("const aVal = (a as Record<string, unknown>)[field] as string | number | Date;");
2422
+ code.line("const bVal = (b as Record<string, unknown>)[field] as string | number | Date;");
2423
+ code.line("if (aVal < bVal) return dir === 'asc' ? -1 : 1;");
2424
+ code.line("if (aVal > bVal) return dir === 'asc' ? 1 : -1;");
2425
+ code.line("return 0;");
2426
+ }, "});");
2427
+ });
2428
+ code.line();
2429
+ code.line("const limit = options?.limit ?? 20;");
2430
+ code.line("const offset = options?.offset ?? 0;");
2431
+ code.line("items = items.slice(offset, offset + limit);");
2432
+ code.line();
2433
+ if (hasRelations) {
2434
+ code.block("if (options?.include?.length) {", () => {
2435
+ code.block("items = items.map(item => {", () => {
2470
2436
  code.line("const result = { ...item } as Record<string, unknown>;");
2471
- code.line();
2472
- code.block("if (options?.include?.length) {", () => {
2473
- for (const rel of relations) {
2474
- code.block(`if (options.include.includes('${rel.name}')) {`, () => {
2475
- generateRelationLoad(code, schema, rel);
2476
- });
2477
- }
2437
+ for (const rel of relations) {
2438
+ code.block(`if (options.include!.includes('${rel.name}')) {`, () => {
2439
+ generateRelationLoad(code, schema, rel);
2440
+ });
2441
+ }
2442
+ code.line(`return result as Types.${pascalName};`);
2443
+ }, "});");
2444
+ });
2445
+ code.line();
2446
+ }
2447
+ code.line("return { data: items, meta: { total, limit, offset, hasMore: offset + limit < total } };");
2448
+ code.dedent();
2449
+ code.line("}),");
2450
+ code.dedent();
2451
+ code.line();
2452
+ code.line(`get: (id: string, options?: { include?: ${includeType}[] }) =>`);
2453
+ code.indent();
2454
+ code.line(`executeRequest('${name}.get', (ctx) => {`);
2455
+ code.indent();
2456
+ code.line(`const rawItem = db.${name}.findFirst({ where: { id: { equals: id } } }) as unknown as Record<string, unknown> | null;`);
2457
+ code.line(`if (!rawItem) throw createNotFoundError('${pascalName}', id);`);
2458
+ if (hasJsonFields) {
2459
+ code.line(`const item = parseRow<Types.${pascalName}>(rawItem, [${jsonFieldsStr}]);`);
2460
+ } else {
2461
+ code.line(`const item = rawItem as Types.${pascalName};`);
2462
+ }
2463
+ code.line();
2464
+ if (hasRLS) {
2465
+ code.comment("Apply RLS check");
2466
+ code.block(`if (!rls${pascalName}Select(item as unknown as Record<string, unknown>, ctx)) {`, () => {
2467
+ code.line(`throw createRLSError('select', '${pascalName}');`);
2468
+ });
2469
+ code.line();
2470
+ }
2471
+ if (hasRelations) {
2472
+ code.line("const result = { ...item } as Record<string, unknown>;");
2473
+ code.line();
2474
+ code.block("if (options?.include?.length) {", () => {
2475
+ for (const rel of relations) {
2476
+ code.block(`if (options.include.includes('${rel.name}')) {`, () => {
2477
+ generateRelationLoad(code, schema, rel);
2478
2478
  });
2479
- code.line();
2480
- code.line(`return { data: result as Types.${pascalName} };`);
2481
- } else {
2482
- code.line("return { data: item };");
2483
2479
  }
2484
- code.dedent();
2485
- code.line("}),");
2486
- }
2487
- );
2480
+ });
2481
+ code.line();
2482
+ code.line(`return { data: result as Types.${pascalName} };`);
2483
+ } else {
2484
+ code.line("return { data: item };");
2485
+ }
2486
+ code.dedent();
2487
+ code.line("}),");
2488
+ code.dedent();
2488
2489
  code.line();
2489
2490
  generateCreateMethodFactory(code, schema, hasJsonFields, jsonFieldsStr, hasRLS);
2490
2491
  code.line();
2491
- code.block(`update: (id: string, input: Types.${pascalName}Update) =>`, () => {
2492
- code.line(`executeRequest('${name}.update', (ctx) => {`);
2493
- code.indent();
2494
- if (hasRLS) {
2495
- code.comment("Check RLS before update");
2496
- code.line(`const existing = db.${name}.findFirst({ where: { id: { equals: id } } }) as unknown as Record<string, unknown> | null;`);
2497
- code.line(`if (!existing) throw createNotFoundError('${pascalName}', id);`);
2498
- code.block(`if (!rls${pascalName}Update(existing, ctx)) {`, () => {
2499
- code.line(`throw createRLSError('update', '${pascalName}');`);
2500
- });
2501
- code.line();
2502
- }
2503
- code.line(`const rawItem = db.${name}.update({`);
2504
- code.line(" where: { id: { equals: id } },");
2505
- code.line(` // eslint-disable-next-line @typescript-eslint/no-explicit-any`);
2506
- code.line(" data: { ...input, updatedAt: new Date() } as any,");
2507
- code.line("}) as unknown as Record<string, unknown> | null;");
2508
- if (!hasRLS) {
2509
- code.line(`if (!rawItem) throw createNotFoundError('${pascalName}', id);`);
2510
- }
2511
- if (hasJsonFields) {
2512
- code.line(`return { data: parseRow<Types.${pascalName}>(rawItem!, [${jsonFieldsStr}]) };`);
2513
- } else {
2514
- code.line(`return { data: rawItem as Types.${pascalName} };`);
2515
- }
2516
- code.dedent();
2517
- code.line("}),");
2518
- });
2492
+ code.line(`update: (id: string, input: Types.${pascalName}Update) =>`);
2493
+ code.indent();
2494
+ code.line(`executeRequest('${name}.update', (ctx) => {`);
2495
+ code.indent();
2496
+ if (hasRLS) {
2497
+ code.comment("Check RLS before update");
2498
+ code.line(`const existing = db.${name}.findFirst({ where: { id: { equals: id } } }) as unknown as Record<string, unknown> | null;`);
2499
+ code.line(`if (!existing) throw createNotFoundError('${pascalName}', id);`);
2500
+ code.block(`if (!rls${pascalName}Update(existing, ctx)) {`, () => {
2501
+ code.line(`throw createRLSError('update', '${pascalName}');`);
2502
+ });
2503
+ code.line();
2504
+ }
2505
+ code.line(`const rawItem = db.${name}.update({`);
2506
+ code.line(" where: { id: { equals: id } },");
2507
+ code.line(` // eslint-disable-next-line @typescript-eslint/no-explicit-any`);
2508
+ code.line(" data: { ...input, updatedAt: new Date() } as any,");
2509
+ code.line("}) as unknown as Record<string, unknown> | null;");
2510
+ if (!hasRLS) {
2511
+ code.line(`if (!rawItem) throw createNotFoundError('${pascalName}', id);`);
2512
+ }
2513
+ if (hasJsonFields) {
2514
+ code.line(`return { data: parseRow<Types.${pascalName}>(rawItem!, [${jsonFieldsStr}]) };`);
2515
+ } else {
2516
+ code.line(`return { data: rawItem as Types.${pascalName} };`);
2517
+ }
2518
+ code.dedent();
2519
+ code.line("}),");
2520
+ code.dedent();
2519
2521
  code.line();
2520
- code.block("delete: (id: string) =>", () => {
2521
- code.line(`executeRequest('${name}.delete', (ctx) => {`);
2522
- code.indent();
2523
- if (hasRLS) {
2524
- code.comment("Check RLS before delete");
2525
- code.line(`const existing = db.${name}.findFirst({ where: { id: { equals: id } } }) as unknown as Record<string, unknown> | null;`);
2526
- code.line(`if (!existing) throw createNotFoundError('${pascalName}', id);`);
2527
- code.block(`if (!rls${pascalName}Delete(existing, ctx)) {`, () => {
2528
- code.line(`throw createRLSError('delete', '${pascalName}');`);
2529
- });
2530
- code.line();
2531
- }
2532
- code.line(`const item = db.${name}.delete({ where: { id: { equals: id } } });`);
2533
- if (!hasRLS) {
2534
- code.line(`if (!item) throw createNotFoundError('${pascalName}', id);`);
2535
- }
2536
- code.dedent();
2537
- code.line("}),");
2538
- });
2522
+ code.line("delete: (id: string) =>");
2523
+ code.indent();
2524
+ code.line(`executeRequest('${name}.delete', (ctx) => {`);
2525
+ code.indent();
2526
+ if (hasRLS) {
2527
+ code.comment("Check RLS before delete");
2528
+ code.line(`const existing = db.${name}.findFirst({ where: { id: { equals: id } } }) as unknown as Record<string, unknown> | null;`);
2529
+ code.line(`if (!existing) throw createNotFoundError('${pascalName}', id);`);
2530
+ code.block(`if (!rls${pascalName}Delete(existing, ctx)) {`, () => {
2531
+ code.line(`throw createRLSError('delete', '${pascalName}');`);
2532
+ });
2533
+ code.line();
2534
+ }
2535
+ code.line(`const item = db.${name}.delete({ where: { id: { equals: id } } });`);
2536
+ if (!hasRLS) {
2537
+ code.line(`if (!item) throw createNotFoundError('${pascalName}', id);`);
2538
+ }
2539
+ code.dedent();
2540
+ code.line("}),");
2541
+ code.dedent();
2539
2542
  }, "},");
2540
2543
  code.line();
2541
2544
  }
@@ -2568,69 +2571,70 @@ function generateRelationLoad(code, schema, rel) {
2568
2571
  function generateCreateMethodFactory(code, schema, hasJsonFields, jsonFieldsStr, hasRLS) {
2569
2572
  const { name, pascalName, relations } = schema;
2570
2573
  const nestedRels = relations.filter((r) => r.type === "hasMany" || r.type === "hasOne");
2571
- code.block(`create: (input: Types.${pascalName}Create) =>`, () => {
2572
- code.line(`executeRequest('${name}.create', (ctx) => {`);
2573
- code.indent();
2574
- if (nestedRels.length > 0) {
2575
- const relNames = nestedRels.map((r) => r.name).join(", ");
2576
- code.line(`const { ${relNames}, ...data } = input;`);
2577
- code.line();
2578
- code.line(`// eslint-disable-next-line @typescript-eslint/no-explicit-any`);
2579
- code.line(`const rawItem = db.${name}.create(data as any) as unknown as Record<string, unknown>;`);
2580
- if (hasJsonFields) {
2581
- code.line(`const item = parseRow<Types.${pascalName}>(rawItem, [${jsonFieldsStr}]);`);
2582
- } else {
2583
- code.line(`const item = rawItem as Types.${pascalName};`);
2584
- }
2574
+ code.line(`create: (input: Types.${pascalName}Create) =>`);
2575
+ code.indent();
2576
+ code.line(`executeRequest('${name}.create', (ctx) => {`);
2577
+ code.indent();
2578
+ if (nestedRels.length > 0) {
2579
+ const relNames = nestedRels.map((r) => r.name).join(", ");
2580
+ code.line(`const { ${relNames}, ...data } = input;`);
2581
+ code.line();
2582
+ code.line(`// eslint-disable-next-line @typescript-eslint/no-explicit-any`);
2583
+ code.line(`const rawItem = db.${name}.create(data as any) as unknown as Record<string, unknown>;`);
2584
+ if (hasJsonFields) {
2585
+ code.line(`const item = parseRow<Types.${pascalName}>(rawItem, [${jsonFieldsStr}]);`);
2586
+ } else {
2587
+ code.line(`const item = rawItem as Types.${pascalName};`);
2588
+ }
2589
+ code.line();
2590
+ if (hasRLS) {
2591
+ code.comment("Check RLS on created item");
2592
+ code.block(`if (!rls${pascalName}Insert(item as unknown as Record<string, unknown>, ctx)) {`, () => {
2593
+ code.comment("Rollback by deleting");
2594
+ code.line(`db.${name}.delete({ where: { id: { equals: item.id } } });`);
2595
+ code.line(`throw createRLSError('insert', '${pascalName}');`);
2596
+ });
2585
2597
  code.line();
2586
- if (hasRLS) {
2587
- code.comment("Check RLS on created item");
2588
- code.block(`if (!rls${pascalName}Insert(item as unknown as Record<string, unknown>, ctx)) {`, () => {
2589
- code.comment("Rollback by deleting");
2590
- code.line(`db.${name}.delete({ where: { id: { equals: item.id } } });`);
2591
- code.line(`throw createRLSError('insert', '${pascalName}');`);
2592
- });
2593
- code.line();
2594
- }
2595
- for (const rel of nestedRels) {
2596
- const targetDbName = toSafePropertyName(rel.resolvedTarget);
2597
- code.block(`if (${rel.name}) {`, () => {
2598
- if (rel.type === "hasMany") {
2599
- code.block(`for (const nested of ${rel.name}) {`, () => {
2600
- code.line(`// eslint-disable-next-line @typescript-eslint/no-explicit-any`);
2601
- code.line(`db.${targetDbName}.create({ ...nested, ${rel.foreignKey}: item.id } as any);`);
2602
- });
2603
- } else {
2598
+ }
2599
+ for (const rel of nestedRels) {
2600
+ const targetDbName = toSafePropertyName(rel.resolvedTarget);
2601
+ code.block(`if (${rel.name}) {`, () => {
2602
+ if (rel.type === "hasMany") {
2603
+ code.block(`for (const nested of ${rel.name}) {`, () => {
2604
2604
  code.line(`// eslint-disable-next-line @typescript-eslint/no-explicit-any`);
2605
- code.line(`db.${targetDbName}.create({ ...${rel.name}, ${rel.foreignKey}: item.id } as any);`);
2606
- }
2607
- });
2608
- }
2609
- code.line();
2610
- code.line("return { data: item };");
2605
+ code.line(`db.${targetDbName}.create({ ...nested, ${rel.foreignKey}: item.id } as any);`);
2606
+ });
2607
+ } else {
2608
+ code.line(`// eslint-disable-next-line @typescript-eslint/no-explicit-any`);
2609
+ code.line(`db.${targetDbName}.create({ ...${rel.name}, ${rel.foreignKey}: item.id } as any);`);
2610
+ }
2611
+ });
2612
+ }
2613
+ code.line();
2614
+ code.line("return { data: item };");
2615
+ } else {
2616
+ code.line(`// eslint-disable-next-line @typescript-eslint/no-explicit-any`);
2617
+ code.line(`const rawItem = db.${name}.create(input as any) as unknown as Record<string, unknown>;`);
2618
+ if (hasJsonFields) {
2619
+ code.line(`const item = parseRow<Types.${pascalName}>(rawItem, [${jsonFieldsStr}]);`);
2611
2620
  } else {
2612
- code.line(`// eslint-disable-next-line @typescript-eslint/no-explicit-any`);
2613
- code.line(`const rawItem = db.${name}.create(input as any) as unknown as Record<string, unknown>;`);
2614
- if (hasJsonFields) {
2615
- code.line(`const item = parseRow<Types.${pascalName}>(rawItem, [${jsonFieldsStr}]);`);
2616
- } else {
2617
- code.line(`const item = rawItem as Types.${pascalName};`);
2618
- }
2619
- if (hasRLS) {
2620
- code.line();
2621
- code.comment("Check RLS on created item");
2622
- code.block(`if (!rls${pascalName}Insert(item as unknown as Record<string, unknown>, ctx)) {`, () => {
2623
- code.comment("Rollback by deleting");
2624
- code.line(`db.${name}.delete({ where: { id: { equals: item.id } } });`);
2625
- code.line(`throw createRLSError('insert', '${pascalName}');`);
2626
- });
2627
- }
2621
+ code.line(`const item = rawItem as Types.${pascalName};`);
2622
+ }
2623
+ if (hasRLS) {
2628
2624
  code.line();
2629
- code.line("return { data: item };");
2625
+ code.comment("Check RLS on created item");
2626
+ code.block(`if (!rls${pascalName}Insert(item as unknown as Record<string, unknown>, ctx)) {`, () => {
2627
+ code.comment("Rollback by deleting");
2628
+ code.line(`db.${name}.delete({ where: { id: { equals: item.id } } });`);
2629
+ code.line(`throw createRLSError('insert', '${pascalName}');`);
2630
+ });
2630
2631
  }
2631
- code.dedent();
2632
- code.line("}),");
2633
- });
2632
+ code.line();
2633
+ code.line("return { data: item };");
2634
+ }
2635
+ code.dedent();
2636
+ code.line("}),");
2637
+ code.dedent();
2634
2638
  }
2635
2639
 
2636
2640
  // src/cli/generators/mock/seed.ts