@emeryld/rrroutes-export 1.0.3 → 1.0.5
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 +132 -3
- package/bin/rrroutes-export-changelog.mjs +16 -0
- package/dist/defaultViewerTemplate.d.ts +1 -0
- package/dist/exportFinalizedLeaves.changelog.cli.d.ts +15 -0
- package/dist/exportFinalizedLeaves.changelog.d.ts +145 -0
- package/dist/exportFinalizedLeaves.cli.d.ts +14 -0
- package/dist/exportFinalizedLeaves.d.ts +61 -0
- package/dist/extractLeafSourceByAst.d.ts +38 -0
- package/dist/flattenSchema.d.ts +12 -0
- package/dist/index.cjs +882 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.mjs +877 -14
- package/dist/index.mjs.map +1 -1
- package/dist/schemaIntrospection.d.ts +47 -0
- package/dist/serializeLeafContract.d.ts +31 -0
- package/package.json +10 -5
- package/tools/finalized-leaves-viewer.html +129 -1
package/dist/index.cjs
CHANGED
|
@@ -31,20 +31,25 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
DEFAULT_VIEWER_TEMPLATE: () => DEFAULT_VIEWER_TEMPLATE,
|
|
34
|
+
__private: () => __private,
|
|
34
35
|
clearSchemaIntrospectionHandlers: () => clearSchemaIntrospectionHandlers,
|
|
35
36
|
createSchemaIntrospector: () => createSchemaIntrospector,
|
|
36
37
|
exportFinalizedLeaves: () => exportFinalizedLeaves,
|
|
38
|
+
exportFinalizedLeavesChangelog: () => exportFinalizedLeavesChangelog,
|
|
37
39
|
extractLeafSourceByAst: () => extractLeafSourceByAst,
|
|
38
40
|
flattenLeafSchemas: () => flattenLeafSchemas,
|
|
39
41
|
flattenSerializableSchema: () => flattenSerializableSchema,
|
|
40
42
|
introspectSchema: () => introspectSchema,
|
|
41
43
|
loadFinalizedLeavesInput: () => loadFinalizedLeavesInput,
|
|
44
|
+
parseFinalizedLeavesChangelogCliArgs: () => parseFinalizedLeavesChangelogCliArgs,
|
|
42
45
|
parseFinalizedLeavesCliArgs: () => parseFinalizedLeavesCliArgs,
|
|
43
46
|
registerSchemaIntrospectionHandler: () => registerSchemaIntrospectionHandler,
|
|
47
|
+
runExportFinalizedLeavesChangelogCli: () => runExportFinalizedLeavesChangelogCli,
|
|
44
48
|
runExportFinalizedLeavesCli: () => runExportFinalizedLeavesCli,
|
|
45
49
|
serializableSchemaKinds: () => serializableSchemaKinds,
|
|
46
50
|
serializeLeafContract: () => serializeLeafContract,
|
|
47
51
|
serializeLeavesContract: () => serializeLeavesContract,
|
|
52
|
+
writeFinalizedLeavesChangelogExport: () => writeFinalizedLeavesChangelogExport,
|
|
48
53
|
writeFinalizedLeavesExport: () => writeFinalizedLeavesExport
|
|
49
54
|
});
|
|
50
55
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -345,46 +350,46 @@ function normalizeType(schema) {
|
|
|
345
350
|
return "unknown";
|
|
346
351
|
}
|
|
347
352
|
}
|
|
348
|
-
function isNonEmptyPath(
|
|
349
|
-
return typeof
|
|
353
|
+
function isNonEmptyPath(path6) {
|
|
354
|
+
return typeof path6 === "string" && path6.length > 0;
|
|
350
355
|
}
|
|
351
|
-
function setNode(out,
|
|
352
|
-
if (!isNonEmptyPath(
|
|
353
|
-
out[
|
|
356
|
+
function setNode(out, path6, schema, inherited) {
|
|
357
|
+
if (!isNonEmptyPath(path6)) return;
|
|
358
|
+
out[path6] = {
|
|
354
359
|
type: normalizeType(schema),
|
|
355
360
|
nullable: inherited.nullable || Boolean(schema.nullable),
|
|
356
361
|
optional: inherited.optional || Boolean(schema.optional),
|
|
357
362
|
literal: schema.kind === "literal" ? schema.literal : void 0
|
|
358
363
|
};
|
|
359
364
|
}
|
|
360
|
-
function flattenInto(out, schema,
|
|
361
|
-
if (!schema || !isNonEmptyPath(
|
|
365
|
+
function flattenInto(out, schema, path6, inherited) {
|
|
366
|
+
if (!schema || !isNonEmptyPath(path6)) return;
|
|
362
367
|
const nextInherited = {
|
|
363
368
|
optional: inherited.optional || Boolean(schema.optional),
|
|
364
369
|
nullable: inherited.nullable || Boolean(schema.nullable)
|
|
365
370
|
};
|
|
366
371
|
if (schema.kind === "union" && Array.isArray(schema.union) && schema.union.length > 0) {
|
|
367
372
|
schema.union.forEach((option, index) => {
|
|
368
|
-
const optionPath = `${
|
|
373
|
+
const optionPath = `${path6}-${index + 1}`;
|
|
369
374
|
flattenInto(out, option, optionPath, inherited);
|
|
370
375
|
});
|
|
371
376
|
return;
|
|
372
377
|
}
|
|
373
|
-
setNode(out,
|
|
378
|
+
setNode(out, path6, schema, inherited);
|
|
374
379
|
if (schema.kind === "object" && schema.properties) {
|
|
375
380
|
for (const [key, child] of Object.entries(schema.properties)) {
|
|
376
|
-
const childPath = `${
|
|
381
|
+
const childPath = `${path6}.${key}`;
|
|
377
382
|
flattenInto(out, child, childPath, nextInherited);
|
|
378
383
|
}
|
|
379
384
|
return;
|
|
380
385
|
}
|
|
381
386
|
if (schema.kind === "array" && schema.element) {
|
|
382
|
-
flattenInto(out, schema.element, `${
|
|
387
|
+
flattenInto(out, schema.element, `${path6}[]`, nextInherited);
|
|
383
388
|
}
|
|
384
389
|
}
|
|
385
|
-
function flattenSerializableSchema(schema,
|
|
390
|
+
function flattenSerializableSchema(schema, path6) {
|
|
386
391
|
const out = {};
|
|
387
|
-
flattenInto(out, schema,
|
|
392
|
+
flattenInto(out, schema, path6, { optional: false, nullable: false });
|
|
388
393
|
return out;
|
|
389
394
|
}
|
|
390
395
|
function isSerializedLeaf(value) {
|
|
@@ -457,7 +462,27 @@ var DEFAULT_VIEWER_TEMPLATE = `<!doctype html>
|
|
|
457
462
|
const resultsEl = document.getElementById('results')
|
|
458
463
|
const payload = window.__FINALIZED_LEAVES_PAYLOAD
|
|
459
464
|
|
|
460
|
-
if (
|
|
465
|
+
if (payload && payload.kind === 'finalized-leaves-changelog') {
|
|
466
|
+
statusEl.textContent =
|
|
467
|
+
'Loaded changelog payload with ' + (payload.commits?.length || 0) + ' commits.'
|
|
468
|
+
|
|
469
|
+
const wrapSection = (title, value) => {
|
|
470
|
+
const card = document.createElement('details')
|
|
471
|
+
const summary = document.createElement('summary')
|
|
472
|
+
summary.textContent = title
|
|
473
|
+
const pre = document.createElement('pre')
|
|
474
|
+
pre.textContent = JSON.stringify(value, null, 2)
|
|
475
|
+
card.appendChild(summary)
|
|
476
|
+
card.appendChild(pre)
|
|
477
|
+
return card
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
resultsEl.appendChild(wrapSection('Commits', payload.commits || []))
|
|
481
|
+
resultsEl.appendChild(wrapSection('By Route', payload.byRoute || {}))
|
|
482
|
+
resultsEl.appendChild(
|
|
483
|
+
wrapSection('By Source Object', payload.bySourceObject || {}),
|
|
484
|
+
)
|
|
485
|
+
} else if (!payload || !Array.isArray(payload.leaves)) {
|
|
461
486
|
statusEl.textContent = 'No baked payload found in this HTML file.'
|
|
462
487
|
} else {
|
|
463
488
|
statusEl.textContent = 'Loaded baked payload with ' + payload.leaves.length + ' routes.'
|
|
@@ -1364,23 +1389,866 @@ async function runExportFinalizedLeavesCli(argv) {
|
|
|
1364
1389
|
outFile: import_node_path3.default.resolve(args.outFile)
|
|
1365
1390
|
};
|
|
1366
1391
|
}
|
|
1392
|
+
|
|
1393
|
+
// src/exportFinalizedLeaves.changelog.ts
|
|
1394
|
+
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
1395
|
+
var import_node_os = __toESM(require("os"), 1);
|
|
1396
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
1397
|
+
var import_node_child_process2 = require("child_process");
|
|
1398
|
+
var import_node_util = require("util");
|
|
1399
|
+
var import_node_url2 = require("url");
|
|
1400
|
+
var execFile = (0, import_node_util.promisify)(import_node_child_process2.execFile);
|
|
1401
|
+
var CHANGESET_CFG_FIELDS = [
|
|
1402
|
+
"summary",
|
|
1403
|
+
"description",
|
|
1404
|
+
"tags",
|
|
1405
|
+
"deprecated",
|
|
1406
|
+
"stability",
|
|
1407
|
+
"docsGroup",
|
|
1408
|
+
"docsHidden",
|
|
1409
|
+
"feed"
|
|
1410
|
+
];
|
|
1411
|
+
var SCHEMA_SECTIONS = ["params", "query", "body", "output"];
|
|
1412
|
+
var SOURCE_KEY_BY_SECTION = {
|
|
1413
|
+
params: "paramsSchema",
|
|
1414
|
+
query: "querySchema",
|
|
1415
|
+
body: "bodySchema",
|
|
1416
|
+
output: "outputSchema"
|
|
1417
|
+
};
|
|
1418
|
+
function normalizePath(p) {
|
|
1419
|
+
return p.replace(/\\/g, "/");
|
|
1420
|
+
}
|
|
1421
|
+
function stableStringify(value) {
|
|
1422
|
+
return JSON.stringify(value, (_key, v) => {
|
|
1423
|
+
if (v && typeof v === "object" && !Array.isArray(v)) {
|
|
1424
|
+
const sorted = Object.entries(v).sort(([a], [b]) => a.localeCompare(b)).reduce((acc, [k, inner]) => {
|
|
1425
|
+
acc[k] = inner;
|
|
1426
|
+
return acc;
|
|
1427
|
+
}, {});
|
|
1428
|
+
return sorted;
|
|
1429
|
+
}
|
|
1430
|
+
return v;
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
function sanitizeCfgValue(value) {
|
|
1434
|
+
if (Array.isArray(value)) {
|
|
1435
|
+
return value.slice().sort((a, b) => String(a).localeCompare(String(b)));
|
|
1436
|
+
}
|
|
1437
|
+
return value;
|
|
1438
|
+
}
|
|
1439
|
+
function toCfgSubset(cfg) {
|
|
1440
|
+
const out = {};
|
|
1441
|
+
for (const field of CHANGESET_CFG_FIELDS) {
|
|
1442
|
+
const value = sanitizeCfgValue(cfg[field]);
|
|
1443
|
+
if (value !== void 0) {
|
|
1444
|
+
out[field] = value;
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
return out;
|
|
1448
|
+
}
|
|
1449
|
+
function entrySignature(entry) {
|
|
1450
|
+
return stableStringify({
|
|
1451
|
+
type: entry.type,
|
|
1452
|
+
nullable: Boolean(entry.nullable),
|
|
1453
|
+
optional: Boolean(entry.optional),
|
|
1454
|
+
literal: entry.literal
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1457
|
+
function semverToTuple(tag) {
|
|
1458
|
+
const match = tag.match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/);
|
|
1459
|
+
if (!match) return null;
|
|
1460
|
+
return {
|
|
1461
|
+
major: Number(match[1]),
|
|
1462
|
+
minor: Number(match[2]),
|
|
1463
|
+
patch: Number(match[3]),
|
|
1464
|
+
prerelease: match[4] ?? ""
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
function compareSemverTags(a, b) {
|
|
1468
|
+
const av = semverToTuple(a);
|
|
1469
|
+
const bv = semverToTuple(b);
|
|
1470
|
+
if (!av && !bv) return 0;
|
|
1471
|
+
if (!av) return -1;
|
|
1472
|
+
if (!bv) return 1;
|
|
1473
|
+
if (av.major !== bv.major) return av.major - bv.major;
|
|
1474
|
+
if (av.minor !== bv.minor) return av.minor - bv.minor;
|
|
1475
|
+
if (av.patch !== bv.patch) return av.patch - bv.patch;
|
|
1476
|
+
if (!av.prerelease && bv.prerelease) return 1;
|
|
1477
|
+
if (av.prerelease && !bv.prerelease) return -1;
|
|
1478
|
+
return av.prerelease.localeCompare(bv.prerelease);
|
|
1479
|
+
}
|
|
1480
|
+
async function runGit(cwd, args) {
|
|
1481
|
+
const { stdout } = await execFile("git", args, { cwd });
|
|
1482
|
+
return stdout.trim();
|
|
1483
|
+
}
|
|
1484
|
+
async function resolveCommit(cwd, rev) {
|
|
1485
|
+
return runGit(cwd, ["rev-parse", "--verify", `${rev}^{commit}`]);
|
|
1486
|
+
}
|
|
1487
|
+
async function findLastReachableSemverTag(cwd, to) {
|
|
1488
|
+
const tagsRaw = await runGit(cwd, ["tag", "--merged", to]);
|
|
1489
|
+
const tags = tagsRaw.split("\n").map((item) => item.trim()).filter(Boolean).filter((tag) => semverToTuple(tag) !== null);
|
|
1490
|
+
if (tags.length === 0) {
|
|
1491
|
+
return void 0;
|
|
1492
|
+
}
|
|
1493
|
+
tags.sort(compareSemverTags);
|
|
1494
|
+
return tags[tags.length - 1];
|
|
1495
|
+
}
|
|
1496
|
+
async function findDefaultFromCommit(cwd, toCommit) {
|
|
1497
|
+
const tag = await findLastReachableSemverTag(cwd, toCommit);
|
|
1498
|
+
if (tag) {
|
|
1499
|
+
return resolveCommit(cwd, tag);
|
|
1500
|
+
}
|
|
1501
|
+
const root = await runGit(cwd, ["rev-list", "--max-parents=0", toCommit]);
|
|
1502
|
+
const rootCommit = root.split("\n").map((item) => item.trim()).filter(Boolean)[0];
|
|
1503
|
+
if (!rootCommit) {
|
|
1504
|
+
throw new Error("Failed to resolve a baseline commit for changelog generation.");
|
|
1505
|
+
}
|
|
1506
|
+
return rootCommit;
|
|
1507
|
+
}
|
|
1508
|
+
async function resolveCommitWindow(cwd, from, to) {
|
|
1509
|
+
const toCommit = await resolveCommit(cwd, to ?? "HEAD");
|
|
1510
|
+
const fromCommit = from ? await resolveCommit(cwd, from) : await findDefaultFromCommit(cwd, toCommit);
|
|
1511
|
+
return { from: fromCommit, to: toCommit };
|
|
1512
|
+
}
|
|
1513
|
+
async function resolveCommitPath(cwd, fromCommit, toCommit) {
|
|
1514
|
+
if (fromCommit === toCommit) {
|
|
1515
|
+
return [toCommit];
|
|
1516
|
+
}
|
|
1517
|
+
const pathRaw = await runGit(cwd, [
|
|
1518
|
+
"rev-list",
|
|
1519
|
+
"--reverse",
|
|
1520
|
+
"--ancestry-path",
|
|
1521
|
+
`${fromCommit}..${toCommit}`
|
|
1522
|
+
]);
|
|
1523
|
+
const commits = pathRaw.split("\n").map((item) => item.trim()).filter(Boolean);
|
|
1524
|
+
return [fromCommit, ...commits];
|
|
1525
|
+
}
|
|
1526
|
+
function parentDirectories(relativePath) {
|
|
1527
|
+
const clean = normalizePath(relativePath).replace(/^\/+/, "");
|
|
1528
|
+
const dir = import_node_path4.default.posix.dirname(clean);
|
|
1529
|
+
if (!dir || dir === ".") return [];
|
|
1530
|
+
const parts = dir.split("/");
|
|
1531
|
+
const out = [];
|
|
1532
|
+
for (let i = parts.length; i > 0; i -= 1) {
|
|
1533
|
+
out.push(parts.slice(0, i).join("/"));
|
|
1534
|
+
}
|
|
1535
|
+
return out;
|
|
1536
|
+
}
|
|
1537
|
+
function buildRelevantConfigFiles(moduleRel, tsconfigRel) {
|
|
1538
|
+
const dirs = new Set(parentDirectories(moduleRel));
|
|
1539
|
+
if (tsconfigRel) {
|
|
1540
|
+
parentDirectories(tsconfigRel).forEach((item) => dirs.add(item));
|
|
1541
|
+
dirs.add(import_node_path4.default.posix.dirname(normalizePath(tsconfigRel)));
|
|
1542
|
+
}
|
|
1543
|
+
const files = /* @__PURE__ */ new Set(["pnpm-workspace.yaml"]);
|
|
1544
|
+
for (const dir of dirs) {
|
|
1545
|
+
if (!dir || dir === ".") continue;
|
|
1546
|
+
files.add(`${dir}/package.json`);
|
|
1547
|
+
files.add(`${dir}/tsconfig.json`);
|
|
1548
|
+
}
|
|
1549
|
+
return files;
|
|
1550
|
+
}
|
|
1551
|
+
async function commitTouchedRelevantPaths(cwd, commit, moduleRel, tsconfigRel) {
|
|
1552
|
+
const changed = await runGit(cwd, [
|
|
1553
|
+
"show",
|
|
1554
|
+
"--pretty=format:",
|
|
1555
|
+
"--name-only",
|
|
1556
|
+
"--diff-filter=ACMRTUXB",
|
|
1557
|
+
commit
|
|
1558
|
+
]);
|
|
1559
|
+
const changedFiles = changed.split("\n").map((item) => normalizePath(item.trim()).replace(/^\/+/, "")).filter(Boolean);
|
|
1560
|
+
const moduleFile = normalizePath(moduleRel).replace(/^\/+/, "");
|
|
1561
|
+
const moduleDir = normalizePath(import_node_path4.default.posix.dirname(moduleFile)).replace(/^\/+/, "");
|
|
1562
|
+
const tsconfigFile = tsconfigRel ? normalizePath(tsconfigRel).replace(/^\/+/, "") : void 0;
|
|
1563
|
+
const configFiles = buildRelevantConfigFiles(moduleFile, tsconfigFile);
|
|
1564
|
+
return changedFiles.some((file) => {
|
|
1565
|
+
if (file === moduleFile) return true;
|
|
1566
|
+
if (moduleDir && moduleDir !== "." && (file === moduleDir || file.startsWith(`${moduleDir}/`))) {
|
|
1567
|
+
return true;
|
|
1568
|
+
}
|
|
1569
|
+
if (tsconfigFile && file === tsconfigFile) return true;
|
|
1570
|
+
return configFiles.has(file);
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
async function filterCommits(cwd, commits, moduleRel, tsconfigRel) {
|
|
1574
|
+
if (commits.length <= 1) {
|
|
1575
|
+
return commits;
|
|
1576
|
+
}
|
|
1577
|
+
const keep = [commits[0]];
|
|
1578
|
+
for (let i = 1; i < commits.length; i += 1) {
|
|
1579
|
+
const commit = commits[i];
|
|
1580
|
+
if (await commitTouchedRelevantPaths(cwd, commit, moduleRel, tsconfigRel)) {
|
|
1581
|
+
keep.push(commit);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
const tip = commits[commits.length - 1];
|
|
1585
|
+
if (!keep.includes(tip)) {
|
|
1586
|
+
keep.push(tip);
|
|
1587
|
+
}
|
|
1588
|
+
return keep;
|
|
1589
|
+
}
|
|
1590
|
+
async function getCommitMetadata(cwd, commit) {
|
|
1591
|
+
const out = await runGit(cwd, ["show", "-s", "--format=%H%x1f%aI%x1f%s", commit]);
|
|
1592
|
+
const [sha, authorDate, subject] = out.split("");
|
|
1593
|
+
if (!sha || !authorDate) {
|
|
1594
|
+
throw new Error(`Unable to read commit metadata for ${commit}`);
|
|
1595
|
+
}
|
|
1596
|
+
return {
|
|
1597
|
+
sha,
|
|
1598
|
+
authorDate,
|
|
1599
|
+
subject: subject ?? ""
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
function buildSourceIdentity(section, value) {
|
|
1603
|
+
const sourceName = typeof value.sourceName === "string" ? value.sourceName : void 0;
|
|
1604
|
+
const tag = typeof value.tag === "string" ? value.tag : void 0;
|
|
1605
|
+
const file = typeof value.file === "string" ? value.file : void 0;
|
|
1606
|
+
const line = typeof value.line === "number" ? value.line : void 0;
|
|
1607
|
+
const column = typeof value.column === "number" ? value.column : void 0;
|
|
1608
|
+
const fallbackName = sourceName ?? tag ?? section;
|
|
1609
|
+
const suffix = file ? `${file}:${line ?? 1}:${column ?? 1}` : `${section}:unknown`;
|
|
1610
|
+
return {
|
|
1611
|
+
id: sourceName ?? `${fallbackName}@${suffix}`,
|
|
1612
|
+
displayName: sourceName ?? `${fallbackName} (${suffix})`,
|
|
1613
|
+
sourceName,
|
|
1614
|
+
tag,
|
|
1615
|
+
file,
|
|
1616
|
+
line,
|
|
1617
|
+
column
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
function asRecord(value) {
|
|
1621
|
+
return value && typeof value === "object" ? value : {};
|
|
1622
|
+
}
|
|
1623
|
+
function toRouteSnapshots(payload) {
|
|
1624
|
+
const routes = {};
|
|
1625
|
+
for (const leaf of payload.leaves) {
|
|
1626
|
+
const flatSchema = payload.schemaFlatByLeaf[leaf.key] ?? {};
|
|
1627
|
+
const cfg = toCfgSubset(asRecord(leaf.cfg));
|
|
1628
|
+
const sourceLeaf = payload.sourceByLeaf?.[leaf.key];
|
|
1629
|
+
const sourceSchemas = sourceLeaf?.schemas;
|
|
1630
|
+
const sourceBySection = {};
|
|
1631
|
+
for (const section of SCHEMA_SECTIONS) {
|
|
1632
|
+
const schemaKey = SOURCE_KEY_BY_SECTION[section];
|
|
1633
|
+
const sectionSource = sourceSchemas?.[schemaKey];
|
|
1634
|
+
if (!sectionSource || typeof sectionSource !== "object") continue;
|
|
1635
|
+
sourceBySection[section] = buildSourceIdentity(
|
|
1636
|
+
section,
|
|
1637
|
+
sectionSource
|
|
1638
|
+
);
|
|
1639
|
+
}
|
|
1640
|
+
routes[leaf.key] = {
|
|
1641
|
+
key: leaf.key,
|
|
1642
|
+
method: String(leaf.method).toUpperCase(),
|
|
1643
|
+
path: leaf.path,
|
|
1644
|
+
cfg,
|
|
1645
|
+
flatSchema,
|
|
1646
|
+
sourceBySection
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
return routes;
|
|
1650
|
+
}
|
|
1651
|
+
function sectionPathEntries(flatSchema, section) {
|
|
1652
|
+
return Object.entries(flatSchema).filter(
|
|
1653
|
+
([schemaPath]) => schemaPath === section || schemaPath.startsWith(`${section}.`) || schemaPath.startsWith(`${section}[]`)
|
|
1654
|
+
);
|
|
1655
|
+
}
|
|
1656
|
+
function toSourceObjectSnapshots(routesByKey) {
|
|
1657
|
+
const working = /* @__PURE__ */ new Map();
|
|
1658
|
+
for (const route of Object.values(routesByKey)) {
|
|
1659
|
+
for (const section of SCHEMA_SECTIONS) {
|
|
1660
|
+
const sourceIdentity = route.sourceBySection[section];
|
|
1661
|
+
if (!sourceIdentity) continue;
|
|
1662
|
+
const entries = sectionPathEntries(route.flatSchema, section);
|
|
1663
|
+
if (entries.length === 0) continue;
|
|
1664
|
+
const existing = working.get(sourceIdentity.id) ?? {
|
|
1665
|
+
id: sourceIdentity.id,
|
|
1666
|
+
displayName: sourceIdentity.displayName,
|
|
1667
|
+
routeKeys: /* @__PURE__ */ new Set(),
|
|
1668
|
+
sections: {
|
|
1669
|
+
params: /* @__PURE__ */ new Map(),
|
|
1670
|
+
query: /* @__PURE__ */ new Map(),
|
|
1671
|
+
body: /* @__PURE__ */ new Map(),
|
|
1672
|
+
output: /* @__PURE__ */ new Map()
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1675
|
+
existing.routeKeys.add(route.key);
|
|
1676
|
+
for (const [schemaPath, schemaEntry] of entries) {
|
|
1677
|
+
const sectionMap = existing.sections[section];
|
|
1678
|
+
const signatures = sectionMap.get(schemaPath) ?? /* @__PURE__ */ new Set();
|
|
1679
|
+
signatures.add(entrySignature(schemaEntry));
|
|
1680
|
+
sectionMap.set(schemaPath, signatures);
|
|
1681
|
+
}
|
|
1682
|
+
working.set(sourceIdentity.id, existing);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
const out = {};
|
|
1686
|
+
for (const [id, item] of working.entries()) {
|
|
1687
|
+
const sections = {
|
|
1688
|
+
params: {},
|
|
1689
|
+
query: {},
|
|
1690
|
+
body: {},
|
|
1691
|
+
output: {}
|
|
1692
|
+
};
|
|
1693
|
+
for (const section of SCHEMA_SECTIONS) {
|
|
1694
|
+
for (const [schemaPath, signatures] of item.sections[section].entries()) {
|
|
1695
|
+
sections[section][schemaPath] = Array.from(signatures).sort();
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
out[id] = {
|
|
1699
|
+
id,
|
|
1700
|
+
displayName: item.displayName,
|
|
1701
|
+
routeKeys: Array.from(item.routeKeys).sort(),
|
|
1702
|
+
sections
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1705
|
+
return out;
|
|
1706
|
+
}
|
|
1707
|
+
function diffRouteSchemas(before, after) {
|
|
1708
|
+
const paths = /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)]);
|
|
1709
|
+
const out = [];
|
|
1710
|
+
for (const schemaPath of Array.from(paths).sort()) {
|
|
1711
|
+
const prev = before[schemaPath];
|
|
1712
|
+
const next = after[schemaPath];
|
|
1713
|
+
if (!prev && next) {
|
|
1714
|
+
out.push({ path: schemaPath, op: "added", after: next });
|
|
1715
|
+
continue;
|
|
1716
|
+
}
|
|
1717
|
+
if (prev && !next) {
|
|
1718
|
+
out.push({ path: schemaPath, op: "removed", before: prev });
|
|
1719
|
+
continue;
|
|
1720
|
+
}
|
|
1721
|
+
if (!prev || !next) continue;
|
|
1722
|
+
if (entrySignature(prev) !== entrySignature(next)) {
|
|
1723
|
+
out.push({ path: schemaPath, op: "changed", before: prev, after: next });
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
return out;
|
|
1727
|
+
}
|
|
1728
|
+
function diffRouteCfg(before, after) {
|
|
1729
|
+
const out = [];
|
|
1730
|
+
for (const field of CHANGESET_CFG_FIELDS) {
|
|
1731
|
+
const prev = before[field];
|
|
1732
|
+
const next = after[field];
|
|
1733
|
+
if (stableStringify(prev) !== stableStringify(next)) {
|
|
1734
|
+
out.push({
|
|
1735
|
+
field,
|
|
1736
|
+
before: prev,
|
|
1737
|
+
after: next
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
return out;
|
|
1742
|
+
}
|
|
1743
|
+
function flattenSourceSections(snapshot) {
|
|
1744
|
+
const map = /* @__PURE__ */ new Map();
|
|
1745
|
+
for (const section of SCHEMA_SECTIONS) {
|
|
1746
|
+
const entries = snapshot.sections[section];
|
|
1747
|
+
for (const [schemaPath, signatures] of Object.entries(entries)) {
|
|
1748
|
+
map.set(`${section}:${schemaPath}`, signatures);
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
return map;
|
|
1752
|
+
}
|
|
1753
|
+
function diffSourceSchemas(before, after) {
|
|
1754
|
+
const prev = flattenSourceSections(before);
|
|
1755
|
+
const next = flattenSourceSections(after);
|
|
1756
|
+
const keys = /* @__PURE__ */ new Set([...prev.keys(), ...next.keys()]);
|
|
1757
|
+
const out = [];
|
|
1758
|
+
for (const key of Array.from(keys).sort()) {
|
|
1759
|
+
const prevSignatures = prev.get(key);
|
|
1760
|
+
const nextSignatures = next.get(key);
|
|
1761
|
+
const [section, ...rest] = key.split(":");
|
|
1762
|
+
const schemaPath = rest.join(":");
|
|
1763
|
+
if (!prevSignatures && nextSignatures) {
|
|
1764
|
+
out.push({
|
|
1765
|
+
section,
|
|
1766
|
+
path: schemaPath,
|
|
1767
|
+
op: "added",
|
|
1768
|
+
after: nextSignatures
|
|
1769
|
+
});
|
|
1770
|
+
continue;
|
|
1771
|
+
}
|
|
1772
|
+
if (prevSignatures && !nextSignatures) {
|
|
1773
|
+
out.push({
|
|
1774
|
+
section,
|
|
1775
|
+
path: schemaPath,
|
|
1776
|
+
op: "removed",
|
|
1777
|
+
before: prevSignatures
|
|
1778
|
+
});
|
|
1779
|
+
continue;
|
|
1780
|
+
}
|
|
1781
|
+
if (!prevSignatures || !nextSignatures) continue;
|
|
1782
|
+
if (stableStringify(prevSignatures) !== stableStringify(nextSignatures)) {
|
|
1783
|
+
out.push({
|
|
1784
|
+
section,
|
|
1785
|
+
path: schemaPath,
|
|
1786
|
+
op: "changed",
|
|
1787
|
+
before: prevSignatures,
|
|
1788
|
+
after: nextSignatures
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
return out;
|
|
1793
|
+
}
|
|
1794
|
+
function routeRecordByKey(events) {
|
|
1795
|
+
var _a;
|
|
1796
|
+
const out = {};
|
|
1797
|
+
for (const event of events) {
|
|
1798
|
+
out[_a = event.routeKey] ?? (out[_a] = []);
|
|
1799
|
+
out[event.routeKey].push(event);
|
|
1800
|
+
}
|
|
1801
|
+
return out;
|
|
1802
|
+
}
|
|
1803
|
+
function sourceRecordById(events) {
|
|
1804
|
+
var _a;
|
|
1805
|
+
const out = {};
|
|
1806
|
+
for (const event of events) {
|
|
1807
|
+
out[_a = event.sourceObjectId] ?? (out[_a] = []);
|
|
1808
|
+
out[event.sourceObjectId].push(event);
|
|
1809
|
+
}
|
|
1810
|
+
return out;
|
|
1811
|
+
}
|
|
1812
|
+
function buildRollups(routeEvents, sourceEvents, commits) {
|
|
1813
|
+
var _a, _b;
|
|
1814
|
+
const route = {
|
|
1815
|
+
route_added: 0,
|
|
1816
|
+
route_removed: 0,
|
|
1817
|
+
schema_changed: 0,
|
|
1818
|
+
cfg_changed: 0
|
|
1819
|
+
};
|
|
1820
|
+
const sourceObject = {
|
|
1821
|
+
source_object_added: 0,
|
|
1822
|
+
source_object_removed: 0,
|
|
1823
|
+
source_schema_changed: 0
|
|
1824
|
+
};
|
|
1825
|
+
for (const event of routeEvents) {
|
|
1826
|
+
route[event.type] += 1;
|
|
1827
|
+
}
|
|
1828
|
+
for (const event of sourceEvents) {
|
|
1829
|
+
sourceObject[event.type] += 1;
|
|
1830
|
+
}
|
|
1831
|
+
const byCommit = {};
|
|
1832
|
+
for (const commit of commits) {
|
|
1833
|
+
byCommit[commit.sha] = { routeEvents: 0, sourceEvents: 0 };
|
|
1834
|
+
}
|
|
1835
|
+
for (const event of routeEvents) {
|
|
1836
|
+
byCommit[_a = event.commitSha] ?? (byCommit[_a] = { routeEvents: 0, sourceEvents: 0 });
|
|
1837
|
+
byCommit[event.commitSha].routeEvents += 1;
|
|
1838
|
+
}
|
|
1839
|
+
for (const event of sourceEvents) {
|
|
1840
|
+
byCommit[_b = event.commitSha] ?? (byCommit[_b] = { routeEvents: 0, sourceEvents: 0 });
|
|
1841
|
+
byCommit[event.commitSha].sourceEvents += 1;
|
|
1842
|
+
}
|
|
1843
|
+
return {
|
|
1844
|
+
route,
|
|
1845
|
+
sourceObject,
|
|
1846
|
+
byCommit
|
|
1847
|
+
};
|
|
1848
|
+
}
|
|
1849
|
+
async function loadChangelogInput(modulePath, exportName) {
|
|
1850
|
+
const mod = await import(`${(0, import_node_url2.pathToFileURL)(modulePath).href}?v=${Date.now()}`);
|
|
1851
|
+
const value = mod[exportName] ?? (mod.default && mod.default[exportName]);
|
|
1852
|
+
if (!value) {
|
|
1853
|
+
throw new Error(`Export "${exportName}" not found in module: ${modulePath}`);
|
|
1854
|
+
}
|
|
1855
|
+
return value;
|
|
1856
|
+
}
|
|
1857
|
+
async function createSnapshot(repoRoot, moduleRel, exportName, commit, tempRoot, tsconfigRel, exportOptions) {
|
|
1858
|
+
const worktreeDir = import_node_path4.default.join(tempRoot, `wt-${commit.sha.slice(0, 12)}`);
|
|
1859
|
+
await runGit(repoRoot, ["worktree", "add", "--detach", worktreeDir, commit.sha]);
|
|
1860
|
+
try {
|
|
1861
|
+
const modulePath = import_node_path4.default.resolve(worktreeDir, moduleRel);
|
|
1862
|
+
const tsconfigPath = tsconfigRel ? import_node_path4.default.resolve(worktreeDir, tsconfigRel) : void 0;
|
|
1863
|
+
const input = await loadChangelogInput(modulePath, exportName);
|
|
1864
|
+
const payload = await exportFinalizedLeaves(input, {
|
|
1865
|
+
includeSource: true,
|
|
1866
|
+
sourceModulePath: modulePath,
|
|
1867
|
+
sourceExportName: exportName,
|
|
1868
|
+
tsconfigPath,
|
|
1869
|
+
...exportOptions
|
|
1870
|
+
});
|
|
1871
|
+
const routesByKey = toRouteSnapshots(payload);
|
|
1872
|
+
const sourceObjectsById = toSourceObjectSnapshots(routesByKey);
|
|
1873
|
+
return {
|
|
1874
|
+
commit,
|
|
1875
|
+
routesByKey,
|
|
1876
|
+
sourceObjectsById
|
|
1877
|
+
};
|
|
1878
|
+
} finally {
|
|
1879
|
+
await runGit(repoRoot, ["worktree", "remove", "--force", worktreeDir]).catch(() => void 0);
|
|
1880
|
+
await import_promises2.default.rm(worktreeDir, { recursive: true, force: true }).catch(() => void 0);
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
function diffSnapshots(previous, current) {
|
|
1884
|
+
const routeEvents = [];
|
|
1885
|
+
const sourceEvents = [];
|
|
1886
|
+
const routeKeys = /* @__PURE__ */ new Set([
|
|
1887
|
+
...Object.keys(previous.routesByKey),
|
|
1888
|
+
...Object.keys(current.routesByKey)
|
|
1889
|
+
]);
|
|
1890
|
+
for (const routeKey of Array.from(routeKeys).sort()) {
|
|
1891
|
+
const prevRoute = previous.routesByKey[routeKey];
|
|
1892
|
+
const nextRoute = current.routesByKey[routeKey];
|
|
1893
|
+
if (!prevRoute && nextRoute) {
|
|
1894
|
+
routeEvents.push({
|
|
1895
|
+
type: "route_added",
|
|
1896
|
+
commitSha: current.commit.sha,
|
|
1897
|
+
routeKey,
|
|
1898
|
+
method: nextRoute.method,
|
|
1899
|
+
path: nextRoute.path
|
|
1900
|
+
});
|
|
1901
|
+
continue;
|
|
1902
|
+
}
|
|
1903
|
+
if (prevRoute && !nextRoute) {
|
|
1904
|
+
routeEvents.push({
|
|
1905
|
+
type: "route_removed",
|
|
1906
|
+
commitSha: current.commit.sha,
|
|
1907
|
+
routeKey,
|
|
1908
|
+
method: prevRoute.method,
|
|
1909
|
+
path: prevRoute.path
|
|
1910
|
+
});
|
|
1911
|
+
continue;
|
|
1912
|
+
}
|
|
1913
|
+
if (!prevRoute || !nextRoute) continue;
|
|
1914
|
+
const schemaDiff = diffRouteSchemas(prevRoute.flatSchema, nextRoute.flatSchema);
|
|
1915
|
+
if (schemaDiff.length > 0) {
|
|
1916
|
+
routeEvents.push({
|
|
1917
|
+
type: "schema_changed",
|
|
1918
|
+
commitSha: current.commit.sha,
|
|
1919
|
+
routeKey,
|
|
1920
|
+
method: nextRoute.method,
|
|
1921
|
+
path: nextRoute.path,
|
|
1922
|
+
schemaDiff
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
const cfgDiff = diffRouteCfg(prevRoute.cfg, nextRoute.cfg);
|
|
1926
|
+
if (cfgDiff.length > 0) {
|
|
1927
|
+
routeEvents.push({
|
|
1928
|
+
type: "cfg_changed",
|
|
1929
|
+
commitSha: current.commit.sha,
|
|
1930
|
+
routeKey,
|
|
1931
|
+
method: nextRoute.method,
|
|
1932
|
+
path: nextRoute.path,
|
|
1933
|
+
cfgDiff
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
const sourceKeys = /* @__PURE__ */ new Set([
|
|
1938
|
+
...Object.keys(previous.sourceObjectsById),
|
|
1939
|
+
...Object.keys(current.sourceObjectsById)
|
|
1940
|
+
]);
|
|
1941
|
+
for (const sourceObjectId of Array.from(sourceKeys).sort()) {
|
|
1942
|
+
const prevSource = previous.sourceObjectsById[sourceObjectId];
|
|
1943
|
+
const nextSource = current.sourceObjectsById[sourceObjectId];
|
|
1944
|
+
if (!prevSource && nextSource) {
|
|
1945
|
+
sourceEvents.push({
|
|
1946
|
+
type: "source_object_added",
|
|
1947
|
+
commitSha: current.commit.sha,
|
|
1948
|
+
sourceObjectId,
|
|
1949
|
+
displayName: nextSource.displayName,
|
|
1950
|
+
impactedRoutes: nextSource.routeKeys
|
|
1951
|
+
});
|
|
1952
|
+
continue;
|
|
1953
|
+
}
|
|
1954
|
+
if (prevSource && !nextSource) {
|
|
1955
|
+
sourceEvents.push({
|
|
1956
|
+
type: "source_object_removed",
|
|
1957
|
+
commitSha: current.commit.sha,
|
|
1958
|
+
sourceObjectId,
|
|
1959
|
+
displayName: prevSource.displayName,
|
|
1960
|
+
impactedRoutes: prevSource.routeKeys
|
|
1961
|
+
});
|
|
1962
|
+
continue;
|
|
1963
|
+
}
|
|
1964
|
+
if (!prevSource || !nextSource) continue;
|
|
1965
|
+
const schemaDiff = diffSourceSchemas(prevSource, nextSource);
|
|
1966
|
+
const prevRoutes = new Set(prevSource.routeKeys);
|
|
1967
|
+
const nextRoutes = new Set(nextSource.routeKeys);
|
|
1968
|
+
const routeAdded = Array.from(nextRoutes).filter((routeKey) => !prevRoutes.has(routeKey));
|
|
1969
|
+
const routeRemoved = Array.from(prevRoutes).filter((routeKey) => !nextRoutes.has(routeKey));
|
|
1970
|
+
if (schemaDiff.length > 0 || routeAdded.length > 0 || routeRemoved.length > 0) {
|
|
1971
|
+
sourceEvents.push({
|
|
1972
|
+
type: "source_schema_changed",
|
|
1973
|
+
commitSha: current.commit.sha,
|
|
1974
|
+
sourceObjectId,
|
|
1975
|
+
displayName: nextSource.displayName,
|
|
1976
|
+
impactedRoutes: nextSource.routeKeys,
|
|
1977
|
+
schemaDiff: schemaDiff.length > 0 ? schemaDiff : void 0,
|
|
1978
|
+
routeAdded: routeAdded.length > 0 ? routeAdded : void 0,
|
|
1979
|
+
routeRemoved: routeRemoved.length > 0 ? routeRemoved : void 0
|
|
1980
|
+
});
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
return {
|
|
1984
|
+
routeEvents,
|
|
1985
|
+
sourceEvents
|
|
1986
|
+
};
|
|
1987
|
+
}
|
|
1988
|
+
var BAKED_PAYLOAD_MARKER2 = "<!--__FINALIZED_LEAVES_BAKED_PAYLOAD__-->";
|
|
1989
|
+
function escapePayloadForInlineScript2(payload) {
|
|
1990
|
+
return JSON.stringify(payload).replace(/<\//g, "<\\/").replace(/<!--/g, "<\\!--");
|
|
1991
|
+
}
|
|
1992
|
+
function injectPayloadIntoViewerHtml2(htmlTemplate, payload) {
|
|
1993
|
+
const payloadScript = `${BAKED_PAYLOAD_MARKER2}
|
|
1994
|
+
<script id="finalized-leaves-baked-payload">window.__FINALIZED_LEAVES_PAYLOAD = ${escapePayloadForInlineScript2(
|
|
1995
|
+
payload
|
|
1996
|
+
)};</script>`;
|
|
1997
|
+
if (htmlTemplate.includes(BAKED_PAYLOAD_MARKER2)) {
|
|
1998
|
+
return htmlTemplate.replace(BAKED_PAYLOAD_MARKER2, payloadScript);
|
|
1999
|
+
}
|
|
2000
|
+
if (htmlTemplate.includes("</body>")) {
|
|
2001
|
+
return htmlTemplate.replace("</body>", `${payloadScript}
|
|
2002
|
+
</body>`);
|
|
2003
|
+
}
|
|
2004
|
+
return `${payloadScript}
|
|
2005
|
+
${htmlTemplate}`;
|
|
2006
|
+
}
|
|
2007
|
+
async function resolveViewerTemplatePath2(viewerTemplateFile) {
|
|
2008
|
+
if (viewerTemplateFile) {
|
|
2009
|
+
const resolved = import_node_path4.default.resolve(viewerTemplateFile);
|
|
2010
|
+
await import_promises2.default.access(resolved);
|
|
2011
|
+
return resolved;
|
|
2012
|
+
}
|
|
2013
|
+
const candidates = [
|
|
2014
|
+
import_node_path4.default.resolve(
|
|
2015
|
+
process.cwd(),
|
|
2016
|
+
"node_modules/@emeryld/rrroutes-export/tools/finalized-leaves-viewer.html"
|
|
2017
|
+
),
|
|
2018
|
+
import_node_path4.default.resolve(process.cwd(), "tools/finalized-leaves-viewer.html"),
|
|
2019
|
+
import_node_path4.default.resolve(process.cwd(), "packages/export/tools/finalized-leaves-viewer.html")
|
|
2020
|
+
];
|
|
2021
|
+
for (const candidate of candidates) {
|
|
2022
|
+
try {
|
|
2023
|
+
await import_promises2.default.access(candidate);
|
|
2024
|
+
return candidate;
|
|
2025
|
+
} catch {
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
return void 0;
|
|
2029
|
+
}
|
|
2030
|
+
async function writeJsonExport2(payload, outFile) {
|
|
2031
|
+
const resolved = import_node_path4.default.resolve(outFile);
|
|
2032
|
+
await import_promises2.default.mkdir(import_node_path4.default.dirname(resolved), { recursive: true });
|
|
2033
|
+
await import_promises2.default.writeFile(resolved, `${JSON.stringify(payload, null, 2)}
|
|
2034
|
+
`, "utf8");
|
|
2035
|
+
return resolved;
|
|
2036
|
+
}
|
|
2037
|
+
async function writeBakedHtmlExport2(payload, htmlFile, viewerTemplateFile) {
|
|
2038
|
+
const templatePath = await resolveViewerTemplatePath2(viewerTemplateFile);
|
|
2039
|
+
const template = templatePath ? await import_promises2.default.readFile(templatePath, "utf8") : DEFAULT_VIEWER_TEMPLATE;
|
|
2040
|
+
const baked = injectPayloadIntoViewerHtml2(template, payload);
|
|
2041
|
+
const resolved = import_node_path4.default.resolve(htmlFile);
|
|
2042
|
+
await import_promises2.default.mkdir(import_node_path4.default.dirname(resolved), { recursive: true });
|
|
2043
|
+
await import_promises2.default.writeFile(resolved, baked, "utf8");
|
|
2044
|
+
return resolved;
|
|
2045
|
+
}
|
|
2046
|
+
async function openHtmlInBrowser2(filePath) {
|
|
2047
|
+
const resolved = import_node_path4.default.resolve(filePath);
|
|
2048
|
+
const platform = process.platform;
|
|
2049
|
+
if (platform === "darwin") {
|
|
2050
|
+
(0, import_node_child_process2.spawn)("open", [resolved], { detached: true, stdio: "ignore" }).unref();
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
if (platform === "win32") {
|
|
2054
|
+
(0, import_node_child_process2.spawn)("cmd", ["/c", "start", "", resolved], {
|
|
2055
|
+
detached: true,
|
|
2056
|
+
stdio: "ignore"
|
|
2057
|
+
}).unref();
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
2060
|
+
(0, import_node_child_process2.spawn)("xdg-open", [resolved], { detached: true, stdio: "ignore" }).unref();
|
|
2061
|
+
}
|
|
2062
|
+
async function writeFinalizedLeavesChangelogExport(payload, outFileOrOptions) {
|
|
2063
|
+
const options = typeof outFileOrOptions === "string" ? { outFile: outFileOrOptions } : outFileOrOptions;
|
|
2064
|
+
const written = {};
|
|
2065
|
+
if (options.outFile) {
|
|
2066
|
+
written.outFile = await writeJsonExport2(payload, options.outFile);
|
|
2067
|
+
}
|
|
2068
|
+
if (options.htmlFile) {
|
|
2069
|
+
written.htmlFile = await writeBakedHtmlExport2(
|
|
2070
|
+
payload,
|
|
2071
|
+
options.htmlFile,
|
|
2072
|
+
options.viewerTemplateFile
|
|
2073
|
+
);
|
|
2074
|
+
}
|
|
2075
|
+
if (options.openOnFinish) {
|
|
2076
|
+
if (!written.htmlFile) {
|
|
2077
|
+
throw new Error(
|
|
2078
|
+
"openOnFinish requires htmlFile. Provide htmlFile to open the baked viewer."
|
|
2079
|
+
);
|
|
2080
|
+
}
|
|
2081
|
+
await openHtmlInBrowser2(written.htmlFile);
|
|
2082
|
+
}
|
|
2083
|
+
return written;
|
|
2084
|
+
}
|
|
2085
|
+
async function exportFinalizedLeavesChangelog(options) {
|
|
2086
|
+
const cwd = process.cwd();
|
|
2087
|
+
const repoRoot = await runGit(cwd, ["rev-parse", "--show-toplevel"]);
|
|
2088
|
+
const modulePathAbsolute = import_node_path4.default.resolve(cwd, options.modulePath);
|
|
2089
|
+
const modulePathRelative = normalizePath(import_node_path4.default.relative(repoRoot, modulePathAbsolute));
|
|
2090
|
+
if (modulePathRelative.startsWith("..")) {
|
|
2091
|
+
throw new Error("modulePath must resolve inside the git repository root.");
|
|
2092
|
+
}
|
|
2093
|
+
const tsconfigAbsolute = options.tsconfigPath ? import_node_path4.default.resolve(cwd, options.tsconfigPath) : void 0;
|
|
2094
|
+
const tsconfigRelative = tsconfigAbsolute ? normalizePath(import_node_path4.default.relative(repoRoot, tsconfigAbsolute)) : void 0;
|
|
2095
|
+
if (tsconfigRelative && tsconfigRelative.startsWith("..")) {
|
|
2096
|
+
throw new Error("tsconfigPath must resolve inside the git repository root.");
|
|
2097
|
+
}
|
|
2098
|
+
const exportName = options.exportName ?? "leaves";
|
|
2099
|
+
const window = await resolveCommitWindow(repoRoot, options.from, options.to);
|
|
2100
|
+
const allCommits = await resolveCommitPath(repoRoot, window.from, window.to);
|
|
2101
|
+
const selectedCommitShas = await filterCommits(
|
|
2102
|
+
repoRoot,
|
|
2103
|
+
allCommits,
|
|
2104
|
+
modulePathRelative,
|
|
2105
|
+
tsconfigRelative
|
|
2106
|
+
);
|
|
2107
|
+
if (selectedCommitShas.length === 0) {
|
|
2108
|
+
selectedCommitShas.push(window.to);
|
|
2109
|
+
}
|
|
2110
|
+
const commits = [];
|
|
2111
|
+
for (const commit of selectedCommitShas) {
|
|
2112
|
+
commits.push(await getCommitMetadata(repoRoot, commit));
|
|
2113
|
+
}
|
|
2114
|
+
const tempRoot = await import_promises2.default.mkdtemp(import_node_path4.default.join(import_node_os.default.tmpdir(), "rrroutes-export-changelog-"));
|
|
2115
|
+
try {
|
|
2116
|
+
const snapshots = [];
|
|
2117
|
+
for (const commit of commits) {
|
|
2118
|
+
snapshots.push(
|
|
2119
|
+
await createSnapshot(
|
|
2120
|
+
repoRoot,
|
|
2121
|
+
modulePathRelative,
|
|
2122
|
+
exportName,
|
|
2123
|
+
commit,
|
|
2124
|
+
tempRoot,
|
|
2125
|
+
tsconfigRelative,
|
|
2126
|
+
options
|
|
2127
|
+
)
|
|
2128
|
+
);
|
|
2129
|
+
}
|
|
2130
|
+
const routeEvents = [];
|
|
2131
|
+
const sourceEvents = [];
|
|
2132
|
+
for (let i = 1; i < snapshots.length; i += 1) {
|
|
2133
|
+
const { routeEvents: nextRouteEvents, sourceEvents: nextSourceEvents } = diffSnapshots(
|
|
2134
|
+
snapshots[i - 1],
|
|
2135
|
+
snapshots[i]
|
|
2136
|
+
);
|
|
2137
|
+
routeEvents.push(...nextRouteEvents);
|
|
2138
|
+
sourceEvents.push(...nextSourceEvents);
|
|
2139
|
+
}
|
|
2140
|
+
const payload = {
|
|
2141
|
+
kind: "finalized-leaves-changelog",
|
|
2142
|
+
_meta: {
|
|
2143
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2144
|
+
description: "Git-history changelog for RRRoutes finalized leaves with route-level and source-object schema diffs.",
|
|
2145
|
+
modulePath: options.modulePath,
|
|
2146
|
+
exportName,
|
|
2147
|
+
from: window.from,
|
|
2148
|
+
to: window.to,
|
|
2149
|
+
selectedCommitCount: commits.length
|
|
2150
|
+
},
|
|
2151
|
+
commits,
|
|
2152
|
+
byRoute: routeRecordByKey(routeEvents),
|
|
2153
|
+
bySourceObject: sourceRecordById(sourceEvents),
|
|
2154
|
+
rollups: buildRollups(routeEvents, sourceEvents, commits)
|
|
2155
|
+
};
|
|
2156
|
+
if (options.outFile || options.htmlFile) {
|
|
2157
|
+
await writeFinalizedLeavesChangelogExport(payload, {
|
|
2158
|
+
outFile: options.outFile,
|
|
2159
|
+
htmlFile: options.htmlFile,
|
|
2160
|
+
viewerTemplateFile: options.viewerTemplateFile,
|
|
2161
|
+
openOnFinish: options.openOnFinish
|
|
2162
|
+
});
|
|
2163
|
+
}
|
|
2164
|
+
return payload;
|
|
2165
|
+
} finally {
|
|
2166
|
+
await import_promises2.default.rm(tempRoot, { recursive: true, force: true }).catch(() => void 0);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
var __private = {
|
|
2170
|
+
resolveCommitWindow,
|
|
2171
|
+
resolveCommitPath,
|
|
2172
|
+
filterCommits,
|
|
2173
|
+
toRouteSnapshots,
|
|
2174
|
+
toSourceObjectSnapshots,
|
|
2175
|
+
diffSnapshots,
|
|
2176
|
+
buildSourceIdentity
|
|
2177
|
+
};
|
|
2178
|
+
|
|
2179
|
+
// src/exportFinalizedLeaves.changelog.cli.ts
|
|
2180
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
2181
|
+
function parseFinalizedLeavesChangelogCliArgs(argv) {
|
|
2182
|
+
const args = /* @__PURE__ */ new Map();
|
|
2183
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
2184
|
+
const key = argv[i];
|
|
2185
|
+
if (key === "--") continue;
|
|
2186
|
+
if (!key.startsWith("--")) continue;
|
|
2187
|
+
if (key === "--with-source") {
|
|
2188
|
+
continue;
|
|
2189
|
+
}
|
|
2190
|
+
const value = argv[i + 1];
|
|
2191
|
+
if (!value || value.startsWith("--")) {
|
|
2192
|
+
throw new Error(`Missing value for ${key}`);
|
|
2193
|
+
}
|
|
2194
|
+
args.set(key, value);
|
|
2195
|
+
i += 1;
|
|
2196
|
+
}
|
|
2197
|
+
const modulePath = args.get("--module");
|
|
2198
|
+
if (!modulePath) {
|
|
2199
|
+
throw new Error("Missing required --module argument");
|
|
2200
|
+
}
|
|
2201
|
+
return {
|
|
2202
|
+
modulePath,
|
|
2203
|
+
exportName: args.get("--export") ?? "leaves",
|
|
2204
|
+
outFile: args.get("--out") ?? "finalized-leaves.changelog.json",
|
|
2205
|
+
htmlFile: args.get("--html") ?? void 0,
|
|
2206
|
+
tsconfigPath: args.get("--tsconfig") ?? void 0,
|
|
2207
|
+
from: args.get("--from") ?? void 0,
|
|
2208
|
+
to: args.get("--to") ?? void 0
|
|
2209
|
+
};
|
|
2210
|
+
}
|
|
2211
|
+
async function runExportFinalizedLeavesChangelogCli(argv) {
|
|
2212
|
+
const args = parseFinalizedLeavesChangelogCliArgs(argv);
|
|
2213
|
+
const options = {
|
|
2214
|
+
modulePath: args.modulePath,
|
|
2215
|
+
exportName: args.exportName,
|
|
2216
|
+
outFile: args.outFile,
|
|
2217
|
+
htmlFile: args.htmlFile,
|
|
2218
|
+
tsconfigPath: args.tsconfigPath,
|
|
2219
|
+
from: args.from,
|
|
2220
|
+
to: args.to,
|
|
2221
|
+
includeSource: true
|
|
2222
|
+
};
|
|
2223
|
+
const payload = await exportFinalizedLeavesChangelog(options);
|
|
2224
|
+
return {
|
|
2225
|
+
payload,
|
|
2226
|
+
outFile: import_node_path5.default.resolve(args.outFile),
|
|
2227
|
+
htmlFile: args.htmlFile ? import_node_path5.default.resolve(args.htmlFile) : void 0
|
|
2228
|
+
};
|
|
2229
|
+
}
|
|
1367
2230
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1368
2231
|
0 && (module.exports = {
|
|
1369
2232
|
DEFAULT_VIEWER_TEMPLATE,
|
|
2233
|
+
__private,
|
|
1370
2234
|
clearSchemaIntrospectionHandlers,
|
|
1371
2235
|
createSchemaIntrospector,
|
|
1372
2236
|
exportFinalizedLeaves,
|
|
2237
|
+
exportFinalizedLeavesChangelog,
|
|
1373
2238
|
extractLeafSourceByAst,
|
|
1374
2239
|
flattenLeafSchemas,
|
|
1375
2240
|
flattenSerializableSchema,
|
|
1376
2241
|
introspectSchema,
|
|
1377
2242
|
loadFinalizedLeavesInput,
|
|
2243
|
+
parseFinalizedLeavesChangelogCliArgs,
|
|
1378
2244
|
parseFinalizedLeavesCliArgs,
|
|
1379
2245
|
registerSchemaIntrospectionHandler,
|
|
2246
|
+
runExportFinalizedLeavesChangelogCli,
|
|
1380
2247
|
runExportFinalizedLeavesCli,
|
|
1381
2248
|
serializableSchemaKinds,
|
|
1382
2249
|
serializeLeafContract,
|
|
1383
2250
|
serializeLeavesContract,
|
|
2251
|
+
writeFinalizedLeavesChangelogExport,
|
|
1384
2252
|
writeFinalizedLeavesExport
|
|
1385
2253
|
});
|
|
1386
2254
|
//# sourceMappingURL=index.cjs.map
|