@bison-lab/payload-core 3.12.0 → 3.14.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
@@ -22,10 +22,10 @@ lockstep.
22
22
 
23
23
  | Import | Contents | Runs where |
24
24
  | ---------------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
25
- | `@bison-lab/payload-core` | `seoPlugin`, `createTheme`, `createBrandAssets`, `createRoles`, `createPages`, `createUsers`, `createMedia`, `adminOnlyApiTab`, `seedTheme`, `seedRoles`, predicates, `lookField`, `colorTokenField`, import-map strings, title and text helpers, types | Node. What `payload.config.ts` imports; it loads the plugin. |
25
+ | `@bison-lab/payload-core` | `seoPlugin`, `createTheme`, `createBrandAssets`, `createRoles`, `createFeatures`, `createPages`, `createUsers`, `createMedia`, `adminOnlyApiTab`, `seedTheme`, `seedRoles`, `seedFeatures`, predicates, `lookField`, `colorTokenField`, import-map strings, title and text helpers, types | Node. What `payload.config.ts` imports; it loads the plugin. |
26
26
  | `@bison-lab/payload-core/metadata` | `pageMetadata`, the title helpers, the same types | Server. What a page route imports; it does not load the plugin. |
27
27
  | `@bison-lab/payload-core/theme` | `getPublishedTheme`, `getPublishedIdentity`, `themeConfigFromDoc`, `themeHead`, `themeHeadFromDoc` | Server. What a root layout imports; it does not load the plugin or React. |
28
- | `@bison-lab/payload-core/admin` | Theme child fields, the Roles matrix field, and the Users Roles checklist | Admin. Referenced by import-map string; `generate:importmap` writes it. |
28
+ | `@bison-lab/payload-core/admin` | Theme child fields, the Roles and Features matrices, and the Users Roles checklist | Admin. Referenced by import-map string; `generate:importmap` writes it. |
29
29
  | `@bison-lab/payload-core/react` | `ThemePreview` (legacy; Theme has no preview pane) | Client. Kept so an older site import does not break. |
30
30
 
31
31
  ## What editors get
@@ -138,14 +138,16 @@ Pass `destructive` (or `seed.brandDestructive`); otherwise Theme uses the
138
138
  library’s `DESTRUCTIVE_SCALE_HEX` (`#ef4444`). Success falls back to `#22c55e`.
139
139
 
140
140
  **1. Register the Global.** Access is required — Theme does not read the
141
- Roles Global. Pass `canManageBrand` as `update`.
141
+ Roles or Features Global. Pass `canUseFeature("theme")` as `update`
142
+ (sites can keep `canManageBrand` until they adopt the switchboard).
142
143
 
143
144
  ```ts
144
145
  // payload.config.ts
145
146
  import {
146
147
  BRAND_ASSETS_SLUG,
147
- canManageBrand,
148
148
  createBrandAssets,
149
+ canUseFeature,
150
+ createFeatures,
149
151
  createRoles,
150
152
  createTheme,
151
153
  isAuthenticated,
@@ -155,13 +157,14 @@ import bisonConfig from "../bison.config.json";
155
157
  export default buildConfig({
156
158
  collections: [
157
159
  createBrandAssets({
158
- access: { read: () => true, update: canManageBrand },
160
+ access: { read: () => true, update: canUseFeature("brand-assets") },
159
161
  }),
160
162
  ],
161
163
  globals: [
162
164
  createRoles(),
165
+ createFeatures(),
163
166
  ...createTheme({
164
- access: { read: isAuthenticated, update: canManageBrand },
167
+ access: { read: isAuthenticated, update: canUseFeature("theme") },
165
168
  seed: bisonConfig,
166
169
  logo: { collection: BRAND_ASSETS_SLUG },
167
170
  identity: {
@@ -170,6 +173,10 @@ export default buildConfig({
170
173
  mark: { url: "/logo-mark.svg", alt: "Acme mark" },
171
174
  },
172
175
  },
176
+ colorUsages: {
177
+ collections: ["pages"],
178
+ globals: ["navigation"],
179
+ },
173
180
  onPublish: async () => {
174
181
  revalidateTag("theme");
175
182
  },
@@ -178,6 +185,15 @@ export default buildConfig({
178
185
  });
179
186
  ```
180
187
 
188
+ **Color usages.** `createTheme({ colorUsages })` is the only site wiring for
189
+ "is this custom color assigned?" Name every collection and global that
190
+ stores a `lookField` / `colorTokenField` (or a string token those fields
191
+ wrote). The package scans those documents — drafts included — and
192
+ rewrites tokens when Colors Delete needs a replacement. Omit the option,
193
+ or pass empty arrays, and every additional color is unused and deletes
194
+ immediately. A new look surface is added to `colorUsages`, not copied
195
+ into a site helper. Site adopt names the slugs (SPI-93).
196
+
181
197
  Then `payload generate:importmap` (the Theme child fields resolve from
182
198
  `@bison-lab/payload-core/admin`), `payload generate:types`, and
183
199
  `payload migrate:create`.
@@ -187,12 +203,13 @@ so the site renders what `bison-theme.css` rendered before anyone opens the
187
203
  admin:
188
204
 
189
205
  ```ts
190
- import { seedRoles, seedTheme } from "@bison-lab/payload-core";
206
+ import { seedFeatures, seedRoles, seedTheme } from "@bison-lab/payload-core";
191
207
  import bisonConfig from "../bison.config.json";
192
208
 
193
209
  export async function up({ payload }) {
194
210
  await seedTheme(payload, bisonConfig);
195
- await seedRoles(payload);
211
+ await seedRoles(payload); // pass the same extras as createRoles, if any
212
+ await seedFeatures(payload); // pass the same extras as createFeatures, if any
196
213
  }
197
214
  ```
198
215
 
@@ -233,7 +250,8 @@ Logo, favicon, and mobile-menu mark live in a locked cupboard, not in
233
250
  ordinary Media. Call `createBrandAssets` and point Theme at
234
251
  `BRAND_ASSETS_SLUG`. Access is the same shape as `createTheme`: `read` is
235
252
  typically public so the live site can load the file; `update` is
236
- `canManageBrand` and covers create, update, delete, and version history.
253
+ `canUseFeature("brand-assets")` (or `canManageBrand` until the site
254
+ adopts the switchboard) and covers create, update, delete, and version history.
237
255
  SVG preferred, PNG and ICO allowed; an SVG is sanitized on upload.
238
256
  Versions stay on so a replaced file can be rolled back.
239
257
 
@@ -243,20 +261,56 @@ has its accessible name. Then `payload migrate:create`.
243
261
  ## Roles
244
262
 
245
263
  Settings → Roles is an Admin-editable array. Rank is drag-to-reorder.
246
- Ticks are Content, Brand, Publish, and Users. The API tab is locked to
247
- Developerit is not a column. Defaults ship in the package (Brand on
248
- Designer only; Admin + Designer both store; Developer is exclusive).
264
+ Ticks are the catalogue rows released on Features. Developer is
265
+ implicitevery catalogue feature, including ones not released. That
266
+ row is visible only to a Developer, with every tick on and locked.
267
+ Defaults ship in the package (Theme
268
+ screens on Designer; Designer also has Users and Roles; Admin + Designer
269
+ both store). A site may pass `extras` into `createRoles` — each extra
270
+ is a slug, a display label, and default `grants` — and an Admin can add
271
+ further custom rows and edit any row’s name. Seed slugs stay read-only.
249
272
  `canManageBrand` and the other predicates read the saved Global and fall
250
- back to that seed. The package never writes `developer` onto a user row.
273
+ back to that seed (plus extras). The package never writes `developer`
274
+ onto a user row.
275
+
276
+ ```ts
277
+ createRoles({
278
+ extras: [{ role: "editor", label: "Editor", grants: ["content"] }],
279
+ });
280
+ ```
281
+
282
+ ## Features
283
+
284
+ Settings → Features is a Developer-only release valve: one switch per
285
+ catalogue row. Off hides the tick on Roles; Developer still has the
286
+ feature. The catalogue is code, grouped for the UI: Pages (Content,
287
+ Publish, API tab), Media, Theme (Colors, Typography, Appearance,
288
+ Identity, Brand assets), and Users (Users, Roles). Features itself is
289
+ not a row. A site may pass `extras` into `createFeatures` — those rows
290
+ appear only there. `canUseFeature(slug)` is true when the row is
291
+ released and a held role is granted it, or when the login is Developer.
292
+ An empty Global falls back to the catalogue defaults. Group slugs
293
+ `pages` and `theme` are true when any child is. `createPages` /
294
+ `createMedia` use `canUseFeature` for create and update (and
295
+ `admin.hidden`). Theme and brand-assets still take `access` arguments —
296
+ pass `canUseFeature("theme")` / `canUseFeature("brand-assets")` as
297
+ `update`.
298
+
299
+ ```ts
300
+ createFeatures({
301
+ extras: [{ slug: "doctors", label: "Doctor search", group: "users" }],
302
+ });
303
+ ```
251
304
 
252
305
  ## Pages, users, and media
253
306
 
254
307
  `createPages`, `createUsers`, `createMedia`, `slugField`, and
255
308
  `adminOnlyApiTab` ship from the same React-free entry. Pages take the
256
309
  site's hero and layout blocks, reserved-slug predicate, and preview
257
- path. Users take `secureCookies`; the Roles checklist reads the Global
258
- (or the seed) and has no second ORDER / WHAT list. `adminOnlyApiTab`
259
- shows the document API tab only to Developer.
310
+ path. Users sit under Settings next to Roles, take `secureCookies`, and
311
+ offer whatever rows the Global (or the seed plus the same `extras`) has.
312
+ The checklist labels are the editable names. `adminOnlyApiTab` shows the
313
+ document API tab only to Developer.
260
314
 
261
315
  ## No generated types
262
316