@cogenta/schema 0.1.2 → 0.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.
Files changed (93) hide show
  1. package/dist/define-collection.d.ts.map +1 -1
  2. package/dist/define-collection.js +32 -0
  3. package/dist/define-collection.js.map +1 -1
  4. package/dist/define-taxonomy.d.ts +9 -0
  5. package/dist/define-taxonomy.d.ts.map +1 -0
  6. package/dist/define-taxonomy.js +144 -0
  7. package/dist/define-taxonomy.js.map +1 -0
  8. package/dist/fields.d.ts +20 -0
  9. package/dist/fields.d.ts.map +1 -1
  10. package/dist/fields.js +14 -0
  11. package/dist/fields.js.map +1 -1
  12. package/dist/generate-schema-json.d.ts +28 -4
  13. package/dist/generate-schema-json.d.ts.map +1 -1
  14. package/dist/generate-schema-json.js +25 -4
  15. package/dist/generate-schema-json.js.map +1 -1
  16. package/dist/generate-types.js +1 -0
  17. package/dist/generate-types.js.map +1 -1
  18. package/dist/index.d.ts +4 -2
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +2 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/links/check.d.ts +76 -0
  23. package/dist/links/check.d.ts.map +1 -0
  24. package/dist/links/check.js +169 -0
  25. package/dist/links/check.js.map +1 -0
  26. package/dist/links/extract.d.ts +36 -0
  27. package/dist/links/extract.d.ts.map +1 -0
  28. package/dist/links/extract.js +69 -0
  29. package/dist/links/extract.js.map +1 -0
  30. package/dist/links/index.d.ts +10 -0
  31. package/dist/links/index.d.ts.map +1 -0
  32. package/dist/links/index.js +8 -0
  33. package/dist/links/index.js.map +1 -0
  34. package/dist/store/columns.d.ts.map +1 -1
  35. package/dist/store/columns.js +5 -2
  36. package/dist/store/columns.js.map +1 -1
  37. package/dist/store/index.d.ts +11 -2
  38. package/dist/store/index.d.ts.map +1 -1
  39. package/dist/store/index.js +6 -1
  40. package/dist/store/index.js.map +1 -1
  41. package/dist/store/lifecycle-events.d.ts +72 -0
  42. package/dist/store/lifecycle-events.d.ts.map +1 -0
  43. package/dist/store/lifecycle-events.js +73 -0
  44. package/dist/store/lifecycle-events.js.map +1 -0
  45. package/dist/store/naming.d.ts +11 -2
  46. package/dist/store/naming.d.ts.map +1 -1
  47. package/dist/store/naming.js +13 -1
  48. package/dist/store/naming.js.map +1 -1
  49. package/dist/store/read-only.d.ts +9 -3
  50. package/dist/store/read-only.d.ts.map +1 -1
  51. package/dist/store/read-only.js +13 -3
  52. package/dist/store/read-only.js.map +1 -1
  53. package/dist/store/schema-2-migration.d.ts +32 -0
  54. package/dist/store/schema-2-migration.d.ts.map +1 -0
  55. package/dist/store/schema-2-migration.js +136 -0
  56. package/dist/store/schema-2-migration.js.map +1 -0
  57. package/dist/store/search-indexing.d.ts +34 -0
  58. package/dist/store/search-indexing.d.ts.map +1 -0
  59. package/dist/store/search-indexing.js +65 -0
  60. package/dist/store/search-indexing.js.map +1 -0
  61. package/dist/store/store.d.ts +36 -7
  62. package/dist/store/store.d.ts.map +1 -1
  63. package/dist/store/store.js +348 -58
  64. package/dist/store/store.js.map +1 -1
  65. package/dist/store/tables.d.ts +15 -3
  66. package/dist/store/tables.d.ts.map +1 -1
  67. package/dist/store/tables.js +98 -6
  68. package/dist/store/tables.js.map +1 -1
  69. package/dist/store/taxonomy-path.d.ts +25 -0
  70. package/dist/store/taxonomy-path.d.ts.map +1 -0
  71. package/dist/store/taxonomy-path.js +64 -0
  72. package/dist/store/taxonomy-path.js.map +1 -0
  73. package/dist/store/taxonomy-store.d.ts +62 -0
  74. package/dist/store/taxonomy-store.d.ts.map +1 -0
  75. package/dist/store/taxonomy-store.js +292 -0
  76. package/dist/store/taxonomy-store.js.map +1 -0
  77. package/dist/store/types.d.ts +42 -2
  78. package/dist/store/types.d.ts.map +1 -1
  79. package/dist/store/values.d.ts.map +1 -1
  80. package/dist/store/values.js +4 -1
  81. package/dist/store/values.js.map +1 -1
  82. package/dist/system-fields.d.ts +2 -1
  83. package/dist/system-fields.d.ts.map +1 -1
  84. package/dist/system-fields.js +4 -0
  85. package/dist/system-fields.js.map +1 -1
  86. package/dist/types.d.ts +86 -4
  87. package/dist/types.d.ts.map +1 -1
  88. package/dist/types.js +12 -3
  89. package/dist/types.js.map +1 -1
  90. package/dist/validation.d.ts.map +1 -1
  91. package/dist/validation.js +5 -1
  92. package/dist/validation.js.map +1 -1
  93. package/package.json +2 -2
@@ -0,0 +1,76 @@
1
+ import type { ContentStore } from '../store/store.js';
2
+ import type { CollectionDefinition } from '../types.js';
3
+ import { type ContentLink } from './extract.js';
4
+ /**
5
+ * The internal crawl the lot asks for (L14 task 3): walk every published
6
+ * entry, follow every link it holds, and report the ones that lead nowhere.
7
+ *
8
+ * Two deliberate limits, both about not surprising a site:
9
+ *
10
+ * - **External URLs are opt-in.** A CMS that reaches out to the whole web
11
+ * every time somebody asks it a question is a CMS that gets its host
12
+ * rate-limited. `checkExternal` turns it on; without it, only links that
13
+ * point back at this site are followed.
14
+ * - **Nothing here schedules itself.** Rule R1 guarantees no durable worker,
15
+ * so a "periodic" crawl that pretended to run on its own would be a promise
16
+ * the deployment cannot keep. This is a function; `cogenta links check`
17
+ * calls it, and an operator's cron calls that.
18
+ */
19
+ export type BrokenLinkReason =
20
+ /** The referenced collection is not part of this site's schema. */
21
+ 'unknown_collection'
22
+ /** The reference resolves to nothing at all — deleted, or never existed. */
23
+ | 'target_missing'
24
+ /** The target exists but is not published, so a visitor gets a 404. */
25
+ | 'target_unpublished'
26
+ /** A site-relative path that matches no route of any collection. */
27
+ | 'unroutable_path'
28
+ /** An external URL that answered with an error status. */
29
+ | 'http_error'
30
+ /** An external URL that could not be reached at all. */
31
+ | 'unreachable';
32
+ export interface BrokenLink {
33
+ /** Where the broken link *is*, not where it points. */
34
+ readonly collection: string;
35
+ readonly entryId: string;
36
+ readonly locale: string;
37
+ /** Dotted path inside the entry — the field an editor has to open. */
38
+ readonly at: string;
39
+ readonly link: ContentLink;
40
+ readonly reason: BrokenLinkReason;
41
+ /** Present only for `http_error`. */
42
+ readonly status?: number;
43
+ }
44
+ export interface LinkCheckReport {
45
+ readonly checkedEntries: number;
46
+ readonly checkedLinks: number;
47
+ /** External URLs skipped because `checkExternal` was off. */
48
+ readonly skippedExternal: number;
49
+ readonly broken: readonly BrokenLink[];
50
+ }
51
+ export type LinkFetch = (url: string, init: {
52
+ method: 'HEAD' | 'GET';
53
+ redirect: 'follow';
54
+ signal: AbortSignal;
55
+ }) => Promise<{
56
+ readonly ok: boolean;
57
+ readonly status: number;
58
+ }>;
59
+ export interface LinkCheckOptions {
60
+ readonly collections: readonly CollectionDefinition[];
61
+ /** The same accessor `serve.ts` builds; the crawl reads through the real store. */
62
+ readonly storeFor: (collection: CollectionDefinition) => ContentStore;
63
+ /** Locale routing, so a site-relative path with a locale prefix resolves. */
64
+ readonly locales?: readonly string[];
65
+ readonly defaultLocale?: string;
66
+ /** Follow `http(s)` URLs that leave the site. Off by default. */
67
+ readonly checkExternal?: boolean;
68
+ /** Defaults to the global `fetch`; overridable for tests. */
69
+ readonly fetchImpl?: LinkFetch;
70
+ /** Per-request budget for an external URL. */
71
+ readonly timeoutMs?: number;
72
+ /** How many entries are read at a time. Small: shared hosting has little memory. */
73
+ readonly pageSize?: number;
74
+ }
75
+ export declare function checkLinks(options: LinkCheckOptions): Promise<LinkCheckReport>;
76
+ //# sourceMappingURL=check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/links/check.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,cAAc,CAAA;AAE7D;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,gBAAgB;AAC1B,mEAAmE;AACjE,oBAAoB;AACtB,4EAA4E;GAC1E,gBAAgB;AAClB,uEAAuE;GACrE,oBAAoB;AACtB,oEAAoE;GAClE,iBAAiB;AACnB,0DAA0D;GACxD,YAAY;AACd,wDAAwD;GACtD,aAAa,CAAA;AAEjB,MAAM,WAAW,UAAU;IACzB,uDAAuD;IACvD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAA;IACjC,qCAAqC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,6DAA6D;IAC7D,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAA;CACvC;AAED,MAAM,MAAM,SAAS,GAAG,CACtB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,KACtE,OAAO,CAAC;IAAE,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAE/D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,SAAS,oBAAoB,EAAE,CAAA;IACrD,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,EAAE,CAAC,UAAU,EAAE,oBAAoB,KAAK,YAAY,CAAA;IACrE,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACpC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,iEAAiE;IACjE,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAA;IAChC,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAA;IAC9B,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAgCD,wBAAsB,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CA8JpF"}
@@ -0,0 +1,169 @@
1
+ import { matchPath } from '../routing/router.js';
2
+ import { extractLinks } from './extract.js';
3
+ /** Schemes that are not web links and have nothing to check. */
4
+ const IGNORED_SCHEMES = ['mailto:', 'tel:', 'sms:', 'data:', 'javascript:', 'blob:'];
5
+ function classify(href) {
6
+ const trimmed = href.trim();
7
+ if (trimmed === '' || trimmed.startsWith('#'))
8
+ return { kind: 'ignore' };
9
+ const lower = trimmed.toLowerCase();
10
+ if (IGNORED_SCHEMES.some((scheme) => lower.startsWith(scheme)))
11
+ return { kind: 'ignore' };
12
+ if (lower.startsWith('http://') || lower.startsWith('https://')) {
13
+ return { kind: 'external', url: trimmed };
14
+ }
15
+ // Protocol-relative (`//host/path`) is external too, and is not a path.
16
+ if (trimmed.startsWith('//'))
17
+ return { kind: 'external', url: `https:${trimmed}` };
18
+ if (trimmed.startsWith('/')) {
19
+ // The query string and fragment are not part of a route.
20
+ const pathname = trimmed.split(/[?#]/u)[0] ?? trimmed;
21
+ return { kind: 'path', pathname };
22
+ }
23
+ // A bare relative reference (`about`, `../x`) has no base to resolve against
24
+ // here — an entry's own URL depends on the route it is reached by. Ignored
25
+ // rather than guessed at.
26
+ return { kind: 'ignore' };
27
+ }
28
+ export async function checkLinks(options) {
29
+ const { collections, storeFor, checkExternal = false, timeoutMs = 10_000, pageSize = 50, } = options;
30
+ const fetchImpl = options.fetchImpl ?? fetch;
31
+ const byName = new Map(collections.map((collection) => [collection.name, collection]));
32
+ const routingOptions = {
33
+ locales: options.locales ?? [],
34
+ defaultLocale: options.defaultLocale ?? 'en',
35
+ };
36
+ const broken = [];
37
+ let checkedEntries = 0;
38
+ let checkedLinks = 0;
39
+ let skippedExternal = 0;
40
+ /**
41
+ * One answer per distinct target, whatever how many entries point at it: a
42
+ * navigation link repeated on eighty pages is one lookup, and one HTTP
43
+ * request. Without this, checking a site is quadratic in its own menu.
44
+ */
45
+ const verdicts = new Map();
46
+ async function entryVerdict(collectionName, id) {
47
+ const target = byName.get(collectionName);
48
+ if (target === undefined)
49
+ return 'unknown_collection';
50
+ const store = storeFor(target);
51
+ if ((await store.read(id, { state: 'published' })) !== null)
52
+ return null;
53
+ // Not published — is it a draft, or gone entirely? The two are different
54
+ // problems for the person who has to fix it.
55
+ return (await store.read(id, { state: 'working' })) === null
56
+ ? 'target_missing'
57
+ : 'target_unpublished';
58
+ }
59
+ async function pathVerdict(pathname) {
60
+ const match = matchPath(collections, pathname, routingOptions);
61
+ if (match === null)
62
+ return 'unroutable_path';
63
+ const target = byName.get(match.collection);
64
+ if (target === undefined)
65
+ return 'unknown_collection';
66
+ const where = { ...match.params };
67
+ const page = await storeFor(target).list({
68
+ state: 'published',
69
+ where,
70
+ ...(match.locale === null ? {} : { locale: match.locale }),
71
+ limit: 1,
72
+ });
73
+ return page.items.length > 0 ? null : 'target_missing';
74
+ }
75
+ async function externalVerdict(url) {
76
+ const controller = new AbortController();
77
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
78
+ try {
79
+ // HEAD first, GET only if the server refuses HEAD: plenty of hosts answer
80
+ // 403/405 to a HEAD they would serve happily, and reporting those as
81
+ // broken would make the whole report untrustworthy.
82
+ let response = await fetchImpl(url, {
83
+ method: 'HEAD',
84
+ redirect: 'follow',
85
+ signal: controller.signal,
86
+ });
87
+ if (!response.ok && (response.status === 403 || response.status === 405)) {
88
+ response = await fetchImpl(url, {
89
+ method: 'GET',
90
+ redirect: 'follow',
91
+ signal: controller.signal,
92
+ });
93
+ }
94
+ return response.ok ? { reason: null } : { reason: 'http_error', status: response.status };
95
+ }
96
+ catch {
97
+ return { reason: 'unreachable' };
98
+ }
99
+ finally {
100
+ clearTimeout(timer);
101
+ }
102
+ }
103
+ async function verdictFor(link) {
104
+ if (link.kind === 'entry') {
105
+ const key = `entry:${link.collection}/${link.id}`;
106
+ const cached = verdicts.get(key);
107
+ if (cached !== undefined)
108
+ return { reason: cached };
109
+ const reason = await entryVerdict(link.collection, link.id);
110
+ verdicts.set(key, reason);
111
+ return { reason };
112
+ }
113
+ const classified = classify(link.href);
114
+ if (classified.kind === 'ignore')
115
+ return { reason: null };
116
+ if (classified.kind === 'path') {
117
+ const key = `path:${classified.pathname}`;
118
+ const cached = verdicts.get(key);
119
+ if (cached !== undefined)
120
+ return { reason: cached };
121
+ const reason = await pathVerdict(classified.pathname);
122
+ verdicts.set(key, reason);
123
+ return { reason };
124
+ }
125
+ if (!checkExternal) {
126
+ skippedExternal += 1;
127
+ return { reason: null };
128
+ }
129
+ const key = `url:${classified.url}`;
130
+ const cached = verdicts.get(key);
131
+ if (cached !== undefined)
132
+ return { reason: cached };
133
+ const outcome = await externalVerdict(classified.url);
134
+ verdicts.set(key, outcome.reason);
135
+ return outcome;
136
+ }
137
+ for (const collection of collections) {
138
+ const store = storeFor(collection);
139
+ let cursor = null;
140
+ do {
141
+ const page = await store.list({
142
+ state: 'published',
143
+ limit: pageSize,
144
+ ...(cursor === null ? {} : { cursor }),
145
+ });
146
+ for (const entry of page.items) {
147
+ checkedEntries += 1;
148
+ for (const link of extractLinks(entry)) {
149
+ checkedLinks += 1;
150
+ const { reason, status } = await verdictFor(link);
151
+ if (reason === null)
152
+ continue;
153
+ broken.push({
154
+ collection: collection.name,
155
+ entryId: entry.id,
156
+ locale: entry.locale,
157
+ at: link.at,
158
+ link,
159
+ reason,
160
+ ...(status === undefined ? {} : { status }),
161
+ });
162
+ }
163
+ }
164
+ cursor = page.hasMore ? page.nextCursor : null;
165
+ } while (cursor !== null);
166
+ }
167
+ return { checkedEntries, checkedLinks, skippedExternal, broken };
168
+ }
169
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/links/check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { ContentEntry } from '../store/types.js';
2
+ /**
3
+ * Every link an entry holds, wherever it holds it (L14 task 3).
4
+ *
5
+ * **Why not the search index.** The lot suggests reusing what full-text search
6
+ * already indexed. It cannot be: `search/extract.ts` deliberately drops
7
+ * `href`, `url`, `src` and `markDefs` from what it stores — indexing them would
8
+ * make `https` a searchable term and rank a page by how many links it has. The
9
+ * index therefore contains no URL at all, so link checking crawls the entries
10
+ * themselves, which is the lot's own stated fallback.
11
+ *
12
+ * **Why a generic walk rather than a per-block reader.** Contract B has three
13
+ * different link shapes — a rich-text `markDefs` entry, an action `target`, a
14
+ * plain `url` field — and blocks are added to the vocabulary over time. A walk
15
+ * that recognises the *shapes* keeps working when a block is added; a switch
16
+ * over block types would silently stop checking the new one.
17
+ */
18
+ /** A link to somewhere outside this entry. */
19
+ export type ContentLink = {
20
+ readonly kind: 'url';
21
+ readonly href: string;
22
+ /** Dotted path inside the entry, so a report names where to go and fix it. */
23
+ readonly at: string;
24
+ } | {
25
+ readonly kind: 'entry';
26
+ readonly collection: string;
27
+ readonly id: string;
28
+ readonly at: string;
29
+ };
30
+ /**
31
+ * Every link in an entry's values and blocks, deduplicated by target — a menu
32
+ * repeated in four blocks is one link to check, reported at the first place it
33
+ * was found.
34
+ */
35
+ export declare function extractLinks(entry: ContentEntry): readonly ContentLink[];
36
+ //# sourceMappingURL=extract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/links/extract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD;;;;;;;;;;;;;;;GAeG;AAEH,8CAA8C;AAC9C,MAAM,MAAM,WAAW,GACnB;IACE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CACpB,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CACpB,CAAA;AAuDL;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,WAAW,EAAE,CAgBxE"}
@@ -0,0 +1,69 @@
1
+ /** Values that hold a URL wherever they appear. `src` is media, checked elsewhere. */
2
+ const URL_KEYS = new Set(['href', 'url']);
3
+ /** Deeper than any real block nests; a bound, not a guess about content. */
4
+ const MAX_DEPTH = 12;
5
+ function isRecord(value) {
6
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
7
+ }
8
+ /**
9
+ * An entity reference: `{ collection, id }`, the shape both `linkTargetSchema`
10
+ * (contract B actions) and an `internalLink` mark definition use. Matched
11
+ * structurally so that a future block reusing the same shape is checked too.
12
+ */
13
+ function entityReferenceOf(value) {
14
+ const collection = value['collection'];
15
+ const id = value['id'];
16
+ if (typeof collection !== 'string' || typeof id !== 'string')
17
+ return null;
18
+ if (collection === '' || id === '')
19
+ return null;
20
+ return { collection, id };
21
+ }
22
+ function walk(value, at, into, depth) {
23
+ if (depth > MAX_DEPTH)
24
+ return;
25
+ if (Array.isArray(value)) {
26
+ value.forEach((item, index) => {
27
+ walk(item, `${at}[${index}]`, into, depth + 1);
28
+ });
29
+ return;
30
+ }
31
+ if (!isRecord(value))
32
+ return;
33
+ const reference = entityReferenceOf(value);
34
+ if (reference !== null) {
35
+ into.push({ kind: 'entry', collection: reference.collection, id: reference.id, at });
36
+ // Still descend: a block may carry a target *and* nested children.
37
+ }
38
+ for (const [key, nested] of Object.entries(value)) {
39
+ const path = at === '' ? key : `${at}.${key}`;
40
+ if (URL_KEYS.has(key) && typeof nested === 'string' && nested !== '') {
41
+ into.push({ kind: 'url', href: nested, at: path });
42
+ continue;
43
+ }
44
+ walk(nested, path, into, depth + 1);
45
+ }
46
+ }
47
+ /**
48
+ * Every link in an entry's values and blocks, deduplicated by target — a menu
49
+ * repeated in four blocks is one link to check, reported at the first place it
50
+ * was found.
51
+ */
52
+ export function extractLinks(entry) {
53
+ const found = [];
54
+ walk(entry.values, '', found, 0);
55
+ for (const [zone, blocks] of Object.entries(entry.blocks)) {
56
+ walk(blocks, `blocks.${zone}`, found, 1);
57
+ }
58
+ const seen = new Set();
59
+ const unique = [];
60
+ for (const link of found) {
61
+ const key = link.kind === 'url' ? `url:${link.href}` : `entry:${link.collection}/${link.id}`;
62
+ if (seen.has(key))
63
+ continue;
64
+ seen.add(key);
65
+ unique.push(link);
66
+ }
67
+ return unique;
68
+ }
69
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/links/extract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Broken link detection (L14 task 3) — see `check.ts` for why it crawls the
3
+ * entries rather than the full-text index, and why nothing here schedules
4
+ * itself.
5
+ */
6
+ export type { BrokenLink, BrokenLinkReason, LinkCheckOptions, LinkCheckReport, LinkFetch, } from './check.js';
7
+ export { checkLinks } from './check.js';
8
+ export type { ContentLink } from './extract.js';
9
+ export { extractLinks } from './extract.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/links/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EACV,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,SAAS,GACV,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Broken link detection (L14 task 3) — see `check.ts` for why it crawls the
3
+ * entries rather than the full-text index, and why nothing here schedules
4
+ * itself.
5
+ */
6
+ export { checkLinks } from './check.js';
7
+ export { extractLinks } from './extract.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/links/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"columns.d.ts","sourceRoot":"","sources":["../../src/store/columns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,WAAW,EAAa,MAAM,eAAe,CAAA;AAC/F,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE5D;;;;;;;;;;;;;;;;;GAiBG;AAEH,wBAAgB,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,WAAW,CAIhE;AAED,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAEhF;AAED,wBAAgB,UAAU,IAAI,WAAW,CAIxC;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,WAAW,CAErE;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,WAAW,CAGnE;AAED,wBAAgB,aAAa,IAAI,WAAW,CAE3C;AAED,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAK5D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,eAAe,GAAG,WAAW,CA+C3F;AAED,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,CAK1E"}
1
+ {"version":3,"file":"columns.d.ts","sourceRoot":"","sources":["../../src/store/columns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,WAAW,EAAa,MAAM,eAAe,CAAA;AAC/F,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE5D;;;;;;;;;;;;;;;;;GAiBG;AAEH,wBAAgB,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,WAAW,CAIhE;AAED,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAEhF;AAED,wBAAgB,UAAU,IAAI,WAAW,CAIxC;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,WAAW,CAErE;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,WAAW,CAGnE;AAED,wBAAgB,aAAa,IAAI,WAAW,CAE3C;AAED,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAO5D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,eAAe,GAAG,WAAW,CAgD3F;AAED,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,CAK1E"}
@@ -49,8 +49,10 @@ export function isColumnless(field) {
49
49
  if (field.kind === 'blocks')
50
50
  return true;
51
51
  // A to-many relation lives in a join table, with a real foreign key on both
52
- // sides (contract A). Only a to-one relation is a column.
53
- return field.kind === 'relation' && field.options['many'] === true;
52
+ // sides (contract A). Only a to-one relation is a column. A taxonomy field
53
+ // works the same way, its join table pointing at the terms table instead.
54
+ const joined = field.kind === 'relation' || field.kind === 'taxonomy';
55
+ return joined && field.options['many'] === true;
54
56
  }
55
57
  export function columnTypeFor(field, dialect) {
56
58
  switch (field.kind) {
@@ -81,6 +83,7 @@ export function columnTypeFor(field, dialect) {
81
83
  // table must not refuse to be created because it is not installed yet.
82
84
  return textColumn(dialect, 36);
83
85
  case 'relation':
86
+ case 'taxonomy':
84
87
  return uuidColumn(dialect);
85
88
  case 'richText':
86
89
  case 'json':
@@ -1 +1 @@
1
- {"version":3,"file":"columns.js","sourceRoot":"","sources":["../../src/store/columns.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"columns.js","sourceRoot":"","sources":["../../src/store/columns.ts"],"names":[],"mappings":"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"}
@@ -10,13 +10,22 @@ export type { Cursor } from './cursor.js';
10
10
  export { decodeCursor, encodeCursor } from './cursor.js';
11
11
  export type { BlockChange, ChangeKind, ContentDiff, FieldChange } from './diff.js';
12
12
  export { deepEqual, diffBlocks, diffBlockZones, diffContent, diffValues } from './diff.js';
13
- export { blocksTable, columnFor, entriesTable, isSystemColumn, relationTable, SYSTEM_COLUMNS, toSnakeCase, versionsTable, } from './naming.js';
13
+ export type { ContentLifecycleEvent, ContentLifecycleEventName, LifecycleEventsOptions, } from './lifecycle-events.js';
14
+ export { withLifecycleEvents } from './lifecycle-events.js';
15
+ export { blocksTable, columnFor, entriesTable, isSystemColumn, relationTable, SYSTEM_COLUMNS, taxonomyTable, toSnakeCase, versionsTable, } from './naming.js';
14
16
  export { withReadOnlyStore } from './read-only.js';
17
+ export type { Schema2MigrationOptions } from './schema-2-migration.js';
18
+ export { schema2Migration } from './schema-2-migration.js';
19
+ export type { SearchIndexingOptions } from './search-indexing.js';
20
+ export { withSearchIndexing } from './search-indexing.js';
15
21
  export type { ContentStore, ContentStoreOptions } from './store.js';
16
22
  export { createContentStore } from './store.js';
17
23
  export type { RelationTarget } from './tables.js';
18
24
  export { assertUsableFields, createSchemaTables, dropSchemaTables, orderByDependency, relationsOf, } from './tables.js';
19
- export type { BlockZones, ContentBlock, ContentEntry, ContentValues, CreateInput, EntryState, ListOptions, LocaleFallback, LocaleResolution, Page, PublishInput, ResolveLocaleOptions, SortField, SortOrder, UpdateInput, VersionSummary, } from './types.js';
25
+ export { assertDepth, childPath, depthOf, isBelow, isWithin, MAX_TAXONOMY_DEPTH, rebasedPath, TAXONOMY_PATH_LENGTH, } from './taxonomy-path.js';
26
+ export type { CreateTermInput, ListTermsOptions, TaxonomyStore, TaxonomyStoreOptions, UpdateTermInput, } from './taxonomy-store.js';
27
+ export { createTaxonomyStore } from './taxonomy-store.js';
28
+ export type { BlockZones, ContentBlock, ContentEntry, ContentValues, CreateInput, DuplicateInput, EntryState, ListOptions, LocaleFallback, LocaleResolution, Page, PublishInput, PurgeReport, ReadOptions, ResolveLocaleOptions, SortField, SortOrder, TrashFilter, TrashOptions, UpdateInput, VersionSummary, } from './types.js';
20
29
  export type { NormalisedValues, NormaliseOptions } from './values.js';
21
30
  export { decodeFieldValue, encodeFieldValue, normaliseBlocks, normaliseValues } from './values.js';
22
31
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,YAAY,EACZ,UAAU,EACV,cAAc,EACd,UAAU,EACV,eAAe,EACf,UAAU,GACX,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACxD,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAClF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC1F,OAAO,EACL,WAAW,EACX,SAAS,EACT,YAAY,EACZ,cAAc,EACd,aAAa,EACb,cAAc,EACd,WAAW,EACX,aAAa,GACd,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAClD,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/C,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,GACZ,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,WAAW,EACX,UAAU,EACV,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,IAAI,EACJ,YAAY,EACZ,oBAAoB,EACpB,SAAS,EACT,SAAS,EACT,WAAW,EACX,cAAc,GACf,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACrE,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,YAAY,EACZ,UAAU,EACV,cAAc,EACd,UAAU,EACV,eAAe,EACf,UAAU,GACX,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACxD,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAClF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC1F,YAAY,EACV,qBAAqB,EACrB,yBAAyB,EACzB,sBAAsB,GACvB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,EACL,WAAW,EACX,SAAS,EACT,YAAY,EACZ,cAAc,EACd,aAAa,EACb,cAAc,EACd,aAAa,EACb,WAAW,EACX,aAAa,GACd,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAClD,YAAY,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/C,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,GACZ,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,WAAW,EACX,SAAS,EACT,OAAO,EACP,OAAO,EACP,QAAQ,EACR,kBAAkB,EAClB,WAAW,EACX,oBAAoB,GACrB,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,oBAAoB,EACpB,eAAe,GAChB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzD,YAAY,EACV,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,WAAW,EACX,cAAc,EACd,UAAU,EACV,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,WAAW,EACX,oBAAoB,EACpB,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,EACZ,WAAW,EACX,cAAc,GACf,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACrE,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA"}
@@ -8,9 +8,14 @@
8
8
  export { booleanColumn, columnTypeFor, integerColumn, isColumnless, jsonColumn, onDeleteClause, textColumn, timestampColumn, uuidColumn, } from './columns.js';
9
9
  export { decodeCursor, encodeCursor } from './cursor.js';
10
10
  export { deepEqual, diffBlocks, diffBlockZones, diffContent, diffValues } from './diff.js';
11
- export { blocksTable, columnFor, entriesTable, isSystemColumn, relationTable, SYSTEM_COLUMNS, toSnakeCase, versionsTable, } from './naming.js';
11
+ export { withLifecycleEvents } from './lifecycle-events.js';
12
+ export { blocksTable, columnFor, entriesTable, isSystemColumn, relationTable, SYSTEM_COLUMNS, taxonomyTable, toSnakeCase, versionsTable, } from './naming.js';
12
13
  export { withReadOnlyStore } from './read-only.js';
14
+ export { schema2Migration } from './schema-2-migration.js';
15
+ export { withSearchIndexing } from './search-indexing.js';
13
16
  export { createContentStore } from './store.js';
14
17
  export { assertUsableFields, createSchemaTables, dropSchemaTables, orderByDependency, relationsOf, } from './tables.js';
18
+ export { assertDepth, childPath, depthOf, isBelow, isWithin, MAX_TAXONOMY_DEPTH, rebasedPath, TAXONOMY_PATH_LENGTH, } from './taxonomy-path.js';
19
+ export { createTaxonomyStore } from './taxonomy-store.js';
15
20
  export { decodeFieldValue, encodeFieldValue, normaliseBlocks, normaliseValues } from './values.js';
16
21
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,YAAY,EACZ,UAAU,EACV,cAAc,EACd,UAAU,EACV,eAAe,EACf,UAAU,GACX,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAExD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC1F,OAAO,EACL,WAAW,EACX,SAAS,EACT,YAAY,EACZ,cAAc,EACd,aAAa,EACb,cAAc,EACd,WAAW,EACX,aAAa,GACd,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAElD,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAE/C,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,GACZ,MAAM,aAAa,CAAA;AAoBpB,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,YAAY,EACZ,UAAU,EACV,cAAc,EACd,UAAU,EACV,eAAe,EACf,UAAU,GACX,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAExD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAM1F,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,EACL,WAAW,EACX,SAAS,EACT,YAAY,EACZ,cAAc,EACd,aAAa,EACb,cAAc,EACd,aAAa,EACb,WAAW,EACX,aAAa,GACd,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAEzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAE/C,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,GACZ,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,WAAW,EACX,SAAS,EACT,OAAO,EACP,OAAO,EACP,QAAQ,EACR,kBAAkB,EAClB,WAAW,EACX,oBAAoB,GACrB,MAAM,oBAAoB,CAAA;AAQ3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAyBzD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,72 @@
1
+ import type { CollectionDefinition, ContentStatus } from '../types.js';
2
+ import type { ContentStore } from './store.js';
3
+ import type { ContentValues } from './types.js';
4
+ /**
5
+ * Emits a content lifecycle event when — and only when — the store really
6
+ * changed what the public can see (L14 task 1).
7
+ *
8
+ * Wrapping the *store* rather than hooking a route is the same choice
9
+ * `withSearchIndexing` and `withReadOnlyStore` already make, for the same
10
+ * reason: REST's `ContentService` and GraphQL's `ContentGateway` are handed
11
+ * the very same store instances by `serve.ts`, so one wrap covers both
12
+ * transports and neither can bypass it. A hook on `/api/content` would have
13
+ * left a GraphQL mutation publishing silently.
14
+ *
15
+ * This module knows nothing about HTTP, signatures or channels: it produces a
16
+ * plain value and hands it to `emit`. What carries it (today, a signed
17
+ * outbound webhook — `@cogenta/channels`) is the caller's decision, and
18
+ * `@cogenta/schema` gains no dependency for it.
19
+ */
20
+ /**
21
+ * The closed set of events. Each one exists because a headless consumer that
22
+ * missed it would serve a page that is wrong, not merely stale:
23
+ *
24
+ * - `content.publish` — something is public that was not. The event the lot
25
+ * names, and the reason this module exists.
26
+ * - `content.unpublish` — something is no longer public. Without it a cached
27
+ * frontend keeps serving withdrawn content for ever.
28
+ * - `content.delete` — same, permanently. `delete` is a hard delete in this
29
+ * schema version, so nothing is left to poll for.
30
+ *
31
+ * Draft edits are deliberately *not* events: with `versioning.drafts` on they
32
+ * change nothing a visitor can see, and a consumer that rebuilt on each
33
+ * keystroke would rebuild for nothing.
34
+ */
35
+ export type ContentLifecycleEventName = 'content.publish' | 'content.unpublish' | 'content.delete';
36
+ /**
37
+ * What a receiver is told. Deliberately *identity and location, never the
38
+ * content body*: the body may be large, may be about to change again, and is
39
+ * already available — with permissions checked — from the API the receiver
40
+ * already talks to. Sending it here would push content into a third party's
41
+ * logs on every save, which is not something publishing a page ought to mean.
42
+ */
43
+ export interface ContentLifecycleEvent {
44
+ readonly event: ContentLifecycleEventName;
45
+ readonly collection: string;
46
+ readonly id: string;
47
+ readonly locale: string;
48
+ readonly status: ContentStatus;
49
+ readonly publishedAt: string | null;
50
+ readonly updatedAt: string;
51
+ readonly version: number;
52
+ /**
53
+ * The site-relative path the entry is reachable at, or `null` when the
54
+ * collection has no route (or its route cannot be built from the values it
55
+ * currently holds). Computed with `buildPath`, the same function the router
56
+ * and the sitemap use — never a second URL convention.
57
+ */
58
+ readonly path: string | null;
59
+ }
60
+ export interface LifecycleEventsOptions {
61
+ readonly collection: CollectionDefinition;
62
+ /**
63
+ * Receives every event. Awaited, so a caller may batch or await delivery —
64
+ * but a rejection is caught and routed to `onError`, never propagated: an
65
+ * editor's publish must not fail because a webhook receiver is down.
66
+ */
67
+ readonly emit: (event: ContentLifecycleEvent) => Promise<void> | void;
68
+ /** Called when `emit` throws or rejects. Defaults to swallowing it; `serve.ts` logs. */
69
+ readonly onError?: (error: unknown) => void;
70
+ }
71
+ export declare function withLifecycleEvents<TValues extends ContentValues = ContentValues>(store: ContentStore<TValues>, options: LifecycleEventsOptions): ContentStore<TValues>;
72
+ //# sourceMappingURL=lifecycle-events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle-events.d.ts","sourceRoot":"","sources":["../../src/store/lifecycle-events.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAgB,aAAa,EAAE,MAAM,YAAY,CAAA;AAE7D;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,yBAAyB,GAAG,iBAAiB,GAAG,mBAAmB,GAAG,gBAAgB,CAAA;AAElG;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,yBAAyB,CAAA;IACzC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAA;IACzC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IACrE,wFAAwF;IACxF,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CAC5C;AAiBD,wBAAgB,mBAAmB,CAAC,OAAO,SAAS,aAAa,GAAG,aAAa,EAC/E,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAC5B,OAAO,EAAE,sBAAsB,GAC9B,YAAY,CAAC,OAAO,CAAC,CAuDvB"}