@datarails-ai/dr-cli 1.6.121 → 1.6.123
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/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import { fileURLToPath as fileURLToPath3 } from "url";
|
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "@datarails-ai/dr-cli",
|
|
11
|
-
version: "1.6.
|
|
11
|
+
version: "1.6.123",
|
|
12
12
|
description: "Datarails CLI",
|
|
13
13
|
type: "module",
|
|
14
14
|
engines: {
|
|
@@ -69,7 +69,7 @@ var package_default = {
|
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
// src/buildInfo.ts
|
|
72
|
-
var BUILD_TIME = "2026-10-
|
|
72
|
+
var BUILD_TIME = "2026-10-12T11:20:38.309Z";
|
|
73
73
|
function formatBuildTime(iso) {
|
|
74
74
|
if (iso === "") return "";
|
|
75
75
|
const d = new Date(iso);
|
|
@@ -891,7 +891,7 @@ async function exchangeApiKey(host, apiKey, options) {
|
|
|
891
891
|
}
|
|
892
892
|
function describeApiKeyExchangeFailure(err, identity2) {
|
|
893
893
|
if (err.httpStatus === 401) {
|
|
894
|
-
return `the server refused the API key for ${identity2} \u2014 it may be revoked or expired, API keys may not be enabled for your organization (ask an admin), or the server may be temporarily unable to evaluate that.`;
|
|
894
|
+
return `the server refused the API key for ${identity2} \u2014 it may be revoked or expired, API keys may not be enabled for your organization (ask an admin), or the server may be temporarily unable to evaluate that. If the key was revoked or expired, start a new session with a new key; retrying with this one will not help.`;
|
|
895
895
|
}
|
|
896
896
|
if (err.httpStatus === 404) {
|
|
897
897
|
return `${identity2}: this server does not support API keys (the exchange endpoint answered 404).`;
|
|
@@ -31029,6 +31029,96 @@ function formatCalcFieldError(name, preview2, full) {
|
|
|
31029
31029
|
return `${head} looks like placeholder text, not a real expression. The server's ETL compiler rejects formulas with no [field] reference, "quoted" literal, function() call, or numeric value \u2014 replace it before retrying. Common cause: a GET \u2192 PUT round-trip on a pre-kickoff starter mapper carried the placeholder text forward.`;
|
|
31030
31030
|
}
|
|
31031
31031
|
|
|
31032
|
+
// src/commands/_mapperConfigShape.ts
|
|
31033
|
+
var OBJECT_LIST_KEYS = [
|
|
31034
|
+
"header",
|
|
31035
|
+
"dimension",
|
|
31036
|
+
"footer",
|
|
31037
|
+
"attributes",
|
|
31038
|
+
"calculated_fields",
|
|
31039
|
+
"fx_conversions",
|
|
31040
|
+
"table_id"
|
|
31041
|
+
];
|
|
31042
|
+
var GET_HINT = '`dr datamappers get <mapper_id> --json` shows the shape the server expects (a mapper with no anchor carries "table_id": []).';
|
|
31043
|
+
function describeValue(v) {
|
|
31044
|
+
if (v === null) return "null";
|
|
31045
|
+
if (Array.isArray(v)) return "an array";
|
|
31046
|
+
switch (typeof v) {
|
|
31047
|
+
case "number":
|
|
31048
|
+
return `a number (${String(v)})`;
|
|
31049
|
+
case "string":
|
|
31050
|
+
return `a string (${JSON.stringify(v.length > 40 ? `${v.slice(0, 40)}\u2026` : v)})`;
|
|
31051
|
+
case "boolean":
|
|
31052
|
+
return `a boolean (${String(v)})`;
|
|
31053
|
+
case "object":
|
|
31054
|
+
return "an object";
|
|
31055
|
+
default:
|
|
31056
|
+
return typeof v;
|
|
31057
|
+
}
|
|
31058
|
+
}
|
|
31059
|
+
function isPlainObject2(v) {
|
|
31060
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
31061
|
+
}
|
|
31062
|
+
function tableIdProblems(value) {
|
|
31063
|
+
if (!Array.isArray(value)) {
|
|
31064
|
+
return [
|
|
31065
|
+
`config.table_id must be an array, got ${describeValue(value)}. It is the table anchor, not the table id: [] for none, or one cell object such as [{"coordinate": "A1", "value": "<text in that cell>", "match_type": "any_text"}]. The table id goes on the command line, not in the body.`
|
|
31066
|
+
];
|
|
31067
|
+
}
|
|
31068
|
+
const out = [];
|
|
31069
|
+
if (value.length > 1) {
|
|
31070
|
+
out.push(
|
|
31071
|
+
`config.table_id holds ${value.length} entries; the server allows at most one anchor ("Only one anchor is allowed").`
|
|
31072
|
+
);
|
|
31073
|
+
}
|
|
31074
|
+
value.forEach((entry, i) => {
|
|
31075
|
+
if (!isPlainObject2(entry)) {
|
|
31076
|
+
out.push(`config.table_id[${i}] must be an object, got ${describeValue(entry)}.`);
|
|
31077
|
+
} else if (typeof entry["match_type"] !== "string") {
|
|
31078
|
+
out.push(
|
|
31079
|
+
`config.table_id[${i}].match_type is required (a string such as "any_text" or "location").`
|
|
31080
|
+
);
|
|
31081
|
+
}
|
|
31082
|
+
});
|
|
31083
|
+
return out;
|
|
31084
|
+
}
|
|
31085
|
+
function validateMapperConfigShape(config) {
|
|
31086
|
+
if (config === void 0) return;
|
|
31087
|
+
if (!isPlainObject2(config)) {
|
|
31088
|
+
throw new Error(`config must be a JSON object, got ${describeValue(config)}. ${GET_HINT}`);
|
|
31089
|
+
}
|
|
31090
|
+
const problems = [];
|
|
31091
|
+
for (const key of OBJECT_LIST_KEYS) {
|
|
31092
|
+
if (!(key in config)) continue;
|
|
31093
|
+
const value = config[key];
|
|
31094
|
+
if (key === "table_id") {
|
|
31095
|
+
problems.push(...tableIdProblems(value));
|
|
31096
|
+
continue;
|
|
31097
|
+
}
|
|
31098
|
+
if (!Array.isArray(value)) {
|
|
31099
|
+
problems.push(`config.${key} must be an array of objects, got ${describeValue(value)}.`);
|
|
31100
|
+
continue;
|
|
31101
|
+
}
|
|
31102
|
+
value.forEach((entry, i) => {
|
|
31103
|
+
if (!isPlainObject2(entry)) {
|
|
31104
|
+
problems.push(`config.${key}[${i}] must be an object, got ${describeValue(entry)}.`);
|
|
31105
|
+
}
|
|
31106
|
+
});
|
|
31107
|
+
}
|
|
31108
|
+
if ("include_sheets" in config && !Array.isArray(config["include_sheets"])) {
|
|
31109
|
+
problems.push(
|
|
31110
|
+
`config.include_sheets must be an array, got ${describeValue(config["include_sheets"])}.`
|
|
31111
|
+
);
|
|
31112
|
+
}
|
|
31113
|
+
if (problems.length > 0) {
|
|
31114
|
+
throw new Error(
|
|
31115
|
+
`invalid datamapper config (the server would answer an unattributed HTTP 500 / "Unknown error"):
|
|
31116
|
+
- ${problems.join("\n - ")}
|
|
31117
|
+
${GET_HINT}`
|
|
31118
|
+
);
|
|
31119
|
+
}
|
|
31120
|
+
}
|
|
31121
|
+
|
|
31032
31122
|
// src/commands/_errorLog.ts
|
|
31033
31123
|
import { promises as fs14 } from "fs";
|
|
31034
31124
|
import * as os3 from "os";
|
|
@@ -31289,6 +31379,7 @@ async function runPreviewData(cfg) {
|
|
|
31289
31379
|
throw new Error("--payload-file is required");
|
|
31290
31380
|
}
|
|
31291
31381
|
let body = await readJsonPayload(payloadFile);
|
|
31382
|
+
validateMapperConfigShape(body["config"]);
|
|
31292
31383
|
const { decoded, changeCount } = decodeBodyEntitiesDeep(body);
|
|
31293
31384
|
if (changeCount > 0) {
|
|
31294
31385
|
body = decoded;
|
|
@@ -31485,7 +31576,12 @@ async function runDatamappersCreate(cfg) {
|
|
|
31485
31576
|
parseKeyEqualsValue(cfg.flags.calcs, "--calc");
|
|
31486
31577
|
parseKeyEqualsValue(cfg.flags.attrs, "--attr");
|
|
31487
31578
|
parseKeyEqualsValue(cfg.flags.dims, "--dim");
|
|
31488
|
-
parseKeyEqualsValue(cfg.flags.tableIdCells ?? [], "--table-id-cell");
|
|
31579
|
+
const tableIdCells = parseKeyEqualsValue(cfg.flags.tableIdCells ?? [], "--table-id-cell");
|
|
31580
|
+
if (tableIdCells.length > 1) {
|
|
31581
|
+
throw new Error(
|
|
31582
|
+
`--table-id-cell takes one cell; got ${tableIdCells.length}. The server allows one table anchor per mapper.`
|
|
31583
|
+
);
|
|
31584
|
+
}
|
|
31489
31585
|
parseKeyEqualsValue(cfg.flags.footers, "--footer");
|
|
31490
31586
|
parseKeyEqualsValue(cfg.flags.renames, "--rename");
|
|
31491
31587
|
}
|
|
@@ -31493,6 +31589,7 @@ async function runDatamappersCreate(cfg) {
|
|
|
31493
31589
|
let body;
|
|
31494
31590
|
if (payloadFile !== "") {
|
|
31495
31591
|
body = await readJsonPayload(payloadFile);
|
|
31592
|
+
validateMapperConfigShape(body["config"]);
|
|
31496
31593
|
} else {
|
|
31497
31594
|
body = await buildConveniencePayload(
|
|
31498
31595
|
cfg.client,
|
|
@@ -31590,6 +31687,7 @@ async function runDatamappersUpdate(cfg) {
|
|
|
31590
31687
|
}
|
|
31591
31688
|
const rescanValue = resolveRescan(cfg.rescan, cfg.noRescan);
|
|
31592
31689
|
let body = await readJsonPayload(payloadFile);
|
|
31690
|
+
validateMapperConfigShape(body["config"]);
|
|
31593
31691
|
if (rescanValue === "") {
|
|
31594
31692
|
delete body["rescan"];
|
|
31595
31693
|
} else {
|
|
@@ -32229,7 +32327,7 @@ function registerDatamappersCommand(root) {
|
|
|
32229
32327
|
addAccountFlag(dimensionCmd);
|
|
32230
32328
|
const multipleCmd = addConvenienceFlags(
|
|
32231
32329
|
createCmd.command("multiple").description(
|
|
32232
|
-
"Create a multiple-tables datamapper (requires --dim \u2265 1 AND --table-id-cell
|
|
32330
|
+
"Create a multiple-tables datamapper (requires --dim \u2265 1 AND one --table-id-cell)"
|
|
32233
32331
|
).argument("<table_id>", "Table ID (positive integer)")
|
|
32234
32332
|
).requiredOption(
|
|
32235
32333
|
"--dim <Coord=Label>",
|
|
@@ -32238,7 +32336,7 @@ function registerDatamappersCommand(root) {
|
|
|
32238
32336
|
[]
|
|
32239
32337
|
).requiredOption(
|
|
32240
32338
|
"--table-id-cell <Coord=Label>",
|
|
32241
|
-
"
|
|
32339
|
+
"The table_id (anchor) cell \u2014 the cell carrying the per-section table identifier. Required; the server allows exactly one.",
|
|
32242
32340
|
collectArrayOpt,
|
|
32243
32341
|
[]
|
|
32244
32342
|
).action(async (tableIdStr, opts) => {
|
|
@@ -37502,22 +37600,68 @@ async function runFileboxSetVersionWorkflow(cfg) {
|
|
|
37502
37600
|
`document ${cfg.docId} has no WORKFLOW tag in its config (available: ${availMsg})`
|
|
37503
37601
|
);
|
|
37504
37602
|
}
|
|
37505
|
-
const
|
|
37506
|
-
|
|
37507
|
-
|
|
37508
|
-
const
|
|
37509
|
-
|
|
37510
|
-
cfg.
|
|
37511
|
-
|
|
37512
|
-
|
|
37513
|
-
|
|
37514
|
-
|
|
37515
|
-
|
|
37516
|
-
|
|
37603
|
+
const err = cfg.err ?? process.stderr;
|
|
37604
|
+
const { target } = await loadTargetVersion(cfg.client, cfg.docId, cfg.versionId, cfg.signal);
|
|
37605
|
+
const current = tagValueOf(target.tags, tag.id);
|
|
37606
|
+
const previous = typeof current === "string" ? current : null;
|
|
37607
|
+
const writer = new FullTagSetWriter({
|
|
37608
|
+
client: cfg.client,
|
|
37609
|
+
docId: cfg.docId,
|
|
37610
|
+
versionId: cfg.versionId,
|
|
37611
|
+
snapshot: target.tags,
|
|
37612
|
+
tag: { id: tag.id, value: cfg.status, name: tag.name, label: cfg.status },
|
|
37613
|
+
action: "set version workflow",
|
|
37614
|
+
signal: cfg.signal,
|
|
37615
|
+
sleep: cfg.sleep,
|
|
37616
|
+
settleMs: cfg.tagSettleDelayMs
|
|
37617
|
+
});
|
|
37618
|
+
await writer.write();
|
|
37619
|
+
const kept = await keepDateTagValue({
|
|
37620
|
+
writer,
|
|
37621
|
+
seen: await writer.readBack(),
|
|
37622
|
+
dateBefore: target.date_tag_value,
|
|
37623
|
+
versionId: cfg.versionId,
|
|
37624
|
+
docId: cfg.docId,
|
|
37625
|
+
what: "WORKFLOW tag",
|
|
37626
|
+
signal: cfg.signal,
|
|
37627
|
+
err
|
|
37628
|
+
});
|
|
37629
|
+
const { seen, dateAfter, datePreserved } = kept;
|
|
37630
|
+
const now = seen === null ? void 0 : tagValueOf(seen.tags, tag.id);
|
|
37631
|
+
const verified = seen !== null && now === cfg.status;
|
|
37632
|
+
if (seen === null) {
|
|
37633
|
+
err.write(
|
|
37634
|
+
`warning: could not read version ${cfg.versionId} back to verify its WORKFLOW tag \u2014 confirm with \`dr filebox versions ${cfg.docId}\`
|
|
37635
|
+
`
|
|
37636
|
+
);
|
|
37637
|
+
} else if (!verified) {
|
|
37638
|
+
err.write(
|
|
37639
|
+
`warning: version ${cfg.versionId} reads WORKFLOW ${JSON.stringify(now ?? null)} after the write, not "${cfg.status}" \u2014 confirm with \`dr filebox versions ${cfg.docId}\`
|
|
37640
|
+
`
|
|
37641
|
+
);
|
|
37642
|
+
}
|
|
37643
|
+
if (cfg.asJson) {
|
|
37644
|
+
writeJSONIndented(cfg.out, {
|
|
37645
|
+
version_id: cfg.versionId,
|
|
37646
|
+
doc_id: cfg.docId,
|
|
37647
|
+
tag_id: tag.id,
|
|
37648
|
+
previous,
|
|
37649
|
+
status: cfg.status,
|
|
37650
|
+
verified,
|
|
37651
|
+
reapplied: kept.reapplied,
|
|
37652
|
+
date_tag_value: dateAfter,
|
|
37653
|
+
date_tag_value_preserved: datePreserved
|
|
37654
|
+
});
|
|
37517
37655
|
return;
|
|
37518
37656
|
}
|
|
37519
|
-
const
|
|
37520
|
-
|
|
37657
|
+
const verdict = !verified ? "unverified" : kept.reapplied ? "verified after one re-apply" : "verified";
|
|
37658
|
+
cfg.out.write(
|
|
37659
|
+
`Set WORKFLOW tag on version ${cfg.versionId}: ${JSON.stringify(previous ?? "(unset)")} -> "${cfg.status}" (${verdict}).
|
|
37660
|
+
`
|
|
37661
|
+
);
|
|
37662
|
+
if (datePreserved === false) {
|
|
37663
|
+
cfg.out.write(dateNotPreservedLine(dateAfter, target.date_tag_value));
|
|
37664
|
+
}
|
|
37521
37665
|
}
|
|
37522
37666
|
var FILE_STATUS_VALUES = ["Draft", "Final"];
|
|
37523
37667
|
function parseFileStatus(raw) {
|
|
@@ -37618,6 +37762,110 @@ function fullTagSetWith(stored, write) {
|
|
|
37618
37762
|
}
|
|
37619
37763
|
return [...kept, write].sort((a, b) => a.id - b.id);
|
|
37620
37764
|
}
|
|
37765
|
+
var FullTagSetWriter = class {
|
|
37766
|
+
constructor(cfg) {
|
|
37767
|
+
this.cfg = cfg;
|
|
37768
|
+
this.base = cfg.snapshot ?? [];
|
|
37769
|
+
}
|
|
37770
|
+
cfg;
|
|
37771
|
+
base;
|
|
37772
|
+
/** PUT the full set; a non-2xx answer throws `TagWriteRefusedError`. */
|
|
37773
|
+
async write() {
|
|
37774
|
+
const { cfg } = this;
|
|
37775
|
+
const res = await setVersionTagsAndLabel(
|
|
37776
|
+
cfg.client,
|
|
37777
|
+
cfg.versionId,
|
|
37778
|
+
{ tags_to_update: JSON.stringify(fullTagSetWith(this.base, cfg.tag)) },
|
|
37779
|
+
{ signal: cfg.signal }
|
|
37780
|
+
);
|
|
37781
|
+
if (res.status < 200 || res.status >= 300) {
|
|
37782
|
+
const body = (await res.text()).trim();
|
|
37783
|
+
throw new TagWriteRefusedError(
|
|
37784
|
+
`${cfg.action} refused (HTTP ${res.status}): ${body}`,
|
|
37785
|
+
`(HTTP ${res.status}): ${body}`
|
|
37786
|
+
);
|
|
37787
|
+
}
|
|
37788
|
+
}
|
|
37789
|
+
/** Wait the settle delay, then read the version back (`null` = unreadable). */
|
|
37790
|
+
async readBack() {
|
|
37791
|
+
const { cfg } = this;
|
|
37792
|
+
const sleep3 = cfg.sleep ?? defaultCycleTagSleep;
|
|
37793
|
+
await sleep3(cfg.settleMs ?? CYCLE_TAG_SETTLE_DELAY_MS, cfg.signal);
|
|
37794
|
+
return readVersionForConfirm(cfg.client, cfg.versionId, cfg.docId, cfg.signal);
|
|
37795
|
+
}
|
|
37796
|
+
/** Build the next write from this read-back instead of the earlier snapshot. */
|
|
37797
|
+
refreshBase(v) {
|
|
37798
|
+
if (v !== null && v.tags.length > 0) {
|
|
37799
|
+
this.base = v.tags;
|
|
37800
|
+
}
|
|
37801
|
+
}
|
|
37802
|
+
};
|
|
37803
|
+
async function keepDateTagValue(cfg) {
|
|
37804
|
+
const { dateBefore, err } = cfg;
|
|
37805
|
+
let seen = cfg.seen;
|
|
37806
|
+
const dateMoved = (v) => v !== null && typeof dateBefore === "number" && v.row.date_tag_value !== void 0 && v.row.date_tag_value !== dateBefore;
|
|
37807
|
+
let reapplied = false;
|
|
37808
|
+
let dateUnknown = false;
|
|
37809
|
+
if (dateMoved(seen)) {
|
|
37810
|
+
err.write(
|
|
37811
|
+
`warning: date_tag_value on version ${cfg.versionId} changed (${JSON.stringify(dateBefore)} -> ${JSON.stringify(seen.row.date_tag_value ?? null)}) when its ${cfg.what} was set; re-applying the full tag set once
|
|
37812
|
+
`
|
|
37813
|
+
);
|
|
37814
|
+
reapplied = true;
|
|
37815
|
+
cfg.writer.refreshBase(seen);
|
|
37816
|
+
let refused = false;
|
|
37817
|
+
try {
|
|
37818
|
+
await cfg.writer.write();
|
|
37819
|
+
} catch (e) {
|
|
37820
|
+
if (cfg.signal.aborted) {
|
|
37821
|
+
throw e;
|
|
37822
|
+
}
|
|
37823
|
+
if (e instanceof TagWriteRefusedError) {
|
|
37824
|
+
refused = true;
|
|
37825
|
+
err.write(`warning: re-applying the full tag set was refused ${e.reason}
|
|
37826
|
+
`);
|
|
37827
|
+
} else {
|
|
37828
|
+
err.write(
|
|
37829
|
+
`warning: re-applying the full tag set failed (${e instanceof Error ? e.message : String(e)}); its outcome is unknown, reading the version again
|
|
37830
|
+
`
|
|
37831
|
+
);
|
|
37832
|
+
}
|
|
37833
|
+
}
|
|
37834
|
+
const after = refused ? seen : await cfg.writer.readBack();
|
|
37835
|
+
if (after === null) {
|
|
37836
|
+
dateUnknown = true;
|
|
37837
|
+
err.write(
|
|
37838
|
+
`warning: could not read version ${cfg.versionId} back to confirm its date_tag_value after re-applying the full tag set \u2014 check it with \`dr filebox versions ${cfg.docId} --json\`
|
|
37839
|
+
`
|
|
37840
|
+
);
|
|
37841
|
+
} else {
|
|
37842
|
+
seen = after;
|
|
37843
|
+
}
|
|
37844
|
+
}
|
|
37845
|
+
const dateAfter = dateUnknown || seen === null || seen.row.date_tag_value === void 0 ? null : seen.row.date_tag_value;
|
|
37846
|
+
const datePreserved = dateUnknown || seen === null || dateBefore === void 0 || seen.row.date_tag_value === void 0 ? null : !dateMoved(seen);
|
|
37847
|
+
if (datePreserved === false) {
|
|
37848
|
+
err.write(
|
|
37849
|
+
`warning: date_tag_value on version ${cfg.versionId} is still ${JSON.stringify(dateAfter)} (was ${JSON.stringify(dateBefore)}) after one re-apply; its tags still carry the date. Restore it with \`dr filebox set-version-date-tag ${cfg.versionId} --doc ${cfg.docId} --<dimension> <date>\` (it sends the date tag alone; on a planning filebox use \`dr filebox set-version-cycle ${cfg.versionId} --doc ${cfg.docId} --date <date> --scenario <name>\` instead, which keeps the cycle), then check \`dr filebox versions ${cfg.docId} --json\`
|
|
37850
|
+
`
|
|
37851
|
+
);
|
|
37852
|
+
}
|
|
37853
|
+
return { seen, reapplied, dateAfter, datePreserved };
|
|
37854
|
+
}
|
|
37855
|
+
function dateNotPreservedLine(dateAfter, dateBefore) {
|
|
37856
|
+
return `date_tag_value NOT preserved: it reads ${JSON.stringify(dateAfter)}, was ${JSON.stringify(dateBefore)} (see the warning above).
|
|
37857
|
+
`;
|
|
37858
|
+
}
|
|
37859
|
+
async function loadTargetVersion(client, docId, versionId, signal) {
|
|
37860
|
+
const rows = await listVersions(client, docId, { includeAutoSaves: true }, { signal });
|
|
37861
|
+
const target = rows.find((r) => r.id === versionId);
|
|
37862
|
+
if (target === void 0) {
|
|
37863
|
+
throw new Error(
|
|
37864
|
+
`version ${versionId} is not a version of filebox ${docId} \u2014 list them with \`dr filebox versions ${docId} --all\``
|
|
37865
|
+
);
|
|
37866
|
+
}
|
|
37867
|
+
return { rows, target };
|
|
37868
|
+
}
|
|
37621
37869
|
async function runFileboxSetVersionFileStatus(cfg) {
|
|
37622
37870
|
if (cfg.docId <= 0) {
|
|
37623
37871
|
throw new Error("document id must be positive");
|
|
@@ -37626,8 +37874,6 @@ async function runFileboxSetVersionFileStatus(cfg) {
|
|
|
37626
37874
|
throw new Error("version id must be positive");
|
|
37627
37875
|
}
|
|
37628
37876
|
const err = cfg.err ?? process.stderr;
|
|
37629
|
-
const sleep3 = cfg.sleep ?? defaultCycleTagSleep;
|
|
37630
|
-
const settleMs = cfg.tagSettleDelayMs ?? CYCLE_TAG_SETTLE_DELAY_MS;
|
|
37631
37877
|
const details = await getDetails(cfg.client, cfg.docId, { signal: cfg.signal });
|
|
37632
37878
|
const tag = (details.tags_config ?? []).find(isFileStatusTag);
|
|
37633
37879
|
if (tag === void 0) {
|
|
@@ -37641,18 +37887,12 @@ async function runFileboxSetVersionFileStatus(cfg) {
|
|
|
37641
37887
|
`File Status on document ${cfg.docId} does not allow "${cfg.status}" (allowed: ${tag.values.map((v) => JSON.stringify(v)).join(" | ")})`
|
|
37642
37888
|
);
|
|
37643
37889
|
}
|
|
37644
|
-
const rows = await
|
|
37890
|
+
const { rows, target } = await loadTargetVersion(
|
|
37645
37891
|
cfg.client,
|
|
37646
37892
|
cfg.docId,
|
|
37647
|
-
|
|
37648
|
-
|
|
37893
|
+
cfg.versionId,
|
|
37894
|
+
cfg.signal
|
|
37649
37895
|
);
|
|
37650
|
-
const target = rows.find((r) => r.id === cfg.versionId);
|
|
37651
|
-
if (target === void 0) {
|
|
37652
|
-
throw new Error(
|
|
37653
|
-
`version ${cfg.versionId} is not a version of filebox ${cfg.docId} \u2014 list them with \`dr filebox versions ${cfg.docId} --all\``
|
|
37654
|
-
);
|
|
37655
|
-
}
|
|
37656
37896
|
const current = tagValueOf(target.tags, tag.id);
|
|
37657
37897
|
const previous = typeof current === "string" ? current : null;
|
|
37658
37898
|
const options = tag.options ?? {};
|
|
@@ -37705,45 +37945,25 @@ async function runFileboxSetVersionFileStatus(cfg) {
|
|
|
37705
37945
|
);
|
|
37706
37946
|
}
|
|
37707
37947
|
}
|
|
37708
|
-
const
|
|
37709
|
-
|
|
37710
|
-
|
|
37711
|
-
|
|
37712
|
-
|
|
37713
|
-
|
|
37714
|
-
|
|
37715
|
-
|
|
37716
|
-
|
|
37717
|
-
|
|
37718
|
-
|
|
37719
|
-
|
|
37720
|
-
const write = async () => {
|
|
37721
|
-
const res = await setVersionTagsAndLabel(
|
|
37722
|
-
cfg.client,
|
|
37723
|
-
cfg.versionId,
|
|
37724
|
-
{ tags_to_update: JSON.stringify(fullTagSetWith(base, statusWrite)) },
|
|
37725
|
-
{ signal: cfg.signal }
|
|
37726
|
-
);
|
|
37727
|
-
if (res.status < 200 || res.status >= 300) {
|
|
37728
|
-
const body = await res.text();
|
|
37729
|
-
throw new TagWriteRefusedError(
|
|
37730
|
-
`set version file status refused (HTTP ${res.status}): ${body.trim()}`,
|
|
37731
|
-
`(HTTP ${res.status}): ${body.trim()}`
|
|
37732
|
-
);
|
|
37733
|
-
}
|
|
37734
|
-
};
|
|
37735
|
-
const readBack = async () => {
|
|
37736
|
-
await sleep3(settleMs, cfg.signal);
|
|
37737
|
-
return readVersionForConfirm(cfg.client, cfg.versionId, cfg.docId, cfg.signal);
|
|
37738
|
-
};
|
|
37739
|
-
await write();
|
|
37948
|
+
const writer = new FullTagSetWriter({
|
|
37949
|
+
client: cfg.client,
|
|
37950
|
+
docId: cfg.docId,
|
|
37951
|
+
versionId: cfg.versionId,
|
|
37952
|
+
snapshot: target.tags,
|
|
37953
|
+
tag: { id: tag.id, value: cfg.status, name: tag.name, label: cfg.status },
|
|
37954
|
+
action: "set version file status",
|
|
37955
|
+
signal: cfg.signal,
|
|
37956
|
+
sleep: cfg.sleep,
|
|
37957
|
+
settleMs: cfg.tagSettleDelayMs
|
|
37958
|
+
});
|
|
37959
|
+
await writer.write();
|
|
37740
37960
|
let reapplied = false;
|
|
37741
|
-
let seen = await readBack();
|
|
37961
|
+
let seen = await writer.readBack();
|
|
37742
37962
|
if (seen !== null && tagValueOf(seen.tags, tag.id) !== cfg.status) {
|
|
37743
37963
|
reapplied = true;
|
|
37744
|
-
refreshBase(seen);
|
|
37745
|
-
await write();
|
|
37746
|
-
seen = await readBack();
|
|
37964
|
+
writer.refreshBase(seen);
|
|
37965
|
+
await writer.write();
|
|
37966
|
+
seen = await writer.readBack();
|
|
37747
37967
|
const now = seen === null ? void 0 : tagValueOf(seen.tags, tag.id);
|
|
37748
37968
|
if (seen !== null && now !== cfg.status) {
|
|
37749
37969
|
throw new Error(
|
|
@@ -37751,53 +37971,19 @@ async function runFileboxSetVersionFileStatus(cfg) {
|
|
|
37751
37971
|
);
|
|
37752
37972
|
}
|
|
37753
37973
|
}
|
|
37754
|
-
const
|
|
37755
|
-
|
|
37756
|
-
|
|
37757
|
-
|
|
37758
|
-
|
|
37759
|
-
|
|
37760
|
-
|
|
37761
|
-
|
|
37762
|
-
|
|
37763
|
-
|
|
37764
|
-
|
|
37765
|
-
|
|
37766
|
-
|
|
37767
|
-
} catch (e) {
|
|
37768
|
-
if (cfg.signal.aborted) {
|
|
37769
|
-
throw e;
|
|
37770
|
-
}
|
|
37771
|
-
if (e instanceof TagWriteRefusedError) {
|
|
37772
|
-
refused = true;
|
|
37773
|
-
err.write(`warning: re-applying the full tag set was refused ${e.reason}
|
|
37774
|
-
`);
|
|
37775
|
-
} else {
|
|
37776
|
-
err.write(
|
|
37777
|
-
`warning: re-applying the full tag set failed (${e instanceof Error ? e.message : String(e)}); its outcome is unknown, reading the version again
|
|
37778
|
-
`
|
|
37779
|
-
);
|
|
37780
|
-
}
|
|
37781
|
-
}
|
|
37782
|
-
const after = refused ? seen : await readBack();
|
|
37783
|
-
if (after === null) {
|
|
37784
|
-
dateUnknown = true;
|
|
37785
|
-
err.write(
|
|
37786
|
-
`warning: could not read version ${cfg.versionId} back to confirm its date_tag_value after re-applying the full tag set \u2014 check it with \`dr filebox versions ${cfg.docId} --json\`
|
|
37787
|
-
`
|
|
37788
|
-
);
|
|
37789
|
-
} else {
|
|
37790
|
-
seen = after;
|
|
37791
|
-
}
|
|
37792
|
-
}
|
|
37793
|
-
const dateAfter = dateUnknown || seen === null || seen.row.date_tag_value === void 0 ? null : seen.row.date_tag_value;
|
|
37794
|
-
const datePreserved = dateUnknown || seen === null || dateBefore === void 0 || seen.row.date_tag_value === void 0 ? null : !dateMoved(seen);
|
|
37795
|
-
if (datePreserved === false) {
|
|
37796
|
-
err.write(
|
|
37797
|
-
`warning: date_tag_value on version ${cfg.versionId} is still ${JSON.stringify(dateAfter)} (was ${JSON.stringify(dateBefore)}) after one re-apply; its tags still carry the date. Restore it with \`dr filebox set-version-date-tag ${cfg.versionId} --doc ${cfg.docId} --<dimension> <date>\` (it sends the date tag alone; on a planning filebox use \`dr filebox set-version-cycle ${cfg.versionId} --doc ${cfg.docId} --date <date> --scenario <name>\` instead, which keeps the cycle), then check \`dr filebox versions ${cfg.docId} --json\`
|
|
37798
|
-
`
|
|
37799
|
-
);
|
|
37800
|
-
}
|
|
37974
|
+
const kept = await keepDateTagValue({
|
|
37975
|
+
writer,
|
|
37976
|
+
seen,
|
|
37977
|
+
dateBefore: target.date_tag_value,
|
|
37978
|
+
versionId: cfg.versionId,
|
|
37979
|
+
docId: cfg.docId,
|
|
37980
|
+
what: "File Status",
|
|
37981
|
+
signal: cfg.signal,
|
|
37982
|
+
err
|
|
37983
|
+
});
|
|
37984
|
+
seen = kept.seen;
|
|
37985
|
+
reapplied ||= kept.reapplied;
|
|
37986
|
+
const { dateAfter, datePreserved } = kept;
|
|
37801
37987
|
if (seen === null) {
|
|
37802
37988
|
err.write(
|
|
37803
37989
|
`warning: could not read version ${cfg.versionId} back to verify its File Status \u2014 confirm with \`dr filebox versions ${cfg.docId}\`
|
|
@@ -37836,10 +38022,7 @@ async function runFileboxSetVersionFileStatus(cfg) {
|
|
|
37836
38022
|
`
|
|
37837
38023
|
);
|
|
37838
38024
|
if (datePreserved === false) {
|
|
37839
|
-
cfg.out.write(
|
|
37840
|
-
`date_tag_value NOT preserved: it reads ${JSON.stringify(dateAfter)}, was ${JSON.stringify(dateBefore)} (see the warning above).
|
|
37841
|
-
`
|
|
37842
|
-
);
|
|
38025
|
+
cfg.out.write(dateNotPreservedLine(dateAfter, target.date_tag_value));
|
|
37843
38026
|
}
|
|
37844
38027
|
if (demotedIds.length > 0) {
|
|
37845
38028
|
cfg.out.write(
|
|
@@ -39537,33 +39720,41 @@ function registerFileboxCommand(root) {
|
|
|
39537
39720
|
).requiredOption(
|
|
39538
39721
|
"--status <s>",
|
|
39539
39722
|
"New WORKFLOW value: Not Started | In Progress | Pending | Approved | Rejected. Setting Approved requires the `approve_workflow` permission server-side."
|
|
39540
|
-
).
|
|
39541
|
-
|
|
39542
|
-
|
|
39543
|
-
|
|
39544
|
-
|
|
39545
|
-
|
|
39546
|
-
|
|
39547
|
-
|
|
39548
|
-
|
|
39549
|
-
|
|
39550
|
-
);
|
|
39551
|
-
|
|
39552
|
-
|
|
39553
|
-
|
|
39554
|
-
|
|
39555
|
-
|
|
39556
|
-
|
|
39557
|
-
|
|
39558
|
-
|
|
39559
|
-
|
|
39560
|
-
|
|
39561
|
-
|
|
39562
|
-
|
|
39563
|
-
|
|
39564
|
-
|
|
39723
|
+
).option(
|
|
39724
|
+
"--json",
|
|
39725
|
+
"Output a JSON record describing the write instead of the human confirmation",
|
|
39726
|
+
false
|
|
39727
|
+
).action(
|
|
39728
|
+
async (idStr, opts) => {
|
|
39729
|
+
const versionId = parseFileboxIdArg(idStr);
|
|
39730
|
+
if (opts.doc === void 0 || opts.doc === "") {
|
|
39731
|
+
throw new Error("--doc is required");
|
|
39732
|
+
}
|
|
39733
|
+
const docId = parseFileboxIdArg(opts.doc);
|
|
39734
|
+
const raw = opts.status ?? "";
|
|
39735
|
+
if (!WORKFLOW_STATUSES.includes(raw)) {
|
|
39736
|
+
throw new Error(
|
|
39737
|
+
`invalid --status "${raw}" (expected one of: ${WORKFLOW_STATUSES.map((s) => `"${s}"`).join(" | ")})`
|
|
39738
|
+
);
|
|
39739
|
+
}
|
|
39740
|
+
const status2 = raw;
|
|
39741
|
+
const setup = await setupCall(opts.server, opts.account, { timeoutMs: 5 * 6e4 });
|
|
39742
|
+
try {
|
|
39743
|
+
await runFileboxSetVersionWorkflow({
|
|
39744
|
+
client: setup.client,
|
|
39745
|
+
docId,
|
|
39746
|
+
versionId,
|
|
39747
|
+
status: status2,
|
|
39748
|
+
asJson: opts.json === true,
|
|
39749
|
+
signal: setup.signal,
|
|
39750
|
+
out: process.stdout,
|
|
39751
|
+
err: process.stderr
|
|
39752
|
+
});
|
|
39753
|
+
} finally {
|
|
39754
|
+
setup.cleanup();
|
|
39755
|
+
}
|
|
39565
39756
|
}
|
|
39566
|
-
|
|
39757
|
+
);
|
|
39567
39758
|
addServerFlag(setVersionWorkflowCmd);
|
|
39568
39759
|
addAccountFlag(setVersionWorkflowCmd);
|
|
39569
39760
|
const setVersionFileStatusCmd = fb.command("set-version-file-status").description(
|
|
@@ -56637,7 +56828,7 @@ async function readHiddenRulesPayload(path12) {
|
|
|
56637
56828
|
raw.forEach((entry, idx) => {
|
|
56638
56829
|
const ctx = `rules[${idx}]`;
|
|
56639
56830
|
const base = validateCreate(entry, ctx);
|
|
56640
|
-
if (!
|
|
56831
|
+
if (!isPlainObject3(entry)) {
|
|
56641
56832
|
throw new Error(`--file: ${ctx} must be a JSON object`);
|
|
56642
56833
|
}
|
|
56643
56834
|
const ctxnId = entry["customer_transaction_id"];
|
|
@@ -56696,11 +56887,11 @@ function parseReorderShorthand(raw) {
|
|
|
56696
56887
|
}
|
|
56697
56888
|
return { rules: entries };
|
|
56698
56889
|
}
|
|
56699
|
-
function
|
|
56890
|
+
function isPlainObject3(v) {
|
|
56700
56891
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
56701
56892
|
}
|
|
56702
56893
|
function validateCreate(raw, ctx) {
|
|
56703
|
-
if (!
|
|
56894
|
+
if (!isPlainObject3(raw)) {
|
|
56704
56895
|
throw new Error(`--file: ${ctx} must be a JSON object`);
|
|
56705
56896
|
}
|
|
56706
56897
|
if (!Array.isArray(raw["conditions"]) || raw["conditions"].length === 0) {
|
|
@@ -56756,7 +56947,7 @@ function validateCreate(raw, ctx) {
|
|
|
56756
56947
|
return out;
|
|
56757
56948
|
}
|
|
56758
56949
|
function validateCondition(raw, ctx) {
|
|
56759
|
-
if (!
|
|
56950
|
+
if (!isPlainObject3(raw)) {
|
|
56760
56951
|
throw new Error(`--file: ${ctx} must be a JSON object`);
|
|
56761
56952
|
}
|
|
56762
56953
|
const inputField = raw["input_field"];
|
|
@@ -56821,7 +57012,7 @@ function validateArgsForOperation(op, args, ctx) {
|
|
|
56821
57012
|
}
|
|
56822
57013
|
}
|
|
56823
57014
|
function validateReorder(raw, ctx) {
|
|
56824
|
-
if (!
|
|
57015
|
+
if (!isPlainObject3(raw)) {
|
|
56825
57016
|
throw new Error(`--file: ${ctx} must be a JSON object`);
|
|
56826
57017
|
}
|
|
56827
57018
|
if (!Array.isArray(raw["rules"]) || raw["rules"].length === 0) {
|
|
@@ -56830,7 +57021,7 @@ function validateReorder(raw, ctx) {
|
|
|
56830
57021
|
const seenIds = /* @__PURE__ */ new Set();
|
|
56831
57022
|
const entries = [];
|
|
56832
57023
|
raw["rules"].forEach((entry, idx) => {
|
|
56833
|
-
if (!
|
|
57024
|
+
if (!isPlainObject3(entry)) {
|
|
56834
57025
|
throw new Error(`--file: ${ctx}.rules[${idx}] must be a JSON object`);
|
|
56835
57026
|
}
|
|
56836
57027
|
const id = entry["id"];
|
package/dist/scopes-map.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
|
-
"cliVersion": "1.6.
|
|
3
|
+
"cliVersion": "1.6.123",
|
|
4
4
|
"_contract": "Resolve a command path through `aliases` (canonical prefix + alias segment -> canonical path) before any lookup. A path listed in `pendingCommands`, or whose first segment is a key of `pending`, is UNDECIDED, not unrestricted: fail closed or skip it, and never read a path's absence from `commands` as 'no requirement'. An entry carrying `denied` is UNAVAILABLE to any AGENT-SCOPED credential — one whose `scopes` claim carries at least one `resource:action` entry; a claim that is absent, empty, capability-only or wholly unparseable is UNRESTRICTED, and this mark does not apply to it — whatever else that entry says: it is not a ceiling and it is not always-available, its `denied.reason` says why and `denied.until` names the ticket that lifts it, and the absence of `requires` on it must NEVER be read as 'no requirement'. Only an entry in `commands` with an empty `requires` means always-available.",
|
|
5
5
|
"commands": {
|
|
6
6
|
"login": {
|
|
@@ -26,7 +26,8 @@ the matching subcommand. `datamappers create <flat|dimension|multiple|from-file>
|
|
|
26
26
|
`--version` — `--version` is reserved by the global `dr --version` and silently
|
|
27
27
|
no-ops the subcommand.
|
|
28
28
|
- `dimension` adds required `--dim Coord=Label` (≥1).
|
|
29
|
-
- `multiple` adds required `--dim` and `--table-id-cell Coord=Label` (
|
|
29
|
+
- `multiple` adds required `--dim` and one `--table-id-cell Coord=Label` (the
|
|
30
|
+
server allows a single table anchor; a second one is refused).
|
|
30
31
|
- `from-file --file <path>` is the power mode — it ships the JSON body without
|
|
31
32
|
the convenience-flag validation, but **not** strictly verbatim. Three things
|
|
32
33
|
still rewrite it: `--rescan`/`--no-rescan` set or strip `rescan`; a source
|
|
@@ -35,6 +36,27 @@ the matching subcommand. `datamappers create <flat|dimension|multiple|from-file>
|
|
|
35
36
|
Required body fields: `name`, `document_version_id`, `selected_document_ids`
|
|
36
37
|
(GET returns this renamed as `mapped_documents_ids`), `config`, `sheet_name`.
|
|
37
38
|
|
|
39
|
+
**`config` shape: `table_id` is not the table id (DR-54216).** `config.table_id`
|
|
40
|
+
is the table *anchor*: `[]` for none (what `datamappers get --json` emits), or a
|
|
41
|
+
list with exactly one cell object, e.g.
|
|
42
|
+
`[{"coordinate": "A1", "value": "<text in that cell>", "match_type": "any_text"}]`.
|
|
43
|
+
The table id goes on the command line. The server does not check `config`, so a
|
|
44
|
+
wrong type gets a bare HTTP 500 on create and "Unknown error while processing
|
|
45
|
+
data for preview" on preview, with no field named. `create` (payload-file /
|
|
46
|
+
from-file), `update` and `preview` therefore check these keys before sending and
|
|
47
|
+
fail naming the field:
|
|
48
|
+
|
|
49
|
+
| Key | Must be |
|
|
50
|
+
|-----|---------|
|
|
51
|
+
| `table_id` | array of at most one object, with a `match_type` string |
|
|
52
|
+
| `header`, `dimension`, `footer`, `attributes` | array of cell objects |
|
|
53
|
+
| `calculated_fields`, `fx_conversions` | array of objects |
|
|
54
|
+
| `include_sheets` | array |
|
|
55
|
+
|
|
56
|
+
An absent key is fine; `null`, a number, a string or a list of strings is not.
|
|
57
|
+
Writing a body from scratch, start from `datamappers get <mapper_id> --json` of a
|
|
58
|
+
similar mapper.
|
|
59
|
+
|
|
38
60
|
**Ordinal: mappers that share one are versions of one mapper.** Each mapper has
|
|
39
61
|
an `ordinal` (the ORDINAL column of `datamappers list --table <id>`). The server
|
|
40
62
|
binds documents per (table, ordinal): binding a mapper to a document moves that
|
|
@@ -170,8 +170,16 @@ set-version-include` (below).
|
|
|
170
170
|
control characters, and not `auto-calculated` (reserved by the server for
|
|
171
171
|
versions it generates); pass the label as one quoted argument. Needs change
|
|
172
172
|
rights on the filebox or to be the version's uploader (HTTP 405 otherwise).
|
|
173
|
-
- `filebox set-version-workflow <vid> --doc <id> --status <Not Started|In Progress|Pending|Approved|Rejected
|
|
174
|
-
— set the workflow LIST-tag value (setting `Approved` requires
|
|
173
|
+
- `filebox set-version-workflow <vid> --doc <id> --status <Not Started|In Progress|Pending|Approved|Rejected>
|
|
174
|
+
[--json]` — set the workflow LIST-tag value (setting `Approved` requires
|
|
175
|
+
`approve_workflow`). Like `set-version-file-status` below, it sends the
|
|
176
|
+
version's **full tag set** with WORKFLOW replaced, and reads the version back:
|
|
177
|
+
the WORKFLOW value is checked, and `date_tag_value` is re-applied once and
|
|
178
|
+
reported exactly as that command does (`date_tag_value`,
|
|
179
|
+
`date_tag_value_preserved`, `verified`, `reapplied` and `previous` in
|
|
180
|
+
`--json`). A WORKFLOW-only body (CLI releases before DR-55186) left the
|
|
181
|
+
version's `date_tag_value` null while `tags` still held the month; repair such
|
|
182
|
+
a version the way the `set-version-file-status` entry describes.
|
|
175
183
|
- `filebox set-version-file-status <vid> --doc <id> --status <Draft|Final> [--json]`
|
|
176
184
|
— set the File Status LIST-tag value (other tags untouched). This is how a
|
|
177
185
|
version uploaded with `upload-version` (always Draft) is made Final without the
|
|
@@ -226,6 +234,11 @@ set-version-include` (below).
|
|
|
226
234
|
tag's `sub_type`/name; a forecast filebox's `Forecast (New)` tag has none, so
|
|
227
235
|
only the tag's presence is checked there. The scenario tag is compared
|
|
228
236
|
exactly.
|
|
237
|
+
It sends those two tags only, and the server rebuilds the version's
|
|
238
|
+
dimension value from them. That is right when the filebox's dimension tags
|
|
239
|
+
are exactly the date and the scenario, as on the planning fileboxes this
|
|
240
|
+
command is for; a third dimension tag would drop out of the dimension.
|
|
241
|
+
`upload-version --date --scenario` writes the same two-tag body.
|
|
229
242
|
On an upload the confirmed row is also cross-checked against the upload
|
|
230
243
|
response (version number, name, filebox) — if the response only carried a
|
|
231
244
|
`latest_version_id` the cascade had already moved, the tag went to the wrong
|