@datarails-ai/dr-appspace-cli 0.11.4 → 0.12.1

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.
Files changed (2) hide show
  1. package/dist/cli.js +50 -2
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ import { fileURLToPath as fileURLToPath6 } from "url";
13
13
  // package.json
14
14
  var package_default = {
15
15
  name: "@datarails-ai/dr-appspace-cli",
16
- version: "0.11.4",
16
+ version: "0.12.1",
17
17
  description: "Datarails App Space CLI \u2014 build, try, push and publish App Space apps",
18
18
  type: "module",
19
19
  engines: {
@@ -57,7 +57,7 @@ var package_default = {
57
57
  };
58
58
 
59
59
  // src/buildInfo.ts
60
- var BUILD_TIME = "2026-10-07T16:55:45.981Z";
60
+ var BUILD_TIME = "2026-10-07T21:55:46.945Z";
61
61
  function formatBuildTime(iso) {
62
62
  if (iso === "") return "";
63
63
  const d = new Date(iso);
@@ -17549,6 +17549,7 @@ function validateContract(contract, catalog) {
17549
17549
  if (kind === "dashboard") {
17550
17550
  checkPeriodOverride(doc, problems);
17551
17551
  checkKinds(doc, problems);
17552
+ checkDrillTargets(doc, problems);
17552
17553
  checkPairing(doc, problems);
17553
17554
  checkTransforms(doc, problems);
17554
17555
  } else {
@@ -18030,6 +18031,19 @@ function checkSources(doc, catalog, problems, skipped) {
18030
18031
  }
18031
18032
  report(held, ["data", "widgets", id, "source", key]);
18032
18033
  }
18034
+ {
18035
+ const held = source["period_field"];
18036
+ const field = isObject4(held) ? held["field"] : held;
18037
+ const epoch = isObject4(held) && held["format"] === "epoch";
18038
+ if (typeof field === "string" && dated.has(field.toLowerCase()) && source["period_is_date"] !== true && !epoch) {
18039
+ problems.push({
18040
+ code: "period-needs-date",
18041
+ pointer: pointer("data", "widgets", id, "source", "period_field"),
18042
+ message: `${JSON.stringify(field)} is a date column, and this source does not say so \u2014 its window is not sent to FinanceOS, a drill reads rows from every period, and a clicked chart point matches no row.`,
18043
+ fix: `Add "period_is_date": true to the source (next to period_field ${JSON.stringify(field)}); the page's window is then pushed down as Unix epoch seconds, with no "where" range needed.`
18044
+ });
18045
+ }
18046
+ }
18033
18047
  for (const key of ["select", "dimensions", "measures"]) {
18034
18048
  for (const [index, name] of list2(source[key]).entries()) {
18035
18049
  if (isObject4(name)) {
@@ -18361,6 +18375,40 @@ function checkKinds(doc, problems) {
18361
18375
  });
18362
18376
  }
18363
18377
  }
18378
+ function checkDrillTargets(doc, problems) {
18379
+ const widgets = obj(obj(doc["data"])["widgets"]);
18380
+ for (const [id, raw] of Object.entries(widgets)) {
18381
+ if (id.startsWith("_") || !isObject4(raw)) continue;
18382
+ const row = raw;
18383
+ if (row["data"] !== void 0 && row["data"] !== null) continue;
18384
+ const drill = list2(row["drill"]).filter((name) => typeof name === "string");
18385
+ if (drill.length === 0) continue;
18386
+ const offered = new Set(drill.map((name) => name.toLowerCase()));
18387
+ const listed = JSON.stringify(drill);
18388
+ if (row["kind"] === "breakdown") {
18389
+ const dimension = row["dimension"];
18390
+ if (typeof dimension !== "string" || dimension === "") continue;
18391
+ if (offered.has(dimension.toLowerCase())) continue;
18392
+ problems.push({
18393
+ code: "drill-needs-dimension",
18394
+ pointer: pointer("data", "widgets", id, "drill"),
18395
+ message: `this breakdown is cut by ${JSON.stringify(dimension)} and its drill list ${listed} does not name it, so no legend row answers a click \u2014 the card looks drillable and is not.`,
18396
+ fix: `Put ${JSON.stringify(dimension)} first in drill \u2014 ${JSON.stringify([dimension, ...drill])}: the clicked slice is a value of the card's own dimension, and the levels after it are where the grid goes next.`
18397
+ });
18398
+ continue;
18399
+ }
18400
+ if (row["kind"] !== "table") continue;
18401
+ const declared = list2(row["columns"]).map((column) => isObject4(column) ? column["key"] : void 0).filter((key) => typeof key === "string" && key !== "");
18402
+ const keys2 = declared.length > 0 ? declared : list2(obj(row["source"])["dimensions"]).filter((key) => typeof key === "string");
18403
+ if (keys2.length === 0 || keys2.some((key) => offered.has(key.toLowerCase()))) continue;
18404
+ problems.push({
18405
+ code: "drill-needs-dimension",
18406
+ pointer: pointer("data", "widgets", id, "drill"),
18407
+ message: `this table's drill list ${listed} names none of its columns (${keys2.map((key) => JSON.stringify(key)).join(", ")}), and a cell drills only when its column key is in drill \u2014 so no cell answers a click.`,
18408
+ fix: `Add the column a reader clicks to drill \u2014 e.g. ${JSON.stringify([keys2[0], ...drill])} \u2014 naming the grid's own dimension column first.`
18409
+ });
18410
+ }
18411
+ }
18364
18412
  function checkPairing(doc, problems) {
18365
18413
  const data = doc["data"];
18366
18414
  if (!isObject4(data) || !isObject4(data["widgets"])) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datarails-ai/dr-appspace-cli",
3
- "version": "0.11.4",
3
+ "version": "0.12.1",
4
4
  "description": "Datarails App Space CLI — build, try, push and publish App Space apps",
5
5
  "type": "module",
6
6
  "engines": {