@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/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>;
@@ -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 };
package/dist/index.js CHANGED
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
8
11
  var __export = (target, all) => {
9
12
  for (var name in all)
10
13
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -27,20 +30,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
30
  ));
28
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
32
 
30
- // src/index.ts
31
- var src_exports = {};
32
- __export(src_exports, {
33
- createServer: () => createServer,
34
- createYamlStorage: () => createYamlStorage,
35
- serverOptionsSchema: () => serverOptionsSchema
33
+ // node_modules/tsup/assets/cjs_shims.js
34
+ var init_cjs_shims = __esm({
35
+ "node_modules/tsup/assets/cjs_shims.js"() {
36
+ "use strict";
37
+ }
36
38
  });
37
- module.exports = __toCommonJS(src_exports);
38
-
39
- // src/storage/yamlStorage.ts
40
- var import_node_fs = require("fs");
41
- var import_node_path = require("path");
42
- var import_node_crypto = require("crypto");
43
- var import_yaml = require("yaml");
44
39
 
45
40
  // src/utils/deepCopy.ts
46
41
  function deepCopyData(source) {
@@ -48,11 +43,21 @@ function deepCopyData(source) {
48
43
  Object.entries(source).map(([k, v]) => [k, v.map((item) => ({ ...item }))])
49
44
  );
50
45
  }
46
+ var init_deepCopy = __esm({
47
+ "src/utils/deepCopy.ts"() {
48
+ "use strict";
49
+ init_cjs_shims();
50
+ }
51
+ });
51
52
 
52
- // src/storage/yamlStorage.ts
53
- function createYamlStorage(filePath) {
53
+ // src/storage/yrestStorage.ts
54
+ var yrestStorage_exports = {};
55
+ __export(yrestStorage_exports, {
56
+ createYrestStorage: () => createYrestStorage
57
+ });
58
+ function createYrestStorage(filePath) {
54
59
  const absPath = (0, import_node_path.resolve)(filePath);
55
- const raw = (0, import_yaml.parse)((0, import_node_fs.readFileSync)(absPath, "utf8")) ?? {};
60
+ const raw = (0, import_yaml2.parse)((0, import_node_fs2.readFileSync)(absPath, "utf8")) ?? {};
56
61
  const relations = raw["_rel"] ?? {};
57
62
  const routes = Array.isArray(raw["_routes"]) ? raw["_routes"] : [];
58
63
  const data = Object.fromEntries(
@@ -84,12 +89,12 @@ function createYamlStorage(filePath) {
84
89
  if (Object.keys(relations).length > 0) payload._rel = relations;
85
90
  if (routes.length > 0) payload._routes = routes;
86
91
  Object.assign(payload, data);
87
- const tmp = (0, import_node_path.resolve)((0, import_node_path.dirname)(absPath), `.yrest-${(0, import_node_crypto.randomUUID)()}.tmp`);
88
- (0, import_node_fs.writeFileSync)(tmp, (0, import_yaml.stringify)(payload), "utf8");
89
- (0, import_node_fs.renameSync)(tmp, absPath);
92
+ const tmp = (0, import_node_path.resolve)((0, import_node_path.dirname)(absPath), `.yrest-${(0, import_node_crypto2.randomUUID)()}.tmp`);
93
+ (0, import_node_fs2.writeFileSync)(tmp, (0, import_yaml2.stringify)(payload), "utf8");
94
+ (0, import_node_fs2.renameSync)(tmp, absPath);
90
95
  },
91
96
  reload() {
92
- const fresh = (0, import_yaml.parse)((0, import_node_fs.readFileSync)(absPath, "utf8")) ?? {};
97
+ const fresh = (0, import_yaml2.parse)((0, import_node_fs2.readFileSync)(absPath, "utf8")) ?? {};
93
98
  const freshRelations = fresh["_rel"] ?? {};
94
99
  const freshData = Object.fromEntries(
95
100
  Object.entries(fresh).filter(([key]) => key !== "_rel" && key !== "_routes")
@@ -119,13 +124,123 @@ function createYamlStorage(filePath) {
119
124
  }
120
125
  };
121
126
  }
127
+ var import_node_fs2, import_node_path, import_node_crypto2, import_yaml2;
128
+ var init_yrestStorage = __esm({
129
+ "src/storage/yrestStorage.ts"() {
130
+ "use strict";
131
+ init_cjs_shims();
132
+ import_node_fs2 = require("fs");
133
+ import_node_path = require("path");
134
+ import_node_crypto2 = require("crypto");
135
+ import_yaml2 = require("yaml");
136
+ init_deepCopy();
137
+ }
138
+ });
139
+
140
+ // src/index.ts
141
+ var src_exports = {};
142
+ __export(src_exports, {
143
+ createServer: () => createServer,
144
+ createYrestServer: () => createYrestServer,
145
+ createYrestServerFromStorage: () => createYrestServerFromStorage,
146
+ createYrestStorage: () => createYrestStorage,
147
+ yrest: () => yrest,
148
+ yrestOptionsSchema: () => yrestOptionsSchema
149
+ });
150
+ module.exports = __toCommonJS(src_exports);
151
+ init_cjs_shims();
152
+
153
+ // src/api/yrest.ts
154
+ init_cjs_shims();
155
+ var import_yaml = require("yaml");
156
+ function yrest(strings, ...values) {
157
+ const raw = strings.reduce(
158
+ (acc, str, i) => acc + str + (values[i] !== void 0 ? stringifyInterpolation(values[i]) : ""),
159
+ ""
160
+ );
161
+ const dedented = dedent(raw);
162
+ let parsed;
163
+ try {
164
+ parsed = (0, import_yaml.parse)(dedented);
165
+ } catch (e) {
166
+ throw new Error(`[yrest] Invalid YAML: ${e instanceof Error ? e.message : String(e)}`, {
167
+ cause: e
168
+ });
169
+ }
170
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
171
+ throw new Error("[yrest] Template must resolve to a YAML object with collection keys.");
172
+ }
173
+ return parsed;
174
+ }
175
+ function stringifyInterpolation(value) {
176
+ if (value === null || value === void 0) return String(value);
177
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
178
+ return String(value);
179
+ }
180
+ const type = Array.isArray(value) ? "array" : typeof value;
181
+ throw new Error(
182
+ `[yrest] Cannot interpolate a value of type "${type}". Only string, number, and boolean are supported.`
183
+ );
184
+ }
185
+ function dedent(str) {
186
+ const lines = str.split("\n");
187
+ const minIndent = lines.filter((line) => line.trim().length > 0).reduce((min, line) => {
188
+ const match = line.match(/^(\s*)/);
189
+ return Math.min(min, match ? match[1].length : 0);
190
+ }, Infinity);
191
+ const indent = minIndent === Infinity ? 0 : minIndent;
192
+ return lines.map((line) => line.slice(indent)).join("\n").trim();
193
+ }
194
+
195
+ // src/api/yrestServer.ts
196
+ init_cjs_shims();
197
+ var import_node_path2 = require("path");
198
+
199
+ // src/utils/handlers.ts
200
+ init_cjs_shims();
201
+ var import_node_fs = require("fs");
202
+ async function loadHandlers(filePath) {
203
+ if (!(0, import_node_fs.existsSync)(filePath)) return /* @__PURE__ */ new Map();
204
+ try {
205
+ const mod = await import(filePath);
206
+ const map = /* @__PURE__ */ new Map();
207
+ for (const [name, value] of Object.entries(mod)) {
208
+ if (typeof value === "function") map.set(name, value);
209
+ }
210
+ return map;
211
+ } catch (err) {
212
+ const msg = err instanceof Error ? err.message : String(err);
213
+ console.error(` \x1B[31m[handlers] failed to load ${filePath} \u2014 ${msg}\x1B[0m`);
214
+ return /* @__PURE__ */ new Map();
215
+ }
216
+ }
217
+
218
+ // src/api/yrestServer.ts
219
+ init_deepCopy();
220
+
221
+ // src/server/index.ts
222
+ init_cjs_shims();
122
223
 
123
224
  // src/server/createServer.ts
225
+ init_cjs_shims();
124
226
  var import_fastify = __toESM(require("fastify"));
125
227
  var import_cors = __toESM(require("@fastify/cors"));
126
228
 
229
+ // src/router/resource.router.ts
230
+ init_cjs_shims();
231
+
232
+ // src/router/routes/index.ts
233
+ init_cjs_shims();
234
+
235
+ // src/router/routes/about.routes.ts
236
+ init_cjs_shims();
237
+
238
+ // src/router/templates/about.template.ts
239
+ init_cjs_shims();
240
+
127
241
  // src/utils/interpolate.ts
128
- var import_node_crypto2 = require("crypto");
242
+ init_cjs_shims();
243
+ var import_node_crypto = require("crypto");
129
244
  function getPath(obj, path) {
130
245
  return path.split(".").reduce((acc, key) => {
131
246
  if (acc != null && typeof acc === "object") return acc[key];
@@ -134,7 +249,7 @@ function getPath(obj, path) {
134
249
  }
135
250
  function resolveVar(path, ctx) {
136
251
  if (path === "now") return (/* @__PURE__ */ new Date()).toISOString();
137
- if (path === "uuid") return (0, import_node_crypto2.randomUUID)();
252
+ if (path === "uuid") return (0, import_node_crypto.randomUUID)();
138
253
  if (path === "body") return ctx.body;
139
254
  if (path.startsWith("body.")) return getPath(ctx.body, path.slice(5));
140
255
  if (path.startsWith("params.")) return ctx.params[path.slice(7)] ?? "";
@@ -609,7 +724,11 @@ var AboutRouteCommand = class {
609
724
  }
610
725
  };
611
726
 
727
+ // src/router/routes/collection.routes.ts
728
+ init_cjs_shims();
729
+
612
730
  // src/utils/params.ts
731
+ init_cjs_shims();
613
732
  function nextId(items) {
614
733
  const ids = items.map((i) => i["id"]).filter((id) => typeof id === "number");
615
734
  return ids.length > 0 ? Math.max(...ids) + 1 : 1;
@@ -620,6 +739,7 @@ function firstParam(value) {
620
739
  }
621
740
 
622
741
  // src/services/query.service.ts
742
+ init_cjs_shims();
623
743
  var OPERATORS = ["_gte", "_lte", "_ne", "_like", "_start", "_regex"];
624
744
  function applyOperator(itemValue, op, filterValue) {
625
745
  const strItem = String(itemValue);
@@ -695,6 +815,7 @@ function paginate(items, page, limit) {
695
815
  }
696
816
 
697
817
  // src/services/resource.service.ts
818
+ init_cjs_shims();
698
819
  function findById(items, id) {
699
820
  return items.find((i) => String(i["id"]) === id);
700
821
  }
@@ -742,6 +863,7 @@ function deleteItem(storage, resource, id) {
742
863
  }
743
864
 
744
865
  // src/services/expand.service.ts
866
+ init_cjs_shims();
745
867
  function expandItems(input, query, resource, storage) {
746
868
  const isArray = Array.isArray(input);
747
869
  const items = isArray ? input : [input];
@@ -889,6 +1011,7 @@ var CollectionRouteCommand = class {
889
1011
  };
890
1012
 
891
1013
  // src/router/routes/custom.routes.ts
1014
+ init_cjs_shims();
892
1015
  var CustomRouteCommand = class {
893
1016
  constructor(storage, base, handlers = /* @__PURE__ */ new Map()) {
894
1017
  this.storage = storage;
@@ -955,6 +1078,7 @@ var CustomRouteCommand = class {
955
1078
  };
956
1079
 
957
1080
  // src/router/routes/item.routes.ts
1081
+ init_cjs_shims();
958
1082
  var ItemRouteCommand = class {
959
1083
  constructor(storage, resource, base) {
960
1084
  this.storage = storage;
@@ -1004,6 +1128,7 @@ var ItemRouteCommand = class {
1004
1128
  };
1005
1129
 
1006
1130
  // src/router/routes/nested.routes.ts
1131
+ init_cjs_shims();
1007
1132
  var NestedRouteCommand = class {
1008
1133
  constructor(storage, relations, base) {
1009
1134
  this.storage = storage;
@@ -1043,6 +1168,7 @@ var NestedRouteCommand = class {
1043
1168
  };
1044
1169
 
1045
1170
  // src/router/routes/snapshot.routes.ts
1171
+ init_cjs_shims();
1046
1172
  var SnapshotRouteCommand = class {
1047
1173
  constructor(storage) {
1048
1174
  this.storage = storage;
@@ -1132,9 +1258,119 @@ async function createServer(storage, options, handlers = /* @__PURE__ */ new Map
1132
1258
  return server;
1133
1259
  }
1134
1260
 
1261
+ // src/server/yrestServer.ts
1262
+ init_cjs_shims();
1263
+ function createYrestServerFromStorage(storage, options, handlers = /* @__PURE__ */ new Map()) {
1264
+ let _port = 0;
1265
+ let _started = false;
1266
+ let _fastify = null;
1267
+ return {
1268
+ async start() {
1269
+ if (_started) return;
1270
+ _fastify = await createServer(storage, options, handlers);
1271
+ await _fastify.listen({ port: options.port, host: options.host });
1272
+ const address = _fastify.addresses()[0];
1273
+ _port = typeof address === "object" && "port" in address ? address.port : options.port;
1274
+ _started = true;
1275
+ },
1276
+ async stop() {
1277
+ if (!_started || !_fastify) return;
1278
+ await _fastify.close();
1279
+ _started = false;
1280
+ },
1281
+ get port() {
1282
+ return _port;
1283
+ },
1284
+ get url() {
1285
+ return `http://${options.host}:${_port}${options.base}`;
1286
+ }
1287
+ };
1288
+ }
1289
+
1290
+ // src/api/yrestServer.ts
1291
+ function createYrestServer(options) {
1292
+ const resolvedOptions = buildOptions(options);
1293
+ let _inner = null;
1294
+ return {
1295
+ async start() {
1296
+ const storage = "data" in options && options.data !== void 0 ? createInMemoryStorage(options.data) : (await Promise.resolve().then(() => (init_yrestStorage(), yrestStorage_exports))).createYrestStorage(resolvedOptions.file);
1297
+ const handlers = resolvedOptions.handlers ? await loadHandlers((0, import_node_path2.resolve)(resolvedOptions.handlers)) : /* @__PURE__ */ new Map();
1298
+ _inner = createYrestServerFromStorage(storage, resolvedOptions, handlers);
1299
+ await _inner.start();
1300
+ },
1301
+ async stop() {
1302
+ await _inner?.stop();
1303
+ },
1304
+ get port() {
1305
+ return _inner?.port ?? 0;
1306
+ },
1307
+ get url() {
1308
+ return _inner?.url ?? "";
1309
+ }
1310
+ };
1311
+ }
1312
+ function buildOptions(opts) {
1313
+ const pageable = opts.pageable;
1314
+ return {
1315
+ file: "file" in opts && opts.file ? opts.file : "",
1316
+ port: opts.port ?? 3070,
1317
+ host: opts.host ?? "localhost",
1318
+ base: opts.base ? opts.base.startsWith("/") ? opts.base : `/${opts.base}` : "",
1319
+ watch: false,
1320
+ snapshot: opts.snapshot ?? false,
1321
+ readonly: opts.readonly ?? false,
1322
+ delay: opts.delay ?? 0,
1323
+ handlers: opts.handlers,
1324
+ pageable: typeof pageable === "number" ? { enabled: true, limit: pageable } : pageable ? { enabled: true, limit: 10 } : { enabled: false, limit: 10 }
1325
+ };
1326
+ }
1327
+ function createInMemoryStorage(data) {
1328
+ const raw = data;
1329
+ const relations = raw["_rel"] ?? {};
1330
+ const routes = Array.isArray(raw["_routes"]) ? raw["_routes"] : [];
1331
+ const collections = Object.fromEntries(
1332
+ Object.entries(raw).filter(([k]) => k !== "_rel" && k !== "_routes")
1333
+ );
1334
+ let snapshot = {
1335
+ data: deepCopyData(collections),
1336
+ relations: { ...relations },
1337
+ savedAt: /* @__PURE__ */ new Date()
1338
+ };
1339
+ return {
1340
+ getData: () => collections,
1341
+ getRelations: () => relations,
1342
+ getRoutes: () => routes,
1343
+ getCollection: (name) => collections[name],
1344
+ setCollection: (name, items) => {
1345
+ collections[name] = items;
1346
+ },
1347
+ persist: () => {
1348
+ },
1349
+ reload: () => {
1350
+ },
1351
+ getSnapshot: () => snapshot,
1352
+ saveSnapshot: () => {
1353
+ snapshot = {
1354
+ data: deepCopyData(collections),
1355
+ relations: { ...relations },
1356
+ savedAt: /* @__PURE__ */ new Date()
1357
+ };
1358
+ },
1359
+ resetToSnapshot: () => {
1360
+ const snap = deepCopyData(snapshot.data);
1361
+ for (const key of Object.keys(collections)) delete collections[key];
1362
+ Object.assign(collections, snap);
1363
+ }
1364
+ };
1365
+ }
1366
+
1367
+ // src/index.ts
1368
+ init_yrestStorage();
1369
+
1135
1370
  // src/config/loadOptions.ts
1371
+ init_cjs_shims();
1136
1372
  var import_zod = require("zod");
1137
- var serverOptionsSchema = import_zod.z.object({
1373
+ var yrestOptionsSchema = import_zod.z.object({
1138
1374
  /** Path to the YAML database file. Must be a non-empty string. */
1139
1375
  file: import_zod.z.string().min(1),
1140
1376
  /** TCP port the server listens on. Accepts string input and coerces to number. */
@@ -1174,6 +1410,9 @@ var serverOptionsSchema = import_zod.z.object({
1174
1410
  // Annotate the CommonJS export names for ESM import in node:
1175
1411
  0 && (module.exports = {
1176
1412
  createServer,
1177
- createYamlStorage,
1178
- serverOptionsSchema
1413
+ createYrestServer,
1414
+ createYrestServerFromStorage,
1415
+ createYrestStorage,
1416
+ yrest,
1417
+ yrestOptionsSchema
1179
1418
  });