@yrest/cli 0.5.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +255 -0
- package/dist/cli/index.js +133 -17
- package/dist/cli/index.mjs +133 -17
- package/dist/index.d.mts +205 -30
- package/dist/index.d.ts +205 -30
- package/dist/index.js +363 -33
- package/dist/index.mjs +378 -41
- package/package.json +1 -1
package/dist/index.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)] ?? "";
|
|
@@ -370,16 +485,33 @@ function generateAboutHtml(storage, options, handlers = /* @__PURE__ */ new Map(
|
|
|
370
485
|
<table><tbody>
|
|
371
486
|
${customRoutes.map((r) => {
|
|
372
487
|
const fullPath = `${base}${r.path}`;
|
|
488
|
+
const tags = [];
|
|
489
|
+
if (r.delay && r.delay > 0) {
|
|
490
|
+
tags.push(`<span style="color:#fb923c;font-size:11px">delay\xB7${r.delay}ms</span>`);
|
|
491
|
+
}
|
|
492
|
+
if (r.scenarios?.length) {
|
|
493
|
+
const hasOr = r.scenarios.some((s) => Array.isArray(s.when));
|
|
494
|
+
tags.push(
|
|
495
|
+
`<span style="color:#a371f7;font-size:11px">scenarios\xB7${r.scenarios.length}${hasOr ? " (OR)" : ""}</span>`
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
if (r.otherwise) {
|
|
499
|
+
tags.push(`<span style="color:var(--text-muted);font-size:11px">otherwise</span>`);
|
|
500
|
+
}
|
|
373
501
|
let desc;
|
|
374
502
|
if (r.handler) {
|
|
375
503
|
const found = handlers.has(r.handler);
|
|
376
504
|
desc = found ? `Handler \u2014 <code>${r.handler}()</code>` : `Handler \u2014 <code>${r.handler}()</code> <span style="color:#f85149">(not loaded)</span>`;
|
|
505
|
+
} else if (r.scenarios?.length) {
|
|
506
|
+
const hasTemplateInScenarios = r.scenarios.some((s) => s.response.body != null && hasTemplates(s.response.body)) || r.otherwise?.body != null && hasTemplates(r.otherwise.body);
|
|
507
|
+
desc = hasTemplateInScenarios ? `Scenarios \u2014 <code>{{\u2026}}</code>` : `Scenarios`;
|
|
377
508
|
} else if (r.response?.body != null && hasTemplates(r.response.body)) {
|
|
378
|
-
desc = `Dynamic
|
|
509
|
+
desc = `Dynamic \u2014 <code>{{\u2026}}</code>`;
|
|
379
510
|
} else {
|
|
380
511
|
const status = r.response?.status ?? 200;
|
|
381
|
-
desc = `Static \u2014 <code>${status}</code>${r.response?.headers ? ` +
|
|
512
|
+
desc = `Static \u2014 <code>${status}</code>${r.response?.headers ? ` + headers` : ""}`;
|
|
382
513
|
}
|
|
514
|
+
if (tags.length) desc += ` ${tags.join(" ")}`;
|
|
383
515
|
return endpointRow(r.method?.toUpperCase() ?? "GET", fullPath, desc);
|
|
384
516
|
}).join("")}
|
|
385
517
|
</tbody></table>
|
|
@@ -531,7 +663,7 @@ function generateAboutHtml(storage, options, handlers = /* @__PURE__ */ new Map(
|
|
|
531
663
|
|
|
532
664
|
<div class="banner">
|
|
533
665
|
<div class="banner-inner">
|
|
534
|
-
<h1><span class="y">y</span><span class="rest">
|
|
666
|
+
<h1><span class="y">y</span><span class="rest">Rest</span></h1>
|
|
535
667
|
<p>Zero-config REST API mock server</p>
|
|
536
668
|
<div class="banner-meta">
|
|
537
669
|
<span>URL <strong>${host}</strong></span>
|
|
@@ -583,7 +715,7 @@ function generateAboutHtml(storage, options, handlers = /* @__PURE__ */ new Map(
|
|
|
583
715
|
${collections.length ? `<h2>Examples</h2><div class="card">${examplesBlock(collections, relations, base, host, options, customRoutes[0])}</div>` : ""}
|
|
584
716
|
|
|
585
717
|
<footer>
|
|
586
|
-
Powered by <a href="https://github.com/aggiovato/
|
|
718
|
+
Powered by <a href="https://github.com/aggiovato/yRest" target="_blank">@yrest/cli</a> \xB7 <a href="/_about">/_about</a>
|
|
587
719
|
</footer>
|
|
588
720
|
|
|
589
721
|
</div>
|
|
@@ -609,7 +741,11 @@ var AboutRouteCommand = class {
|
|
|
609
741
|
}
|
|
610
742
|
};
|
|
611
743
|
|
|
744
|
+
// src/router/routes/collection.routes.ts
|
|
745
|
+
init_cjs_shims();
|
|
746
|
+
|
|
612
747
|
// src/utils/params.ts
|
|
748
|
+
init_cjs_shims();
|
|
613
749
|
function nextId(items) {
|
|
614
750
|
const ids = items.map((i) => i["id"]).filter((id) => typeof id === "number");
|
|
615
751
|
return ids.length > 0 ? Math.max(...ids) + 1 : 1;
|
|
@@ -620,6 +756,7 @@ function firstParam(value) {
|
|
|
620
756
|
}
|
|
621
757
|
|
|
622
758
|
// src/services/query.service.ts
|
|
759
|
+
init_cjs_shims();
|
|
623
760
|
var OPERATORS = ["_gte", "_lte", "_ne", "_like", "_start", "_regex"];
|
|
624
761
|
function applyOperator(itemValue, op, filterValue) {
|
|
625
762
|
const strItem = String(itemValue);
|
|
@@ -695,6 +832,7 @@ function paginate(items, page, limit) {
|
|
|
695
832
|
}
|
|
696
833
|
|
|
697
834
|
// src/services/resource.service.ts
|
|
835
|
+
init_cjs_shims();
|
|
698
836
|
function findById(items, id) {
|
|
699
837
|
return items.find((i) => String(i["id"]) === id);
|
|
700
838
|
}
|
|
@@ -742,6 +880,7 @@ function deleteItem(storage, resource, id) {
|
|
|
742
880
|
}
|
|
743
881
|
|
|
744
882
|
// src/services/expand.service.ts
|
|
883
|
+
init_cjs_shims();
|
|
745
884
|
function expandItems(input, query, resource, storage) {
|
|
746
885
|
const isArray = Array.isArray(input);
|
|
747
886
|
const items = isArray ? input : [input];
|
|
@@ -889,6 +1028,66 @@ var CollectionRouteCommand = class {
|
|
|
889
1028
|
};
|
|
890
1029
|
|
|
891
1030
|
// src/router/routes/custom.routes.ts
|
|
1031
|
+
init_cjs_shims();
|
|
1032
|
+
|
|
1033
|
+
// src/utils/conditions.ts
|
|
1034
|
+
init_cjs_shims();
|
|
1035
|
+
function resolveRequestPath(dotPath, req) {
|
|
1036
|
+
const [root, ...rest] = dotPath.split(".");
|
|
1037
|
+
let value;
|
|
1038
|
+
switch (root) {
|
|
1039
|
+
case "body":
|
|
1040
|
+
value = req.body;
|
|
1041
|
+
break;
|
|
1042
|
+
case "params":
|
|
1043
|
+
value = req.params;
|
|
1044
|
+
break;
|
|
1045
|
+
case "query":
|
|
1046
|
+
value = req.query;
|
|
1047
|
+
break;
|
|
1048
|
+
case "headers":
|
|
1049
|
+
value = req.headers;
|
|
1050
|
+
break;
|
|
1051
|
+
default:
|
|
1052
|
+
return void 0;
|
|
1053
|
+
}
|
|
1054
|
+
for (const key of rest) {
|
|
1055
|
+
if (value != null && typeof value === "object") {
|
|
1056
|
+
value = value[key];
|
|
1057
|
+
} else {
|
|
1058
|
+
return void 0;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
return value;
|
|
1062
|
+
}
|
|
1063
|
+
function matchConditionGroup(group, req) {
|
|
1064
|
+
return Object.entries(group).every(([key, expected]) => {
|
|
1065
|
+
const op = OPERATORS.find((o) => key.endsWith(o));
|
|
1066
|
+
if (op) {
|
|
1067
|
+
const path = key.slice(0, -op.length);
|
|
1068
|
+
const value2 = resolveRequestPath(path, req);
|
|
1069
|
+
if (value2 === void 0) return false;
|
|
1070
|
+
return applyOperator(value2, op, String(expected));
|
|
1071
|
+
}
|
|
1072
|
+
const value = resolveRequestPath(key, req);
|
|
1073
|
+
return String(value) === String(expected);
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
function matchWhen(when, req) {
|
|
1077
|
+
if (Array.isArray(when)) {
|
|
1078
|
+
return when.some((group) => matchConditionGroup(group, req));
|
|
1079
|
+
}
|
|
1080
|
+
return matchConditionGroup(when, req);
|
|
1081
|
+
}
|
|
1082
|
+
function findMatchingScenario(scenarios, req) {
|
|
1083
|
+
return scenarios.find((s) => matchWhen(s.when, req));
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// src/router/routes/custom.routes.ts
|
|
1087
|
+
function resolveBody(body, ctx) {
|
|
1088
|
+
if (body != null && hasTemplates(body)) return interpolate(body, ctx);
|
|
1089
|
+
return body ?? null;
|
|
1090
|
+
}
|
|
892
1091
|
var CustomRouteCommand = class {
|
|
893
1092
|
constructor(storage, base, handlers = /* @__PURE__ */ new Map()) {
|
|
894
1093
|
this.storage = storage;
|
|
@@ -913,6 +1112,9 @@ var CustomRouteCommand = class {
|
|
|
913
1112
|
method,
|
|
914
1113
|
url,
|
|
915
1114
|
handler: async (req, reply) => {
|
|
1115
|
+
if (route.delay && route.delay > 0) {
|
|
1116
|
+
await new Promise((resolve3) => setTimeout(resolve3, route.delay));
|
|
1117
|
+
}
|
|
916
1118
|
for (const [key, value] of Object.entries(headers)) {
|
|
917
1119
|
reply.header(key, value);
|
|
918
1120
|
}
|
|
@@ -922,13 +1124,13 @@ var CustomRouteCommand = class {
|
|
|
922
1124
|
return reply.status(501).send({ error: `Handler "${handlerName}" is not defined in the handlers file` });
|
|
923
1125
|
}
|
|
924
1126
|
try {
|
|
925
|
-
const
|
|
1127
|
+
const ctx2 = {
|
|
926
1128
|
params: req.params,
|
|
927
1129
|
query: req.query,
|
|
928
1130
|
body: req.body,
|
|
929
1131
|
headers: req.headers
|
|
930
1132
|
};
|
|
931
|
-
const result = await fn(
|
|
1133
|
+
const result = await fn(ctx2);
|
|
932
1134
|
const resStatus = result.status ?? 200;
|
|
933
1135
|
for (const [k, v] of Object.entries(result.headers ?? {})) {
|
|
934
1136
|
reply.header(k, v);
|
|
@@ -940,12 +1142,24 @@ var CustomRouteCommand = class {
|
|
|
940
1142
|
return reply.status(500).send({ error: `Handler "${handlerName}" threw an error: ${msg}` });
|
|
941
1143
|
}
|
|
942
1144
|
}
|
|
943
|
-
const
|
|
1145
|
+
const ctx = {
|
|
944
1146
|
params: req.params,
|
|
945
1147
|
query: req.query,
|
|
946
1148
|
body: req.body,
|
|
947
1149
|
headers: req.headers
|
|
948
|
-
}
|
|
1150
|
+
};
|
|
1151
|
+
if (route.scenarios?.length) {
|
|
1152
|
+
const matched = findMatchingScenario(route.scenarios, ctx);
|
|
1153
|
+
const active = matched?.response ?? route.otherwise;
|
|
1154
|
+
if (active) {
|
|
1155
|
+
const aStatus = active.status ?? 200;
|
|
1156
|
+
const aBody = resolveBody(active.body, ctx);
|
|
1157
|
+
for (const [k, v] of Object.entries(active.headers ?? {})) reply.header(k, v);
|
|
1158
|
+
if (!active.body && aStatus === 204) return reply.status(aStatus).send();
|
|
1159
|
+
return reply.status(aStatus).send(aBody);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
const body = dynamic ? interpolate(rawBody, ctx) : rawBody;
|
|
949
1163
|
if (body === null && status === 204) return reply.status(status).send();
|
|
950
1164
|
return reply.status(status).send(body);
|
|
951
1165
|
}
|
|
@@ -955,6 +1169,7 @@ var CustomRouteCommand = class {
|
|
|
955
1169
|
};
|
|
956
1170
|
|
|
957
1171
|
// src/router/routes/item.routes.ts
|
|
1172
|
+
init_cjs_shims();
|
|
958
1173
|
var ItemRouteCommand = class {
|
|
959
1174
|
constructor(storage, resource, base) {
|
|
960
1175
|
this.storage = storage;
|
|
@@ -1004,6 +1219,7 @@ var ItemRouteCommand = class {
|
|
|
1004
1219
|
};
|
|
1005
1220
|
|
|
1006
1221
|
// src/router/routes/nested.routes.ts
|
|
1222
|
+
init_cjs_shims();
|
|
1007
1223
|
var NestedRouteCommand = class {
|
|
1008
1224
|
constructor(storage, relations, base) {
|
|
1009
1225
|
this.storage = storage;
|
|
@@ -1043,6 +1259,7 @@ var NestedRouteCommand = class {
|
|
|
1043
1259
|
};
|
|
1044
1260
|
|
|
1045
1261
|
// src/router/routes/snapshot.routes.ts
|
|
1262
|
+
init_cjs_shims();
|
|
1046
1263
|
var SnapshotRouteCommand = class {
|
|
1047
1264
|
constructor(storage) {
|
|
1048
1265
|
this.storage = storage;
|
|
@@ -1132,9 +1349,119 @@ async function createServer(storage, options, handlers = /* @__PURE__ */ new Map
|
|
|
1132
1349
|
return server;
|
|
1133
1350
|
}
|
|
1134
1351
|
|
|
1352
|
+
// src/server/yrestServer.ts
|
|
1353
|
+
init_cjs_shims();
|
|
1354
|
+
function createYrestServerFromStorage(storage, options, handlers = /* @__PURE__ */ new Map()) {
|
|
1355
|
+
let _port = 0;
|
|
1356
|
+
let _started = false;
|
|
1357
|
+
let _fastify = null;
|
|
1358
|
+
return {
|
|
1359
|
+
async start() {
|
|
1360
|
+
if (_started) return;
|
|
1361
|
+
_fastify = await createServer(storage, options, handlers);
|
|
1362
|
+
await _fastify.listen({ port: options.port, host: options.host });
|
|
1363
|
+
const address = _fastify.addresses()[0];
|
|
1364
|
+
_port = typeof address === "object" && "port" in address ? address.port : options.port;
|
|
1365
|
+
_started = true;
|
|
1366
|
+
},
|
|
1367
|
+
async stop() {
|
|
1368
|
+
if (!_started || !_fastify) return;
|
|
1369
|
+
await _fastify.close();
|
|
1370
|
+
_started = false;
|
|
1371
|
+
},
|
|
1372
|
+
get port() {
|
|
1373
|
+
return _port;
|
|
1374
|
+
},
|
|
1375
|
+
get url() {
|
|
1376
|
+
return `http://${options.host}:${_port}${options.base}`;
|
|
1377
|
+
}
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
// src/api/yrestServer.ts
|
|
1382
|
+
function createYrestServer(options) {
|
|
1383
|
+
const resolvedOptions = buildOptions(options);
|
|
1384
|
+
let _inner = null;
|
|
1385
|
+
return {
|
|
1386
|
+
async start() {
|
|
1387
|
+
const storage = "data" in options && options.data !== void 0 ? createInMemoryStorage(options.data) : (await Promise.resolve().then(() => (init_yrestStorage(), yrestStorage_exports))).createYrestStorage(resolvedOptions.file);
|
|
1388
|
+
const handlers = resolvedOptions.handlers ? await loadHandlers((0, import_node_path2.resolve)(resolvedOptions.handlers)) : /* @__PURE__ */ new Map();
|
|
1389
|
+
_inner = createYrestServerFromStorage(storage, resolvedOptions, handlers);
|
|
1390
|
+
await _inner.start();
|
|
1391
|
+
},
|
|
1392
|
+
async stop() {
|
|
1393
|
+
await _inner?.stop();
|
|
1394
|
+
},
|
|
1395
|
+
get port() {
|
|
1396
|
+
return _inner?.port ?? 0;
|
|
1397
|
+
},
|
|
1398
|
+
get url() {
|
|
1399
|
+
return _inner?.url ?? "";
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
function buildOptions(opts) {
|
|
1404
|
+
const pageable = opts.pageable;
|
|
1405
|
+
return {
|
|
1406
|
+
file: "file" in opts && opts.file ? opts.file : "",
|
|
1407
|
+
port: opts.port ?? 3070,
|
|
1408
|
+
host: opts.host ?? "localhost",
|
|
1409
|
+
base: opts.base ? opts.base.startsWith("/") ? opts.base : `/${opts.base}` : "",
|
|
1410
|
+
watch: false,
|
|
1411
|
+
snapshot: opts.snapshot ?? false,
|
|
1412
|
+
readonly: opts.readonly ?? false,
|
|
1413
|
+
delay: opts.delay ?? 0,
|
|
1414
|
+
handlers: opts.handlers,
|
|
1415
|
+
pageable: typeof pageable === "number" ? { enabled: true, limit: pageable } : pageable ? { enabled: true, limit: 10 } : { enabled: false, limit: 10 }
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
function createInMemoryStorage(data) {
|
|
1419
|
+
const raw = data;
|
|
1420
|
+
const relations = raw["_rel"] ?? {};
|
|
1421
|
+
const routes = Array.isArray(raw["_routes"]) ? raw["_routes"] : [];
|
|
1422
|
+
const collections = Object.fromEntries(
|
|
1423
|
+
Object.entries(raw).filter(([k]) => k !== "_rel" && k !== "_routes")
|
|
1424
|
+
);
|
|
1425
|
+
let snapshot = {
|
|
1426
|
+
data: deepCopyData(collections),
|
|
1427
|
+
relations: { ...relations },
|
|
1428
|
+
savedAt: /* @__PURE__ */ new Date()
|
|
1429
|
+
};
|
|
1430
|
+
return {
|
|
1431
|
+
getData: () => collections,
|
|
1432
|
+
getRelations: () => relations,
|
|
1433
|
+
getRoutes: () => routes,
|
|
1434
|
+
getCollection: (name) => collections[name],
|
|
1435
|
+
setCollection: (name, items) => {
|
|
1436
|
+
collections[name] = items;
|
|
1437
|
+
},
|
|
1438
|
+
persist: () => {
|
|
1439
|
+
},
|
|
1440
|
+
reload: () => {
|
|
1441
|
+
},
|
|
1442
|
+
getSnapshot: () => snapshot,
|
|
1443
|
+
saveSnapshot: () => {
|
|
1444
|
+
snapshot = {
|
|
1445
|
+
data: deepCopyData(collections),
|
|
1446
|
+
relations: { ...relations },
|
|
1447
|
+
savedAt: /* @__PURE__ */ new Date()
|
|
1448
|
+
};
|
|
1449
|
+
},
|
|
1450
|
+
resetToSnapshot: () => {
|
|
1451
|
+
const snap = deepCopyData(snapshot.data);
|
|
1452
|
+
for (const key of Object.keys(collections)) delete collections[key];
|
|
1453
|
+
Object.assign(collections, snap);
|
|
1454
|
+
}
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
// src/index.ts
|
|
1459
|
+
init_yrestStorage();
|
|
1460
|
+
|
|
1135
1461
|
// src/config/loadOptions.ts
|
|
1462
|
+
init_cjs_shims();
|
|
1136
1463
|
var import_zod = require("zod");
|
|
1137
|
-
var
|
|
1464
|
+
var yrestOptionsSchema = import_zod.z.object({
|
|
1138
1465
|
/** Path to the YAML database file. Must be a non-empty string. */
|
|
1139
1466
|
file: import_zod.z.string().min(1),
|
|
1140
1467
|
/** TCP port the server listens on. Accepts string input and coerces to number. */
|
|
@@ -1174,6 +1501,9 @@ var serverOptionsSchema = import_zod.z.object({
|
|
|
1174
1501
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1175
1502
|
0 && (module.exports = {
|
|
1176
1503
|
createServer,
|
|
1177
|
-
|
|
1178
|
-
|
|
1504
|
+
createYrestServer,
|
|
1505
|
+
createYrestServerFromStorage,
|
|
1506
|
+
createYrestStorage,
|
|
1507
|
+
yrest,
|
|
1508
|
+
yrestOptionsSchema
|
|
1179
1509
|
});
|