@objectstack/lint 11.2.0 → 11.4.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 +93 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -9
- package/dist/index.d.ts +72 -9
- package/dist/index.js +89 -2
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
package/dist/index.d.cts
CHANGED
|
@@ -69,7 +69,7 @@ interface WidgetBindingFinding {
|
|
|
69
69
|
/** How to fix (or deliberately suppress) it. */
|
|
70
70
|
hint: string;
|
|
71
71
|
}
|
|
72
|
-
type AnyRec$
|
|
72
|
+
type AnyRec$5 = Record<string, unknown>;
|
|
73
73
|
/**
|
|
74
74
|
* Validate every dashboard widget's dataset binding. Returns the list of
|
|
75
75
|
* findings (empty = clean). Caller decides how to surface them: `error`
|
|
@@ -77,7 +77,7 @@ type AnyRec$3 = Record<string, unknown>;
|
|
|
77
77
|
* should fail validate/build; `warning` findings are advisory and must
|
|
78
78
|
* never fail the build on their own.
|
|
79
79
|
*/
|
|
80
|
-
declare function validateWidgetBindings(stack: AnyRec$
|
|
80
|
+
declare function validateWidgetBindings(stack: AnyRec$5): WidgetBindingFinding[];
|
|
81
81
|
|
|
82
82
|
interface ExprIssue {
|
|
83
83
|
where: string;
|
|
@@ -90,12 +90,12 @@ interface ExprIssue {
|
|
|
90
90
|
*/
|
|
91
91
|
severity?: 'error' | 'warning';
|
|
92
92
|
}
|
|
93
|
-
type AnyRec$
|
|
93
|
+
type AnyRec$4 = Record<string, unknown>;
|
|
94
94
|
/**
|
|
95
95
|
* Validate every predicate in the stack. Returns the list of issues (empty =
|
|
96
96
|
* clean). Caller decides how to surface / whether to fail the build.
|
|
97
97
|
*/
|
|
98
|
-
declare function validateStackExpressions(stack: AnyRec$
|
|
98
|
+
declare function validateStackExpressions(stack: AnyRec$4): ExprIssue[];
|
|
99
99
|
|
|
100
100
|
type StyleSeverity = 'error' | 'warning';
|
|
101
101
|
interface StyleFinding {
|
|
@@ -113,14 +113,14 @@ declare const STYLE_CLASSNAME_TAILWIND = "style-classname-tailwind";
|
|
|
113
113
|
declare const STYLE_RESPONSIVE_NO_BASE = "style-responsive-no-base";
|
|
114
114
|
declare const STYLE_UNKNOWN_CSS_PROPERTY = "style-unknown-css-property";
|
|
115
115
|
declare const STYLE_UNKNOWN_TOKEN = "style-unknown-token";
|
|
116
|
-
type AnyRec$
|
|
116
|
+
type AnyRec$3 = Record<string, unknown>;
|
|
117
117
|
/**
|
|
118
118
|
* Validate every page's component tree for SDUI styling correctness (ADR-0065).
|
|
119
119
|
* Returns findings (empty = clean). `error` findings describe styles that are
|
|
120
120
|
* silently dropped and should fail validate/build; `warning` findings are
|
|
121
121
|
* advisory (typos, drift, footguns).
|
|
122
122
|
*/
|
|
123
|
-
declare function validateResponsiveStyles(stack: AnyRec$
|
|
123
|
+
declare function validateResponsiveStyles(stack: AnyRec$3): StyleFinding[];
|
|
124
124
|
|
|
125
125
|
type JsxPageSeverity = 'error' | 'warning';
|
|
126
126
|
interface JsxPageFinding {
|
|
@@ -133,9 +133,72 @@ interface JsxPageFinding {
|
|
|
133
133
|
message: string;
|
|
134
134
|
hint: string;
|
|
135
135
|
}
|
|
136
|
-
type AnyRec = Record<string, unknown>;
|
|
137
|
-
declare function validateJsxPages(stack: AnyRec, opts?: {
|
|
136
|
+
type AnyRec$2 = Record<string, unknown>;
|
|
137
|
+
declare function validateJsxPages(stack: AnyRec$2, opts?: {
|
|
138
138
|
manifest?: Manifest;
|
|
139
139
|
}): JsxPageFinding[];
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
type ReactPageSeverity = 'error' | 'warning';
|
|
142
|
+
interface ReactPageFinding {
|
|
143
|
+
severity: ReactPageSeverity;
|
|
144
|
+
rule: string;
|
|
145
|
+
where: string;
|
|
146
|
+
path: string;
|
|
147
|
+
message: string;
|
|
148
|
+
hint: string;
|
|
149
|
+
}
|
|
150
|
+
type AnyRec$1 = Record<string, unknown>;
|
|
151
|
+
declare function validateReactPages(stack: AnyRec$1): ReactPageFinding[];
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Build-time record-title diagnostics (ADR-0079).
|
|
155
|
+
*
|
|
156
|
+
* A record's human title is a structural invariant: every object resolves a
|
|
157
|
+
* primary title from a real STORED field via `nameField` (the canonical
|
|
158
|
+
* pointer; `displayNameField` is the deprecated alias) or a deterministic
|
|
159
|
+
* derivation. Two authoring smells are flagged here so `os build`/`os lint`,
|
|
160
|
+
* the MCP authoring surface, and hand-authoring all get the coverage cloud
|
|
161
|
+
* graph-lint already has (the ADR-0078 "not cloud-only" principle):
|
|
162
|
+
*
|
|
163
|
+
* - `title-format-retired` — the object declares a `titleFormat`. That field
|
|
164
|
+
* is a RENDER-ONLY template the server can neither return nor query; ADR-0079
|
|
165
|
+
* retires it in favour of `nameField`. The schema still parses it (existing
|
|
166
|
+
* metadata keeps loading), so this is advisory, not an error.
|
|
167
|
+
* - `title-unresolvable` — `objectTitleCompleteness` reports `status: 'none'`:
|
|
168
|
+
* no `nameField`/`displayNameField` pointer and no title-eligible field to
|
|
169
|
+
* derive one from. Records will have no meaningful title (the runtime falls
|
|
170
|
+
* back to the auto-provisioned primary / `Record #<id>` floor), so this is a
|
|
171
|
+
* warning, not an error — nothing is fully broken.
|
|
172
|
+
*
|
|
173
|
+
* Both are warnings: the auto-provision transform and the id floor mean a
|
|
174
|
+
* green build never ships a fully title-less object. Reuses the shared spec
|
|
175
|
+
* predicate (`@objectstack/spec/data` → display-name) so cloud and framework
|
|
176
|
+
* classify titles identically.
|
|
177
|
+
*/
|
|
178
|
+
declare const TITLE_FORMAT_RETIRED = "title-format-retired";
|
|
179
|
+
declare const TITLE_UNRESOLVABLE = "title-unresolvable";
|
|
180
|
+
type RecordTitleSeverity = 'error' | 'warning';
|
|
181
|
+
interface RecordTitleFinding {
|
|
182
|
+
/** Always `warning` today — both rules are advisory (see module note). */
|
|
183
|
+
severity: RecordTitleSeverity;
|
|
184
|
+
/** Diagnostic rule id (registry entry), e.g. `title-format-retired`. */
|
|
185
|
+
rule: string;
|
|
186
|
+
/** Human-readable location, e.g. `object "invoice"`. */
|
|
187
|
+
where: string;
|
|
188
|
+
/** Config path, e.g. `objects[3]`. */
|
|
189
|
+
path: string;
|
|
190
|
+
/** What is wrong. */
|
|
191
|
+
message: string;
|
|
192
|
+
/** How to fix it. */
|
|
193
|
+
hint: string;
|
|
194
|
+
}
|
|
195
|
+
type AnyRec = Record<string, unknown>;
|
|
196
|
+
/**
|
|
197
|
+
* Validate every object's record-title declaration. Returns the list of
|
|
198
|
+
* findings (empty = clean). Both rules are advisory (`warning`): the caller
|
|
199
|
+
* must never fail the build on them alone — auto-provision + the `Record #<id>`
|
|
200
|
+
* floor guarantee a resolvable title at runtime.
|
|
201
|
+
*/
|
|
202
|
+
declare function validateRecordTitle(stack: AnyRec): RecordTitleFinding[];
|
|
203
|
+
|
|
204
|
+
export { CHART_CONFIG_MISSING, CHART_FIELD_UNKNOWN, type ExprIssue, type JsxPageFinding, type JsxPageSeverity, MEASURE_AGGREGATE_INCOHERENT, type ReactPageFinding, type ReactPageSeverity, type RecordTitleFinding, type RecordTitleSeverity, STYLE_CLASSNAME_TAILWIND, STYLE_NODE_MISSING_ID, STYLE_RESPONSIVE_NO_BASE, STYLE_UNKNOWN_CSS_PROPERTY, STYLE_UNKNOWN_TOKEN, type StyleFinding, type StyleSeverity, TABLE_COUNT_ONLY, TITLE_FORMAT_RETIRED, TITLE_UNRESOLVABLE, WIDGET_DATASET_UNKNOWN, WIDGET_DIMENSION_UNKNOWN, WIDGET_MEASURE_UNKNOWN, type WidgetBindingFinding, type WidgetBindingSeverity, validateJsxPages, validateReactPages, validateRecordTitle, validateResponsiveStyles, validateStackExpressions, validateWidgetBindings };
|
package/dist/index.d.ts
CHANGED
|
@@ -69,7 +69,7 @@ interface WidgetBindingFinding {
|
|
|
69
69
|
/** How to fix (or deliberately suppress) it. */
|
|
70
70
|
hint: string;
|
|
71
71
|
}
|
|
72
|
-
type AnyRec$
|
|
72
|
+
type AnyRec$5 = Record<string, unknown>;
|
|
73
73
|
/**
|
|
74
74
|
* Validate every dashboard widget's dataset binding. Returns the list of
|
|
75
75
|
* findings (empty = clean). Caller decides how to surface them: `error`
|
|
@@ -77,7 +77,7 @@ type AnyRec$3 = Record<string, unknown>;
|
|
|
77
77
|
* should fail validate/build; `warning` findings are advisory and must
|
|
78
78
|
* never fail the build on their own.
|
|
79
79
|
*/
|
|
80
|
-
declare function validateWidgetBindings(stack: AnyRec$
|
|
80
|
+
declare function validateWidgetBindings(stack: AnyRec$5): WidgetBindingFinding[];
|
|
81
81
|
|
|
82
82
|
interface ExprIssue {
|
|
83
83
|
where: string;
|
|
@@ -90,12 +90,12 @@ interface ExprIssue {
|
|
|
90
90
|
*/
|
|
91
91
|
severity?: 'error' | 'warning';
|
|
92
92
|
}
|
|
93
|
-
type AnyRec$
|
|
93
|
+
type AnyRec$4 = Record<string, unknown>;
|
|
94
94
|
/**
|
|
95
95
|
* Validate every predicate in the stack. Returns the list of issues (empty =
|
|
96
96
|
* clean). Caller decides how to surface / whether to fail the build.
|
|
97
97
|
*/
|
|
98
|
-
declare function validateStackExpressions(stack: AnyRec$
|
|
98
|
+
declare function validateStackExpressions(stack: AnyRec$4): ExprIssue[];
|
|
99
99
|
|
|
100
100
|
type StyleSeverity = 'error' | 'warning';
|
|
101
101
|
interface StyleFinding {
|
|
@@ -113,14 +113,14 @@ declare const STYLE_CLASSNAME_TAILWIND = "style-classname-tailwind";
|
|
|
113
113
|
declare const STYLE_RESPONSIVE_NO_BASE = "style-responsive-no-base";
|
|
114
114
|
declare const STYLE_UNKNOWN_CSS_PROPERTY = "style-unknown-css-property";
|
|
115
115
|
declare const STYLE_UNKNOWN_TOKEN = "style-unknown-token";
|
|
116
|
-
type AnyRec$
|
|
116
|
+
type AnyRec$3 = Record<string, unknown>;
|
|
117
117
|
/**
|
|
118
118
|
* Validate every page's component tree for SDUI styling correctness (ADR-0065).
|
|
119
119
|
* Returns findings (empty = clean). `error` findings describe styles that are
|
|
120
120
|
* silently dropped and should fail validate/build; `warning` findings are
|
|
121
121
|
* advisory (typos, drift, footguns).
|
|
122
122
|
*/
|
|
123
|
-
declare function validateResponsiveStyles(stack: AnyRec$
|
|
123
|
+
declare function validateResponsiveStyles(stack: AnyRec$3): StyleFinding[];
|
|
124
124
|
|
|
125
125
|
type JsxPageSeverity = 'error' | 'warning';
|
|
126
126
|
interface JsxPageFinding {
|
|
@@ -133,9 +133,72 @@ interface JsxPageFinding {
|
|
|
133
133
|
message: string;
|
|
134
134
|
hint: string;
|
|
135
135
|
}
|
|
136
|
-
type AnyRec = Record<string, unknown>;
|
|
137
|
-
declare function validateJsxPages(stack: AnyRec, opts?: {
|
|
136
|
+
type AnyRec$2 = Record<string, unknown>;
|
|
137
|
+
declare function validateJsxPages(stack: AnyRec$2, opts?: {
|
|
138
138
|
manifest?: Manifest;
|
|
139
139
|
}): JsxPageFinding[];
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
type ReactPageSeverity = 'error' | 'warning';
|
|
142
|
+
interface ReactPageFinding {
|
|
143
|
+
severity: ReactPageSeverity;
|
|
144
|
+
rule: string;
|
|
145
|
+
where: string;
|
|
146
|
+
path: string;
|
|
147
|
+
message: string;
|
|
148
|
+
hint: string;
|
|
149
|
+
}
|
|
150
|
+
type AnyRec$1 = Record<string, unknown>;
|
|
151
|
+
declare function validateReactPages(stack: AnyRec$1): ReactPageFinding[];
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Build-time record-title diagnostics (ADR-0079).
|
|
155
|
+
*
|
|
156
|
+
* A record's human title is a structural invariant: every object resolves a
|
|
157
|
+
* primary title from a real STORED field via `nameField` (the canonical
|
|
158
|
+
* pointer; `displayNameField` is the deprecated alias) or a deterministic
|
|
159
|
+
* derivation. Two authoring smells are flagged here so `os build`/`os lint`,
|
|
160
|
+
* the MCP authoring surface, and hand-authoring all get the coverage cloud
|
|
161
|
+
* graph-lint already has (the ADR-0078 "not cloud-only" principle):
|
|
162
|
+
*
|
|
163
|
+
* - `title-format-retired` — the object declares a `titleFormat`. That field
|
|
164
|
+
* is a RENDER-ONLY template the server can neither return nor query; ADR-0079
|
|
165
|
+
* retires it in favour of `nameField`. The schema still parses it (existing
|
|
166
|
+
* metadata keeps loading), so this is advisory, not an error.
|
|
167
|
+
* - `title-unresolvable` — `objectTitleCompleteness` reports `status: 'none'`:
|
|
168
|
+
* no `nameField`/`displayNameField` pointer and no title-eligible field to
|
|
169
|
+
* derive one from. Records will have no meaningful title (the runtime falls
|
|
170
|
+
* back to the auto-provisioned primary / `Record #<id>` floor), so this is a
|
|
171
|
+
* warning, not an error — nothing is fully broken.
|
|
172
|
+
*
|
|
173
|
+
* Both are warnings: the auto-provision transform and the id floor mean a
|
|
174
|
+
* green build never ships a fully title-less object. Reuses the shared spec
|
|
175
|
+
* predicate (`@objectstack/spec/data` → display-name) so cloud and framework
|
|
176
|
+
* classify titles identically.
|
|
177
|
+
*/
|
|
178
|
+
declare const TITLE_FORMAT_RETIRED = "title-format-retired";
|
|
179
|
+
declare const TITLE_UNRESOLVABLE = "title-unresolvable";
|
|
180
|
+
type RecordTitleSeverity = 'error' | 'warning';
|
|
181
|
+
interface RecordTitleFinding {
|
|
182
|
+
/** Always `warning` today — both rules are advisory (see module note). */
|
|
183
|
+
severity: RecordTitleSeverity;
|
|
184
|
+
/** Diagnostic rule id (registry entry), e.g. `title-format-retired`. */
|
|
185
|
+
rule: string;
|
|
186
|
+
/** Human-readable location, e.g. `object "invoice"`. */
|
|
187
|
+
where: string;
|
|
188
|
+
/** Config path, e.g. `objects[3]`. */
|
|
189
|
+
path: string;
|
|
190
|
+
/** What is wrong. */
|
|
191
|
+
message: string;
|
|
192
|
+
/** How to fix it. */
|
|
193
|
+
hint: string;
|
|
194
|
+
}
|
|
195
|
+
type AnyRec = Record<string, unknown>;
|
|
196
|
+
/**
|
|
197
|
+
* Validate every object's record-title declaration. Returns the list of
|
|
198
|
+
* findings (empty = clean). Both rules are advisory (`warning`): the caller
|
|
199
|
+
* must never fail the build on them alone — auto-provision + the `Record #<id>`
|
|
200
|
+
* floor guarantee a resolvable title at runtime.
|
|
201
|
+
*/
|
|
202
|
+
declare function validateRecordTitle(stack: AnyRec): RecordTitleFinding[];
|
|
203
|
+
|
|
204
|
+
export { CHART_CONFIG_MISSING, CHART_FIELD_UNKNOWN, type ExprIssue, type JsxPageFinding, type JsxPageSeverity, MEASURE_AGGREGATE_INCOHERENT, type ReactPageFinding, type ReactPageSeverity, type RecordTitleFinding, type RecordTitleSeverity, STYLE_CLASSNAME_TAILWIND, STYLE_NODE_MISSING_ID, STYLE_RESPONSIVE_NO_BASE, STYLE_UNKNOWN_CSS_PROPERTY, STYLE_UNKNOWN_TOKEN, type StyleFinding, type StyleSeverity, TABLE_COUNT_ONLY, TITLE_FORMAT_RETIRED, TITLE_UNRESOLVABLE, WIDGET_DATASET_UNKNOWN, WIDGET_DIMENSION_UNKNOWN, WIDGET_MEASURE_UNKNOWN, type WidgetBindingFinding, type WidgetBindingSeverity, validateJsxPages, validateReactPages, validateRecordTitle, validateResponsiveStyles, validateStackExpressions, validateWidgetBindings };
|
package/dist/index.js
CHANGED
|
@@ -690,7 +690,7 @@ function validateJsxPages(stack, opts = {}) {
|
|
|
690
690
|
const pages = asArray4(stack.pages);
|
|
691
691
|
for (let p = 0; p < pages.length; p++) {
|
|
692
692
|
const page = pages[p];
|
|
693
|
-
if (!page || page.kind !== "jsx") continue;
|
|
693
|
+
if (!page || page.kind !== "html" && page.kind !== "jsx") continue;
|
|
694
694
|
const name = String(page.name ?? `#${p}`);
|
|
695
695
|
const source = page.source;
|
|
696
696
|
if (typeof source !== "string" || source.trim() === "") {
|
|
@@ -699,7 +699,7 @@ function validateJsxPages(stack, opts = {}) {
|
|
|
699
699
|
rule: "jsx-page-empty-source",
|
|
700
700
|
where: `page "${name}"`,
|
|
701
701
|
path: `pages[${p}].source`,
|
|
702
|
-
message:
|
|
702
|
+
message: `kind:'${page.kind}' page has no \`source\`.`,
|
|
703
703
|
hint: "Author the page as a constrained JSX/Tailwind string in `source`."
|
|
704
704
|
});
|
|
705
705
|
continue;
|
|
@@ -718,6 +718,89 @@ function validateJsxPages(stack, opts = {}) {
|
|
|
718
718
|
}
|
|
719
719
|
return findings;
|
|
720
720
|
}
|
|
721
|
+
|
|
722
|
+
// src/validate-react-pages.ts
|
|
723
|
+
import { transform } from "sucrase";
|
|
724
|
+
var asArray5 = (v) => Array.isArray(v) ? v : [];
|
|
725
|
+
function validateReactPages(stack) {
|
|
726
|
+
const findings = [];
|
|
727
|
+
const pages = asArray5(stack.pages);
|
|
728
|
+
for (let p = 0; p < pages.length; p++) {
|
|
729
|
+
const page = pages[p];
|
|
730
|
+
if (!page || page.kind !== "react") continue;
|
|
731
|
+
const name = String(page.name ?? `#${p}`);
|
|
732
|
+
const source = page.source;
|
|
733
|
+
if (typeof source !== "string" || source.trim() === "") {
|
|
734
|
+
findings.push({
|
|
735
|
+
severity: "error",
|
|
736
|
+
rule: "react-page-empty-source",
|
|
737
|
+
where: `page "${name}"`,
|
|
738
|
+
path: `pages[${p}].source`,
|
|
739
|
+
message: "kind:'react' page has no `source`.",
|
|
740
|
+
hint: "Author the page as a real React component string in `source`."
|
|
741
|
+
});
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
try {
|
|
745
|
+
transform(source, { transforms: ["jsx", "typescript"], production: true });
|
|
746
|
+
} catch (err) {
|
|
747
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
748
|
+
findings.push({
|
|
749
|
+
severity: "error",
|
|
750
|
+
rule: "react-page-syntax",
|
|
751
|
+
where: `page "${name}"`,
|
|
752
|
+
path: `pages[${p}].source`,
|
|
753
|
+
message: `kind:'react' source has a syntax error: ${message.split("\n")[0]}`,
|
|
754
|
+
hint: "The source is transpiled (never executed) at build to catch syntax errors early \u2014 fix the JS/JSX."
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
return findings;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// src/validate-record-title.ts
|
|
762
|
+
import { objectTitleCompleteness } from "@objectstack/spec/data";
|
|
763
|
+
var TITLE_FORMAT_RETIRED = "title-format-retired";
|
|
764
|
+
var TITLE_UNRESOLVABLE = "title-unresolvable";
|
|
765
|
+
function asArray6(v) {
|
|
766
|
+
if (Array.isArray(v)) return v;
|
|
767
|
+
if (v && typeof v === "object") {
|
|
768
|
+
return Object.entries(v).map(([name, def]) => ({ name, ...def }));
|
|
769
|
+
}
|
|
770
|
+
return [];
|
|
771
|
+
}
|
|
772
|
+
function validateRecordTitle(stack) {
|
|
773
|
+
const findings = [];
|
|
774
|
+
const objects = asArray6(stack.objects);
|
|
775
|
+
for (let i = 0; i < objects.length; i++) {
|
|
776
|
+
const obj = objects[i];
|
|
777
|
+
const objName = typeof obj.name === "string" ? obj.name : `(object ${i})`;
|
|
778
|
+
const where = `object "${objName}"`;
|
|
779
|
+
const path = `objects[${i}]`;
|
|
780
|
+
if (obj.titleFormat !== void 0 && obj.titleFormat !== null && obj.titleFormat !== "") {
|
|
781
|
+
findings.push({
|
|
782
|
+
severity: "warning",
|
|
783
|
+
rule: TITLE_FORMAT_RETIRED,
|
|
784
|
+
where,
|
|
785
|
+
path,
|
|
786
|
+
message: `${objName}: titleFormat is retired (ADR-0079) \u2014 migrate to nameField (single field) or a formula field designated nameField`,
|
|
787
|
+
hint: `titleFormat is a render-only template the server cannot return or query, and an explicit nameField now takes precedence. For a single-field title set nameField: '<field>'. For a composite title, add a formula field (returnType: 'text') and designate it via nameField.`
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
const completeness = objectTitleCompleteness(obj);
|
|
791
|
+
if (completeness.status === "none") {
|
|
792
|
+
findings.push({
|
|
793
|
+
severity: "warning",
|
|
794
|
+
rule: TITLE_UNRESOLVABLE,
|
|
795
|
+
where,
|
|
796
|
+
path,
|
|
797
|
+
message: `${objName}: no resolvable record title \u2014 records will have no meaningful name (no nameField and no title-eligible field to derive one)`,
|
|
798
|
+
hint: `Set nameField to a text/email field (or a formula field with returnType: 'text'), or add a text field named "name"/"title". The runtime auto-provisions a primary and falls back to "Record #<id>", but an explicit title is far more useful.`
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return findings;
|
|
803
|
+
}
|
|
721
804
|
export {
|
|
722
805
|
CHART_CONFIG_MISSING,
|
|
723
806
|
CHART_FIELD_UNKNOWN,
|
|
@@ -728,10 +811,14 @@ export {
|
|
|
728
811
|
STYLE_UNKNOWN_CSS_PROPERTY,
|
|
729
812
|
STYLE_UNKNOWN_TOKEN,
|
|
730
813
|
TABLE_COUNT_ONLY,
|
|
814
|
+
TITLE_FORMAT_RETIRED,
|
|
815
|
+
TITLE_UNRESOLVABLE,
|
|
731
816
|
WIDGET_DATASET_UNKNOWN,
|
|
732
817
|
WIDGET_DIMENSION_UNKNOWN,
|
|
733
818
|
WIDGET_MEASURE_UNKNOWN,
|
|
734
819
|
validateJsxPages,
|
|
820
|
+
validateReactPages,
|
|
821
|
+
validateRecordTitle,
|
|
735
822
|
validateResponsiveStyles,
|
|
736
823
|
validateStackExpressions,
|
|
737
824
|
validateWidgetBindings
|