@datarails-ai/dr-appspace-cli 0.10.0 → 0.10.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.
- package/dist/cli.js +69 -8
- 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.10.
|
|
16
|
+
version: "0.10.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-09-
|
|
60
|
+
var BUILD_TIME = "2026-09-30T13:19:16.568Z";
|
|
61
61
|
function formatBuildTime(iso) {
|
|
62
62
|
if (iso === "") return "";
|
|
63
63
|
const d = new Date(iso);
|
|
@@ -24703,6 +24703,11 @@ function judgeInteraction(interaction, calls) {
|
|
|
24703
24703
|
`${call.method} ${call.url} \u2014 ${call.failure} (the flow made this call; the UI runtime catches it and paints a banner, so nothing reaches the console)`
|
|
24704
24704
|
);
|
|
24705
24705
|
}
|
|
24706
|
+
const askedForReason = mine.some((call) => call.needsReason);
|
|
24707
|
+
const reasonCovered = askedForReason && mine.some((call) => call.reasonSupplied === true && call.needsReason !== true && call.failure === void 0 && call.error === void 0 && call.status !== void 0 && call.status >= 200 && call.status < 300);
|
|
24708
|
+
if (askedForReason && !reasonCovered) {
|
|
24709
|
+
problems.push(`${interaction.handler} needs a reason \u2014 no successful retry with a reason was observed.`);
|
|
24710
|
+
}
|
|
24706
24711
|
for (const field of interaction.fields) {
|
|
24707
24712
|
if (field.unknownColumn === true) {
|
|
24708
24713
|
problems.push(
|
|
@@ -24719,6 +24724,7 @@ function judgeInteraction(interaction, calls) {
|
|
|
24719
24724
|
calls,
|
|
24720
24725
|
problems
|
|
24721
24726
|
};
|
|
24727
|
+
if (reasonCovered) outcome.reasonCovered = true;
|
|
24722
24728
|
if (interaction.id !== void 0) outcome.id = interaction.id;
|
|
24723
24729
|
if (interaction.entity !== void 0) outcome.entity = interaction.entity;
|
|
24724
24730
|
const attached = interaction.fields.flatMap(
|
|
@@ -25162,6 +25168,7 @@ function movedBetween(before, after, param, echoes = []) {
|
|
|
25162
25168
|
}
|
|
25163
25169
|
|
|
25164
25170
|
// src/commands/appspace/_uiSmoke.ts
|
|
25171
|
+
var SMOKE_REASON = "App Space smoke test: exercising the required reason flow.";
|
|
25165
25172
|
var NULL_TEXT_CONTEXT = 80;
|
|
25166
25173
|
var NULL_TEXT_LIMIT = 10;
|
|
25167
25174
|
var NULL_LITERAL = /(?<![A-Za-z0-9_])(?:(?:null|undefined|NaN)+|\[object Object\])(?![A-Za-z0-9_])/g;
|
|
@@ -25336,7 +25343,13 @@ function answerOf(res) {
|
|
|
25336
25343
|
}
|
|
25337
25344
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
|
|
25338
25345
|
const out = { object: true };
|
|
25339
|
-
const { rows: rows2, error } = parsed;
|
|
25346
|
+
const { rows: rows2, error, needs } = parsed;
|
|
25347
|
+
if (needs && typeof needs === "object" && !Array.isArray(needs)) {
|
|
25348
|
+
const reason = needs.reason;
|
|
25349
|
+
if (reason === true || reason && typeof reason === "object" && !Array.isArray(reason)) {
|
|
25350
|
+
out.needsReason = true;
|
|
25351
|
+
}
|
|
25352
|
+
}
|
|
25340
25353
|
if (Array.isArray(rows2)) {
|
|
25341
25354
|
out.rows = rows2.length;
|
|
25342
25355
|
const { keys: keys2, numeric } = rowKeys(rows2);
|
|
@@ -25866,6 +25879,13 @@ async function runUiSmoke(opts) {
|
|
|
25866
25879
|
let lastActivity = Date.now();
|
|
25867
25880
|
async function record2(req, via, refuse) {
|
|
25868
25881
|
const call = { method: req.method, url: maskToken(req.url), via };
|
|
25882
|
+
if (via === "fetch" && req.body) {
|
|
25883
|
+
try {
|
|
25884
|
+
const payload = JSON.parse(req.body);
|
|
25885
|
+
if (typeof payload?.reason === "string" && payload.reason.trim()) call.reasonSupplied = true;
|
|
25886
|
+
} catch {
|
|
25887
|
+
}
|
|
25888
|
+
}
|
|
25869
25889
|
const auth = req.headers["authorization"];
|
|
25870
25890
|
if (auth !== void 0) call.authorization = summariseAuthorization(auth);
|
|
25871
25891
|
calls.push(call);
|
|
@@ -25893,12 +25913,13 @@ async function runUiSmoke(opts) {
|
|
|
25893
25913
|
call.status = res.status;
|
|
25894
25914
|
if (res.status >= 400) call.failure = `HTTP ${String(res.status)}`;
|
|
25895
25915
|
else if (via === "fetch") {
|
|
25896
|
-
const { rows: rows2, error, keys: keys2, numeric, object } = answerOf(res);
|
|
25916
|
+
const { rows: rows2, error, keys: keys2, numeric, object, needsReason } = answerOf(res);
|
|
25897
25917
|
if (rows2 !== void 0) call.rows = rows2;
|
|
25898
25918
|
if (error !== void 0) call.error = error;
|
|
25899
25919
|
if (keys2 !== void 0) call.keys = keys2;
|
|
25900
25920
|
if (numeric !== void 0) call.numeric = numeric;
|
|
25901
25921
|
if (object !== void 0) call.object = object;
|
|
25922
|
+
if (needsReason) call.needsReason = true;
|
|
25902
25923
|
}
|
|
25903
25924
|
return res;
|
|
25904
25925
|
} catch (err) {
|
|
@@ -25981,9 +26002,10 @@ async function runUiSmoke(opts) {
|
|
|
25981
26002
|
const displaced = process.listeners("unhandledRejection");
|
|
25982
26003
|
process.removeAllListeners("unhandledRejection");
|
|
25983
26004
|
process.on("unhandledRejection", onUnhandled);
|
|
25984
|
-
async function settle(deadline, since = calls.length, graceMs = 0) {
|
|
26005
|
+
async function settle(deadline, since = calls.length, graceMs = 0, drivePrompt) {
|
|
25985
26006
|
const graceUntil = Date.now() + graceMs;
|
|
25986
26007
|
while (Date.now() < deadline) {
|
|
26008
|
+
if (drivePrompt?.()) lastActivity = Date.now();
|
|
25987
26009
|
const parsed = dom.window.document.readyState === "complete";
|
|
25988
26010
|
const started = calls.length > since || inFlight > 0;
|
|
25989
26011
|
if (!started && Date.now() < graceUntil) {
|
|
@@ -26213,7 +26235,24 @@ async function runUiSmoke(opts) {
|
|
|
26213
26235
|
} catch (err) {
|
|
26214
26236
|
notes = [`firing it threw: ${describeRejection(err)}`];
|
|
26215
26237
|
}
|
|
26216
|
-
|
|
26238
|
+
const answeredPrompts = /* @__PURE__ */ new Set();
|
|
26239
|
+
await settle(Math.min(Date.now() + perInteraction, budgetUntil), since, graceMs, () => {
|
|
26240
|
+
const pending = calls.slice(since).filter((call) => callsHandler(call.url, interaction.handler)).at(-1);
|
|
26241
|
+
if (!pending?.needsReason || answeredPrompts.has(pending) || answeredPrompts.size >= 3) return false;
|
|
26242
|
+
const dialog = dom.window.document.querySelector('[data-test="confirmDialog"]');
|
|
26243
|
+
const reason = dialog?.querySelector('textarea[data-test="confirmReason"]');
|
|
26244
|
+
const confirm = dialog?.querySelector('button[data-test="confirmOk"]');
|
|
26245
|
+
if (!(reason instanceof dom.window.HTMLTextAreaElement) || !(confirm instanceof dom.window.HTMLButtonElement)) return false;
|
|
26246
|
+
if (reason.value !== SMOKE_REASON) {
|
|
26247
|
+
Object.getOwnPropertyDescriptor(dom.window.HTMLTextAreaElement.prototype, "value").set.call(reason, SMOKE_REASON);
|
|
26248
|
+
reason.dispatchEvent(new dom.window.Event("input", { bubbles: true }));
|
|
26249
|
+
return true;
|
|
26250
|
+
}
|
|
26251
|
+
if (confirm.disabled) return false;
|
|
26252
|
+
answeredPrompts.add(pending);
|
|
26253
|
+
confirm.click();
|
|
26254
|
+
return true;
|
|
26255
|
+
});
|
|
26217
26256
|
const outcome = judgeInteraction(
|
|
26218
26257
|
interaction,
|
|
26219
26258
|
calls.slice(since).map((call) => ({ ...call }))
|
|
@@ -27870,6 +27909,26 @@ function cardTitleFindings(contract) {
|
|
|
27870
27909
|
see: SEE.labels
|
|
27871
27910
|
}));
|
|
27872
27911
|
}
|
|
27912
|
+
function uxFindings(contract) {
|
|
27913
|
+
if (contract === null || !isAppContract(contract)) return [];
|
|
27914
|
+
const codes = {
|
|
27915
|
+
"A3.23": "create-form-inline",
|
|
27916
|
+
"A3.24": "row-no-detail",
|
|
27917
|
+
"A3.25": "dialog-no-trigger"
|
|
27918
|
+
};
|
|
27919
|
+
const { violations } = checkUx({}, contract, null);
|
|
27920
|
+
return violations.flatMap((found) => {
|
|
27921
|
+
if (!(found.rule in codes)) return [];
|
|
27922
|
+
return [{
|
|
27923
|
+
code: codes[found.rule],
|
|
27924
|
+
where: found.where,
|
|
27925
|
+
message: found.message,
|
|
27926
|
+
fix: "Apply the kit guidance above to the named element in contract.json and recompile",
|
|
27927
|
+
see: `App kit ${found.rule} (checks/rules/ux.mjs)`,
|
|
27928
|
+
severity: "warning"
|
|
27929
|
+
}];
|
|
27930
|
+
});
|
|
27931
|
+
}
|
|
27873
27932
|
function collectFindings(input) {
|
|
27874
27933
|
const levers = input.bundle === void 0 ? [] : leverFindings(input.contract, input.bundle, input.handlerCode ?? {});
|
|
27875
27934
|
const unread = new Set(levers.filter((one) => one.code === "param-unread").map((one) => one.where));
|
|
@@ -27895,7 +27954,8 @@ function collectFindings(input) {
|
|
|
27895
27954
|
...input.contract === null ? [] : auditFindings(input.contract),
|
|
27896
27955
|
...input.declared === null ? [] : unreachedFindings(input.declared, input.cases, input.contract, input.runs),
|
|
27897
27956
|
// Last: said and never counted, so they never push a counted finding down the list.
|
|
27898
|
-
...workflow.filter((one) => one.severity === "warning")
|
|
27957
|
+
...workflow.filter((one) => one.severity === "warning"),
|
|
27958
|
+
...uxFindings(input.contract)
|
|
27899
27959
|
];
|
|
27900
27960
|
}
|
|
27901
27961
|
function formatFindings(findings) {
|
|
@@ -28390,7 +28450,8 @@ function renderInteractions(report) {
|
|
|
28390
28450
|
if (outcome.problems.length === 0) {
|
|
28391
28451
|
const answered = outcome.calls.find((call) => callsHandler(call.url, outcome.handler));
|
|
28392
28452
|
const status = answered?.status === void 0 ? "" : ` (HTTP ${String(answered.status)})`;
|
|
28393
|
-
|
|
28453
|
+
const reason = outcome.reasonCovered ? " \u2014 needs a reason: covered (canned smoke-test reason)" : "";
|
|
28454
|
+
out += ` ok ${head}${status}${reason}
|
|
28394
28455
|
${attached}`;
|
|
28395
28456
|
continue;
|
|
28396
28457
|
}
|