@specbook/cli 0.4.0 → 0.5.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/agent-guide.md +1 -1
- package/dist/bin.js +304 -213
- package/dist/standalone.js +1 -1
- package/package.json +5 -5
package/agent-guide.md
CHANGED
|
@@ -106,7 +106,7 @@ export default sequence({
|
|
|
106
106
|
- `request.with({ ... })` changes a request for one step (for example its `assert`).
|
|
107
107
|
- Also: `.input(name, ctx => controls)` asks a person for values, `.each(name, items, make)` repeats a request per item, `.check(name, fn)` asserts on the flow's state, `.use(otherFlow)` includes another flow.
|
|
108
108
|
- Controls are `c.number`, `c.text`, `c.select`, `c.multiselect`, `c.boolean`, `c.date`, `c.object`, `c.array`. A flow also runs unattended in CI and as a monitor, so every input and every `.input()` control needs a default that makes sense with nobody to ask.
|
|
109
|
-
- For generated data: `import { faker } from '@specbook/core/faker'` and use it in a default, for example `c.text({ default: () => faker.person.fullName() })`.
|
|
109
|
+
- For generated data: `import { faker } from '@specbook/core/faker'` and use it in a default, for example `c.text({ default: () => faker.person.fullName() })`. It is typed, and `specbook init` installs `@faker-js/faker`. A `{{faker.person.firstName}}` placeholder (`faker.<module>.<method>`, no arguments) also works in a field.
|
|
110
110
|
- Each step is also a request you can run alone.
|
|
111
111
|
|
|
112
112
|
### Recipe: list, check, pick, act
|
package/dist/bin.js
CHANGED
|
@@ -243,35 +243,6 @@ function exampleBoolean(key) {
|
|
|
243
243
|
key.toLowerCase()
|
|
244
244
|
);
|
|
245
245
|
}
|
|
246
|
-
var FAKE_BY_FORMAT = {
|
|
247
|
-
email: "internet.email",
|
|
248
|
-
uuid: "string.uuid",
|
|
249
|
-
uri: "internet.url",
|
|
250
|
-
url: "internet.url",
|
|
251
|
-
hostname: "internet.domainName",
|
|
252
|
-
ipv4: "internet.ipv4"
|
|
253
|
-
};
|
|
254
|
-
var FAKE_BY_NAME = [
|
|
255
|
-
[/^(first_?name|given_?name)$/, "person.firstName"],
|
|
256
|
-
[/^(last_?name|family_?name|surname)$/, "person.lastName"],
|
|
257
|
-
[/^(full_?name|display_?name)$/, "person.fullName"],
|
|
258
|
-
[/^name$/, "word.noun"],
|
|
259
|
-
[/^(user_?name|login|handle)$/, "internet.username"],
|
|
260
|
-
[/^e?-?mail(_?address)?$/, "internet.email"],
|
|
261
|
-
[/^(phone|phone_?number|mobile)$/, "phone.number"],
|
|
262
|
-
[/^(city|town)$/, "location.city"],
|
|
263
|
-
[/^country$/, "location.country"],
|
|
264
|
-
[/^(postal_?code|zip|zip_?code)$/, "location.zipCode"],
|
|
265
|
-
[/^(line1|address|street|address_?line_?1)$/, "location.streetAddress"],
|
|
266
|
-
[/^(company|organization|org)$/, "company.name"],
|
|
267
|
-
[/^(title|subject)$/, "lorem.sentence"],
|
|
268
|
-
[/^(description|summary|bio|about|body|comment|note|notes|message)$/, "lorem.sentence"],
|
|
269
|
-
[/^(url|website|homepage)$/, "internet.url"]
|
|
270
|
-
];
|
|
271
|
-
function fakerFor(key, format) {
|
|
272
|
-
const path = format && FAKE_BY_FORMAT[format] || FAKE_BY_NAME.find(([re2]) => re2.test(key.toLowerCase()))?.[1];
|
|
273
|
-
return path ? `{{faker.${path}}}` : void 0;
|
|
274
|
-
}
|
|
275
246
|
var MAX_DEPTH = 6;
|
|
276
247
|
var PLACEHOLDER_VALUE = /^(string|str|<string>|\{string\}|string\d+|null)$/i;
|
|
277
248
|
function sanitizeExample(value, key = "", format, status) {
|
|
@@ -308,18 +279,14 @@ function fits(schema, value) {
|
|
|
308
279
|
}
|
|
309
280
|
}
|
|
310
281
|
var HARD_DEPTH = 14;
|
|
311
|
-
function synthesize(schema, resolve16, depth = 0, seen = [], key = "", status,
|
|
282
|
+
function synthesize(schema, resolve16, depth = 0, seen = [], key = "", status, sparse = false) {
|
|
312
283
|
if (!schema) return null;
|
|
313
|
-
const again = (s, k = key, d = depth, sn = seen) => synthesize(s, resolve16, d, sn, k, status,
|
|
284
|
+
const again = (s, k = key, d = depth, sn = seen) => synthesize(s, resolve16, d, sn, k, status, sparse);
|
|
314
285
|
if (schema.$ref) {
|
|
315
286
|
const target = resolve16(schema.$ref);
|
|
316
287
|
if (seen.includes(schema.$ref)) return again(target, key, Math.max(depth, MAX_DEPTH), []);
|
|
317
288
|
return again(target, key, depth, [...seen, schema.$ref]);
|
|
318
289
|
}
|
|
319
|
-
if (fake && schema.type === "string" && !schema.enum) {
|
|
320
|
-
const generated = fakerFor(key, schema.format);
|
|
321
|
-
if (generated) return generated;
|
|
322
|
-
}
|
|
323
290
|
if (schema.example !== void 0 && fits(schema, schema.example))
|
|
324
291
|
return schema.enum ? schema.example : sanitizeExample(schema.example, key, schema.format, status);
|
|
325
292
|
if (schema.default !== void 0 && fits(schema, schema.default)) return schema.default;
|
|
@@ -725,13 +692,12 @@ ${typeBlocks.join("\n\n") || "export {}"}
|
|
|
725
692
|
const fileFieldPaths = body2?.encoding === "multipart" ? fileFields(body2.schema, resolve16) : [];
|
|
726
693
|
const wholeBodyFile = body2?.encoding === "binary";
|
|
727
694
|
const sample = `./${SAMPLE_FILE}`;
|
|
728
|
-
const paramsFor = (
|
|
695
|
+
const paramsFor = () => {
|
|
729
696
|
const pick = (where) => {
|
|
730
697
|
const out = {};
|
|
731
698
|
for (const p of group(where)) {
|
|
732
699
|
if (p.example !== void 0) out[p.name] = p.example;
|
|
733
|
-
else if (p.required)
|
|
734
|
-
out[p.name] = synthesize(p.schema, resolve16, 0, [], p.name, void 0, fake);
|
|
700
|
+
else if (p.required) out[p.name] = synthesize(p.schema, resolve16, 0, [], p.name);
|
|
735
701
|
}
|
|
736
702
|
return Object.keys(out).length ? out : void 0;
|
|
737
703
|
};
|
|
@@ -752,7 +718,7 @@ ${typeBlocks.join("\n\n") || "export {}"}
|
|
|
752
718
|
title: ex.summary ?? key,
|
|
753
719
|
summary: ex.description,
|
|
754
720
|
source: "spec",
|
|
755
|
-
...paramsFor(
|
|
721
|
+
...paramsFor(),
|
|
756
722
|
body: bodyFor(sanitizeExample(ex.value))
|
|
757
723
|
});
|
|
758
724
|
}
|
|
@@ -761,26 +727,14 @@ ${typeBlocks.join("\n\n") || "export {}"}
|
|
|
761
727
|
name: take("example"),
|
|
762
728
|
title: "Example",
|
|
763
729
|
source: "spec",
|
|
764
|
-
...paramsFor(
|
|
730
|
+
...paramsFor(),
|
|
765
731
|
body: bodyFor(sanitizeExample(body2.media.example))
|
|
766
732
|
});
|
|
767
733
|
const plain = {
|
|
768
|
-
...paramsFor(
|
|
769
|
-
body: body2?.schema ? bodyFor(synthesize(body2.schema, resolve16, 0, [], "", void 0,
|
|
770
|
-
};
|
|
771
|
-
const dynamic = {
|
|
772
|
-
...paramsFor(true),
|
|
773
|
-
body: body2?.schema ? bodyFor(synthesize(body2.schema, resolve16, 0, [], "", void 0, true, true)) : void 0
|
|
734
|
+
...paramsFor(),
|
|
735
|
+
body: body2?.schema ? bodyFor(synthesize(body2.schema, resolve16, 0, [], "", void 0, true)) : void 0
|
|
774
736
|
};
|
|
775
737
|
docs.push({ name: take("default"), title: "Default", source: "generated", ...plain });
|
|
776
|
-
if (JSON.stringify(dynamic) !== JSON.stringify(plain))
|
|
777
|
-
docs.push({
|
|
778
|
-
name: take("dynamic"),
|
|
779
|
-
title: "Dynamic",
|
|
780
|
-
summary: "Generated values, fresh on every play.",
|
|
781
|
-
source: "generated",
|
|
782
|
-
...dynamic
|
|
783
|
-
});
|
|
784
738
|
for (const d of docs)
|
|
785
739
|
exampleFiles.push({
|
|
786
740
|
path: `examples/${id}/${d.name}.example.json`,
|
|
@@ -2108,7 +2062,7 @@ function diffAssertion(a3, context = 3) {
|
|
|
2108
2062
|
return [];
|
|
2109
2063
|
}
|
|
2110
2064
|
|
|
2111
|
-
// ../core/src/faker.ts
|
|
2065
|
+
// ../core/src/faker-runtime.ts
|
|
2112
2066
|
var KEY = "__specbookFaker";
|
|
2113
2067
|
var store = globalThis;
|
|
2114
2068
|
var faker = new Proxy({}, {
|
|
@@ -2129,6 +2083,12 @@ function fakerLocaleNames(config = {}, locale) {
|
|
|
2129
2083
|
const name = locale ?? config.default ?? "en";
|
|
2130
2084
|
return [.../* @__PURE__ */ new Set([...config.locales?.[name] ?? [name, "en"], "base"])];
|
|
2131
2085
|
}
|
|
2086
|
+
function fakerLocaleExports(config = {}) {
|
|
2087
|
+
const names = /* @__PURE__ */ new Set(["en", ...fakerLocaleNames(config)]);
|
|
2088
|
+
for (const name of Object.keys(config.locales ?? {}))
|
|
2089
|
+
for (const n of fakerLocaleNames(config, name)) names.add(n);
|
|
2090
|
+
return [...names].sort();
|
|
2091
|
+
}
|
|
2132
2092
|
function installFaker(mod, config = {}, opts = {}) {
|
|
2133
2093
|
const name = opts.locale ?? config.default ?? "en";
|
|
2134
2094
|
const locales = fakerLocaleNames(config, name).map((n) => {
|
|
@@ -2161,15 +2121,25 @@ function isDeferred(name) {
|
|
|
2161
2121
|
return DEFERRED_NAMESPACES.includes(ns) && name.includes(".");
|
|
2162
2122
|
}
|
|
2163
2123
|
var isFakerName = (name) => name.startsWith("faker.");
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
cur = cur?.[part];
|
|
2170
|
-
if (cur === void 0) return { found: false };
|
|
2124
|
+
var FakerPlaceholderError = class extends TemplateError {
|
|
2125
|
+
constructor(variable, message) {
|
|
2126
|
+
super(variable);
|
|
2127
|
+
this.message = message;
|
|
2128
|
+
this.name = "FakerPlaceholderError";
|
|
2171
2129
|
}
|
|
2172
|
-
|
|
2130
|
+
message;
|
|
2131
|
+
};
|
|
2132
|
+
function fromFaker(path) {
|
|
2133
|
+
const [, group, method, ...rest] = path.split(".");
|
|
2134
|
+
const unknown = () => new FakerPlaceholderError(
|
|
2135
|
+
path,
|
|
2136
|
+
`Unknown faker generator "{{${path}}}": use faker.<module>.<method>`
|
|
2137
|
+
);
|
|
2138
|
+
if (!group || !method || rest.length > 0) throw unknown();
|
|
2139
|
+
const mod = faker[group];
|
|
2140
|
+
const fn = typeof mod === "object" && mod !== null ? mod[method] : void 0;
|
|
2141
|
+
if (typeof fn !== "function" || method === "constructor") throw unknown();
|
|
2142
|
+
return { found: true, value: fn.call(mod) };
|
|
2173
2143
|
}
|
|
2174
2144
|
var steps = (path) => path.replace(/\[(\d+)\]/g, ".$1").split(".");
|
|
2175
2145
|
function lookup(scope, path) {
|
|
@@ -2184,7 +2154,19 @@ function lookup(scope, path) {
|
|
|
2184
2154
|
}
|
|
2185
2155
|
return { found: true, value: cur };
|
|
2186
2156
|
}
|
|
2157
|
+
function rejectFakerCall(s) {
|
|
2158
|
+
if (!s.includes("faker")) return;
|
|
2159
|
+
const left = /\{\{\s*(faker\.[\s\S]*?)(?:\}\}|$)/.exec(s.replace(EMBED, ""));
|
|
2160
|
+
if (left) {
|
|
2161
|
+
const name = left[1].trim();
|
|
2162
|
+
throw new FakerPlaceholderError(
|
|
2163
|
+
name,
|
|
2164
|
+
`"{{${name}}}" is not a faker placeholder: placeholders take no arguments. Use faker in a request file for those.`
|
|
2165
|
+
);
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2187
2168
|
function renderString(s, scope) {
|
|
2169
|
+
rejectFakerCall(s);
|
|
2188
2170
|
const whole = WHOLE.exec(s);
|
|
2189
2171
|
if (whole) {
|
|
2190
2172
|
const name = whole[1];
|
|
@@ -2944,21 +2926,21 @@ function formatSummary(results, o = {}) {
|
|
|
2944
2926
|
const c = o.colors ?? createColors(false);
|
|
2945
2927
|
const t = reportTotals(results);
|
|
2946
2928
|
const part = (n, word, paintFn) => n ? paintFn(`${n} ${word}`) : "";
|
|
2947
|
-
const
|
|
2929
|
+
const join20 = (...parts) => parts.filter(Boolean).join(c.dim(" | ")) || c.dim("none");
|
|
2948
2930
|
const lines2 = [
|
|
2949
2931
|
"",
|
|
2950
|
-
`${c.bold(" Flows ")} ${
|
|
2932
|
+
`${c.bold(" Flows ")} ${join20(
|
|
2951
2933
|
part(t.flows.failed, "failed", c.red),
|
|
2952
2934
|
part(t.flows.errored, "errored", c.yellow),
|
|
2953
2935
|
part(t.flows.flaky, "flaky", c.yellow),
|
|
2954
2936
|
part(t.flows.passed, "passed", c.green)
|
|
2955
2937
|
)} ${c.dim(`(${results.length})`)}`,
|
|
2956
|
-
`${c.bold(" Steps ")} ${
|
|
2938
|
+
`${c.bold(" Steps ")} ${join20(
|
|
2957
2939
|
part(t.steps.failed, "failed", c.red),
|
|
2958
2940
|
part(t.steps.skipped, "skipped", c.dim),
|
|
2959
2941
|
part(t.steps.passed, "passed", c.green)
|
|
2960
2942
|
)}`,
|
|
2961
|
-
`${c.bold(" Assertions ")} ${
|
|
2943
|
+
`${c.bold(" Assertions ")} ${join20(
|
|
2962
2944
|
part(t.assertions.failed, "failed", c.red),
|
|
2963
2945
|
part(t.assertions.passed, "passed", c.green)
|
|
2964
2946
|
)}`
|
|
@@ -5501,6 +5483,16 @@ var faker2 = new Proxy({}, {
|
|
|
5501
5483
|
return current[key];
|
|
5502
5484
|
}
|
|
5503
5485
|
});
|
|
5486
|
+
function fakerLocaleNames2(config = {}, locale) {
|
|
5487
|
+
const name = locale ?? config.default ?? "en";
|
|
5488
|
+
return [.../* @__PURE__ */ new Set([...config.locales?.[name] ?? [name, "en"], "base"])];
|
|
5489
|
+
}
|
|
5490
|
+
function fakerLocaleExports2(config = {}) {
|
|
5491
|
+
const names = /* @__PURE__ */ new Set(["en", ...fakerLocaleNames2(config)]);
|
|
5492
|
+
for (const name of Object.keys(config.locales ?? {}))
|
|
5493
|
+
for (const n of fakerLocaleNames2(config, name)) names.add(n);
|
|
5494
|
+
return [...names].sort();
|
|
5495
|
+
}
|
|
5504
5496
|
var NAME2 = String.raw`[\w.$\[\]-]+`;
|
|
5505
5497
|
var WHOLE2 = new RegExp(String.raw`^\s*\{\{\s*(${NAME2})\s*\}\}\s*$`);
|
|
5506
5498
|
var EMBED2 = new RegExp(String.raw`\{\{\s*(${NAME2})\s*\}\}`, "g");
|
|
@@ -5518,15 +5510,25 @@ function isDeferred2(name) {
|
|
|
5518
5510
|
return DEFERRED_NAMESPACES2.includes(ns) && name.includes(".");
|
|
5519
5511
|
}
|
|
5520
5512
|
var isFakerName2 = (name) => name.startsWith("faker.");
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
cur = cur?.[part];
|
|
5527
|
-
if (cur === void 0) return { found: false };
|
|
5513
|
+
var FakerPlaceholderError2 = class extends TemplateError2 {
|
|
5514
|
+
constructor(variable, message) {
|
|
5515
|
+
super(variable);
|
|
5516
|
+
this.message = message;
|
|
5517
|
+
this.name = "FakerPlaceholderError";
|
|
5528
5518
|
}
|
|
5529
|
-
|
|
5519
|
+
message;
|
|
5520
|
+
};
|
|
5521
|
+
function fromFaker2(path) {
|
|
5522
|
+
const [, group, method, ...rest] = path.split(".");
|
|
5523
|
+
const unknown = () => new FakerPlaceholderError2(
|
|
5524
|
+
path,
|
|
5525
|
+
`Unknown faker generator "{{${path}}}": use faker.<module>.<method>`
|
|
5526
|
+
);
|
|
5527
|
+
if (!group || !method || rest.length > 0) throw unknown();
|
|
5528
|
+
const mod = faker2[group];
|
|
5529
|
+
const fn = typeof mod === "object" && mod !== null ? mod[method] : void 0;
|
|
5530
|
+
if (typeof fn !== "function" || method === "constructor") throw unknown();
|
|
5531
|
+
return { found: true, value: fn.call(mod) };
|
|
5530
5532
|
}
|
|
5531
5533
|
var steps2 = (path) => path.replace(/\[(\d+)\]/g, ".$1").split(".");
|
|
5532
5534
|
function lookup2(scope, path) {
|
|
@@ -5541,7 +5543,19 @@ function lookup2(scope, path) {
|
|
|
5541
5543
|
}
|
|
5542
5544
|
return { found: true, value: cur };
|
|
5543
5545
|
}
|
|
5546
|
+
function rejectFakerCall2(s) {
|
|
5547
|
+
if (!s.includes("faker")) return;
|
|
5548
|
+
const left = /\{\{\s*(faker\.[\s\S]*?)(?:\}\}|$)/.exec(s.replace(EMBED2, ""));
|
|
5549
|
+
if (left) {
|
|
5550
|
+
const name = left[1].trim();
|
|
5551
|
+
throw new FakerPlaceholderError2(
|
|
5552
|
+
name,
|
|
5553
|
+
`"{{${name}}}" is not a faker placeholder: placeholders take no arguments. Use faker in a request file for those.`
|
|
5554
|
+
);
|
|
5555
|
+
}
|
|
5556
|
+
}
|
|
5544
5557
|
function renderString2(s, scope) {
|
|
5558
|
+
rejectFakerCall2(s);
|
|
5545
5559
|
const whole = WHOLE2.exec(s);
|
|
5546
5560
|
if (whole) {
|
|
5547
5561
|
const name = whole[1];
|
|
@@ -6221,7 +6235,7 @@ async function scanExamples(root, outDir, operations) {
|
|
|
6221
6235
|
list.sort((a3, b) => order(a3) - order(b) || a3.name.localeCompare(b.name));
|
|
6222
6236
|
return { examples, assets };
|
|
6223
6237
|
}
|
|
6224
|
-
var order = (e) => e.source === "project" ? 0 : e.name === "default" ? 2 :
|
|
6238
|
+
var order = (e) => e.source === "project" ? 0 : e.name === "default" ? 2 : 1;
|
|
6225
6239
|
var IGNORE2 = ["**/node_modules/**", "**/.generated/**", "**/dist/**", "**/.git/**"];
|
|
6226
6240
|
async function scanProject(root) {
|
|
6227
6241
|
const rel2 = (e) => e.map((f2) => relative3(root, f2).split(sep3).join("/")).sort();
|
|
@@ -6508,7 +6522,7 @@ async function readBody2(req) {
|
|
|
6508
6522
|
req.on("error", fail);
|
|
6509
6523
|
});
|
|
6510
6524
|
}
|
|
6511
|
-
function fakerLocales(entry) {
|
|
6525
|
+
function fakerLocales(entry, cfg) {
|
|
6512
6526
|
const names = /* @__PURE__ */ new Set(["en", "base"]);
|
|
6513
6527
|
const dir = join5(dirname2(entry), "locale");
|
|
6514
6528
|
if (existsSync3(dir))
|
|
@@ -6516,8 +6530,11 @@ function fakerLocales(entry) {
|
|
|
6516
6530
|
const m = /^([A-Za-z0-9_]+)\.js$/.exec(f2);
|
|
6517
6531
|
if (m) names.add(m[1]);
|
|
6518
6532
|
}
|
|
6519
|
-
return [...names].sort();
|
|
6533
|
+
if (!cfg) return [...names].sort();
|
|
6534
|
+
const wanted = new Set(fakerLocaleExports2(cfg));
|
|
6535
|
+
return [...names].filter((n) => wanted.has(n)).sort();
|
|
6520
6536
|
}
|
|
6537
|
+
var USES_FAKER = /@specbook\/core\/faker|\{\{\s*faker\./;
|
|
6521
6538
|
function slim(manifests) {
|
|
6522
6539
|
return Object.fromEntries(
|
|
6523
6540
|
Object.entries(manifests).map(([k, m]) => [
|
|
@@ -6616,6 +6633,14 @@ function specbook(options) {
|
|
|
6616
6633
|
fakerEntry = createRequire(join5(root, "package.json")).resolve("@faker-js/faker");
|
|
6617
6634
|
} catch {
|
|
6618
6635
|
}
|
|
6636
|
+
if (!fakerEntry) {
|
|
6637
|
+
for (const f2 of [...files.requests, ...files.sequences]) {
|
|
6638
|
+
if (USES_FAKER.test(await readFile6(join5(root, f2), "utf8").catch(() => "")))
|
|
6639
|
+
throw new Error(
|
|
6640
|
+
`${f2} uses faker, but @faker-js/faker is not installed in this project. Add it to devDependencies (npm install --save-dev @faker-js/faker).`
|
|
6641
|
+
);
|
|
6642
|
+
}
|
|
6643
|
+
}
|
|
6619
6644
|
const operationEntries = Object.values(manifests).flatMap((m) => {
|
|
6620
6645
|
const mm = m;
|
|
6621
6646
|
return mm.operations.map(
|
|
@@ -6987,7 +7012,10 @@ export const loadConfig = ${cfg ? imp(cfg) : '() => Promise.resolve({ default: {
|
|
|
6987
7012
|
const r = await this.resolve("@faker-js/faker", join5(root, "package.json"), {
|
|
6988
7013
|
skipSelf: true
|
|
6989
7014
|
});
|
|
6990
|
-
if (!r)
|
|
7015
|
+
if (!r)
|
|
7016
|
+
throw new Error(
|
|
7017
|
+
"@faker-js/faker could not be resolved from this project. Add it to devDependencies."
|
|
7018
|
+
);
|
|
6991
7019
|
const pkg = JSON.stringify(r.id);
|
|
6992
7020
|
if (id.startsWith(RESOLVED_FAKER_LOCALE))
|
|
6993
7021
|
return `export { ${id.slice(RESOLVED_FAKER_LOCALE.length)} as default } from ${pkg}`;
|
|
@@ -6996,7 +7024,7 @@ export const loadConfig = ${cfg ? imp(cfg) : '() => Promise.resolve({ default: {
|
|
|
6996
7024
|
entry = createRequire(join5(root, "package.json")).resolve("@faker-js/faker");
|
|
6997
7025
|
} catch {
|
|
6998
7026
|
}
|
|
6999
|
-
const names = fakerLocales(entry);
|
|
7027
|
+
const names = fakerLocales(entry, options.faker);
|
|
7000
7028
|
return `export { Faker, base } from ${pkg}
|
|
7001
7029
|
export const locales = {
|
|
7002
7030
|
${names.map((n) => ` ${JSON.stringify(n)}: () => import(${JSON.stringify(`${VIRTUAL_FAKER_LOCALE}${n}`)}).then((m) => m.default)`).join(",\n")}
|
|
@@ -7484,16 +7512,44 @@ ${entry}`
|
|
|
7484
7512
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
7485
7513
|
import { createHash as createHash2 } from "crypto";
|
|
7486
7514
|
import { existsSync as existsSync7, statSync as statSync2 } from "fs";
|
|
7487
|
-
import { chmod, mkdir as mkdir6, readdir as readdir3, readFile as
|
|
7515
|
+
import { chmod, mkdir as mkdir6, readdir as readdir3, readFile as readFile14, writeFile as writeFile7 } from "fs/promises";
|
|
7488
7516
|
import { createRequire as createRequire4 } from "module";
|
|
7489
|
-
import { basename as basename4, dirname as dirname6, join as
|
|
7517
|
+
import { basename as basename4, dirname as dirname6, join as join13, relative as relative9, resolve as resolve9, sep as sep7 } from "path";
|
|
7490
7518
|
import { fileURLToPath, pathToFileURL as pathToFileURL2 } from "url";
|
|
7491
7519
|
import { rolldown } from "rolldown";
|
|
7492
7520
|
|
|
7493
|
-
// src/
|
|
7521
|
+
// src/faker-use.ts
|
|
7494
7522
|
import { readFile as readFile10 } from "fs/promises";
|
|
7495
|
-
import { join as join9
|
|
7523
|
+
import { join as join9 } from "path";
|
|
7496
7524
|
import fg6 from "fast-glob";
|
|
7525
|
+
var USES_FAKER2 = /@specbook\/core\/faker|\{\{\s*faker\./;
|
|
7526
|
+
var IGNORE3 = [
|
|
7527
|
+
"**/node_modules/**",
|
|
7528
|
+
"**/.generated/**",
|
|
7529
|
+
"**/dist/**",
|
|
7530
|
+
"**/.git/**",
|
|
7531
|
+
"**/openapi/**",
|
|
7532
|
+
"**/*.d.ts",
|
|
7533
|
+
"**/package.json",
|
|
7534
|
+
"**/package-lock.json",
|
|
7535
|
+
"**/tsconfig*.json"
|
|
7536
|
+
];
|
|
7537
|
+
async function projectUsesFaker(root) {
|
|
7538
|
+
const files = await fg6(["**/*.ts", "**/*.json", ".specbook/**/*.env"], {
|
|
7539
|
+
cwd: root,
|
|
7540
|
+
ignore: IGNORE3,
|
|
7541
|
+
dot: true,
|
|
7542
|
+
absolute: false
|
|
7543
|
+
});
|
|
7544
|
+
for (const f2 of files)
|
|
7545
|
+
if (USES_FAKER2.test(await readFile10(join9(root, f2), "utf8").catch(() => ""))) return true;
|
|
7546
|
+
return false;
|
|
7547
|
+
}
|
|
7548
|
+
|
|
7549
|
+
// src/ops.ts
|
|
7550
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
7551
|
+
import { join as join10, relative as relative7, sep as sep5 } from "path";
|
|
7552
|
+
import fg7 from "fast-glob";
|
|
7497
7553
|
import { parseSync } from "vite";
|
|
7498
7554
|
var prefixOf = (project) => (project.config.codegen?.importPrefix ?? "#api").replace(/\/+$/, "");
|
|
7499
7555
|
var IMPORT_RE = /import\s+(type\s+)?(?:([\w$]+)\s*,?\s*)?(?:\*\s+as\s+[\w$]+|\{([^}]*)\})?\s*from\s*['"]([^'"]+)['"]/g;
|
|
@@ -7540,7 +7596,7 @@ function importedOperations(file, text, prefix = "#api") {
|
|
|
7540
7596
|
return [...found.values()];
|
|
7541
7597
|
}
|
|
7542
7598
|
async function projectSources(root) {
|
|
7543
|
-
return
|
|
7599
|
+
return fg7(["**/*.ts", ".specbook/**/*.ts"], {
|
|
7544
7600
|
cwd: root,
|
|
7545
7601
|
absolute: true,
|
|
7546
7602
|
dot: true,
|
|
@@ -7558,7 +7614,7 @@ async function usedOperations(project) {
|
|
|
7558
7614
|
const out = { named: /* @__PURE__ */ new Map(), whole: /* @__PURE__ */ new Set(), where: /* @__PURE__ */ new Map() };
|
|
7559
7615
|
const prefix = prefixOf(project);
|
|
7560
7616
|
for (const file of (await projectSources(project.root)).sort()) {
|
|
7561
|
-
const text = await
|
|
7617
|
+
const text = await readFile11(file, "utf8").catch(() => "");
|
|
7562
7618
|
if (!text.includes(prefix)) continue;
|
|
7563
7619
|
const rel2 = relative7(project.root, file).split(sep5).join("/");
|
|
7564
7620
|
for (const imp of importedOperations(file, text, prefix)) {
|
|
@@ -7593,10 +7649,10 @@ async function usedOperationKeys(project, outDir) {
|
|
|
7593
7649
|
}
|
|
7594
7650
|
return new Set([...keys].filter((k) => known.has(k)));
|
|
7595
7651
|
}
|
|
7596
|
-
var metaFile = (outDir, service) =>
|
|
7652
|
+
var metaFile = (outDir, service) => join10(outDir, service, "opmeta.json");
|
|
7597
7653
|
async function readJson(file) {
|
|
7598
7654
|
try {
|
|
7599
|
-
return JSON.parse(await
|
|
7655
|
+
return JSON.parse(await readFile11(file, "utf8"));
|
|
7600
7656
|
} catch {
|
|
7601
7657
|
return void 0;
|
|
7602
7658
|
}
|
|
@@ -7630,11 +7686,11 @@ async function unknownOperationsMessage(project, outDir) {
|
|
|
7630
7686
|
}
|
|
7631
7687
|
|
|
7632
7688
|
// src/runtime.ts
|
|
7633
|
-
import { readFile as
|
|
7689
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
7634
7690
|
import { createRequire as createRequire3 } from "module";
|
|
7635
|
-
import { join as
|
|
7691
|
+
import { join as join12, relative as relative8, resolve as resolve8, sep as sep6 } from "path";
|
|
7636
7692
|
import { pathToFileURL } from "url";
|
|
7637
|
-
import
|
|
7693
|
+
import fg8 from "fast-glob";
|
|
7638
7694
|
|
|
7639
7695
|
// src/run-tests.ts
|
|
7640
7696
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -7645,8 +7701,8 @@ import { dirname as dirname5, resolve as resolve7 } from "path";
|
|
|
7645
7701
|
|
|
7646
7702
|
// src/project.ts
|
|
7647
7703
|
import { existsSync as existsSync6 } from "fs";
|
|
7648
|
-
import { readFile as
|
|
7649
|
-
import { dirname as dirname4, join as
|
|
7704
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
7705
|
+
import { dirname as dirname4, join as join11, resolve as resolve6 } from "path";
|
|
7650
7706
|
var CANDIDATES = [
|
|
7651
7707
|
".specbook/specbook.config.ts",
|
|
7652
7708
|
"specbook.config.ts",
|
|
@@ -7656,7 +7712,7 @@ function findConfigFile(cwd) {
|
|
|
7656
7712
|
let dir = resolve6(cwd);
|
|
7657
7713
|
for (; ; ) {
|
|
7658
7714
|
for (const c of CANDIDATES) {
|
|
7659
|
-
const p =
|
|
7715
|
+
const p = join11(dir, c);
|
|
7660
7716
|
if (existsSync6(p)) return p;
|
|
7661
7717
|
}
|
|
7662
7718
|
const up = dirname4(dir);
|
|
@@ -7665,7 +7721,7 @@ function findConfigFile(cwd) {
|
|
|
7665
7721
|
}
|
|
7666
7722
|
}
|
|
7667
7723
|
function rootOf(configFile) {
|
|
7668
|
-
return configFile.endsWith(`${
|
|
7724
|
+
return configFile.endsWith(`${join11(".specbook", "specbook.config.ts")}`) ? dirname4(dirname4(configFile)) : dirname4(configFile);
|
|
7669
7725
|
}
|
|
7670
7726
|
async function loadProject(cwd, loader) {
|
|
7671
7727
|
const configFile = findConfigFile(cwd);
|
|
@@ -7676,7 +7732,7 @@ async function loadProject(cwd, loader) {
|
|
|
7676
7732
|
}
|
|
7677
7733
|
async function readDotenv(path) {
|
|
7678
7734
|
if (!existsSync6(path)) return {};
|
|
7679
|
-
return parseDotenv(await
|
|
7735
|
+
return parseDotenv(await readFile12(path, "utf8"));
|
|
7680
7736
|
}
|
|
7681
7737
|
|
|
7682
7738
|
// src/version.ts
|
|
@@ -8055,7 +8111,7 @@ async function execute2(ctx, o, picked, specs, fileVars, secrets) {
|
|
|
8055
8111
|
|
|
8056
8112
|
// src/runtime.ts
|
|
8057
8113
|
async function discoverTargets(project, loader, patterns) {
|
|
8058
|
-
const files = (await
|
|
8114
|
+
const files = (await fg8(patterns, {
|
|
8059
8115
|
cwd: project.root,
|
|
8060
8116
|
absolute: true,
|
|
8061
8117
|
ignore: ["**/node_modules/**", "**/.generated/**"]
|
|
@@ -8074,7 +8130,7 @@ function mockLoaderFor(project) {
|
|
|
8074
8130
|
const manifest = (service) => {
|
|
8075
8131
|
let m = manifests.get(service);
|
|
8076
8132
|
if (!m) {
|
|
8077
|
-
m =
|
|
8133
|
+
m = readFile13(join12(outDir, service, "manifest.json"), "utf8").then(JSON.parse);
|
|
8078
8134
|
manifests.set(service, m);
|
|
8079
8135
|
}
|
|
8080
8136
|
return m;
|
|
@@ -8088,8 +8144,8 @@ function mockLoaderFor(project) {
|
|
|
8088
8144
|
for (const name of [String(status), "default"]) {
|
|
8089
8145
|
try {
|
|
8090
8146
|
return JSON.parse(
|
|
8091
|
-
await
|
|
8092
|
-
|
|
8147
|
+
await readFile13(
|
|
8148
|
+
join12(outDir, service, "examples", opId2, `${name}.response.json`),
|
|
8093
8149
|
"utf8"
|
|
8094
8150
|
)
|
|
8095
8151
|
);
|
|
@@ -8102,7 +8158,7 @@ function mockLoaderFor(project) {
|
|
|
8102
8158
|
}
|
|
8103
8159
|
async function loadFaker(project) {
|
|
8104
8160
|
try {
|
|
8105
|
-
const entry = createRequire3(
|
|
8161
|
+
const entry = createRequire3(join12(project.root, "package.json")).resolve("@faker-js/faker");
|
|
8106
8162
|
return await import(pathToFileURL(entry).href);
|
|
8107
8163
|
} catch {
|
|
8108
8164
|
return void 0;
|
|
@@ -8130,9 +8186,9 @@ function git(root) {
|
|
|
8130
8186
|
function standalonePath() {
|
|
8131
8187
|
const here = dirname6(fileURLToPath(import.meta.url));
|
|
8132
8188
|
for (const p of [
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8189
|
+
join13(here, "standalone.js"),
|
|
8190
|
+
join13(here, "..", "standalone.js"),
|
|
8191
|
+
join13(here, "..", "standalone.ts")
|
|
8136
8192
|
])
|
|
8137
8193
|
if (existsSync7(p)) return p;
|
|
8138
8194
|
throw new Error("The test runner is missing from this installation of specbook");
|
|
@@ -8140,7 +8196,7 @@ function standalonePath() {
|
|
|
8140
8196
|
async function walk(dir) {
|
|
8141
8197
|
const entries = await readdir3(dir, { withFileTypes: true }).catch(() => []);
|
|
8142
8198
|
return (await Promise.all(
|
|
8143
|
-
entries.map((e) => e.isDirectory() ? walk(
|
|
8199
|
+
entries.map((e) => e.isDirectory() ? walk(join13(dir, e.name)) : [join13(dir, e.name)])
|
|
8144
8200
|
)).flat();
|
|
8145
8201
|
}
|
|
8146
8202
|
var SKIP = /(^|\/)(node_modules|\.generated|\.git|dist|dist-[^/]+)(\/|$)/;
|
|
@@ -8148,15 +8204,15 @@ var CODE = /\.(ts|tsx|mts|cts|js|mjs|cjs|md)$/;
|
|
|
8148
8204
|
async function embed(project, outDir, used) {
|
|
8149
8205
|
const root = project.root;
|
|
8150
8206
|
const out = { examples: {}, files: {}, bytes: 0 };
|
|
8151
|
-
const read = async (file) => JSON.parse(await
|
|
8207
|
+
const read = async (file) => JSON.parse(await readFile14(file, "utf8"));
|
|
8152
8208
|
for (const [service, ops] of Object.entries(used))
|
|
8153
8209
|
for (const id of Object.keys(ops)) {
|
|
8154
|
-
const dir =
|
|
8210
|
+
const dir = join13(outDir, service, "examples", id);
|
|
8155
8211
|
for (const f2 of await readdir3(dir).catch(() => [])) {
|
|
8156
8212
|
const m = /^(.+)\.example\.json$/.exec(f2);
|
|
8157
8213
|
if (!m) continue;
|
|
8158
8214
|
out.examples[`${service}/${id}/${m[1]}`] = {
|
|
8159
|
-
doc: await read(
|
|
8215
|
+
doc: await read(join13(dir, f2)),
|
|
8160
8216
|
source: "generated",
|
|
8161
8217
|
dir: ""
|
|
8162
8218
|
};
|
|
@@ -8181,24 +8237,21 @@ async function embed(project, outDir, used) {
|
|
|
8181
8237
|
for (const abs of await walk(folder)) {
|
|
8182
8238
|
const rel2 = posix(relative9(root, abs));
|
|
8183
8239
|
if (SKIP.test(rel2) || CODE.test(rel2) || rel2.endsWith(".example.json")) continue;
|
|
8184
|
-
const data2 = await
|
|
8240
|
+
const data2 = await readFile14(abs);
|
|
8185
8241
|
out.files[rel2] = data2.toString("base64");
|
|
8186
8242
|
out.bytes += data2.length;
|
|
8187
8243
|
}
|
|
8188
8244
|
return out;
|
|
8189
8245
|
}
|
|
8190
|
-
async function fakerSource(project
|
|
8246
|
+
async function fakerSource(project) {
|
|
8191
8247
|
let entry;
|
|
8192
8248
|
try {
|
|
8193
|
-
entry = createRequire4(
|
|
8249
|
+
entry = createRequire4(join13(project.root, "package.json")).resolve("@faker-js/faker");
|
|
8194
8250
|
} catch {
|
|
8195
8251
|
return void 0;
|
|
8196
8252
|
}
|
|
8197
|
-
if (!project.config.faker && !/faker/.test(sources)) return void 0;
|
|
8198
8253
|
const cfg = project.config.faker ?? {};
|
|
8199
|
-
const wanted = /* @__PURE__ */ new Set(["Faker", ...
|
|
8200
|
-
for (const name of Object.keys(cfg.locales ?? {}))
|
|
8201
|
-
for (const n of fakerLocaleNames(cfg, name)) wanted.add(n);
|
|
8254
|
+
const wanted = /* @__PURE__ */ new Set(["Faker", ...fakerLocaleExports(cfg)]);
|
|
8202
8255
|
const have = new Set(Object.keys(await import(pathToFileURL2(entry).href)));
|
|
8203
8256
|
const names = [...wanted].filter((n) => have.has(n)).sort();
|
|
8204
8257
|
return `export { ${names.join(", ")} } from ${JSON.stringify(entry)}
|
|
@@ -8228,11 +8281,10 @@ async function bundleCommand(project, loader, o) {
|
|
|
8228
8281
|
const files = [...new Set(chosen.map((t) => t.file))].sort();
|
|
8229
8282
|
const ops = await usedMetadata(project, outDir);
|
|
8230
8283
|
const sourceText = (await Promise.all(
|
|
8231
|
-
[project.configFile, ...files.map((f2) =>
|
|
8232
|
-
(f2) =>
|
|
8284
|
+
[project.configFile, ...files.map((f2) => join13(root, f2))].map(
|
|
8285
|
+
(f2) => readFile14(f2, "utf8").catch(() => "")
|
|
8233
8286
|
)
|
|
8234
8287
|
)).join("\n");
|
|
8235
|
-
const faker3 = await fakerSource(project, sourceText);
|
|
8236
8288
|
const embedded = await embed(project, outDir, ops.services);
|
|
8237
8289
|
const environments = {};
|
|
8238
8290
|
for (const env of await listEnvironmentFiles(root))
|
|
@@ -8246,48 +8298,48 @@ async function bundleCommand(project, loader, o) {
|
|
|
8246
8298
|
examples: embedded.examples,
|
|
8247
8299
|
files: embedded.files
|
|
8248
8300
|
};
|
|
8249
|
-
const entry = [
|
|
8250
|
-
`import { main } from ${JSON.stringify(standalonePath())}`,
|
|
8251
|
-
`import config from ${JSON.stringify(project.configFile)}`,
|
|
8252
|
-
...files.map((f2, i) => `import * as m${i} from ${JSON.stringify(join12(root, f2))}`),
|
|
8253
|
-
...faker3 ? [`import * as fakerModule from ${JSON.stringify(FAKER)}`] : [],
|
|
8254
|
-
`const data = { ...${JSON.stringify(data2)}, config,`,
|
|
8255
|
-
` modules: [${files.map((f2, i) => `{ file: ${JSON.stringify(f2)}, mod: m${i} }`).join(", ")}],`,
|
|
8256
|
-
` faker: ${faker3 ? "fakerModule" : "undefined"} }`,
|
|
8257
|
-
"main(data).then((code) => process.exit(code), (e) => {",
|
|
8258
|
-
" console.error(e instanceof Error ? e.message : e)",
|
|
8259
|
-
" process.exit(2)",
|
|
8260
|
-
"})"
|
|
8261
|
-
].join("\n");
|
|
8262
8301
|
const ours = createRequire4(import.meta.url);
|
|
8263
|
-
const plugin = {
|
|
8264
|
-
name: "specbook-bundle",
|
|
8265
|
-
resolveId(id) {
|
|
8266
|
-
if (id === ENTRY) return ENTRY;
|
|
8267
|
-
if (id === FAKER) return FAKER;
|
|
8268
|
-
if (id.startsWith(`${prefix}/`) && !id.slice(prefix.length + 1).includes("/"))
|
|
8269
|
-
return `\0specbook:api:${id.slice(prefix.length + 1)}`;
|
|
8270
|
-
if (id === "@specbook/core" || id.startsWith("@specbook/core/")) return ours.resolve(id);
|
|
8271
|
-
return void 0;
|
|
8272
|
-
},
|
|
8273
|
-
load(id) {
|
|
8274
|
-
if (id === ENTRY) return entry;
|
|
8275
|
-
if (id === FAKER) return faker3;
|
|
8276
|
-
if (!id.startsWith("\0specbook:api:")) return void 0;
|
|
8277
|
-
const service = id.slice("\0specbook:api:".length);
|
|
8278
|
-
const metas = ops.services[service];
|
|
8279
|
-
if (!metas) throw new Error(`Service "${service}" has no operations your requests use`);
|
|
8280
|
-
return [
|
|
8281
|
-
"import { defineOperation } from '@specbook/core'",
|
|
8282
|
-
...Object.entries(metas).map(
|
|
8283
|
-
([opId2, meta]) => `export const ${opId2} = /* @__PURE__ */ defineOperation(${JSON.stringify(meta)})`
|
|
8284
|
-
),
|
|
8285
|
-
""
|
|
8286
|
-
].join("\n");
|
|
8287
|
-
}
|
|
8288
|
-
};
|
|
8289
8302
|
const warnings = [];
|
|
8290
|
-
|
|
8303
|
+
const compile = async (faker3) => {
|
|
8304
|
+
const entry = [
|
|
8305
|
+
`import { main } from ${JSON.stringify(standalonePath())}`,
|
|
8306
|
+
`import config from ${JSON.stringify(project.configFile)}`,
|
|
8307
|
+
...files.map((f2, i) => `import * as m${i} from ${JSON.stringify(join13(root, f2))}`),
|
|
8308
|
+
...faker3 ? [`import * as fakerModule from ${JSON.stringify(FAKER)}`] : [],
|
|
8309
|
+
`const data = { ...${JSON.stringify(data2)}, config,`,
|
|
8310
|
+
` modules: [${files.map((f2, i) => `{ file: ${JSON.stringify(f2)}, mod: m${i} }`).join(", ")}],`,
|
|
8311
|
+
` faker: ${faker3 ? "fakerModule" : "undefined"} }`,
|
|
8312
|
+
"main(data).then((code) => process.exit(code), (e) => {",
|
|
8313
|
+
" console.error(e instanceof Error ? e.message : e)",
|
|
8314
|
+
" process.exit(2)",
|
|
8315
|
+
"})"
|
|
8316
|
+
].join("\n");
|
|
8317
|
+
const plugin = {
|
|
8318
|
+
name: "specbook-bundle",
|
|
8319
|
+
resolveId(id) {
|
|
8320
|
+
if (id === ENTRY) return ENTRY;
|
|
8321
|
+
if (id === FAKER) return FAKER;
|
|
8322
|
+
if (id.startsWith(`${prefix}/`) && !id.slice(prefix.length + 1).includes("/"))
|
|
8323
|
+
return `\0specbook:api:${id.slice(prefix.length + 1)}`;
|
|
8324
|
+
if (id === "@specbook/core" || id.startsWith("@specbook/core/")) return ours.resolve(id);
|
|
8325
|
+
return void 0;
|
|
8326
|
+
},
|
|
8327
|
+
load(id) {
|
|
8328
|
+
if (id === ENTRY) return entry;
|
|
8329
|
+
if (id === FAKER) return faker3;
|
|
8330
|
+
if (!id.startsWith("\0specbook:api:")) return void 0;
|
|
8331
|
+
const service = id.slice("\0specbook:api:".length);
|
|
8332
|
+
const metas = ops.services[service];
|
|
8333
|
+
if (!metas) throw new Error(`Service "${service}" has no operations your requests use`);
|
|
8334
|
+
return [
|
|
8335
|
+
"import { defineOperation } from '@specbook/core'",
|
|
8336
|
+
...Object.entries(metas).map(
|
|
8337
|
+
([opId2, meta]) => `export const ${opId2} = /* @__PURE__ */ defineOperation(${JSON.stringify(meta)})`
|
|
8338
|
+
),
|
|
8339
|
+
""
|
|
8340
|
+
].join("\n");
|
|
8341
|
+
}
|
|
8342
|
+
};
|
|
8291
8343
|
const build2 = await rolldown({
|
|
8292
8344
|
input: ENTRY,
|
|
8293
8345
|
platform: "node",
|
|
@@ -8300,7 +8352,7 @@ async function bundleCommand(project, loader, o) {
|
|
|
8300
8352
|
}
|
|
8301
8353
|
});
|
|
8302
8354
|
await mkdir6(dirname6(out), { recursive: true });
|
|
8303
|
-
await build2.write({
|
|
8355
|
+
const written = await build2.write({
|
|
8304
8356
|
file: out,
|
|
8305
8357
|
format: "esm",
|
|
8306
8358
|
minify: true,
|
|
@@ -8308,6 +8360,31 @@ async function bundleCommand(project, loader, o) {
|
|
|
8308
8360
|
banner: "#!/usr/bin/env node"
|
|
8309
8361
|
});
|
|
8310
8362
|
await build2.close();
|
|
8363
|
+
return written.output.some(
|
|
8364
|
+
(c) => c.type === "chunk" && Object.keys(c.modules).some((m) => m.endsWith("/core/src/faker.ts"))
|
|
8365
|
+
);
|
|
8366
|
+
};
|
|
8367
|
+
const noFaker = () => console.error(
|
|
8368
|
+
"These tests use faker, but @faker-js/faker is not installed in this project. Add it to devDependencies (npm install --save-dev @faker-js/faker)."
|
|
8369
|
+
);
|
|
8370
|
+
try {
|
|
8371
|
+
const text = `${sourceText}
|
|
8372
|
+
${JSON.stringify(embedded.examples)}
|
|
8373
|
+
${JSON.stringify(environments)}`;
|
|
8374
|
+
let faker3 = USES_FAKER2.test(text) ? await fakerSource(project) : void 0;
|
|
8375
|
+
if (USES_FAKER2.test(text) && !faker3) {
|
|
8376
|
+
noFaker();
|
|
8377
|
+
return 2;
|
|
8378
|
+
}
|
|
8379
|
+
const importsFaker = await compile(faker3);
|
|
8380
|
+
if (importsFaker && !faker3) {
|
|
8381
|
+
faker3 = await fakerSource(project);
|
|
8382
|
+
if (!faker3) {
|
|
8383
|
+
noFaker();
|
|
8384
|
+
return 2;
|
|
8385
|
+
}
|
|
8386
|
+
await compile(faker3);
|
|
8387
|
+
}
|
|
8311
8388
|
} catch (e) {
|
|
8312
8389
|
console.error(
|
|
8313
8390
|
`Could not compile the tests into one file:
|
|
@@ -8319,7 +8396,7 @@ ${e instanceof Error ? e.message : e}`
|
|
|
8319
8396
|
return 2;
|
|
8320
8397
|
}
|
|
8321
8398
|
await chmod(out, 493);
|
|
8322
|
-
const bytes = await
|
|
8399
|
+
const bytes = await readFile14(out);
|
|
8323
8400
|
const hash = sha2562(bytes);
|
|
8324
8401
|
await writeFile7(`${out}.sha256`, `${hash} ${basename4(out)}
|
|
8325
8402
|
`);
|
|
@@ -8339,9 +8416,9 @@ ${e instanceof Error ? e.message : e}`
|
|
|
8339
8416
|
|
|
8340
8417
|
// src/commands/check.ts
|
|
8341
8418
|
import { existsSync as existsSync8 } from "fs";
|
|
8342
|
-
import { readFile as
|
|
8343
|
-
import { join as
|
|
8344
|
-
import
|
|
8419
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
8420
|
+
import { join as join14, resolve as resolve10 } from "path";
|
|
8421
|
+
import fg9 from "fast-glob";
|
|
8345
8422
|
async function checkCommand(project, loader) {
|
|
8346
8423
|
const errors = [];
|
|
8347
8424
|
const warnings = [];
|
|
@@ -8360,8 +8437,8 @@ async function checkCommand(project, loader) {
|
|
|
8360
8437
|
if (untracked.includes(name)) continue;
|
|
8361
8438
|
let drift = 0;
|
|
8362
8439
|
for (const f2 of files2) {
|
|
8363
|
-
const p =
|
|
8364
|
-
if (!existsSync8(p) || await
|
|
8440
|
+
const p = join14(outDir, name, f2.path);
|
|
8441
|
+
if (!existsSync8(p) || await readFile15(p, "utf8") !== f2.content) drift++;
|
|
8365
8442
|
}
|
|
8366
8443
|
if (drift)
|
|
8367
8444
|
errors.push(
|
|
@@ -8440,7 +8517,7 @@ async function checkCommand(project, loader) {
|
|
|
8440
8517
|
...fileVarNames
|
|
8441
8518
|
]);
|
|
8442
8519
|
const declared = new Set(Object.keys(cfg.secrets ?? {}));
|
|
8443
|
-
const files = await
|
|
8520
|
+
const files = await fg9("**/*.req.ts", {
|
|
8444
8521
|
cwd: project.root,
|
|
8445
8522
|
absolute: true,
|
|
8446
8523
|
ignore: ["**/node_modules/**", "**/.generated/**"]
|
|
@@ -8504,13 +8581,13 @@ async function generateCommand(project, loader, only) {
|
|
|
8504
8581
|
|
|
8505
8582
|
// src/commands/init.ts
|
|
8506
8583
|
import { existsSync as existsSync10 } from "fs";
|
|
8507
|
-
import { mkdir as mkdir8, readFile as
|
|
8508
|
-
import { join as
|
|
8584
|
+
import { mkdir as mkdir8, readFile as readFile17, writeFile as writeFile9 } from "fs/promises";
|
|
8585
|
+
import { join as join16 } from "path";
|
|
8509
8586
|
|
|
8510
8587
|
// src/commands/agents.ts
|
|
8511
8588
|
import { existsSync as existsSync9, realpathSync } from "fs";
|
|
8512
|
-
import { mkdir as mkdir7, readFile as
|
|
8513
|
-
import { join as
|
|
8589
|
+
import { mkdir as mkdir7, readFile as readFile16, writeFile as writeFile8 } from "fs/promises";
|
|
8590
|
+
import { join as join15 } from "path";
|
|
8514
8591
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8515
8592
|
var FILES = ["AGENTS.md", "CLAUDE.md"];
|
|
8516
8593
|
var START = "<!-- specbook:start";
|
|
@@ -8548,18 +8625,18 @@ var importsAgents = (text) => /^@AGENTS\.md\b/m.test(text);
|
|
|
8548
8625
|
async function readGuide() {
|
|
8549
8626
|
for (const rel2 of ["../agent-guide.md", "../../agent-guide.md"])
|
|
8550
8627
|
try {
|
|
8551
|
-
return await
|
|
8628
|
+
return await readFile16(fileURLToPath2(new URL(rel2, import.meta.url)), "utf8");
|
|
8552
8629
|
} catch {
|
|
8553
8630
|
}
|
|
8554
8631
|
throw new Error("agent-guide.md is missing from the @specbook/cli package");
|
|
8555
8632
|
}
|
|
8556
8633
|
async function agentsStep(cwd, agents) {
|
|
8557
8634
|
if (agents === false) return;
|
|
8558
|
-
const dir =
|
|
8635
|
+
const dir = join15(cwd, ".specbook");
|
|
8559
8636
|
await mkdir7(dir, { recursive: true });
|
|
8560
8637
|
const guide = await readGuide();
|
|
8561
8638
|
await writeFile8(
|
|
8562
|
-
|
|
8639
|
+
join15(dir, "agent-guide.md"),
|
|
8563
8640
|
`<!-- Written by specbook init v${cliVersion()}. Re-run \`specbook init\` to refresh it, and put project rules in AGENTS.md. -->
|
|
8564
8641
|
|
|
8565
8642
|
${guide}`
|
|
@@ -8568,15 +8645,15 @@ ${guide}`
|
|
|
8568
8645
|
const seen = /* @__PURE__ */ new Set();
|
|
8569
8646
|
const found = [];
|
|
8570
8647
|
for (const f2 of FILES) {
|
|
8571
|
-
if (!existsSync9(
|
|
8572
|
-
const real = realpathSync(
|
|
8648
|
+
if (!existsSync9(join15(cwd, f2))) continue;
|
|
8649
|
+
const real = realpathSync(join15(cwd, f2));
|
|
8573
8650
|
if (seen.has(real)) continue;
|
|
8574
8651
|
seen.add(real);
|
|
8575
8652
|
found.push(f2);
|
|
8576
8653
|
}
|
|
8577
8654
|
if (found.length === 0) {
|
|
8578
8655
|
await writeFile8(
|
|
8579
|
-
|
|
8656
|
+
join15(cwd, "AGENTS.md"),
|
|
8580
8657
|
`# AGENTS.md
|
|
8581
8658
|
|
|
8582
8659
|
Instructions for AI coding agents working in this repository.
|
|
@@ -8594,8 +8671,8 @@ ${agentBlock()}
|
|
|
8594
8671
|
return;
|
|
8595
8672
|
}
|
|
8596
8673
|
for (const file of found) {
|
|
8597
|
-
const path =
|
|
8598
|
-
const text = await
|
|
8674
|
+
const path = join15(cwd, file);
|
|
8675
|
+
const text = await readFile16(path, "utf8");
|
|
8599
8676
|
if (file === "CLAUDE.md" && found.includes("AGENTS.md") && importsAgents(text)) {
|
|
8600
8677
|
console.log("CLAUDE.md imports AGENTS.md: its section covers Claude Code too");
|
|
8601
8678
|
continue;
|
|
@@ -8615,10 +8692,11 @@ ${agentBlock()}
|
|
|
8615
8692
|
}
|
|
8616
8693
|
|
|
8617
8694
|
// src/commands/init.ts
|
|
8695
|
+
var FAKER_VERSION = "9.3.0";
|
|
8618
8696
|
async function initCommand(cwd, opts = {}) {
|
|
8619
|
-
const dir =
|
|
8697
|
+
const dir = join16(cwd, ".specbook");
|
|
8620
8698
|
await mkdir8(dir, { recursive: true });
|
|
8621
|
-
const cfg =
|
|
8699
|
+
const cfg = join16(dir, "specbook.config.ts");
|
|
8622
8700
|
if (!existsSync10(cfg)) {
|
|
8623
8701
|
await writeFile9(
|
|
8624
8702
|
cfg,
|
|
@@ -8639,28 +8717,35 @@ export default defineConfig({
|
|
|
8639
8717
|
console.log("\u2713 created .specbook/specbook.config.ts");
|
|
8640
8718
|
}
|
|
8641
8719
|
const envDir = await ensureEnvironmentsDir(cwd);
|
|
8642
|
-
const example =
|
|
8720
|
+
const example = join16(envDir, "staging.secrets.env.example");
|
|
8643
8721
|
if (!existsSync10(example)) {
|
|
8644
8722
|
await writeFile9(
|
|
8645
8723
|
example,
|
|
8646
8724
|
"# Copy to staging.secrets.env (gitignored) and fill in. One KEY=value per line.\n# YOUR_API_KEY=\n"
|
|
8647
8725
|
);
|
|
8648
8726
|
await writeFile9(
|
|
8649
|
-
|
|
8727
|
+
join16(envDir, "staging.env"),
|
|
8650
8728
|
'# Non-secret variables for the "staging" environment (committed).\n# region=eu-west-1\n'
|
|
8651
8729
|
);
|
|
8652
8730
|
console.log("\u2713 created .specbook/environments/ (staging.env, staging.secrets.env.example)");
|
|
8653
8731
|
}
|
|
8654
|
-
const pkgPath =
|
|
8655
|
-
const pkg = existsSync10(pkgPath) ? JSON.parse(await
|
|
8732
|
+
const pkgPath = join16(cwd, "package.json");
|
|
8733
|
+
const pkg = existsSync10(pkgPath) ? JSON.parse(await readFile17(pkgPath, "utf8")) : { name: "my-apis", private: true, type: "module" };
|
|
8656
8734
|
pkg.imports = { ...pkg.imports ?? {}, "#api/*": "./.generated/*/index.ts" };
|
|
8657
8735
|
pkg.scripts = { ...pkg.scripts ?? {} };
|
|
8658
8736
|
if (!pkg.scripts.prepare) pkg.scripts.prepare = "specbook generate";
|
|
8737
|
+
pkg.devDependencies = { ...pkg.devDependencies ?? {} };
|
|
8738
|
+
const hasFaker = ["dependencies", "devDependencies", "peerDependencies"].some(
|
|
8739
|
+
(k) => pkg[k]?.["@faker-js/faker"]
|
|
8740
|
+
);
|
|
8741
|
+
if (!hasFaker) pkg.devDependencies["@faker-js/faker"] = FAKER_VERSION;
|
|
8659
8742
|
await writeFile9(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
8660
8743
|
`);
|
|
8661
|
-
console.log(
|
|
8662
|
-
|
|
8663
|
-
|
|
8744
|
+
console.log(
|
|
8745
|
+
`\u2713 added the "#api/*" subpath import, a "prepare" script${hasFaker ? "" : " and @faker-js/faker"} to package.json`
|
|
8746
|
+
);
|
|
8747
|
+
const gi = join16(cwd, ".gitignore");
|
|
8748
|
+
const current = existsSync10(gi) ? await readFile17(gi, "utf8") : "";
|
|
8664
8749
|
const missing = [".generated", ".specbook/secrets.local.env"].filter(
|
|
8665
8750
|
(entry) => !current.split("\n").some((l) => l.trim() === entry)
|
|
8666
8751
|
);
|
|
@@ -8681,13 +8766,13 @@ import { existsSync as existsSync11, readFileSync as readFileSync2 } from "fs";
|
|
|
8681
8766
|
import { mkdir as mkdir9, mkdtemp as mkdtemp2, rm as rm4, symlink, writeFile as writeFile11 } from "fs/promises";
|
|
8682
8767
|
import { createRequire as createRequire6 } from "module";
|
|
8683
8768
|
import { tmpdir as tmpdir2 } from "os";
|
|
8684
|
-
import { dirname as dirname8, extname, join as
|
|
8769
|
+
import { dirname as dirname8, extname, join as join18, resolve as resolve12 } from "path";
|
|
8685
8770
|
import SwaggerParser4 from "@apidevtools/swagger-parser";
|
|
8686
8771
|
|
|
8687
8772
|
// src/commands/ui.ts
|
|
8688
|
-
import { copyFile, cp, readFile as
|
|
8773
|
+
import { copyFile, cp, readFile as readFile18, stat, writeFile as writeFile10 } from "fs/promises";
|
|
8689
8774
|
import { createRequire as createRequire5 } from "module";
|
|
8690
|
-
import { basename as basename5, dirname as dirname7, join as
|
|
8775
|
+
import { basename as basename5, dirname as dirname7, join as join17, resolve as resolve11, sep as sep8 } from "path";
|
|
8691
8776
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
8692
8777
|
import { build, createServer } from "vite";
|
|
8693
8778
|
function uiRoot() {
|
|
@@ -8708,9 +8793,9 @@ function enclosingNodeModules(file) {
|
|
|
8708
8793
|
function dependencyFolders(root) {
|
|
8709
8794
|
const found = /* @__PURE__ */ new Set([
|
|
8710
8795
|
...enclosingNodeModules(fileURLToPath3(import.meta.url)),
|
|
8711
|
-
...enclosingNodeModules(
|
|
8796
|
+
...enclosingNodeModules(join17(root, "package.json"))
|
|
8712
8797
|
]);
|
|
8713
|
-
for (const from of [
|
|
8798
|
+
for (const from of [join17(root, "package.json"), fileURLToPath3(import.meta.url)]) {
|
|
8714
8799
|
const require2 = createRequire5(from);
|
|
8715
8800
|
for (const name of ["vite", "prism-code-editor", "preact", "@preact/signals"]) {
|
|
8716
8801
|
try {
|
|
@@ -8735,7 +8820,12 @@ function uiConfig(project, operations) {
|
|
|
8735
8820
|
rolldownOptions: { transform: { jsx: { runtime: "automatic", importSource: "preact" } } }
|
|
8736
8821
|
},
|
|
8737
8822
|
plugins: [
|
|
8738
|
-
specbook({
|
|
8823
|
+
specbook({
|
|
8824
|
+
projectRoot: project.root,
|
|
8825
|
+
outDir: project.config.codegen?.outDir,
|
|
8826
|
+
operations,
|
|
8827
|
+
faker: project.config.faker
|
|
8828
|
+
})
|
|
8739
8829
|
],
|
|
8740
8830
|
server: {
|
|
8741
8831
|
fs: {
|
|
@@ -8768,7 +8858,7 @@ async function devCommand(project, port = 5173) {
|
|
|
8768
8858
|
}
|
|
8769
8859
|
async function buildCommand(project, outDir, options = {}) {
|
|
8770
8860
|
const out = resolve11(project.root, outDir);
|
|
8771
|
-
const generated =
|
|
8861
|
+
const generated = join17(project.root, project.config.codegen?.outDir ?? ".generated");
|
|
8772
8862
|
const operations = options.minimal ? await usedOperationKeys(project, generated) : void 0;
|
|
8773
8863
|
if (operations && operations.size === 0)
|
|
8774
8864
|
throw new Error("Nothing to publish: no request or flow imports an operation (see --minimal)");
|
|
@@ -8790,12 +8880,12 @@ async function buildCommand(project, outDir, options = {}) {
|
|
|
8790
8880
|
}
|
|
8791
8881
|
}
|
|
8792
8882
|
});
|
|
8793
|
-
await copyMetadata(
|
|
8883
|
+
await copyMetadata(join17(generated, "specbook.json"), join17(out, "specbook.json"), operations);
|
|
8794
8884
|
}
|
|
8795
8885
|
async function copyMetadata(from, to, operations) {
|
|
8796
8886
|
try {
|
|
8797
8887
|
if (!operations) return await copyFile(from, to);
|
|
8798
|
-
const meta = JSON.parse(await
|
|
8888
|
+
const meta = JSON.parse(await readFile18(from, "utf8"));
|
|
8799
8889
|
for (const [svc, s] of Object.entries(meta.services)) {
|
|
8800
8890
|
s.operations = Object.fromEntries(
|
|
8801
8891
|
Object.entries(s.operations).filter(([id]) => operations.has(`${svc}/${id}`))
|
|
@@ -8808,7 +8898,7 @@ async function copyMetadata(from, to, operations) {
|
|
|
8808
8898
|
}
|
|
8809
8899
|
}
|
|
8810
8900
|
async function copyViewer(project, outDir, viewerDir = resolve11(uiRoot(), "dist-viewer")) {
|
|
8811
|
-
const has2 = await stat(
|
|
8901
|
+
const has2 = await stat(join17(viewerDir, "index.html")).then(
|
|
8812
8902
|
() => true,
|
|
8813
8903
|
() => false
|
|
8814
8904
|
);
|
|
@@ -8827,9 +8917,9 @@ var slug3 = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/
|
|
|
8827
8917
|
async function link(root, name) {
|
|
8828
8918
|
try {
|
|
8829
8919
|
let dir = dirname8(createRequire6(import.meta.url).resolve(name));
|
|
8830
|
-
while (!existsSync11(
|
|
8920
|
+
while (!existsSync11(join18(dir, "package.json")) || JSON.parse(readFileSync2(join18(dir, "package.json"), "utf8")).name !== name)
|
|
8831
8921
|
dir = dirname8(dir);
|
|
8832
|
-
const dest =
|
|
8922
|
+
const dest = join18(root, "node_modules", ...name.split("/"));
|
|
8833
8923
|
await mkdir9(dirname8(dest), { recursive: true });
|
|
8834
8924
|
await symlink(dir, dest, "dir");
|
|
8835
8925
|
} catch {
|
|
@@ -8850,7 +8940,7 @@ async function previewCommand(source, o = {}) {
|
|
|
8850
8940
|
const { servers } = openApiServers(api, source);
|
|
8851
8941
|
const where = servers.length === 0 ? "" : servers.length === 1 && servers[0]?.name === "live" ? `, live calls go to ${servers[0].url}` : `
|
|
8852
8942
|
Environments: ${servers.map((s) => `${s.name} \u2192 ${s.url}`).join(", ")}`;
|
|
8853
|
-
const root = await mkdtemp2(
|
|
8943
|
+
const root = await mkdtemp2(join18(tmpdir2(), "specbook-preview-"));
|
|
8854
8944
|
const cleanup = () => rm4(root, { recursive: true, force: true }).catch(() => {
|
|
8855
8945
|
});
|
|
8856
8946
|
process.once("SIGINT", () => cleanup().then(() => process.exit(0)));
|
|
@@ -8860,7 +8950,7 @@ async function previewCommand(source, o = {}) {
|
|
|
8860
8950
|
const res = await fetch(source);
|
|
8861
8951
|
if (!res.ok) throw new Error(`Could not fetch ${source} (${res.status})`);
|
|
8862
8952
|
const ext = /ya?ml/.test(extname(new URL(source).pathname)) ? ".yaml" : ".json";
|
|
8863
|
-
specFile =
|
|
8953
|
+
specFile = join18(root, `openapi/api${ext}`);
|
|
8864
8954
|
await mkdir9(dirname8(specFile), { recursive: true });
|
|
8865
8955
|
await writeFile11(specFile, await res.text());
|
|
8866
8956
|
}
|
|
@@ -8895,14 +8985,14 @@ async function previewCommand(source, o = {}) {
|
|
|
8895
8985
|
}
|
|
8896
8986
|
}
|
|
8897
8987
|
};
|
|
8898
|
-
const configFile =
|
|
8988
|
+
const configFile = join18(root, ".specbook/specbook.config.ts");
|
|
8899
8989
|
await mkdir9(dirname8(configFile), { recursive: true });
|
|
8900
8990
|
await writeFile11(configFile, `export default ${JSON.stringify(config, null, 2)}
|
|
8901
8991
|
`);
|
|
8902
8992
|
await link(root, "@specbook/core");
|
|
8903
8993
|
await link(root, "@faker-js/faker");
|
|
8904
8994
|
console.log(`Generating "${title}" \u2026`);
|
|
8905
|
-
const out = await generateToDisk({ spec: specFile, service, outDir:
|
|
8995
|
+
const out = await generateToDisk({ spec: specFile, service, outDir: join18(root, ".generated") });
|
|
8906
8996
|
console.log(`\u2713 ${out.operations.length} operations${where}`);
|
|
8907
8997
|
const project = { root, configFile, config };
|
|
8908
8998
|
const server = await devCommand(project, o.port ?? 5173);
|
|
@@ -8971,18 +9061,18 @@ import { resolve as resolve15 } from "path";
|
|
|
8971
9061
|
|
|
8972
9062
|
// src/examples.ts
|
|
8973
9063
|
import { File } from "buffer";
|
|
8974
|
-
import { readFile as
|
|
8975
|
-
import { basename as basename6, dirname as dirname9, join as
|
|
8976
|
-
import
|
|
8977
|
-
var
|
|
9064
|
+
import { readFile as readFile19 } from "fs/promises";
|
|
9065
|
+
import { basename as basename6, dirname as dirname9, join as join19, resolve as resolve14 } from "path";
|
|
9066
|
+
import fg10 from "fast-glob";
|
|
9067
|
+
var IGNORE4 = ["**/node_modules/**", "**/.generated/**", "**/dist/**", "**/.git/**"];
|
|
8978
9068
|
function exampleLoadersFor(project) {
|
|
8979
9069
|
const outDir = resolve14(project.root, project.config.codegen?.outDir ?? ".generated");
|
|
8980
9070
|
const example = async (op, name) => {
|
|
8981
9071
|
const rel2 = `${op.service}/${op.id}/${name}.example.json`;
|
|
8982
|
-
const [custom] = await
|
|
8983
|
-
const file = custom ??
|
|
9072
|
+
const [custom] = await fg10(`**/${rel2}`, { cwd: project.root, absolute: true, ignore: IGNORE4 });
|
|
9073
|
+
const file = custom ?? join19(outDir, op.service, "examples", op.id, `${name}.example.json`);
|
|
8984
9074
|
try {
|
|
8985
|
-
const doc = JSON.parse(await
|
|
9075
|
+
const doc = JSON.parse(await readFile19(file, "utf8"));
|
|
8986
9076
|
return { ...doc, source: custom ? "project" : "generated", dir: dirname9(file) };
|
|
8987
9077
|
} catch {
|
|
8988
9078
|
return void 0;
|
|
@@ -8990,7 +9080,7 @@ function exampleLoadersFor(project) {
|
|
|
8990
9080
|
};
|
|
8991
9081
|
const read = async (path, dir) => {
|
|
8992
9082
|
const abs = resolve14(dir ?? project.root, path);
|
|
8993
|
-
return new File([await
|
|
9083
|
+
return new File([await readFile19(abs)], basename6(abs));
|
|
8994
9084
|
};
|
|
8995
9085
|
return { example, readFile: read };
|
|
8996
9086
|
}
|
|
@@ -9021,7 +9111,8 @@ async function testCommand(project, loader, o) {
|
|
|
9021
9111
|
readEnvironment: (env) => readEnvironment(project.root, env),
|
|
9022
9112
|
examples: exampleLoadersFor(project),
|
|
9023
9113
|
mock: mockLoaderFor(project),
|
|
9024
|
-
|
|
9114
|
+
// only a project that uses faker pays for importing it
|
|
9115
|
+
loadFaker: async () => await projectUsesFaker(project.root) ? loadFaker(project) : void 0,
|
|
9025
9116
|
readSource: (file) => {
|
|
9026
9117
|
try {
|
|
9027
9118
|
return readFileSync3(file, "utf8");
|
|
@@ -9105,7 +9196,7 @@ var TEST_HELP = ` --env <name> environment (default: mock, for
|
|
|
9105
9196
|
--input k=v flow input value (repeatable)
|
|
9106
9197
|
--var k=v variable override (repeatable)
|
|
9107
9198
|
--secrets-file <path> dotenv file with secrets
|
|
9108
|
-
--locale <l> --seed <n>
|
|
9199
|
+
--locale <l> --seed <n> generated values: locale / seed for a repeatable run
|
|
9109
9200
|
--notify <url> POST a summary to a webhook when something fails`;
|
|
9110
9201
|
var parseValue = (v2) => {
|
|
9111
9202
|
try {
|
package/dist/standalone.js
CHANGED
|
@@ -470,7 +470,7 @@ var TEST_HELP = ` --env <name> environment (default: mock, for
|
|
|
470
470
|
--input k=v flow input value (repeatable)
|
|
471
471
|
--var k=v variable override (repeatable)
|
|
472
472
|
--secrets-file <path> dotenv file with secrets
|
|
473
|
-
--locale <l> --seed <n>
|
|
473
|
+
--locale <l> --seed <n> generated values: locale / seed for a repeatable run
|
|
474
474
|
--notify <url> POST a summary to a webhook when something fails`;
|
|
475
475
|
var parseValue = (v) => {
|
|
476
476
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@specbook/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Specbook command line: init, add, generate, dev, build, test, bundle, check, secrets and preview.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"specbook",
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@apidevtools/swagger-parser": "10.1.1",
|
|
40
40
|
"@faker-js/faker": "9.3.0",
|
|
41
|
-
"@specbook/codegen": "0.
|
|
42
|
-
"@specbook/core": "0.
|
|
43
|
-
"@specbook/ui": "0.
|
|
44
|
-
"@specbook/vite": "0.
|
|
41
|
+
"@specbook/codegen": "0.5.0",
|
|
42
|
+
"@specbook/core": "0.5.0",
|
|
43
|
+
"@specbook/ui": "0.5.0",
|
|
44
|
+
"@specbook/vite": "0.5.0",
|
|
45
45
|
"fast-glob": "3.3.2",
|
|
46
46
|
"rolldown": "1.2.13",
|
|
47
47
|
"swagger2openapi": "7.0.8",
|