@byline/core 1.8.0 → 1.8.2
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/collection-types.d.ts +10 -12
- package/dist/@types/db-types.d.ts +10 -9
- package/dist/@types/site-config.d.ts +3 -2
- package/dist/auth/assert-actor-can-perform.d.ts +1 -1
- package/dist/auth/assert-actor-can-perform.js +1 -1
- package/dist/auth/register-collection-abilities.d.ts +1 -1
- package/dist/auth/register-collection-abilities.js +1 -1
- package/dist/core.d.ts +2 -3
- package/dist/query/parse-where.d.ts +4 -1
- package/dist/query/parse-where.test.node.js +4 -4
- package/dist/services/document-lifecycle.d.ts +5 -3
- package/dist/services/document-lifecycle.js +2 -1
- package/dist/services/populate.d.ts +1 -1
- package/dist/services/walk-field-tree.d.ts +1 -2
- package/dist/services/walk-field-tree.js +1 -2
- package/dist/utils/slugify.d.ts +2 -1
- package/package.json +2 -2
|
@@ -516,9 +516,7 @@ export interface UploadHooks {
|
|
|
516
516
|
* by populate. A hook that performs its own reads should thread this
|
|
517
517
|
* context back in via `client.collection(...).findById(id, { _readContext:
|
|
518
518
|
* readContext })` so the visited set and read budget are preserved —
|
|
519
|
-
* essential to foreclose the A→B→A loop (see
|
|
520
|
-
* `docs/analysis/RELATIONSHIPS-ANALYSIS.md` § "Special consideration:
|
|
521
|
-
* recursive-read safety").
|
|
519
|
+
* essential to foreclose the A→B→A loop (see `docs/RELATIONSHIPS.md`).
|
|
522
520
|
*/
|
|
523
521
|
export interface AfterReadContext {
|
|
524
522
|
/** The raw reconstructed document. Mutate in place — changes persist. */
|
|
@@ -551,9 +549,8 @@ export interface AfterReadContext {
|
|
|
551
549
|
* - `collectionPath` — the collection being queried (useful when the
|
|
552
550
|
* same hook function is reused across collections).
|
|
553
551
|
*
|
|
554
|
-
* See `docs/
|
|
555
|
-
*
|
|
556
|
-
* examples.
|
|
552
|
+
* See `docs/AUTHN-AUTHZ.md` for the strategic rationale and
|
|
553
|
+
* `docs/ACCESS-CONTROL-RECIPES.md` for worked examples.
|
|
557
554
|
*/
|
|
558
555
|
export interface BeforeReadContext {
|
|
559
556
|
collectionPath: string;
|
|
@@ -630,7 +627,7 @@ export interface CollectionHooks {
|
|
|
630
627
|
* read-side row scoping (multi-tenant, owner-only-drafts, soft-delete
|
|
631
628
|
* hide, etc). Returning `void` applies no scoping. Multiple functions
|
|
632
629
|
* combine with implicit AND. See
|
|
633
|
-
* `docs/
|
|
630
|
+
* `docs/ACCESS-CONTROL-RECIPES.md`.
|
|
634
631
|
*/
|
|
635
632
|
beforeRead?: BeforeReadHookSlot;
|
|
636
633
|
/**
|
|
@@ -675,11 +672,12 @@ export interface CollectionDefinition {
|
|
|
675
672
|
*/
|
|
676
673
|
useAsTitle?: string;
|
|
677
674
|
/**
|
|
678
|
-
* Names the field whose value initialises
|
|
679
|
-
* `
|
|
680
|
-
*
|
|
681
|
-
*
|
|
682
|
-
*
|
|
675
|
+
* Names the field whose value initialises a document's `path` row in
|
|
676
|
+
* `byline_document_paths` (the dedicated per-(document, locale) URL slug
|
|
677
|
+
* table, separate from `documentVersions`). The value is slugified (in
|
|
678
|
+
* the default content locale) using the installation slugifier and
|
|
679
|
+
* stored as system metadata — `path` itself is a reserved name and
|
|
680
|
+
* cannot be declared as a field.
|
|
683
681
|
*
|
|
684
682
|
* `path` is sticky after creation: subsequent updates do not
|
|
685
683
|
* re-derive. Users edit it via the system path widget; collections
|
|
@@ -285,10 +285,9 @@ export interface IDocumentQueries {
|
|
|
285
285
|
*/
|
|
286
286
|
filters?: DocumentFilter[];
|
|
287
287
|
/**
|
|
288
|
-
* Request-scoped auth context.
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
* See docs/analysis/AUTHN-AUTHZ-ANALYSIS.md.
|
|
288
|
+
* Request-scoped auth context. Adapters thread it through to
|
|
289
|
+
* `assertActorCanPerform` for ability assertion and to `beforeRead`
|
|
290
|
+
* hooks for query scoping. See docs/AUTHN-AUTHZ.md.
|
|
292
291
|
*/
|
|
293
292
|
requestContext?: RequestContext;
|
|
294
293
|
/**
|
|
@@ -306,9 +305,12 @@ export interface IDocumentQueries {
|
|
|
306
305
|
* Fetch only the current version's metadata row (no field reconstruction).
|
|
307
306
|
*
|
|
308
307
|
* Use this when the caller only needs `{document_version_id, status,
|
|
309
|
-
*
|
|
310
|
-
* status and version ID before mutating. Skipping
|
|
311
|
-
* the full 7-way UNION ALL and the meta-row fetch.
|
|
308
|
+
* created_at, updated_at}` — for example, workflow transitions that read
|
|
309
|
+
* the current status and version ID before mutating. Skipping
|
|
310
|
+
* reconstruction avoids the full 7-way UNION ALL and the meta-row fetch.
|
|
311
|
+
*
|
|
312
|
+
* `path` is intentionally not returned: callers that need it should
|
|
313
|
+
* use `getDocumentById` (which projects the locale-resolved path).
|
|
312
314
|
*
|
|
313
315
|
* Returns `null` when the document does not exist (or has been soft-deleted).
|
|
314
316
|
*/
|
|
@@ -319,7 +321,6 @@ export interface IDocumentQueries {
|
|
|
319
321
|
document_version_id: string;
|
|
320
322
|
document_id: string;
|
|
321
323
|
collection_id: string;
|
|
322
|
-
path: string;
|
|
323
324
|
status: string;
|
|
324
325
|
created_at: Date;
|
|
325
326
|
updated_at: Date;
|
|
@@ -462,7 +463,7 @@ export interface IDocumentQueries {
|
|
|
462
463
|
/** Text search across the collection's configured search fields. */
|
|
463
464
|
query?: string;
|
|
464
465
|
sort?: FieldSort;
|
|
465
|
-
/** Document-level sort column (created_at
|
|
466
|
+
/** Document-level sort column (`created_at`, `updated_at`). Used when `sort` is not a field-level sort. */
|
|
466
467
|
orderBy?: string;
|
|
467
468
|
orderDirection?: 'asc' | 'desc';
|
|
468
469
|
locale?: string;
|
|
@@ -123,8 +123,9 @@ export interface ServerConfig<TAdminStore = unknown> extends BaseConfig {
|
|
|
123
123
|
*/
|
|
124
124
|
storage?: IStorageProvider;
|
|
125
125
|
/**
|
|
126
|
-
* Installation-wide slugifier used to derive `
|
|
127
|
-
* from the field named by
|
|
126
|
+
* Installation-wide slugifier used to derive a document's `path` (stored
|
|
127
|
+
* in `byline_document_paths`) from the field named by
|
|
128
|
+
* `CollectionDefinition.useAsPath`.
|
|
128
129
|
*
|
|
129
130
|
* Falls back to the default `slugify` from `@byline/core` when not set.
|
|
130
131
|
* Must be pure and synchronous — it runs server-side at write time and
|
|
@@ -33,6 +33,6 @@ import { type CollectionAbilityVerb } from './register-collection-abilities.js';
|
|
|
33
33
|
* bypass this helper — the same escape hatch that skips collection
|
|
34
34
|
* hooks. Seeds, migrations, and internal tooling live there.
|
|
35
35
|
*
|
|
36
|
-
* See docs/
|
|
36
|
+
* See docs/AUTHN-AUTHZ.md.
|
|
37
37
|
*/
|
|
38
38
|
export declare function assertActorCanPerform(context: RequestContext | undefined, collectionPath: string, verb: CollectionAbilityVerb): void;
|
|
@@ -33,7 +33,7 @@ import { collectionAbilityKey, } from './register-collection-abilities.js';
|
|
|
33
33
|
* bypass this helper — the same escape hatch that skips collection
|
|
34
34
|
* hooks. Seeds, migrations, and internal tooling live there.
|
|
35
35
|
*
|
|
36
|
-
* See docs/
|
|
36
|
+
* See docs/AUTHN-AUTHZ.md.
|
|
37
37
|
*/
|
|
38
38
|
export function assertActorCanPerform(context, collectionPath, verb) {
|
|
39
39
|
if (!context) {
|
|
@@ -29,7 +29,7 @@ import type { CollectionDefinition } from '../@types/index.js';
|
|
|
29
29
|
* avoids hidden conditional logic downstream.
|
|
30
30
|
*
|
|
31
31
|
* Called from `initBylineCore()` for each declared collection. See
|
|
32
|
-
* docs/
|
|
32
|
+
* docs/AUTHN-AUTHZ.md.
|
|
33
33
|
*/
|
|
34
34
|
export declare function registerCollectionAbilities(registry: AbilityRegistry, definition: CollectionDefinition): void;
|
|
35
35
|
/** The ability suffixes that every collection contributes. Exposed for contract tests. */
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* avoids hidden conditional logic downstream.
|
|
28
28
|
*
|
|
29
29
|
* Called from `initBylineCore()` for each declared collection. See
|
|
30
|
-
* docs/
|
|
30
|
+
* docs/AUTHN-AUTHZ.md.
|
|
31
31
|
*/
|
|
32
32
|
export function registerCollectionAbilities(registry, definition) {
|
|
33
33
|
const path = definition.path;
|
package/dist/core.d.ts
CHANGED
|
@@ -37,9 +37,8 @@ export interface BylineCore<TAdminStore = unknown> {
|
|
|
37
37
|
* `registerAbility()` — or directly against `core.abilities` — typically
|
|
38
38
|
* during server bootstrap and before any admin UI renders.
|
|
39
39
|
*
|
|
40
|
-
* Consumed at runtime by `AdminAuth.assertAbility()`
|
|
41
|
-
*
|
|
42
|
-
* docs/analysis/AUTHN-AUTHZ-ANALYSIS.md §3.
|
|
40
|
+
* Consumed at runtime by `AdminAuth.assertAbility()` and at design
|
|
41
|
+
* time by the admin role-editor UI. See docs/AUTHN-AUTHZ.md.
|
|
43
42
|
*/
|
|
44
43
|
abilities: AbilityRegistry;
|
|
45
44
|
/** Convenience wrapper around `abilities.register()`. */
|
|
@@ -43,7 +43,10 @@ export interface ParsedWhere {
|
|
|
43
43
|
status?: string;
|
|
44
44
|
/** Text search query (for collection-configured search fields). */
|
|
45
45
|
query?: string;
|
|
46
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Filter on a document's `path` (resolved against `byline_document_paths`
|
|
48
|
+
* via the locale priority chain) with an operator.
|
|
49
|
+
*/
|
|
47
50
|
pathFilter?: {
|
|
48
51
|
operator: FieldFilterOperator;
|
|
49
52
|
value: string;
|
|
@@ -41,7 +41,7 @@ const categoriesCollection = defineCollection({
|
|
|
41
41
|
fields: [
|
|
42
42
|
{ name: 'name', type: 'text', label: 'Name', localized: true },
|
|
43
43
|
// `slug`, not `path` — `path` is a reserved key that resolves to the
|
|
44
|
-
// target
|
|
44
|
+
// target document's `byline_document_paths` row inside a nested
|
|
45
45
|
// sub-clause (same precedence as the top level), so a real `path`
|
|
46
46
|
// field would be unreachable through the where clause.
|
|
47
47
|
{ name: 'slug', type: 'text', label: 'Slug' },
|
|
@@ -245,9 +245,9 @@ describe('parseWhere', () => {
|
|
|
245
245
|
});
|
|
246
246
|
it('promotes `path` inside a nested sub-where to a DocumentColumnFilter', async () => {
|
|
247
247
|
// Reserved-key precedence: `path` inside a relation sub-clause maps to
|
|
248
|
-
// the target
|
|
249
|
-
//
|
|
250
|
-
//
|
|
248
|
+
// the target document's `byline_document_paths` row, never to a field
|
|
249
|
+
// of the same name. The adapter resolves it via a `pathProjection`
|
|
250
|
+
// subquery scoped to the inner relation hop's `td${depth}.document_id`.
|
|
251
251
|
const result = await parseWhere({ category: { path: 'news' } }, testCollection, ctx);
|
|
252
252
|
expect(result.filters).toHaveLength(1);
|
|
253
253
|
expect(result.filters[0]).toEqual({
|
|
@@ -83,10 +83,12 @@ export interface DocumentLifecycleContext {
|
|
|
83
83
|
* entry points will call `context.requestContext?.actor?.assertAbility(...)`
|
|
84
84
|
* before any storage mutation.
|
|
85
85
|
*
|
|
86
|
-
* Optional
|
|
87
|
-
*
|
|
86
|
+
* Optional so that internal-tooling callers (seed scripts, migration
|
|
87
|
+
* tools) continue to compile. Production write paths always supply it
|
|
88
|
+
* — `assertActorCanPerform` runs at every lifecycle entry and rejects
|
|
89
|
+
* a missing context.
|
|
88
90
|
*
|
|
89
|
-
* See docs/
|
|
91
|
+
* See docs/AUTHN-AUTHZ.md.
|
|
90
92
|
*/
|
|
91
93
|
requestContext?: RequestContext;
|
|
92
94
|
}
|
|
@@ -96,7 +96,8 @@ function extractDocumentId(document) {
|
|
|
96
96
|
return document?.document_id ?? '';
|
|
97
97
|
}
|
|
98
98
|
/**
|
|
99
|
-
* Derive
|
|
99
|
+
* Derive the `path` value written into `byline_document_paths` at
|
|
100
|
+
* create time.
|
|
100
101
|
*
|
|
101
102
|
* 1. `definition.useAsPath` set → slugify the named source field's value
|
|
102
103
|
* in the default content locale.
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* documents. The guard is in place from day one so that the hook work
|
|
26
26
|
* in Phase 4+ cannot reintroduce the problem.
|
|
27
27
|
*
|
|
28
|
-
* See docs/
|
|
28
|
+
* See docs/RELATIONSHIPS.md for the full design rationale.
|
|
29
29
|
*
|
|
30
30
|
* ---------------------------------------------------------------------
|
|
31
31
|
* DSL summary
|
|
@@ -15,8 +15,7 @@
|
|
|
15
15
|
* shape, populate-spec match, `_resolved` skip, richText null handling).
|
|
16
16
|
*
|
|
17
17
|
* Both `collectRelationLeaves` (populate.ts) and `collectRichTextLeaves`
|
|
18
|
-
* (richtext-populate.ts) are
|
|
19
|
-
* docs/TODO.md "walkFieldTree" entry for the rationale.
|
|
18
|
+
* (richtext-populate.ts) are thin filters over this primitive.
|
|
20
19
|
*/
|
|
21
20
|
import { type Field, type FieldSet } from '../@types/field-types.js';
|
|
22
21
|
/**
|
|
@@ -15,8 +15,7 @@
|
|
|
15
15
|
* shape, populate-spec match, `_resolved` skip, richText null handling).
|
|
16
16
|
*
|
|
17
17
|
* Both `collectRelationLeaves` (populate.ts) and `collectRichTextLeaves`
|
|
18
|
-
* (richtext-populate.ts) are
|
|
19
|
-
* docs/TODO.md "walkFieldTree" entry for the rationale.
|
|
18
|
+
* (richtext-populate.ts) are thin filters over this primitive.
|
|
20
19
|
*/
|
|
21
20
|
import { isArrayField, isBlocksField, isGroupField, } from '../@types/field-types.js';
|
|
22
21
|
/**
|
package/dist/utils/slugify.d.ts
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
* Context passed to a `SlugifierFn`.
|
|
10
10
|
*
|
|
11
11
|
* `locale` - The content locale being slugified (e.g. the default
|
|
12
|
-
* content locale when deriving `
|
|
12
|
+
* content locale when deriving a document's `path`
|
|
13
|
+
* row in `byline_document_paths`).
|
|
13
14
|
* `collectionPath` - The collection that owns the value being slugified.
|
|
14
15
|
* Lets installation-supplied slugifiers branch by
|
|
15
16
|
* collection if URL policies differ.
|
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": "1.8.
|
|
5
|
+
"version": "1.8.2",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.9.0"
|
|
8
8
|
},
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"sharp": "^0.34.5",
|
|
80
80
|
"uuid": "^14.0.0",
|
|
81
81
|
"zod": "^4.4.2",
|
|
82
|
-
"@byline/auth": "1.8.
|
|
82
|
+
"@byline/auth": "1.8.2"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@biomejs/biome": "2.4.14",
|