@vibes.diy/prompts 2.5.15 → 2.5.16

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.
Files changed (2) hide show
  1. package/llms/fireproof.md +29 -0
  2. package/package.json +4 -4
package/llms/fireproof.md CHANGED
@@ -521,6 +521,35 @@ export default function App() {
521
521
 
522
522
  The pattern: `useVibe().can` gates every write surface — including owner-only management, which the access function enforces via `if (!user.isOwner) throw` so `can.create({ type: "roleGrant", … })` is false for non-owners. `access.hasChannel()` reflects display-only membership, and `isOwner` is a display hint only, never the write gate. The access function is the server-side authority — `useVibe().can` is how the UI reflects its decisions for writes.
523
523
 
524
+ **Owner-management panels (appoint/revoke moderators, grant/revoke roles) gate on `can.*`, not `isOwner`.** It's tempting to wrap an admin panel in `{isOwner && <ModeratorPanel />}` and have its buttons call `database.put`/`database.del` directly. Don't let `isOwner` be what decides a write — it's the same display hint as everywhere else. Gate each mutating control on the verdict from the doc you'll actually write — appoint on `can.create({ type: "modGrant", role: "moderator", userHandle }).ok`, revoke on `can.delete(grantDoc).ok` — and render `.reason` when denied. `can.*` runs the app's own `access.js` to produce the verdict, so the control's enabled state and message track what the access function decides and stay correct as it grows beyond owner-only (a delegated admin role, say). Gate the panel's *visibility* on those same verdicts too, not `isOwner` alone — otherwise a delegated admin the access function now allows never reaches the controls or their reason.
525
+
526
+ ```jsx
527
+ // Don't: isOwner decides visibility AND the writes are ungated — a non-owner the
528
+ // access function would allow never reaches the controls, and isOwner drifts from access.js
529
+ {isOwner && (
530
+ <ModeratorPanel>
531
+ <button onClick={() => database.put({ type: "modGrant", role: "moderator", userHandle })}>Appoint</button>
532
+ <button onClick={() => database.del(grantDoc._id)}>Revoke</button>
533
+ </ModeratorPanel>
534
+ )}
535
+
536
+ // Do: can.* decides visibility AND gates each write, and supplies the denial reason
537
+ const canAppoint = can.create({ type: "modGrant", role: "moderator", userHandle });
538
+ const canRevoke = can.delete(grantDoc);
539
+ {(canAppoint.ok || canRevoke.ok) && (
540
+ <ModeratorPanel>
541
+ {canAppoint.ok && (
542
+ <button onClick={() => database.put({ type: "modGrant", role: "moderator", userHandle })}>Appoint</button>
543
+ )}
544
+ {canRevoke.ok ? (
545
+ <button onClick={() => database.del(grantDoc._id)}>Revoke</button>
546
+ ) : (
547
+ <p>{canRevoke.reason}</p>
548
+ )}
549
+ </ModeratorPanel>
550
+ )}
551
+ ```
552
+
524
553
  ### Example: Channel board with open channels (any member posts)
525
554
 
526
555
  Channels everyone can read, and any signed-in user can post to. The channel doc is `grant.public` (read for everyone) and the post rule checks only the author — **no `ctx.requireAccess`**, because public is read-only and would block every non-owner. (For a members-only board where the owner appoints who can post, grant a poster role and `requireAccess` it, as in the announcements example above.) The UI uses `access.hasChannel()` to filter which channels to display, and `useVibe().can` to gate writes.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibes.diy/prompts",
3
- "version": "2.5.15",
3
+ "version": "2.5.16",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "description": "",
@@ -30,9 +30,9 @@
30
30
  "@fireproof/core-types-base": "~0.24.19",
31
31
  "@fireproof/core-types-protocols-cloud": "~0.24.19",
32
32
  "@fireproof/use-fireproof": "~0.24.19",
33
- "@vibes.diy/call-ai-v2": "^2.5.15",
34
- "@vibes.diy/identity": "^2.5.15",
35
- "@vibes.diy/use-vibes-types": "^2.5.15",
33
+ "@vibes.diy/call-ai-v2": "^2.5.16",
34
+ "@vibes.diy/identity": "^2.5.16",
35
+ "@vibes.diy/use-vibes-types": "^2.5.16",
36
36
  "arktype": "~2.2.1",
37
37
  "json-schema-faker": "~0.6.2"
38
38
  },