@yrest/cli 0.5.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +255 -0
- package/dist/cli/index.js +133 -17
- package/dist/cli/index.mjs +133 -17
- package/dist/index.d.mts +205 -30
- package/dist/index.d.ts +205 -30
- package/dist/index.js +363 -33
- package/dist/index.mjs +378 -41
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import * as fastify from 'fastify';
|
|
2
3
|
import * as http from 'http';
|
|
3
|
-
import { z } from 'zod';
|
|
4
4
|
|
|
5
5
|
/** A single REST resource item. Field names and value types are user-defined in the YAML file. */
|
|
6
6
|
type Resource = Record<string, unknown>;
|
|
@@ -21,19 +21,72 @@ type Data = Record<string, Resource[]>;
|
|
|
21
21
|
* // GET /users/1/posts → returns posts where userId === "1"
|
|
22
22
|
*/
|
|
23
23
|
type Relations = Record<string, Record<string, string>>;
|
|
24
|
+
/**
|
|
25
|
+
* A static response block shared by {@link CustomRoute} and {@link Scenario}.
|
|
26
|
+
*/
|
|
27
|
+
type RouteResponse = {
|
|
28
|
+
/** HTTP status code. Defaults to `200` if omitted. */
|
|
29
|
+
status?: number;
|
|
30
|
+
/** Response body. Any YAML-serialisable value (object, array, string, number, null). */
|
|
31
|
+
body?: unknown;
|
|
32
|
+
/** Additional response headers to set alongside `Content-Type`. */
|
|
33
|
+
headers?: Record<string, string>;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* A conditional response variant for a custom route.
|
|
37
|
+
*
|
|
38
|
+
* Evaluated in declaration order — the first scenario whose `when` conditions match wins.
|
|
39
|
+
* If none match, the route falls back to `otherwise:` (if defined) or `response:`.
|
|
40
|
+
*
|
|
41
|
+
* **`when` as an object** — all entries must match (AND):
|
|
42
|
+
* ```yaml
|
|
43
|
+
* when: { body.email: ana@test.com, body.password: secret }
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* **`when` as an array** — any group must match (OR of ANDs):
|
|
47
|
+
* ```yaml
|
|
48
|
+
* when:
|
|
49
|
+
* - { body.role: admin }
|
|
50
|
+
* - { body.role: superadmin }
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* Condition keys use dot-notation (`body.X`, `params.X`, `query.X`, `headers.X`).
|
|
54
|
+
* Field operator suffixes are supported: `_ne`, `_like`, `_start`, `_regex`, `_gte`, `_lte`.
|
|
55
|
+
* Response bodies support `{{}}` template variables (same as static routes).
|
|
56
|
+
*/
|
|
57
|
+
type Scenario = {
|
|
58
|
+
/**
|
|
59
|
+
* Condition(s) to evaluate against the request.
|
|
60
|
+
* - Object → all entries AND
|
|
61
|
+
* - Array of objects → any group OR (each group is AND internally)
|
|
62
|
+
*/
|
|
63
|
+
when: Record<string, unknown> | Record<string, unknown>[];
|
|
64
|
+
/** Response to return when the conditions match. Supports `{{}}` template variables. */
|
|
65
|
+
response: RouteResponse;
|
|
66
|
+
};
|
|
24
67
|
/**
|
|
25
68
|
* A single custom route declared under `_routes` in the YAML file.
|
|
26
69
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
70
|
+
* Resolution priority per request:
|
|
71
|
+
* 1. `handler` function (if defined and found in the handlers file)
|
|
72
|
+
* 2. First matching `scenario` (evaluated in declaration order)
|
|
73
|
+
* 3. `otherwise` block (explicit fallback when scenarios are defined but none matched)
|
|
74
|
+
* 4. Static `response` block (final fallback)
|
|
29
75
|
*
|
|
30
76
|
* @example
|
|
31
77
|
* // _routes:
|
|
32
78
|
* // - method: POST
|
|
33
79
|
* // path: /login
|
|
34
|
-
* //
|
|
35
|
-
* //
|
|
36
|
-
* //
|
|
80
|
+
* // scenarios:
|
|
81
|
+
* // - when: { body.password: secret }
|
|
82
|
+
* // response: { status: 200, body: { token: real-tok } }
|
|
83
|
+
* // - when:
|
|
84
|
+
* // - { body.role: admin }
|
|
85
|
+
* // - { body.role: superadmin }
|
|
86
|
+
* // response: { status: 200, body: { token: admin-tok } }
|
|
87
|
+
* // otherwise:
|
|
88
|
+
* // status: 401
|
|
89
|
+
* // body: { error: Invalid credentials }
|
|
37
90
|
*/
|
|
38
91
|
type CustomRoute = {
|
|
39
92
|
/** HTTP method (case-insensitive: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS). */
|
|
@@ -42,19 +95,23 @@ type CustomRoute = {
|
|
|
42
95
|
path: string;
|
|
43
96
|
/**
|
|
44
97
|
* Name of an exported function in the handlers file (`handlers:` in config).
|
|
45
|
-
*
|
|
46
|
-
* Takes priority over `response:`. Falls back to `response:` if the name is not found.
|
|
98
|
+
* Takes priority over `scenarios` and `response`. Falls back to `response` if not found.
|
|
47
99
|
*/
|
|
48
100
|
handler?: string;
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
101
|
+
/** Conditional response variants. Evaluated in order — first match wins. */
|
|
102
|
+
scenarios?: Scenario[];
|
|
103
|
+
/**
|
|
104
|
+
* Explicit fallback response when `scenarios` are defined but none matched.
|
|
105
|
+
* Takes priority over `response` when present. Supports `{{}}` template variables.
|
|
106
|
+
*/
|
|
107
|
+
otherwise?: RouteResponse;
|
|
108
|
+
/** Static or template response. Final fallback when no handler, scenario, or otherwise applies. */
|
|
109
|
+
response?: RouteResponse;
|
|
110
|
+
/**
|
|
111
|
+
* Per-route response delay in milliseconds. Overrides the global `--delay` option for this route.
|
|
112
|
+
* Applied before any response is sent, regardless of which path resolved the response.
|
|
113
|
+
*/
|
|
114
|
+
delay?: number;
|
|
58
115
|
};
|
|
59
116
|
/**
|
|
60
117
|
* In-memory store backed by a YAML file.
|
|
@@ -63,7 +120,7 @@ type CustomRoute = {
|
|
|
63
120
|
* flushed to disk by calling {@link persist}. Use {@link reload} to pull in
|
|
64
121
|
* changes made to the file externally (e.g. in watch mode).
|
|
65
122
|
*/
|
|
66
|
-
interface
|
|
123
|
+
interface YrestStorage {
|
|
67
124
|
/** Returns the full in-memory dataset (all collections). */
|
|
68
125
|
getData(): Data;
|
|
69
126
|
/** Returns the relational mappings declared under `_rel`. */
|
|
@@ -130,16 +187,27 @@ interface YamlStorage {
|
|
|
130
187
|
}
|
|
131
188
|
|
|
132
189
|
/**
|
|
133
|
-
*
|
|
190
|
+
* Tagged template literal that parses inline YAML into a yRest {@link Data} object.
|
|
134
191
|
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* all other top-level keys become collections.
|
|
192
|
+
* Strips common leading indentation automatically, so the template can be
|
|
193
|
+
* indented naturally inside the calling function without affecting YAML parsing.
|
|
138
194
|
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
195
|
+
* Supports interpolated values — they are stringified and inserted inline.
|
|
196
|
+
*
|
|
197
|
+
* @throws {Error} If the template is not valid YAML or does not resolve to a plain object.
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* const data = yrest`
|
|
201
|
+
* users:
|
|
202
|
+
* - id: 1
|
|
203
|
+
* name: Ana
|
|
204
|
+
* posts:
|
|
205
|
+
* - id: 1
|
|
206
|
+
* title: First post
|
|
207
|
+
* userId: 1
|
|
208
|
+
* `;
|
|
141
209
|
*/
|
|
142
|
-
declare function
|
|
210
|
+
declare function yrest(strings: TemplateStringsArray, ...values: unknown[]): Data;
|
|
143
211
|
|
|
144
212
|
/**
|
|
145
213
|
* Zod schema for all server runtime options.
|
|
@@ -147,7 +215,7 @@ declare function createYamlStorage(filePath: string): YamlStorage;
|
|
|
147
215
|
* Validates and normalises options from three sources in ascending priority:
|
|
148
216
|
* schema defaults → `yrest.config.yml` → explicit CLI flags.
|
|
149
217
|
*/
|
|
150
|
-
declare const
|
|
218
|
+
declare const yrestOptionsSchema: z.ZodObject<{
|
|
151
219
|
/** Path to the YAML database file. Must be a non-empty string. */
|
|
152
220
|
file: z.ZodString;
|
|
153
221
|
/** TCP port the server listens on. Accepts string input and coerces to number. */
|
|
@@ -211,9 +279,9 @@ declare const serverOptionsSchema: z.ZodObject<{
|
|
|
211
279
|
}>;
|
|
212
280
|
/**
|
|
213
281
|
* Resolved server configuration after Zod validation and transformation.
|
|
214
|
-
* Inferred from {@link
|
|
282
|
+
* Inferred from {@link yrestOptionsSchema}.
|
|
215
283
|
*/
|
|
216
|
-
type
|
|
284
|
+
type YrestOptions = z.infer<typeof yrestOptionsSchema>;
|
|
217
285
|
|
|
218
286
|
/** Incoming request data passed to every handler function. */
|
|
219
287
|
type HandlerRequest = {
|
|
@@ -252,6 +320,113 @@ type HandlerMap = Map<string, Handler>;
|
|
|
252
320
|
* @param options - Validated server options.
|
|
253
321
|
* @param handlers - Map of named handler functions loaded from yrest.handlers.js.
|
|
254
322
|
*/
|
|
255
|
-
declare function createServer(storage:
|
|
323
|
+
declare function createServer(storage: YrestStorage, options: YrestOptions, handlers?: HandlerMap): Promise<fastify.FastifyInstance<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>, http.IncomingMessage, http.ServerResponse<http.IncomingMessage>, fastify.FastifyBaseLogger, fastify.FastifyTypeProviderDefault>>;
|
|
324
|
+
|
|
325
|
+
/** Handle returned by {@link createYrestServerFromStorage} and {@link createYrestServer}. */
|
|
326
|
+
interface YrestServer {
|
|
327
|
+
/** Starts the server and begins listening on the configured port. */
|
|
328
|
+
start(): Promise<void>;
|
|
329
|
+
/** Gracefully closes the server. */
|
|
330
|
+
stop(): Promise<void>;
|
|
331
|
+
/** The port the server is listening on. Only valid after `start()`. */
|
|
332
|
+
readonly port: number;
|
|
333
|
+
/** The base URL of the server (e.g. `http://localhost:3070`). Only valid after `start()`. */
|
|
334
|
+
readonly url: string;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Creates a {@link YrestServer} from an already-initialised storage and parsed options.
|
|
338
|
+
*
|
|
339
|
+
* This is the shared Fastify lifecycle owner used by both the CLI (`serve` command)
|
|
340
|
+
* and the programmatic API (`createYrestServer`). It is the only place where
|
|
341
|
+
* `createServer → listen → close` lives.
|
|
342
|
+
*
|
|
343
|
+
* Each consumer is responsible for building storage and resolving options before
|
|
344
|
+
* calling this function:
|
|
345
|
+
* - CLI (`serve.ts`): Zod parsing, config-file merging, `process.exit` error handling
|
|
346
|
+
* - Programmatic API (`createYrestServer`): raw → parsed options conversion, inline data support
|
|
347
|
+
*
|
|
348
|
+
* @param storage - Initialised storage instance (file-based or in-memory).
|
|
349
|
+
* @param options - Fully resolved and validated server options.
|
|
350
|
+
* @param handlers - Pre-loaded handler map. Defaults to an empty map.
|
|
351
|
+
*/
|
|
352
|
+
declare function createYrestServerFromStorage(storage: YrestStorage, options: YrestOptions, handlers?: HandlerMap): YrestServer;
|
|
353
|
+
|
|
354
|
+
/** Base options shared between file-based and data-based server instances. */
|
|
355
|
+
type YrestServerBaseOptions = {
|
|
356
|
+
/** TCP port to listen on. Defaults to `3070`. Use `0` to get a random available port. */
|
|
357
|
+
port?: number;
|
|
358
|
+
/** Host to bind. Defaults to `"localhost"`. */
|
|
359
|
+
host?: string;
|
|
360
|
+
/** URL prefix prepended to every route (e.g. `"/api"`). */
|
|
361
|
+
base?: string;
|
|
362
|
+
/** Reject all mutating requests (POST, PUT, PATCH, DELETE) with `405`. */
|
|
363
|
+
readonly?: boolean;
|
|
364
|
+
/** Milliseconds to delay every response. */
|
|
365
|
+
delay?: number;
|
|
366
|
+
/** Wrap GET collection responses in `{ data, pagination }`. Pass `true` (limit 10) or a number. */
|
|
367
|
+
pageable?: boolean | number;
|
|
368
|
+
/** Save a snapshot at startup and expose `/_snapshot` endpoints. */
|
|
369
|
+
snapshot?: boolean;
|
|
370
|
+
/** Path to a JS file exporting handler functions for custom `_routes` entries. */
|
|
371
|
+
handlers?: string;
|
|
372
|
+
};
|
|
373
|
+
/**
|
|
374
|
+
* Options for {@link createYrestServer}.
|
|
375
|
+
* Either `file` (path to a `db.yml`) or `data` (inline object, e.g. from `yrest\`...\``) is required.
|
|
376
|
+
*/
|
|
377
|
+
type YrestServerOptions = YrestServerBaseOptions & ({
|
|
378
|
+
file: string;
|
|
379
|
+
data?: never;
|
|
380
|
+
} | {
|
|
381
|
+
data: Data;
|
|
382
|
+
file?: never;
|
|
383
|
+
});
|
|
384
|
+
/**
|
|
385
|
+
* Creates a yRest server instance for programmatic use.
|
|
386
|
+
*
|
|
387
|
+
* Accepts either a `file` path to a `db.yml` or an inline `data` object
|
|
388
|
+
* (typically produced by the {@link yrest} tagged template literal).
|
|
389
|
+
*
|
|
390
|
+
* The server is not started until you call `start()`.
|
|
391
|
+
*
|
|
392
|
+
* @example — file-based (e.g. in Playwright globalSetup)
|
|
393
|
+
* ```ts
|
|
394
|
+
* const server = createYrestServer({ file: "./tests/db.yml", readonly: true });
|
|
395
|
+
* await server.start();
|
|
396
|
+
* // → http://localhost:3070
|
|
397
|
+
* await server.stop();
|
|
398
|
+
* ```
|
|
399
|
+
*
|
|
400
|
+
* @example — inline data (e.g. in Vitest)
|
|
401
|
+
* ```ts
|
|
402
|
+
* import { createYrestServer, yrest } from "@yrest/cli";
|
|
403
|
+
*
|
|
404
|
+
* const server = createYrestServer({
|
|
405
|
+
* data: yrest`
|
|
406
|
+
* users:
|
|
407
|
+
* - id: 1
|
|
408
|
+
* name: Ana
|
|
409
|
+
* `,
|
|
410
|
+
* port: 0,
|
|
411
|
+
* readonly: true,
|
|
412
|
+
* });
|
|
413
|
+
*
|
|
414
|
+
* beforeAll(() => server.start());
|
|
415
|
+
* afterAll(() => server.stop());
|
|
416
|
+
* ```
|
|
417
|
+
*/
|
|
418
|
+
declare function createYrestServer(options: YrestServerOptions): YrestServer;
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Creates a {@link YrestStorage} instance backed by the given YAML file.
|
|
422
|
+
*
|
|
423
|
+
* The file is read and parsed eagerly on construction. The `_rel` key is
|
|
424
|
+
* extracted as relational metadata; `_routes` as custom route declarations;
|
|
425
|
+
* all other top-level keys become collections.
|
|
426
|
+
*
|
|
427
|
+
* @param filePath - Relative or absolute path to the YAML database file.
|
|
428
|
+
* @throws {Error} If the file cannot be read or its YAML is invalid.
|
|
429
|
+
*/
|
|
430
|
+
declare function createYrestStorage(filePath: string): YrestStorage;
|
|
256
431
|
|
|
257
|
-
export { type CustomRoute, type Data, type Handler, type HandlerMap, type HandlerRequest, type HandlerResponse, type Relations, type Resource, type
|
|
432
|
+
export { type CustomRoute, type Data, type Handler, type HandlerMap, type HandlerRequest, type HandlerResponse, type Relations, type Resource, type YrestOptions, type YrestServer, type YrestServerBaseOptions, type YrestServerOptions, type YrestStorage, createServer, createYrestServer, createYrestServerFromStorage, createYrestStorage, yrest, yrestOptionsSchema };
|