@lessly/sdk-app 62.1.0 → 63.1.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/_types/index.d.ts +1 -0
- package/dist/_types/runtime/operations.d.ts +13 -0
- package/dist/index.cjs +6 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/docs/recipes/access.md +53 -0
- package/docs/rules.md +22 -0
- package/package.json +1 -1
package/docs/rules.md
CHANGED
|
@@ -382,6 +382,14 @@ The level MUST come from the generated method itself (`op.level`, on the
|
|
|
382
382
|
the App, not a literal, and never a rule of thumb about verbs — `analytics_*_delete`
|
|
383
383
|
is `write` while `mail_domain_create` is `admin`, and only the catalog knows.
|
|
384
384
|
|
|
385
|
+
Where there is no method to read from — a test double, a runtime probe over keys
|
|
386
|
+
it was handed — the App MUST NOT hand-type the level either. `@lessly/sdk-app`
|
|
387
|
+
exports `operations`, the catalog's own level map
|
|
388
|
+
(`Readonly<Record<string, ToolLevel>>`, keyed by operation key), regenerated at
|
|
389
|
+
publish time from the live catalog; read the level from it. A key it does not
|
|
390
|
+
carry answers `undefined`, which is exactly the case `accessReason`'s string form
|
|
391
|
+
covers — no level is invented for it.
|
|
392
|
+
|
|
385
393
|
The gate belongs to the OPERATION, not to a gesture: where a write is reachable
|
|
386
394
|
by several paths (drag and arrow keys, click and Enter, button and hotkey) all of
|
|
387
395
|
them MUST be gated together, or the hole left behind is one only keyboard users
|
|
@@ -389,6 +397,20 @@ find. Implicit form submission is such a path: a disabled submit button does not
|
|
|
389
397
|
reliably stop Enter inside a field, so the `onSubmit` handler MUST be gated (or
|
|
390
398
|
the fields made `readOnly`) rather than the button alone.
|
|
391
399
|
|
|
400
|
+
A gate that disables a capability across a WHOLE SURFACE — dragging any node on
|
|
401
|
+
a canvas, a bulk action over a list or a grid — MUST attach the reason to the
|
|
402
|
+
control that owns the capability (the toggle, the toolbar, the surface header)
|
|
403
|
+
and MUST NOT attach it to every affected element: thirty identical focusable
|
|
404
|
+
wrappers are thirty extra tab stops and one sentence thirty times, not thirty
|
|
405
|
+
explanations. Keyboard reachability is satisfied on the owning control; the
|
|
406
|
+
elements themselves just go inert.
|
|
407
|
+
|
|
408
|
+
A control that STAGES a change — it writes nothing itself, the write is sent
|
|
409
|
+
later by a Save or an Apply — MUST be gated on the operation the staged change
|
|
410
|
+
will send, UNLESS the upstream control that reaches it is already gated on that
|
|
411
|
+
same operation. Never on nothing (the caller does the work and meets the refusal
|
|
412
|
+
later, somewhere else) and never twice in a row.
|
|
413
|
+
|
|
392
414
|
A control that performs SEVERAL operations per click is disabled if ANY of them
|
|
393
415
|
is denied. A DISPATCHER control — one operation per click, selected by a mode or
|
|
394
416
|
a switch — MUST instead be gated on the operation it is about to call, and name
|