@yrest/cli 0.5.3

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.
@@ -0,0 +1,257 @@
1
+ import * as fastify from 'fastify';
2
+ import * as http from 'http';
3
+ import { z } from 'zod';
4
+
5
+ /** A single REST resource item. Field names and value types are user-defined in the YAML file. */
6
+ type Resource = Record<string, unknown>;
7
+ /**
8
+ * The full in-memory database.
9
+ * Keys are collection names (e.g. `"users"`); values are arrays of {@link Resource} items.
10
+ */
11
+ type Data = Record<string, Resource[]>;
12
+ /**
13
+ * Relational mappings declared under `_rel` in the YAML file.
14
+ *
15
+ * - Outer key: child collection name.
16
+ * - Inner key: foreign key field on the child.
17
+ * - Inner value: parent collection name.
18
+ *
19
+ * @example
20
+ * // Given: _rel: { posts: { userId: users } }
21
+ * // GET /users/1/posts → returns posts where userId === "1"
22
+ */
23
+ type Relations = Record<string, Record<string, string>>;
24
+ /**
25
+ * A single custom route declared under `_routes` in the YAML file.
26
+ *
27
+ * Custom routes are registered before resource routes and take priority over them.
28
+ * They always return a static, pre-defined response regardless of the request body or params.
29
+ *
30
+ * @example
31
+ * // _routes:
32
+ * // - method: POST
33
+ * // path: /login
34
+ * // response:
35
+ * // status: 200
36
+ * // body: { token: abc123 }
37
+ */
38
+ type CustomRoute = {
39
+ /** HTTP method (case-insensitive: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS). */
40
+ method: string;
41
+ /** URL path. May include Fastify path params (e.g. `/users/:id`). Must start with `/`. */
42
+ path: string;
43
+ /**
44
+ * Name of an exported function in the handlers file (`handlers:` in config).
45
+ * When set, the function is called on every request and its return value is used as the response.
46
+ * Takes priority over `response:`. Falls back to `response:` if the name is not found.
47
+ */
48
+ handler?: string;
49
+ /** Static or template response. Used when `handler` is absent or not found in the handlers file. */
50
+ response?: {
51
+ /** HTTP status code. Defaults to `200` if omitted. */
52
+ status?: number;
53
+ /** Response body. Any YAML-serialisable value (object, array, string, number, null). */
54
+ body?: unknown;
55
+ /** Additional response headers to set alongside `Content-Type`. */
56
+ headers?: Record<string, string>;
57
+ };
58
+ };
59
+ /**
60
+ * In-memory store backed by a YAML file.
61
+ *
62
+ * All reads operate on a live in-memory snapshot. Writes must be explicitly
63
+ * flushed to disk by calling {@link persist}. Use {@link reload} to pull in
64
+ * changes made to the file externally (e.g. in watch mode).
65
+ */
66
+ interface YamlStorage {
67
+ /** Returns the full in-memory dataset (all collections). */
68
+ getData(): Data;
69
+ /** Returns the relational mappings declared under `_rel`. */
70
+ getRelations(): Relations;
71
+ /**
72
+ * Returns the items in a named collection, or `undefined` if it does not exist.
73
+ *
74
+ * @param name - Collection name as declared in the YAML file (e.g. `"users"`).
75
+ */
76
+ getCollection(name: string): Resource[] | undefined;
77
+ /**
78
+ * Replaces the items of a named collection in memory.
79
+ * Call {@link persist} afterwards to flush the change to disk.
80
+ *
81
+ * @param name - Collection name.
82
+ * @param items - New array of items to store.
83
+ */
84
+ setCollection(name: string, items: Resource[]): void;
85
+ /**
86
+ * Atomically writes the current in-memory state to the YAML file.
87
+ *
88
+ * Uses a write-to-temp-then-rename strategy so a crash during the write
89
+ * never leaves the file in a partially written state.
90
+ *
91
+ * @throws {Error} If the filesystem write or rename fails.
92
+ */
93
+ persist(): void;
94
+ /**
95
+ * Reloads the YAML file from disk and updates the in-memory state in place.
96
+ *
97
+ * Mutates the existing `data` and `relations` objects rather than replacing them,
98
+ * so any code holding a reference to the storage instance sees the updated values
99
+ * without needing to re-fetch.
100
+ *
101
+ * @throws {Error} If the file cannot be read or the YAML is malformed.
102
+ */
103
+ reload(): void;
104
+ /**
105
+ * Returns the custom routes declared under `_routes` in the YAML file.
106
+ * Returns an empty array if no `_routes` block is present.
107
+ */
108
+ getRoutes(): CustomRoute[];
109
+ /**
110
+ * Returns the saved snapshot: a frozen copy of `data` and `relations` taken
111
+ * at the last call to {@link saveSnapshot} (or at construction time).
112
+ */
113
+ getSnapshot(): {
114
+ data: Data;
115
+ relations: Relations;
116
+ savedAt: Date;
117
+ };
118
+ /**
119
+ * Replaces the stored snapshot with a deep copy of the current in-memory state.
120
+ * Future calls to {@link resetToSnapshot} will restore to this point.
121
+ */
122
+ saveSnapshot(): void;
123
+ /**
124
+ * Restores the in-memory state to the last saved snapshot and persists to disk.
125
+ * Mutates `data` and `relations` in place so existing references stay valid.
126
+ *
127
+ * @throws {Error} If the filesystem write fails.
128
+ */
129
+ resetToSnapshot(): void;
130
+ }
131
+
132
+ /**
133
+ * Creates a {@link YamlStorage} instance backed by the given YAML file.
134
+ *
135
+ * The file is read and parsed eagerly on construction. The `_rel` key is
136
+ * extracted as relational metadata; `_routes` as custom route declarations;
137
+ * all other top-level keys become collections.
138
+ *
139
+ * @param filePath - Relative or absolute path to the YAML database file.
140
+ * @throws {Error} If the file cannot be read or its YAML is invalid.
141
+ */
142
+ declare function createYamlStorage(filePath: string): YamlStorage;
143
+
144
+ /**
145
+ * Zod schema for all server runtime options.
146
+ *
147
+ * Validates and normalises options from three sources in ascending priority:
148
+ * schema defaults → `yrest.config.yml` → explicit CLI flags.
149
+ */
150
+ declare const serverOptionsSchema: z.ZodObject<{
151
+ /** Path to the YAML database file. Must be a non-empty string. */
152
+ file: z.ZodString;
153
+ /** TCP port the server listens on. Accepts string input and coerces to number. */
154
+ port: z.ZodDefault<z.ZodNumber>;
155
+ /** Hostname or IP address to bind. */
156
+ host: z.ZodDefault<z.ZodString>;
157
+ /**
158
+ * URL prefix prepended to every route (e.g. `/api`).
159
+ * A leading slash is added automatically if omitted.
160
+ */
161
+ base: z.ZodEffects<z.ZodDefault<z.ZodString>, string, string | undefined>;
162
+ /** When `true`, the server reloads the YAML file automatically on disk changes. */
163
+ watch: z.ZodDefault<z.ZodBoolean>;
164
+ /**
165
+ * When `true`, saves a snapshot of the initial database state on startup.
166
+ * Exposes `/_snapshot` endpoints to inspect, restore or update the snapshot.
167
+ */
168
+ snapshot: z.ZodDefault<z.ZodBoolean>;
169
+ /** When `true`, all mutating requests (POST, PUT, PATCH, DELETE) are rejected with 405. */
170
+ readonly: z.ZodDefault<z.ZodBoolean>;
171
+ /** Milliseconds to delay every response, simulating network latency. `0` = disabled. */
172
+ delay: z.ZodDefault<z.ZodNumber>;
173
+ /**
174
+ * Path to a JavaScript file exporting handler functions for custom routes.
175
+ * When set, functions are loaded at startup and referenced by name via `handler:` in `_routes`.
176
+ */
177
+ handlers: z.ZodOptional<z.ZodString>;
178
+ /**
179
+ * Wraps GET collection responses in a `{ data, pagination }` envelope.
180
+ * Accepts `true` (default limit 10), `false` (disabled), or a positive integer (custom limit).
181
+ */
182
+ pageable: z.ZodEffects<z.ZodDefault<z.ZodUnion<[z.ZodBoolean, z.ZodNumber]>>, {
183
+ enabled: boolean;
184
+ limit: number;
185
+ }, number | boolean | undefined>;
186
+ }, "strip", z.ZodTypeAny, {
187
+ file: string;
188
+ port: number;
189
+ host: string;
190
+ base: string;
191
+ watch: boolean;
192
+ snapshot: boolean;
193
+ readonly: boolean;
194
+ delay: number;
195
+ pageable: {
196
+ enabled: boolean;
197
+ limit: number;
198
+ };
199
+ handlers?: string | undefined;
200
+ }, {
201
+ file: string;
202
+ port?: number | undefined;
203
+ host?: string | undefined;
204
+ base?: string | undefined;
205
+ watch?: boolean | undefined;
206
+ snapshot?: boolean | undefined;
207
+ readonly?: boolean | undefined;
208
+ delay?: number | undefined;
209
+ handlers?: string | undefined;
210
+ pageable?: number | boolean | undefined;
211
+ }>;
212
+ /**
213
+ * Resolved server configuration after Zod validation and transformation.
214
+ * Inferred from {@link serverOptionsSchema}.
215
+ */
216
+ type ServerOptions = z.infer<typeof serverOptionsSchema>;
217
+
218
+ /** Incoming request data passed to every handler function. */
219
+ type HandlerRequest = {
220
+ params: Record<string, string>;
221
+ query: Record<string, string | string[]>;
222
+ body: unknown;
223
+ headers: Record<string, string | string[]>;
224
+ };
225
+ /** Value returned by a handler function to describe the HTTP response. */
226
+ type HandlerResponse = {
227
+ /** HTTP status code. Defaults to `200` if omitted. */
228
+ status?: number;
229
+ /** Response body. Any JSON-serialisable value. */
230
+ body?: unknown;
231
+ /** Additional response headers to set on the reply. */
232
+ headers?: Record<string, string>;
233
+ };
234
+ /** A function that handles a custom route request and returns a response descriptor. */
235
+ type Handler = (req: HandlerRequest) => HandlerResponse | Promise<HandlerResponse>;
236
+ /** Map of exported function name → handler function. */
237
+ type HandlerMap = Map<string, Handler>;
238
+
239
+ /**
240
+ * Creates and configures a Fastify instance wired to the given YAML storage.
241
+ *
242
+ * Registers, in order:
243
+ * 1. CORS (all origins, permissive defaults).
244
+ * 2. Readonly guard hook — rejects mutating requests with 405 when enabled.
245
+ * 3. Delay hook — defers `onSend` by N ms when enabled.
246
+ * 4. All route commands: about, snapshot (optional), custom routes, resource routes.
247
+ *
248
+ * The server is returned before `listen()` is called so tests can inject
249
+ * requests without binding a real port.
250
+ *
251
+ * @param storage - Initialised YAML storage instance.
252
+ * @param options - Validated server options.
253
+ * @param handlers - Map of named handler functions loaded from yrest.handlers.js.
254
+ */
255
+ declare function createServer(storage: YamlStorage, options: ServerOptions, handlers?: HandlerMap): Promise<fastify.FastifyInstance<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>, http.IncomingMessage, http.ServerResponse<http.IncomingMessage>, fastify.FastifyBaseLogger, fastify.FastifyTypeProviderDefault>>;
256
+
257
+ export { type CustomRoute, type Data, type Handler, type HandlerMap, type HandlerRequest, type HandlerResponse, type Relations, type Resource, type ServerOptions, type YamlStorage, createServer, createYamlStorage, serverOptionsSchema };
@@ -0,0 +1,257 @@
1
+ import * as fastify from 'fastify';
2
+ import * as http from 'http';
3
+ import { z } from 'zod';
4
+
5
+ /** A single REST resource item. Field names and value types are user-defined in the YAML file. */
6
+ type Resource = Record<string, unknown>;
7
+ /**
8
+ * The full in-memory database.
9
+ * Keys are collection names (e.g. `"users"`); values are arrays of {@link Resource} items.
10
+ */
11
+ type Data = Record<string, Resource[]>;
12
+ /**
13
+ * Relational mappings declared under `_rel` in the YAML file.
14
+ *
15
+ * - Outer key: child collection name.
16
+ * - Inner key: foreign key field on the child.
17
+ * - Inner value: parent collection name.
18
+ *
19
+ * @example
20
+ * // Given: _rel: { posts: { userId: users } }
21
+ * // GET /users/1/posts → returns posts where userId === "1"
22
+ */
23
+ type Relations = Record<string, Record<string, string>>;
24
+ /**
25
+ * A single custom route declared under `_routes` in the YAML file.
26
+ *
27
+ * Custom routes are registered before resource routes and take priority over them.
28
+ * They always return a static, pre-defined response regardless of the request body or params.
29
+ *
30
+ * @example
31
+ * // _routes:
32
+ * // - method: POST
33
+ * // path: /login
34
+ * // response:
35
+ * // status: 200
36
+ * // body: { token: abc123 }
37
+ */
38
+ type CustomRoute = {
39
+ /** HTTP method (case-insensitive: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS). */
40
+ method: string;
41
+ /** URL path. May include Fastify path params (e.g. `/users/:id`). Must start with `/`. */
42
+ path: string;
43
+ /**
44
+ * Name of an exported function in the handlers file (`handlers:` in config).
45
+ * When set, the function is called on every request and its return value is used as the response.
46
+ * Takes priority over `response:`. Falls back to `response:` if the name is not found.
47
+ */
48
+ handler?: string;
49
+ /** Static or template response. Used when `handler` is absent or not found in the handlers file. */
50
+ response?: {
51
+ /** HTTP status code. Defaults to `200` if omitted. */
52
+ status?: number;
53
+ /** Response body. Any YAML-serialisable value (object, array, string, number, null). */
54
+ body?: unknown;
55
+ /** Additional response headers to set alongside `Content-Type`. */
56
+ headers?: Record<string, string>;
57
+ };
58
+ };
59
+ /**
60
+ * In-memory store backed by a YAML file.
61
+ *
62
+ * All reads operate on a live in-memory snapshot. Writes must be explicitly
63
+ * flushed to disk by calling {@link persist}. Use {@link reload} to pull in
64
+ * changes made to the file externally (e.g. in watch mode).
65
+ */
66
+ interface YamlStorage {
67
+ /** Returns the full in-memory dataset (all collections). */
68
+ getData(): Data;
69
+ /** Returns the relational mappings declared under `_rel`. */
70
+ getRelations(): Relations;
71
+ /**
72
+ * Returns the items in a named collection, or `undefined` if it does not exist.
73
+ *
74
+ * @param name - Collection name as declared in the YAML file (e.g. `"users"`).
75
+ */
76
+ getCollection(name: string): Resource[] | undefined;
77
+ /**
78
+ * Replaces the items of a named collection in memory.
79
+ * Call {@link persist} afterwards to flush the change to disk.
80
+ *
81
+ * @param name - Collection name.
82
+ * @param items - New array of items to store.
83
+ */
84
+ setCollection(name: string, items: Resource[]): void;
85
+ /**
86
+ * Atomically writes the current in-memory state to the YAML file.
87
+ *
88
+ * Uses a write-to-temp-then-rename strategy so a crash during the write
89
+ * never leaves the file in a partially written state.
90
+ *
91
+ * @throws {Error} If the filesystem write or rename fails.
92
+ */
93
+ persist(): void;
94
+ /**
95
+ * Reloads the YAML file from disk and updates the in-memory state in place.
96
+ *
97
+ * Mutates the existing `data` and `relations` objects rather than replacing them,
98
+ * so any code holding a reference to the storage instance sees the updated values
99
+ * without needing to re-fetch.
100
+ *
101
+ * @throws {Error} If the file cannot be read or the YAML is malformed.
102
+ */
103
+ reload(): void;
104
+ /**
105
+ * Returns the custom routes declared under `_routes` in the YAML file.
106
+ * Returns an empty array if no `_routes` block is present.
107
+ */
108
+ getRoutes(): CustomRoute[];
109
+ /**
110
+ * Returns the saved snapshot: a frozen copy of `data` and `relations` taken
111
+ * at the last call to {@link saveSnapshot} (or at construction time).
112
+ */
113
+ getSnapshot(): {
114
+ data: Data;
115
+ relations: Relations;
116
+ savedAt: Date;
117
+ };
118
+ /**
119
+ * Replaces the stored snapshot with a deep copy of the current in-memory state.
120
+ * Future calls to {@link resetToSnapshot} will restore to this point.
121
+ */
122
+ saveSnapshot(): void;
123
+ /**
124
+ * Restores the in-memory state to the last saved snapshot and persists to disk.
125
+ * Mutates `data` and `relations` in place so existing references stay valid.
126
+ *
127
+ * @throws {Error} If the filesystem write fails.
128
+ */
129
+ resetToSnapshot(): void;
130
+ }
131
+
132
+ /**
133
+ * Creates a {@link YamlStorage} instance backed by the given YAML file.
134
+ *
135
+ * The file is read and parsed eagerly on construction. The `_rel` key is
136
+ * extracted as relational metadata; `_routes` as custom route declarations;
137
+ * all other top-level keys become collections.
138
+ *
139
+ * @param filePath - Relative or absolute path to the YAML database file.
140
+ * @throws {Error} If the file cannot be read or its YAML is invalid.
141
+ */
142
+ declare function createYamlStorage(filePath: string): YamlStorage;
143
+
144
+ /**
145
+ * Zod schema for all server runtime options.
146
+ *
147
+ * Validates and normalises options from three sources in ascending priority:
148
+ * schema defaults → `yrest.config.yml` → explicit CLI flags.
149
+ */
150
+ declare const serverOptionsSchema: z.ZodObject<{
151
+ /** Path to the YAML database file. Must be a non-empty string. */
152
+ file: z.ZodString;
153
+ /** TCP port the server listens on. Accepts string input and coerces to number. */
154
+ port: z.ZodDefault<z.ZodNumber>;
155
+ /** Hostname or IP address to bind. */
156
+ host: z.ZodDefault<z.ZodString>;
157
+ /**
158
+ * URL prefix prepended to every route (e.g. `/api`).
159
+ * A leading slash is added automatically if omitted.
160
+ */
161
+ base: z.ZodEffects<z.ZodDefault<z.ZodString>, string, string | undefined>;
162
+ /** When `true`, the server reloads the YAML file automatically on disk changes. */
163
+ watch: z.ZodDefault<z.ZodBoolean>;
164
+ /**
165
+ * When `true`, saves a snapshot of the initial database state on startup.
166
+ * Exposes `/_snapshot` endpoints to inspect, restore or update the snapshot.
167
+ */
168
+ snapshot: z.ZodDefault<z.ZodBoolean>;
169
+ /** When `true`, all mutating requests (POST, PUT, PATCH, DELETE) are rejected with 405. */
170
+ readonly: z.ZodDefault<z.ZodBoolean>;
171
+ /** Milliseconds to delay every response, simulating network latency. `0` = disabled. */
172
+ delay: z.ZodDefault<z.ZodNumber>;
173
+ /**
174
+ * Path to a JavaScript file exporting handler functions for custom routes.
175
+ * When set, functions are loaded at startup and referenced by name via `handler:` in `_routes`.
176
+ */
177
+ handlers: z.ZodOptional<z.ZodString>;
178
+ /**
179
+ * Wraps GET collection responses in a `{ data, pagination }` envelope.
180
+ * Accepts `true` (default limit 10), `false` (disabled), or a positive integer (custom limit).
181
+ */
182
+ pageable: z.ZodEffects<z.ZodDefault<z.ZodUnion<[z.ZodBoolean, z.ZodNumber]>>, {
183
+ enabled: boolean;
184
+ limit: number;
185
+ }, number | boolean | undefined>;
186
+ }, "strip", z.ZodTypeAny, {
187
+ file: string;
188
+ port: number;
189
+ host: string;
190
+ base: string;
191
+ watch: boolean;
192
+ snapshot: boolean;
193
+ readonly: boolean;
194
+ delay: number;
195
+ pageable: {
196
+ enabled: boolean;
197
+ limit: number;
198
+ };
199
+ handlers?: string | undefined;
200
+ }, {
201
+ file: string;
202
+ port?: number | undefined;
203
+ host?: string | undefined;
204
+ base?: string | undefined;
205
+ watch?: boolean | undefined;
206
+ snapshot?: boolean | undefined;
207
+ readonly?: boolean | undefined;
208
+ delay?: number | undefined;
209
+ handlers?: string | undefined;
210
+ pageable?: number | boolean | undefined;
211
+ }>;
212
+ /**
213
+ * Resolved server configuration after Zod validation and transformation.
214
+ * Inferred from {@link serverOptionsSchema}.
215
+ */
216
+ type ServerOptions = z.infer<typeof serverOptionsSchema>;
217
+
218
+ /** Incoming request data passed to every handler function. */
219
+ type HandlerRequest = {
220
+ params: Record<string, string>;
221
+ query: Record<string, string | string[]>;
222
+ body: unknown;
223
+ headers: Record<string, string | string[]>;
224
+ };
225
+ /** Value returned by a handler function to describe the HTTP response. */
226
+ type HandlerResponse = {
227
+ /** HTTP status code. Defaults to `200` if omitted. */
228
+ status?: number;
229
+ /** Response body. Any JSON-serialisable value. */
230
+ body?: unknown;
231
+ /** Additional response headers to set on the reply. */
232
+ headers?: Record<string, string>;
233
+ };
234
+ /** A function that handles a custom route request and returns a response descriptor. */
235
+ type Handler = (req: HandlerRequest) => HandlerResponse | Promise<HandlerResponse>;
236
+ /** Map of exported function name → handler function. */
237
+ type HandlerMap = Map<string, Handler>;
238
+
239
+ /**
240
+ * Creates and configures a Fastify instance wired to the given YAML storage.
241
+ *
242
+ * Registers, in order:
243
+ * 1. CORS (all origins, permissive defaults).
244
+ * 2. Readonly guard hook — rejects mutating requests with 405 when enabled.
245
+ * 3. Delay hook — defers `onSend` by N ms when enabled.
246
+ * 4. All route commands: about, snapshot (optional), custom routes, resource routes.
247
+ *
248
+ * The server is returned before `listen()` is called so tests can inject
249
+ * requests without binding a real port.
250
+ *
251
+ * @param storage - Initialised YAML storage instance.
252
+ * @param options - Validated server options.
253
+ * @param handlers - Map of named handler functions loaded from yrest.handlers.js.
254
+ */
255
+ declare function createServer(storage: YamlStorage, options: ServerOptions, handlers?: HandlerMap): Promise<fastify.FastifyInstance<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>, http.IncomingMessage, http.ServerResponse<http.IncomingMessage>, fastify.FastifyBaseLogger, fastify.FastifyTypeProviderDefault>>;
256
+
257
+ export { type CustomRoute, type Data, type Handler, type HandlerMap, type HandlerRequest, type HandlerResponse, type Relations, type Resource, type ServerOptions, type YamlStorage, createServer, createYamlStorage, serverOptionsSchema };