@byline/core 0.9.3
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/LICENSE +373 -0
- package/README.md +17 -0
- package/dist/@types/admin-types.d.ts +275 -0
- package/dist/@types/admin-types.d.ts.map +1 -0
- package/dist/@types/admin-types.js +18 -0
- package/dist/@types/admin-types.js.map +1 -0
- package/dist/@types/collection-types.d.ts +816 -0
- package/dist/@types/collection-types.d.ts.map +1 -0
- package/dist/@types/collection-types.js +217 -0
- package/dist/@types/collection-types.js.map +1 -0
- package/dist/@types/db-types.d.ts +463 -0
- package/dist/@types/db-types.d.ts.map +1 -0
- package/dist/@types/db-types.js +2 -0
- package/dist/@types/db-types.js.map +1 -0
- package/dist/@types/field-data-types.d.ts +147 -0
- package/dist/@types/field-data-types.d.ts.map +1 -0
- package/dist/@types/field-data-types.js +38 -0
- package/dist/@types/field-data-types.js.map +1 -0
- package/dist/@types/field-types.d.ts +579 -0
- package/dist/@types/field-types.d.ts.map +1 -0
- package/dist/@types/field-types.js +32 -0
- package/dist/@types/field-types.js.map +1 -0
- package/dist/@types/index.d.ts +18 -0
- package/dist/@types/index.d.ts.map +1 -0
- package/dist/@types/index.js +18 -0
- package/dist/@types/index.js.map +1 -0
- package/dist/@types/populate-types.d.ts +54 -0
- package/dist/@types/populate-types.d.ts.map +1 -0
- package/dist/@types/populate-types.js +9 -0
- package/dist/@types/populate-types.js.map +1 -0
- package/dist/@types/query-predicate.d.ts +74 -0
- package/dist/@types/query-predicate.d.ts.map +1 -0
- package/dist/@types/query-predicate.js +9 -0
- package/dist/@types/query-predicate.js.map +1 -0
- package/dist/@types/site-config.d.ts +212 -0
- package/dist/@types/site-config.d.ts.map +1 -0
- package/dist/@types/site-config.js +9 -0
- package/dist/@types/site-config.js.map +1 -0
- package/dist/@types/storage-types.d.ts +86 -0
- package/dist/@types/storage-types.d.ts.map +1 -0
- package/dist/@types/storage-types.js +9 -0
- package/dist/@types/storage-types.js.map +1 -0
- package/dist/@types/store-types.d.ts +134 -0
- package/dist/@types/store-types.d.ts.map +1 -0
- package/dist/@types/store-types.js +24 -0
- package/dist/@types/store-types.js.map +1 -0
- package/dist/@types/type-utils.d.ts +17 -0
- package/dist/@types/type-utils.d.ts.map +1 -0
- package/dist/@types/type-utils.js +9 -0
- package/dist/@types/type-utils.js.map +1 -0
- package/dist/auth/apply-before-read.d.ts +36 -0
- package/dist/auth/apply-before-read.d.ts.map +1 -0
- package/dist/auth/apply-before-read.js +68 -0
- package/dist/auth/apply-before-read.js.map +1 -0
- package/dist/auth/apply-before-read.test.node.d.ts +9 -0
- package/dist/auth/apply-before-read.test.node.d.ts.map +1 -0
- package/dist/auth/apply-before-read.test.node.js +144 -0
- package/dist/auth/apply-before-read.test.node.js.map +1 -0
- package/dist/auth/assert-actor-can-perform.d.ts +39 -0
- package/dist/auth/assert-actor-can-perform.d.ts.map +1 -0
- package/dist/auth/assert-actor-can-perform.js +64 -0
- package/dist/auth/assert-actor-can-perform.js.map +1 -0
- package/dist/auth/assert-actor-can-perform.test.node.d.ts +9 -0
- package/dist/auth/assert-actor-can-perform.test.node.d.ts.map +1 -0
- package/dist/auth/assert-actor-can-perform.test.node.js +119 -0
- package/dist/auth/assert-actor-can-perform.test.node.js.map +1 -0
- package/dist/auth/index.d.ts +11 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +11 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/register-collection-abilities.d.ts +40 -0
- package/dist/auth/register-collection-abilities.d.ts.map +1 -0
- package/dist/auth/register-collection-abilities.js +87 -0
- package/dist/auth/register-collection-abilities.js.map +1 -0
- package/dist/auth/register-collection-abilities.test.node.d.ts +9 -0
- package/dist/auth/register-collection-abilities.test.node.d.ts.map +1 -0
- package/dist/auth/register-collection-abilities.test.node.js +124 -0
- package/dist/auth/register-collection-abilities.test.node.js.map +1 -0
- package/dist/config/config.d.ts +10 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +108 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/routes.d.ts +16 -0
- package/dist/config/routes.d.ts.map +1 -0
- package/dist/config/routes.js +26 -0
- package/dist/config/routes.js.map +1 -0
- package/dist/config/validate-admin-configs.d.ts +33 -0
- package/dist/config/validate-admin-configs.d.ts.map +1 -0
- package/dist/config/validate-admin-configs.js +250 -0
- package/dist/config/validate-admin-configs.js.map +1 -0
- package/dist/config/validate-admin-configs.test.node.d.ts +9 -0
- package/dist/config/validate-admin-configs.test.node.d.ts.map +1 -0
- package/dist/config/validate-admin-configs.test.node.js +224 -0
- package/dist/config/validate-admin-configs.test.node.js.map +1 -0
- package/dist/config/validate-collections.d.ts +33 -0
- package/dist/config/validate-collections.d.ts.map +1 -0
- package/dist/config/validate-collections.js +70 -0
- package/dist/config/validate-collections.js.map +1 -0
- package/dist/config/validate-collections.test.node.d.ts +9 -0
- package/dist/config/validate-collections.test.node.d.ts.map +1 -0
- package/dist/config/validate-collections.test.node.js +149 -0
- package/dist/config/validate-collections.test.node.js.map +1 -0
- package/dist/core.d.ts +89 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +99 -0
- package/dist/core.js.map +1 -0
- package/dist/defaults/default-values.d.ts +13 -0
- package/dist/defaults/default-values.d.ts.map +1 -0
- package/dist/defaults/default-values.js +60 -0
- package/dist/defaults/default-values.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/errors.d.ts +98 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +134 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/logger.d.ts +62 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +120 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/registry.d.ts +65 -0
- package/dist/lib/registry.d.ts.map +1 -0
- package/dist/lib/registry.js +133 -0
- package/dist/lib/registry.js.map +1 -0
- package/dist/logger/index.d.ts +3 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/index.js +3 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/patches/apply-patches.d.ts +21 -0
- package/dist/patches/apply-patches.d.ts.map +1 -0
- package/dist/patches/apply-patches.js +357 -0
- package/dist/patches/apply-patches.js.map +1 -0
- package/dist/patches/index.d.ts +3 -0
- package/dist/patches/index.d.ts.map +1 -0
- package/dist/patches/index.js +4 -0
- package/dist/patches/index.js.map +1 -0
- package/dist/patches/patch-types.d.ts +82 -0
- package/dist/patches/patch-types.d.ts.map +1 -0
- package/dist/patches/patch-types.js +3 -0
- package/dist/patches/patch-types.js.map +1 -0
- package/dist/patches/patch.test.node.d.ts +2 -0
- package/dist/patches/patch.test.node.d.ts.map +1 -0
- package/dist/patches/patch.test.node.js +193 -0
- package/dist/patches/patch.test.node.js.map +1 -0
- package/dist/query/parse-where.d.ts +100 -0
- package/dist/query/parse-where.d.ts.map +1 -0
- package/dist/query/parse-where.js +352 -0
- package/dist/query/parse-where.js.map +1 -0
- package/dist/query/parse-where.test.node.d.ts +9 -0
- package/dist/query/parse-where.test.node.d.ts.map +1 -0
- package/dist/query/parse-where.test.node.js +581 -0
- package/dist/query/parse-where.test.node.js.map +1 -0
- package/dist/schemas/zod/builder.d.ts +466 -0
- package/dist/schemas/zod/builder.d.ts.map +1 -0
- package/dist/schemas/zod/builder.js +276 -0
- package/dist/schemas/zod/builder.js.map +1 -0
- package/dist/schemas/zod/cache.d.ts +14 -0
- package/dist/schemas/zod/cache.d.ts.map +1 -0
- package/dist/schemas/zod/cache.js +40 -0
- package/dist/schemas/zod/cache.js.map +1 -0
- package/dist/schemas/zod/index.d.ts +4 -0
- package/dist/schemas/zod/index.d.ts.map +1 -0
- package/dist/schemas/zod/index.js +4 -0
- package/dist/schemas/zod/index.js.map +1 -0
- package/dist/schemas/zod/types.d.ts +13 -0
- package/dist/schemas/zod/types.d.ts.map +1 -0
- package/dist/schemas/zod/types.js +2 -0
- package/dist/schemas/zod/types.js.map +1 -0
- package/dist/services/collection-bootstrap.d.ts +46 -0
- package/dist/services/collection-bootstrap.d.ts.map +1 -0
- package/dist/services/collection-bootstrap.js +108 -0
- package/dist/services/collection-bootstrap.js.map +1 -0
- package/dist/services/collection-bootstrap.test.node.d.ts +9 -0
- package/dist/services/collection-bootstrap.test.node.d.ts.map +1 -0
- package/dist/services/collection-bootstrap.test.node.js +208 -0
- package/dist/services/collection-bootstrap.test.node.js.map +1 -0
- package/dist/services/document-lifecycle.d.ts +245 -0
- package/dist/services/document-lifecycle.d.ts.map +1 -0
- package/dist/services/document-lifecycle.js +481 -0
- package/dist/services/document-lifecycle.js.map +1 -0
- package/dist/services/document-lifecycle.test.node.d.ts +9 -0
- package/dist/services/document-lifecycle.test.node.d.ts.map +1 -0
- package/dist/services/document-lifecycle.test.node.js +781 -0
- package/dist/services/document-lifecycle.test.node.js.map +1 -0
- package/dist/services/document-read.d.ts +26 -0
- package/dist/services/document-read.d.ts.map +1 -0
- package/dist/services/document-read.js +60 -0
- package/dist/services/document-read.js.map +1 -0
- package/dist/services/field-upload.d.ts +100 -0
- package/dist/services/field-upload.d.ts.map +1 -0
- package/dist/services/field-upload.js +328 -0
- package/dist/services/field-upload.js.map +1 -0
- package/dist/services/field-upload.test.node.d.ts +9 -0
- package/dist/services/field-upload.test.node.d.ts.map +1 -0
- package/dist/services/field-upload.test.node.js +337 -0
- package/dist/services/field-upload.test.node.js.map +1 -0
- package/dist/services/index.d.ts +10 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +11 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/populate.d.ts +299 -0
- package/dist/services/populate.d.ts.map +1 -0
- package/dist/services/populate.js +484 -0
- package/dist/services/populate.js.map +1 -0
- package/dist/services/populate.test.node.d.ts +9 -0
- package/dist/services/populate.test.node.d.ts.map +1 -0
- package/dist/services/populate.test.node.js +910 -0
- package/dist/services/populate.test.node.js.map +1 -0
- package/dist/services/relation-projection.d.ts +52 -0
- package/dist/services/relation-projection.d.ts.map +1 -0
- package/dist/services/relation-projection.js +81 -0
- package/dist/services/relation-projection.js.map +1 -0
- package/dist/services/richtext-populate.d.ts +87 -0
- package/dist/services/richtext-populate.d.ts.map +1 -0
- package/dist/services/richtext-populate.js +189 -0
- package/dist/services/richtext-populate.js.map +1 -0
- package/dist/services/richtext-populate.test.node.d.ts +9 -0
- package/dist/services/richtext-populate.test.node.d.ts.map +1 -0
- package/dist/services/richtext-populate.test.node.js +197 -0
- package/dist/services/richtext-populate.test.node.js.map +1 -0
- package/dist/storage/collection-fingerprint.d.ts +21 -0
- package/dist/storage/collection-fingerprint.d.ts.map +1 -0
- package/dist/storage/collection-fingerprint.js +172 -0
- package/dist/storage/collection-fingerprint.js.map +1 -0
- package/dist/storage/collection-fingerprint.test.node.d.ts +9 -0
- package/dist/storage/collection-fingerprint.test.node.d.ts.map +1 -0
- package/dist/storage/collection-fingerprint.test.node.js +256 -0
- package/dist/storage/collection-fingerprint.test.node.js.map +1 -0
- package/dist/storage/field-store-map.d.ts +59 -0
- package/dist/storage/field-store-map.d.ts.map +1 -0
- package/dist/storage/field-store-map.js +75 -0
- package/dist/storage/field-store-map.js.map +1 -0
- package/dist/storage/field-store-map.test.node.d.ts +9 -0
- package/dist/storage/field-store-map.test.node.d.ts.map +1 -0
- package/dist/storage/field-store-map.test.node.js +117 -0
- package/dist/storage/field-store-map.test.node.js.map +1 -0
- package/dist/storage/index.d.ts +10 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +10 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/utils/normalise-dates.d.ts +15 -0
- package/dist/utils/normalise-dates.d.ts.map +1 -0
- package/dist/utils/normalise-dates.js +22 -0
- package/dist/utils/normalise-dates.js.map +1 -0
- package/dist/utils/slugify.d.ts +56 -0
- package/dist/utils/slugify.d.ts.map +1 -0
- package/dist/utils/slugify.js +91 -0
- package/dist/utils/slugify.js.map +1 -0
- package/dist/utils/slugify.test.node.d.ts +9 -0
- package/dist/utils/slugify.test.node.d.ts.map +1 -0
- package/dist/utils/slugify.test.node.js +86 -0
- package/dist/utils/slugify.test.node.js.map +1 -0
- package/dist/utils/storage-utils.d.ts +36 -0
- package/dist/utils/storage-utils.d.ts.map +1 -0
- package/dist/utils/storage-utils.js +38 -0
- package/dist/utils/storage-utils.js.map +1 -0
- package/dist/utils/utils.general.d.ts +64 -0
- package/dist/utils/utils.general.d.ts.map +1 -0
- package/dist/utils/utils.general.js +219 -0
- package/dist/utils/utils.general.js.map +1 -0
- package/dist/validation/index.d.ts +9 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +9 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/shared.d.ts +36 -0
- package/dist/validation/shared.d.ts.map +1 -0
- package/dist/validation/shared.js +42 -0
- package/dist/validation/shared.js.map +1 -0
- package/dist/workflow/index.d.ts +2 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +3 -0
- package/dist/workflow/index.js.map +1 -0
- package/dist/workflow/workflow.d.ts +40 -0
- package/dist/workflow/workflow.d.ts.map +1 -0
- package/dist/workflow/workflow.js +96 -0
- package/dist/workflow/workflow.js.map +1 -0
- package/dist/workflow/workflow.test.node.d.ts +2 -0
- package/dist/workflow/workflow.test.node.d.ts.map +1 -0
- package/dist/workflow/workflow.test.node.js +198 -0
- package/dist/workflow/workflow.test.node.js.map +1 -0
- package/package.json +88 -0
|
@@ -0,0 +1,208 @@
|
|
|
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, vi } from 'vitest';
|
|
9
|
+
import { fingerprintCollection } from '../storage/collection-fingerprint.js';
|
|
10
|
+
import { ensureCollections } from './collection-bootstrap.js';
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Fixtures
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
function baseCollection() {
|
|
15
|
+
return {
|
|
16
|
+
path: 'news',
|
|
17
|
+
labels: { singular: 'News', plural: 'News' },
|
|
18
|
+
fields: [{ name: 'title', type: 'text' }],
|
|
19
|
+
workflow: {
|
|
20
|
+
statuses: [{ name: 'draft' }, { name: 'published' }, { name: 'archived' }],
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
// Build a minimal IDbAdapter. We only wire the methods `ensureCollections`
|
|
25
|
+
// actually calls; the others throw if touched so we catch accidental usage.
|
|
26
|
+
function createMockDb(options) {
|
|
27
|
+
const getCollectionByPath = vi.fn().mockResolvedValue(options.existingRow ?? null);
|
|
28
|
+
const create = vi
|
|
29
|
+
.fn()
|
|
30
|
+
.mockImplementation(async (_path, _config, _opts) => [
|
|
31
|
+
{ id: 'col-new' },
|
|
32
|
+
]);
|
|
33
|
+
const update = vi.fn().mockResolvedValue([{ id: options.existingRow?.id ?? 'col-new' }]);
|
|
34
|
+
const fail = () => {
|
|
35
|
+
throw new Error('not expected to be called');
|
|
36
|
+
};
|
|
37
|
+
const db = {
|
|
38
|
+
commands: {
|
|
39
|
+
collections: { create, update, delete: vi.fn(fail) },
|
|
40
|
+
documents: {
|
|
41
|
+
createDocumentVersion: vi.fn(fail),
|
|
42
|
+
setDocumentStatus: vi.fn(fail),
|
|
43
|
+
archivePublishedVersions: vi.fn(fail),
|
|
44
|
+
softDeleteDocument: vi.fn(fail),
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
queries: {
|
|
48
|
+
collections: {
|
|
49
|
+
getAllCollections: vi.fn(fail),
|
|
50
|
+
getCollectionByPath,
|
|
51
|
+
getCollectionById: vi.fn(fail),
|
|
52
|
+
},
|
|
53
|
+
documents: {
|
|
54
|
+
getDocumentById: vi.fn(fail),
|
|
55
|
+
getCurrentVersionMetadata: vi.fn(fail),
|
|
56
|
+
getDocumentByPath: vi.fn(fail),
|
|
57
|
+
getDocumentByVersion: vi.fn(fail),
|
|
58
|
+
getDocumentsByVersionIds: vi.fn(fail),
|
|
59
|
+
getDocumentsByDocumentIds: vi.fn(fail),
|
|
60
|
+
getDocumentHistory: vi.fn(fail),
|
|
61
|
+
getPublishedVersion: vi.fn(fail),
|
|
62
|
+
getPublishedDocumentIds: vi.fn(fail),
|
|
63
|
+
getDocumentCountsByStatus: vi.fn(fail),
|
|
64
|
+
findDocuments: vi.fn(fail),
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
return { db, create, update, getCollectionByPath };
|
|
69
|
+
}
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Tests
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
describe('ensureCollections', () => {
|
|
74
|
+
it('inserts a new row with version 1 and the schema fingerprint', async () => {
|
|
75
|
+
const def = baseCollection();
|
|
76
|
+
const { db, create, update } = createMockDb({ existingRow: null });
|
|
77
|
+
const records = await ensureCollections({ definitions: [def], db });
|
|
78
|
+
expect(create).toHaveBeenCalledTimes(1);
|
|
79
|
+
expect(update).not.toHaveBeenCalled();
|
|
80
|
+
const [path, config, opts] = create.mock.calls[0];
|
|
81
|
+
expect(path).toBe('news');
|
|
82
|
+
expect(config).toBe(def);
|
|
83
|
+
const expectedHash = await fingerprintCollection(def);
|
|
84
|
+
expect(opts).toEqual({ version: 1, schemaHash: expectedHash });
|
|
85
|
+
expect(records.get('news')).toEqual({
|
|
86
|
+
collectionId: 'col-new',
|
|
87
|
+
version: 1,
|
|
88
|
+
schemaHash: expectedHash,
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
it('honours an explicit version pin on first insert', async () => {
|
|
92
|
+
const def = { ...baseCollection(), version: 5 };
|
|
93
|
+
const { db, create } = createMockDb({ existingRow: null });
|
|
94
|
+
await ensureCollections({ definitions: [def], db });
|
|
95
|
+
const [, , opts] = create.mock.calls[0];
|
|
96
|
+
expect(opts.version).toBe(5);
|
|
97
|
+
});
|
|
98
|
+
it('is a no-op when the stored hash matches the current fingerprint', async () => {
|
|
99
|
+
const def = baseCollection();
|
|
100
|
+
const hash = await fingerprintCollection(def);
|
|
101
|
+
const { db, create, update } = createMockDb({
|
|
102
|
+
existingRow: { id: 'col-1', version: 3, schema_hash: hash },
|
|
103
|
+
});
|
|
104
|
+
const records = await ensureCollections({ definitions: [def], db });
|
|
105
|
+
expect(create).not.toHaveBeenCalled();
|
|
106
|
+
expect(update).not.toHaveBeenCalled();
|
|
107
|
+
expect(records.get('news')).toEqual({
|
|
108
|
+
collectionId: 'col-1',
|
|
109
|
+
version: 3,
|
|
110
|
+
schemaHash: hash,
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
it('auto-bumps the version when the fingerprint changes', async () => {
|
|
114
|
+
const def = baseCollection();
|
|
115
|
+
// Seed the DB with a mismatched hash.
|
|
116
|
+
const { db, update } = createMockDb({
|
|
117
|
+
existingRow: { id: 'col-1', version: 3, schema_hash: 'stale-hash' },
|
|
118
|
+
});
|
|
119
|
+
const records = await ensureCollections({ definitions: [def], db });
|
|
120
|
+
expect(update).toHaveBeenCalledTimes(1);
|
|
121
|
+
const [id, patch] = update.mock.calls[0];
|
|
122
|
+
expect(id).toBe('col-1');
|
|
123
|
+
expect(patch).toEqual({
|
|
124
|
+
config: def,
|
|
125
|
+
version: 4,
|
|
126
|
+
schemaHash: await fingerprintCollection(def),
|
|
127
|
+
});
|
|
128
|
+
expect(records.get('news')?.version).toBe(4);
|
|
129
|
+
});
|
|
130
|
+
it('uses an explicit pin on update when >= the stored version', async () => {
|
|
131
|
+
const def = { ...baseCollection(), version: 9 };
|
|
132
|
+
const { db, update } = createMockDb({
|
|
133
|
+
existingRow: { id: 'col-1', version: 3, schema_hash: 'stale' },
|
|
134
|
+
});
|
|
135
|
+
const records = await ensureCollections({ definitions: [def], db });
|
|
136
|
+
expect(update.mock.calls[0]?.[1].version).toBe(9);
|
|
137
|
+
expect(records.get('news')?.version).toBe(9);
|
|
138
|
+
});
|
|
139
|
+
it('throws when an explicit pin is less than the stored version', async () => {
|
|
140
|
+
const def = { ...baseCollection(), version: 2 };
|
|
141
|
+
const { db } = createMockDb({
|
|
142
|
+
existingRow: { id: 'col-1', version: 5, schema_hash: 'stale' },
|
|
143
|
+
});
|
|
144
|
+
await expect(ensureCollections({ definitions: [def], db })).rejects.toThrow(/backwards/i);
|
|
145
|
+
});
|
|
146
|
+
it('backfills schema_hash without bumping when stored hash is null', async () => {
|
|
147
|
+
const def = baseCollection();
|
|
148
|
+
const { db, update } = createMockDb({
|
|
149
|
+
existingRow: { id: 'col-1', version: 3, schema_hash: null },
|
|
150
|
+
});
|
|
151
|
+
const records = await ensureCollections({ definitions: [def], db });
|
|
152
|
+
expect(update).toHaveBeenCalledTimes(1);
|
|
153
|
+
const [, patch] = update.mock.calls[0];
|
|
154
|
+
expect(patch.version).toBe(3); // unchanged
|
|
155
|
+
expect(patch.schemaHash).toBe(await fingerprintCollection(def));
|
|
156
|
+
expect(records.get('news')?.version).toBe(3);
|
|
157
|
+
});
|
|
158
|
+
it('reconciles multiple collections in one pass', async () => {
|
|
159
|
+
const a = baseCollection();
|
|
160
|
+
const b = { ...baseCollection(), path: 'pages' };
|
|
161
|
+
const hashA = await fingerprintCollection(a);
|
|
162
|
+
// For `a` the DB matches; for `b` it does not exist yet.
|
|
163
|
+
const getCollectionByPath = vi.fn(async (path) => {
|
|
164
|
+
if (path === 'news')
|
|
165
|
+
return { id: 'col-news', version: 2, schema_hash: hashA };
|
|
166
|
+
return null;
|
|
167
|
+
});
|
|
168
|
+
const create = vi.fn().mockResolvedValue([{ id: 'col-pages' }]);
|
|
169
|
+
const update = vi.fn();
|
|
170
|
+
const db = {
|
|
171
|
+
commands: {
|
|
172
|
+
collections: { create, update, delete: vi.fn() },
|
|
173
|
+
documents: {
|
|
174
|
+
createDocumentVersion: vi.fn(),
|
|
175
|
+
setDocumentStatus: vi.fn(),
|
|
176
|
+
archivePublishedVersions: vi.fn(),
|
|
177
|
+
softDeleteDocument: vi.fn(),
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
queries: {
|
|
181
|
+
collections: {
|
|
182
|
+
getAllCollections: vi.fn(),
|
|
183
|
+
getCollectionByPath,
|
|
184
|
+
getCollectionById: vi.fn(),
|
|
185
|
+
},
|
|
186
|
+
documents: {
|
|
187
|
+
getDocumentById: vi.fn(),
|
|
188
|
+
getCurrentVersionMetadata: vi.fn(),
|
|
189
|
+
getDocumentByPath: vi.fn(),
|
|
190
|
+
getDocumentByVersion: vi.fn(),
|
|
191
|
+
getDocumentsByVersionIds: vi.fn(),
|
|
192
|
+
getDocumentsByDocumentIds: vi.fn(),
|
|
193
|
+
getDocumentHistory: vi.fn(),
|
|
194
|
+
getPublishedVersion: vi.fn(),
|
|
195
|
+
getPublishedDocumentIds: vi.fn(),
|
|
196
|
+
getDocumentCountsByStatus: vi.fn(),
|
|
197
|
+
findDocuments: vi.fn(),
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
const records = await ensureCollections({ definitions: [a, b], db });
|
|
202
|
+
expect(records.get('news')?.version).toBe(2);
|
|
203
|
+
expect(records.get('pages')?.version).toBe(1);
|
|
204
|
+
expect(create).toHaveBeenCalledTimes(1);
|
|
205
|
+
expect(update).not.toHaveBeenCalled();
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
//# sourceMappingURL=collection-bootstrap.test.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection-bootstrap.test.node.js","sourceRoot":"","sources":["../../src/services/collection-bootstrap.test.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,245 @@
|
|
|
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
|
+
* Document lifecycle service.
|
|
10
|
+
*
|
|
11
|
+
* Orchestrates CRUD operations and workflow transitions, invoking collection
|
|
12
|
+
* hooks at the appropriate points. Sits between the API route layer and the
|
|
13
|
+
* storage adapter (`IDbAdapter`) so that every operation path — POST, PUT,
|
|
14
|
+
* PATCH, status change, unpublish — goes through a single set of hooks.
|
|
15
|
+
*
|
|
16
|
+
* Hook invocations run **outside** the storage transaction. They are suitable
|
|
17
|
+
* for logging, cache invalidation, webhooks, and similar side-effects.
|
|
18
|
+
*
|
|
19
|
+
* This module depends only on `@byline/core` types and utilities — it has no
|
|
20
|
+
* dependency on any specific database adapter.
|
|
21
|
+
*/
|
|
22
|
+
import type { RequestContext } from '@byline/auth';
|
|
23
|
+
import { type CollectionDefinition, type IDbAdapter, type IStorageProvider } from '../@types/index.js';
|
|
24
|
+
import { type SlugifierFn } from '../utils/slugify.js';
|
|
25
|
+
import type { BylineLogger } from '../lib/logger.js';
|
|
26
|
+
import type { DocumentPatch } from '../patches/index.js';
|
|
27
|
+
/**
|
|
28
|
+
* The shared context every lifecycle function requires. Built once per
|
|
29
|
+
* request by the API route layer and passed through.
|
|
30
|
+
*/
|
|
31
|
+
export interface DocumentLifecycleContext {
|
|
32
|
+
/** The database adapter returned by `getServerConfig().db`. */
|
|
33
|
+
db: IDbAdapter;
|
|
34
|
+
/** The resolved `CollectionDefinition` (includes `hooks`). */
|
|
35
|
+
definition: CollectionDefinition;
|
|
36
|
+
/** The database-level collection row ID. */
|
|
37
|
+
collectionId: string;
|
|
38
|
+
/**
|
|
39
|
+
* The collection's current schema version. Stamped onto every
|
|
40
|
+
* `documentVersions` row written during the lifecycle call so that
|
|
41
|
+
* Phase-2 in-memory migration can later resolve each document against
|
|
42
|
+
* the shape it was authored under. Callers resolve this from the core
|
|
43
|
+
* registry (`core.getCollectionRecord(path).version`).
|
|
44
|
+
*/
|
|
45
|
+
collectionVersion: number;
|
|
46
|
+
/** The collection `path` string (e.g. `'docs'`, `'news'`). */
|
|
47
|
+
collectionPath: string;
|
|
48
|
+
/**
|
|
49
|
+
* Storage provider for this collection. Required when the collection
|
|
50
|
+
* has any upload-capable image/file field, so that the original files
|
|
51
|
+
* and their persisted variants can be cleaned up on document deletion.
|
|
52
|
+
*
|
|
53
|
+
* Resolved by the route layer as:
|
|
54
|
+
* `field.upload?.storage ?? serverConfig.storage`
|
|
55
|
+
*
|
|
56
|
+
* Optional — callers whose collections have no upload-capable fields
|
|
57
|
+
* are unaffected.
|
|
58
|
+
*/
|
|
59
|
+
storage?: IStorageProvider;
|
|
60
|
+
/** Structured logger instance. Provided via the DI registry. */
|
|
61
|
+
logger: BylineLogger;
|
|
62
|
+
/**
|
|
63
|
+
* The default content locale (e.g. `'en'`). Used to anchor `path`
|
|
64
|
+
* derivation: the slugifier always runs against the default-locale
|
|
65
|
+
* source value, and creating a brand-new document in any other locale
|
|
66
|
+
* is rejected.
|
|
67
|
+
*
|
|
68
|
+
* Sourced by callers from `ServerConfig.i18n.content.defaultLocale`.
|
|
69
|
+
*/
|
|
70
|
+
defaultLocale: string;
|
|
71
|
+
/**
|
|
72
|
+
* Installation slugifier. When omitted, the lifecycle falls back to
|
|
73
|
+
* the default `slugify` exported from `@byline/core`.
|
|
74
|
+
*/
|
|
75
|
+
slugifier?: SlugifierFn;
|
|
76
|
+
/**
|
|
77
|
+
* Request-scoped context carrying the authenticated actor, request id,
|
|
78
|
+
* and related per-request metadata.
|
|
79
|
+
*
|
|
80
|
+
* Plumbing only in Phase 0 of the auth roadmap — present on the context
|
|
81
|
+
* so every lifecycle service can accept and forward it, but no ability
|
|
82
|
+
* assertions are performed yet. Phase 4 turns enforcement on: lifecycle
|
|
83
|
+
* entry points will call `context.requestContext?.actor?.assertAbility(...)`
|
|
84
|
+
* before any storage mutation.
|
|
85
|
+
*
|
|
86
|
+
* Optional in Phase 0 so that existing callers (admin server fns, seed
|
|
87
|
+
* scripts, tests) continue to compile. Phase 4 tightens the type.
|
|
88
|
+
*
|
|
89
|
+
* See docs/analysis/AUTHN-AUTHZ-ANALYSIS.md.
|
|
90
|
+
*/
|
|
91
|
+
requestContext?: RequestContext;
|
|
92
|
+
}
|
|
93
|
+
export interface CreateDocumentResult {
|
|
94
|
+
documentId: string;
|
|
95
|
+
documentVersionId: string;
|
|
96
|
+
}
|
|
97
|
+
export interface UpdateDocumentResult {
|
|
98
|
+
documentId: string;
|
|
99
|
+
documentVersionId: string;
|
|
100
|
+
}
|
|
101
|
+
export interface UpdateDocumentWithPatchesResult {
|
|
102
|
+
documentId: string;
|
|
103
|
+
documentVersionId: string;
|
|
104
|
+
}
|
|
105
|
+
export interface ChangeStatusResult {
|
|
106
|
+
previousStatus: string;
|
|
107
|
+
newStatus: string;
|
|
108
|
+
}
|
|
109
|
+
export interface UnpublishResult {
|
|
110
|
+
archivedCount: number;
|
|
111
|
+
}
|
|
112
|
+
export interface DeleteDocumentResult {
|
|
113
|
+
deletedVersionCount: number;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Create a new document.
|
|
117
|
+
*
|
|
118
|
+
* Flow:
|
|
119
|
+
* 1. Default-locale enforcement: reject if `params.locale` is anything
|
|
120
|
+
* other than the configured default content locale (a brand-new
|
|
121
|
+
* document's canonical `path` lives in the default locale).
|
|
122
|
+
* 2. `normaliseDateFields(data)`
|
|
123
|
+
* 3. `hooks.beforeCreate({ data, collectionPath })`
|
|
124
|
+
* 4. Resolve `path` — explicit `params.path` → derive via `useAsPath`
|
|
125
|
+
* → UUID fallback.
|
|
126
|
+
* 5. `db.commands.documents.createDocumentVersion(...)` (action = 'create')
|
|
127
|
+
* 6. `hooks.afterCreate({ data, collectionPath, documentId, documentVersionId })`
|
|
128
|
+
*/
|
|
129
|
+
export declare function createDocument(ctx: DocumentLifecycleContext, params: {
|
|
130
|
+
data: Record<string, any>;
|
|
131
|
+
locale?: string;
|
|
132
|
+
status?: string;
|
|
133
|
+
/**
|
|
134
|
+
* Explicit, user-supplied path (e.g. from the admin sidebar widget
|
|
135
|
+
* or an SDK caller importing legacy content). When omitted, the
|
|
136
|
+
* lifecycle derives the value from `definition.useAsPath`.
|
|
137
|
+
*/
|
|
138
|
+
path?: string;
|
|
139
|
+
}): Promise<CreateDocumentResult>;
|
|
140
|
+
/**
|
|
141
|
+
* Update a document via full replacement (PUT semantics).
|
|
142
|
+
*
|
|
143
|
+
* Unlike the previous implementation, this now fetches the current version
|
|
144
|
+
* from storage to provide a real `originalData` to hooks.
|
|
145
|
+
*
|
|
146
|
+
* Flow:
|
|
147
|
+
* 1. Fetch current document via `getDocumentById({ reconstruct: true })`
|
|
148
|
+
* 2. `normaliseDateFields(data)`
|
|
149
|
+
* 3. `hooks.beforeUpdate({ data, originalData, collectionPath })`
|
|
150
|
+
* 4. `db.commands.documents.createDocumentVersion(...)` (action = 'update')
|
|
151
|
+
* 5. `hooks.afterUpdate({ data, originalData, collectionPath, documentId, documentVersionId })`
|
|
152
|
+
*/
|
|
153
|
+
export declare function updateDocument(ctx: DocumentLifecycleContext, params: {
|
|
154
|
+
documentId: string;
|
|
155
|
+
data: Record<string, any>;
|
|
156
|
+
locale?: string;
|
|
157
|
+
/**
|
|
158
|
+
* Explicit path override. When omitted, the previous version's path
|
|
159
|
+
* carries forward unchanged (sticky). The lifecycle never re-derives
|
|
160
|
+
* `path` from the source field on update — that is an explicit user
|
|
161
|
+
* action driven by the admin path widget.
|
|
162
|
+
*/
|
|
163
|
+
path?: string;
|
|
164
|
+
}): Promise<UpdateDocumentResult>;
|
|
165
|
+
/**
|
|
166
|
+
* Update a document via patch application.
|
|
167
|
+
*
|
|
168
|
+
* Flow:
|
|
169
|
+
* 1. Fetch current document via `getDocumentById({ reconstruct: true })`
|
|
170
|
+
* 2. Optimistic concurrency check on `documentVersionId`
|
|
171
|
+
* 3. `applyPatches(definition, originalData, patches)` → `nextData`
|
|
172
|
+
* 4. `normaliseDateFields(nextData)`
|
|
173
|
+
* 5. `hooks.beforeUpdate({ data: nextData, originalData, collectionPath })`
|
|
174
|
+
* 6. `db.commands.documents.createDocumentVersion(...)` (action = 'update')
|
|
175
|
+
* 7. `hooks.afterUpdate({ data: nextData, originalData, collectionPath, documentId, documentVersionId })`
|
|
176
|
+
*
|
|
177
|
+
* @throws {BylineError} ERR_CONFLICT if the supplied `documentVersionId` does not match the current version.
|
|
178
|
+
* @throws {BylineError} ERR_PATCH_FAILED if `applyPatches` fails.
|
|
179
|
+
*/
|
|
180
|
+
export declare function updateDocumentWithPatches(ctx: DocumentLifecycleContext, params: {
|
|
181
|
+
documentId: string;
|
|
182
|
+
patches: DocumentPatch[];
|
|
183
|
+
/** Client-supplied version ID for optimistic concurrency. */
|
|
184
|
+
documentVersionId?: string;
|
|
185
|
+
locale?: string;
|
|
186
|
+
/**
|
|
187
|
+
* Explicit path override (typically supplied alongside patches when
|
|
188
|
+
* the admin path widget has been edited). When omitted, sticky from
|
|
189
|
+
* the previous version.
|
|
190
|
+
*/
|
|
191
|
+
path?: string;
|
|
192
|
+
}): Promise<UpdateDocumentWithPatchesResult>;
|
|
193
|
+
/**
|
|
194
|
+
* Change a document's workflow status.
|
|
195
|
+
*
|
|
196
|
+
* Flow:
|
|
197
|
+
* 1. Fetch current document metadata
|
|
198
|
+
* 2. Validate transition via `validateStatusTransition()`
|
|
199
|
+
* 3. `hooks.beforeStatusChange({ documentId, documentVersionId, collectionPath, previousStatus, nextStatus })`
|
|
200
|
+
* 4. `db.commands.documents.setDocumentStatus(...)` — in-place mutation
|
|
201
|
+
* 5. Auto-archive: if transitioning to `'published'`, archive other published versions
|
|
202
|
+
* 6. `hooks.afterStatusChange({ documentId, documentVersionId, collectionPath, previousStatus, nextStatus })`
|
|
203
|
+
*/
|
|
204
|
+
export declare function changeDocumentStatus(ctx: DocumentLifecycleContext, params: {
|
|
205
|
+
documentId: string;
|
|
206
|
+
nextStatus: string;
|
|
207
|
+
}): Promise<ChangeStatusResult>;
|
|
208
|
+
/**
|
|
209
|
+
* Unpublish a document by archiving its published version(s).
|
|
210
|
+
*
|
|
211
|
+
* Flow:
|
|
212
|
+
* 1. `hooks.beforeUnpublish({ documentId, collectionPath })`
|
|
213
|
+
* 2. `db.commands.documents.archivePublishedVersions(...)`
|
|
214
|
+
* 3. `hooks.afterUnpublish({ documentId, collectionPath, archivedCount })`
|
|
215
|
+
*/
|
|
216
|
+
export declare function unpublishDocument(ctx: DocumentLifecycleContext, params: {
|
|
217
|
+
documentId: string;
|
|
218
|
+
}): Promise<UnpublishResult>;
|
|
219
|
+
/**
|
|
220
|
+
* Soft-delete a document.
|
|
221
|
+
*
|
|
222
|
+
* Marks all versions of the document as deleted (`is_deleted = true`). The
|
|
223
|
+
* `current_documents` view automatically filters deleted rows, so the
|
|
224
|
+
* document disappears from all list / page queries without physically
|
|
225
|
+
* removing data.
|
|
226
|
+
*
|
|
227
|
+
* When the collection has any upload-capable image/file field and
|
|
228
|
+
* `ctx.storage` is provided, every original file and persisted variant
|
|
229
|
+
* across those fields is also removed from storage after the DB
|
|
230
|
+
* soft-delete succeeds. Variant paths are read from the field value's
|
|
231
|
+
* `variants` array (no re-derivation from `upload.sizes`), so cleanup
|
|
232
|
+
* stays correct even if the size set changed between upload and delete.
|
|
233
|
+
* File cleanup failures are logged but are non-fatal.
|
|
234
|
+
*
|
|
235
|
+
* Flow:
|
|
236
|
+
* 1. Fetch current document (reconstruct when upload-capable fields exist)
|
|
237
|
+
* 2. `hooks.beforeDelete({ documentId, collectionPath })`
|
|
238
|
+
* 3. `db.commands.documents.softDeleteDocument({ document_id })`
|
|
239
|
+
* 4. Storage file + variant cleanup (skipped when no upload fields, non-fatal)
|
|
240
|
+
* 5. `hooks.afterDelete({ documentId, collectionPath })`
|
|
241
|
+
*/
|
|
242
|
+
export declare function deleteDocument(ctx: DocumentLifecycleContext, params: {
|
|
243
|
+
documentId: string;
|
|
244
|
+
}): Promise<DeleteDocumentResult>;
|
|
245
|
+
//# sourceMappingURL=document-lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-lifecycle.d.ts","sourceRoot":"","sources":["../../src/services/document-lifecycle.ts"],"names":[],"mappings":"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"}
|