@estiva-app/ui 0.30.0 → 0.31.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/README.md CHANGED
@@ -64,7 +64,7 @@ export default {
64
64
 
65
65
  The UI Guardrails' rules ship with the package, as an ESLint plugin, so an app
66
66
  gets a new rule with a version bump. Each rule names the component to use
67
- instead. There are three:
67
+ instead. There are four:
68
68
 
69
69
  - `estiva/no-raw-element` refuses a raw interactive element (`<a>`, `<input>`,
70
70
  `<form>`, `<dialog>`…) and names the part to use, or says the package has none yet.
@@ -79,6 +79,10 @@ instead. There are three:
79
79
  `className` or an inner box's class prop — a colour, a text size, a border, a
80
80
  corner, a shadow — and names the part's look props (`PART_LOOK_PROPS`). Only
81
81
  placement goes through (`PLACEMENT`), and `EmptyState` takes no padding.
82
+ - `estiva/no-handmade-header` refuses a column's header bar drawn by hand: a
83
+ row first in a column, with its own side padding and a title, where the column
84
+ fills its height or the row has a bar's height or hairline. It reads the
85
+ shape, not the class names, and names `ContainerHeader`.
82
86
 
83
87
  ```js
84
88
  // eslint.config.js — alongside your own rules
@@ -1 +1 @@
1
- {"version":3,"file":"CommandPalette.d.ts","sourceRoot":"","sources":["../src/CommandPalette.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CommandPalette.d.ts","sourceRoot":"","sources":["../src/CommandPalette.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DialogShell.d.ts","sourceRoot":"","sources":["../src/DialogShell.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAQ9C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,WAAW,gBAAgB;IAC/B;8CAC0C;IAC1C,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB;yDACqD;IACrD,aAAa,CAAC,EAAE,SAAS,CAAA;IACzB;uCACmC;IACnC,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,QAAQ,EAAE,SAAS,CAAA;IACnB,0GAA0G;IAC1G,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,KAAW,EAAE,KAAa,EAAE,EAAE,gBAAgB,+BAkG1J"}
1
+ {"version":3,"file":"DialogShell.d.ts","sourceRoot":"","sources":["../src/DialogShell.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAS9C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,WAAW,gBAAgB;IAC/B;8CAC0C;IAC1C,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB;yDACqD;IACrD,aAAa,CAAC,EAAE,SAAS,CAAA;IACzB;uCACmC;IACnC,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,QAAQ,EAAE,SAAS,CAAA;IACnB,0GAA0G;IAC1G,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,KAAW,EAAE,KAAa,EAAE,EAAE,gBAAgB,+BAsG1J"}
@@ -15,6 +15,7 @@ declare const plugin: {
15
15
  'component-has-a-page': import("eslint").Rule.RuleModule;
16
16
  'component-has-a-story': import("eslint").Rule.RuleModule;
17
17
  'no-restyled-part': import("eslint").Rule.RuleModule;
18
+ 'no-handmade-header': import("eslint").Rule.RuleModule;
18
19
  'no-raw-element': import("eslint").Rule.RuleModule;
19
20
  'no-rebuilt-behaviour': import("eslint").Rule.RuleModule;
20
21
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/eslint/index.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAQ5C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAA;AAClG,OAAO,EAAE,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAC9E,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAI/D,2FAA2F;AAC3F,eAAO,MAAM,UAAU,WAAW,CAAA;AAuClC,QAAA,MAAM,MAAM;;;;;;;;;;;;;;aAGK;QAAE,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAA;KAAE;CACrE,CAAA;AAEzB,qFAAqF;AACrF,eAAO,MAAM,YAAY,UAA+D,CAAA;AACxF,wFAAwF;AACxF,eAAO,MAAM,gBAAgB,UAAmE,CAAA;AA8BhG,eAAe,MAAM,CAAA;AAErB,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IACpC;;;OAGG;IACH,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC/D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE,IAAI,GAAE,SAAS,MAAM,EAAiB,GAAG,SAAS,CAmB1G"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/eslint/index.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAS5C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAA;AAClG,OAAO,EAAE,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAC9E,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAI/D,2FAA2F;AAC3F,eAAO,MAAM,UAAU,WAAW,CAAA;AAyClC,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;aAGK;QAAE,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAA;KAAE;CACrE,CAAA;AAEzB,qFAAqF;AACrF,eAAO,MAAM,YAAY,UAA+D,CAAA;AACxF,wFAAwF;AACxF,eAAO,MAAM,gBAAgB,UAAmE,CAAA;AA+BhG,eAAe,MAAM,CAAA;AAErB,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IACpC;;;OAGG;IACH,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC/D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE,IAAI,GAAE,SAAS,MAAM,EAAiB,GAAG,SAAS,CAmB1G"}
@@ -158,6 +158,100 @@ var noHandRolledBehaviour = {
158
158
  }
159
159
  };
160
160
 
161
+ // src/eslint/no-handmade-header.ts
162
+ var isElement = (n) => typeof n === "object" && n !== null && n.type === "JSXElement";
163
+ function tagOf(el) {
164
+ const n = el.openingElement.name;
165
+ if (n.type === "JSXIdentifier") return n.name ?? "";
166
+ if (n.type === "JSXMemberExpression") return `${n.object?.name ?? ""}.${n.property?.name ?? ""}`;
167
+ return "";
168
+ }
169
+ function attribute(el, name) {
170
+ return el.openingElement.attributes.find((a) => a.type === "JSXAttribute" && a.name?.name === name);
171
+ }
172
+ function strings(node, out = []) {
173
+ if (!node || typeof node !== "object") return out;
174
+ if (node.type === "Literal" && typeof node.value === "string") out.push(node.value);
175
+ else if (node.type === "TemplateLiteral") for (const q of node.quasis) out.push(q.value.cooked ?? "");
176
+ for (const [key, value] of Object.entries(node)) {
177
+ if (key === "parent" || key === "loc" || key === "range") continue;
178
+ if (Array.isArray(value)) for (const v of value) strings(v, out);
179
+ else if (value && typeof value === "object" && value.type) strings(value, out);
180
+ }
181
+ return out;
182
+ }
183
+ function classesOf(el) {
184
+ const value = attribute(el, "className")?.value;
185
+ if (!value) return [];
186
+ const text = value.type === "Literal" ? String(value.value) : strings(value.expression).join(" ");
187
+ return text.split(/\s+/).filter(Boolean).map((c) => c.replace(/^(?:[^:\s[\]]+:)+/, ""));
188
+ }
189
+ function meaningful(el) {
190
+ const out = [];
191
+ for (const child2 of el.children) {
192
+ if (child2.type === "JSXText") {
193
+ if (String(child2.value).trim()) out.push(child2);
194
+ } else if (child2.type === "JSXExpressionContainer") {
195
+ const e = child2.expression;
196
+ if (e.type === "JSXEmptyExpression") continue;
197
+ if (e.type === "LogicalExpression" && isElement(e.right)) out.push(e.right);
198
+ else if (isElement(e)) out.push(e);
199
+ else out.push(child2);
200
+ } else if (child2.type === "JSXElement" || child2.type === "JSXFragment") out.push(child2);
201
+ }
202
+ return out;
203
+ }
204
+ var TEXT_PARTS = /* @__PURE__ */ new Set(["EditableText", "Person", "Breadcrumb", "Link", "SectionLabel", "InlineChip"]);
205
+ var VALUE_EXPRESSIONS = /* @__PURE__ */ new Set(["Identifier", "MemberExpression", "TemplateLiteral", "Literal", "LogicalExpression", "ConditionalExpression", "ChainExpression", "CallExpression"]);
206
+ function carriesTitle(el, depth = 0) {
207
+ if (depth > 3) return false;
208
+ for (const child2 of meaningful(el)) {
209
+ if (child2.type === "JSXText") return true;
210
+ if (child2.type === "JSXExpressionContainer") {
211
+ if (VALUE_EXPRESSIONS.has(child2.expression.type)) return true;
212
+ continue;
213
+ }
214
+ if (!isElement(child2)) continue;
215
+ const tag = tagOf(child2);
216
+ if (/^h[1-6]$/.test(tag) || TEXT_PARTS.has(tag) || /\.(?:Title|Heading)$/.test(tag)) return true;
217
+ if (/^[a-z]/.test(tag) && carriesTitle(child2, depth + 1)) return true;
218
+ }
219
+ return false;
220
+ }
221
+ var ROW = (c) => c.includes("flex") && !c.includes("flex-col");
222
+ var OWN_SIDE_PADDING = (c) => c.some((x) => /^-?(?:p|px|pl|pr|ps|pe)-/.test(x));
223
+ var FILLS = (c) => c.includes("flex-col") && c.some((x) => /^(?:h-full|h-screen|flex-1|grow|min-h-0|h-dvh)$/.test(x));
224
+ var BAR = (c) => c.some((x) => /^(?:h|min-h)-(?:10|11|12|13|14|\[(?:4[0-9]|5[0-6])px\])$/.test(x)) || c.some((x) => /^border-b(?:-|$)/.test(x));
225
+ var INTERACTIVE = ["onClick", "href", "role", "onKeyDown", "onPointerDown"];
226
+ var noHandmadeHeader = {
227
+ meta: {
228
+ type: "problem",
229
+ docs: { description: "A column's header bar drawn by hand where ContainerHeader is the part" },
230
+ schema: [],
231
+ messages: {
232
+ handmade: "A column's header bar drawn by hand. Use `ContainerHeader` from @estiva-app/ui: 48px, the title, the column's buttons at the right edge (`actions`), a hairline under it.",
233
+ ...ESCAPE_MESSAGES
234
+ }
235
+ },
236
+ create(context) {
237
+ return {
238
+ JSXElement(node) {
239
+ const column = node;
240
+ const kids = meaningful(column);
241
+ const first = kids[0];
242
+ if (kids.length < 2 || !isElement(first) || !/^[a-z]/.test(tagOf(first))) return;
243
+ const own = classesOf(first);
244
+ if (!ROW(own) || !OWN_SIDE_PADDING(own)) return;
245
+ if (INTERACTIVE.some((name) => attribute(first, name))) return;
246
+ if (!FILLS(classesOf(column)) && !BAR(own)) return;
247
+ if (!carriesTitle(first)) return;
248
+ if (isEscaped(context, first.openingElement)) return;
249
+ context.report({ loc: first.openingElement.loc, messageId: "handmade" });
250
+ }
251
+ };
252
+ }
253
+ };
254
+
161
255
  // src/eslint/no-raw-element.ts
162
256
  var RAW_ELEMENT_PARTS = {
163
257
  a: { use: "Link", more: " For a chip, `InlineChip`; for a whole card, `Card` with `href`." },
@@ -199,7 +293,7 @@ var ANY_INPUT = {
199
293
  use: "TextInput",
200
294
  more: " For a search, `SearchInput`; for a tick box, `Checkbox`; to pick files, `FilePicker`."
201
295
  };
202
- function attribute(element, name) {
296
+ function attribute2(element, name) {
203
297
  return element.attributes.find((a) => a.type === "JSXAttribute" && a.name.name === name);
204
298
  }
205
299
  function literal(attr) {
@@ -213,14 +307,14 @@ function literal(attr) {
213
307
  return void 0;
214
308
  }
215
309
  function isOn(element, name) {
216
- const attr = attribute(element, name);
310
+ const attr = attribute2(element, name);
217
311
  return attr !== void 0 && literal(attr) !== false;
218
312
  }
219
313
  function classify(element) {
220
314
  if (element.name.type !== "JSXIdentifier" || !element.name.name) return null;
221
315
  const name = element.name.name;
222
316
  if (name === "input") {
223
- const attr = attribute(element, "type");
317
+ const attr = attribute2(element, "type");
224
318
  const type = attr ? literal(attr) : "text";
225
319
  if (typeof type !== "string") return { report: "raw", element: "<input>", part: ANY_INPUT };
226
320
  const key = type.toLowerCase();
@@ -232,7 +326,7 @@ function classify(element) {
232
326
  }
233
327
  if (!Object.hasOwn(RAW_ELEMENT_PARTS, name)) return null;
234
328
  if ((name === "audio" || name === "video") && !isOn(element, "controls")) return null;
235
- if (name === "img" && !attribute(element, "usemap") && !attribute(element, "useMap")) return null;
329
+ if (name === "img" && !attribute2(element, "usemap") && !attribute2(element, "useMap")) return null;
236
330
  const part = RAW_ELEMENT_PARTS[name];
237
331
  return part ? { report: "raw", element: `<${name}>`, part } : { report: "noPart", element: `<${name}>` };
238
332
  }
@@ -1050,18 +1144,18 @@ function handsOnTo(fn, bindingOf) {
1050
1144
  if (wrapper || node.type !== "JSXOpeningElement") return;
1051
1145
  const binding = bindingOf(child(node, "name"));
1052
1146
  if (!binding) return;
1053
- for (const attribute2 of children(node, "attributes")) {
1054
- if (!attribute2) continue;
1055
- if (attribute2.type === "JSXSpreadAttribute") {
1056
- const argument = nameOf(child(attribute2, "argument"));
1147
+ for (const attribute3 of children(node, "attributes")) {
1148
+ if (!attribute3) continue;
1149
+ if (attribute3.type === "JSXSpreadAttribute") {
1150
+ const argument = nameOf(child(attribute3, "argument"));
1057
1151
  if (argument && (argument === restName || argument === propsName)) wrapper = { part: binding.part, props: binding.props };
1058
1152
  continue;
1059
1153
  }
1060
- const prop = nameOf(child(attribute2, "name"));
1154
+ const prop = nameOf(child(attribute3, "name"));
1061
1155
  if (!prop || !CLASS_PROP.test(prop)) continue;
1062
1156
  const partProp = binding.props === "all" ? prop : binding.props[prop];
1063
1157
  if (!partProp) continue;
1064
- walk(attribute2, (inner) => {
1158
+ walk(attribute3, (inner) => {
1065
1159
  let callerProp;
1066
1160
  if (inner.type === "Identifier") callerProp = classParams.get(inner.name);
1067
1161
  if (inner.type === "MemberExpression" && nameOf(child(inner, "object")) === propsName) {
@@ -1093,10 +1187,10 @@ function constantOf(identifier, scope) {
1093
1187
  }
1094
1188
  return void 0;
1095
1189
  }
1096
- function classesOf(value, scopeOf, asMap = false, seen = /* @__PURE__ */ new Set()) {
1190
+ function classesOf2(value, scopeOf, asMap = false, seen = /* @__PURE__ */ new Set()) {
1097
1191
  if (!value || seen.has(value) || seen.size > 200) return [];
1098
1192
  seen.add(value);
1099
- const read = (node, map = false) => classesOf(node, scopeOf, map, seen);
1193
+ const read = (node, map = false) => classesOf2(node, scopeOf, map, seen);
1100
1194
  const words = (text) => typeof text === "string" ? text.split(/\s+/).filter(Boolean) : [];
1101
1195
  switch (value.type) {
1102
1196
  case "Literal":
@@ -1174,7 +1268,7 @@ var noRestyledPart = {
1174
1268
  const check = (element, binding, prop, value, at) => {
1175
1269
  const partProp = binding.props === "all" ? prop : binding.props[prop];
1176
1270
  if (!partProp || !value) return;
1177
- const classes = classesOf(value, scopeOf);
1271
+ const classes = classesOf2(value, scopeOf);
1178
1272
  const handedOn = binding.props === "all" ? "its props" : `its \`${prop}\``;
1179
1273
  const via = binding.via && binding.via !== binding.part ? `\`${binding.via}\` (it hands ${handedOn} to \`${binding.part}\`)` : `\`${binding.part}\``;
1180
1274
  const where = `${via}${partProp === "className" ? "" : `'s \`${partProp}\``}`;
@@ -1197,14 +1291,14 @@ var noRestyledPart = {
1197
1291
  const element = ruleNode;
1198
1292
  const binding = locals && bindingOfTag(child(element, "name"), locals);
1199
1293
  if (!binding) return;
1200
- for (const attribute2 of children(element, "attributes")) {
1201
- if (!attribute2) continue;
1202
- if (attribute2.type === "JSXAttribute") {
1203
- const prop = nameOf(child(attribute2, "name"));
1204
- if (prop && CLASS_PROP.test(prop)) check(element, binding, prop, child(attribute2, "value"), attribute2);
1294
+ for (const attribute3 of children(element, "attributes")) {
1295
+ if (!attribute3) continue;
1296
+ if (attribute3.type === "JSXAttribute") {
1297
+ const prop = nameOf(child(attribute3, "name"));
1298
+ if (prop && CLASS_PROP.test(prop)) check(element, binding, prop, child(attribute3, "value"), attribute3);
1205
1299
  continue;
1206
1300
  }
1207
- let spread = child(attribute2, "argument");
1301
+ let spread = child(attribute3, "argument");
1208
1302
  if (spread?.type === "Identifier") spread = constantOf(spread, scopeOf(spread));
1209
1303
  while (spread?.type === "TSAsExpression" || spread?.type === "TSSatisfiesExpression") spread = child(spread, "expression");
1210
1304
  if (spread?.type !== "ObjectExpression") continue;
@@ -1260,14 +1354,16 @@ var PLUGIN_KEY = "estiva";
1260
1354
  var appRules = {
1261
1355
  "no-raw-element": noRawElement,
1262
1356
  "no-rebuilt-behaviour": noRebuiltBehaviour,
1263
- "no-restyled-part": noRestyledPart
1357
+ "no-restyled-part": noRestyledPart,
1358
+ "no-handmade-header": noHandmadeHeader
1264
1359
  };
1265
1360
  var packageRules = {
1266
1361
  "raw-element-outside-a-wrapper": rawElementOutsideAWrapper,
1267
1362
  "no-hand-rolled-behaviour": noHandRolledBehaviour,
1268
1363
  "component-has-a-page": componentHasAPage,
1269
1364
  "component-has-a-story": componentHasAStory,
1270
- "no-restyled-part": noRestyledPart
1365
+ "no-restyled-part": noRestyledPart,
1366
+ "no-handmade-header": noHandmadeHeader
1271
1367
  };
1272
1368
  var rules = { ...appRules, ...packageRules };
1273
1369
  var plugin = {
@@ -1283,7 +1379,8 @@ plugin.configs.recommended = {
1283
1379
  rules: {
1284
1380
  [`${PLUGIN_KEY}/no-raw-element`]: "error",
1285
1381
  [`${PLUGIN_KEY}/no-rebuilt-behaviour`]: "error",
1286
- [`${PLUGIN_KEY}/no-restyled-part`]: "error"
1382
+ [`${PLUGIN_KEY}/no-restyled-part`]: "error",
1383
+ [`${PLUGIN_KEY}/no-handmade-header`]: "error"
1287
1384
  }
1288
1385
  };
1289
1386
  plugin.configs.strict = {