@objectstack/lint 14.8.0 → 15.0.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/index.cjs +28 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -7
- package/dist/index.d.ts +29 -7
- package/dist/index.js +28 -11
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -1294,10 +1294,22 @@ function predicateSource(v) {
|
|
|
1294
1294
|
}
|
|
1295
1295
|
return void 0;
|
|
1296
1296
|
}
|
|
1297
|
-
function
|
|
1298
|
-
return
|
|
1297
|
+
function usesRoot(source, root) {
|
|
1298
|
+
return new RegExp(`(^|[^.\\w$])${root}\\.\\w`).test(source);
|
|
1299
1299
|
}
|
|
1300
|
-
|
|
1300
|
+
var MISLAYER_BY_LAYER = {
|
|
1301
|
+
runtime: {
|
|
1302
|
+
forbiddenRoot: "data",
|
|
1303
|
+
message: "visibility predicate is rooted at `data.` \u2014 that is the metadata-editing-form root (a `*.form.ts` row under edit), not a runtime surface. A runtime view/page predicate that binds `data.` never matches and the element renders unconditionally (ADR-0089).",
|
|
1304
|
+
hint: "Runtime record surfaces bind `record` + `current_user` (pages also expose `page.<var>`). Use e.g. `record.status == 'open'` instead of `data.status == 'open'`."
|
|
1305
|
+
},
|
|
1306
|
+
metadata: {
|
|
1307
|
+
forbiddenRoot: "record",
|
|
1308
|
+
message: "visibility predicate is rooted at `record.` \u2014 that is the runtime record-surface root (a `*.view.ts` / `*.page.ts` live record), not a metadata-editing form. A `*.form.ts` predicate that binds `record.` never matches and the element renders unconditionally (ADR-0089).",
|
|
1309
|
+
hint: "Metadata-editing forms bind `data` (the row under edit). Use e.g. `data.type == 'grid'` instead of `record.type == 'grid'`."
|
|
1310
|
+
}
|
|
1311
|
+
};
|
|
1312
|
+
function checkElement(el, where, path, layer, findings) {
|
|
1301
1313
|
for (const alias of ALIASES) {
|
|
1302
1314
|
if (el[alias] !== void 0) {
|
|
1303
1315
|
findings.push({
|
|
@@ -1312,21 +1324,23 @@ function checkElement(el, where, path, findings) {
|
|
|
1312
1324
|
}
|
|
1313
1325
|
const raw = el[CANONICAL] ?? el.visibleOn ?? el.visibility;
|
|
1314
1326
|
const source = predicateSource(raw);
|
|
1315
|
-
|
|
1327
|
+
const rule = MISLAYER_BY_LAYER[layer];
|
|
1328
|
+
if (source && usesRoot(source, rule.forbiddenRoot)) {
|
|
1316
1329
|
findings.push({
|
|
1317
1330
|
severity: "warning",
|
|
1318
1331
|
rule: VISIBILITY_ROOT_MISLAYERED,
|
|
1319
1332
|
where,
|
|
1320
1333
|
path,
|
|
1321
|
-
message:
|
|
1322
|
-
hint:
|
|
1334
|
+
message: rule.message,
|
|
1335
|
+
hint: rule.hint
|
|
1323
1336
|
});
|
|
1324
1337
|
}
|
|
1325
1338
|
}
|
|
1326
1339
|
function isFieldObject(entry) {
|
|
1327
1340
|
return !!entry && typeof entry === "object" && !Array.isArray(entry);
|
|
1328
1341
|
}
|
|
1329
|
-
function validateVisibilityPredicates(stack) {
|
|
1342
|
+
function validateVisibilityPredicates(stack, opts = {}) {
|
|
1343
|
+
const layer = opts.layer ?? "runtime";
|
|
1330
1344
|
const findings = [];
|
|
1331
1345
|
const views = asArray12(stack.views);
|
|
1332
1346
|
for (let i = 0; i < views.length; i++) {
|
|
@@ -1340,12 +1354,12 @@ function validateVisibilityPredicates(stack) {
|
|
|
1340
1354
|
const sec = sections[s];
|
|
1341
1355
|
if (!sec || typeof sec !== "object") continue;
|
|
1342
1356
|
const secPath = `views[${i}].${bucket}[${s}]`;
|
|
1343
|
-
checkElement(sec, where, secPath, findings);
|
|
1357
|
+
checkElement(sec, where, secPath, layer, findings);
|
|
1344
1358
|
const secFields = Array.isArray(sec.fields) ? sec.fields : [];
|
|
1345
1359
|
for (let f = 0; f < secFields.length; f++) {
|
|
1346
1360
|
const entry = secFields[f];
|
|
1347
1361
|
if (isFieldObject(entry)) {
|
|
1348
|
-
checkElement(entry, where, `${secPath}.fields[${f}]`, findings);
|
|
1362
|
+
checkElement(entry, where, `${secPath}.fields[${f}]`, layer, findings);
|
|
1349
1363
|
}
|
|
1350
1364
|
}
|
|
1351
1365
|
}
|
|
@@ -1364,7 +1378,7 @@ function validateVisibilityPredicates(stack) {
|
|
|
1364
1378
|
for (let c = 0; c < components.length; c++) {
|
|
1365
1379
|
const comp = components[c];
|
|
1366
1380
|
if (comp && typeof comp === "object") {
|
|
1367
|
-
checkElement(comp, where, `pages[${i}].regions[${r}].components[${c}]`, findings);
|
|
1381
|
+
checkElement(comp, where, `pages[${i}].regions[${r}].components[${c}]`, layer, findings);
|
|
1368
1382
|
}
|
|
1369
1383
|
}
|
|
1370
1384
|
}
|
|
@@ -1400,6 +1414,9 @@ function validateCapabilityReferences(stack) {
|
|
|
1400
1414
|
const findings = [];
|
|
1401
1415
|
if (!stack || typeof stack !== "object") return findings;
|
|
1402
1416
|
const known = new Set(import_security.PLATFORM_CAPABILITY_NAMES);
|
|
1417
|
+
for (const cap of asArray13(stack.capabilities)) {
|
|
1418
|
+
if (typeof cap.name === "string" && cap.name.length > 0) known.add(cap.name);
|
|
1419
|
+
}
|
|
1403
1420
|
for (const ps of asArray13(stack.permissions)) {
|
|
1404
1421
|
for (const cap of asCapArray(ps.systemPermissions)) known.add(cap);
|
|
1405
1422
|
}
|
|
@@ -1410,7 +1427,7 @@ function validateCapabilityReferences(stack) {
|
|
|
1410
1427
|
if (typeof name === "string" && name.length > 0) known.add(name);
|
|
1411
1428
|
}
|
|
1412
1429
|
}
|
|
1413
|
-
const hint = "Fix the capability name, declare it on a permission set\u2019s systemPermissions, ship a sys_capability seed row, or ignore this if the capability is provided by another installed package (references fail closed at runtime).";
|
|
1430
|
+
const hint = "Fix the capability name, define it with defineCapability (stack.capabilities), declare it on a permission set\u2019s systemPermissions, ship a sys_capability seed row, or ignore this if the capability is provided by another installed package (references fail closed at runtime).";
|
|
1414
1431
|
const flag = (cap, where, path) => {
|
|
1415
1432
|
if (known.has(cap)) return;
|
|
1416
1433
|
findings.push({
|