@uwmd/core 1.3.0 → 1.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/dist/browser.d.ts +10 -4
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +5 -2
- package/dist/browser.js.map +1 -1
- package/dist/cascade.d.ts +60 -6
- package/dist/cascade.d.ts.map +1 -1
- package/dist/cascade.js +93 -14
- package/dist/cascade.js.map +1 -1
- package/dist/cli.js +269 -11
- package/dist/cli.js.map +1 -1
- package/dist/composition.d.ts +182 -0
- package/dist/composition.d.ts.map +1 -0
- package/dist/composition.js +536 -0
- package/dist/composition.js.map +1 -0
- package/dist/context.js +23 -23
- package/dist/index.d.ts +10 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/lite-bridge.d.ts +9 -0
- package/dist/lite-bridge.d.ts.map +1 -1
- package/dist/lite-bridge.js +44 -5
- package/dist/lite-bridge.js.map +1 -1
- package/dist/lite.js +37 -1
- package/dist/lite.js.map +1 -1
- package/dist/market-data.d.ts +134 -0
- package/dist/market-data.d.ts.map +1 -0
- package/dist/market-data.js +292 -0
- package/dist/market-data.js.map +1 -0
- package/dist/protocol.d.ts +57 -8
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +74 -4
- package/dist/protocol.js.map +1 -1
- package/dist/receipts.d.ts +189 -9
- package/dist/receipts.d.ts.map +1 -1
- package/dist/receipts.js +373 -14
- package/dist/receipts.js.map +1 -1
- package/dist/report.js +165 -165
- package/dist/types.d.ts +54 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/validator.d.ts.map +1 -1
- package/dist/validator.js +17 -0
- package/dist/validator.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// uwmd CLI — command-line interface for .uw.md files
|
|
3
3
|
// Commands: parse, validate, compact, diff, init, summary, render
|
|
4
4
|
// Usage: uwmd <command> <file> [options]
|
|
5
|
-
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
6
|
-
import { resolve, basename } from 'node:path';
|
|
5
|
+
import { readFileSync, writeFileSync, existsSync, readdirSync, mkdirSync } from 'node:fs';
|
|
6
|
+
import { resolve, basename, dirname } from 'node:path';
|
|
7
7
|
import { parseUWFile } from './parser.js';
|
|
8
8
|
import { validateUWFile } from './validator.js';
|
|
9
9
|
import { compact, diff } from './compactor.js';
|
|
@@ -24,6 +24,8 @@ import { runBancroftAgent } from './agents/bancroft.js';
|
|
|
24
24
|
import { buildContext } from './context-profiles.js';
|
|
25
25
|
import { rankGaps } from './refinement.js';
|
|
26
26
|
import { resolveValue } from './cascade.js';
|
|
27
|
+
import { parseUWPart, resolveComposition, externalizeSection, stringifyUWPart, UWPART_EXTENSION, } from './composition.js';
|
|
28
|
+
import { createDocumentMarketData, parseMarketDataDocument } from './market-data.js';
|
|
27
29
|
import { getAssetClassDefaults } from './defaults.js';
|
|
28
30
|
import { MULTIFAMILY_PACK, getPackForAssetClass } from './packs/index.js';
|
|
29
31
|
import { issueReceipt, verifyReceipt, assertUWReceipt } from './receipts.js';
|
|
@@ -171,7 +173,9 @@ function cmdValidate(file, flags) {
|
|
|
171
173
|
}
|
|
172
174
|
async function cmdVerify(file, flags) {
|
|
173
175
|
const content = readFile(file);
|
|
174
|
-
|
|
176
|
+
// --resolved verifies the assembled record. Without it an externalized
|
|
177
|
+
// section verifies as a directive, which is not the document anyone means.
|
|
178
|
+
const parsed = withResolved(parseUWFile(content), file, flags);
|
|
175
179
|
// No flag set → run all three.
|
|
176
180
|
const onlyValidate = flags['validate'] === true;
|
|
177
181
|
const onlyIntegrity = flags['integrity'] === true;
|
|
@@ -329,7 +333,11 @@ function cmdSummary(file) {
|
|
|
329
333
|
}
|
|
330
334
|
async function cmdExport(file, flags) {
|
|
331
335
|
const includeSuperseded = !(flags['no-superseded'] === true || flags['compact'] === true);
|
|
332
|
-
|
|
336
|
+
// --resolved exports the assembled record. Exporting the directive instead
|
|
337
|
+
// would ship a document whose rent roll is a list of filenames.
|
|
338
|
+
const loaded = flags['resolved']
|
|
339
|
+
? toUWEnvelope(withResolved(parseUWFile(readFile(file)), file, flags))
|
|
340
|
+
: await loadEnvelope(file);
|
|
333
341
|
const envelope = includeSuperseded ? loaded : { ...loaded, superseded: {} };
|
|
334
342
|
const text = stringifyUWEnvelope(await stampEnvelopeDigest(envelope));
|
|
335
343
|
// Default output path: swap the .uw.md suffix for .uw.json (fall back to appending).
|
|
@@ -419,9 +427,15 @@ async function cmdConvert(file, flags) {
|
|
|
419
427
|
const projection = projectUWEnvelopeToLite(envelope);
|
|
420
428
|
encoded = projection.content;
|
|
421
429
|
extension = '.uw.md';
|
|
422
|
-
|
|
430
|
+
// Two independent losses, reported separately. A record whose only loss is
|
|
431
|
+
// an externalized section omits zero *paths*, so folding these together
|
|
432
|
+
// would print "omitted 0 advanced path(s)" over a missing rent roll.
|
|
433
|
+
if (projection.report.omitted_paths.length > 0) {
|
|
423
434
|
console.warn(`Warning: Lite projection omitted ${projection.report.omitted_paths.length} advanced path(s).`);
|
|
424
435
|
}
|
|
436
|
+
if (projection.report.externalized_sections.length > 0) {
|
|
437
|
+
console.warn(`Warning: Lite projection omitted ${projection.report.externalized_sections.length} externalized section(s), unresolved here: ${projection.report.externalized_sections.join(', ')}.`);
|
|
438
|
+
}
|
|
425
439
|
if (typeof flags['projection-report'] === 'string') {
|
|
426
440
|
writeFileSync(resolve(flags['projection-report']), JSON.stringify(projection.report, null, 2), 'utf-8');
|
|
427
441
|
}
|
|
@@ -644,6 +658,65 @@ function cmdReport(file, flags) {
|
|
|
644
658
|
console.log(`Rendered ${label} → ${basename(outPath)} (${result.sectionsRendered.length} sections${result.sectionsSkipped.length ? `, skipped: ${result.sectionsSkipped.join(', ')}` : ''})`);
|
|
645
659
|
console.log('For PDF output, use @uwmd/report (uwmd-report) or print this HTML from a browser.');
|
|
646
660
|
}
|
|
661
|
+
/**
|
|
662
|
+
* Load `--market-data <file>` into a cascade-ready lookup, or return null when
|
|
663
|
+
* the flag is absent. Exits with a message rather than throwing, so a bad
|
|
664
|
+
* observation set is a usage error rather than a stack trace.
|
|
665
|
+
*/
|
|
666
|
+
function loadMarketData(flags) {
|
|
667
|
+
const path = flags['market-data'];
|
|
668
|
+
if (!path)
|
|
669
|
+
return null;
|
|
670
|
+
try {
|
|
671
|
+
const doc = parseMarketDataDocument(parseUWFile(readFile(path)));
|
|
672
|
+
return {
|
|
673
|
+
lookup: createDocumentMarketData(doc),
|
|
674
|
+
identity: {
|
|
675
|
+
document_id: doc.document_id,
|
|
676
|
+
as_of: doc.as_of,
|
|
677
|
+
provider: doc.provider,
|
|
678
|
+
geo: doc.geo,
|
|
679
|
+
},
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
catch (e) {
|
|
683
|
+
console.error(`Market data: ${e instanceof Error ? e.message : String(e)}`);
|
|
684
|
+
process.exit(1);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
function cmdMarketDataValidate(file, flags) {
|
|
688
|
+
const parsed = parseUWFile(readFile(file));
|
|
689
|
+
try {
|
|
690
|
+
const doc = parseMarketDataDocument(parsed);
|
|
691
|
+
if (flags['json']) {
|
|
692
|
+
process.stdout.write(`${JSON.stringify({ valid: true, ...doc }, null, 2)}\n`);
|
|
693
|
+
}
|
|
694
|
+
else {
|
|
695
|
+
console.log(`OK ${doc.document_id}`);
|
|
696
|
+
console.log(` as of ${doc.as_of} — ${doc.provider}`);
|
|
697
|
+
console.log(` ${doc.geo}${doc.asset_class ? ` — ${doc.asset_class}` : ''}`);
|
|
698
|
+
console.log(` ${doc.observations.length} observation(s):`);
|
|
699
|
+
for (const o of doc.observations) {
|
|
700
|
+
console.log(` ${o.field_path} = ${String(o.value)} ${o.unit}${o.confidence ? ` (${o.confidence})` : ''}`);
|
|
701
|
+
console.log(` basis: ${o.basis}`);
|
|
702
|
+
}
|
|
703
|
+
// Restated on every run because it is the thing people forget.
|
|
704
|
+
console.log('\n Attributable, not verified: this says the observations');
|
|
705
|
+
console.log(' are traceable, not that they are accurate or current.');
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
catch (e) {
|
|
709
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
710
|
+
if (flags['json']) {
|
|
711
|
+
process.stdout.write(`${JSON.stringify({ valid: false, error: message }, null, 2)}\n`);
|
|
712
|
+
}
|
|
713
|
+
else {
|
|
714
|
+
console.error(`INVALID ${file}`);
|
|
715
|
+
console.error(` ${message}`);
|
|
716
|
+
}
|
|
717
|
+
process.exit(1);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
647
720
|
function cmdScope(file, flags) {
|
|
648
721
|
const content = readFile(file);
|
|
649
722
|
const parsed = parseUWFile(content);
|
|
@@ -655,12 +728,19 @@ function cmdScope(file, flags) {
|
|
|
655
728
|
console.error(`No published asset-class default table for '${assetClass}'.`);
|
|
656
729
|
process.exit(1);
|
|
657
730
|
}
|
|
731
|
+
const market = loadMarketData(flags);
|
|
658
732
|
const out = {};
|
|
659
733
|
for (const path of Object.keys(table.fields)) {
|
|
660
|
-
const r = resolveValue(path, parsed
|
|
734
|
+
const r = resolveValue(path, parsed, {
|
|
735
|
+
asset_class: assetClass,
|
|
736
|
+
...(market ? { market: market.lookup } : {}),
|
|
737
|
+
});
|
|
661
738
|
out[path] = {
|
|
662
739
|
value: r.value,
|
|
663
740
|
step: r.step,
|
|
741
|
+
// Surfaced so a promoted value stays distinguishable in the output: it
|
|
742
|
+
// resolves at the `user_input` step but is tagged `market_data_accepted`.
|
|
743
|
+
source: r.source,
|
|
664
744
|
range: r.range,
|
|
665
745
|
resolved_from: r.resolved_from,
|
|
666
746
|
};
|
|
@@ -670,6 +750,7 @@ function cmdScope(file, flags) {
|
|
|
670
750
|
deal_stage_target: 'scope',
|
|
671
751
|
asset_class: assetClass,
|
|
672
752
|
defaults_table: `${assetClass}@${table.version}`,
|
|
753
|
+
...(market ? { market_data: market.identity } : {}),
|
|
673
754
|
resolved: out,
|
|
674
755
|
};
|
|
675
756
|
const text = JSON.stringify(payload, null, 2);
|
|
@@ -692,7 +773,16 @@ function cmdRefine(file, flags) {
|
|
|
692
773
|
parsed.frontmatter.asset_class ??
|
|
693
774
|
'multifamily';
|
|
694
775
|
const pack = getPackForAssetClass(assetClass) ?? MULTIFAMILY_PACK;
|
|
695
|
-
|
|
776
|
+
// With `--market-data`, a field the observation set covers is no longer a
|
|
777
|
+
// gap the cascade cannot fill, so it drops out of the VOI ranking. That is
|
|
778
|
+
// the point: the ranking should tell you what is still worth diligencing.
|
|
779
|
+
const market = loadMarketData(flags);
|
|
780
|
+
const result = rankGaps(parsed, {
|
|
781
|
+
targets,
|
|
782
|
+
top,
|
|
783
|
+
packs: [pack],
|
|
784
|
+
...(market ? { cascadeContext: { asset_class: assetClass, market: market.lookup } } : {}),
|
|
785
|
+
});
|
|
696
786
|
if (flags['json']) {
|
|
697
787
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
698
788
|
return;
|
|
@@ -729,6 +819,137 @@ for (let _i = 0; _i < args.length; _i++) {
|
|
|
729
819
|
} // skip flag + its value
|
|
730
820
|
positional.push(a);
|
|
731
821
|
}
|
|
822
|
+
// ─── Composition (RFC 0021) ──────────────────────────────────────────────────
|
|
823
|
+
/** Load every `.uwpart.md` in a directory, keyed by `part_id`. */
|
|
824
|
+
function loadPartsDir(dir) {
|
|
825
|
+
const parts = new Map();
|
|
826
|
+
if (!existsSync(dir))
|
|
827
|
+
return parts;
|
|
828
|
+
for (const file of readdirSync(dir).filter((f) => f.endsWith(UWPART_EXTENSION)).sort()) {
|
|
829
|
+
const path = resolve(dir, file);
|
|
830
|
+
const part = parseUWPart(parseUWFile(readFileSync(path, 'utf-8')), { filename: file });
|
|
831
|
+
if (parts.has(part.part_id)) {
|
|
832
|
+
console.error(`Duplicate part_id '${part.part_id}' in ${dir}`);
|
|
833
|
+
process.exit(1);
|
|
834
|
+
}
|
|
835
|
+
parts.set(part.part_id, part);
|
|
836
|
+
}
|
|
837
|
+
return parts;
|
|
838
|
+
}
|
|
839
|
+
/** Where fragments live for a record, unless told otherwise. */
|
|
840
|
+
function defaultPartsDir(file) {
|
|
841
|
+
return resolve(dirname(resolve(file)), 'parts');
|
|
842
|
+
}
|
|
843
|
+
function cmdCompose(file, flags) {
|
|
844
|
+
const section = flags['externalize'];
|
|
845
|
+
if (typeof section !== 'string') {
|
|
846
|
+
console.error('Usage: uwmd compose <file> --externalize <section> [--collection-key <k>] [--collection-path <p>]');
|
|
847
|
+
process.exit(1);
|
|
848
|
+
}
|
|
849
|
+
const collectionKey = flags['collection-key'] ?? 'unit_id';
|
|
850
|
+
const collectionPath = flags['collection-path'] ?? 'units';
|
|
851
|
+
const parsed = parseUWFile(readFile(file));
|
|
852
|
+
let result;
|
|
853
|
+
try {
|
|
854
|
+
result = externalizeSection(parsed, {
|
|
855
|
+
section,
|
|
856
|
+
collectionKey,
|
|
857
|
+
collectionPath,
|
|
858
|
+
...(flags['part-prefix'] ? { partIdPrefix: flags['part-prefix'] } : {}),
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
catch (e) {
|
|
862
|
+
console.error(e instanceof Error ? e.message : String(e));
|
|
863
|
+
process.exit(1);
|
|
864
|
+
}
|
|
865
|
+
const outDir = flags['out-dir'] ? resolve(flags['out-dir']) : defaultPartsDir(file);
|
|
866
|
+
const recordPath = flags['in-place']
|
|
867
|
+
? resolve(file)
|
|
868
|
+
: flags['output']
|
|
869
|
+
? resolve(flags['output'])
|
|
870
|
+
: resolve(replaceUWExtension(file, '.externalized.uwx.md'));
|
|
871
|
+
const recordText = stringifyUWX(toUWEnvelope(result.document));
|
|
872
|
+
if (flags['dry-run']) {
|
|
873
|
+
console.log(`Would write ${result.parts.length} fragment(s) to ${outDir}:`);
|
|
874
|
+
for (const part of result.parts)
|
|
875
|
+
console.log(` ${part.part_id}${UWPART_EXTENSION}`);
|
|
876
|
+
console.log(`Would write the record to ${recordPath}`);
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
mkdirSync(outDir, { recursive: true });
|
|
880
|
+
for (const part of result.parts) {
|
|
881
|
+
writeFileSync(resolve(outDir, `${part.part_id}${UWPART_EXTENSION}`), stringifyUWPart(part), 'utf-8');
|
|
882
|
+
}
|
|
883
|
+
writeFileSync(recordPath, recordText, 'utf-8');
|
|
884
|
+
console.log(`Externalized ${section} → ${result.parts.length} fragment(s) in ${basename(outDir)}/`);
|
|
885
|
+
console.log(`Record written to ${basename(recordPath)}`);
|
|
886
|
+
// Externalizing is a packaging decision, not a model change — worth saying,
|
|
887
|
+
// because the file looks dramatically different afterwards.
|
|
888
|
+
console.log('The resolved record has the same semantic digest as the original.');
|
|
889
|
+
}
|
|
890
|
+
function cmdResolve(file, flags) {
|
|
891
|
+
const partsDir = flags['parts'] ? resolve(flags['parts']) : defaultPartsDir(file);
|
|
892
|
+
const parsed = parseUWFile(readFile(file));
|
|
893
|
+
let parts;
|
|
894
|
+
try {
|
|
895
|
+
parts = loadPartsDir(partsDir);
|
|
896
|
+
}
|
|
897
|
+
catch (e) {
|
|
898
|
+
console.error(e instanceof Error ? e.message : String(e));
|
|
899
|
+
process.exit(1);
|
|
900
|
+
}
|
|
901
|
+
const resolution = resolveComposition(parsed, { parts });
|
|
902
|
+
if (flags['json']) {
|
|
903
|
+
process.stdout.write(`${JSON.stringify({
|
|
904
|
+
status: resolution.status,
|
|
905
|
+
externalized: resolution.externalized,
|
|
906
|
+
parts_available: parts.size,
|
|
907
|
+
issues: resolution.issues,
|
|
908
|
+
}, null, 2)}\n`);
|
|
909
|
+
}
|
|
910
|
+
else {
|
|
911
|
+
console.log(`${resolution.status === 'resolved' ? 'RESOLVED' : 'UNRESOLVED'} ${basename(file)}`);
|
|
912
|
+
console.log(` ${parts.size} fragment(s) available in ${basename(partsDir)}/`);
|
|
913
|
+
console.log(` externalized section(s): ${resolution.externalized.join(', ') || '(none)'}`);
|
|
914
|
+
for (const issue of resolution.issues) {
|
|
915
|
+
console.log(` [${issue.code}] ${issue.message}`);
|
|
916
|
+
}
|
|
917
|
+
if (resolution.status === 'unresolved') {
|
|
918
|
+
// The rule worth restating: under-resolution is never a smaller answer.
|
|
919
|
+
console.log('\n Sections stay externalized rather than resolving partially —');
|
|
920
|
+
console.log(' a collection missing rows would still total and still validate.');
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
if (flags['output'] && resolution.status === 'resolved') {
|
|
924
|
+
const outPath = resolve(flags['output']);
|
|
925
|
+
writeFileSync(outPath, stringifyUWX(toUWEnvelope(resolution.document)), 'utf-8');
|
|
926
|
+
if (!flags['json'])
|
|
927
|
+
console.log(` resolved record → ${basename(outPath)}`);
|
|
928
|
+
}
|
|
929
|
+
if (resolution.status !== 'resolved')
|
|
930
|
+
process.exit(1);
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Resolve a record's externalized sections before another command reads it.
|
|
934
|
+
*
|
|
935
|
+
* Returns the record unchanged when `--resolved` was not requested, so callers
|
|
936
|
+
* can apply it unconditionally. Exits when resolution fails: verifying or
|
|
937
|
+
* exporting a partially-resolved record would report on a document that does
|
|
938
|
+
* not exist.
|
|
939
|
+
*/
|
|
940
|
+
function withResolved(parsed, file, flags) {
|
|
941
|
+
if (!flags['resolved'])
|
|
942
|
+
return parsed;
|
|
943
|
+
const partsDir = flags['parts'] ? resolve(flags['parts']) : defaultPartsDir(file);
|
|
944
|
+
const resolution = resolveComposition(parsed, { parts: loadPartsDir(partsDir) });
|
|
945
|
+
if (resolution.status !== 'resolved') {
|
|
946
|
+
console.error(`Cannot resolve ${basename(file)} against ${basename(partsDir)}/:`);
|
|
947
|
+
for (const issue of resolution.issues)
|
|
948
|
+
console.error(` [${issue.code}] ${issue.message}`);
|
|
949
|
+
process.exit(1);
|
|
950
|
+
}
|
|
951
|
+
return resolution.document;
|
|
952
|
+
}
|
|
732
953
|
// Top-level async wrapper so `run --live` can use await
|
|
733
954
|
(async () => {
|
|
734
955
|
switch (command) {
|
|
@@ -748,7 +969,7 @@ for (let _i = 0; _i < args.length; _i++) {
|
|
|
748
969
|
break;
|
|
749
970
|
case 'verify':
|
|
750
971
|
if (!positional[0]) {
|
|
751
|
-
console.error('Usage: uwmd verify <file> [--validate] [--integrity] [--policy] [--json]');
|
|
972
|
+
console.error('Usage: uwmd verify <file> [--validate] [--integrity] [--policy] [--resolved] [--json]');
|
|
752
973
|
process.exit(1);
|
|
753
974
|
}
|
|
754
975
|
await cmdVerify(positional[0], flags);
|
|
@@ -1020,7 +1241,7 @@ for (let _i = 0; _i < args.length; _i++) {
|
|
|
1020
1241
|
break;
|
|
1021
1242
|
case 'export':
|
|
1022
1243
|
if (!positional[0]) {
|
|
1023
|
-
console.error('Usage: uwmd export <file.uw.md> [--output <file.uw.json>] [--no-superseded] [--stdout]');
|
|
1244
|
+
console.error('Usage: uwmd export <file.uw.md> [--output <file.uw.json>] [--no-superseded] [--resolved] [--stdout]');
|
|
1024
1245
|
process.exit(1);
|
|
1025
1246
|
}
|
|
1026
1247
|
await cmdExport(positional[0], flags);
|
|
@@ -1044,18 +1265,47 @@ for (let _i = 0; _i < args.length; _i++) {
|
|
|
1044
1265
|
break;
|
|
1045
1266
|
case 'scope':
|
|
1046
1267
|
if (!positional[0]) {
|
|
1047
|
-
console.error('Usage: uwmd scope <file> [--asset-class multifamily] [--output <file>]');
|
|
1268
|
+
console.error('Usage: uwmd scope <file> [--asset-class multifamily] [--market-data <file>] [--output <file>]');
|
|
1048
1269
|
process.exit(1);
|
|
1049
1270
|
}
|
|
1050
1271
|
cmdScope(positional[0], flags);
|
|
1051
1272
|
break;
|
|
1052
1273
|
case 'refine':
|
|
1053
1274
|
if (!positional[0]) {
|
|
1054
|
-
console.error('Usage: uwmd refine <file> [--targets dscr,debt_yield] [--top 5] [--json]');
|
|
1275
|
+
console.error('Usage: uwmd refine <file> [--targets dscr,debt_yield] [--top 5] [--market-data <file>] [--json]');
|
|
1055
1276
|
process.exit(1);
|
|
1056
1277
|
}
|
|
1057
1278
|
cmdRefine(positional[0], flags);
|
|
1058
1279
|
break;
|
|
1280
|
+
case 'compose':
|
|
1281
|
+
if (!positional[0]) {
|
|
1282
|
+
console.error('Usage: uwmd compose <file> --externalize <section> [--collection-key <k>] [--collection-path <p>] [--out-dir <dir>] [--in-place] [--dry-run]');
|
|
1283
|
+
process.exit(1);
|
|
1284
|
+
}
|
|
1285
|
+
cmdCompose(positional[0], flags);
|
|
1286
|
+
break;
|
|
1287
|
+
case 'resolve':
|
|
1288
|
+
if (!positional[0]) {
|
|
1289
|
+
console.error('Usage: uwmd resolve <file> [--parts <dir>] [--output <file>] [--json]');
|
|
1290
|
+
process.exit(1);
|
|
1291
|
+
}
|
|
1292
|
+
cmdResolve(positional[0], flags);
|
|
1293
|
+
break;
|
|
1294
|
+
case 'market-data': {
|
|
1295
|
+
const sub = positional[0];
|
|
1296
|
+
if (sub === 'validate') {
|
|
1297
|
+
if (!positional[1]) {
|
|
1298
|
+
console.error('Usage: uwmd market-data validate <file> [--json]');
|
|
1299
|
+
process.exit(1);
|
|
1300
|
+
}
|
|
1301
|
+
cmdMarketDataValidate(positional[1], flags);
|
|
1302
|
+
}
|
|
1303
|
+
else {
|
|
1304
|
+
console.error('Usage: uwmd market-data validate <file> [--json]');
|
|
1305
|
+
process.exit(1);
|
|
1306
|
+
}
|
|
1307
|
+
break;
|
|
1308
|
+
}
|
|
1059
1309
|
case 'lease': {
|
|
1060
1310
|
const sub = positional[0];
|
|
1061
1311
|
if (sub === 'validate') {
|
|
@@ -1191,6 +1441,8 @@ Commands:
|
|
|
1191
1441
|
init Generate a blank .uwx.md file
|
|
1192
1442
|
summary <file> Print quick metrics to terminal
|
|
1193
1443
|
export <file> Export a digested UW JSON 1.0 document
|
|
1444
|
+
compose <file> Externalize a section into .uwpart.md fragments (RFC 0021)
|
|
1445
|
+
resolve <file> Resolve externalized sections from a parts directory (RFC 0021)
|
|
1194
1446
|
formats List registered machine representations
|
|
1195
1447
|
convert <file> Convert Lite/UWX/JSON/XML/CSV bundle representations
|
|
1196
1448
|
report <file> Render the lender package / credit memo HTML (§7.1/§7.2)
|
|
@@ -1209,6 +1461,12 @@ Options:
|
|
|
1209
1461
|
--strict Throw on parse errors instead of collecting
|
|
1210
1462
|
--format <f> Render format: json|csv|chat|summary (render, default: summary)
|
|
1211
1463
|
--no-superseded Drop append-only history from the .uw.json export (export)
|
|
1464
|
+
--resolved Resolve externalized sections first (verify, export)
|
|
1465
|
+
--parts <dir> Fragment directory (compose, resolve, --resolved; default: ./parts)
|
|
1466
|
+
--externalize <s> Section to split into fragments (compose)
|
|
1467
|
+
--collection-key <k> Row identity field (compose, default: unit_id)
|
|
1468
|
+
--collection-path <p> Field the rows occupy (compose, default: units)
|
|
1469
|
+
--in-place Overwrite the input record (compose)
|
|
1212
1470
|
--stdout Write export/convert output to stdout
|
|
1213
1471
|
--to <format> Target: lite|uwx|uw-json|uw-xml|uw-csv-bundle (convert)
|
|
1214
1472
|
--projection-report Write the UWX-to-Lite omission report as JSON (convert)
|