@yrest/cli 0.5.3 → 0.6.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aggiovato
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -63,6 +63,7 @@ A YAML-first alternative to json-server for frontend development.
63
63
  | Readonly mode | ✅ | ❌ |
64
64
  | Atomic writes | ✅ | ✅ |
65
65
  | TypeScript types | ✅ | ❌ |
66
+ | Programmatic API for test frameworks | ✅ | ❌ |
66
67
 
67
68
  ---
68
69
 
@@ -694,6 +695,139 @@ const session = await fetch("http://localhost:3070/login", {
694
695
  // → { token: "tok-ana@test.com" }
695
696
  ```
696
697
 
698
+ ## Programmatic API
699
+
700
+ Use yRest directly inside your test suite — no CLI, no separate process to manage.
701
+
702
+ Install as a dev dependency:
703
+
704
+ ```bash
705
+ npm install -D @yrest/cli
706
+ ```
707
+
708
+ ### `createYrestServer`
709
+
710
+ Creates a server instance that you control with `start()` and `stop()`. Accepts either inline YAML data or a path to a `db.yml` file.
711
+
712
+ ```ts
713
+ import { createYrestServer, yrest } from "@yrest/cli";
714
+ ```
715
+
716
+ **Options:**
717
+
718
+ | Option | Type | Default | Description |
719
+ | ---------- | ----------------- | ----------- | ------------------------------------------------------ |
720
+ | `data` | `Data` | — | Inline data object (use with `yrest\`...\``) |
721
+ | `file` | `string` | — | Path to a `db.yml` file (`data` or `file` is required) |
722
+ | `port` | `number` | `3070` | TCP port. Use `0` to get a random available port |
723
+ | `host` | `string` | `localhost` | Host to bind |
724
+ | `base` | `string` | — | URL prefix for all routes (e.g. `"/api"`) |
725
+ | `readonly` | `boolean` | `false` | Reject all write operations with `405` |
726
+ | `delay` | `number` | `0` | Fixed delay in ms added to every response |
727
+ | `pageable` | `boolean\|number` | `false` | Wrap GET responses in `{ data, pagination }` envelope |
728
+ | `snapshot` | `boolean` | `false` | Enable snapshot endpoints (`/_snapshot`) |
729
+ | `handlers` | `string` | — | Path to a JS file exporting handler functions |
730
+
731
+ **Returned handle:**
732
+
733
+ | Member | Description |
734
+ | --------- | -------------------------------------------------------- |
735
+ | `start()` | Starts the server and begins listening |
736
+ | `stop()` | Gracefully closes the server |
737
+ | `port` | The actual port after `start()` (useful when `port: 0`) |
738
+ | `url` | Base URL after `start()` (e.g. `http://localhost:49821`) |
739
+
740
+ ### `yrest` tagged template literal
741
+
742
+ Parses inline YAML into a data object. Strips common leading indentation automatically, so you can indent naturally inside your test files. Supports interpolated values.
743
+
744
+ ```ts
745
+ const data = yrest`
746
+ users:
747
+ - id: 1
748
+ name: Ana
749
+ posts:
750
+ - id: 1
751
+ title: First post
752
+ userId: 1
753
+ `;
754
+ ```
755
+
756
+ ### Vitest example
757
+
758
+ ```ts
759
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
760
+ import { createYrestServer, yrest } from "@yrest/cli";
761
+
762
+ const server = createYrestServer({
763
+ data: yrest`
764
+ users:
765
+ - id: 1
766
+ name: Ana
767
+ - id: 2
768
+ name: Luis
769
+ `,
770
+ port: 0, // random port — no conflicts between parallel tests
771
+ readonly: true,
772
+ });
773
+
774
+ beforeAll(() => server.start());
775
+ afterAll(() => server.stop());
776
+
777
+ describe("users API", () => {
778
+ it("returns all users", async () => {
779
+ const res = await fetch(`${server.url}/users`);
780
+ expect(res.status).toBe(200);
781
+ expect(await res.json()).toHaveLength(2);
782
+ });
783
+
784
+ it("returns a single user", async () => {
785
+ const res = await fetch(`${server.url}/users/1`);
786
+ expect(await res.json()).toMatchObject({ name: "Ana" });
787
+ });
788
+ });
789
+ ```
790
+
791
+ ### Playwright example
792
+
793
+ ```ts
794
+ // tests/api.spec.ts
795
+ import { test, expect, beforeAll, afterAll } from "@playwright/test";
796
+ import { createYrestServer, yrest } from "@yrest/cli";
797
+
798
+ const server = createYrestServer({
799
+ data: yrest`
800
+ users:
801
+ - id: 1
802
+ name: Ana
803
+ `,
804
+ port: 0,
805
+ readonly: true,
806
+ });
807
+
808
+ beforeAll(() => server.start());
809
+ afterAll(() => server.stop());
810
+
811
+ test("lists users", async () => {
812
+ const res = await fetch(`${server.url}/users`);
813
+ expect(await res.json()).toHaveLength(1);
814
+ });
815
+ ```
816
+
817
+ ### File-based example
818
+
819
+ When your test data is too large for inline YAML, point to a file:
820
+
821
+ ```ts
822
+ const server = createYrestServer({
823
+ file: "./tests/fixtures/db.yml",
824
+ port: 0,
825
+ readonly: true,
826
+ });
827
+ ```
828
+
829
+ ---
830
+
697
831
  ## Use in package.json scripts
698
832
 
699
833
  ```json
@@ -708,6 +842,28 @@ const session = await fetch("http://localhost:3070/login", {
708
842
 
709
843
  ---
710
844
 
845
+ ## Roadmap
846
+
847
+ | Feature | Status |
848
+ | ------------------------------------------------- | ------ |
849
+ | Full CRUD from `db.yml` | ✅ |
850
+ | Field filters, operators, full-text search | ✅ |
851
+ | Relations, `_expand`, `_embed`, nested routes | ✅ |
852
+ | Pagination, sorting, field projection | ✅ |
853
+ | Watch, readonly, delay, snapshot modes | ✅ |
854
+ | Custom routes (`_routes`) with static responses | ✅ |
855
+ | Template variables in responses (`{{params.id}}`) | ✅ |
856
+ | Handler functions (`yrest.handlers.js`) | ✅ |
857
+ | Visual panel (`/_panel`) | 🔜 |
858
+ | Programmatic API for Vitest / Playwright | ✅ |
859
+ | Docker image | 🔜 |
860
+ | OpenAPI export (`yrest openapi db.yml`) | 🔜 |
861
+ | VS Code extension with YAML snippets | 🔜 |
862
+ | Request validation with JSON Schema | 🔜 |
863
+ | Conditional scenarios | 🔜 |
864
+
865
+ ---
866
+
711
867
  ## Contributing
712
868
 
713
869
  ### Prerequisites
package/dist/cli/index.js CHANGED
@@ -135,7 +135,7 @@ function registerInit(program2) {
135
135
  var import_node_fs5 = require("fs");
136
136
  var import_node_path3 = require("path");
137
137
 
138
- // src/storage/yamlStorage.ts
138
+ // src/storage/yrestStorage.ts
139
139
  var import_node_fs2 = require("fs");
140
140
  var import_node_path2 = require("path");
141
141
  var import_node_crypto = require("crypto");
@@ -148,8 +148,8 @@ function deepCopyData(source) {
148
148
  );
149
149
  }
150
150
 
151
- // src/storage/yamlStorage.ts
152
- function createYamlStorage(filePath) {
151
+ // src/storage/yrestStorage.ts
152
+ function createYrestStorage(filePath) {
153
153
  const absPath = (0, import_node_path2.resolve)(filePath);
154
154
  const raw = (0, import_yaml.parse)((0, import_node_fs2.readFileSync)(absPath, "utf8")) ?? {};
155
155
  const relations = raw["_rel"] ?? {};
@@ -1231,9 +1231,37 @@ async function createServer(storage, options, handlers = /* @__PURE__ */ new Map
1231
1231
  return server;
1232
1232
  }
1233
1233
 
1234
+ // src/server/yrestServer.ts
1235
+ function createYrestServerFromStorage(storage, options, handlers = /* @__PURE__ */ new Map()) {
1236
+ let _port = 0;
1237
+ let _started = false;
1238
+ let _fastify = null;
1239
+ return {
1240
+ async start() {
1241
+ if (_started) return;
1242
+ _fastify = await createServer(storage, options, handlers);
1243
+ await _fastify.listen({ port: options.port, host: options.host });
1244
+ const address = _fastify.addresses()[0];
1245
+ _port = typeof address === "object" && "port" in address ? address.port : options.port;
1246
+ _started = true;
1247
+ },
1248
+ async stop() {
1249
+ if (!_started || !_fastify) return;
1250
+ await _fastify.close();
1251
+ _started = false;
1252
+ },
1253
+ get port() {
1254
+ return _port;
1255
+ },
1256
+ get url() {
1257
+ return `http://${options.host}:${_port}${options.base}`;
1258
+ }
1259
+ };
1260
+ }
1261
+
1234
1262
  // src/config/loadOptions.ts
1235
1263
  var import_zod = require("zod");
1236
- var serverOptionsSchema = import_zod.z.object({
1264
+ var yrestOptionsSchema = import_zod.z.object({
1237
1265
  /** Path to the YAML database file. Must be a non-empty string. */
1238
1266
  file: import_zod.z.string().min(1),
1239
1267
  /** TCP port the server listens on. Accepts string input and coerces to number. */
@@ -1324,18 +1352,18 @@ function registerServe(program2) {
1324
1352
  ...cmd.args.length > 0 ? { file } : {},
1325
1353
  ...cliOverrides
1326
1354
  };
1327
- const options = serverOptionsSchema.parse(merged);
1355
+ const options = yrestOptionsSchema.parse(merged);
1328
1356
  let storage;
1329
1357
  try {
1330
- storage = createYamlStorage(options.file);
1358
+ storage = createYrestStorage(options.file);
1331
1359
  } catch (err) {
1332
1360
  const msg = err instanceof Error ? err.message : String(err);
1333
1361
  console.error(`Error: cannot load "${options.file}" \u2014 ${msg}`);
1334
1362
  process.exit(1);
1335
1363
  }
1336
1364
  const handlers = options.handlers ? await loadHandlers((0, import_node_path3.resolve)(options.handlers)) : /* @__PURE__ */ new Map();
1337
- const server = await createServer(storage, options, handlers);
1338
- await server.listen({ port: options.port, host: options.host });
1365
+ const yrestServer = createYrestServerFromStorage(storage, options, handlers);
1366
+ await yrestServer.start();
1339
1367
  const collections = Object.keys(storage.getData());
1340
1368
  const customRoutes = storage.getRoutes();
1341
1369
  const base = options.base || "/";
@@ -1355,7 +1383,7 @@ function registerServe(program2) {
1355
1383
  };
1356
1384
  console.log(
1357
1385
  `
1358
- ${b("yrest")} ${dim("\xB7")} ${green(`http://${options.host}:${options.port}`)}
1386
+ ${b("yrest")} ${dim("\xB7")} ${green(`http://${options.host}:${yrestServer.port}`)}
1359
1387
  `
1360
1388
  );
1361
1389
  console.log(` ${b("Collections")} ${dim(`(base: ${base})`)}:`);
@@ -108,7 +108,7 @@ function registerInit(program2) {
108
108
  import { watchFile } from "fs";
109
109
  import { join, resolve as resolve3 } from "path";
110
110
 
111
- // src/storage/yamlStorage.ts
111
+ // src/storage/yrestStorage.ts
112
112
  import { readFileSync, writeFileSync as writeFileSync2, renameSync } from "fs";
113
113
  import { resolve as resolve2, dirname } from "path";
114
114
  import { randomUUID } from "crypto";
@@ -121,8 +121,8 @@ function deepCopyData(source) {
121
121
  );
122
122
  }
123
123
 
124
- // src/storage/yamlStorage.ts
125
- function createYamlStorage(filePath) {
124
+ // src/storage/yrestStorage.ts
125
+ function createYrestStorage(filePath) {
126
126
  const absPath = resolve2(filePath);
127
127
  const raw = parse(readFileSync(absPath, "utf8")) ?? {};
128
128
  const relations = raw["_rel"] ?? {};
@@ -1204,9 +1204,37 @@ async function createServer(storage, options, handlers = /* @__PURE__ */ new Map
1204
1204
  return server;
1205
1205
  }
1206
1206
 
1207
+ // src/server/yrestServer.ts
1208
+ function createYrestServerFromStorage(storage, options, handlers = /* @__PURE__ */ new Map()) {
1209
+ let _port = 0;
1210
+ let _started = false;
1211
+ let _fastify = null;
1212
+ return {
1213
+ async start() {
1214
+ if (_started) return;
1215
+ _fastify = await createServer(storage, options, handlers);
1216
+ await _fastify.listen({ port: options.port, host: options.host });
1217
+ const address = _fastify.addresses()[0];
1218
+ _port = typeof address === "object" && "port" in address ? address.port : options.port;
1219
+ _started = true;
1220
+ },
1221
+ async stop() {
1222
+ if (!_started || !_fastify) return;
1223
+ await _fastify.close();
1224
+ _started = false;
1225
+ },
1226
+ get port() {
1227
+ return _port;
1228
+ },
1229
+ get url() {
1230
+ return `http://${options.host}:${_port}${options.base}`;
1231
+ }
1232
+ };
1233
+ }
1234
+
1207
1235
  // src/config/loadOptions.ts
1208
1236
  import { z } from "zod";
1209
- var serverOptionsSchema = z.object({
1237
+ var yrestOptionsSchema = z.object({
1210
1238
  /** Path to the YAML database file. Must be a non-empty string. */
1211
1239
  file: z.string().min(1),
1212
1240
  /** TCP port the server listens on. Accepts string input and coerces to number. */
@@ -1297,18 +1325,18 @@ function registerServe(program2) {
1297
1325
  ...cmd.args.length > 0 ? { file } : {},
1298
1326
  ...cliOverrides
1299
1327
  };
1300
- const options = serverOptionsSchema.parse(merged);
1328
+ const options = yrestOptionsSchema.parse(merged);
1301
1329
  let storage;
1302
1330
  try {
1303
- storage = createYamlStorage(options.file);
1331
+ storage = createYrestStorage(options.file);
1304
1332
  } catch (err) {
1305
1333
  const msg = err instanceof Error ? err.message : String(err);
1306
1334
  console.error(`Error: cannot load "${options.file}" \u2014 ${msg}`);
1307
1335
  process.exit(1);
1308
1336
  }
1309
1337
  const handlers = options.handlers ? await loadHandlers(resolve3(options.handlers)) : /* @__PURE__ */ new Map();
1310
- const server = await createServer(storage, options, handlers);
1311
- await server.listen({ port: options.port, host: options.host });
1338
+ const yrestServer = createYrestServerFromStorage(storage, options, handlers);
1339
+ await yrestServer.start();
1312
1340
  const collections = Object.keys(storage.getData());
1313
1341
  const customRoutes = storage.getRoutes();
1314
1342
  const base = options.base || "/";
@@ -1328,7 +1356,7 @@ function registerServe(program2) {
1328
1356
  };
1329
1357
  console.log(
1330
1358
  `
1331
- ${b("yrest")} ${dim("\xB7")} ${green(`http://${options.host}:${options.port}`)}
1359
+ ${b("yrest")} ${dim("\xB7")} ${green(`http://${options.host}:${yrestServer.port}`)}
1332
1360
  `
1333
1361
  );
1334
1362
  console.log(` ${b("Collections")} ${dim(`(base: ${base})`)}:`);
package/dist/index.d.mts 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>;
@@ -63,7 +63,7 @@ type CustomRoute = {
63
63
  * flushed to disk by calling {@link persist}. Use {@link reload} to pull in
64
64
  * changes made to the file externally (e.g. in watch mode).
65
65
  */
66
- interface YamlStorage {
66
+ interface YrestStorage {
67
67
  /** Returns the full in-memory dataset (all collections). */
68
68
  getData(): Data;
69
69
  /** Returns the relational mappings declared under `_rel`. */
@@ -130,16 +130,27 @@ interface YamlStorage {
130
130
  }
131
131
 
132
132
  /**
133
- * Creates a {@link YamlStorage} instance backed by the given YAML file.
133
+ * Tagged template literal that parses inline YAML into a yRest {@link Data} object.
134
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.
135
+ * Strips common leading indentation automatically, so the template can be
136
+ * indented naturally inside the calling function without affecting YAML parsing.
138
137
  *
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.
138
+ * Supports interpolated values they are stringified and inserted inline.
139
+ *
140
+ * @throws {Error} If the template is not valid YAML or does not resolve to a plain object.
141
+ *
142
+ * @example
143
+ * const data = yrest`
144
+ * users:
145
+ * - id: 1
146
+ * name: Ana
147
+ * posts:
148
+ * - id: 1
149
+ * title: First post
150
+ * userId: 1
151
+ * `;
141
152
  */
142
- declare function createYamlStorage(filePath: string): YamlStorage;
153
+ declare function yrest(strings: TemplateStringsArray, ...values: unknown[]): Data;
143
154
 
144
155
  /**
145
156
  * Zod schema for all server runtime options.
@@ -147,7 +158,7 @@ declare function createYamlStorage(filePath: string): YamlStorage;
147
158
  * Validates and normalises options from three sources in ascending priority:
148
159
  * schema defaults → `yrest.config.yml` → explicit CLI flags.
149
160
  */
150
- declare const serverOptionsSchema: z.ZodObject<{
161
+ declare const yrestOptionsSchema: z.ZodObject<{
151
162
  /** Path to the YAML database file. Must be a non-empty string. */
152
163
  file: z.ZodString;
153
164
  /** TCP port the server listens on. Accepts string input and coerces to number. */
@@ -211,9 +222,9 @@ declare const serverOptionsSchema: z.ZodObject<{
211
222
  }>;
212
223
  /**
213
224
  * Resolved server configuration after Zod validation and transformation.
214
- * Inferred from {@link serverOptionsSchema}.
225
+ * Inferred from {@link yrestOptionsSchema}.
215
226
  */
216
- type ServerOptions = z.infer<typeof serverOptionsSchema>;
227
+ type YrestOptions = z.infer<typeof yrestOptionsSchema>;
217
228
 
218
229
  /** Incoming request data passed to every handler function. */
219
230
  type HandlerRequest = {
@@ -252,6 +263,113 @@ type HandlerMap = Map<string, Handler>;
252
263
  * @param options - Validated server options.
253
264
  * @param handlers - Map of named handler functions loaded from yrest.handlers.js.
254
265
  */
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>>;
266
+ 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>>;
267
+
268
+ /** Handle returned by {@link createYrestServerFromStorage} and {@link createYrestServer}. */
269
+ interface YrestServer {
270
+ /** Starts the server and begins listening on the configured port. */
271
+ start(): Promise<void>;
272
+ /** Gracefully closes the server. */
273
+ stop(): Promise<void>;
274
+ /** The port the server is listening on. Only valid after `start()`. */
275
+ readonly port: number;
276
+ /** The base URL of the server (e.g. `http://localhost:3070`). Only valid after `start()`. */
277
+ readonly url: string;
278
+ }
279
+ /**
280
+ * Creates a {@link YrestServer} from an already-initialised storage and parsed options.
281
+ *
282
+ * This is the shared Fastify lifecycle owner used by both the CLI (`serve` command)
283
+ * and the programmatic API (`createYrestServer`). It is the only place where
284
+ * `createServer → listen → close` lives.
285
+ *
286
+ * Each consumer is responsible for building storage and resolving options before
287
+ * calling this function:
288
+ * - CLI (`serve.ts`): Zod parsing, config-file merging, `process.exit` error handling
289
+ * - Programmatic API (`createYrestServer`): raw → parsed options conversion, inline data support
290
+ *
291
+ * @param storage - Initialised storage instance (file-based or in-memory).
292
+ * @param options - Fully resolved and validated server options.
293
+ * @param handlers - Pre-loaded handler map. Defaults to an empty map.
294
+ */
295
+ declare function createYrestServerFromStorage(storage: YrestStorage, options: YrestOptions, handlers?: HandlerMap): YrestServer;
296
+
297
+ /** Base options shared between file-based and data-based server instances. */
298
+ type YrestServerBaseOptions = {
299
+ /** TCP port to listen on. Defaults to `3070`. Use `0` to get a random available port. */
300
+ port?: number;
301
+ /** Host to bind. Defaults to `"localhost"`. */
302
+ host?: string;
303
+ /** URL prefix prepended to every route (e.g. `"/api"`). */
304
+ base?: string;
305
+ /** Reject all mutating requests (POST, PUT, PATCH, DELETE) with `405`. */
306
+ readonly?: boolean;
307
+ /** Milliseconds to delay every response. */
308
+ delay?: number;
309
+ /** Wrap GET collection responses in `{ data, pagination }`. Pass `true` (limit 10) or a number. */
310
+ pageable?: boolean | number;
311
+ /** Save a snapshot at startup and expose `/_snapshot` endpoints. */
312
+ snapshot?: boolean;
313
+ /** Path to a JS file exporting handler functions for custom `_routes` entries. */
314
+ handlers?: string;
315
+ };
316
+ /**
317
+ * Options for {@link createYrestServer}.
318
+ * Either `file` (path to a `db.yml`) or `data` (inline object, e.g. from `yrest\`...\``) is required.
319
+ */
320
+ type YrestServerOptions = YrestServerBaseOptions & ({
321
+ file: string;
322
+ data?: never;
323
+ } | {
324
+ data: Data;
325
+ file?: never;
326
+ });
327
+ /**
328
+ * Creates a yRest server instance for programmatic use.
329
+ *
330
+ * Accepts either a `file` path to a `db.yml` or an inline `data` object
331
+ * (typically produced by the {@link yrest} tagged template literal).
332
+ *
333
+ * The server is not started until you call `start()`.
334
+ *
335
+ * @example — file-based (e.g. in Playwright globalSetup)
336
+ * ```ts
337
+ * const server = createYrestServer({ file: "./tests/db.yml", readonly: true });
338
+ * await server.start();
339
+ * // → http://localhost:3070
340
+ * await server.stop();
341
+ * ```
342
+ *
343
+ * @example — inline data (e.g. in Vitest)
344
+ * ```ts
345
+ * import { createYrestServer, yrest } from "@yrest/cli";
346
+ *
347
+ * const server = createYrestServer({
348
+ * data: yrest`
349
+ * users:
350
+ * - id: 1
351
+ * name: Ana
352
+ * `,
353
+ * port: 0,
354
+ * readonly: true,
355
+ * });
356
+ *
357
+ * beforeAll(() => server.start());
358
+ * afterAll(() => server.stop());
359
+ * ```
360
+ */
361
+ declare function createYrestServer(options: YrestServerOptions): YrestServer;
362
+
363
+ /**
364
+ * Creates a {@link YrestStorage} instance backed by the given YAML file.
365
+ *
366
+ * The file is read and parsed eagerly on construction. The `_rel` key is
367
+ * extracted as relational metadata; `_routes` as custom route declarations;
368
+ * all other top-level keys become collections.
369
+ *
370
+ * @param filePath - Relative or absolute path to the YAML database file.
371
+ * @throws {Error} If the file cannot be read or its YAML is invalid.
372
+ */
373
+ declare function createYrestStorage(filePath: string): YrestStorage;
256
374
 
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 };
375
+ 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 };