@cogenta/schema 0.3.0 → 0.4.1
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 +1160 -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 @@
|
|
|
1
|
+
{"version":3,"file":"role-permission-export.js","sourceRoot":"","sources":["../../src/store/role-permission-export.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ContentAction, NormalisedPermissionRule } from '../types.js';
|
|
2
|
+
import type { RolePermissionStore } from './role-permission-store.js';
|
|
3
|
+
/**
|
|
4
|
+
* The synchronous face `PermissionLayer` (`@cogenta/api`) actually reads.
|
|
5
|
+
*
|
|
6
|
+
* `can()`/`canTerm()` run on every request, in a tight loop, and must stay
|
|
7
|
+
* synchronous — turning them `async` would ripple through every REST route,
|
|
8
|
+
* the GraphQL gateway and the page-builder preview for one feature. So the
|
|
9
|
+
* database read happens once, here, ahead of time; the request path only
|
|
10
|
+
* ever does a `Map.get`.
|
|
11
|
+
*/
|
|
12
|
+
export interface RolePermissionOverrides {
|
|
13
|
+
/** `undefined` means no override exists — the caller falls back to the file. Never the reverse. */
|
|
14
|
+
getCollectionRule(collection: string, action: ContentAction): NormalisedPermissionRule | undefined;
|
|
15
|
+
getTaxonomyRule(taxonomy: string, action: ContentAction): NormalisedPermissionRule | undefined;
|
|
16
|
+
}
|
|
17
|
+
export interface RolePermissionOverlay extends RolePermissionOverrides {
|
|
18
|
+
/**
|
|
19
|
+
* Re-reads every row from the store. Call after a write completes so the
|
|
20
|
+
* very next request already sees it — no restart, the same promise the
|
|
21
|
+
* appearance screen's live theme swap already makes.
|
|
22
|
+
*/
|
|
23
|
+
refresh(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/** Built once at startup (`assembleSite`), awaited before `createPermissionLayer` runs. */
|
|
26
|
+
export declare function createRolePermissionOverlay(store: RolePermissionStore): Promise<RolePermissionOverlay>;
|
|
27
|
+
//# sourceMappingURL=role-permission-overlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-permission-overlay.d.ts","sourceRoot":"","sources":["../../src/store/role-permission-overlay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAC1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAErE;;;;;;;;GAQG;AACH,MAAM,WAAW,uBAAuB;IACtC,mGAAmG;IACnG,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,wBAAwB,GAAG,SAAS,CAAA;IAClG,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,wBAAwB,GAAG,SAAS,CAAA;CAC/F;AAED,MAAM,WAAW,qBAAsB,SAAQ,uBAAuB;IACpE;;;;OAIG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB;AAMD,2FAA2F;AAC3F,wBAAsB,2BAA2B,CAC/C,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,qBAAqB,CAAC,CA0BhC"}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-permission-overlay.js","sourceRoot":"","sources":["../../src/store/role-permission-overlay.ts"],"names":[],"mappings":"AA2BA,SAAS,GAAG,CAAC,IAAY,EAAE,MAAqB;IAC9C,OAAO,GAAG,IAAI,IAAI,MAAM,EAAE,CAAA;AAC5B,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,KAA0B;IAE1B,IAAI,YAAY,GAAG,IAAI,GAAG,EAAoC,CAAA;IAC9D,IAAI,UAAU,GAAG,IAAI,GAAG,EAAoC,CAAA;IAE5D,KAAK,UAAU,MAAM;QACnB,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,CAAA;QAClC,MAAM,cAAc,GAAG,IAAI,GAAG,EAAoC,CAAA;QAClE,MAAM,YAAY,GAAG,IAAI,GAAG,EAAoC,CAAA;QAEhE,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,IAAI,GAA6B,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAA;YAC/E,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,KAAK,YAAY,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,YAAY,CAAA;YACjF,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAA;QACzD,CAAC;QAED,YAAY,GAAG,cAAc,CAAA;QAC7B,UAAU,GAAG,YAAY,CAAA;IAC3B,CAAC;IAED,MAAM,MAAM,EAAE,CAAA;IAEd,OAAO;QACL,iBAAiB,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACpF,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC5E,OAAO,EAAE,MAAM;KAChB,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { type DatabaseHandle } from '@cogenta/core';
|
|
2
|
+
import type { CollectionDefinition, ContentAction, TaxonomyDefinition } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The database-backed override layer of a role's permissions (fiche 63,
|
|
5
|
+
* ADR-0028).
|
|
6
|
+
*
|
|
7
|
+
* A row here **replaces** a `(targetType, targetName, action)` rule from
|
|
8
|
+
* `cogenta.schema.*` entirely; it never merges with it. `PermissionLayer`
|
|
9
|
+
* (`@cogenta/api`) is the only reader that turns this into an access
|
|
10
|
+
* decision — this store's job stops at persisting a validated row.
|
|
11
|
+
*
|
|
12
|
+
* **Validation reuses `defineCollection`/`defineTaxonomy` — task 4 of the
|
|
13
|
+
* fiche, "ne pas dupliquer une seconde logique de validation."** A candidate
|
|
14
|
+
* write is folded into the real, already-loaded `CollectionDefinition` or
|
|
15
|
+
* `TaxonomyDefinition` (the same objects `PermissionLayer` was built from) in
|
|
16
|
+
* place of the one action being changed, and the result is handed to the
|
|
17
|
+
* same function that validates a collection or taxonomy loaded from the
|
|
18
|
+
* schema file. An invalid role name, an unknown action, or `own` on a
|
|
19
|
+
* taxonomy (which has no author — `defineTaxonomy`'s own `checkPermissions`
|
|
20
|
+
* only accepts a plain role list) is refused by that single door, not a
|
|
21
|
+
* second copy of its rules.
|
|
22
|
+
*/
|
|
23
|
+
export type RolePermissionTargetType = 'collection' | 'taxonomy';
|
|
24
|
+
export interface RolePermissionOverrideRecord {
|
|
25
|
+
readonly targetType: RolePermissionTargetType;
|
|
26
|
+
readonly targetName: string;
|
|
27
|
+
readonly action: ContentAction;
|
|
28
|
+
readonly roles: readonly string[];
|
|
29
|
+
/** Always `false` for a taxonomy target — see the module comment. */
|
|
30
|
+
readonly own: boolean;
|
|
31
|
+
readonly updatedAt: string;
|
|
32
|
+
readonly updatedBy: string | null;
|
|
33
|
+
}
|
|
34
|
+
export interface SetRolePermissionInput {
|
|
35
|
+
readonly targetType: RolePermissionTargetType;
|
|
36
|
+
readonly targetName: string;
|
|
37
|
+
readonly action: ContentAction;
|
|
38
|
+
readonly roles: readonly string[];
|
|
39
|
+
readonly own?: boolean;
|
|
40
|
+
readonly updatedBy?: string | null;
|
|
41
|
+
}
|
|
42
|
+
export interface RolePermissionStoreOptions {
|
|
43
|
+
readonly db: DatabaseHandle;
|
|
44
|
+
/**
|
|
45
|
+
* The file-declared collections/taxonomies to validate an override
|
|
46
|
+
* against — the same arrays `createPermissionLayer` was built from at
|
|
47
|
+
* startup. A target absent from both is `ROLE_PERMISSION_TARGET_UNKNOWN`:
|
|
48
|
+
* an override can narrow or widen an existing collection or taxonomy's
|
|
49
|
+
* rule, never invent a collection that is not declared in code.
|
|
50
|
+
*/
|
|
51
|
+
readonly collections: readonly CollectionDefinition[];
|
|
52
|
+
readonly taxonomies: readonly TaxonomyDefinition[];
|
|
53
|
+
readonly now?: () => Date;
|
|
54
|
+
}
|
|
55
|
+
export interface RolePermissionStore {
|
|
56
|
+
/** Creates the table if it is missing. Called for you by every other method. */
|
|
57
|
+
ensureTable(): Promise<void>;
|
|
58
|
+
list(): Promise<readonly RolePermissionOverrideRecord[]>;
|
|
59
|
+
/**
|
|
60
|
+
* Validates the candidate (see the module comment) and writes it,
|
|
61
|
+
* replacing any existing row for the same `(targetType, targetName,
|
|
62
|
+
* action)` triple.
|
|
63
|
+
*/
|
|
64
|
+
set(input: SetRolePermissionInput): Promise<RolePermissionOverrideRecord>;
|
|
65
|
+
/**
|
|
66
|
+
* Deletes the override, if any — the target's action falls back to
|
|
67
|
+
* `cogenta.schema.*` on the very next check. Returns whether a row existed.
|
|
68
|
+
*/
|
|
69
|
+
remove(targetType: RolePermissionTargetType, targetName: string, action: ContentAction): Promise<boolean>;
|
|
70
|
+
}
|
|
71
|
+
export declare function createRolePermissionStore(options: RolePermissionStoreOptions): RolePermissionStore;
|
|
72
|
+
//# sourceMappingURL=role-permission-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-permission-store.d.ts","sourceRoot":"","sources":["../../src/store/role-permission-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,cAAc,EAAmB,MAAM,eAAe,CAAA;AAGlF,OAAO,KAAK,EACV,oBAAoB,EAEpB,aAAa,EACb,kBAAkB,EACnB,MAAM,aAAa,CAAA;AAGpB;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,UAAU,CAAA;AAEhE,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,UAAU,EAAE,wBAAwB,CAAA;IAC7C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,qEAAqE;IACrE,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAA;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,UAAU,EAAE,wBAAwB,CAAA;IAC7C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACnC;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAA;IAC3B;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,oBAAoB,EAAE,CAAA;IACrD,QAAQ,CAAC,UAAU,EAAE,SAAS,kBAAkB,EAAE,CAAA;IAClD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,gFAAgF;IAChF,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5B,IAAI,IAAI,OAAO,CAAC,SAAS,4BAA4B,EAAE,CAAC,CAAA;IACxD;;;;OAIG;IACH,GAAG,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAA;IACzE;;;OAGG;IACH,MAAM,CACJ,UAAU,EAAE,wBAAwB,EACpC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,OAAO,CAAC,CAAA;CACpB;AAgFD,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,0BAA0B,GAClC,mBAAmB,CAqGrB"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { CogentaError, identifier, sql } from '@cogenta/core';
|
|
2
|
+
import { defineCollection } from '../define-collection.js';
|
|
3
|
+
import { defineTaxonomy } from '../define-taxonomy.js';
|
|
4
|
+
import { ensureRolePermissionTable, ROLE_PERMISSIONS_TABLE } from './role-permission-tables.js';
|
|
5
|
+
function text(value) {
|
|
6
|
+
return typeof value === 'string' ? value : String(value);
|
|
7
|
+
}
|
|
8
|
+
function nullableText(value) {
|
|
9
|
+
return value === null || value === undefined ? null : text(value);
|
|
10
|
+
}
|
|
11
|
+
function toBool(value) {
|
|
12
|
+
return value === true || value === 'true' || value === 1 || value === '1';
|
|
13
|
+
}
|
|
14
|
+
function toRecord(row) {
|
|
15
|
+
return {
|
|
16
|
+
targetType: text(row['target_type']),
|
|
17
|
+
targetName: text(row['target_name']),
|
|
18
|
+
action: text(row['action']),
|
|
19
|
+
roles: JSON.parse(text(row['roles'])),
|
|
20
|
+
own: toBool(row['own']),
|
|
21
|
+
updatedAt: text(row['updated_at']),
|
|
22
|
+
updatedBy: nullableText(row['updated_by']),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function unknownTarget(targetType, targetName) {
|
|
26
|
+
return new CogentaError({
|
|
27
|
+
code: 'ROLE_PERMISSION_TARGET_UNKNOWN',
|
|
28
|
+
message: `No ${targetType} named "${targetName}" is declared in the site's schema.`,
|
|
29
|
+
hint: targetType === 'collection'
|
|
30
|
+
? 'Overriding a role permission needs a real defineCollection() — check the spelling against cogenta.schema.*.'
|
|
31
|
+
: 'Overriding a role permission needs a real defineTaxonomy() — check the spelling against cogenta.schema.*.',
|
|
32
|
+
details: { targetType, targetName },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function ownNotSupported(targetName) {
|
|
36
|
+
return new CogentaError({
|
|
37
|
+
code: 'ROLE_PERMISSION_INVALID',
|
|
38
|
+
message: `Taxonomy "${targetName}" has no author, so "own" has no meaning here.`,
|
|
39
|
+
hint: 'Drop "own" — it only applies to a collection action (schema@2.1, ADR-0027).',
|
|
40
|
+
details: { targetName },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Folds one action's candidate rule into the real, already-validated
|
|
45
|
+
* definition and re-validates the whole thing through `defineCollection` —
|
|
46
|
+
* the single door contract A's permission shape ever passes through. Any
|
|
47
|
+
* other action already on the collection is carried over unchanged.
|
|
48
|
+
*/
|
|
49
|
+
function validateCollectionCandidate(target, action, roles, own) {
|
|
50
|
+
const permissions = {
|
|
51
|
+
...target.permissions,
|
|
52
|
+
[action]: own ? { roles, own: true } : [...roles],
|
|
53
|
+
};
|
|
54
|
+
// Throws SCHEMA_INVALID (via `schemaError`) naming every issue, exactly as
|
|
55
|
+
// it would for a hand-edited `cogenta.schema.*` — the only difference here
|
|
56
|
+
// is *where* the candidate came from.
|
|
57
|
+
defineCollection({ ...target, permissions });
|
|
58
|
+
}
|
|
59
|
+
function validateTaxonomyCandidate(target, action, roles) {
|
|
60
|
+
const permissions = { ...target.permissions, [action]: [...roles] };
|
|
61
|
+
defineTaxonomy({ ...target, permissions });
|
|
62
|
+
}
|
|
63
|
+
export function createRolePermissionStore(options) {
|
|
64
|
+
const { db, collections, taxonomies } = options;
|
|
65
|
+
const dialect = db.dialect;
|
|
66
|
+
const now = options.now ?? (() => new Date());
|
|
67
|
+
const table = identifier(ROLE_PERMISSIONS_TABLE, dialect);
|
|
68
|
+
let ready = false;
|
|
69
|
+
async function ensureTable() {
|
|
70
|
+
if (ready)
|
|
71
|
+
return;
|
|
72
|
+
await ensureRolePermissionTable(db);
|
|
73
|
+
ready = true;
|
|
74
|
+
}
|
|
75
|
+
function findCollection(name) {
|
|
76
|
+
return collections.find((collection) => collection.name === name);
|
|
77
|
+
}
|
|
78
|
+
function findTaxonomy(name) {
|
|
79
|
+
return taxonomies.find((taxonomy) => taxonomy.name === name);
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
ensureTable,
|
|
83
|
+
list: async () => {
|
|
84
|
+
await ensureTable();
|
|
85
|
+
const found = await db.query(sql `select * from ${table} order by ${identifier('target_type', dialect)} asc, ${identifier('target_name', dialect)} asc, ${identifier('action', dialect)} asc`);
|
|
86
|
+
return found.rows.map(toRecord);
|
|
87
|
+
},
|
|
88
|
+
set: async (input) => {
|
|
89
|
+
await ensureTable();
|
|
90
|
+
const own = input.own === true;
|
|
91
|
+
if (input.targetType === 'collection') {
|
|
92
|
+
const target = findCollection(input.targetName);
|
|
93
|
+
if (target === undefined)
|
|
94
|
+
throw unknownTarget('collection', input.targetName);
|
|
95
|
+
validateCollectionCandidate(target, input.action, input.roles, own);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
if (own)
|
|
99
|
+
throw ownNotSupported(input.targetName);
|
|
100
|
+
const target = findTaxonomy(input.targetName);
|
|
101
|
+
if (target === undefined)
|
|
102
|
+
throw unknownTarget('taxonomy', input.targetName);
|
|
103
|
+
validateTaxonomyCandidate(target, input.action, input.roles);
|
|
104
|
+
}
|
|
105
|
+
const at = now().toISOString();
|
|
106
|
+
const updatedBy = input.updatedBy ?? null;
|
|
107
|
+
const rolesJson = JSON.stringify(input.roles);
|
|
108
|
+
const ownValue = own ? 'true' : 'false';
|
|
109
|
+
await db.transaction(async (tx) => {
|
|
110
|
+
// Delete-then-insert rather than an upsert: `ON CONFLICT`, `ON
|
|
111
|
+
// DUPLICATE KEY` and `INSERT OR REPLACE` are three different
|
|
112
|
+
// statements across the three dialects — the same reasoning
|
|
113
|
+
// `redirects.ts`'s `performAdd` already gives for its own writes.
|
|
114
|
+
await tx.query(sql `delete from ${table}
|
|
115
|
+
where ${identifier('target_type', dialect)} = ${input.targetType}
|
|
116
|
+
and ${identifier('target_name', dialect)} = ${input.targetName}
|
|
117
|
+
and ${identifier('action', dialect)} = ${input.action}`);
|
|
118
|
+
await tx.query(sql `insert into ${table} (
|
|
119
|
+
${identifier('target_type', dialect)}, ${identifier('target_name', dialect)},
|
|
120
|
+
${identifier('action', dialect)}, ${identifier('roles', dialect)},
|
|
121
|
+
${identifier('own', dialect)}, ${identifier('updated_at', dialect)},
|
|
122
|
+
${identifier('updated_by', dialect)}
|
|
123
|
+
) values (
|
|
124
|
+
${input.targetType}, ${input.targetName}, ${input.action}, ${rolesJson},
|
|
125
|
+
${ownValue}, ${at}, ${updatedBy}
|
|
126
|
+
)`);
|
|
127
|
+
}, { immediate: true });
|
|
128
|
+
return {
|
|
129
|
+
targetType: input.targetType,
|
|
130
|
+
targetName: input.targetName,
|
|
131
|
+
action: input.action,
|
|
132
|
+
roles: input.roles,
|
|
133
|
+
own,
|
|
134
|
+
updatedAt: at,
|
|
135
|
+
updatedBy,
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
remove: async (targetType, targetName, action) => {
|
|
139
|
+
await ensureTable();
|
|
140
|
+
const result = await db.query(sql `delete from ${table}
|
|
141
|
+
where ${identifier('target_type', dialect)} = ${targetType}
|
|
142
|
+
and ${identifier('target_name', dialect)} = ${targetName}
|
|
143
|
+
and ${identifier('action', dialect)} = ${action}`);
|
|
144
|
+
return result.rowsAffected > 0;
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=role-permission-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-permission-store.js","sourceRoot":"","sources":["../../src/store/role-permission-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type DatabaseHandle } from '@cogenta/core';
|
|
2
|
+
/**
|
|
3
|
+
* Role permission overrides (fiche 63, ADR-0028).
|
|
4
|
+
*
|
|
5
|
+
* `cogenta.schema.*` (contract A's `permissions` block) is the file the site
|
|
6
|
+
* was deployed with — code, reviewed and versioned like any other (ADR-0010).
|
|
7
|
+
* This table is the deliberate exception that decision names: a permission
|
|
8
|
+
* change an admin makes in production, applied without a deploy cycle. One
|
|
9
|
+
* fixed table, exactly `menu-tables.ts`'s and `site-settings-tables.ts`'s
|
|
10
|
+
* pattern — a role's grant on a collection or taxonomy action is not
|
|
11
|
+
* schema-declared content, it is runtime, admin-editable state.
|
|
12
|
+
*
|
|
13
|
+
* One row per `(targetType, targetName, action)` triple **overrides** that
|
|
14
|
+
* action's rule entirely — it does not merge with the file. `PermissionLayer`
|
|
15
|
+
* (`@cogenta/api`) checks this table first and falls back to the file only
|
|
16
|
+
* when no row exists for that exact triple, never the other way around: a
|
|
17
|
+
* deployment that regressed the file must not silently widen what the table
|
|
18
|
+
* had already narrowed.
|
|
19
|
+
*
|
|
20
|
+
* `own` only ever applies to a collection (`schema@2.1`, ADR-0027) — a
|
|
21
|
+
* taxonomy term has no author, and `createRolePermissionStore`'s own
|
|
22
|
+
* validation (reusing `defineTaxonomy`'s `checkPermissions`) refuses `own` on
|
|
23
|
+
* a taxonomy target before a row is ever written.
|
|
24
|
+
*/
|
|
25
|
+
export declare const ROLE_PERMISSIONS_TABLE = "cogenta_role_permissions";
|
|
26
|
+
export declare function ensureRolePermissionTable(db: DatabaseHandle): Promise<void>;
|
|
27
|
+
//# sourceMappingURL=role-permission-tables.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-permission-tables.d.ts","sourceRoot":"","sources":["../../src/store/role-permission-tables.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAqC,MAAM,eAAe,CAAA;AAGtF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,eAAO,MAAM,sBAAsB,6BAA6B,CAAA;AAShE,wBAAsB,yBAAyB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBjF"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { identifier, sql } from '@cogenta/core';
|
|
2
|
+
import { booleanColumn, textColumn, timestampColumn } from './columns.js';
|
|
3
|
+
/**
|
|
4
|
+
* Role permission overrides (fiche 63, ADR-0028).
|
|
5
|
+
*
|
|
6
|
+
* `cogenta.schema.*` (contract A's `permissions` block) is the file the site
|
|
7
|
+
* was deployed with — code, reviewed and versioned like any other (ADR-0010).
|
|
8
|
+
* This table is the deliberate exception that decision names: a permission
|
|
9
|
+
* change an admin makes in production, applied without a deploy cycle. One
|
|
10
|
+
* fixed table, exactly `menu-tables.ts`'s and `site-settings-tables.ts`'s
|
|
11
|
+
* pattern — a role's grant on a collection or taxonomy action is not
|
|
12
|
+
* schema-declared content, it is runtime, admin-editable state.
|
|
13
|
+
*
|
|
14
|
+
* One row per `(targetType, targetName, action)` triple **overrides** that
|
|
15
|
+
* action's rule entirely — it does not merge with the file. `PermissionLayer`
|
|
16
|
+
* (`@cogenta/api`) checks this table first and falls back to the file only
|
|
17
|
+
* when no row exists for that exact triple, never the other way around: a
|
|
18
|
+
* deployment that regressed the file must not silently widen what the table
|
|
19
|
+
* had already narrowed.
|
|
20
|
+
*
|
|
21
|
+
* `own` only ever applies to a collection (`schema@2.1`, ADR-0027) — a
|
|
22
|
+
* taxonomy term has no author, and `createRolePermissionStore`'s own
|
|
23
|
+
* validation (reusing `defineTaxonomy`'s `checkPermissions`) refuses `own` on
|
|
24
|
+
* a taxonomy target before a row is ever written.
|
|
25
|
+
*/
|
|
26
|
+
export const ROLE_PERMISSIONS_TABLE = 'cogenta_role_permissions';
|
|
27
|
+
/** `collection` or `taxonomy` — matches `RolePermissionTargetType`. */
|
|
28
|
+
const TARGET_TYPE_LENGTH = 16;
|
|
29
|
+
/** Same bound `naming.ts`'s `checked()` enforces on a collection/taxonomy name. */
|
|
30
|
+
const TARGET_NAME_LENGTH = 64;
|
|
31
|
+
/** `read`, `create`, `update`, `delete`, `publish` — contract A's fixed set. */
|
|
32
|
+
const ACTION_LENGTH = 16;
|
|
33
|
+
export async function ensureRolePermissionTable(db) {
|
|
34
|
+
const dialect = db.dialect;
|
|
35
|
+
const table = identifier(ROLE_PERMISSIONS_TABLE, dialect);
|
|
36
|
+
const statements = [
|
|
37
|
+
sql `create table if not exists ${table} (
|
|
38
|
+
${identifier('target_type', dialect)} ${textColumn(dialect, TARGET_TYPE_LENGTH)} not null,
|
|
39
|
+
${identifier('target_name', dialect)} ${textColumn(dialect, TARGET_NAME_LENGTH)} not null,
|
|
40
|
+
${identifier('action', dialect)} ${textColumn(dialect, ACTION_LENGTH)} not null,
|
|
41
|
+
${identifier('roles', dialect)} ${textColumn(dialect, 2000)} not null,
|
|
42
|
+
${identifier('own', dialect)} ${booleanColumn(dialect)} not null,
|
|
43
|
+
${identifier('updated_at', dialect)} ${timestampColumn(dialect)} not null,
|
|
44
|
+
${identifier('updated_by', dialect)} ${textColumn(dialect, 36)},
|
|
45
|
+
primary key (${identifier('target_type', dialect)}, ${identifier('target_name', dialect)}, ${identifier('action', dialect)})
|
|
46
|
+
)`,
|
|
47
|
+
];
|
|
48
|
+
for (const statement of statements) {
|
|
49
|
+
await db.query(statement);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=role-permission-tables.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-permission-tables.js","sourceRoot":"","sources":["../../src/store/role-permission-tables.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,UAAU,EAAoB,GAAG,EAAE,MAAM,eAAe,CAAA;AACtF,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAEzE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,CAAC,MAAM,sBAAsB,GAAG,0BAA0B,CAAA;AAEhE,uEAAuE;AACvE,MAAM,kBAAkB,GAAG,EAAE,CAAA;AAC7B,mFAAmF;AACnF,MAAM,kBAAkB,GAAG,EAAE,CAAA;AAC7B,gFAAgF;AAChF,MAAM,aAAa,GAAG,EAAE,CAAA;AAExB,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,EAAkB;IAChE,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAA;IAC1B,MAAM,KAAK,GAAG,UAAU,CAAC,sBAAsB,EAAE,OAAO,CAAC,CAAA;IAEzD,MAAM,UAAU,GAAkB;QAChC,GAAG,CAAA,8BAA8B,KAAK;QAClC,UAAU,CAAC,aAAa,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,kBAAkB,CAAC;QAC7E,UAAU,CAAC,aAAa,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,kBAAkB,CAAC;QAC7E,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,aAAa,CAAC;QACnE,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC;QACzD,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,aAAa,CAAC,OAAO,CAAC;QACpD,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,eAAe,CAAC,OAAO,CAAC;QAC7D,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC;qBAC/C,UAAU,CAAC,aAAa,EAAE,OAAO,CAAC,KAAK,UAAU,CAAC,aAAa,EAAE,OAAO,CAAC,KAAK,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC;MAC1H;KACH,CAAA;IAED,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IAC3B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type DatabaseHandle } from '@cogenta/core';
|
|
2
|
+
/**
|
|
3
|
+
* "Contenu programmé dont la publication a échoué" — fiche 38 task 1's last
|
|
4
|
+
* named source. `registerScheduledPublishing`'s handler (wired by
|
|
5
|
+
* `cogenta serve`) can throw — the target collection's own validation refuses
|
|
6
|
+
* the entry, a `restrict` foreign key blocks it, a driver hiccups — and until
|
|
7
|
+
* now nothing surfaced that anywhere: the queue's own row silently sits in
|
|
8
|
+
* `status: 'failed'` after its retries are exhausted, and the only way to
|
|
9
|
+
* notice was to already suspect something and go look at the table by hand.
|
|
10
|
+
*
|
|
11
|
+
* Deliberately its own tiny table rather than a query against the queue
|
|
12
|
+
* driver's internal job table: the queue interface (`@cogenta/core`) has no
|
|
13
|
+
* "list jobs of this name past their retries" method, and reaching past it
|
|
14
|
+
* into one specific driver's schema would tie this notice to whichever queue
|
|
15
|
+
* driver a site happens to run — the opposite of R1. `record`/`clear` are
|
|
16
|
+
* called from the same handler that already knows collection/entry/locale,
|
|
17
|
+
* so nothing here has to guess.
|
|
18
|
+
*/
|
|
19
|
+
export declare const SCHEDULED_PUBLISH_FAILURES_TABLE = "cogenta_scheduled_publish_failures";
|
|
20
|
+
export interface ScheduledPublishFailure {
|
|
21
|
+
readonly collection: string;
|
|
22
|
+
readonly entryId: string;
|
|
23
|
+
readonly locale: string;
|
|
24
|
+
readonly error: string;
|
|
25
|
+
readonly failedAt: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ScheduledPublishFailureStore {
|
|
28
|
+
ensureTable(): Promise<void>;
|
|
29
|
+
/** Idempotent: a second failed attempt on the same entry replaces the first, it does not pile up. */
|
|
30
|
+
record(failure: Omit<ScheduledPublishFailure, 'failedAt'>): Promise<void>;
|
|
31
|
+
/** Called once a later attempt (or a fresh schedule) actually publishes the entry. */
|
|
32
|
+
clear(collection: string, entryId: string, locale: string): Promise<void>;
|
|
33
|
+
list(): Promise<readonly ScheduledPublishFailure[]>;
|
|
34
|
+
}
|
|
35
|
+
export declare function createScheduledPublishFailureStore(db: DatabaseHandle, now?: () => number): ScheduledPublishFailureStore;
|
|
36
|
+
//# sourceMappingURL=scheduled-publish-failures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduled-publish-failures.d.ts","sourceRoot":"","sources":["../../src/store/scheduled-publish-failures.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAA0B,MAAM,eAAe,CAAA;AAI3E;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,gCAAgC,uCAAuC,CAAA;AAEpF,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,4BAA4B;IAC3C,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5B,qGAAqG;IACrG,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,uBAAuB,EAAE,UAAU,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzE,sFAAsF;IACtF,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzE,IAAI,IAAI,OAAO,CAAC,SAAS,uBAAuB,EAAE,CAAC,CAAA;CACpD;AAUD,wBAAgB,kCAAkC,CAChD,EAAE,EAAE,cAAc,EAClB,GAAG,GAAE,MAAM,MAAiB,GAC3B,4BAA4B,CAgD9B"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { identifier, newId, sql } from '@cogenta/core';
|
|
2
|
+
import { textColumn, timestampColumn, uuidColumn } from './columns.js';
|
|
3
|
+
import { indexName } from './naming.js';
|
|
4
|
+
/**
|
|
5
|
+
* "Contenu programmé dont la publication a échoué" — fiche 38 task 1's last
|
|
6
|
+
* named source. `registerScheduledPublishing`'s handler (wired by
|
|
7
|
+
* `cogenta serve`) can throw — the target collection's own validation refuses
|
|
8
|
+
* the entry, a `restrict` foreign key blocks it, a driver hiccups — and until
|
|
9
|
+
* now nothing surfaced that anywhere: the queue's own row silently sits in
|
|
10
|
+
* `status: 'failed'` after its retries are exhausted, and the only way to
|
|
11
|
+
* notice was to already suspect something and go look at the table by hand.
|
|
12
|
+
*
|
|
13
|
+
* Deliberately its own tiny table rather than a query against the queue
|
|
14
|
+
* driver's internal job table: the queue interface (`@cogenta/core`) has no
|
|
15
|
+
* "list jobs of this name past their retries" method, and reaching past it
|
|
16
|
+
* into one specific driver's schema would tie this notice to whichever queue
|
|
17
|
+
* driver a site happens to run — the opposite of R1. `record`/`clear` are
|
|
18
|
+
* called from the same handler that already knows collection/entry/locale,
|
|
19
|
+
* so nothing here has to guess.
|
|
20
|
+
*/
|
|
21
|
+
export const SCHEDULED_PUBLISH_FAILURES_TABLE = 'cogenta_scheduled_publish_failures';
|
|
22
|
+
export function createScheduledPublishFailureStore(db, now = Date.now) {
|
|
23
|
+
const table = identifier(SCHEDULED_PUBLISH_FAILURES_TABLE, db.dialect);
|
|
24
|
+
const d = db.dialect;
|
|
25
|
+
return {
|
|
26
|
+
async ensureTable() {
|
|
27
|
+
await db.query(sql `
|
|
28
|
+
create table if not exists ${table} (
|
|
29
|
+
id ${uuidColumn(d)} not null primary key,
|
|
30
|
+
collection ${textColumn(d, 128)} not null,
|
|
31
|
+
entry_id ${uuidColumn(d)} not null,
|
|
32
|
+
locale ${textColumn(d, 16)} not null,
|
|
33
|
+
error ${textColumn(d, 2048)} not null,
|
|
34
|
+
failed_at ${timestampColumn(d)} not null
|
|
35
|
+
)`);
|
|
36
|
+
await db
|
|
37
|
+
.query(sql `
|
|
38
|
+
create unique index ${identifier(indexName(SCHEDULED_PUBLISH_FAILURES_TABLE, 'entry'), d)}
|
|
39
|
+
on ${table} (collection, entry_id, locale)`)
|
|
40
|
+
.catch(() => undefined); // already there — no portable "if not exists" for indexes
|
|
41
|
+
},
|
|
42
|
+
async record(failure) {
|
|
43
|
+
await db.query(sql `
|
|
44
|
+
delete from ${table}
|
|
45
|
+
where collection = ${failure.collection} and entry_id = ${failure.entryId} and locale = ${failure.locale}`);
|
|
46
|
+
await db.query(sql `
|
|
47
|
+
insert into ${table} (id, collection, entry_id, locale, error, failed_at)
|
|
48
|
+
values (${newId(now)}, ${failure.collection}, ${failure.entryId}, ${failure.locale}, ${failure.error}, ${new Date(now()).toISOString()})`);
|
|
49
|
+
},
|
|
50
|
+
async clear(collection, entryId, locale) {
|
|
51
|
+
await db.query(sql `
|
|
52
|
+
delete from ${table}
|
|
53
|
+
where collection = ${collection} and entry_id = ${entryId} and locale = ${locale}`);
|
|
54
|
+
},
|
|
55
|
+
async list() {
|
|
56
|
+
const result = await db.query(sql `select * from ${table} order by failed_at desc`);
|
|
57
|
+
return result.rows.map((row) => ({
|
|
58
|
+
collection: row.collection,
|
|
59
|
+
entryId: row.entry_id,
|
|
60
|
+
locale: row.locale,
|
|
61
|
+
error: row.error,
|
|
62
|
+
failedAt: row.failed_at,
|
|
63
|
+
}));
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=scheduled-publish-failures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduled-publish-failures.js","sourceRoot":"","sources":["../../src/store/scheduled-publish-failures.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AAC3E,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEvC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,oCAAoC,CAAA;AA2BpF,MAAM,UAAU,kCAAkC,CAChD,EAAkB,EAClB,GAAG,GAAiB,IAAI,CAAC,GAAG;IAE5B,MAAM,KAAK,GAAG,UAAU,CAAC,gCAAgC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAA;IACtE,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAA;IAEpB,OAAO;QACL,KAAK,CAAC,WAAW;YACf,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA;qCACa,KAAK;eAC3B,UAAU,CAAC,CAAC,CAAC;uBACL,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC;qBACpB,UAAU,CAAC,CAAC,CAAC;mBACf,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC;kBAClB,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC;sBACf,eAAe,CAAC,CAAC,CAAC;UAC9B,CAAC,CAAA;YACL,MAAM,EAAE;iBACL,KAAK,CAAC,GAAG,CAAA;gCACc,UAAU,CAAC,SAAS,CAAC,gCAAgC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;eACpF,KAAK,iCAAiC,CAAC;iBAC7C,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA,CAAC,0DAA0D;QACtF,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,OAAO;YAClB,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA;sBACF,KAAK;6BACE,OAAO,CAAC,UAAU,mBAAmB,OAAO,CAAC,OAAO,iBAAiB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;YAC7G,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA;sBACF,KAAK;kBACT,KAAK,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,UAAU,KAAK,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAA;QAC9I,CAAC;QAED,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM;YACrC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA;sBACF,KAAK;6BACE,UAAU,mBAAmB,OAAO,iBAAiB,MAAM,EAAE,CAAC,CAAA;QACvF,CAAC;QAED,KAAK,CAAC,IAAI;YACR,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAAa,GAAG,CAAA,iBAAiB,KAAK,0BAA0B,CAAC,CAAA;YAC9F,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;gBAC/B,UAAU,EAAE,GAAG,CAAC,UAAU;gBAC1B,OAAO,EAAE,GAAG,CAAC,QAAQ;gBACrB,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,QAAQ,EAAE,GAAG,CAAC,SAAS;aACxB,CAAC,CAAC,CAAA;QACL,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type Migration } from '@cogenta/core';
|
|
2
|
+
import type { CollectionDefinition } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The `schema@2.0 → 2.1` migration (ADR-0027).
|
|
5
|
+
*
|
|
6
|
+
* Adds the editorial workflow's two columns to every collection's entry
|
|
7
|
+
* table — `review_state` (`schema@2.1`'s `reviewState`, orthogonal to
|
|
8
|
+
* `status` exactly as `deleted_at` is) and `assigned_reviewer`. Both are
|
|
9
|
+
* additive and reversible:
|
|
10
|
+
*
|
|
11
|
+
* - `review_state` is `not null default 'none'`, so every existing row reads
|
|
12
|
+
* `'none'` the instant the column exists — a client that reads `status`
|
|
13
|
+
* and ignores everything else gets exactly the values it always did.
|
|
14
|
+
* - `assigned_reviewer` is nullable, defaulting to unassigned.
|
|
15
|
+
*
|
|
16
|
+
* **Not marked `destructive`.** Unlike `schema@1.0 → 2.0`, whose rollback
|
|
17
|
+
* discarded the trash and every classification, rolling this one back only
|
|
18
|
+
* drops two columns nothing yet depends on the day it ships — there is no
|
|
19
|
+
* site in production, so no real workflow state exists to lose. A future
|
|
20
|
+
* migration that adds this to a live site with real review history should
|
|
21
|
+
* reconsider that if the down path would then discard something real.
|
|
22
|
+
*/
|
|
23
|
+
export interface Schema21MigrationOptions {
|
|
24
|
+
readonly collections: readonly CollectionDefinition[];
|
|
25
|
+
/** Overridable so a site can slot this into its own numbering. */
|
|
26
|
+
readonly id?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare function schema21Migration(options: Schema21MigrationOptions): Migration;
|
|
29
|
+
//# sourceMappingURL=schema-2-1-migration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-2-1-migration.d.ts","sourceRoot":"","sources":["../../src/store/schema-2-1-migration.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,SAAS,EAKf,MAAM,eAAe,CAAA;AACtB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAIvD;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,WAAW,EAAE,SAAS,oBAAoB,EAAE,CAAA;IACrD,kEAAkE;IAClE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CACrB;AAwDD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,SAAS,CAsC9E"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { identifier, sql, unsafeRaw, } from '@cogenta/core';
|
|
2
|
+
import { textColumn } from './columns.js';
|
|
3
|
+
import { entriesTable, indexName } from './naming.js';
|
|
4
|
+
const DEFAULT_ID = '0003_schema_2_1_editorial_workflow';
|
|
5
|
+
function addReviewState(collection, dialect) {
|
|
6
|
+
const table = identifier(entriesTable(collection.name), dialect);
|
|
7
|
+
return sql `alter table ${table}
|
|
8
|
+
add column ${identifier('review_state', dialect)} ${textColumn(dialect, 24)}
|
|
9
|
+
not null default ${unsafeRaw("'none'")}`;
|
|
10
|
+
}
|
|
11
|
+
function addAssignedReviewer(collection, dialect) {
|
|
12
|
+
const table = identifier(entriesTable(collection.name), dialect);
|
|
13
|
+
return sql `alter table ${table}
|
|
14
|
+
add column ${identifier('assigned_reviewer', dialect)} ${textColumn(dialect, 64)}`;
|
|
15
|
+
}
|
|
16
|
+
function dropReviewState(collection, dialect) {
|
|
17
|
+
const table = identifier(entriesTable(collection.name), dialect);
|
|
18
|
+
return sql `alter table ${table} drop column ${identifier('review_state', dialect)}`;
|
|
19
|
+
}
|
|
20
|
+
function dropAssignedReviewer(collection, dialect) {
|
|
21
|
+
const table = identifier(entriesTable(collection.name), dialect);
|
|
22
|
+
return sql `alter table ${table} drop column ${identifier('assigned_reviewer', dialect)}`;
|
|
23
|
+
}
|
|
24
|
+
function reviewIndex(collection, dialect) {
|
|
25
|
+
const name = entriesTable(collection.name);
|
|
26
|
+
return sql `create index ${identifier(indexName(name, 'review'), dialect)}
|
|
27
|
+
on ${identifier(name, dialect)}
|
|
28
|
+
(${identifier('review_state', dialect)}, ${identifier('assigned_reviewer', dialect)})`;
|
|
29
|
+
}
|
|
30
|
+
/** MySQL needs the table named to drop an index; SQLite (and Postgres) name it directly. */
|
|
31
|
+
function dropReviewIndexOnTable(collection, dialect) {
|
|
32
|
+
return sql `drop index ${identifier(indexName(entriesTable(collection.name), 'review'), dialect)}
|
|
33
|
+
on ${identifier(entriesTable(collection.name), dialect)}`;
|
|
34
|
+
}
|
|
35
|
+
function dropReviewIndexStandalone(collection, dialect) {
|
|
36
|
+
return sql `drop index ${identifier(indexName(entriesTable(collection.name), 'review'), dialect)}`;
|
|
37
|
+
}
|
|
38
|
+
export function schema21Migration(options) {
|
|
39
|
+
const { collections } = options;
|
|
40
|
+
return {
|
|
41
|
+
id: options.id ?? DEFAULT_ID,
|
|
42
|
+
name: 'schema@2.1 — editorial workflow and owner permission',
|
|
43
|
+
destructive: false,
|
|
44
|
+
impact: "Adds review_state (not null, default 'none') and a nullable assigned_reviewer column " +
|
|
45
|
+
'to every collection. Moves no existing row and changes no existing value: a client ' +
|
|
46
|
+
'that reads status and ignores the rest sees exactly what it always did. The rollback ' +
|
|
47
|
+
'drops both columns, which is safe today because no site in production carries real ' +
|
|
48
|
+
'review history yet.',
|
|
49
|
+
estimatedDurationMs: 300 * Math.max(1, collections.length),
|
|
50
|
+
up: async (tx) => {
|
|
51
|
+
const dialect = tx.dialect;
|
|
52
|
+
for (const collection of collections) {
|
|
53
|
+
await tx.query(addReviewState(collection, dialect));
|
|
54
|
+
await tx.query(addAssignedReviewer(collection, dialect));
|
|
55
|
+
// `create index` has no portable `if not exists`; a failure here means
|
|
56
|
+
// it is already there, which is the state we wanted anyway.
|
|
57
|
+
await tx.query(reviewIndex(collection, dialect)).catch(() => undefined);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
down: async (tx) => {
|
|
61
|
+
const dialect = tx.dialect;
|
|
62
|
+
for (const collection of [...collections].reverse()) {
|
|
63
|
+
const drop = dialect === 'mysql' ? dropReviewIndexOnTable : dropReviewIndexStandalone;
|
|
64
|
+
await tx.query(drop(collection, dialect)).catch(() => undefined);
|
|
65
|
+
await tx.query(dropAssignedReviewer(collection, dialect));
|
|
66
|
+
await tx.query(dropReviewState(collection, dialect));
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=schema-2-1-migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-2-1-migration.js","sourceRoot":"","sources":["../../src/store/schema-2-1-migration.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EAIV,GAAG,EACH,SAAS,GACV,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AA6BrD,MAAM,UAAU,GAAG,oCAAoC,CAAA;AAEvD,SAAS,cAAc,CAAC,UAAgC,EAAE,OAAwB;IAChF,MAAM,KAAK,GAAG,UAAU,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAA;IAChE,OAAO,GAAG,CAAA,eAAe,KAAK;0BACN,UAAU,CAAC,cAAc,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC;gCACxD,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAA;AACrD,CAAC;AAED,SAAS,mBAAmB,CAC1B,UAAgC,EAChC,OAAwB;IAExB,MAAM,KAAK,GAAG,UAAU,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAA;IAChE,OAAO,GAAG,CAAA,eAAe,KAAK;0BACN,UAAU,CAAC,mBAAmB,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAA;AAC/F,CAAC;AAED,SAAS,eAAe,CAAC,UAAgC,EAAE,OAAwB;IACjF,MAAM,KAAK,GAAG,UAAU,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAA;IAChE,OAAO,GAAG,CAAA,eAAe,KAAK,gBAAgB,UAAU,CAAC,cAAc,EAAE,OAAO,CAAC,EAAE,CAAA;AACrF,CAAC;AAED,SAAS,oBAAoB,CAC3B,UAAgC,EAChC,OAAwB;IAExB,MAAM,KAAK,GAAG,UAAU,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAA;IAChE,OAAO,GAAG,CAAA,eAAe,KAAK,gBAAgB,UAAU,CAAC,mBAAmB,EAAE,OAAO,CAAC,EAAE,CAAA;AAC1F,CAAC;AAED,SAAS,WAAW,CAAC,UAAgC,EAAE,OAAwB;IAC7E,MAAM,IAAI,GAAG,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;IAC1C,OAAO,GAAG,CAAA,gBAAgB,UAAU,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;kBACxD,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC;gBAC3B,UAAU,CAAC,cAAc,EAAE,OAAO,CAAC,KAAK,UAAU,CAAC,mBAAmB,EAAE,OAAO,CAAC,GAAG,CAAA;AACnG,CAAC;AAED,4FAA4F;AAC5F,SAAS,sBAAsB,CAC7B,UAAgC,EAChC,OAAwB;IAExB,OAAO,GAAG,CAAA,cAAc,UAAU,CAAC,SAAS,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;kBAC/E,UAAU,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,EAAE,CAAA;AACtE,CAAC;AAED,SAAS,yBAAyB,CAChC,UAAgC,EAChC,OAAwB;IAExB,OAAO,GAAG,CAAA,cAAc,UAAU,CAAC,SAAS,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC,EAAE,CAAA;AACnG,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,OAAiC;IACjE,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAA;IAE/B,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,UAAU;QAC5B,IAAI,EAAE,sDAAsD;QAC5D,WAAW,EAAE,KAAK;QAClB,MAAM,EACJ,uFAAuF;YACvF,qFAAqF;YACrF,uFAAuF;YACvF,qFAAqF;YACrF,qBAAqB;QACvB,mBAAmB,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC;QAE1D,EAAE,EAAE,KAAK,EAAE,EAAe,EAAiB,EAAE;YAC3C,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAA;YAE1B,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;gBACrC,MAAM,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAA;gBACnD,MAAM,EAAE,CAAC,KAAK,CAAC,mBAAmB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAA;gBACxD,uEAAuE;gBACvE,4DAA4D;gBAC5D,MAAM,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;YACzE,CAAC;QACH,CAAC;QAED,IAAI,EAAE,KAAK,EAAE,EAAe,EAAiB,EAAE;YAC7C,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAA;YAE1B,KAAK,MAAM,UAAU,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;gBACpD,MAAM,IAAI,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,yBAAyB,CAAA;gBACrF,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;gBAChE,MAAM,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAA;gBACzD,MAAM,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAA;YACtD,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
|