@cogenta/schema 0.3.0 → 0.5.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 (189) hide show
  1. package/dist/define-collection.d.ts.map +1 -1
  2. package/dist/define-collection.js +53 -3
  3. package/dist/define-collection.js.map +1 -1
  4. package/dist/generate-types.d.ts.map +1 -1
  5. package/dist/generate-types.js +6 -1
  6. package/dist/generate-types.js.map +1 -1
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/media-usage.d.ts +56 -0
  12. package/dist/media-usage.d.ts.map +1 -0
  13. package/dist/media-usage.js +89 -0
  14. package/dist/media-usage.js.map +1 -0
  15. package/dist/rich-text.d.ts +28 -2
  16. package/dist/rich-text.d.ts.map +1 -1
  17. package/dist/rich-text.js +26 -3
  18. package/dist/rich-text.js.map +1 -1
  19. package/dist/routing/index.d.ts +5 -1
  20. package/dist/routing/index.d.ts.map +1 -1
  21. package/dist/routing/index.js +2 -0
  22. package/dist/routing/index.js.map +1 -1
  23. package/dist/routing/not-found-log.d.ts +78 -0
  24. package/dist/routing/not-found-log.d.ts.map +1 -0
  25. package/dist/routing/not-found-log.js +153 -0
  26. package/dist/routing/not-found-log.js.map +1 -0
  27. package/dist/routing/redirect-patterns.d.ts +61 -0
  28. package/dist/routing/redirect-patterns.d.ts.map +1 -0
  29. package/dist/routing/redirect-patterns.js +173 -0
  30. package/dist/routing/redirect-patterns.js.map +1 -0
  31. package/dist/routing/redirects.d.ts +27 -4
  32. package/dist/routing/redirects.d.ts.map +1 -1
  33. package/dist/routing/redirects.js +160 -48
  34. package/dist/routing/redirects.js.map +1 -1
  35. package/dist/scheduling/index.d.ts +2 -0
  36. package/dist/scheduling/index.d.ts.map +1 -1
  37. package/dist/scheduling/index.js +1 -0
  38. package/dist/scheduling/index.js.map +1 -1
  39. package/dist/scheduling/registry.d.ts +147 -0
  40. package/dist/scheduling/registry.d.ts.map +1 -0
  41. package/dist/scheduling/registry.js +318 -0
  42. package/dist/scheduling/registry.js.map +1 -0
  43. package/dist/search/extract.d.ts +24 -0
  44. package/dist/search/extract.d.ts.map +1 -1
  45. package/dist/search/extract.js +63 -8
  46. package/dist/search/extract.js.map +1 -1
  47. package/dist/search/index.d.ts +2 -1
  48. package/dist/search/index.d.ts.map +1 -1
  49. package/dist/search/index.js +1 -9
  50. package/dist/search/index.js.map +1 -1
  51. package/dist/store/admin-theme-store.d.ts +26 -0
  52. package/dist/store/admin-theme-store.d.ts.map +1 -0
  53. package/dist/store/admin-theme-store.js +81 -0
  54. package/dist/store/admin-theme-store.js.map +1 -0
  55. package/dist/store/admin-theme-tables.d.ts +24 -0
  56. package/dist/store/admin-theme-tables.d.ts.map +1 -0
  57. package/dist/store/admin-theme-tables.js +41 -0
  58. package/dist/store/admin-theme-tables.js.map +1 -0
  59. package/dist/store/admin-theme-templates.d.ts +135 -0
  60. package/dist/store/admin-theme-templates.d.ts.map +1 -0
  61. package/dist/store/admin-theme-templates.js +217 -0
  62. package/dist/store/admin-theme-templates.js.map +1 -0
  63. package/dist/store/columns.d.ts.map +1 -1
  64. package/dist/store/columns.js +8 -2
  65. package/dist/store/columns.js.map +1 -1
  66. package/dist/store/diff.d.ts +40 -0
  67. package/dist/store/diff.d.ts.map +1 -1
  68. package/dist/store/diff.js +148 -0
  69. package/dist/store/diff.js.map +1 -1
  70. package/dist/store/index.d.ts +44 -6
  71. package/dist/store/index.d.ts.map +1 -1
  72. package/dist/store/index.js +24 -3
  73. package/dist/store/index.js.map +1 -1
  74. package/dist/store/maintenance-store.d.ts +34 -0
  75. package/dist/store/maintenance-store.d.ts.map +1 -0
  76. package/dist/store/maintenance-store.js +92 -0
  77. package/dist/store/maintenance-store.js.map +1 -0
  78. package/dist/store/menu-store.d.ts +48 -1
  79. package/dist/store/menu-store.d.ts.map +1 -1
  80. package/dist/store/menu-store.js +151 -4
  81. package/dist/store/menu-store.js.map +1 -1
  82. package/dist/store/menu-tables.d.ts +12 -0
  83. package/dist/store/menu-tables.d.ts.map +1 -1
  84. package/dist/store/menu-tables.js +38 -0
  85. package/dist/store/menu-tables.js.map +1 -1
  86. package/dist/store/naming.d.ts +1 -1
  87. package/dist/store/naming.d.ts.map +1 -1
  88. package/dist/store/naming.js +2 -0
  89. package/dist/store/naming.js.map +1 -1
  90. package/dist/store/pattern-store.d.ts +75 -0
  91. package/dist/store/pattern-store.d.ts.map +1 -0
  92. package/dist/store/pattern-store.js +118 -0
  93. package/dist/store/pattern-store.js.map +1 -0
  94. package/dist/store/pattern-tables.d.ts +29 -0
  95. package/dist/store/pattern-tables.d.ts.map +1 -0
  96. package/dist/store/pattern-tables.js +53 -0
  97. package/dist/store/pattern-tables.js.map +1 -0
  98. package/dist/store/read-only.d.ts +3 -3
  99. package/dist/store/read-only.d.ts.map +1 -1
  100. package/dist/store/read-only.js +4 -3
  101. package/dist/store/read-only.js.map +1 -1
  102. package/dist/store/redirect-tracking.d.ts +50 -0
  103. package/dist/store/redirect-tracking.d.ts.map +1 -0
  104. package/dist/store/redirect-tracking.js +91 -0
  105. package/dist/store/redirect-tracking.js.map +1 -0
  106. package/dist/store/review-transitions.d.ts +34 -0
  107. package/dist/store/review-transitions.d.ts.map +1 -0
  108. package/dist/store/review-transitions.js +51 -0
  109. package/dist/store/review-transitions.js.map +1 -0
  110. package/dist/store/role-permission-export.d.ts +22 -0
  111. package/dist/store/role-permission-export.d.ts.map +1 -0
  112. package/dist/store/role-permission-export.js +0 -0
  113. package/dist/store/role-permission-export.js.map +1 -0
  114. package/dist/store/role-permission-overlay.d.ts +27 -0
  115. package/dist/store/role-permission-overlay.d.ts.map +1 -0
  116. package/dist/store/role-permission-overlay.js +0 -0
  117. package/dist/store/role-permission-overlay.js.map +1 -0
  118. package/dist/store/role-permission-store.d.ts +72 -0
  119. package/dist/store/role-permission-store.d.ts.map +1 -0
  120. package/dist/store/role-permission-store.js +148 -0
  121. package/dist/store/role-permission-store.js.map +1 -0
  122. package/dist/store/role-permission-tables.d.ts +27 -0
  123. package/dist/store/role-permission-tables.d.ts.map +1 -0
  124. package/dist/store/role-permission-tables.js +52 -0
  125. package/dist/store/role-permission-tables.js.map +1 -0
  126. package/dist/store/scheduled-publish-failures.d.ts +36 -0
  127. package/dist/store/scheduled-publish-failures.d.ts.map +1 -0
  128. package/dist/store/scheduled-publish-failures.js +67 -0
  129. package/dist/store/scheduled-publish-failures.js.map +1 -0
  130. package/dist/store/schema-2-1-migration.d.ts +29 -0
  131. package/dist/store/schema-2-1-migration.d.ts.map +1 -0
  132. package/dist/store/schema-2-1-migration.js +71 -0
  133. package/dist/store/schema-2-1-migration.js.map +1 -0
  134. package/dist/store/search-console-store.d.ts +44 -0
  135. package/dist/store/search-console-store.d.ts.map +1 -0
  136. package/dist/store/search-console-store.js +90 -0
  137. package/dist/store/search-console-store.js.map +1 -0
  138. package/dist/store/search-console-tables.d.ts +14 -0
  139. package/dist/store/search-console-tables.d.ts.map +1 -0
  140. package/dist/store/search-console-tables.js +29 -0
  141. package/dist/store/search-console-tables.js.map +1 -0
  142. package/dist/store/search-indexing.d.ts +36 -0
  143. package/dist/store/search-indexing.d.ts.map +1 -1
  144. package/dist/store/search-indexing.js +33 -0
  145. package/dist/store/search-indexing.js.map +1 -1
  146. package/dist/store/site-settings-registry.d.ts +164 -0
  147. package/dist/store/site-settings-registry.d.ts.map +1 -0
  148. package/dist/store/site-settings-registry.js +1200 -0
  149. package/dist/store/site-settings-registry.js.map +1 -0
  150. package/dist/store/site-settings-store.d.ts +42 -0
  151. package/dist/store/site-settings-store.d.ts.map +1 -0
  152. package/dist/store/site-settings-store.js +97 -0
  153. package/dist/store/site-settings-store.js.map +1 -0
  154. package/dist/store/site-settings-tables.d.ts +29 -0
  155. package/dist/store/site-settings-tables.d.ts.map +1 -0
  156. package/dist/store/site-settings-tables.js +48 -0
  157. package/dist/store/site-settings-tables.js.map +1 -0
  158. package/dist/store/store.d.ts +48 -1
  159. package/dist/store/store.d.ts.map +1 -1
  160. package/dist/store/store.js +231 -39
  161. package/dist/store/store.js.map +1 -1
  162. package/dist/store/tables.d.ts.map +1 -1
  163. package/dist/store/tables.js +11 -0
  164. package/dist/store/tables.js.map +1 -1
  165. package/dist/store/taxonomy-store.d.ts.map +1 -1
  166. package/dist/store/taxonomy-store.js +90 -39
  167. package/dist/store/taxonomy-store.js.map +1 -1
  168. package/dist/store/taxonomy-usage.d.ts +47 -0
  169. package/dist/store/taxonomy-usage.d.ts.map +1 -0
  170. package/dist/store/taxonomy-usage.js +101 -0
  171. package/dist/store/taxonomy-usage.js.map +1 -0
  172. package/dist/store/theme-store.d.ts +73 -0
  173. package/dist/store/theme-store.d.ts.map +1 -0
  174. package/dist/store/theme-store.js +193 -0
  175. package/dist/store/theme-store.js.map +1 -0
  176. package/dist/store/types.d.ts +36 -1
  177. package/dist/store/types.d.ts.map +1 -1
  178. package/dist/store/values.d.ts.map +1 -1
  179. package/dist/store/values.js +28 -1
  180. package/dist/store/values.js.map +1 -1
  181. package/dist/system-fields.d.ts +8 -1
  182. package/dist/system-fields.d.ts.map +1 -1
  183. package/dist/system-fields.js +14 -1
  184. package/dist/system-fields.js.map +1 -1
  185. package/dist/types.d.ts +54 -1
  186. package/dist/types.d.ts.map +1 -1
  187. package/dist/types.js +21 -0
  188. package/dist/types.js.map +1 -1
  189. package/package.json +2 -2
@@ -0,0 +1,44 @@
1
+ import { type DatabaseHandle } from '@cogenta/core';
2
+ /**
3
+ * The one Google Search Console connection a site can have (fiche 70 task
4
+ * 4, ADR-0032). Same encryption discipline as `@cogenta/agents`'
5
+ * `ProviderConfigStore` and `@cogenta/mcp`'s connection store — AES-256-GCM
6
+ * via `node:crypto`, key derived from `COGENTA_AUTH_SIGNING_KEY` with a
7
+ * purpose-specific salt so the derived key is never the literal signing key
8
+ * reused across purposes (R7: no second secret to generate, rotate or
9
+ * lose). The refresh token is the only thing encrypted; `siteUrl` is not a
10
+ * secret (it is the property URL, already public knowledge to anyone who
11
+ * knows the site's own domain).
12
+ *
13
+ * Real database table (`ensureSearchConsoleConnectionTable`), not a file
14
+ * store: a Search Console connection is exactly the kind of "fixed row, not
15
+ * schema-declared content" `menu-tables.ts`/`pattern-tables.ts` already
16
+ * established a home for in this package, and it gets the same
17
+ * SQLite/Postgres/MySQL portability their tables already have for free.
18
+ */
19
+ export interface SearchConsoleConnectionSummary {
20
+ readonly siteUrl: string;
21
+ readonly connectedAt: string;
22
+ readonly updatedAt: string;
23
+ }
24
+ export interface ConnectSearchConsoleInput {
25
+ readonly siteUrl: string;
26
+ readonly refreshToken: string;
27
+ }
28
+ export interface SearchConsoleConnectionStore {
29
+ /** `null` when no site has ever completed the OAuth flow. */
30
+ read(): Promise<SearchConsoleConnectionSummary | null>;
31
+ /** Encrypts and persists `refreshToken`; replaces any existing connection (a site connects to at most one GSC property at a time). */
32
+ connect(input: ConnectSearchConsoleInput): Promise<SearchConsoleConnectionSummary>;
33
+ disconnect(): Promise<void>;
34
+ /** The one place the real refresh token is ever decrypted. Throws `SEARCH_CONSOLE_NOT_CONNECTED` if there is none. */
35
+ decryptRefreshToken(): Promise<string>;
36
+ }
37
+ export interface SearchConsoleConnectionStoreOptions {
38
+ readonly db: DatabaseHandle;
39
+ /** `COGENTA_AUTH_SIGNING_KEY` — the encryption key is derived from it, never stored itself. */
40
+ readonly signingKey: string;
41
+ readonly now?: () => Date;
42
+ }
43
+ export declare function createSearchConsoleConnectionStore(options: SearchConsoleConnectionStoreOptions): SearchConsoleConnectionStore;
44
+ //# sourceMappingURL=search-console-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-console-store.d.ts","sourceRoot":"","sources":["../../src/store/search-console-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,cAAc,EAAmB,MAAM,eAAe,CAAA;AAMlF;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,4BAA4B;IAC3C,6DAA6D;IAC7D,IAAI,IAAI,OAAO,CAAC,8BAA8B,GAAG,IAAI,CAAC,CAAA;IACtD,sIAAsI;IACtI,OAAO,CAAC,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAAC,8BAA8B,CAAC,CAAA;IAClF,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,sHAAsH;IACtH,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;CACvC;AA+BD,MAAM,WAAW,mCAAmC;IAClD,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAA;IAC3B,+FAA+F;IAC/F,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CAC1B;AAED,wBAAgB,kCAAkC,CAChD,OAAO,EAAE,mCAAmC,GAC3C,4BAA4B,CA+E9B"}
@@ -0,0 +1,90 @@
1
+ import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto';
2
+ import { CogentaError, identifier, sql } from '@cogenta/core';
3
+ import { SEARCH_CONSOLE_CONNECTION_ID, SEARCH_CONSOLE_CONNECTION_TABLE, } from './search-console-tables.js';
4
+ const KEY_DERIVATION_SALT = 'cogenta-search-console-secrets-v1';
5
+ const ALGORITHM = 'aes-256-gcm';
6
+ function deriveKey(signingKey) {
7
+ return scryptSync(signingKey, KEY_DERIVATION_SALT, 32);
8
+ }
9
+ function notConnected() {
10
+ return new CogentaError({
11
+ code: 'SEARCH_CONSOLE_NOT_CONNECTED',
12
+ message: 'No site has connected a Google Search Console property yet.',
13
+ hint: "Connect one from the SEO screen's Diagnostics tab first.",
14
+ });
15
+ }
16
+ function toSummary(row) {
17
+ return { siteUrl: row.site_url, connectedAt: row.connected_at, updatedAt: row.updated_at };
18
+ }
19
+ export function createSearchConsoleConnectionStore(options) {
20
+ const { db } = options;
21
+ const now = options.now ?? (() => new Date());
22
+ const key = deriveKey(options.signingKey);
23
+ const table = identifier(SEARCH_CONSOLE_CONNECTION_TABLE, db.dialect);
24
+ function encrypt(plaintext) {
25
+ const iv = randomBytes(12);
26
+ const cipher = createCipheriv(ALGORITHM, key, iv);
27
+ const ciphertext = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
28
+ return {
29
+ iv: iv.toString('base64'),
30
+ authTag: cipher.getAuthTag().toString('base64'),
31
+ ciphertext: ciphertext.toString('base64'),
32
+ };
33
+ }
34
+ function decrypt(row) {
35
+ const decipher = createDecipheriv(ALGORITHM, key, Buffer.from(row.refresh_token_iv, 'base64'));
36
+ decipher.setAuthTag(Buffer.from(row.refresh_token_auth_tag, 'base64'));
37
+ const plaintext = Buffer.concat([
38
+ decipher.update(Buffer.from(row.refresh_token_ciphertext, 'base64')),
39
+ decipher.final(),
40
+ ]);
41
+ return plaintext.toString('utf8');
42
+ }
43
+ async function findRow() {
44
+ const found = await db.query(sql `select * from ${table} where id = ${SEARCH_CONSOLE_CONNECTION_ID}`);
45
+ return found.rows[0] ?? null;
46
+ }
47
+ return {
48
+ async read() {
49
+ const row = await findRow();
50
+ return row === null ? null : toSummary(row);
51
+ },
52
+ async connect(input) {
53
+ const { iv, authTag, ciphertext } = encrypt(input.refreshToken);
54
+ const existing = await findRow();
55
+ const nowIso = now().toISOString();
56
+ const connectedAt = existing?.connected_at ?? nowIso;
57
+ if (existing === null) {
58
+ await db.query(sql `
59
+ insert into ${table} (
60
+ id, site_url, refresh_token_iv, refresh_token_auth_tag,
61
+ refresh_token_ciphertext, connected_at, updated_at
62
+ ) values (
63
+ ${SEARCH_CONSOLE_CONNECTION_ID}, ${input.siteUrl}, ${iv}, ${authTag},
64
+ ${ciphertext}, ${connectedAt}, ${nowIso}
65
+ )`);
66
+ }
67
+ else {
68
+ await db.query(sql `
69
+ update ${table} set
70
+ site_url = ${input.siteUrl},
71
+ refresh_token_iv = ${iv},
72
+ refresh_token_auth_tag = ${authTag},
73
+ refresh_token_ciphertext = ${ciphertext},
74
+ updated_at = ${nowIso}
75
+ where id = ${SEARCH_CONSOLE_CONNECTION_ID}`);
76
+ }
77
+ return { siteUrl: input.siteUrl, connectedAt, updatedAt: nowIso };
78
+ },
79
+ async disconnect() {
80
+ await db.query(sql `delete from ${table} where id = ${SEARCH_CONSOLE_CONNECTION_ID}`);
81
+ },
82
+ async decryptRefreshToken() {
83
+ const row = await findRow();
84
+ if (row === null)
85
+ throw notConnected();
86
+ return decrypt(row);
87
+ },
88
+ };
89
+ }
90
+ //# sourceMappingURL=search-console-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-console-store.js","sourceRoot":"","sources":["../../src/store/search-console-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type DatabaseHandle } from '@cogenta/core';
2
+ /**
3
+ * Google Search Console connector (fiche 70 task 4, ADR-0032) — same
4
+ * one-fixed-table treatment as `menu-tables.ts`/`pattern-tables.ts`: this is
5
+ * not schema-declared content, it is one site-wide connection record. At
6
+ * most one row ever exists (`id` is always `'default'`), because a Cogenta
7
+ * install is one site with one Search Console property, not a multi-tenant
8
+ * registry.
9
+ */
10
+ export declare const SEARCH_CONSOLE_CONNECTION_TABLE = "cogenta_seo_search_console_connection";
11
+ /** The table's one and only possible row id. */
12
+ export declare const SEARCH_CONSOLE_CONNECTION_ID = "default";
13
+ export declare function ensureSearchConsoleConnectionTable(db: DatabaseHandle): Promise<void>;
14
+ //# sourceMappingURL=search-console-tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-console-tables.d.ts","sourceRoot":"","sources":["../../src/store/search-console-tables.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAA8B,MAAM,eAAe,CAAA;AAG/E;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,0CAA0C,CAAA;AAEtF,gDAAgD;AAChD,eAAO,MAAM,4BAA4B,YAAY,CAAA;AAKrD,wBAAsB,kCAAkC,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAa1F"}
@@ -0,0 +1,29 @@
1
+ import { identifier, sql, unsafeRaw } from '@cogenta/core';
2
+ import { textColumn, timestampColumn } from './columns.js';
3
+ /**
4
+ * Google Search Console connector (fiche 70 task 4, ADR-0032) — same
5
+ * one-fixed-table treatment as `menu-tables.ts`/`pattern-tables.ts`: this is
6
+ * not schema-declared content, it is one site-wide connection record. At
7
+ * most one row ever exists (`id` is always `'default'`), because a Cogenta
8
+ * install is one site with one Search Console property, not a multi-tenant
9
+ * registry.
10
+ */
11
+ export const SEARCH_CONSOLE_CONNECTION_TABLE = 'cogenta_seo_search_console_connection';
12
+ /** The table's one and only possible row id. */
13
+ export const SEARCH_CONSOLE_CONNECTION_ID = 'default';
14
+ /** How long a GSC `siteUrl` (`https://example.com/` or `sc-domain:example.com`) may be. */
15
+ const SITE_URL_LENGTH = 255;
16
+ export async function ensureSearchConsoleConnectionTable(db) {
17
+ const dialect = db.dialect;
18
+ const table = identifier(SEARCH_CONSOLE_CONNECTION_TABLE, dialect);
19
+ await db.query(sql `create table if not exists ${table} (
20
+ ${identifier('id', dialect)} ${textColumn(dialect, 16)} not null primary key,
21
+ ${identifier('site_url', dialect)} ${textColumn(dialect, SITE_URL_LENGTH)} not null,
22
+ ${identifier('refresh_token_iv', dialect)} ${textColumn(dialect, 64)} not null,
23
+ ${identifier('refresh_token_auth_tag', dialect)} ${textColumn(dialect, 64)} not null,
24
+ ${identifier('refresh_token_ciphertext', dialect)} ${unsafeRaw('text')} not null,
25
+ ${identifier('connected_at', dialect)} ${timestampColumn(dialect)} not null,
26
+ ${identifier('updated_at', dialect)} ${timestampColumn(dialect)} not null
27
+ )`);
28
+ }
29
+ //# sourceMappingURL=search-console-tables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-console-tables.js","sourceRoot":"","sources":["../../src/store/search-console-tables.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC/E,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAE1D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,uCAAuC,CAAA;AAEtF,gDAAgD;AAChD,MAAM,CAAC,MAAM,4BAA4B,GAAG,SAAS,CAAA;AAErD,2FAA2F;AAC3F,MAAM,eAAe,GAAG,GAAG,CAAA;AAE3B,MAAM,CAAC,KAAK,UAAU,kCAAkC,CAAC,EAAkB;IACzE,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAA;IAC1B,MAAM,KAAK,GAAG,UAAU,CAAC,+BAA+B,EAAE,OAAO,CAAC,CAAA;IAElE,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA,8BAA8B,KAAK;MACjD,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC;MACpD,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,eAAe,CAAC;MACvE,UAAU,CAAC,kBAAkB,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC;MAClE,UAAU,CAAC,wBAAwB,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC;MACxE,UAAU,CAAC,0BAA0B,EAAE,OAAO,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC;MACpE,UAAU,CAAC,cAAc,EAAE,OAAO,CAAC,IAAI,eAAe,CAAC,OAAO,CAAC;MAC/D,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,eAAe,CAAC,OAAO,CAAC;IAC/D,CAAC,CAAA;AACL,CAAC"}
@@ -30,5 +30,41 @@ export interface SearchIndexingOptions {
30
30
  */
31
31
  readonly onError?: (error: unknown) => void;
32
32
  }
33
+ /**
34
+ * Re-derives the indexed document for one entry.
35
+ *
36
+ * The published face is asked for **first**, and it is the one indexed
37
+ * whenever it exists. That ordering is the whole safety property: with drafts
38
+ * enabled, `update()` returns the working face of an entry whose `status` is
39
+ * still `published`, and indexing *that* would file unreviewed text under a
40
+ * status a public search is allowed to reach. Reading back through
41
+ * `state: 'published'` makes it structurally impossible — the store only
42
+ * returns a row there when its status really is `published`.
43
+ *
44
+ * When there is no published face, the working one is indexed under its real
45
+ * status (`draft`, `scheduled`, `archived`), which is what lets the admin
46
+ * find a draft while `SearchQuery`'s `published` default keeps it away from
47
+ * anyone who did not explicitly ask for it and hold the permission to.
48
+ */
49
+ declare function reindex<TValues extends ContentValues>(store: ContentStore<TValues>, options: SearchIndexingOptions, id: string): Promise<void>;
50
+ /**
51
+ * Re-derives the indexed document for one entry — exported so a full rebuild
52
+ * (`reindexAll`, and `cogenta`'s "Reindex search" tool, fiche 24 task 3) uses
53
+ * the exact same per-entry logic as the write path, rather than a second
54
+ * copy that could drift from what `withSearchIndexing` actually does on save.
55
+ */
56
+ export declare const reindexEntry: typeof reindex;
57
+ /**
58
+ * Walks every entry of a collection — trashed included, since a trashed entry
59
+ * can still be `published` (ADR-0022: trash is orthogonal to status) and a
60
+ * stale search hit for it is exactly the kind of thing a full rebuild exists
61
+ * to fix — and re-derives its indexed document.
62
+ *
63
+ * This is the "Reindexer la recherche" tool (fiche 24 task 3): after an
64
+ * import, or after `SearchDriver.clear()`, nothing else ever repopulates the
65
+ * index for content that already existed before the write path saw it.
66
+ */
67
+ export declare function reindexAll<TValues extends ContentValues = ContentValues>(store: ContentStore<TValues>, options: SearchIndexingOptions, onProgress?: (count: number) => void): Promise<number>;
33
68
  export declare function withSearchIndexing<TValues extends ContentValues = ContentValues>(store: ContentStore<TValues>, options: SearchIndexingOptions): ContentStore<TValues>;
69
+ export {};
34
70
  //# sourceMappingURL=search-indexing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search-indexing.d.ts","sourceRoot":"","sources":["../../src/store/search-indexing.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAA;IACzC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;IAC5B;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CAC5C;AA0CD,wBAAgB,kBAAkB,CAAC,OAAO,SAAS,aAAa,GAAG,aAAa,EAC9E,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAC5B,OAAO,EAAE,qBAAqB,GAC7B,YAAY,CAAC,OAAO,CAAC,CA2BvB"}
1
+ {"version":3,"file":"search-indexing.d.ts","sourceRoot":"","sources":["../../src/store/search-indexing.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAA;IACzC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;IAC5B;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CAC5C;AAED;;;;;;;;;;;;;;;GAeG;AACH,iBAAe,OAAO,CAAC,OAAO,SAAS,aAAa,EAClD,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAC5B,OAAO,EAAE,qBAAqB,EAC9B,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,gBAAU,CAAA;AAEnC;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAAC,OAAO,SAAS,aAAa,GAAG,aAAa,EAC5E,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAC5B,OAAO,EAAE,qBAAqB,EAC9B,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GACnC,OAAO,CAAC,MAAM,CAAC,CAcjB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,SAAS,aAAa,GAAG,aAAa,EAC9E,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAC5B,OAAO,EAAE,qBAAqB,GAC7B,YAAY,CAAC,OAAO,CAAC,CA2BvB"}
@@ -34,6 +34,39 @@ async function reindex(store, options, id) {
34
34
  options.onError?.(error);
35
35
  }
36
36
  }
37
+ /**
38
+ * Re-derives the indexed document for one entry — exported so a full rebuild
39
+ * (`reindexAll`, and `cogenta`'s "Reindex search" tool, fiche 24 task 3) uses
40
+ * the exact same per-entry logic as the write path, rather than a second
41
+ * copy that could drift from what `withSearchIndexing` actually does on save.
42
+ */
43
+ export const reindexEntry = reindex;
44
+ /**
45
+ * Walks every entry of a collection — trashed included, since a trashed entry
46
+ * can still be `published` (ADR-0022: trash is orthogonal to status) and a
47
+ * stale search hit for it is exactly the kind of thing a full rebuild exists
48
+ * to fix — and re-derives its indexed document.
49
+ *
50
+ * This is the "Reindexer la recherche" tool (fiche 24 task 3): after an
51
+ * import, or after `SearchDriver.clear()`, nothing else ever repopulates the
52
+ * index for content that already existed before the write path saw it.
53
+ */
54
+ export async function reindexAll(store, options, onProgress) {
55
+ let cursor;
56
+ let count = 0;
57
+ for (;;) {
58
+ const page = await store.list({ trashed: 'include', limit: 100, ...(cursor ? { cursor } : {}) });
59
+ for (const entry of page.items) {
60
+ await reindex(store, options, entry.id);
61
+ count += 1;
62
+ onProgress?.(count);
63
+ }
64
+ if (!page.hasMore || page.nextCursor === null)
65
+ break;
66
+ cursor = page.nextCursor;
67
+ }
68
+ return count;
69
+ }
37
70
  export function withSearchIndexing(store, options) {
38
71
  async function after(entry) {
39
72
  await reindex(store, options, entry.id);
@@ -1 +1 @@
1
- {"version":3,"file":"search-indexing.js","sourceRoot":"","sources":["../../src/store/search-indexing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"search-indexing.js","sourceRoot":"","sources":["../../src/store/search-indexing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,164 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * The declarative registry fiche 23 task 1 asks for: "un magasin clé/valeur
4
+ * typé, avec un schéma Zod déclarant chaque réglage : clé, type, valeur par
5
+ * défaut, portée (site ou locale), permission requise. Un réglage inconnu est
6
+ * refusé — pas un dépotoir de clés libres."
7
+ *
8
+ * Adding a setting is one entry here — `SiteSettingsStore` validates,
9
+ * defaults and scopes every key generically from this list (never a special
10
+ * case per key), and the admin's settings screen renders a field from each
11
+ * entry's `group`/`order`/`uiType` the same way (`site-settings-field.tsx`).
12
+ * That is what makes the fiche's own acceptance test true: "ajouter un
13
+ * réglage nouveau = une ligne de déclaration, et il apparaît à l'écran sans
14
+ * code d'interface supplémentaire."
15
+ *
16
+ * Every key here is **editorial** (ADR-0025's third category) — a rédacteur
17
+ * changes it from the admin, and it never duplicates a field
18
+ * `cogenta.config.mjs` already owns. `general.defaultLocale` and
19
+ * `reading.notFoundPath`, for instance, stay in the config file on purpose
20
+ * (section 8 of the fiche recommends migrating nothing that already exists);
21
+ * the settings screen shows them too, but read-only, mirrored from
22
+ * `SchemaDocument`/`Site.site` rather than declared here.
23
+ */
24
+ /** How the admin renders a value — never a free-form widget choice per key. */
25
+ export declare const SITE_SETTING_UI_TYPES: readonly ['string', 'text', 'email', 'boolean', 'path', 'number', 'timeZone', 'dateStyle', 'timeStyle',
26
+ /**
27
+ * A closed choice, rendered as a `<select>`. Fiche 34 task 4's first user:
28
+ * `commerce.priceDisplay` (`ttc`/`ht`) — free text would let an operator
29
+ * type a third value the totals engine does not know what to do with.
30
+ */
31
+ 'select',
32
+ /**
33
+ * A single optional media reference, rendered as `MediaPicker` (fiche L21
34
+ * task 8's first and only user: `branding.customLogoMediaId`). The value
35
+ * stored is a media id, or `''` for "unset" — a plain string, like every
36
+ * other value this registry holds; nothing here talks to `MediaStore`
37
+ * directly, exactly the way a `path` setting names a route without
38
+ * checking the route exists.
39
+ */
40
+ 'media',
41
+ /**
42
+ * A short ordered list of `{label, url}` pairs (L25 D2's first and only
43
+ * user: `general.socialLinks`), rendered as a textarea of one
44
+ * `Label | https://url` line per entry, converted to/from JSON by the
45
+ * field component itself — the same "closest shape in the closed set"
46
+ * reasoning `seo.collectionTitleTemplates` already documents for a `text`
47
+ * bypass, except this one *is* a real, generic editor (a JSON object map
48
+ * has no natural line-per-entry form; an ordered list of two short strings
49
+ * does) rather than a placeholder for a bespoke screen.
50
+ */
51
+ 'linkList'];
52
+ export type SiteSettingUiType = (typeof SITE_SETTING_UI_TYPES)[number];
53
+ export declare const SITE_SETTING_GROUPS: readonly ['general', 'reading', 'discussion', 'media', 'privacy',
54
+ /**
55
+ * The shop-wide settings of fiche 34 task 4 — currency, tax-inclusive vs.
56
+ * exclusive display, countries served, minimum order, default backorder
57
+ * policy, and the invoice mentions of task 5. `commerce.tosPagePath` and
58
+ * `commerce.returnPolicyPagePath` are, deliberately, **paths** here —
59
+ * exactly the way `reading.homePath`/`privacy.policyPath` already point at
60
+ * a real content entry rather than storing legal text as a settings field
61
+ * (fiche 34 § pièges: "les CGV doivent être une page publique").
62
+ */
63
+ 'commerce',
64
+ /**
65
+ * Cogenta's own credit and white-label override (fiche L21 task 8) —
66
+ * whether the public footer and the admin shell name Cogenta at all, and
67
+ * what replaces it when they don't. Editorial on purpose: a reseller
68
+ * turns this off from the admin the same way any other setting here
69
+ * changes, with no redeploy and no code touched.
70
+ */
71
+ 'branding',
72
+ /**
73
+ * Search-engine visibility (fiche 21 task 3): title/description templates,
74
+ * per-collection sitemap hints, and social-card defaults. Previously
75
+ * `seo.tsx` was read-only "by design" — that was a scope choice of a
76
+ * previous lot, never an ADR, and this group is what makes it editable
77
+ * without inventing a second settings store: exactly the same registry
78
+ * that already backs `SettingsRoute`, just with its own screen
79
+ * (`SeoRoute`) rather than a tab there, the same way `commerce` gets its
80
+ * own screen instead of a `SettingsRoute` tab.
81
+ */
82
+ 'seo',
83
+ /**
84
+ * OpenTelemetry tracing and structured-log collection (fiche L22 task 5).
85
+ * Editorial on purpose, unlike the OTLP export destination (which stays in
86
+ * `cogenta.config.mjs`'s `observability` section, because it can carry a
87
+ * bearer-token header — rule R7): whether the local recent-events buffer
88
+ * runs at all, and how verbose it is, are exactly the kind of switch an
89
+ * admin should be able to flip from the "Exploitation" screen with no
90
+ * redeploy, the same way `discussion.enabled` already works.
91
+ */
92
+ 'observability',
93
+ /**
94
+ * L22 task 4 — which collections the writing assistant's vector index
95
+ * actually contains. Its own screen (the redesigned `AssistantRoute`),
96
+ * not a `SettingsRoute` tab, for the same reason `commerce`/`seo` get
97
+ * their own: the one key this group holds (`assistant.indexedCollections`)
98
+ * is a per-collection map, not a single scalar a generic form field can
99
+ * render — exactly `seo.sitemapCollectionSettings`'s own shape.
100
+ */
101
+ 'assistant',
102
+ /**
103
+ * The update system (L22 task 9): whether `cogenta serve`'s own
104
+ * `updates-auto-check` scheduled task may apply an npm update on its own,
105
+ * and how far it may go. Lives here, not in `cogenta.config.mjs`, for the
106
+ * same reason `discussion.moderationRequired` does — it is a live
107
+ * operational choice an admin flips from the screen at 3am because a
108
+ * package just misbehaved, not something that should need a git commit
109
+ * and a redeploy to change. Read by the scheduled task on every tick
110
+ * (`updates.ts` in `cogenta serve`'s wiring), never cached at startup.
111
+ */
112
+ 'updates',
113
+ /**
114
+ * Admin nav reordering/hiding (fiche 22 tâche 8, part 3) — which sidebar
115
+ * sections and entries a signed-in actor sees, and in what order.
116
+ * Deliberately **site-wide**, not per-browser: `sidebarCollapsed` and
117
+ * `groupOpen` (`app-shell.tsx`) stay in `localStorage` on purpose (each
118
+ * person's own "collapsed today" is not something anyone else needs to
119
+ * see), but "this site sells nothing, so hide Boutique for everyone" is an
120
+ * editorial decision about the site, no different from every other setting
121
+ * in this registry — it has to survive a different browser, a different
122
+ * person signing in, and a fresh install pulling the same database.
123
+ */
124
+ 'navigation',
125
+ /**
126
+ * Fiche 59 — the one non-secret, operator-set fact a channel's step-by-step
127
+ * guide needs to name the bot by handle instead of "the Cogenta bot": each
128
+ * channel's bot display name/handle, free text, never a credential. Bot
129
+ * *tokens* stay environment-only (R7) and never enter this table — this
130
+ * group exists specifically so a guide can say "open a chat with
131
+ * @my_cogenta_bot" without ever storing anything a leaked table could turn
132
+ * into a working credential.
133
+ */
134
+ 'channels'];
135
+ export type SiteSettingGroup = (typeof SITE_SETTING_GROUPS)[number];
136
+ export declare const SITE_SETTING_SCOPES: readonly ['site', 'locale'];
137
+ export type SiteSettingScope = (typeof SITE_SETTING_SCOPES)[number];
138
+ export interface SiteSettingDefinition {
139
+ /** The machine key, `<group-ish>.<name>` by convention — never parsed, just a stable id. */
140
+ readonly key: string;
141
+ readonly group: SiteSettingGroup;
142
+ /** Sort order within the group, ascending. Ties break on `key`. */
143
+ readonly order: number;
144
+ readonly uiType: SiteSettingUiType;
145
+ readonly scope: SiteSettingScope;
146
+ readonly schema: z.ZodType;
147
+ readonly defaultValue: unknown;
148
+ /** Only meaningful for `uiType: 'select'` — the closed set of choices the admin renders. */
149
+ readonly options?: readonly {
150
+ readonly value: string;
151
+ readonly label: string;
152
+ }[];
153
+ /**
154
+ * Only `admin` may write any of these (fiche 23 § "Critères d'acceptation":
155
+ * "réglages éditoriaux réservés à admin"). Kept on the descriptor rather
156
+ * than hardcoded in the router so a future setting could in principle name
157
+ * a different role — none does today, and the router still refuses
158
+ * anything this field does not say.
159
+ */
160
+ readonly writeRoles: readonly string[];
161
+ }
162
+ export declare const SITE_SETTINGS_REGISTRY: readonly SiteSettingDefinition[];
163
+ export declare function siteSettingByKey(key: string): SiteSettingDefinition | undefined;
164
+ //# sourceMappingURL=site-settings-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-settings-registry.d.ts","sourceRoot":"","sources":["../../src/store/site-settings-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,+EAA+E;AAC/E,eAAO,MAAM,qBAAqB,YAChC,QAAQ,EACR,MAAM,EACN,OAAO,EACP,SAAS,EACT,MAAM,EACN,QAAQ,EACR,UAAU,EACV,WAAW,EACX,WAAW;AACX;;;;GAIG;AACH,QAAQ;AACR;;;;;;;GAOG;AACH,OAAO;AACP;;;;;;;;;GASG;AACH,UAAU,CACF,CAAA;AAEV,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEtE,eAAO,MAAM,mBAAmB,YAC9B,SAAS,EACT,SAAS,EACT,YAAY,EACZ,OAAO,EACP,SAAS;AACT;;;;;;;;GAQG;AACH,UAAU;AACV;;;;;;GAMG;AACH,UAAU;AACV;;;;;;;;;GASG;AACH,KAAK;AACL;;;;;;;;GAQG;AACH,eAAe;AACf;;;;;;;GAOG;AACH,WAAW;AACX;;;;;;;;;GASG;AACH,SAAS;AACT;;;;;;;;;;GAUG;AACH,YAAY;AACZ;;;;;;;;GAQG;AACH,UAAU,CACF,CAAA;AAEV,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnE,eAAO,MAAM,mBAAmB,YAAI,MAAM,EAAE,QAAQ,CAAU,CAAA;AAE9D,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnE,MAAM,WAAW,qBAAqB;IACpC,4FAA4F;IAC5F,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAA;IAChC,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAA;IAClC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAA;IAChC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAA;IAC1B,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAA;IAC9B,4FAA4F;IAC5F,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAChF;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;CACvC;AA4LD,eAAO,MAAM,sBAAsB,EAAE,SAAS,qBAAqB,EA+3BzD,CAAA;AAIV,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAE/E"}