schemock 0.0.3-alpha.1 → 0.0.4-alpha.2

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 (37) hide show
  1. package/README.md +98 -0
  2. package/dist/adapters/index.d.mts +4 -4
  3. package/dist/adapters/index.d.ts +4 -4
  4. package/dist/cli/index.d.mts +18 -2
  5. package/dist/cli/index.d.ts +18 -2
  6. package/dist/cli/index.js +66 -14
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/cli/index.mjs +66 -14
  9. package/dist/cli/index.mjs.map +1 -1
  10. package/dist/cli.js +108 -19
  11. package/dist/index.d.mts +3 -3
  12. package/dist/index.d.ts +3 -3
  13. package/dist/index.js +6 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/index.mjs +6 -1
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/middleware/index.d.mts +4 -4
  18. package/dist/middleware/index.d.ts +4 -4
  19. package/dist/react/index.d.mts +3 -3
  20. package/dist/react/index.d.ts +3 -3
  21. package/dist/runtime/index.d.mts +2 -2
  22. package/dist/runtime/index.d.ts +2 -2
  23. package/dist/runtime/index.js.map +1 -1
  24. package/dist/runtime/index.mjs.map +1 -1
  25. package/dist/schema/index.d.mts +2 -2
  26. package/dist/schema/index.d.ts +2 -2
  27. package/dist/schema/index.js +6 -1
  28. package/dist/schema/index.js.map +1 -1
  29. package/dist/schema/index.mjs +6 -1
  30. package/dist/schema/index.mjs.map +1 -1
  31. package/dist/{types-c2AN3vky.d.ts → types-4EDTne0h.d.ts} +1 -1
  32. package/dist/{types-C2bd2vgy.d.mts → types-CWZQ6vqt.d.mts} +65 -0
  33. package/dist/{types-C2bd2vgy.d.ts → types-CWZQ6vqt.d.ts} +65 -0
  34. package/dist/{types-DV2DS7wj.d.mts → types-D-u5uw1E.d.mts} +1 -1
  35. package/dist/{types-C1MiZh1d.d.ts → types-Ku_ykMzx.d.ts} +1 -1
  36. package/dist/{types-C9VMgu3E.d.mts → types-vPJ91BuY.d.mts} +1 -1
  37. package/package.json +6 -1
package/README.md CHANGED
@@ -652,6 +652,104 @@ npx schemock generate --exclude audit,log
652
652
 
653
653
  **Note:** Types always include all entities to preserve relations (e.g., `User.posts`). Only CRUD operations/routes are filtered.
654
654
 
655
+ ### Entity Tagging
656
+
657
+ Tags provide flexible, freeform categorization for filtering entities across targets. Unlike `entities`/`excludeEntities` which use explicit names, tags let you organize schemas by domain, access level, or any custom taxonomy.
658
+
659
+ **Adding tags to schemas:**
660
+
661
+ ```typescript
662
+ const User = defineData('user', {
663
+ id: field.uuid(),
664
+ email: field.email(),
665
+ }, {
666
+ tags: ['auth', 'public', 'core'],
667
+ module: 'identity',
668
+ group: 'public',
669
+ });
670
+
671
+ const AuditLog = defineData('auditLog', {
672
+ id: field.uuid(),
673
+ action: field.string(),
674
+ }, {
675
+ tags: ['internal', 'compliance'],
676
+ module: 'security',
677
+ group: 'internal',
678
+ });
679
+ ```
680
+
681
+ **Filtering by tags in targets:**
682
+
683
+ ```typescript
684
+ targets: [
685
+ {
686
+ name: 'public-api',
687
+ type: 'nextjs-api',
688
+ output: './src/app/api/public',
689
+ tags: ['public'], // Include entities with 'public' tag
690
+ excludeTags: ['internal'], // Exclude entities with 'internal' tag
691
+ },
692
+ {
693
+ name: 'admin-dashboard',
694
+ type: 'mock',
695
+ output: './src/generated/admin',
696
+ module: 'security', // Only entities in 'security' module
697
+ },
698
+ {
699
+ name: 'auth-service',
700
+ type: 'node-handlers',
701
+ output: './src/auth/handlers',
702
+ tags: ['auth'],
703
+ group: 'public',
704
+ },
705
+ ]
706
+ ```
707
+
708
+ #### Common Tagging Patterns
709
+
710
+ Tags are freeform strings - use whatever fits your project. Here are patterns others find useful:
711
+
712
+ **By access level:**
713
+ ```typescript
714
+ tags: ['public'] // Exposed to external clients
715
+ tags: ['internal'] // Internal services only
716
+ tags: ['admin-only'] // Admin dashboard only
717
+ ```
718
+
719
+ **By feature area:**
720
+ ```typescript
721
+ tags: ['auth'] // Authentication/authorization
722
+ tags: ['billing'] // Payment and subscriptions
723
+ tags: ['content'] // User-generated content
724
+ tags: ['analytics'] // Metrics and reporting
725
+ ```
726
+
727
+ **By lifecycle:**
728
+ ```typescript
729
+ tags: ['stable'] // Production-ready
730
+ tags: ['experimental'] // Beta features
731
+ tags: ['deprecated'] // Scheduled for removal
732
+ ```
733
+
734
+ **By team ownership:**
735
+ ```typescript
736
+ tags: ['team-platform'] // Platform team owns this
737
+ tags: ['team-growth'] // Growth team owns this
738
+ ```
739
+
740
+ **Combined example:**
741
+ ```typescript
742
+ const Payment = defineData('payment', { /* ... */ }, {
743
+ tags: ['billing', 'internal', 'stable'],
744
+ module: 'billing',
745
+ group: 'internal',
746
+ metadata: {
747
+ owner: 'payments-team',
748
+ pii: true,
749
+ },
750
+ });
751
+ ```
752
+
655
753
  ### Generated Server Files
656
754
 
657
755
  **Next.js API Routes** (`nextjs-api` target):
@@ -1,7 +1,7 @@
1
- import { A as Adapter, a as AdapterContext, b as AdapterResponse, d as MswjsDataFactory, F as FetchAdapterOptions } from '../types-C9VMgu3E.mjs';
2
- export { c as AdapterResponseMeta, D as Database, M as MockAdapterOptions, S as SchemaRegistry } from '../types-C9VMgu3E.mjs';
3
- import { o as EntitySchema, b as FieldDefinition } from '../types-C2bd2vgy.mjs';
4
- import { M as Middleware } from '../types-DV2DS7wj.mjs';
1
+ import { A as Adapter, a as AdapterContext, b as AdapterResponse, d as MswjsDataFactory, F as FetchAdapterOptions } from '../types-vPJ91BuY.mjs';
2
+ export { c as AdapterResponseMeta, D as Database, M as MockAdapterOptions, S as SchemaRegistry } from '../types-vPJ91BuY.mjs';
3
+ import { o as EntitySchema, b as FieldDefinition } from '../types-CWZQ6vqt.mjs';
4
+ import { M as Middleware } from '../types-D-u5uw1E.mjs';
5
5
 
6
6
  /**
7
7
  * Storage Driver Types - Abstract storage interface for different backends
@@ -1,7 +1,7 @@
1
- import { A as Adapter, a as AdapterContext, b as AdapterResponse, d as MswjsDataFactory, F as FetchAdapterOptions } from '../types-c2AN3vky.js';
2
- export { c as AdapterResponseMeta, D as Database, M as MockAdapterOptions, S as SchemaRegistry } from '../types-c2AN3vky.js';
3
- import { o as EntitySchema, b as FieldDefinition } from '../types-C2bd2vgy.js';
4
- import { M as Middleware } from '../types-C1MiZh1d.js';
1
+ import { A as Adapter, a as AdapterContext, b as AdapterResponse, d as MswjsDataFactory, F as FetchAdapterOptions } from '../types-4EDTne0h.js';
2
+ export { c as AdapterResponseMeta, D as Database, M as MockAdapterOptions, S as SchemaRegistry } from '../types-4EDTne0h.js';
3
+ import { o as EntitySchema, b as FieldDefinition } from '../types-CWZQ6vqt.js';
4
+ import { M as Middleware } from '../types-Ku_ykMzx.js';
5
5
 
6
6
  /**
7
7
  * Storage Driver Types - Abstract storage interface for different backends
@@ -1,5 +1,5 @@
1
- import { l as RLSScopeMapping, m as RLSBypass, n as RLSConfig, o as EntitySchema, v as EndpointSchema, b as FieldDefinition } from '../types-C2bd2vgy.mjs';
2
- export { I as IndexConfig, j as RLSContext, k as RLSFilter, f as RPCArgument, g as RPCConfig } from '../types-C2bd2vgy.mjs';
1
+ import { l as RLSScopeMapping, m as RLSBypass, n as RLSConfig, o as EntitySchema, v as EndpointSchema, b as FieldDefinition } from '../types-CWZQ6vqt.mjs';
2
+ export { I as IndexConfig, j as RLSContext, k as RLSFilter, f as RPCArgument, g as RPCConfig } from '../types-CWZQ6vqt.mjs';
3
3
 
4
4
  /**
5
5
  * CLI type definitions for Schemock code generation
@@ -134,6 +134,14 @@ interface GenerationTarget {
134
134
  entities?: string[];
135
135
  /** Which entities to exclude */
136
136
  excludeEntities?: string[];
137
+ /** Include only entities with these tags (OR logic - entity must have at least one) */
138
+ tags?: string[];
139
+ /** Exclude entities with these tags */
140
+ excludeTags?: string[];
141
+ /** Include only entities from this module */
142
+ module?: string;
143
+ /** Include only entities from this group */
144
+ group?: string;
137
145
  /** Backend to use for server targets (e.g., nextjs-api uses supabase under the hood) */
138
146
  backend?: 'supabase' | 'firebase' | 'pglite' | 'fetch';
139
147
  /** Middleware configuration for this target */
@@ -362,6 +370,14 @@ interface AnalyzedSchema {
362
370
  rls: AnalyzedRLS;
363
371
  indexes: AnalyzedIndex[];
364
372
  rpc: AnalyzedRPC[];
373
+ /** Tags for entity classification and filtering */
374
+ tags: string[];
375
+ /** Module/domain grouping */
376
+ module?: string;
377
+ /** Logical grouping (e.g., access level) */
378
+ group?: string;
379
+ /** Extensible metadata */
380
+ metadata?: Record<string, unknown>;
365
381
  original: EntitySchema;
366
382
  }
367
383
  /**
@@ -1,5 +1,5 @@
1
- import { l as RLSScopeMapping, m as RLSBypass, n as RLSConfig, o as EntitySchema, v as EndpointSchema, b as FieldDefinition } from '../types-C2bd2vgy.js';
2
- export { I as IndexConfig, j as RLSContext, k as RLSFilter, f as RPCArgument, g as RPCConfig } from '../types-C2bd2vgy.js';
1
+ import { l as RLSScopeMapping, m as RLSBypass, n as RLSConfig, o as EntitySchema, v as EndpointSchema, b as FieldDefinition } from '../types-CWZQ6vqt.js';
2
+ export { I as IndexConfig, j as RLSContext, k as RLSFilter, f as RPCArgument, g as RPCConfig } from '../types-CWZQ6vqt.js';
3
3
 
4
4
  /**
5
5
  * CLI type definitions for Schemock code generation
@@ -134,6 +134,14 @@ interface GenerationTarget {
134
134
  entities?: string[];
135
135
  /** Which entities to exclude */
136
136
  excludeEntities?: string[];
137
+ /** Include only entities with these tags (OR logic - entity must have at least one) */
138
+ tags?: string[];
139
+ /** Exclude entities with these tags */
140
+ excludeTags?: string[];
141
+ /** Include only entities from this module */
142
+ module?: string;
143
+ /** Include only entities from this group */
144
+ group?: string;
137
145
  /** Backend to use for server targets (e.g., nextjs-api uses supabase under the hood) */
138
146
  backend?: 'supabase' | 'firebase' | 'pglite' | 'fetch';
139
147
  /** Middleware configuration for this target */
@@ -362,6 +370,14 @@ interface AnalyzedSchema {
362
370
  rls: AnalyzedRLS;
363
371
  indexes: AnalyzedIndex[];
364
372
  rpc: AnalyzedRPC[];
373
+ /** Tags for entity classification and filtering */
374
+ tags: string[];
375
+ /** Module/domain grouping */
376
+ module?: string;
377
+ /** Logical grouping (e.g., access level) */
378
+ group?: string;
379
+ /** Extensible metadata */
380
+ metadata?: Record<string, unknown>;
365
381
  original: EntitySchema;
366
382
  }
367
383
  /**
package/dist/cli/index.js CHANGED
@@ -90,6 +90,11 @@ var GenerationTargetSchema = zod.z.object({
90
90
  output: zod.z.string().min(1, "target output path is required"),
91
91
  entities: zod.z.array(zod.z.string()).optional(),
92
92
  excludeEntities: zod.z.array(zod.z.string()).optional(),
93
+ // Tag-based filtering
94
+ tags: zod.z.array(zod.z.string()).optional(),
95
+ excludeTags: zod.z.array(zod.z.string()).optional(),
96
+ module: zod.z.string().optional(),
97
+ group: zod.z.string().optional(),
93
98
  backend: zod.z.enum(["supabase", "firebase", "pglite", "fetch"]).optional(),
94
99
  middleware: TargetMiddlewareConfigSchema.optional(),
95
100
  hooks: zod.z.string().optional(),
@@ -475,6 +480,12 @@ function toCamelCase(str) {
475
480
  function toSnakeCase(str) {
476
481
  return str.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "").replace(/[-\s]+/g, "_");
477
482
  }
483
+ function toSafePropertyName(str) {
484
+ if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(str)) {
485
+ return str;
486
+ }
487
+ return toCamelCase(str);
488
+ }
478
489
 
479
490
  // src/cli/utils/faker-mapping.ts
480
491
  function escapeJsString(value) {
@@ -826,6 +837,11 @@ function analyzeSchema(schema, schemaMap, config) {
826
837
  // Will be populated after fields analysis
827
838
  rpc: [],
828
839
  // Will be populated after RPC analysis
840
+ // Entity Organization & Tagging
841
+ tags: schema.tags ?? [],
842
+ module: schema.module,
843
+ group: schema.group,
844
+ metadata: schema.metadata,
829
845
  original: schema
830
846
  };
831
847
  let refCount = 0;
@@ -1241,11 +1257,12 @@ function deriveEndpointName(path) {
1241
1257
  const parts = cleaned.split("/");
1242
1258
  const nameParts = [];
1243
1259
  for (let i = 0; i < parts.length; i++) {
1244
- const part = parts[i];
1260
+ let part = parts[i];
1245
1261
  if (part.startsWith(":")) {
1246
- const paramName = part.slice(1);
1262
+ const paramName = toCamelCaseFromHyphen(part.slice(1));
1247
1263
  nameParts.push("By" + capitalize(paramName));
1248
1264
  } else if (part) {
1265
+ part = toCamelCaseFromHyphen(part);
1249
1266
  if (i === 0) {
1250
1267
  nameParts.push(part);
1251
1268
  } else {
@@ -1265,6 +1282,9 @@ function toPascalCase2(str) {
1265
1282
  function capitalize(str) {
1266
1283
  return str.charAt(0).toUpperCase() + str.slice(1);
1267
1284
  }
1285
+ function toCamelCaseFromHyphen(str) {
1286
+ return str.split("-").map((part, index) => index === 0 ? part : capitalize(part)).join("");
1287
+ }
1268
1288
  function analyzeFields(fields) {
1269
1289
  return Object.entries(fields).map(([name, field]) => analyzeField2(name, field));
1270
1290
  }
@@ -1577,7 +1597,7 @@ function generateCommonTypes(code) {
1577
1597
  // src/cli/generators/mock/db.ts
1578
1598
  function generateMockDb(schemas, config) {
1579
1599
  const code = new CodeBuilder();
1580
- const entityNames = schemas.map((s) => s.name);
1600
+ const entityNames = schemas.map((s) => toSafePropertyName(s.name));
1581
1601
  const persist = config.persist !== false;
1582
1602
  code.comment("GENERATED BY SCHEMOCK - DO NOT EDIT");
1583
1603
  code.line("import { factory, primaryKey, nullable } from '@mswjs/data';");
@@ -1733,7 +1753,8 @@ function generatePersistenceLayer(code, entityNames, storageKey) {
1733
1753
  code.line("wrapDbMethods();");
1734
1754
  }
1735
1755
  function generateEntityFactory(code, schema) {
1736
- code.block(`${schema.name}: {`, () => {
1756
+ const safeName = toSafePropertyName(schema.name);
1757
+ code.block(`${safeName}: {`, () => {
1737
1758
  for (const field of schema.fields) {
1738
1759
  if (field.name === "id") {
1739
1760
  code.line("id: primaryKey(faker.string.uuid),");
@@ -2455,15 +2476,17 @@ function generateSeed(schemas, config) {
2455
2476
  code.block("export interface SeedCounts {", () => {
2456
2477
  for (const schema of schemas) {
2457
2478
  if (schema.isJunctionTable) continue;
2458
- code.line(`${schema.name}?: number;`);
2479
+ const safeName = toSafePropertyName(schema.name);
2480
+ code.line(`${safeName}?: number;`);
2459
2481
  }
2460
2482
  });
2461
2483
  code.line();
2462
2484
  code.block("const defaultCounts: Required<SeedCounts> = {", () => {
2463
2485
  for (const schema of schemas) {
2464
2486
  if (schema.isJunctionTable) continue;
2487
+ const safeName = toSafePropertyName(schema.name);
2465
2488
  const count = config.seed?.[schema.name] ?? 10;
2466
- code.line(`${schema.name}: ${count},`);
2489
+ code.line(`${safeName}: ${count},`);
2467
2490
  }
2468
2491
  }, "};");
2469
2492
  code.line();
@@ -2480,16 +2503,17 @@ function generateSeed(schemas, config) {
2480
2503
  code.line();
2481
2504
  for (const schema of schemas) {
2482
2505
  if (schema.isJunctionTable) continue;
2506
+ const safeName = toSafePropertyName(schema.name);
2483
2507
  const belongsToRels = schema.relations.filter((r) => r.type === "belongsTo");
2484
2508
  const fkFields = belongsToRels.map((r) => ({
2485
2509
  fieldName: r.localField || r.foreignKey,
2486
- target: r.target,
2510
+ target: toSafePropertyName(r.target),
2487
2511
  nullable: schema.fields.find((f) => f.name === (r.localField || r.foreignKey))?.nullable ?? false
2488
2512
  }));
2489
- code.line(`ids.${schema.name} = [];`);
2490
- code.block(`for (let i = 0; i < merged.${schema.name}; i++) {`, () => {
2513
+ code.line(`ids.${safeName} = [];`);
2514
+ code.block(`for (let i = 0; i < merged.${safeName}; i++) {`, () => {
2491
2515
  if (fkFields.length > 0) {
2492
- code.line(`const item = db.${schema.name}.create({`);
2516
+ code.line(`const item = db.${safeName}.create({`);
2493
2517
  code.indent();
2494
2518
  for (const fk of fkFields) {
2495
2519
  if (fk.nullable) {
@@ -2502,9 +2526,9 @@ function generateSeed(schemas, config) {
2502
2526
  code.line(`// eslint-disable-next-line @typescript-eslint/no-explicit-any`);
2503
2527
  code.line("} as any);");
2504
2528
  } else {
2505
- code.line(`const item = db.${schema.name}.create({});`);
2529
+ code.line(`const item = db.${safeName}.create({});`);
2506
2530
  }
2507
- code.line(`ids.${schema.name}.push(item.id);`);
2531
+ code.line(`ids.${safeName}.push(item.id);`);
2508
2532
  });
2509
2533
  code.line();
2510
2534
  }
@@ -2512,14 +2536,16 @@ function generateSeed(schemas, config) {
2512
2536
  code.line();
2513
2537
  code.block("export function reset(): void {", () => {
2514
2538
  for (const schema of [...schemas].reverse()) {
2515
- code.line(`db.${schema.name}.deleteMany({ where: {} });`);
2539
+ const safeName = toSafePropertyName(schema.name);
2540
+ code.line(`db.${safeName}.deleteMany({ where: {} });`);
2516
2541
  }
2517
2542
  });
2518
2543
  code.line();
2519
2544
  code.block("export function getAll(): Record<string, unknown[]> {", () => {
2520
2545
  code.block("return {", () => {
2521
2546
  for (const schema of schemas) {
2522
- code.line(`${schema.name}: db.${schema.name}.getAll(),`);
2547
+ const safeName = toSafePropertyName(schema.name);
2548
+ code.line(`${safeName}: db.${safeName}.getAll(),`);
2523
2549
  }
2524
2550
  }, "};");
2525
2551
  });
@@ -5934,6 +5960,32 @@ function filterSchemasForTarget(schemas, target) {
5934
5960
  (s) => !excludeSet.has(s.name.toLowerCase()) && !excludeSet.has(s.pascalName.toLowerCase()) && !excludeSet.has(s.singularName.toLowerCase())
5935
5961
  );
5936
5962
  }
5963
+ if (target.tags && target.tags.length > 0) {
5964
+ const includeTags = new Set(target.tags.map((t) => t.toLowerCase()));
5965
+ filtered = filtered.filter((s) => {
5966
+ if (!s.tags || s.tags.length === 0) return false;
5967
+ return s.tags.some((tag) => includeTags.has(tag.toLowerCase()));
5968
+ });
5969
+ }
5970
+ if (target.excludeTags && target.excludeTags.length > 0) {
5971
+ const excludeTags = new Set(target.excludeTags.map((t) => t.toLowerCase()));
5972
+ filtered = filtered.filter((s) => {
5973
+ if (!s.tags || s.tags.length === 0) return true;
5974
+ return !s.tags.some((tag) => excludeTags.has(tag.toLowerCase()));
5975
+ });
5976
+ }
5977
+ if (target.module) {
5978
+ const targetModule = target.module.toLowerCase();
5979
+ filtered = filtered.filter(
5980
+ (s) => s.module && s.module.toLowerCase() === targetModule
5981
+ );
5982
+ }
5983
+ if (target.group) {
5984
+ const targetGroup = target.group.toLowerCase();
5985
+ filtered = filtered.filter(
5986
+ (s) => s.group && s.group.toLowerCase() === targetGroup
5987
+ );
5988
+ }
5937
5989
  return filtered;
5938
5990
  }
5939
5991
  function isServerTarget(type) {