@datarails-ai/dr-appspace-cli 0.10.4 → 0.10.6
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 +2 -0
- package/dist/cli.js +72 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -85,6 +85,8 @@ A dashboard can be **assembled** rather than typed. `compile --draft
|
|
|
85
85
|
(`--skeleton <file>`, or the draft's own `skeleton` key) and one SPEC draft
|
|
86
86
|
per page — each card's `props` and SPEC `row`, plus its `page`, `reads`,
|
|
87
87
|
`datasets` and `capabilities` — and then compiles it like any other. The
|
|
88
|
+
app's `name` and `description` (the line under the name on its card, which
|
|
89
|
+
kit rule R1.5 requires) are the skeleton's `app`, as written. The rest of the
|
|
88
90
|
frame is the CLI's: the first page is `ui/index.html` at `/`, every other
|
|
89
91
|
`ui/<name>.html` at `/<name>.html`, the skeleton's sections and card order,
|
|
90
92
|
a page not built yet carries one card saying it comes next, and — only
|
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.10.
|
|
16
|
+
version: "0.10.6",
|
|
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-09-
|
|
60
|
+
var BUILD_TIME = "2026-09-30T16:43:46.444Z";
|
|
61
61
|
function formatBuildTime(iso) {
|
|
62
62
|
if (iso === "") return "";
|
|
63
63
|
const d = new Date(iso);
|
|
@@ -3531,9 +3531,10 @@ function canonical(value) {
|
|
|
3531
3531
|
return JSON.stringify(value) ?? "null";
|
|
3532
3532
|
}
|
|
3533
3533
|
function frame(skeleton, pages, appId) {
|
|
3534
|
+
const described = skeleton.app.description === void 0 ? {} : { description: skeleton.app.description };
|
|
3534
3535
|
return {
|
|
3535
3536
|
v: 1,
|
|
3536
|
-
app: { id: appId, name: skeleton.app.name, kind: "dashboard", routes: pages.map((plan) => plan.address) },
|
|
3537
|
+
app: { id: appId, name: skeleton.app.name, ...described, kind: "dashboard", routes: pages.map((plan) => plan.address) },
|
|
3537
3538
|
pages: pages.map(pageFrom),
|
|
3538
3539
|
elements: {},
|
|
3539
3540
|
data: { source: "static", widgets: {} }
|
|
@@ -4936,6 +4937,9 @@ function appSection(app, manifest) {
|
|
|
4936
4937
|
return {
|
|
4937
4938
|
id: manifest.id ?? null,
|
|
4938
4939
|
name: manifest.name ?? null,
|
|
4940
|
+
// What the app does, in the line under its name on the card Studio lists
|
|
4941
|
+
// it on. Read as written: the verifier, not the reader, judges the words.
|
|
4942
|
+
description: typeof manifest.description === "string" ? manifest.description : null,
|
|
4939
4943
|
kind: manifest.kind ?? null,
|
|
4940
4944
|
// What the app is called in the nav, which is not always what it is
|
|
4941
4945
|
// called: `name` is the title of the page and `nav.label` is the word
|
|
@@ -6186,9 +6190,13 @@ var EXPECT_EMPTY = "empty";
|
|
|
6186
6190
|
var EXPECTATIONS = /* @__PURE__ */ new Set([EXPECT_FIGURE, EXPECT_EMPTY]);
|
|
6187
6191
|
var FIGURE = {
|
|
6188
6192
|
series: (body) => Array.isArray(body.series) && body.series.some((entry) => Array.isArray(entry?.values) && entry.values.some(isNumber)),
|
|
6189
|
-
|
|
6193
|
+
// A container of labels with nothing beside them draws no figure either:
|
|
6194
|
+
// seven departments down a variance table with every cell null is the same
|
|
6195
|
+
// blank card as no rows at all (dev-1, 2026-09-30, `actual - budget` where
|
|
6196
|
+
// neither was a measure of the dataset), and it passed here as "rows > 0".
|
|
6197
|
+
breakdown: (body) => Array.isArray(body.items) && body.items.some((item) => isNumber(item?.value)),
|
|
6190
6198
|
kpi: (body) => isNumber(body.value),
|
|
6191
|
-
table: (body) => Array.isArray(body.rows) && body.rows.
|
|
6199
|
+
table: (body) => Array.isArray(body.rows) && body.rows.some((row) => isPlainObject2(row) && Object.values(row).some(isNumber)),
|
|
6192
6200
|
ohlc: (body) => Array.isArray(body.candles) && body.candles.length > 0,
|
|
6193
6201
|
scatter: (body) => Array.isArray(body.points) && body.points.length > 0,
|
|
6194
6202
|
// A narrative in AGENT mode has no prose to carry: the words arrive from the
|
|
@@ -7853,6 +7861,7 @@ var contract_schema_default = {
|
|
|
7853
7861
|
properties: {
|
|
7854
7862
|
id: { type: ["string", "null"] },
|
|
7855
7863
|
name: { type: ["string", "null"] },
|
|
7864
|
+
description: { type: ["string", "null"], description: "manifest.description - what the app does, in one sentence: the line under its name on the card Studio lists it on. OPTIONAL: a contract written before this key existed omits it." },
|
|
7856
7865
|
kind: { type: ["string", "null"], description: "dashboard, app, or absent" },
|
|
7857
7866
|
nav_label: { type: ["string", "null"], description: "manifest.nav.label - what the app is called in the nav, which is not always manifest.name. OPTIONAL: a contract written before this key existed omits it." },
|
|
7858
7867
|
version: { type: ["string", "null"] },
|
|
@@ -11663,6 +11672,8 @@ function manifestFile(contract, handlerName) {
|
|
|
11663
11672
|
apiVersion: app.api_version ?? "appspace/v1",
|
|
11664
11673
|
id: app.id ?? null,
|
|
11665
11674
|
name: app.name ?? null,
|
|
11675
|
+
// The line under the name on the dashboard's card, as `compileApp` writes an app's.
|
|
11676
|
+
...typeof app.description === "string" ? { description: app.description } : {},
|
|
11666
11677
|
version: app.version ?? "0.1.0",
|
|
11667
11678
|
kind: "dashboard",
|
|
11668
11679
|
...app.nav_label ? { nav: { label: app.nav_label } } : {},
|
|
@@ -15734,6 +15745,10 @@ function manifestOf(contract, app, handlers, names, model, served = []) {
|
|
|
15734
15745
|
apiVersion: app.api_version ?? DEFAULT_API_VERSION,
|
|
15735
15746
|
id: app.id ?? null,
|
|
15736
15747
|
name: app.name ?? app.id ?? null,
|
|
15748
|
+
// The line under the name on the app's card. Any string is written, an
|
|
15749
|
+
// empty one too, so the verifier says "blank" and the round trip holds;
|
|
15750
|
+
// a contract that states none writes the manifest it always did.
|
|
15751
|
+
...typeof app.description === "string" ? { description: app.description } : {},
|
|
15737
15752
|
version: app.version ?? DEFAULT_VERSION
|
|
15738
15753
|
};
|
|
15739
15754
|
if (named4(app.nav_label)) manifest.nav = { label: app.nav_label };
|
|
@@ -17405,6 +17420,27 @@ function isReferenced2(name, frontendText) {
|
|
|
17405
17420
|
return new RegExp(`(['"\`])${escaped}\\1|/${escaped}\\b`).test(frontendText);
|
|
17406
17421
|
}
|
|
17407
17422
|
|
|
17423
|
+
// ../flows/insights-builder/kit/checks/lib/description.mjs
|
|
17424
|
+
var DESCRIPTION_MAX = 120;
|
|
17425
|
+
var SAYS_NOTHING = "so the app's card in Studio says nothing under its name";
|
|
17426
|
+
var WRITE = `Write app.description in the contract: one sentence on what the app does, for the card; no newline, and at most ${DESCRIPTION_MAX} characters`;
|
|
17427
|
+
var LINE_BREAK = /[\n\r\u2028\u2029]/;
|
|
17428
|
+
function descriptionProblems(description) {
|
|
17429
|
+
if (description === void 0 || description === null) return [`manifest.description is missing, ${SAYS_NOTHING}. ${WRITE}`];
|
|
17430
|
+
if (typeof description !== "string") return [`manifest.description is ${JSON.stringify(description)}, not text, ${SAYS_NOTHING}. ${WRITE}`];
|
|
17431
|
+
const shown2 = description.trim().split(/\s+/).join(" ");
|
|
17432
|
+
if (shown2 === "") return [`manifest.description is blank, ${SAYS_NOTHING}. ${WRITE}`];
|
|
17433
|
+
const problems = [];
|
|
17434
|
+
if (LINE_BREAK.test(description)) {
|
|
17435
|
+
problems.push(`manifest.description breaks onto a second line, and the card in Studio shows one paragraph. ${WRITE}`);
|
|
17436
|
+
}
|
|
17437
|
+
const characters = [...shown2].length;
|
|
17438
|
+
if (characters > DESCRIPTION_MAX) {
|
|
17439
|
+
problems.push(`manifest.description is ${characters} characters, and the card in Studio has room for ${DESCRIPTION_MAX} (three lines). ${WRITE}`);
|
|
17440
|
+
}
|
|
17441
|
+
return problems;
|
|
17442
|
+
}
|
|
17443
|
+
|
|
17408
17444
|
// ../flows/insights-builder/kit/checks/rules/manifest.mjs
|
|
17409
17445
|
var DASHBOARD_CAPABILITIES = /* @__PURE__ */ new Set(["financeos", "state"]);
|
|
17410
17446
|
var FINANCEOS_READ_SCOPE = /^(metrics|semantic|tables|status|filebox|raw):read$/;
|
|
@@ -17418,6 +17454,7 @@ function checkManifest(app) {
|
|
|
17418
17454
|
if (dataModel !== null || manifest.dataModel !== void 0 || manifest.data_model !== void 0) {
|
|
17419
17455
|
violations.push({ rule: "R1.1", where: where2, message: "a dashboard declares no data_model: every number is read live from FinanceOS" });
|
|
17420
17456
|
}
|
|
17457
|
+
for (const message of descriptionProblems(manifest.description)) violations.push({ rule: "R1.5", where: where2, message });
|
|
17421
17458
|
const capabilities = manifest.capabilities ?? {};
|
|
17422
17459
|
if (typeof capabilities !== "object" || Array.isArray(capabilities)) {
|
|
17423
17460
|
violations.push({ rule: "R1.1", where: where2, message: "capabilities must be an object" });
|
|
@@ -18883,6 +18920,9 @@ function appSection2(app, manifest) {
|
|
|
18883
18920
|
return {
|
|
18884
18921
|
id: manifest.id ?? null,
|
|
18885
18922
|
name: manifest.name ?? null,
|
|
18923
|
+
// What the app does, in the line under its name on the card Studio lists
|
|
18924
|
+
// it on. Read as written: the verifier, not the reader, judges the words.
|
|
18925
|
+
description: typeof manifest.description === "string" ? manifest.description : null,
|
|
18886
18926
|
kind: manifest.kind ?? null,
|
|
18887
18927
|
// What the app is called in the nav, which is not always what it is
|
|
18888
18928
|
// called: `name` is the title of the page and `nav.label` is the word
|
|
@@ -21595,6 +21635,27 @@ function checkLook(app, contract, contractError) {
|
|
|
21595
21635
|
return { violations, skipped: [] };
|
|
21596
21636
|
}
|
|
21597
21637
|
|
|
21638
|
+
// ../flows/appspace-builder/kit/checks/lib/description.mjs
|
|
21639
|
+
var DESCRIPTION_MAX2 = 120;
|
|
21640
|
+
var SAYS_NOTHING2 = "so the app's card in Studio says nothing under its name";
|
|
21641
|
+
var WRITE2 = `Write app.description in the contract: one sentence on what the app does, for the card; no newline, and at most ${DESCRIPTION_MAX2} characters`;
|
|
21642
|
+
var LINE_BREAK2 = /[\n\r\u2028\u2029]/;
|
|
21643
|
+
function descriptionProblems2(description) {
|
|
21644
|
+
if (description === void 0 || description === null) return [`manifest.description is missing, ${SAYS_NOTHING2}. ${WRITE2}`];
|
|
21645
|
+
if (typeof description !== "string") return [`manifest.description is ${JSON.stringify(description)}, not text, ${SAYS_NOTHING2}. ${WRITE2}`];
|
|
21646
|
+
const shown2 = description.trim().split(/\s+/).join(" ");
|
|
21647
|
+
if (shown2 === "") return [`manifest.description is blank, ${SAYS_NOTHING2}. ${WRITE2}`];
|
|
21648
|
+
const problems = [];
|
|
21649
|
+
if (LINE_BREAK2.test(description)) {
|
|
21650
|
+
problems.push(`manifest.description breaks onto a second line, and the card in Studio shows one paragraph. ${WRITE2}`);
|
|
21651
|
+
}
|
|
21652
|
+
const characters = [...shown2].length;
|
|
21653
|
+
if (characters > DESCRIPTION_MAX2) {
|
|
21654
|
+
problems.push(`manifest.description is ${characters} characters, and the card in Studio has room for ${DESCRIPTION_MAX2} (three lines). ${WRITE2}`);
|
|
21655
|
+
}
|
|
21656
|
+
return problems;
|
|
21657
|
+
}
|
|
21658
|
+
|
|
21598
21659
|
// ../flows/appspace-builder/kit/checks/rules/manifest.mjs
|
|
21599
21660
|
function checkManifest2(app) {
|
|
21600
21661
|
const violations = [];
|
|
@@ -21603,6 +21664,7 @@ function checkManifest2(app) {
|
|
|
21603
21664
|
if (typeof manifest.name !== "string" || !manifest.name.trim()) {
|
|
21604
21665
|
violations.push({ rule: "A1.1", where: where2, message: "manifest.name is empty; the app is listed under its id" });
|
|
21605
21666
|
}
|
|
21667
|
+
for (const message of descriptionProblems2(manifest.description)) violations.push({ rule: "A1.7", where: where2, message });
|
|
21606
21668
|
if (manifest.kind === "dashboard") {
|
|
21607
21669
|
violations.push({ rule: "A1.2", where: where2, message: 'manifest.kind is "dashboard"; dashboards are built by the Insights Builder and its kit, not here' });
|
|
21608
21670
|
}
|
|
@@ -22537,7 +22599,7 @@ var kit_default = {
|
|
|
22537
22599
|
// ../flows/appspace-builder/kit/kit.json
|
|
22538
22600
|
var kit_default2 = {
|
|
22539
22601
|
name: "appspace-app-kit",
|
|
22540
|
-
description: "The checks the App Builder runs on every pushed version of a regular App Space app: a manifest that names the app, handlers that exist and take (ctx, payload), a page that calls only handlers that exist, no handler nothing reaches, roles declared wherever a handler reads ctx.role, and the component vocabulary held to the data model \u2014 every data-dr-action a declared handler, every data-dr-entity a table, every data-dr-field one of its columns.",
|
|
22602
|
+
description: "The checks the App Builder runs on every pushed version of a regular App Space app: a manifest that names the app and says in a line what it does, handlers that exist and take (ctx, payload), a page that calls only handlers that exist, no handler nothing reaches, roles declared wherever a handler reads ctx.role, and the component vocabulary held to the data model \u2014 every data-dr-action a declared handler, every data-dr-entity a table, every data-dr-field one of its columns.",
|
|
22541
22603
|
checks: "checks/run.mjs",
|
|
22542
22604
|
contract: "contract/extract.mjs",
|
|
22543
22605
|
charts: "echarts@5.6.0",
|
|
@@ -29511,6 +29573,8 @@ function dashboardStarter(values) {
|
|
|
29511
29573
|
app: {
|
|
29512
29574
|
id: values.id,
|
|
29513
29575
|
name: values.name,
|
|
29576
|
+
// What the card Studio lists the dashboard on says under its name.
|
|
29577
|
+
description: "What this dashboard answers, in one sentence its reader would use.",
|
|
29514
29578
|
kind: "dashboard",
|
|
29515
29579
|
nav_label: values.name,
|
|
29516
29580
|
routes: ["/"],
|
|
@@ -29597,6 +29661,8 @@ function appStarter(values) {
|
|
|
29597
29661
|
app: {
|
|
29598
29662
|
id: values.id,
|
|
29599
29663
|
name: values.name,
|
|
29664
|
+
// What the card Studio lists the app on says under its name.
|
|
29665
|
+
description: `Raise a ${record2} and follow it until it is done.`,
|
|
29600
29666
|
nav_label: values.name,
|
|
29601
29667
|
routes: ["/"],
|
|
29602
29668
|
capabilities: { data: ["rw"] },
|