@byline/core 6.0.2 → 6.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.
@@ -392,6 +392,19 @@ export interface CollectionAdminConfig<T = any> extends FormAdminConfig {
392
392
  locale?: string;
393
393
  }) => string | null;
394
394
  };
395
+ /**
396
+ * Declares this collection's document paths managed — minted by the system
397
+ * or an import rather than typed by an editor. The admin path widget then
398
+ * renders read-only in both modes, with no preview or "Regenerate" action,
399
+ * and renders even when the collection declares no `useAsPath`.
400
+ *
401
+ * An admin-interface guard only: it does not make the path immutable, and
402
+ * it is independent of `useAsPath`. See
403
+ * [Document paths](../../../../docs/04-collections/05-document-paths.md).
404
+ *
405
+ * @default false
406
+ */
407
+ lockPath?: boolean;
395
408
  /**
396
409
  * Custom list-view component for this collection.
397
410
  *
@@ -458,6 +471,8 @@ export interface SingletonAdminConfig<T = any> extends FormAdminConfig {
458
471
  itemViewSort?: never;
459
472
  listView?: never;
460
473
  listActions?: never;
474
+ /** A singleton has no document path and no path widget. */
475
+ lockPath?: never;
461
476
  }
462
477
  /** Admin presentation config for either registered document-resource kind. */
463
478
  export type AdminResourceConfig<T = any> = CollectionAdminConfig<T> | SingletonAdminConfig<T>;
@@ -52,6 +52,13 @@ describe('singleton admin config', () => {
52
52
  columns: [{ fieldName: 'title', label: 'Title' }],
53
53
  });
54
54
  });
55
+ it('rejects lockPath at the singleton definition site and accepts it on a collection', () => {
56
+ defineSingletonAdmin(settings, {
57
+ // @ts-expect-error — a singleton has no document path and no path widget.
58
+ lockPath: true,
59
+ });
60
+ expect(defineAdmin(pages, { lockPath: true }).lockPath).toBe(true);
61
+ });
55
62
  it('keeps the singleton preview document pathless', () => {
56
63
  defineSingletonAdmin(settings, {
57
64
  preview: {
@@ -0,0 +1,8 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ export {};
@@ -0,0 +1,30 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ import { describe, expect, it } from 'vitest';
9
+ import { resolveItemViewColumns } from './config.js';
10
+ const itemViewCols = [{ fieldName: 'title', label: 'Title' }];
11
+ const pickerCols = [{ fieldName: 'name', label: 'Name' }];
12
+ const cfg = (over) => ({ slug: 'x', ...over });
13
+ describe('resolveItemViewColumns', () => {
14
+ it('returns itemView when present', () => {
15
+ expect(resolveItemViewColumns(cfg({ itemView: itemViewCols }))).toBe(itemViewCols);
16
+ });
17
+ it('falls back to the deprecated picker alias', () => {
18
+ expect(resolveItemViewColumns(cfg({ picker: pickerCols }))).toBe(pickerCols);
19
+ });
20
+ it('prefers itemView over picker when both are present', () => {
21
+ expect(resolveItemViewColumns(cfg({ itemView: itemViewCols, picker: pickerCols }))).toBe(itemViewCols);
22
+ });
23
+ it('returns undefined when neither is set', () => {
24
+ expect(resolveItemViewColumns(cfg({}))).toBeUndefined();
25
+ });
26
+ it('tolerates null / undefined config', () => {
27
+ expect(resolveItemViewColumns(null)).toBeUndefined();
28
+ expect(resolveItemViewColumns(undefined)).toBeUndefined();
29
+ });
30
+ });
@@ -19,6 +19,7 @@ const COLLECTION_ONLY_ADMIN_KEYS = completeCollectionOnlyAdminKeys([
19
19
  'itemViewSort',
20
20
  'listView',
21
21
  'listActions',
22
+ 'lockPath',
22
23
  ]);
23
24
  /**
24
25
  * Resolve a `fields{}` override key against a field set.
@@ -72,6 +72,13 @@ describe('validateAdminConfigs', () => {
72
72
  };
73
73
  expect(() => validateAdminConfigs([admin], [collection, singleton])).toThrow(/columns.*not allowed on a singleton admin config/);
74
74
  });
75
+ it('rejects the collection-only lockPath option on a singleton from untyped callers', () => {
76
+ const admin = {
77
+ ...defineSingletonAdmin(singleton, {}),
78
+ lockPath: true,
79
+ };
80
+ expect(() => validateAdminConfigs([admin], [collection, singleton])).toThrow(/lockPath.*not allowed on a singleton admin config/);
81
+ });
75
82
  it('rejects a singleton admin config targeting a multi-collection', () => {
76
83
  const admin = { ...defineSingletonAdmin(singleton, {}), slug: collection.path };
77
84
  expect(() => validateAdminConfigs([admin], [collection, singleton])).toThrow(/targets a collection definition/);
@@ -452,7 +452,7 @@ describe('uploadField service', () => {
452
452
  it('beforeStore { storagePath } threads targetStoragePath verbatim and derives the filename from its basename', async () => {
453
453
  const definition = withFieldUpload(uploadCollection, 'image', (f) => {
454
454
  f.upload.hooks = {
455
- beforeStore: () => ({ storagePath: 'publications/forru-0000447-0001-en.png' }),
455
+ beforeStore: () => ({ storagePath: 'publications/pub-0000447-0001-en.png' }),
456
456
  };
457
457
  });
458
458
  const { ctx, upload } = buildCtx({ definition });
@@ -466,17 +466,17 @@ describe('uploadField service', () => {
466
466
  // Explicit key is handed to the provider verbatim — no entropy suffix,
467
467
  // no collection-derived key.
468
468
  expect(upload).toHaveBeenCalledWith(expect.any(Buffer), expect.objectContaining({
469
- targetStoragePath: 'publications/forru-0000447-0001-en.png',
470
- filename: 'forru-0000447-0001-en.png',
469
+ targetStoragePath: 'publications/pub-0000447-0001-en.png',
470
+ filename: 'pub-0000447-0001-en.png',
471
471
  }));
472
472
  // Stored filename follows the explicit key's basename.
473
- expect(result.storedFile.filename).toBe('forru-0000447-0001-en.png');
473
+ expect(result.storedFile.filename).toBe('pub-0000447-0001-en.png');
474
474
  });
475
475
  it('beforeStore { storagePath, filename } honours both; a leading slash is stripped from the key', async () => {
476
476
  const definition = withFieldUpload(uploadCollection, 'image', (f) => {
477
477
  f.upload.hooks = {
478
478
  beforeStore: () => ({
479
- storagePath: '/publications/forru-0000447-0002-en.png',
479
+ storagePath: '/publications/pub-0000447-0002-en.png',
480
480
  filename: 'display-name.png',
481
481
  }),
482
482
  };
@@ -490,7 +490,7 @@ describe('uploadField service', () => {
490
490
  shouldCreateDocument: false,
491
491
  });
492
492
  expect(upload).toHaveBeenCalledWith(expect.any(Buffer), expect.objectContaining({
493
- targetStoragePath: 'publications/forru-0000447-0002-en.png',
493
+ targetStoragePath: 'publications/pub-0000447-0002-en.png',
494
494
  filename: 'display-name.png',
495
495
  }));
496
496
  expect(result.storedFile.filename).toBe('display-name.png');
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@byline/core",
3
3
  "private": false,
4
4
  "license": "MPL-2.0",
5
- "version": "6.0.2",
5
+ "version": "6.1.0",
6
6
  "engines": {
7
7
  "node": ">=20.9.0"
8
8
  },
@@ -87,7 +87,7 @@
87
87
  "sharp": "^0.35.4",
88
88
  "uuid": "^14.0.2",
89
89
  "zod": "^4.6.2",
90
- "@byline/auth": "6.0.2"
90
+ "@byline/auth": "6.1.0"
91
91
  },
92
92
  "devDependencies": {
93
93
  "@biomejs/biome": "2.5.13",