@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.
- package/dist/define-collection.d.ts.map +1 -1
- package/dist/define-collection.js +53 -3
- package/dist/define-collection.js.map +1 -1
- package/dist/generate-types.d.ts.map +1 -1
- package/dist/generate-types.js +6 -1
- package/dist/generate-types.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/media-usage.d.ts +56 -0
- package/dist/media-usage.d.ts.map +1 -0
- package/dist/media-usage.js +89 -0
- package/dist/media-usage.js.map +1 -0
- package/dist/rich-text.d.ts +28 -2
- package/dist/rich-text.d.ts.map +1 -1
- package/dist/rich-text.js +26 -3
- package/dist/rich-text.js.map +1 -1
- package/dist/routing/index.d.ts +5 -1
- package/dist/routing/index.d.ts.map +1 -1
- package/dist/routing/index.js +2 -0
- package/dist/routing/index.js.map +1 -1
- package/dist/routing/not-found-log.d.ts +78 -0
- package/dist/routing/not-found-log.d.ts.map +1 -0
- package/dist/routing/not-found-log.js +153 -0
- package/dist/routing/not-found-log.js.map +1 -0
- package/dist/routing/redirect-patterns.d.ts +61 -0
- package/dist/routing/redirect-patterns.d.ts.map +1 -0
- package/dist/routing/redirect-patterns.js +173 -0
- package/dist/routing/redirect-patterns.js.map +1 -0
- package/dist/routing/redirects.d.ts +27 -4
- package/dist/routing/redirects.d.ts.map +1 -1
- package/dist/routing/redirects.js +160 -48
- package/dist/routing/redirects.js.map +1 -1
- package/dist/scheduling/index.d.ts +2 -0
- package/dist/scheduling/index.d.ts.map +1 -1
- package/dist/scheduling/index.js +1 -0
- package/dist/scheduling/index.js.map +1 -1
- package/dist/scheduling/registry.d.ts +147 -0
- package/dist/scheduling/registry.d.ts.map +1 -0
- package/dist/scheduling/registry.js +318 -0
- package/dist/scheduling/registry.js.map +1 -0
- package/dist/search/extract.d.ts +24 -0
- package/dist/search/extract.d.ts.map +1 -1
- package/dist/search/extract.js +63 -8
- package/dist/search/extract.js.map +1 -1
- package/dist/search/index.d.ts +2 -1
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/index.js +1 -9
- package/dist/search/index.js.map +1 -1
- package/dist/store/admin-theme-store.d.ts +26 -0
- package/dist/store/admin-theme-store.d.ts.map +1 -0
- package/dist/store/admin-theme-store.js +81 -0
- package/dist/store/admin-theme-store.js.map +1 -0
- package/dist/store/admin-theme-tables.d.ts +24 -0
- package/dist/store/admin-theme-tables.d.ts.map +1 -0
- package/dist/store/admin-theme-tables.js +41 -0
- package/dist/store/admin-theme-tables.js.map +1 -0
- package/dist/store/admin-theme-templates.d.ts +135 -0
- package/dist/store/admin-theme-templates.d.ts.map +1 -0
- package/dist/store/admin-theme-templates.js +217 -0
- package/dist/store/admin-theme-templates.js.map +1 -0
- package/dist/store/columns.d.ts.map +1 -1
- package/dist/store/columns.js +8 -2
- package/dist/store/columns.js.map +1 -1
- package/dist/store/diff.d.ts +40 -0
- package/dist/store/diff.d.ts.map +1 -1
- package/dist/store/diff.js +148 -0
- package/dist/store/diff.js.map +1 -1
- package/dist/store/index.d.ts +44 -6
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +24 -3
- package/dist/store/index.js.map +1 -1
- package/dist/store/maintenance-store.d.ts +34 -0
- package/dist/store/maintenance-store.d.ts.map +1 -0
- package/dist/store/maintenance-store.js +92 -0
- package/dist/store/maintenance-store.js.map +1 -0
- package/dist/store/menu-store.d.ts +48 -1
- package/dist/store/menu-store.d.ts.map +1 -1
- package/dist/store/menu-store.js +151 -4
- package/dist/store/menu-store.js.map +1 -1
- package/dist/store/menu-tables.d.ts +12 -0
- package/dist/store/menu-tables.d.ts.map +1 -1
- package/dist/store/menu-tables.js +38 -0
- package/dist/store/menu-tables.js.map +1 -1
- package/dist/store/naming.d.ts +1 -1
- package/dist/store/naming.d.ts.map +1 -1
- package/dist/store/naming.js +2 -0
- package/dist/store/naming.js.map +1 -1
- package/dist/store/pattern-store.d.ts +75 -0
- package/dist/store/pattern-store.d.ts.map +1 -0
- package/dist/store/pattern-store.js +118 -0
- package/dist/store/pattern-store.js.map +1 -0
- package/dist/store/pattern-tables.d.ts +29 -0
- package/dist/store/pattern-tables.d.ts.map +1 -0
- package/dist/store/pattern-tables.js +53 -0
- package/dist/store/pattern-tables.js.map +1 -0
- package/dist/store/read-only.d.ts +3 -3
- package/dist/store/read-only.d.ts.map +1 -1
- package/dist/store/read-only.js +4 -3
- package/dist/store/read-only.js.map +1 -1
- package/dist/store/redirect-tracking.d.ts +50 -0
- package/dist/store/redirect-tracking.d.ts.map +1 -0
- package/dist/store/redirect-tracking.js +91 -0
- package/dist/store/redirect-tracking.js.map +1 -0
- package/dist/store/review-transitions.d.ts +34 -0
- package/dist/store/review-transitions.d.ts.map +1 -0
- package/dist/store/review-transitions.js +51 -0
- package/dist/store/review-transitions.js.map +1 -0
- package/dist/store/role-permission-export.d.ts +22 -0
- package/dist/store/role-permission-export.d.ts.map +1 -0
- package/dist/store/role-permission-export.js +0 -0
- package/dist/store/role-permission-export.js.map +1 -0
- package/dist/store/role-permission-overlay.d.ts +27 -0
- package/dist/store/role-permission-overlay.d.ts.map +1 -0
- package/dist/store/role-permission-overlay.js +0 -0
- package/dist/store/role-permission-overlay.js.map +1 -0
- package/dist/store/role-permission-store.d.ts +72 -0
- package/dist/store/role-permission-store.d.ts.map +1 -0
- package/dist/store/role-permission-store.js +148 -0
- package/dist/store/role-permission-store.js.map +1 -0
- package/dist/store/role-permission-tables.d.ts +27 -0
- package/dist/store/role-permission-tables.d.ts.map +1 -0
- package/dist/store/role-permission-tables.js +52 -0
- package/dist/store/role-permission-tables.js.map +1 -0
- package/dist/store/scheduled-publish-failures.d.ts +36 -0
- package/dist/store/scheduled-publish-failures.d.ts.map +1 -0
- package/dist/store/scheduled-publish-failures.js +67 -0
- package/dist/store/scheduled-publish-failures.js.map +1 -0
- package/dist/store/schema-2-1-migration.d.ts +29 -0
- package/dist/store/schema-2-1-migration.d.ts.map +1 -0
- package/dist/store/schema-2-1-migration.js +71 -0
- package/dist/store/schema-2-1-migration.js.map +1 -0
- package/dist/store/search-console-store.d.ts +44 -0
- package/dist/store/search-console-store.d.ts.map +1 -0
- package/dist/store/search-console-store.js +90 -0
- package/dist/store/search-console-store.js.map +1 -0
- package/dist/store/search-console-tables.d.ts +14 -0
- package/dist/store/search-console-tables.d.ts.map +1 -0
- package/dist/store/search-console-tables.js +29 -0
- package/dist/store/search-console-tables.js.map +1 -0
- package/dist/store/search-indexing.d.ts +36 -0
- package/dist/store/search-indexing.d.ts.map +1 -1
- package/dist/store/search-indexing.js +33 -0
- package/dist/store/search-indexing.js.map +1 -1
- package/dist/store/site-settings-registry.d.ts +164 -0
- package/dist/store/site-settings-registry.d.ts.map +1 -0
- package/dist/store/site-settings-registry.js +1200 -0
- package/dist/store/site-settings-registry.js.map +1 -0
- package/dist/store/site-settings-store.d.ts +42 -0
- package/dist/store/site-settings-store.d.ts.map +1 -0
- package/dist/store/site-settings-store.js +97 -0
- package/dist/store/site-settings-store.js.map +1 -0
- package/dist/store/site-settings-tables.d.ts +29 -0
- package/dist/store/site-settings-tables.d.ts.map +1 -0
- package/dist/store/site-settings-tables.js +48 -0
- package/dist/store/site-settings-tables.js.map +1 -0
- package/dist/store/store.d.ts +48 -1
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +231 -39
- package/dist/store/store.js.map +1 -1
- package/dist/store/tables.d.ts.map +1 -1
- package/dist/store/tables.js +11 -0
- package/dist/store/tables.js.map +1 -1
- package/dist/store/taxonomy-store.d.ts.map +1 -1
- package/dist/store/taxonomy-store.js +90 -39
- package/dist/store/taxonomy-store.js.map +1 -1
- package/dist/store/taxonomy-usage.d.ts +47 -0
- package/dist/store/taxonomy-usage.d.ts.map +1 -0
- package/dist/store/taxonomy-usage.js +101 -0
- package/dist/store/taxonomy-usage.js.map +1 -0
- package/dist/store/theme-store.d.ts +73 -0
- package/dist/store/theme-store.d.ts.map +1 -0
- package/dist/store/theme-store.js +193 -0
- package/dist/store/theme-store.js.map +1 -0
- package/dist/store/types.d.ts +36 -1
- package/dist/store/types.d.ts.map +1 -1
- package/dist/store/values.d.ts.map +1 -1
- package/dist/store/values.js +28 -1
- package/dist/store/values.js.map +1 -1
- package/dist/system-fields.d.ts +8 -1
- package/dist/system-fields.d.ts.map +1 -1
- package/dist/system-fields.js +14 -1
- package/dist/system-fields.js.map +1 -1
- package/dist/types.d.ts +54 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +21 -0
- package/dist/types.js.map +1 -1
- 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;
|
|
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"}
|