@byline/core 4.1.0 → 4.2.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/admin-types.d.ts +74 -2
- package/dist/@types/admin-types.js +29 -0
- package/dist/@types/collection-types.d.ts +29 -7
- package/dist/@types/db-types.d.ts +7 -7
- package/dist/@types/field-data-types.d.ts +1 -0
- package/dist/@types/field-types.d.ts +36 -1
- package/dist/@types/site-config.d.ts +28 -1
- package/dist/@types/storage-types.d.ts +14 -1
- package/dist/codegen/fixtures/all-fields.d.ts +23 -0
- package/dist/codegen/fixtures/all-fields.expected.d.ts +4 -0
- package/dist/codegen/fixtures/all-fields.js +2 -0
- package/dist/codegen/index.js +2 -0
- package/dist/config/config.js +2 -1
- package/dist/config/validate-admin-configs.d.ts +17 -1
- package/dist/config/validate-admin-configs.js +84 -11
- package/dist/config/validate-admin-configs.test.node.js +114 -1
- package/dist/config/validate-collections.js +43 -0
- package/dist/config/validate-collections.test.node.js +60 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/schemas/zod/builder.js +11 -0
- package/dist/services/build-search-document.js +5 -1
- package/dist/services/document-to-markdown.js +10 -0
- package/dist/services/field-upload.d.ts +7 -0
- package/dist/services/field-upload.js +15 -13
- package/dist/services/field-upload.test.node.js +1 -1
- package/dist/services/populate.d.ts +1 -1
- package/dist/services/populate.js +1 -1
- package/dist/storage/collection-fingerprint.js +9 -0
- package/dist/storage/field-store-map.js +1 -0
- package/dist/storage/field-store-map.test.node.js +1 -0
- package/dist/utils/slugify-filename.d.ts +50 -0
- package/dist/utils/slugify-filename.js +36 -0
- package/dist/utils/slugify-filename.test.node.d.ts +8 -0
- package/dist/utils/slugify-filename.test.node.js +50 -0
- package/package.json +2 -2
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* Context passed to a `FilenameSlugifierFn`.
|
|
10
|
+
*
|
|
11
|
+
* `collectionPath` - The collection receiving the upload. Lets an
|
|
12
|
+
* installation-supplied slugifier branch by collection
|
|
13
|
+
* if filename policies differ.
|
|
14
|
+
* `fieldName` - The upload field's name within the collection.
|
|
15
|
+
* `mimeType` - The uploaded file's MIME type.
|
|
16
|
+
*/
|
|
17
|
+
export type FilenameSlugifyContext = {
|
|
18
|
+
collectionPath: string;
|
|
19
|
+
fieldName: string;
|
|
20
|
+
mimeType: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* A pure function that converts an uploaded file's **base name** (extension
|
|
24
|
+
* already split off) into a storage-safe slug. The extension is handled by
|
|
25
|
+
* the framework — lowercased and reattached after slugification — so
|
|
26
|
+
* implementations only see and return the base name.
|
|
27
|
+
*
|
|
28
|
+
* Registered installation-wide via `ServerConfig.uploads.filenameSlugifier`;
|
|
29
|
+
* the parallel of `ServerConfig.slugifier` for document paths. Must be
|
|
30
|
+
* synchronous and side-effect free. Return value is sanitised again by the
|
|
31
|
+
* storage provider as a safety net, but implementations should stay within
|
|
32
|
+
* `A–Z a–z 0–9 . _ -` to keep the slug authoritative.
|
|
33
|
+
*/
|
|
34
|
+
export type FilenameSlugifierFn = (basename: string, ctx: FilenameSlugifyContext) => string;
|
|
35
|
+
/**
|
|
36
|
+
* Default filename slugifier — lowercases, replaces unsafe characters with
|
|
37
|
+
* hyphens, collapses runs, trims leading/trailing hyphens, and falls back
|
|
38
|
+
* to `'file'` for empty results. Mirrors the storage providers' own
|
|
39
|
+
* sanitisation rules so the default round-trips them unchanged.
|
|
40
|
+
*/
|
|
41
|
+
export declare const slugifyFilename: FilenameSlugifierFn;
|
|
42
|
+
/**
|
|
43
|
+
* Resolve an uploaded file's stored filename: split the extension, apply the
|
|
44
|
+
* installation slugifier (or the default) to the base name, and reattach the
|
|
45
|
+
* lowercased extension. This runs in `uploadField` **before** the
|
|
46
|
+
* `beforeStore` hook chain, so hooks observe (and may override) the
|
|
47
|
+
* already-slugified name — the same ordering the previous fixed sanitiser
|
|
48
|
+
* had.
|
|
49
|
+
*/
|
|
50
|
+
export declare function resolveUploadFilename(originalFilename: string, slugifier: FilenameSlugifierFn | undefined, ctx: FilenameSlugifyContext): string;
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* Default filename slugifier — lowercases, replaces unsafe characters with
|
|
10
|
+
* hyphens, collapses runs, trims leading/trailing hyphens, and falls back
|
|
11
|
+
* to `'file'` for empty results. Mirrors the storage providers' own
|
|
12
|
+
* sanitisation rules so the default round-trips them unchanged.
|
|
13
|
+
*/
|
|
14
|
+
export const slugifyFilename = (basename) => {
|
|
15
|
+
const safe = basename
|
|
16
|
+
.toLowerCase()
|
|
17
|
+
.replace(/[^a-z0-9._-]/g, '-')
|
|
18
|
+
.replace(/-+/g, '-')
|
|
19
|
+
.replace(/^-|-$/g, '');
|
|
20
|
+
return safe || 'file';
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Resolve an uploaded file's stored filename: split the extension, apply the
|
|
24
|
+
* installation slugifier (or the default) to the base name, and reattach the
|
|
25
|
+
* lowercased extension. This runs in `uploadField` **before** the
|
|
26
|
+
* `beforeStore` hook chain, so hooks observe (and may override) the
|
|
27
|
+
* already-slugified name — the same ordering the previous fixed sanitiser
|
|
28
|
+
* had.
|
|
29
|
+
*/
|
|
30
|
+
export function resolveUploadFilename(originalFilename, slugifier, ctx) {
|
|
31
|
+
const lastDot = originalFilename.lastIndexOf('.');
|
|
32
|
+
const ext = lastDot > 0 ? originalFilename.slice(lastDot).toLowerCase() : '';
|
|
33
|
+
const base = lastDot > 0 ? originalFilename.slice(0, lastDot) : originalFilename;
|
|
34
|
+
const slug = (slugifier ?? slugifyFilename)(base, ctx);
|
|
35
|
+
return `${slug || 'file'}${ext}`;
|
|
36
|
+
}
|
|
@@ -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,50 @@
|
|
|
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 { resolveUploadFilename, slugifyFilename } from './slugify-filename.js';
|
|
10
|
+
const ctx = { collectionPath: 'events', fieldName: 'attachment', mimeType: 'application/pdf' };
|
|
11
|
+
describe('slugifyFilename (default)', () => {
|
|
12
|
+
it('lowercases and hyphenates unsafe characters', () => {
|
|
13
|
+
expect(slugifyFilename('Meeting Agenda (Final)', ctx)).toBe('meeting-agenda-final');
|
|
14
|
+
});
|
|
15
|
+
it('collapses hyphen runs and trims leading/trailing hyphens', () => {
|
|
16
|
+
expect(slugifyFilename('--Board -- Notes--', ctx)).toBe('board-notes');
|
|
17
|
+
});
|
|
18
|
+
it('falls back to "file" for empty results', () => {
|
|
19
|
+
expect(slugifyFilename('***', ctx)).toBe('file');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe('resolveUploadFilename', () => {
|
|
23
|
+
it('slugifies the base name and lowercases the extension', () => {
|
|
24
|
+
expect(resolveUploadFilename('Meeting Agenda.PDF', undefined, ctx)).toBe('meeting-agenda.pdf');
|
|
25
|
+
});
|
|
26
|
+
it('handles filenames without an extension', () => {
|
|
27
|
+
expect(resolveUploadFilename('README', undefined, ctx)).toBe('readme');
|
|
28
|
+
});
|
|
29
|
+
it('treats a leading dot as part of the base name, not an extension', () => {
|
|
30
|
+
expect(resolveUploadFilename('.gitignore', undefined, ctx)).toBe('.gitignore');
|
|
31
|
+
});
|
|
32
|
+
it('only splits at the last dot', () => {
|
|
33
|
+
expect(resolveUploadFilename('archive.tar.gz', undefined, ctx)).toBe('archive.tar.gz');
|
|
34
|
+
});
|
|
35
|
+
it('applies a custom slugifier to the base name only', () => {
|
|
36
|
+
const shouty = (base) => base.toUpperCase().replace(/[^A-Z0-9]/g, '_');
|
|
37
|
+
expect(resolveUploadFilename('Meeting Agenda.pdf', shouty, ctx)).toBe('MEETING_AGENDA.pdf');
|
|
38
|
+
});
|
|
39
|
+
it('passes the slugify context through to the custom slugifier', () => {
|
|
40
|
+
const seen = [];
|
|
41
|
+
resolveUploadFilename('photo.jpg', (base, c) => {
|
|
42
|
+
seen.push(c);
|
|
43
|
+
return base;
|
|
44
|
+
}, ctx);
|
|
45
|
+
expect(seen).toEqual([ctx]);
|
|
46
|
+
});
|
|
47
|
+
it('guards against a custom slugifier returning an empty string', () => {
|
|
48
|
+
expect(resolveUploadFilename('photo.jpg', () => '', ctx)).toBe('file.jpg');
|
|
49
|
+
});
|
|
50
|
+
});
|
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": "4.
|
|
5
|
+
"version": "4.2.0",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.9.0"
|
|
8
8
|
},
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"pino": "^10.3.1",
|
|
82
82
|
"sharp": "^0.35.3",
|
|
83
83
|
"zod": "^4.4.3",
|
|
84
|
-
"@byline/auth": "4.
|
|
84
|
+
"@byline/auth": "4.2.0"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
87
|
"@biomejs/biome": "2.5.2",
|