@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.
Files changed (283) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +17 -0
  3. package/dist/@types/admin-types.d.ts +275 -0
  4. package/dist/@types/admin-types.d.ts.map +1 -0
  5. package/dist/@types/admin-types.js +18 -0
  6. package/dist/@types/admin-types.js.map +1 -0
  7. package/dist/@types/collection-types.d.ts +816 -0
  8. package/dist/@types/collection-types.d.ts.map +1 -0
  9. package/dist/@types/collection-types.js +217 -0
  10. package/dist/@types/collection-types.js.map +1 -0
  11. package/dist/@types/db-types.d.ts +463 -0
  12. package/dist/@types/db-types.d.ts.map +1 -0
  13. package/dist/@types/db-types.js +2 -0
  14. package/dist/@types/db-types.js.map +1 -0
  15. package/dist/@types/field-data-types.d.ts +147 -0
  16. package/dist/@types/field-data-types.d.ts.map +1 -0
  17. package/dist/@types/field-data-types.js +38 -0
  18. package/dist/@types/field-data-types.js.map +1 -0
  19. package/dist/@types/field-types.d.ts +579 -0
  20. package/dist/@types/field-types.d.ts.map +1 -0
  21. package/dist/@types/field-types.js +32 -0
  22. package/dist/@types/field-types.js.map +1 -0
  23. package/dist/@types/index.d.ts +18 -0
  24. package/dist/@types/index.d.ts.map +1 -0
  25. package/dist/@types/index.js +18 -0
  26. package/dist/@types/index.js.map +1 -0
  27. package/dist/@types/populate-types.d.ts +54 -0
  28. package/dist/@types/populate-types.d.ts.map +1 -0
  29. package/dist/@types/populate-types.js +9 -0
  30. package/dist/@types/populate-types.js.map +1 -0
  31. package/dist/@types/query-predicate.d.ts +74 -0
  32. package/dist/@types/query-predicate.d.ts.map +1 -0
  33. package/dist/@types/query-predicate.js +9 -0
  34. package/dist/@types/query-predicate.js.map +1 -0
  35. package/dist/@types/site-config.d.ts +212 -0
  36. package/dist/@types/site-config.d.ts.map +1 -0
  37. package/dist/@types/site-config.js +9 -0
  38. package/dist/@types/site-config.js.map +1 -0
  39. package/dist/@types/storage-types.d.ts +86 -0
  40. package/dist/@types/storage-types.d.ts.map +1 -0
  41. package/dist/@types/storage-types.js +9 -0
  42. package/dist/@types/storage-types.js.map +1 -0
  43. package/dist/@types/store-types.d.ts +134 -0
  44. package/dist/@types/store-types.d.ts.map +1 -0
  45. package/dist/@types/store-types.js +24 -0
  46. package/dist/@types/store-types.js.map +1 -0
  47. package/dist/@types/type-utils.d.ts +17 -0
  48. package/dist/@types/type-utils.d.ts.map +1 -0
  49. package/dist/@types/type-utils.js +9 -0
  50. package/dist/@types/type-utils.js.map +1 -0
  51. package/dist/auth/apply-before-read.d.ts +36 -0
  52. package/dist/auth/apply-before-read.d.ts.map +1 -0
  53. package/dist/auth/apply-before-read.js +68 -0
  54. package/dist/auth/apply-before-read.js.map +1 -0
  55. package/dist/auth/apply-before-read.test.node.d.ts +9 -0
  56. package/dist/auth/apply-before-read.test.node.d.ts.map +1 -0
  57. package/dist/auth/apply-before-read.test.node.js +144 -0
  58. package/dist/auth/apply-before-read.test.node.js.map +1 -0
  59. package/dist/auth/assert-actor-can-perform.d.ts +39 -0
  60. package/dist/auth/assert-actor-can-perform.d.ts.map +1 -0
  61. package/dist/auth/assert-actor-can-perform.js +64 -0
  62. package/dist/auth/assert-actor-can-perform.js.map +1 -0
  63. package/dist/auth/assert-actor-can-perform.test.node.d.ts +9 -0
  64. package/dist/auth/assert-actor-can-perform.test.node.d.ts.map +1 -0
  65. package/dist/auth/assert-actor-can-perform.test.node.js +119 -0
  66. package/dist/auth/assert-actor-can-perform.test.node.js.map +1 -0
  67. package/dist/auth/index.d.ts +11 -0
  68. package/dist/auth/index.d.ts.map +1 -0
  69. package/dist/auth/index.js +11 -0
  70. package/dist/auth/index.js.map +1 -0
  71. package/dist/auth/register-collection-abilities.d.ts +40 -0
  72. package/dist/auth/register-collection-abilities.d.ts.map +1 -0
  73. package/dist/auth/register-collection-abilities.js +87 -0
  74. package/dist/auth/register-collection-abilities.js.map +1 -0
  75. package/dist/auth/register-collection-abilities.test.node.d.ts +9 -0
  76. package/dist/auth/register-collection-abilities.test.node.d.ts.map +1 -0
  77. package/dist/auth/register-collection-abilities.test.node.js +124 -0
  78. package/dist/auth/register-collection-abilities.test.node.js.map +1 -0
  79. package/dist/config/config.d.ts +10 -0
  80. package/dist/config/config.d.ts.map +1 -0
  81. package/dist/config/config.js +108 -0
  82. package/dist/config/config.js.map +1 -0
  83. package/dist/config/routes.d.ts +16 -0
  84. package/dist/config/routes.d.ts.map +1 -0
  85. package/dist/config/routes.js +26 -0
  86. package/dist/config/routes.js.map +1 -0
  87. package/dist/config/validate-admin-configs.d.ts +33 -0
  88. package/dist/config/validate-admin-configs.d.ts.map +1 -0
  89. package/dist/config/validate-admin-configs.js +250 -0
  90. package/dist/config/validate-admin-configs.js.map +1 -0
  91. package/dist/config/validate-admin-configs.test.node.d.ts +9 -0
  92. package/dist/config/validate-admin-configs.test.node.d.ts.map +1 -0
  93. package/dist/config/validate-admin-configs.test.node.js +224 -0
  94. package/dist/config/validate-admin-configs.test.node.js.map +1 -0
  95. package/dist/config/validate-collections.d.ts +33 -0
  96. package/dist/config/validate-collections.d.ts.map +1 -0
  97. package/dist/config/validate-collections.js +70 -0
  98. package/dist/config/validate-collections.js.map +1 -0
  99. package/dist/config/validate-collections.test.node.d.ts +9 -0
  100. package/dist/config/validate-collections.test.node.d.ts.map +1 -0
  101. package/dist/config/validate-collections.test.node.js +149 -0
  102. package/dist/config/validate-collections.test.node.js.map +1 -0
  103. package/dist/core.d.ts +89 -0
  104. package/dist/core.d.ts.map +1 -0
  105. package/dist/core.js +99 -0
  106. package/dist/core.js.map +1 -0
  107. package/dist/defaults/default-values.d.ts +13 -0
  108. package/dist/defaults/default-values.d.ts.map +1 -0
  109. package/dist/defaults/default-values.js +60 -0
  110. package/dist/defaults/default-values.js.map +1 -0
  111. package/dist/index.d.ts +20 -0
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.js +36 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/lib/errors.d.ts +98 -0
  116. package/dist/lib/errors.d.ts.map +1 -0
  117. package/dist/lib/errors.js +134 -0
  118. package/dist/lib/errors.js.map +1 -0
  119. package/dist/lib/logger.d.ts +62 -0
  120. package/dist/lib/logger.d.ts.map +1 -0
  121. package/dist/lib/logger.js +120 -0
  122. package/dist/lib/logger.js.map +1 -0
  123. package/dist/lib/registry.d.ts +65 -0
  124. package/dist/lib/registry.d.ts.map +1 -0
  125. package/dist/lib/registry.js +133 -0
  126. package/dist/lib/registry.js.map +1 -0
  127. package/dist/logger/index.d.ts +3 -0
  128. package/dist/logger/index.d.ts.map +1 -0
  129. package/dist/logger/index.js +3 -0
  130. package/dist/logger/index.js.map +1 -0
  131. package/dist/patches/apply-patches.d.ts +21 -0
  132. package/dist/patches/apply-patches.d.ts.map +1 -0
  133. package/dist/patches/apply-patches.js +357 -0
  134. package/dist/patches/apply-patches.js.map +1 -0
  135. package/dist/patches/index.d.ts +3 -0
  136. package/dist/patches/index.d.ts.map +1 -0
  137. package/dist/patches/index.js +4 -0
  138. package/dist/patches/index.js.map +1 -0
  139. package/dist/patches/patch-types.d.ts +82 -0
  140. package/dist/patches/patch-types.d.ts.map +1 -0
  141. package/dist/patches/patch-types.js +3 -0
  142. package/dist/patches/patch-types.js.map +1 -0
  143. package/dist/patches/patch.test.node.d.ts +2 -0
  144. package/dist/patches/patch.test.node.d.ts.map +1 -0
  145. package/dist/patches/patch.test.node.js +193 -0
  146. package/dist/patches/patch.test.node.js.map +1 -0
  147. package/dist/query/parse-where.d.ts +100 -0
  148. package/dist/query/parse-where.d.ts.map +1 -0
  149. package/dist/query/parse-where.js +352 -0
  150. package/dist/query/parse-where.js.map +1 -0
  151. package/dist/query/parse-where.test.node.d.ts +9 -0
  152. package/dist/query/parse-where.test.node.d.ts.map +1 -0
  153. package/dist/query/parse-where.test.node.js +581 -0
  154. package/dist/query/parse-where.test.node.js.map +1 -0
  155. package/dist/schemas/zod/builder.d.ts +466 -0
  156. package/dist/schemas/zod/builder.d.ts.map +1 -0
  157. package/dist/schemas/zod/builder.js +276 -0
  158. package/dist/schemas/zod/builder.js.map +1 -0
  159. package/dist/schemas/zod/cache.d.ts +14 -0
  160. package/dist/schemas/zod/cache.d.ts.map +1 -0
  161. package/dist/schemas/zod/cache.js +40 -0
  162. package/dist/schemas/zod/cache.js.map +1 -0
  163. package/dist/schemas/zod/index.d.ts +4 -0
  164. package/dist/schemas/zod/index.d.ts.map +1 -0
  165. package/dist/schemas/zod/index.js +4 -0
  166. package/dist/schemas/zod/index.js.map +1 -0
  167. package/dist/schemas/zod/types.d.ts +13 -0
  168. package/dist/schemas/zod/types.d.ts.map +1 -0
  169. package/dist/schemas/zod/types.js +2 -0
  170. package/dist/schemas/zod/types.js.map +1 -0
  171. package/dist/services/collection-bootstrap.d.ts +46 -0
  172. package/dist/services/collection-bootstrap.d.ts.map +1 -0
  173. package/dist/services/collection-bootstrap.js +108 -0
  174. package/dist/services/collection-bootstrap.js.map +1 -0
  175. package/dist/services/collection-bootstrap.test.node.d.ts +9 -0
  176. package/dist/services/collection-bootstrap.test.node.d.ts.map +1 -0
  177. package/dist/services/collection-bootstrap.test.node.js +208 -0
  178. package/dist/services/collection-bootstrap.test.node.js.map +1 -0
  179. package/dist/services/document-lifecycle.d.ts +245 -0
  180. package/dist/services/document-lifecycle.d.ts.map +1 -0
  181. package/dist/services/document-lifecycle.js +481 -0
  182. package/dist/services/document-lifecycle.js.map +1 -0
  183. package/dist/services/document-lifecycle.test.node.d.ts +9 -0
  184. package/dist/services/document-lifecycle.test.node.d.ts.map +1 -0
  185. package/dist/services/document-lifecycle.test.node.js +781 -0
  186. package/dist/services/document-lifecycle.test.node.js.map +1 -0
  187. package/dist/services/document-read.d.ts +26 -0
  188. package/dist/services/document-read.d.ts.map +1 -0
  189. package/dist/services/document-read.js +60 -0
  190. package/dist/services/document-read.js.map +1 -0
  191. package/dist/services/field-upload.d.ts +100 -0
  192. package/dist/services/field-upload.d.ts.map +1 -0
  193. package/dist/services/field-upload.js +328 -0
  194. package/dist/services/field-upload.js.map +1 -0
  195. package/dist/services/field-upload.test.node.d.ts +9 -0
  196. package/dist/services/field-upload.test.node.d.ts.map +1 -0
  197. package/dist/services/field-upload.test.node.js +337 -0
  198. package/dist/services/field-upload.test.node.js.map +1 -0
  199. package/dist/services/index.d.ts +10 -0
  200. package/dist/services/index.d.ts.map +1 -0
  201. package/dist/services/index.js +11 -0
  202. package/dist/services/index.js.map +1 -0
  203. package/dist/services/populate.d.ts +299 -0
  204. package/dist/services/populate.d.ts.map +1 -0
  205. package/dist/services/populate.js +484 -0
  206. package/dist/services/populate.js.map +1 -0
  207. package/dist/services/populate.test.node.d.ts +9 -0
  208. package/dist/services/populate.test.node.d.ts.map +1 -0
  209. package/dist/services/populate.test.node.js +910 -0
  210. package/dist/services/populate.test.node.js.map +1 -0
  211. package/dist/services/relation-projection.d.ts +52 -0
  212. package/dist/services/relation-projection.d.ts.map +1 -0
  213. package/dist/services/relation-projection.js +81 -0
  214. package/dist/services/relation-projection.js.map +1 -0
  215. package/dist/services/richtext-populate.d.ts +87 -0
  216. package/dist/services/richtext-populate.d.ts.map +1 -0
  217. package/dist/services/richtext-populate.js +189 -0
  218. package/dist/services/richtext-populate.js.map +1 -0
  219. package/dist/services/richtext-populate.test.node.d.ts +9 -0
  220. package/dist/services/richtext-populate.test.node.d.ts.map +1 -0
  221. package/dist/services/richtext-populate.test.node.js +197 -0
  222. package/dist/services/richtext-populate.test.node.js.map +1 -0
  223. package/dist/storage/collection-fingerprint.d.ts +21 -0
  224. package/dist/storage/collection-fingerprint.d.ts.map +1 -0
  225. package/dist/storage/collection-fingerprint.js +172 -0
  226. package/dist/storage/collection-fingerprint.js.map +1 -0
  227. package/dist/storage/collection-fingerprint.test.node.d.ts +9 -0
  228. package/dist/storage/collection-fingerprint.test.node.d.ts.map +1 -0
  229. package/dist/storage/collection-fingerprint.test.node.js +256 -0
  230. package/dist/storage/collection-fingerprint.test.node.js.map +1 -0
  231. package/dist/storage/field-store-map.d.ts +59 -0
  232. package/dist/storage/field-store-map.d.ts.map +1 -0
  233. package/dist/storage/field-store-map.js +75 -0
  234. package/dist/storage/field-store-map.js.map +1 -0
  235. package/dist/storage/field-store-map.test.node.d.ts +9 -0
  236. package/dist/storage/field-store-map.test.node.d.ts.map +1 -0
  237. package/dist/storage/field-store-map.test.node.js +117 -0
  238. package/dist/storage/field-store-map.test.node.js.map +1 -0
  239. package/dist/storage/index.d.ts +10 -0
  240. package/dist/storage/index.d.ts.map +1 -0
  241. package/dist/storage/index.js +10 -0
  242. package/dist/storage/index.js.map +1 -0
  243. package/dist/utils/normalise-dates.d.ts +15 -0
  244. package/dist/utils/normalise-dates.d.ts.map +1 -0
  245. package/dist/utils/normalise-dates.js +22 -0
  246. package/dist/utils/normalise-dates.js.map +1 -0
  247. package/dist/utils/slugify.d.ts +56 -0
  248. package/dist/utils/slugify.d.ts.map +1 -0
  249. package/dist/utils/slugify.js +91 -0
  250. package/dist/utils/slugify.js.map +1 -0
  251. package/dist/utils/slugify.test.node.d.ts +9 -0
  252. package/dist/utils/slugify.test.node.d.ts.map +1 -0
  253. package/dist/utils/slugify.test.node.js +86 -0
  254. package/dist/utils/slugify.test.node.js.map +1 -0
  255. package/dist/utils/storage-utils.d.ts +36 -0
  256. package/dist/utils/storage-utils.d.ts.map +1 -0
  257. package/dist/utils/storage-utils.js +38 -0
  258. package/dist/utils/storage-utils.js.map +1 -0
  259. package/dist/utils/utils.general.d.ts +64 -0
  260. package/dist/utils/utils.general.d.ts.map +1 -0
  261. package/dist/utils/utils.general.js +219 -0
  262. package/dist/utils/utils.general.js.map +1 -0
  263. package/dist/validation/index.d.ts +9 -0
  264. package/dist/validation/index.d.ts.map +1 -0
  265. package/dist/validation/index.js +9 -0
  266. package/dist/validation/index.js.map +1 -0
  267. package/dist/validation/shared.d.ts +36 -0
  268. package/dist/validation/shared.d.ts.map +1 -0
  269. package/dist/validation/shared.js +42 -0
  270. package/dist/validation/shared.js.map +1 -0
  271. package/dist/workflow/index.d.ts +2 -0
  272. package/dist/workflow/index.d.ts.map +1 -0
  273. package/dist/workflow/index.js +3 -0
  274. package/dist/workflow/index.js.map +1 -0
  275. package/dist/workflow/workflow.d.ts +40 -0
  276. package/dist/workflow/workflow.d.ts.map +1 -0
  277. package/dist/workflow/workflow.js +96 -0
  278. package/dist/workflow/workflow.js.map +1 -0
  279. package/dist/workflow/workflow.test.node.d.ts +2 -0
  280. package/dist/workflow/workflow.test.node.d.ts.map +1 -0
  281. package/dist/workflow/workflow.test.node.js +198 -0
  282. package/dist/workflow/workflow.test.node.js.map +1 -0
  283. 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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"}