@yrest/cli 0.11.1 → 0.12.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/README.md +170 -24
- package/dist/cli/index.js +230 -16
- package/dist/cli/index.mjs +230 -16
- package/dist/index.d.mts +110 -3
- package/dist/index.d.ts +110 -3
- package/dist/index.js +243 -11
- package/dist/index.mjs +243 -11
- package/package.json +6 -2
package/dist/index.mjs
CHANGED
|
@@ -130,6 +130,49 @@ var init_parseRelations = __esm({
|
|
|
130
130
|
}
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
+
// src/storage/parseSseRoutes.ts
|
|
134
|
+
function parseSseRoutes(raw) {
|
|
135
|
+
if (!Array.isArray(raw)) return [];
|
|
136
|
+
return raw.map(normaliseSseRoute).filter((r) => r !== null);
|
|
137
|
+
}
|
|
138
|
+
function normaliseSseRoute(raw) {
|
|
139
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
140
|
+
const v = raw;
|
|
141
|
+
const method = typeof v["_method"] === "string" ? v["_method"].toUpperCase() : null;
|
|
142
|
+
const path2 = v["_path"];
|
|
143
|
+
const sse = v["_sse"];
|
|
144
|
+
if (method !== "SSE") return null;
|
|
145
|
+
if (typeof path2 !== "string" || !sse || typeof sse !== "object" || Array.isArray(sse)) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
const s = sse;
|
|
149
|
+
const rawEvents = s["_events"];
|
|
150
|
+
if (!Array.isArray(rawEvents)) return null;
|
|
151
|
+
const events = rawEvents.map(normaliseEvent).filter((e) => e !== null);
|
|
152
|
+
if (!events.length) return null;
|
|
153
|
+
return {
|
|
154
|
+
path: path2,
|
|
155
|
+
interval: typeof s["_interval"] === "number" ? s["_interval"] : 1e3,
|
|
156
|
+
loop: s["_loop"] !== false,
|
|
157
|
+
repeat: typeof s["_repeat"] === "number" ? s["_repeat"] : void 0,
|
|
158
|
+
events
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function normaliseEvent(raw) {
|
|
162
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
163
|
+
const v = raw;
|
|
164
|
+
if (!("_data" in v)) return null;
|
|
165
|
+
const event = { data: v["_data"] };
|
|
166
|
+
if (typeof v["_event"] === "string") event.event = v["_event"];
|
|
167
|
+
return event;
|
|
168
|
+
}
|
|
169
|
+
var init_parseSseRoutes = __esm({
|
|
170
|
+
"src/storage/parseSseRoutes.ts"() {
|
|
171
|
+
"use strict";
|
|
172
|
+
init_esm_shims();
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
133
176
|
// src/storage/parseSchema.ts
|
|
134
177
|
function parseSchema(raw) {
|
|
135
178
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
@@ -151,18 +194,48 @@ function normaliseFieldDef(value) {
|
|
|
151
194
|
const v = value;
|
|
152
195
|
const def = {};
|
|
153
196
|
if (v["_required"] === true || v["_required"] === false) def.required = v["_required"];
|
|
154
|
-
if (typeof v["_type"] === "string" &&
|
|
197
|
+
if (typeof v["_type"] === "string" && VALID_TYPES.has(v["_type"]))
|
|
155
198
|
def.type = v["_type"];
|
|
156
199
|
if (typeof v["_format"] === "string") def.format = v["_format"];
|
|
157
200
|
if (Array.isArray(v["_enum"])) def.enum = v["_enum"];
|
|
158
201
|
if (typeof v["_description"] === "string") def.description = v["_description"];
|
|
159
202
|
if (v["_default"] !== void 0) def.default = v["_default"];
|
|
203
|
+
if (v["_example"] !== void 0) def.example = v["_example"];
|
|
204
|
+
if (v["_nullable"] === true || v["_nullable"] === false) def.nullable = v["_nullable"];
|
|
205
|
+
if (typeof v["_minLength"] === "number") def.minLength = v["_minLength"];
|
|
206
|
+
if (typeof v["_maxLength"] === "number") def.maxLength = v["_maxLength"];
|
|
207
|
+
if (typeof v["_pattern"] === "string") def.pattern = v["_pattern"];
|
|
208
|
+
if (typeof v["_minimum"] === "number") def.minimum = v["_minimum"];
|
|
209
|
+
if (typeof v["_maximum"] === "number") def.maximum = v["_maximum"];
|
|
210
|
+
if (typeof v["_exclusiveMinimum"] === "number") def.exclusiveMinimum = v["_exclusiveMinimum"];
|
|
211
|
+
if (typeof v["_exclusiveMaximum"] === "number") def.exclusiveMaximum = v["_exclusiveMaximum"];
|
|
212
|
+
if (typeof v["_multipleOf"] === "number") def.multipleOf = v["_multipleOf"];
|
|
213
|
+
if (typeof v["_minItems"] === "number") def.minItems = v["_minItems"];
|
|
214
|
+
if (typeof v["_maxItems"] === "number") def.maxItems = v["_maxItems"];
|
|
215
|
+
if (v["_uniqueItems"] === true || v["_uniqueItems"] === false)
|
|
216
|
+
def.uniqueItems = v["_uniqueItems"];
|
|
217
|
+
if (v["_items"] && typeof v["_items"] === "object" && !Array.isArray(v["_items"])) {
|
|
218
|
+
const items = v["_items"];
|
|
219
|
+
const itemsDef = {};
|
|
220
|
+
if (typeof items["_type"] === "string" && VALID_TYPES.has(items["_type"]))
|
|
221
|
+
itemsDef.type = items["_type"];
|
|
222
|
+
if (typeof items["_format"] === "string") itemsDef.format = items["_format"];
|
|
223
|
+
if (Array.isArray(items["_enum"])) itemsDef.enum = items["_enum"];
|
|
224
|
+
def.items = itemsDef;
|
|
225
|
+
}
|
|
226
|
+
if (v["_deprecated"] === true || v["_deprecated"] === false)
|
|
227
|
+
def.deprecated = v["_deprecated"];
|
|
228
|
+
if (v["_readOnly"] === true || v["_readOnly"] === false) def.readOnly = v["_readOnly"];
|
|
229
|
+
if (v["_writeOnly"] === true || v["_writeOnly"] === false)
|
|
230
|
+
def.writeOnly = v["_writeOnly"];
|
|
160
231
|
return def;
|
|
161
232
|
}
|
|
233
|
+
var VALID_TYPES;
|
|
162
234
|
var init_parseSchema = __esm({
|
|
163
235
|
"src/storage/parseSchema.ts"() {
|
|
164
236
|
"use strict";
|
|
165
237
|
init_esm_shims();
|
|
238
|
+
VALID_TYPES = /* @__PURE__ */ new Set(["string", "integer", "number", "boolean", "object", "array"]);
|
|
166
239
|
}
|
|
167
240
|
});
|
|
168
241
|
|
|
@@ -188,6 +261,8 @@ function normaliseRoute(raw) {
|
|
|
188
261
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
189
262
|
const v = raw;
|
|
190
263
|
const method = v["_method"];
|
|
264
|
+
if (typeof method === "string" && method.toUpperCase() === "SSE") return null;
|
|
265
|
+
if ("_sse" in v) return null;
|
|
191
266
|
const path2 = v["_path"];
|
|
192
267
|
if (typeof method !== "string" || typeof path2 !== "string") return null;
|
|
193
268
|
const route = { method, path: path2 };
|
|
@@ -233,6 +308,30 @@ var init_parseRoutes = __esm({
|
|
|
233
308
|
}
|
|
234
309
|
});
|
|
235
310
|
|
|
311
|
+
// src/storage/parseData.ts
|
|
312
|
+
function parseData(raw) {
|
|
313
|
+
const block = raw["_data"];
|
|
314
|
+
const fromBlock = block && typeof block === "object" && !Array.isArray(block) ? Object.fromEntries(
|
|
315
|
+
Object.entries(block).filter(([, v]) => Array.isArray(v))
|
|
316
|
+
) : {};
|
|
317
|
+
const fromRoot = Object.fromEntries(
|
|
318
|
+
Object.entries(raw).filter(([key, val]) => !RESERVED.has(key) && Array.isArray(val))
|
|
319
|
+
);
|
|
320
|
+
return { ...fromBlock, ...fromRoot };
|
|
321
|
+
}
|
|
322
|
+
function hasDataBlock(raw) {
|
|
323
|
+
const d = raw["_data"];
|
|
324
|
+
return d !== null && d !== void 0 && typeof d === "object" && !Array.isArray(d);
|
|
325
|
+
}
|
|
326
|
+
var RESERVED;
|
|
327
|
+
var init_parseData = __esm({
|
|
328
|
+
"src/storage/parseData.ts"() {
|
|
329
|
+
"use strict";
|
|
330
|
+
init_esm_shims();
|
|
331
|
+
RESERVED = /* @__PURE__ */ new Set(["_rel", "_routes", "_schema", "_data"]);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
|
|
236
335
|
// src/storage/yrestStorage.ts
|
|
237
336
|
var yrestStorage_exports = {};
|
|
238
337
|
__export(yrestStorage_exports, {
|
|
@@ -299,13 +398,12 @@ function serializeScenario(s) {
|
|
|
299
398
|
function createYrestStorage(filePath) {
|
|
300
399
|
const absPath = resolve(filePath);
|
|
301
400
|
const raw = parse2(readFileSync2(absPath, "utf8")) ?? {};
|
|
302
|
-
const RESERVED = /* @__PURE__ */ new Set(["_rel", "_routes", "_schema"]);
|
|
303
401
|
const relations = parseRelations(raw["_rel"]);
|
|
304
402
|
const routes = parseRoutes(raw["_routes"]);
|
|
403
|
+
const sseRoutes = parseSseRoutes(raw["_routes"]);
|
|
305
404
|
const schema = parseSchema(raw["_schema"]);
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
);
|
|
405
|
+
const dataBlock = hasDataBlock(raw);
|
|
406
|
+
const data = parseData(raw);
|
|
309
407
|
let snapshot = {
|
|
310
408
|
data: deepCopyData(data),
|
|
311
409
|
relations: { ...relations },
|
|
@@ -324,6 +422,9 @@ function createYrestStorage(filePath) {
|
|
|
324
422
|
getRoutes() {
|
|
325
423
|
return routes;
|
|
326
424
|
},
|
|
425
|
+
getSseRoutes() {
|
|
426
|
+
return sseRoutes;
|
|
427
|
+
},
|
|
327
428
|
getCollection(name) {
|
|
328
429
|
return data[name];
|
|
329
430
|
},
|
|
@@ -334,7 +435,11 @@ function createYrestStorage(filePath) {
|
|
|
334
435
|
const payload = {};
|
|
335
436
|
if (Object.keys(relations).length > 0) payload._rel = serializeRelations(relations);
|
|
336
437
|
if (routes.length > 0) payload._routes = serializeRoutes(routes);
|
|
337
|
-
|
|
438
|
+
if (dataBlock) {
|
|
439
|
+
payload._data = data;
|
|
440
|
+
} else {
|
|
441
|
+
Object.assign(payload, data);
|
|
442
|
+
}
|
|
338
443
|
const tmp = resolve(dirname2(absPath), `.yrest-${randomUUID2()}.tmp`);
|
|
339
444
|
writeFileSync(tmp, stringify2(payload), "utf8");
|
|
340
445
|
renameSync(tmp, absPath);
|
|
@@ -342,9 +447,7 @@ function createYrestStorage(filePath) {
|
|
|
342
447
|
reload() {
|
|
343
448
|
const fresh = parse2(readFileSync2(absPath, "utf8")) ?? {};
|
|
344
449
|
const freshRelations = parseRelations(fresh["_rel"]);
|
|
345
|
-
const freshData =
|
|
346
|
-
Object.entries(fresh).filter(([key]) => !RESERVED.has(key))
|
|
347
|
-
);
|
|
450
|
+
const freshData = parseData(fresh);
|
|
348
451
|
for (const key of Object.keys(data)) delete data[key];
|
|
349
452
|
Object.assign(data, freshData);
|
|
350
453
|
for (const key of Object.keys(relations)) delete relations[key];
|
|
@@ -376,7 +479,9 @@ var init_yrestStorage = __esm({
|
|
|
376
479
|
init_esm_shims();
|
|
377
480
|
init_parseRelations();
|
|
378
481
|
init_parseRoutes();
|
|
482
|
+
init_parseSseRoutes();
|
|
379
483
|
init_parseSchema();
|
|
484
|
+
init_parseData();
|
|
380
485
|
init_deepCopy();
|
|
381
486
|
}
|
|
382
487
|
});
|
|
@@ -429,6 +534,7 @@ function dedent(str) {
|
|
|
429
534
|
// src/api/yrestServer.ts
|
|
430
535
|
init_esm_shims();
|
|
431
536
|
init_parseRelations();
|
|
537
|
+
init_parseSseRoutes();
|
|
432
538
|
init_parseSchema();
|
|
433
539
|
import { resolve as resolve2 } from "path";
|
|
434
540
|
|
|
@@ -541,6 +647,8 @@ var METHOD_COLOR = {
|
|
|
541
647
|
PUT: "#d29922",
|
|
542
648
|
PATCH: "#a371f7",
|
|
543
649
|
DELETE: "#f85149",
|
|
650
|
+
SSE: "#0ea5e9",
|
|
651
|
+
WS: "#8b5cf6",
|
|
544
652
|
fn: "#f0883e"
|
|
545
653
|
};
|
|
546
654
|
function escapeHtml(str) {
|
|
@@ -732,6 +840,30 @@ function handlersAccordion(handlers, routes, base) {
|
|
|
732
840
|
</tbody></table>
|
|
733
841
|
</details>`;
|
|
734
842
|
}
|
|
843
|
+
function sseRoutesAccordion(routes, base) {
|
|
844
|
+
if (!routes.length) return "";
|
|
845
|
+
const rows = routes.map((r) => {
|
|
846
|
+
const tags = [
|
|
847
|
+
`<span style="color:#0ea5e9;font-size:11px">interval\xB7${r.interval}ms</span>`,
|
|
848
|
+
`<span style="color:var(--text-muted);font-size:11px">${r.events.length} event${r.events.length !== 1 ? "s" : ""}</span>`
|
|
849
|
+
];
|
|
850
|
+
if (!r.loop) tags.push(`<span style="color:var(--text-muted);font-size:11px">no\xB7loop</span>`);
|
|
851
|
+
if (r.repeat !== void 0)
|
|
852
|
+
tags.push(`<span style="color:var(--text-muted);font-size:11px">repeat\xB7${r.repeat}\xD7</span>`);
|
|
853
|
+
const desc = `SSE stream ${tags.join(" ")}`;
|
|
854
|
+
return endpointRow("SSE", `${base}${r.path}`, desc);
|
|
855
|
+
});
|
|
856
|
+
return `
|
|
857
|
+
<details class="resource-card nested-card">
|
|
858
|
+
<summary>
|
|
859
|
+
<span class="resource-name">SSE streams</span>
|
|
860
|
+
<span class="route-count">${routes.length} stream${routes.length !== 1 ? "s" : ""}</span>
|
|
861
|
+
</summary>
|
|
862
|
+
<table><tbody>
|
|
863
|
+
${rows.join("")}
|
|
864
|
+
</tbody></table>
|
|
865
|
+
</details>`;
|
|
866
|
+
}
|
|
735
867
|
function examplesBlock(collections, relations, base, host, options, firstCustomRoute) {
|
|
736
868
|
const examples = [];
|
|
737
869
|
const firstCol = collections[0];
|
|
@@ -1404,6 +1536,7 @@ function generateAboutHtml(storage, options, handlers = /* @__PURE__ */ new Map(
|
|
|
1404
1536
|
const collections = Object.keys(storage.getData());
|
|
1405
1537
|
const relations = storage.getRelations();
|
|
1406
1538
|
const customRoutes = storage.getRoutes();
|
|
1539
|
+
const sseRoutes = storage.getSseRoutes();
|
|
1407
1540
|
const base = options.base;
|
|
1408
1541
|
const host = `http://${options.host}:${options.port}`;
|
|
1409
1542
|
const swaggerUrl = `https://petstore.swagger.io/?url=${encodeURIComponent(`${host}/_openapi.json`)}`;
|
|
@@ -1587,6 +1720,7 @@ function generateAboutHtml(storage, options, handlers = /* @__PURE__ */ new Map(
|
|
|
1587
1720
|
${accordions}
|
|
1588
1721
|
${nestedRoutesAccordion(relations, base)}
|
|
1589
1722
|
${options.snapshot ? snapshotAccordion() : ""}
|
|
1723
|
+
${sseRoutesAccordion(sseRoutes, base)}
|
|
1590
1724
|
${customRoutesAccordion(customRoutes, base, handlers)}
|
|
1591
1725
|
${handlersAccordion(handlers, customRoutes, base)}
|
|
1592
1726
|
</div>
|
|
@@ -2025,15 +2159,19 @@ function resolveRequestPath(dotPath, req) {
|
|
|
2025
2159
|
const [root, ...rest] = dotPath.split(".");
|
|
2026
2160
|
let value;
|
|
2027
2161
|
switch (root) {
|
|
2162
|
+
case "_body":
|
|
2028
2163
|
case "body":
|
|
2029
2164
|
value = req.body;
|
|
2030
2165
|
break;
|
|
2166
|
+
case "_params":
|
|
2031
2167
|
case "params":
|
|
2032
2168
|
value = req.params;
|
|
2033
2169
|
break;
|
|
2170
|
+
case "_query":
|
|
2034
2171
|
case "query":
|
|
2035
2172
|
value = req.query;
|
|
2036
2173
|
break;
|
|
2174
|
+
case "_headers":
|
|
2037
2175
|
case "headers":
|
|
2038
2176
|
value = req.headers;
|
|
2039
2177
|
break;
|
|
@@ -2334,6 +2472,23 @@ function buildCollectionSchema(items, fieldDefs = {}) {
|
|
|
2334
2472
|
if (def?.description) prop.description = def.description;
|
|
2335
2473
|
if (def?.enum) prop.enum = def.enum;
|
|
2336
2474
|
if (def?.default !== void 0) prop.default = def.default;
|
|
2475
|
+
if (def?.example !== void 0) prop.example = def.example;
|
|
2476
|
+
if (def?.nullable) prop.nullable = def.nullable;
|
|
2477
|
+
if (def?.minLength !== void 0) prop.minLength = def.minLength;
|
|
2478
|
+
if (def?.maxLength !== void 0) prop.maxLength = def.maxLength;
|
|
2479
|
+
if (def?.pattern) prop.pattern = def.pattern;
|
|
2480
|
+
if (def?.minimum !== void 0) prop.minimum = def.minimum;
|
|
2481
|
+
if (def?.maximum !== void 0) prop.maximum = def.maximum;
|
|
2482
|
+
if (def?.exclusiveMinimum !== void 0) prop.exclusiveMinimum = def.exclusiveMinimum;
|
|
2483
|
+
if (def?.exclusiveMaximum !== void 0) prop.exclusiveMaximum = def.exclusiveMaximum;
|
|
2484
|
+
if (def?.multipleOf !== void 0) prop.multipleOf = def.multipleOf;
|
|
2485
|
+
if (def?.items) prop.items = def.items;
|
|
2486
|
+
if (def?.minItems !== void 0) prop.minItems = def.minItems;
|
|
2487
|
+
if (def?.maxItems !== void 0) prop.maxItems = def.maxItems;
|
|
2488
|
+
if (def?.uniqueItems !== void 0) prop.uniqueItems = def.uniqueItems;
|
|
2489
|
+
if (def?.deprecated) prop.deprecated = def.deprecated;
|
|
2490
|
+
if (def?.readOnly) prop.readOnly = def.readOnly;
|
|
2491
|
+
if (def?.writeOnly) prop.writeOnly = def.writeOnly;
|
|
2337
2492
|
properties[field] = prop;
|
|
2338
2493
|
if (def?.required === true) required.push(field);
|
|
2339
2494
|
}
|
|
@@ -2718,6 +2873,80 @@ var SnapshotRouteCommand = class {
|
|
|
2718
2873
|
}
|
|
2719
2874
|
};
|
|
2720
2875
|
|
|
2876
|
+
// src/router/routes/sse.routes.ts
|
|
2877
|
+
init_esm_shims();
|
|
2878
|
+
var SSERouteCommand = class {
|
|
2879
|
+
constructor(storage, base) {
|
|
2880
|
+
this.storage = storage;
|
|
2881
|
+
this.base = base;
|
|
2882
|
+
}
|
|
2883
|
+
storage;
|
|
2884
|
+
base;
|
|
2885
|
+
register(server) {
|
|
2886
|
+
for (const route of this.storage.getSseRoutes()) {
|
|
2887
|
+
const url = `${this.base}${route.path}`;
|
|
2888
|
+
server.route({
|
|
2889
|
+
method: "GET",
|
|
2890
|
+
url,
|
|
2891
|
+
handler: async (req, reply) => {
|
|
2892
|
+
reply.hijack();
|
|
2893
|
+
const raw = reply.raw;
|
|
2894
|
+
raw.setHeader("Content-Type", "text/event-stream");
|
|
2895
|
+
raw.setHeader("Cache-Control", "no-cache");
|
|
2896
|
+
raw.setHeader("Connection", "keep-alive");
|
|
2897
|
+
raw.setHeader("Access-Control-Allow-Origin", "*");
|
|
2898
|
+
raw.flushHeaders();
|
|
2899
|
+
let alive = true;
|
|
2900
|
+
req.raw.on("close", () => {
|
|
2901
|
+
alive = false;
|
|
2902
|
+
});
|
|
2903
|
+
const keepAlive = setInterval(() => {
|
|
2904
|
+
if (alive) raw.write(": ping\n\n");
|
|
2905
|
+
}, 15e3);
|
|
2906
|
+
let frameIndex = 0;
|
|
2907
|
+
let cycleCount = 0;
|
|
2908
|
+
function sendNext() {
|
|
2909
|
+
if (!alive) {
|
|
2910
|
+
clearInterval(keepAlive);
|
|
2911
|
+
return;
|
|
2912
|
+
}
|
|
2913
|
+
const ev = route.events[frameIndex];
|
|
2914
|
+
const ctx = {
|
|
2915
|
+
params: req.params,
|
|
2916
|
+
query: req.query,
|
|
2917
|
+
body: null,
|
|
2918
|
+
headers: req.headers
|
|
2919
|
+
};
|
|
2920
|
+
const data = hasTemplates(ev.data) ? interpolate(ev.data, ctx) : ev.data;
|
|
2921
|
+
let chunk = "";
|
|
2922
|
+
if (ev.event) chunk += `event: ${ev.event}
|
|
2923
|
+
`;
|
|
2924
|
+
chunk += `data: ${JSON.stringify(data)}
|
|
2925
|
+
|
|
2926
|
+
`;
|
|
2927
|
+
raw.write(chunk);
|
|
2928
|
+
frameIndex++;
|
|
2929
|
+
if (frameIndex >= route.events.length) {
|
|
2930
|
+
frameIndex = 0;
|
|
2931
|
+
cycleCount++;
|
|
2932
|
+
const done = !route.loop || route.repeat !== void 0 && cycleCount >= route.repeat;
|
|
2933
|
+
if (done) {
|
|
2934
|
+
clearInterval(keepAlive);
|
|
2935
|
+
raw.end();
|
|
2936
|
+
return;
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
if (alive) {
|
|
2940
|
+
setTimeout(sendNext, route.interval);
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
sendNext();
|
|
2944
|
+
}
|
|
2945
|
+
});
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
};
|
|
2949
|
+
|
|
2721
2950
|
// src/router/resource.router.ts
|
|
2722
2951
|
function buildResourceRouteCommands(storage, options) {
|
|
2723
2952
|
const commands = [];
|
|
@@ -2759,6 +2988,7 @@ async function createServer(storage, options, handlers = /* @__PURE__ */ new Map
|
|
|
2759
2988
|
new AboutRouteCommand(storage, options, handlers),
|
|
2760
2989
|
new OpenApiRouteCommand(storage, options),
|
|
2761
2990
|
...options.snapshot ? [new SnapshotRouteCommand(storage)] : [],
|
|
2991
|
+
new SSERouteCommand(storage, options.base),
|
|
2762
2992
|
new CustomRouteCommand(storage, options.base, handlers),
|
|
2763
2993
|
...buildResourceRouteCommands(storage, options)
|
|
2764
2994
|
];
|
|
@@ -2837,12 +3067,13 @@ function buildOptions(opts) {
|
|
|
2837
3067
|
}
|
|
2838
3068
|
function createInMemoryStorage(data) {
|
|
2839
3069
|
const raw = data;
|
|
2840
|
-
const
|
|
3070
|
+
const RESERVED2 = /* @__PURE__ */ new Set(["_rel", "_routes", "_schema"]);
|
|
2841
3071
|
const relations = parseRelations(raw["_rel"]);
|
|
2842
3072
|
const routes = Array.isArray(raw["_routes"]) ? raw["_routes"] : [];
|
|
3073
|
+
const sseRoutes = parseSseRoutes(raw["_routes"]);
|
|
2843
3074
|
const schema = parseSchema(raw["_schema"]);
|
|
2844
3075
|
const collections = Object.fromEntries(
|
|
2845
|
-
Object.entries(raw).filter(([k]) => !
|
|
3076
|
+
Object.entries(raw).filter(([k]) => !RESERVED2.has(k))
|
|
2846
3077
|
);
|
|
2847
3078
|
let snapshot = {
|
|
2848
3079
|
data: deepCopyData(collections),
|
|
@@ -2854,6 +3085,7 @@ function createInMemoryStorage(data) {
|
|
|
2854
3085
|
getRelations: () => relations,
|
|
2855
3086
|
getSchema: () => schema,
|
|
2856
3087
|
getRoutes: () => routes,
|
|
3088
|
+
getSseRoutes: () => sseRoutes,
|
|
2857
3089
|
getCollection: (name) => collections[name],
|
|
2858
3090
|
setCollection: (name, items) => {
|
|
2859
3091
|
collections[name] = items;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yrest/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "YAML-powered json-server alternative. Zero-config REST API mock server with full CRUD, relations, filters and snapshots from a db.yml file.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"yrest",
|
|
@@ -60,12 +60,16 @@
|
|
|
60
60
|
"dev": "tsup --watch",
|
|
61
61
|
"test": "vitest",
|
|
62
62
|
"test:run": "vitest run",
|
|
63
|
+
"test:unit": "vitest run tests/config tests/cli tests/storage tests/utils",
|
|
64
|
+
"test:integration": "vitest run tests/routes tests/openapi tests/security tests/api",
|
|
65
|
+
"test:e2e": "vitest run tests/e2e",
|
|
66
|
+
"test:preflight": "vitest run",
|
|
63
67
|
"typecheck": "tsc --noEmit",
|
|
64
68
|
"lint": "eslint src tests",
|
|
65
69
|
"lint:fix": "eslint src tests --fix",
|
|
66
70
|
"format": "prettier --write .",
|
|
67
71
|
"format:check": "prettier --check .",
|
|
68
|
-
"prepublishOnly": "npm run
|
|
72
|
+
"prepublishOnly": "npm run build && npm run test:preflight",
|
|
69
73
|
"version": "node scripts/update-version-badge.mjs && git add README.md"
|
|
70
74
|
},
|
|
71
75
|
"dependencies": {
|