create-mock-api-from-types 1.0.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 (46) hide show
  1. package/README.md +220 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +6 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/router/routeBuilder.d.ts +4 -0
  11. package/dist/router/routeBuilder.d.ts.map +1 -0
  12. package/dist/router/routeBuilder.js +78 -0
  13. package/dist/router/routeBuilder.js.map +1 -0
  14. package/dist/schema.d.ts +2 -0
  15. package/dist/schema.d.ts.map +1 -0
  16. package/dist/schema.js +2 -0
  17. package/dist/schema.js.map +1 -0
  18. package/dist/server.d.ts +5 -0
  19. package/dist/server.d.ts.map +1 -0
  20. package/dist/server.js +87 -0
  21. package/dist/server.js.map +1 -0
  22. package/dist/store/dataStore.d.ts +16 -0
  23. package/dist/store/dataStore.d.ts.map +1 -0
  24. package/dist/store/dataStore.js +77 -0
  25. package/dist/store/dataStore.js.map +1 -0
  26. package/dist/types.d.ts +6 -0
  27. package/dist/types.d.ts.map +1 -0
  28. package/dist/types.js +2 -0
  29. package/dist/types.js.map +1 -0
  30. package/dist/utils/mockGenerator.d.ts +5 -0
  31. package/dist/utils/mockGenerator.d.ts.map +1 -0
  32. package/dist/utils/mockGenerator.js +84 -0
  33. package/dist/utils/mockGenerator.js.map +1 -0
  34. package/dist/utils/nameUtils.d.ts +5 -0
  35. package/dist/utils/nameUtils.d.ts.map +1 -0
  36. package/dist/utils/nameUtils.js +58 -0
  37. package/dist/utils/nameUtils.js.map +1 -0
  38. package/dist/utils/processArgs.d.ts +3 -0
  39. package/dist/utils/processArgs.d.ts.map +1 -0
  40. package/dist/utils/processArgs.js +31 -0
  41. package/dist/utils/processArgs.js.map +1 -0
  42. package/dist/utils/propGenerator.d.ts +2 -0
  43. package/dist/utils/propGenerator.d.ts.map +1 -0
  44. package/dist/utils/propGenerator.js +73 -0
  45. package/dist/utils/propGenerator.js.map +1 -0
  46. package/package.json +50 -0
package/README.md ADDED
@@ -0,0 +1,220 @@
1
+ # Mock API
2
+
3
+ Generate a mock REST API server from TypeScript interfaces and object type aliases. Perfect for frontend development, testing, and prototyping—define your data shapes in TypeScript and get a fully functional API with realistic fake data powered by [Faker](https://fakerjs.dev/).
4
+
5
+ ## Features
6
+
7
+ - **TypeScript-first**: Define schemas as interfaces or object type aliases
8
+ - **Smart mock data**: Property names are matched to appropriate Faker generators (e.g. `email` → realistic emails, `price` → commerce prices)
9
+ - **Full REST API**: GET, POST, PUT, PATCH, DELETE with pagination support
10
+ - **Nested types**: References to other interfaces/types are resolved recursively
11
+ - **Multiple schemas**: Point to one or more schema files to build a complete API
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pnpm add mock-api
17
+ # or
18
+ npm install mock-api
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ 1. Create a schema file (e.g. `schema.ts`):
24
+
25
+ ```typescript
26
+ interface User {
27
+ id: string;
28
+ name: string;
29
+ email: string;
30
+ phone: string;
31
+ isActive: boolean;
32
+ }
33
+
34
+ type Product = {
35
+ id: string;
36
+ name: string;
37
+ price: number;
38
+ inStock: boolean;
39
+ };
40
+ ```
41
+
42
+ 2. Start the mock API:
43
+
44
+ ```bash
45
+ npx mock-api schema.ts
46
+ ```
47
+
48
+ 3. Visit `http://localhost:3000` to see all available routes and collections.
49
+
50
+ ## CLI Usage
51
+
52
+ ```bash
53
+ mock-api <schemas...> [options]
54
+ ```
55
+
56
+ ### Arguments
57
+
58
+ | Argument | Description |
59
+ | ---------- | ------------------------------------ |
60
+ | `<schemas...>` | One or more TypeScript schema file paths (required) |
61
+
62
+ ### Options
63
+
64
+ | Option | Short | Default | Description |
65
+ | ------ | ----- | ------- | ------------------------------------ |
66
+ | `--port` | `-p` | `3000` | Port to run the server on |
67
+ | `--count` | `-c` | `10` | Number of mock items to generate per collection |
68
+
69
+ ### Examples
70
+
71
+ ```bash
72
+ # Basic usage
73
+ mock-api src/schema.ts
74
+
75
+ # Custom port and item count
76
+ mock-api schemas/*.ts -p 8080 -c 25
77
+
78
+ # Multiple schema files
79
+ mock-api src/models/user.ts src/models/product.ts
80
+ ```
81
+
82
+ ## Schema Definition
83
+
84
+ ### Interfaces
85
+
86
+ ```typescript
87
+ interface User {
88
+ id: string;
89
+ name: string;
90
+ email: string;
91
+ }
92
+ ```
93
+
94
+ ### Object Type Aliases
95
+
96
+ ```typescript
97
+ type Product = {
98
+ id: string;
99
+ name: string;
100
+ price: number;
101
+ };
102
+ ```
103
+
104
+ Both interfaces and object type aliases (`type X = { ... }`) are supported. Only object-shaped types are used—primitives and union aliases (e.g. `type ID = string`) are ignored.
105
+
106
+ ### Supported Types
107
+
108
+ - **Primitives**: `string`, `number`, `boolean`
109
+ - **Arrays**: `T[]` or `Array<T>`
110
+ - **Unions**: `'a' | 'b'` (picks a random option)
111
+ - **Nested types**: References to other interfaces or type aliases in the same project
112
+ - **Optional**: `| undefined` and `| null` are stripped when generating mocks
113
+
114
+ ## API Endpoints
115
+
116
+ Each schema becomes a collection with the following REST endpoints:
117
+
118
+ | Method | Path | Description |
119
+ | ------ | ---- | ----------- |
120
+ | GET | `/` | List all collections and their endpoints |
121
+ | GET | `/:collection` | List items (supports `?page` and `?limit` for pagination) |
122
+ | GET | `/:collection/:id` | Get a single item by ID |
123
+ | POST | `/:collection` | Create a new item |
124
+ | PUT | `/:collection/:id` | Replace an item |
125
+ | PATCH | `/:collection/:id` | Partially update an item |
126
+ | DELETE | `/:collection/:id` | Delete an item |
127
+
128
+ ### Route Naming
129
+
130
+ Interface/type names are converted to kebab-case and pluralized:
131
+
132
+ - `User` → `/users`
133
+ - `Product` → `/products`
134
+ - `OrderItem` → `/order-items`
135
+
136
+ ### Response Format
137
+
138
+ **List (GET `/:collection`):**
139
+
140
+ ```json
141
+ {
142
+ "data": [...],
143
+ "total": 10,
144
+ "page": 1,
145
+ "limit": 10
146
+ }
147
+ ```
148
+
149
+ **Single item (GET `/:collection/:id`):**
150
+
151
+ ```json
152
+ {
153
+ "id": "uuid-here",
154
+ "name": "John Doe",
155
+ "email": "john@example.com"
156
+ }
157
+ ```
158
+
159
+ ## Smart Property Generation
160
+
161
+ Property names are matched to Faker generators for realistic data:
162
+
163
+ | Pattern | Example |
164
+ | ------- | ------- |
165
+ | `id`, `*Id` | UUID |
166
+ | `email` | `user@example.com` |
167
+ | `name`, `firstName`, `lastName` | Person names |
168
+ | `phone`, `mobile` | Phone numbers |
169
+ | `password`, `secret` | Passwords |
170
+ | `address`, `city`, `state`, `zip`, `country` | Location data |
171
+ | `price`, `cost`, `amount` | Commerce prices |
172
+ | `createdAt`, `updatedAt`, `date` | ISO date strings |
173
+ | `description`, `content`, `body` | Lorem paragraphs |
174
+ | `url`, `image`, `avatar` | URLs |
175
+ | `isActive`, `isEnabled`, `verified` | Booleans |
176
+ | ...and more | See `propGenerator.ts` for full list |
177
+
178
+ Unmatched properties fall back to type-based generation (`string` → word, `number` → integer, etc.).
179
+
180
+ ## Programmatic Usage
181
+
182
+ Use the library in your own Node.js application:
183
+
184
+ ```typescript
185
+ import { createServer, startServer } from "mock-api";
186
+ import type { MockApiOptions } from "mock-api";
187
+
188
+ const options: MockApiOptions = {
189
+ schemaPaths: ["src/schema.ts"],
190
+ port: 3000,
191
+ count: 10,
192
+ };
193
+
194
+ // Start a server (blocks)
195
+ startServer(options);
196
+
197
+ // Or create an Express app for custom use (e.g. testing)
198
+ const app = createServer(options);
199
+ // app is a standard Express application
200
+ ```
201
+
202
+ ## Development
203
+
204
+ ```bash
205
+ # Install dependencies
206
+ pnpm install
207
+
208
+ # Build
209
+ pnpm run build
210
+
211
+ # Run with tsx (no build step)
212
+ pnpm run dev src/schema.ts
213
+
214
+ # Type check
215
+ pnpm run typecheck
216
+ ```
217
+
218
+ ## License
219
+
220
+ ISC
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import { processArgs } from "./utils/processArgs.js";
3
+ import { startServer } from "./server.js";
4
+ const options = processArgs(process.argv);
5
+ startServer(options);
6
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC1C,WAAW,CAAC,OAAO,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { createServer, startServer } from "./server.js";
2
+ export type { MockApiOptions } from "./types.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { createServer, startServer } from "./server.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Router } from "express";
2
+ import type { DataStore } from "../store/dataStore.js";
3
+ export declare function buildRouter(collection: string, store: DataStore): Router;
4
+ //# sourceMappingURL=routeBuilder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routeBuilder.d.ts","sourceRoot":"","sources":["../../src/router/routeBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAmBvD,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,MAAM,CA4ExE"}
@@ -0,0 +1,78 @@
1
+ import { Router } from "express";
2
+ // parseQueryInt(value) parses the given value as an integer
3
+ // returns: value as an int if valid, undefined otherwise
4
+ function parseQueryInt(value) {
5
+ if (typeof value !== "string") {
6
+ return undefined;
7
+ }
8
+ const num = parseInt(value, 10);
9
+ return isNaN(num) ? undefined : num;
10
+ }
11
+ // buildRouter(collection, store) builds a router for the given collection and store
12
+ // returns: the router
13
+ // example: "users" -> "/users"
14
+ // "posts" -> "/posts"
15
+ // "comments" -> "/comments"
16
+ // "products" -> "/products"
17
+ // "orders" -> "/orders"
18
+ export function buildRouter(collection, store) {
19
+ const router = Router();
20
+ // GET /: paginated list of items
21
+ router.get("/", (req, res) => {
22
+ const page = parseQueryInt(req.query["page"]);
23
+ const limit = parseQueryInt(req.query["limit"]);
24
+ const data = store.getAll(collection, page, limit);
25
+ const total = store.count(collection);
26
+ res.json({ data, total, page: page ?? 1, limit: limit ?? total });
27
+ });
28
+ // GET /:id: get an item by id
29
+ router.get("/:id", (req, res) => {
30
+ const id = req.params["id"];
31
+ const item = store.getById(collection, id);
32
+ if (item === undefined) {
33
+ res.status(404).json({ error: "Not found" });
34
+ return;
35
+ }
36
+ res.json(item);
37
+ });
38
+ // POST /: create a new item
39
+ router.post("/", (req, res) => {
40
+ const body = req.body;
41
+ const item = store.create(collection, body);
42
+ res.status(201).json(item);
43
+ });
44
+ // PUT /:id: update an item by id
45
+ router.put("/:id", (req, res) => {
46
+ const id = req.params["id"];
47
+ const body = req.body;
48
+ const item = store.update(collection, id, body);
49
+ if (item === undefined) {
50
+ res.status(404).json({ error: "Not found" });
51
+ return;
52
+ }
53
+ res.json(item);
54
+ });
55
+ // PATCH /:id: update an item by id
56
+ router.patch("/:id", (req, res) => {
57
+ const id = req.params["id"];
58
+ const body = req.body;
59
+ const item = store.patch(collection, id, body);
60
+ if (item === undefined) {
61
+ res.status(404).json({ error: "Not found" });
62
+ return;
63
+ }
64
+ res.json(item);
65
+ });
66
+ // DELETE /:id: delete an item by id
67
+ router.delete("/:id", (req, res) => {
68
+ const id = req.params["id"];
69
+ const deleted = store.delete(collection, id);
70
+ if (!deleted) {
71
+ res.status(404).json({ error: "Not found" });
72
+ return;
73
+ }
74
+ res.status(204).send();
75
+ });
76
+ return router;
77
+ }
78
+ //# sourceMappingURL=routeBuilder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routeBuilder.js","sourceRoot":"","sources":["../../src/router/routeBuilder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":""}
package/dist/schema.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ import { type Express } from "express";
2
+ import type { MockApiOptions } from "./types.js";
3
+ export declare function createServer(options: MockApiOptions): Express;
4
+ export declare function startServer(options: MockApiOptions): void;
5
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAgB,EAAE,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AAEhD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAYjD,wBAAgB,YAAY,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CA2E7D;AAKD,wBAAgB,WAAW,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAezD"}
package/dist/server.js ADDED
@@ -0,0 +1,87 @@
1
+ import { resolve } from "node:path";
2
+ import express, {} from "express";
3
+ import { Node, Project } from "ts-morph";
4
+ import { DataStore } from "./store/dataStore.js";
5
+ import { buildRouter } from "./router/routeBuilder.js";
6
+ import { generateMockCollection, } from "./utils/mockGenerator.js";
7
+ import { toRoutePath, toCollectionKey } from "./utils/nameUtils.js";
8
+ // createServer(options) creates a new server with the given options
9
+ // options: the options for the server
10
+ // returns: the Express application (server)
11
+ export function createServer(options) {
12
+ const app = express();
13
+ const project = new Project({ skipAddingFilesFromTsConfig: true });
14
+ const store = new DataStore();
15
+ app.use(express.json());
16
+ // Add the source files to the project
17
+ for (const schemaPath of options.schemaPaths) {
18
+ project.addSourceFilesAtPaths(resolve(schemaPath));
19
+ }
20
+ const schemas = [];
21
+ for (const sf of project.getSourceFiles()) {
22
+ // Add all interfaces to the schemas
23
+ for (const iface of sf.getInterfaces()) {
24
+ schemas.push({ name: iface.getName(), schema: iface });
25
+ }
26
+ // Add all type aliases to the schemas
27
+ for (const typeAlias of sf.getTypeAliases()) {
28
+ const typeNode = typeAlias.getTypeNode();
29
+ if (typeNode !== undefined && Node.isTypeLiteral(typeNode)) {
30
+ schemas.push({ name: typeAlias.getName(), schema: typeNode });
31
+ }
32
+ }
33
+ }
34
+ if (schemas.length === 0) {
35
+ console.warn("Warning: No interfaces or object type aliases found in the provided schema files");
36
+ }
37
+ const routes = [];
38
+ for (const { name, schema } of schemas) {
39
+ const routePath = toRoutePath(name);
40
+ const collectionKey = toCollectionKey(name);
41
+ const mockData = generateMockCollection(schema, project, options.count);
42
+ store.seed(collectionKey, mockData);
43
+ const router = buildRouter(collectionKey, store);
44
+ app.use(routePath, router);
45
+ routes.push({
46
+ path: routePath,
47
+ interface: name,
48
+ count: options.count,
49
+ });
50
+ }
51
+ // Root route lists all available collections
52
+ app.get("/", (_req, res) => {
53
+ res.json({
54
+ message: "Mock API is running",
55
+ routes: routes.map((r) => ({
56
+ collection: r.path,
57
+ interface: r.interface,
58
+ seeded: r.count,
59
+ endpoints: [
60
+ `GET ${r.path}`,
61
+ `GET ${r.path}/:id`,
62
+ `POST ${r.path}`,
63
+ `PUT ${r.path}/:id`,
64
+ `PATCH ${r.path}/:id`,
65
+ `DELETE ${r.path}/:id`,
66
+ ],
67
+ })),
68
+ });
69
+ });
70
+ return app;
71
+ }
72
+ // startServer(options) starts the server with the given options
73
+ // options: the options for the server
74
+ // returns: void
75
+ export function startServer(options) {
76
+ const app = createServer(options);
77
+ console.log("\nRegistering routes:");
78
+ for (const schemaPath of options.schemaPaths) {
79
+ console.log(` Schema: ${schemaPath}`);
80
+ }
81
+ app.listen(options.port, () => {
82
+ console.log(`\nMock API running at http://localhost:${options.port}`);
83
+ console.log(`Visit http://localhost:${options.port}/ to see all routes`);
84
+ console.log("Press Ctrl+C to stop\n");
85
+ });
86
+ }
87
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,OAAO,EAAE,EAAgB,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EACH,sBAAsB,GAEzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEpE,oEAAoE;AACpE,wCAAwC;AACxC,8CAA8C;AAC9C,MAAM,UAAU,YAAY,CAAC,OAAuB;IAChD,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,2BAA2B,EAAE,IAAI,EAAE,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,IAAI,SAAS,EAAE,CAAC;IAE9B,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAExB,sCAAsC;IACtC,KAAK,MAAM,UAAU,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;QAC3C,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,OAAO,GAAkD,EAAE,CAAC;IAElE,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;QACxC,oCAAoC;QACpC,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,aAAa,EAAE,EAAE,CAAC;YACrC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,sCAAsC;QACtC,KAAK,MAAM,SAAS,IAAI,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC;YAC1C,MAAM,QAAQ,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;YACzC,IAAI,QAAQ,KAAK,SAAS,IAAI,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACzD,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;YAClE,CAAC;QACL,CAAC;IACL,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,IAAI,CACR,kFAAkF,CACrF,CAAC;IACN,CAAC;IAED,MAAM,MAAM,GACR,EAAE,CAAC;IAEP,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;QACrC,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;QAE5C,MAAM,QAAQ,GAAG,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QACxE,KAAK,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;QAEpC,MAAM,MAAM,GAAG,WAAW,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;QACjD,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAE3B,MAAM,CAAC,IAAI,CAAC;YACR,IAAI,EAAE,SAAS;YACf,SAAS,EAAE,IAAI;YACf,KAAK,EAAE,OAAO,CAAC,KAAK;SACvB,CAAC,CAAC;IACP,CAAC;IAED,6CAA6C;IAC7C,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,GAAG,EAAQ,EAAE;QAC7B,GAAG,CAAC,IAAI,CAAC;YACL,OAAO,EAAE,qBAAqB;YAC9B,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACvB,UAAU,EAAE,CAAC,CAAC,IAAI;gBAClB,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,MAAM,EAAE,CAAC,CAAC,KAAK;gBACf,SAAS,EAAE;oBACP,OAAO,CAAC,CAAC,IAAI,EAAE;oBACf,OAAO,CAAC,CAAC,IAAI,MAAM;oBACnB,QAAQ,CAAC,CAAC,IAAI,EAAE;oBAChB,OAAO,CAAC,CAAC,IAAI,MAAM;oBACnB,SAAS,CAAC,CAAC,IAAI,MAAM;oBACrB,UAAU,CAAC,CAAC,IAAI,MAAM;iBACzB;aACJ,CAAC,CAAC;SACN,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACf,CAAC;AAED,gEAAgE;AAChE,wCAAwC;AACxC,kBAAkB;AAClB,MAAM,UAAU,WAAW,CAAC,OAAuB;IAC/C,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IAElC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACrC,KAAK,MAAM,UAAU,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,aAAa,UAAU,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE;QAC1B,OAAO,CAAC,GAAG,CAAC,0CAA0C,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CACP,0BAA0B,OAAO,CAAC,IAAI,qBAAqB,CAC9D,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,16 @@
1
+ type MockRecord = Record<string, unknown> & {
2
+ id: string;
3
+ };
4
+ export declare class DataStore {
5
+ private readonly collections;
6
+ seed(collection: string, items: Record<string, unknown>[]): void;
7
+ count(collection: string): number;
8
+ getAll(collection: string, page?: number, limit?: number): MockRecord[];
9
+ getById(collection: string, id: string): MockRecord | undefined;
10
+ create(collection: string, data: Record<string, unknown>): MockRecord;
11
+ update(collection: string, id: string, data: Record<string, unknown>): MockRecord | undefined;
12
+ patch(collection: string, id: string, data: Record<string, unknown>): MockRecord | undefined;
13
+ delete(collection: string, id: string): boolean;
14
+ }
15
+ export {};
16
+ //# sourceMappingURL=dataStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataStore.d.ts","sourceRoot":"","sources":["../../src/store/dataStore.ts"],"names":[],"mappings":"AAEA,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D,qBAAa,SAAS;IAClB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAwC;IAIpE,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI;IAShE,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAMjC,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE;IAWvE,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAM/D,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU;IASrE,MAAM,CACF,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,UAAU,GAAG,SAAS;IAiBzB,KAAK,CACD,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,UAAU,GAAG,SAAS;IAiBzB,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO;CAalD"}
@@ -0,0 +1,77 @@
1
+ import { randomUUID } from "node:crypto";
2
+ export class DataStore {
3
+ collections = new Map();
4
+ // seed(collection, items) seeds the collection with the given items
5
+ // adds the items to the collection
6
+ seed(collection, items) {
7
+ const records = items.map((item) => ({ id: randomUUID(), ...item }));
8
+ this.collections.set(collection, records);
9
+ }
10
+ // count(collection) counts the number of items in the collection
11
+ // returns: the number of items in the collection
12
+ count(collection) {
13
+ return this.collections.get(collection)?.length ?? 0;
14
+ }
15
+ // getAll(collection, page, limit) gets all items from the collection
16
+ // returns: a slice of the collection based on the page and limit
17
+ getAll(collection, page, limit) {
18
+ const items = this.collections.get(collection) ?? [];
19
+ if (page !== undefined && limit !== undefined) {
20
+ const start = (page - 1) * limit;
21
+ return items.slice(start, start + limit);
22
+ }
23
+ return items;
24
+ }
25
+ // getById(collection, id) gets an item from the collection by id
26
+ // returns: the item if it exists, undefined otherwise
27
+ getById(collection, id) {
28
+ return this.collections.get(collection)?.find((item) => item.id === id);
29
+ }
30
+ // create(collection, data) creates a new item in the collection
31
+ // returns: the created item
32
+ create(collection, data) {
33
+ const record = { id: randomUUID(), ...data };
34
+ const existing = this.collections.get(collection) ?? [];
35
+ this.collections.set(collection, [...existing, record]);
36
+ return record;
37
+ }
38
+ // update(collection, id, data) updates an item in the collection
39
+ // returns: the updated item if it exists, undefined otherwise
40
+ update(collection, id, data) {
41
+ const items = this.collections.get(collection);
42
+ if (items === undefined)
43
+ return undefined;
44
+ const exists = items.some((item) => item.id === id);
45
+ if (!exists)
46
+ return undefined;
47
+ const updated = { id, ...data };
48
+ this.collections.set(collection, items.map((item) => (item.id === id ? updated : item)));
49
+ return updated;
50
+ }
51
+ // patch(collection, id, data) partially updates an item in the collection
52
+ // returns: the updated item if it exists, undefined otherwise
53
+ patch(collection, id, data) {
54
+ const items = this.collections.get(collection);
55
+ if (items === undefined)
56
+ return undefined;
57
+ const existing = items.find((item) => item.id === id);
58
+ if (existing === undefined)
59
+ return undefined;
60
+ const updated = { ...existing, ...data, id };
61
+ this.collections.set(collection, items.map((item) => (item.id === id ? updated : item)));
62
+ return updated;
63
+ }
64
+ // delete(collection, id) deletes an item from the collection
65
+ // returns: true if the item was deleted, false otherwise
66
+ delete(collection, id) {
67
+ const items = this.collections.get(collection);
68
+ if (items === undefined)
69
+ return false;
70
+ const exists = items.some((item) => item.id === id);
71
+ if (!exists)
72
+ return false;
73
+ this.collections.set(collection, items.filter((item) => item.id !== id));
74
+ return true;
75
+ }
76
+ }
77
+ //# sourceMappingURL=dataStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataStore.js","sourceRoot":"","sources":["../../src/store/dataStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export type MockApiOptions = {
2
+ schemaPaths: string[];
3
+ port: number;
4
+ count: number;
5
+ };
6
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG;IACzB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ import { type InterfaceDeclaration, type Project, type TypeLiteralNode } from "ts-morph";
2
+ export type ObjectSchema = InterfaceDeclaration | TypeLiteralNode;
3
+ export declare function generateMockObject(schema: ObjectSchema, project: Project, depth?: number): Record<string, unknown>;
4
+ export declare function generateMockCollection(schema: ObjectSchema, project: Project, count: number): Record<string, unknown>[];
5
+ //# sourceMappingURL=mockGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mockGenerator.d.ts","sourceRoot":"","sources":["../../src/utils/mockGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,KAAK,oBAAoB,EACzB,KAAK,OAAO,EACZ,KAAK,eAAe,EACvB,MAAM,UAAU,CAAC;AAKlB,MAAM,MAAM,YAAY,GAAG,oBAAoB,GAAG,eAAe,CAAC;AA4FlE,wBAAgB,kBAAkB,CAC9B,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,OAAO,EAChB,KAAK,GAAE,MAAU,GAClB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAYzB;AAID,wBAAgB,sBAAsB,CAClC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,GACd,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAI3B"}
@@ -0,0 +1,84 @@
1
+ import { Node, } from "ts-morph";
2
+ import { generateProp } from "./propGenerator.js";
3
+ const MAX_DEPTH = 5;
4
+ // findSchema(typeName, project) finds the interface or type alias with the given name in the project
5
+ // returns: the schema if it exists (interface or type literal from object type alias), undefined otherwise
6
+ function findSchema(typeName, project) {
7
+ const iface = project
8
+ .getSourceFiles()
9
+ .flatMap((sf) => sf.getInterfaces())
10
+ .find((i) => i.getName() === typeName);
11
+ if (iface !== undefined) {
12
+ return iface;
13
+ }
14
+ const typeAlias = project
15
+ .getSourceFiles()
16
+ .flatMap((sf) => sf.getTypeAliases())
17
+ .find((t) => t.getName() === typeName);
18
+ if (typeAlias === undefined) {
19
+ return undefined;
20
+ }
21
+ const typeNode = typeAlias.getTypeNode();
22
+ if (typeNode === undefined || !Node.isTypeLiteral(typeNode)) {
23
+ return undefined;
24
+ }
25
+ return typeNode;
26
+ }
27
+ // resolveTypeText(propName, typeText, project, depth) resolves the type text for the given property name,
28
+ // type text, project, and depth.
29
+ // returns: the resolved type text
30
+ function resolveTypeText(propName, typeText, project, depth) {
31
+ if (depth > MAX_DEPTH) {
32
+ return null;
33
+ }
34
+ // Strip optional modifiers (| undefined, | null)
35
+ const cleaned = typeText
36
+ .replace(/\s*\|\s*undefined/g, "")
37
+ .replace(/\s*\|\s*null/g, "")
38
+ .trim();
39
+ // Array type: T[]
40
+ if (cleaned.endsWith("[]")) {
41
+ const inner = cleaned.slice(0, -2);
42
+ return Array.from({ length: 3 }, () => resolveTypeText(propName, inner, project, depth + 1));
43
+ }
44
+ // Generic array: Array<T>
45
+ const arrayMatch = cleaned.match(/^Array<(.+)>$/);
46
+ const arrayInner = arrayMatch?.[1];
47
+ if (arrayInner !== undefined) {
48
+ return Array.from({ length: 3 }, () => resolveTypeText(propName, arrayInner, project, depth + 1));
49
+ }
50
+ // String literal: 'value' or "value"
51
+ if (/^'[^']*'$/.test(cleaned) || /^"[^"]*"$/.test(cleaned)) {
52
+ return cleaned.slice(1, -1);
53
+ }
54
+ // Union type: pick a random option
55
+ if (cleaned.includes(" | ")) {
56
+ const options = cleaned.split(" | ").map((t) => t.trim());
57
+ const picked = options[Math.floor(Math.random() * options.length)] ?? "string";
58
+ return resolveTypeText(propName, picked, project, depth + 1);
59
+ }
60
+ // Nested interface or type alias
61
+ const nested = findSchema(cleaned, project);
62
+ if (nested !== undefined) {
63
+ return generateMockObject(nested, project, depth + 1);
64
+ }
65
+ return generateProp(propName, cleaned);
66
+ }
67
+ // generateMockObject(schema, project, depth) generates a mock object with the given schema, project, and depth
68
+ // returns: a mock object
69
+ export function generateMockObject(schema, project, depth = 0) {
70
+ const obj = {};
71
+ for (const prop of schema.getProperties()) {
72
+ const propName = prop.getName();
73
+ // Prefer the type node text (as written) for clean type names
74
+ const typeText = prop.getTypeNode()?.getText() ?? prop.getType().getText();
75
+ obj[propName] = resolveTypeText(propName, typeText, project, depth);
76
+ }
77
+ return obj;
78
+ }
79
+ // generateMockCollection(schema, project, count) generates a mock collection with the given schema, project, and count
80
+ // returns: an array of mock objects
81
+ export function generateMockCollection(schema, project, count) {
82
+ return Array.from({ length: count }, () => generateMockObject(schema, project));
83
+ }
84
+ //# sourceMappingURL=mockGenerator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mockGenerator.js","sourceRoot":"","sources":["../../src/utils/mockGenerator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare function pluralize(word: string): string;
2
+ export declare function toKebabCase(str: string): string;
3
+ export declare function toRoutePath(interfaceName: string): string;
4
+ export declare function toCollectionKey(interfaceName: string): string;
5
+ //# sourceMappingURL=nameUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nameUtils.d.ts","sourceRoot":"","sources":["../../src/utils/nameUtils.ts"],"names":[],"mappings":"AAeA,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAiB9C;AAKD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK/C;AAKD,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAQzD;AAKD,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE7D"}
@@ -0,0 +1,58 @@
1
+ const IRREGULAR_PLURALS = {
2
+ person: "people",
3
+ man: "men",
4
+ woman: "women",
5
+ child: "children",
6
+ mouse: "mice",
7
+ tooth: "teeth",
8
+ foot: "feet",
9
+ goose: "geese",
10
+ };
11
+ // pluralize(word) pluralizes the given word
12
+ // returns: the pluralized word
13
+ // example: "Person" -> "People"
14
+ // "User" -> "Users"
15
+ export function pluralize(word) {
16
+ const lower = word.toLowerCase();
17
+ const irregular = IRREGULAR_PLURALS[lower];
18
+ if (irregular !== undefined) {
19
+ return irregular;
20
+ }
21
+ // If the word ends in s, x, z, ch, or sh, add es
22
+ if (/(?:s|x|z|ch|sh)$/i.test(word)) {
23
+ return word + "es";
24
+ }
25
+ // If the word ends in y and is not preceded by a vowel, add ies
26
+ if (/[^aeiou]y$/i.test(word)) {
27
+ return word.slice(0, -1) + "ies";
28
+ }
29
+ return word + "s";
30
+ }
31
+ // toKebabCase(str) converts the given string to kebab case
32
+ // returns: the kebab case string
33
+ // example: "User" -> "user"
34
+ export function toKebabCase(str) {
35
+ return str
36
+ .replace(/([A-Z])/g, (_, char) => `-${char}`)
37
+ .toLowerCase()
38
+ .replace(/^-/, "");
39
+ }
40
+ // toRoutePath(interfaceName) converts the given interface name to a route path
41
+ // returns: route path
42
+ // example: "User" -> "/users"
43
+ export function toRoutePath(interfaceName) {
44
+ const kebab = toKebabCase(interfaceName);
45
+ const parts = kebab.split("-");
46
+ const lastPart = parts[parts.length - 1];
47
+ if (lastPart !== undefined) {
48
+ parts[parts.length - 1] = pluralize(lastPart);
49
+ }
50
+ return "/" + parts.join("-");
51
+ }
52
+ // toCollectionKey(interfaceName) converts the given interface name to a collection key
53
+ // returns: collection key
54
+ // example: "User" -> "users"
55
+ export function toCollectionKey(interfaceName) {
56
+ return toRoutePath(interfaceName).slice(1);
57
+ }
58
+ //# sourceMappingURL=nameUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nameUtils.js","sourceRoot":"","sources":["../../src/utils/nameUtils.ts"],"names":[],"mappings":"AAAA,MAAM,iBAAiB,GAAqC;IACxD,MAAM,EAAE,QAAQ;IAChB,GAAG,EAAE,KAAK;IACV,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,UAAU;IACjB,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,OAAO;CACjB,CAAC;AAEF,4CAA4C;AAC5C,+BAA+B;AAC/B,gCAAgC;AAChC,6BAA6B;AAC7B,MAAM,UAAU,SAAS,CAAC,IAAY;IAClC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAEjC,MAAM,SAAS,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,iDAAiD;IACjD,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,OAAO,IAAI,GAAG,IAAI,CAAC;IACvB,CAAC;IACD,gEAAgE;IAChE,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;IACrC,CAAC;IAED,OAAO,IAAI,GAAG,GAAG,CAAC;AACtB,CAAC;AAED,2DAA2D;AAC3D,iCAAiC;AACjC,4BAA4B;AAC5B,MAAM,UAAU,WAAW,CAAC,GAAW;IACnC,OAAO,GAAG;SACL,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,IAAY,EAAE,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;SACpD,WAAW,EAAE;SACb,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAC3B,CAAC;AAED,+EAA+E;AAC/E,sBAAsB;AACtB,8BAA8B;AAC9B,MAAM,UAAU,WAAW,CAAC,aAAqB;IAC7C,MAAM,KAAK,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QACzB,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACjC,CAAC;AAED,uFAAuF;AACvF,0BAA0B;AAC1B,6BAA6B;AAC7B,MAAM,UAAU,eAAe,CAAC,aAAqB;IACjD,OAAO,WAAW,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { MockApiOptions } from "../types.js";
2
+ export declare function processArgs(argv: string[]): MockApiOptions;
3
+ //# sourceMappingURL=processArgs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processArgs.d.ts","sourceRoot":"","sources":["../../src/utils/processArgs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIlD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,cAAc,CAuC1D"}
@@ -0,0 +1,31 @@
1
+ import { Command } from "commander";
2
+ // processArgs(argv) processes the given arguments and returns the options
3
+ // retunrs: object with schema paths, port, and count
4
+ export function processArgs(argv) {
5
+ const program = new Command();
6
+ program
7
+ .name("mock-api")
8
+ .description("Generate a mock REST API from TypeScript interfaces and object type aliases")
9
+ .argument("<schemas...>", "TypeScript schema file path(s)")
10
+ .option("-p, --port <number>", "Port to run the server on", "3000")
11
+ .option("-c, --count <number>", "Number of mock items to generate per collection", "10")
12
+ .parse(argv);
13
+ const schemaPaths = program.args;
14
+ const opts = program.opts();
15
+ const port = parseInt(opts.port, 10);
16
+ const count = parseInt(opts.count, 10);
17
+ if (schemaPaths.length === 0) {
18
+ console.error("Error: At least one schema file is required");
19
+ process.exit(1);
20
+ }
21
+ if (isNaN(port) || port < 1 || port > 65535) {
22
+ console.error("Error: Port must be a number between 1 and 65535");
23
+ process.exit(1);
24
+ }
25
+ if (isNaN(count) || count < 1) {
26
+ console.error("Error: Count must be a positive integer");
27
+ process.exit(1);
28
+ }
29
+ return { schemaPaths, port, count };
30
+ }
31
+ //# sourceMappingURL=processArgs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processArgs.js","sourceRoot":"","sources":["../../src/utils/processArgs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,0EAA0E;AAC1E,qDAAqD;AACrD,MAAM,UAAU,WAAW,CAAC,IAAc;IACtC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACF,IAAI,CAAC,UAAU,CAAC;SAChB,WAAW,CACR,6EAA6E,CAChF;SACA,QAAQ,CAAC,cAAc,EAAE,gCAAgC,CAAC;SAC1D,MAAM,CAAC,qBAAqB,EAAE,2BAA2B,EAAE,MAAM,CAAC;SAClE,MAAM,CACH,sBAAsB,EACtB,iDAAiD,EACjD,IAAI,CACP;SACA,KAAK,CAAC,IAAI,CAAC,CAAC;IAEjB,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IACjC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAmC,CAAC;IAE7D,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAEvC,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;QAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;QAC1C,OAAO,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;QAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;QACzD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACxC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function generateProp(propName: string, propType: string): unknown;
2
+ //# sourceMappingURL=propGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"propGenerator.d.ts","sourceRoot":"","sources":["../../src/utils/propGenerator.ts"],"names":[],"mappings":"AAiEA,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAaxE"}
@@ -0,0 +1,73 @@
1
+ import { faker } from "@faker-js/faker";
2
+ // Ordered by specificity — first match wins
3
+ const NAME_PATTERNS = [
4
+ [/^id$|Id$/, () => faker.string.uuid()],
5
+ [/email/i, () => faker.internet.email()],
6
+ [/username/i, () => faker.internet.username()],
7
+ [/password|secret/i, () => faker.internet.password()],
8
+ [/phone|mobile/i, () => faker.phone.number()],
9
+ [/firstName|first_name/i, () => faker.person.firstName()],
10
+ [/lastName|last_name/i, () => faker.person.lastName()],
11
+ [/fullName|full_name/i, () => faker.person.fullName()],
12
+ [/name/i, () => faker.person.fullName()],
13
+ [/address|street/i, () => faker.location.streetAddress()],
14
+ [/city/i, () => faker.location.city()],
15
+ [/state|province/i, () => faker.location.state()],
16
+ [/zip|postal/i, () => faker.location.zipCode()],
17
+ [/country/i, () => faker.location.country()],
18
+ [/latitude|lat$/i, () => faker.location.latitude()],
19
+ [/longitude|lng$|lon$/i, () => faker.location.longitude()],
20
+ [/createdAt|updatedAt|deletedAt/i, () => faker.date.recent().toISOString()],
21
+ [/date|time/i, () => faker.date.recent().toISOString()],
22
+ [/description|bio|summary|about/i, () => faker.lorem.paragraph()],
23
+ [/title|headline/i, () => faker.lorem.words(4)],
24
+ [/slug/i, () => faker.helpers.slugify(faker.lorem.words(3))],
25
+ [/image|avatar|photo|picture/i, () => faker.image.url()],
26
+ [/url|website|link|href/i, () => faker.internet.url()],
27
+ [/price|cost|amount|fee/i, () => faker.commerce.price()],
28
+ [/color|colour/i, () => faker.color.human()],
29
+ [/company|organization|org/i, () => faker.company.name()],
30
+ [/job|position|role|occupation/i, () => faker.person.jobTitle()],
31
+ [/age/i, () => faker.number.int({ min: 18, max: 80 })],
32
+ [
33
+ /rating|score/i,
34
+ () => faker.number.float({ min: 0, max: 5, fractionDigits: 1 }),
35
+ ],
36
+ [/count|total|quantity|qty/i, () => faker.number.int({ min: 0, max: 100 })],
37
+ [/content|body|text/i, () => faker.lorem.paragraphs(2)],
38
+ [/token|key|hash/i, () => faker.string.alphanumeric(32)],
39
+ [/tag|label|category/i, () => faker.commerce.department()],
40
+ [
41
+ /isActive|isEnabled|isVerified|active|enabled|verified/i,
42
+ () => faker.datatype.boolean(),
43
+ ],
44
+ ];
45
+ // Record of type generic type fallbacks in case no pattern matches
46
+ const TYPE_FALLBACKS = {
47
+ string: () => faker.lorem.word(),
48
+ number: () => faker.number.int({ max: 1000 }),
49
+ boolean: () => faker.datatype.boolean(),
50
+ Date: () => faker.date.recent().toISOString(),
51
+ any: () => faker.lorem.word(),
52
+ unknown: () => faker.lorem.word(),
53
+ };
54
+ // generateProp(propName, propType) generates a mock property with the given name and type
55
+ // returns: mock property
56
+ // example: "name" -> "John Doe"
57
+ // "email" -> "john.doe@example.com"
58
+ // "password" -> "password123"
59
+ // "phone" -> "1234567890"
60
+ // "createdAt" -> "2021-01-01T00:00:00.000Z"
61
+ export function generateProp(propName, propType) {
62
+ for (const [pattern, generator] of NAME_PATTERNS) {
63
+ if (pattern.test(propName)) {
64
+ return generator();
65
+ }
66
+ }
67
+ const fallback = TYPE_FALLBACKS[propType];
68
+ if (fallback !== undefined) {
69
+ return fallback();
70
+ }
71
+ return faker.lorem.word();
72
+ }
73
+ //# sourceMappingURL=propGenerator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"propGenerator.js","sourceRoot":"","sources":["../../src/utils/propGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAIxC,4CAA4C;AAC5C,MAAM,aAAa,GAAoD;IACnE,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACvC,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxC,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;IAC9C,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;IACrD,CAAC,eAAe,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;IAC7C,CAAC,uBAAuB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;IACzD,CAAC,qBAAqB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;IACtD,CAAC,qBAAqB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;IACtD,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;IACxC,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;IACzD,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IACtC,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACjD,CAAC,aAAa,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;IAC/C,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;IAC5C,CAAC,gBAAgB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;IACnD,CAAC,sBAAsB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;IAC1D,CAAC,gCAAgC,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3E,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC;IACvD,CAAC,gCAAgC,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;IACjE,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC,6BAA6B,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;IACxD,CAAC,wBAAwB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;IACtD,CAAC,wBAAwB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxD,CAAC,eAAe,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IAC5C,CAAC,2BAA2B,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IACzD,CAAC,+BAA+B,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;IAChE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;IACtD;QACI,eAAe;QACf,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC;KAClE;IACD,CAAC,2BAA2B,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3E,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IACxD,CAAC,qBAAqB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IAC1D;QACI,wDAAwD;QACxD,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE;KACjC;CACK,CAAC;AAEX,mEAAmE;AACnE,MAAM,cAAc,GAA4C;IAC5D,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE;IAChC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;IAC7C,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE;IACvC,IAAI,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE;IAC7C,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE;IAC7B,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE;CACpC,CAAC;AAEF,0FAA0F;AAC1F,yBAAyB;AACzB,gCAAgC;AAChC,6CAA6C;AAC7C,uCAAuC;AACvC,mCAAmC;AACnC,qDAAqD;AACrD,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,QAAgB;IAC3D,KAAK,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,aAAa,EAAE,CAAC;QAC/C,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,OAAO,SAAS,EAAE,CAAC;QACvB,CAAC;IACL,CAAC;IAED,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC1C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,QAAQ,EAAE,CAAC;IACtB,CAAC;IAED,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;AAC9B,CAAC"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "create-mock-api-from-types",
3
+ "type": "module",
4
+ "version": "1.0.0",
5
+ "description": "Generate a mock REST API server from TypeScript interfaces",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "mock-api": "dist/cli.js"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.js",
14
+ "types": "./dist/index.d.ts"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "keywords": [
21
+ "mock",
22
+ "api",
23
+ "faker",
24
+ "typescript",
25
+ "rest",
26
+ "testing"
27
+ ],
28
+ "author": "",
29
+ "license": "ISC",
30
+ "devDependencies": {
31
+ "@types/express": "^5.0.6",
32
+ "@types/node": "^25.5.0",
33
+ "ts-node": "^10.9.2",
34
+ "tsx": "^4.21.0",
35
+ "typescript": "^5.9.3"
36
+ },
37
+ "dependencies": {
38
+ "@faker-js/faker": "^10.3.0",
39
+ "commander": "^14.0.3",
40
+ "express": "^5.2.1",
41
+ "ts-morph": "^27.0.2"
42
+ },
43
+ "scripts": {
44
+ "build": "tsc",
45
+ "postbuild": "chmod +x dist/cli.js",
46
+ "start": "node dist/cli.js",
47
+ "dev": "tsx src/cli.ts",
48
+ "typecheck": "tsc --noEmit"
49
+ }
50
+ }