@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 +21 -0
- package/README.md +156 -0
- package/dist/cli/index.js +37 -9
- package/dist/cli/index.mjs +37 -9
- package/dist/index.d.mts +132 -14
- package/dist/index.d.ts +132 -14
- package/dist/index.js +264 -25
- package/dist/index.mjs +279 -33
- 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>;
|
|
@@ -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
|
|
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
|
-
*
|
|
133
|
+
* Tagged template literal that parses inline YAML into a yRest {@link Data} object.
|
|
134
134
|
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
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
|
-
*
|
|
140
|
-
*
|
|
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
|
|
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
|
|
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
|
|
225
|
+
* Inferred from {@link yrestOptionsSchema}.
|
|
215
226
|
*/
|
|
216
|
-
type
|
|
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:
|
|
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
|
|
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
|
-
//
|
|
31
|
-
var
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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/
|
|
53
|
-
|
|
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,
|
|
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,
|
|
88
|
-
(0,
|
|
89
|
-
(0,
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
1178
|
-
|
|
1413
|
+
createYrestServer,
|
|
1414
|
+
createYrestServerFromStorage,
|
|
1415
|
+
createYrestStorage,
|
|
1416
|
+
yrest,
|
|
1417
|
+
yrestOptionsSchema
|
|
1179
1418
|
});
|