@pramen/cms 0.0.61 → 0.0.64
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/README.md +34 -0
- package/dist/blockkit.d.ts +85 -13
- package/dist/blockkit.js +140 -11
- package/dist/index.d.ts +202 -12
- package/dist/index.js +415 -10
- package/dist/panel.d.ts +53 -0
- package/dist/panel.js +74 -0
- package/package.json +2 -2
- package/src/blockkit.ts +195 -25
- package/src/index.ts +445 -25
- package/src/panel.ts +105 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HandlerContext, Policy, FileRef, BootstrapFn, JsonValue, SchemaDef } from "@pramen/server";
|
|
1
|
+
import type { HandlerContext, Policy, FileRef, BootstrapFn, DataMigration, JsonValue, SchemaDef } from "@pramen/server";
|
|
2
2
|
import type { EnvBag } from "@pramen/server";
|
|
3
3
|
/** A field in a block type's (or content type's) field schema. Recursive: a `repeater`
|
|
4
4
|
* or `group` nests `fields`. Mirrors WollyCMS's FieldDefinition. */
|
|
@@ -887,6 +887,9 @@ export declare const cmsSchema: {
|
|
|
887
887
|
} & {
|
|
888
888
|
readonly default: false;
|
|
889
889
|
};
|
|
890
|
+
appliesTo: {
|
|
891
|
+
readonly type: "json";
|
|
892
|
+
};
|
|
890
893
|
createdAt: {
|
|
891
894
|
readonly type: "text";
|
|
892
895
|
} & {
|
|
@@ -953,6 +956,11 @@ export declare const cmsSchema: {
|
|
|
953
956
|
readonly target: "cms_page_terms";
|
|
954
957
|
readonly column: string;
|
|
955
958
|
};
|
|
959
|
+
media: {
|
|
960
|
+
readonly kind: "hasMany";
|
|
961
|
+
readonly target: "cms_media_terms";
|
|
962
|
+
readonly column: string;
|
|
963
|
+
};
|
|
956
964
|
}>;
|
|
957
965
|
cms_page_terms: import("@pramen/server").EntityDef<{
|
|
958
966
|
id: {
|
|
@@ -991,6 +999,43 @@ export declare const cmsSchema: {
|
|
|
991
999
|
readonly onDelete: import("@pramen/server").OnDelete | undefined;
|
|
992
1000
|
};
|
|
993
1001
|
}>;
|
|
1002
|
+
cms_media_terms: import("@pramen/server").EntityDef<{
|
|
1003
|
+
id: {
|
|
1004
|
+
readonly type: "uuid";
|
|
1005
|
+
} & {
|
|
1006
|
+
readonly generated: true;
|
|
1007
|
+
} & {
|
|
1008
|
+
readonly primaryKey: true;
|
|
1009
|
+
readonly notNull: true;
|
|
1010
|
+
};
|
|
1011
|
+
mediaId: {
|
|
1012
|
+
readonly type: "uuid";
|
|
1013
|
+
} & {
|
|
1014
|
+
readonly notNull: true;
|
|
1015
|
+
} & {
|
|
1016
|
+
readonly index: true;
|
|
1017
|
+
};
|
|
1018
|
+
termId: {
|
|
1019
|
+
readonly type: "uuid";
|
|
1020
|
+
} & {
|
|
1021
|
+
readonly notNull: true;
|
|
1022
|
+
} & {
|
|
1023
|
+
readonly index: true;
|
|
1024
|
+
};
|
|
1025
|
+
}, {
|
|
1026
|
+
media: {
|
|
1027
|
+
readonly kind: "belongsTo";
|
|
1028
|
+
readonly target: "cms_media";
|
|
1029
|
+
readonly column: string;
|
|
1030
|
+
readonly onDelete: import("@pramen/server").OnDelete | undefined;
|
|
1031
|
+
};
|
|
1032
|
+
term: {
|
|
1033
|
+
readonly kind: "belongsTo";
|
|
1034
|
+
readonly target: "cms_terms";
|
|
1035
|
+
readonly column: string;
|
|
1036
|
+
readonly onDelete: import("@pramen/server").OnDelete | undefined;
|
|
1037
|
+
};
|
|
1038
|
+
}>;
|
|
994
1039
|
cms_widget_areas: import("@pramen/server").EntityDef<{
|
|
995
1040
|
id: {
|
|
996
1041
|
readonly type: "uuid";
|
|
@@ -1046,6 +1091,19 @@ export declare const cmsSchema: {
|
|
|
1046
1091
|
file: {
|
|
1047
1092
|
readonly type: "fileRef";
|
|
1048
1093
|
};
|
|
1094
|
+
filename: {
|
|
1095
|
+
readonly type: "text";
|
|
1096
|
+
} & {
|
|
1097
|
+
readonly index: true;
|
|
1098
|
+
};
|
|
1099
|
+
contentType: {
|
|
1100
|
+
readonly type: "text";
|
|
1101
|
+
} & {
|
|
1102
|
+
readonly index: true;
|
|
1103
|
+
};
|
|
1104
|
+
size: {
|
|
1105
|
+
readonly type: "integer";
|
|
1106
|
+
};
|
|
1049
1107
|
alt: {
|
|
1050
1108
|
readonly type: "text";
|
|
1051
1109
|
};
|
|
@@ -1059,12 +1117,44 @@ export declare const cmsSchema: {
|
|
|
1059
1117
|
} & {
|
|
1060
1118
|
readonly defaultExpr: string;
|
|
1061
1119
|
};
|
|
1062
|
-
},
|
|
1120
|
+
}, {
|
|
1121
|
+
terms: {
|
|
1122
|
+
readonly kind: "manyToMany";
|
|
1123
|
+
readonly target: "cms_terms";
|
|
1124
|
+
readonly through: string;
|
|
1125
|
+
readonly sourceColumn: string;
|
|
1126
|
+
readonly targetColumn: string;
|
|
1127
|
+
};
|
|
1128
|
+
}>;
|
|
1063
1129
|
};
|
|
1130
|
+
/** How a media list may be ordered. */
|
|
1131
|
+
export type MediaSort = "newest" | "oldest" | "name" | "name_desc" | "largest" | "smallest";
|
|
1132
|
+
/** The coarse type buckets the library filters by — the first segment of a MIME type, which
|
|
1133
|
+
* is the distinction someone browsing a library actually makes ("show me the images"). */
|
|
1134
|
+
export declare const MEDIA_KINDS: readonly ["image", "video", "audio", "document", "other"];
|
|
1135
|
+
export type MediaKind = (typeof MEDIA_KINDS)[number];
|
|
1136
|
+
/** The object types a vocabulary can be applied to. A CLOSED vocabulary, like `MEDIA_KINDS`:
|
|
1137
|
+
* it decides which panels offer a taxonomy AND which assignments are accepted, so an unknown
|
|
1138
|
+
* value must not be storable. */
|
|
1139
|
+
export declare const TAXONOMY_TARGETS: readonly ["page", "media"];
|
|
1140
|
+
export type TaxonomyTarget = (typeof TAXONOMY_TARGETS)[number];
|
|
1141
|
+
/** Whether a vocabulary applies to `target`.
|
|
1142
|
+
*
|
|
1143
|
+
* The permissive readings all collapse to TRUE, deliberately: NULL (never narrowed, or written
|
|
1144
|
+
* before the column existed), a non-array, an array of things that are not targets. A stored
|
|
1145
|
+
* value nobody can interpret must not silently stop a vocabulary from working — the failure
|
|
1146
|
+
* would be a taxonomy that has quietly vanished from every panel, with the row still there. */
|
|
1147
|
+
export declare function taxonomyApplies(row: {
|
|
1148
|
+
appliesTo?: unknown;
|
|
1149
|
+
}, target: TaxonomyTarget): boolean;
|
|
1064
1150
|
/** Block Kit — custom admin pages, described as JSON and rendered by the editor. See
|
|
1065
1151
|
* `./blockkit`. Re-exported so a host imports `adminPage` beside `collection`. */
|
|
1066
|
-
export { adminPage, createAdminPageHandlers, normalizeAdminResponse, validateAdminPages, MAX_ADMIN_BLOCK_DEPTH, } from "./blockkit";
|
|
1067
|
-
export type { AdminBlock, AdminButton, AdminElement, AdminInput, AdminInteractionType, AdminPageDef, AdminPageHandlerOpts, AdminPageInteraction, AdminPageMeta, AdminPageResponse, AdminText, } from "./blockkit";
|
|
1152
|
+
export { adminPage, createAdminPageHandlers, normalizeAdminResponse, validateAdminPages, ADMIN_ELEMENT_TYPES, ADMIN_PAGE_KINDS, MAX_ADMIN_BLOCK_DEPTH, } from "./blockkit";
|
|
1153
|
+
export type { AdminBlock, AdminButton, AdminCell, AdminElement, AdminInput, AdminInteractionType, AdminPageDef, AdminPageHandlerOpts, AdminPageInteraction, AdminPageKind, AdminPageMeta, AdminPageResponse, AdminScreenDef, AdminText, } from "./blockkit";
|
|
1154
|
+
/** Custom admin PANELS — a project's own React screen inside the editor's chrome, for the
|
|
1155
|
+
* screens a server-driven vocabulary cannot carry. See `./panel`. */
|
|
1156
|
+
export { adminPanel, isAdminPanel } from "./panel";
|
|
1157
|
+
export type { AdminPanelDef } from "./panel";
|
|
1068
1158
|
/**
|
|
1069
1159
|
* Columns this package wrote in the pre-ISO space form that the SCHEMA cannot identify.
|
|
1070
1160
|
*
|
|
@@ -1478,28 +1568,48 @@ export declare function createCmsHandlers(opts?: CmsHandlerOpts): {
|
|
|
1478
1568
|
}, {
|
|
1479
1569
|
ok: true;
|
|
1480
1570
|
}>;
|
|
1481
|
-
/** Every vocabulary
|
|
1482
|
-
* is a URL segment) and a front end routes on
|
|
1483
|
-
|
|
1571
|
+
/** Every vocabulary, or just the ones that classify `target`. PUBLIC — like content
|
|
1572
|
+
* types, a taxonomy's slug is structural (it is a URL segment) and a front end routes on
|
|
1573
|
+
* it.
|
|
1574
|
+
*
|
|
1575
|
+
* Narrowed HERE rather than in each caller, so the page panel, the media panel and the
|
|
1576
|
+
* write-side guard cannot disagree about what a vocabulary applies to. In memory, because
|
|
1577
|
+
* `appliesTo` is a `t.json()` column that `where` cannot see into — which costs nothing:
|
|
1578
|
+
* this handler already reads every taxonomy, and nothing pages by them.
|
|
1579
|
+
*
|
|
1580
|
+
* No `target` means EVERY vocabulary, which is what the Taxonomies screen needs: the one
|
|
1581
|
+
* place that edits `appliesTo` must be able to see a vocabulary it has narrowed away. */
|
|
1582
|
+
listTaxonomies: import("@pramen/server").Handler<{
|
|
1583
|
+
target?: TaxonomyTarget;
|
|
1584
|
+
}, Record<string, unknown>[]>;
|
|
1484
1585
|
createTaxonomy: import("@pramen/server").Handler<{
|
|
1485
1586
|
slug: string;
|
|
1486
1587
|
label: string;
|
|
1487
1588
|
pluralLabel?: string;
|
|
1488
1589
|
description?: string;
|
|
1489
1590
|
hierarchical?: boolean;
|
|
1591
|
+
appliesTo?: TaxonomyTarget[] | null;
|
|
1490
1592
|
}, Record<string, unknown>>;
|
|
1491
1593
|
/** Patch a vocabulary. `slug` is a URL segment and the key `listTerms` resolves, so it
|
|
1492
1594
|
* is not mutable — the same rule content types and block types already follow.
|
|
1493
1595
|
*
|
|
1494
1596
|
* Turning `hierarchical` OFF is refused while any term still has a parent. Allowing it
|
|
1495
1597
|
* would leave a stored hierarchy that no reader renders and no writer can clear, and
|
|
1496
|
-
* flattening the terms silently is a destructive edit behind a checkbox.
|
|
1598
|
+
* flattening the terms silently is a destructive edit behind a checkbox.
|
|
1599
|
+
*
|
|
1600
|
+
* NARROWING `appliesTo` is refused on exactly the same grounds, and it is the same bug:
|
|
1601
|
+
* dropping a target this vocabulary is already used for would strand those assignments —
|
|
1602
|
+
* still stored, still returned by `listPageTerms`/`listMediaTerms`, but invisible in the
|
|
1603
|
+
* panel that could remove them, because the panel only renders vocabularies that apply.
|
|
1604
|
+
* Unassign them first; then the narrowing is a settings change rather than a silent
|
|
1605
|
+
* orphaning. WIDENING is always fine — it strands nothing. */
|
|
1497
1606
|
updateTaxonomy: import("@pramen/server").Handler<{
|
|
1498
1607
|
id: string;
|
|
1499
1608
|
label?: string;
|
|
1500
1609
|
pluralLabel?: string | null;
|
|
1501
1610
|
description?: string | null;
|
|
1502
1611
|
hierarchical?: boolean;
|
|
1612
|
+
appliesTo?: TaxonomyTarget[] | null;
|
|
1503
1613
|
}, Record<string, unknown> | undefined>;
|
|
1504
1614
|
/** Delete a vocabulary. Its terms go with it, and their page assignments with those —
|
|
1505
1615
|
* both by real `ON DELETE CASCADE`, so the cleanup is the DB's and cannot be half-done
|
|
@@ -1617,6 +1727,10 @@ export declare function createCmsHandlers(opts?: CmsHandlerOpts): {
|
|
|
1617
1727
|
listMedia: import("@pramen/server").Handler<{
|
|
1618
1728
|
limit?: number;
|
|
1619
1729
|
offset?: number;
|
|
1730
|
+
sort?: MediaSort;
|
|
1731
|
+
kind?: MediaKind;
|
|
1732
|
+
q?: string;
|
|
1733
|
+
term?: string;
|
|
1620
1734
|
}, Record<string, unknown>[]>;
|
|
1621
1735
|
getMedia: import("@pramen/server").Handler<{
|
|
1622
1736
|
id: string;
|
|
@@ -1626,6 +1740,25 @@ export declare function createCmsHandlers(opts?: CmsHandlerOpts): {
|
|
|
1626
1740
|
id: string;
|
|
1627
1741
|
alt: string | null;
|
|
1628
1742
|
}, Record<string, unknown>>;
|
|
1743
|
+
/** A media asset's assigned terms.
|
|
1744
|
+
*
|
|
1745
|
+
* The media row is read FIRST, through `ctx.db`, so the caller's own scope decides
|
|
1746
|
+
* whether this answers — the junction and `cms_terms` are granted unscoped, so without
|
|
1747
|
+
* it anyone holding an id could read a TRASHED file's tags and the non-empty answer
|
|
1748
|
+
* would confirm the file exists. Same rule as `listPageTerms`, for the same reason. */
|
|
1749
|
+
listMediaTerms: import("@pramen/server").Handler<{
|
|
1750
|
+
mediaId: string;
|
|
1751
|
+
}, Term[]>;
|
|
1752
|
+
/** Replace a media asset's term assignments wholesale — set semantics, like
|
|
1753
|
+
* `setPageTerms`, and for the same reason: the panel holds the whole selection, and two
|
|
1754
|
+
* calls each patching one end of it race into a state neither asked for. */
|
|
1755
|
+
setMediaTerms: import("@pramen/server").Handler<{
|
|
1756
|
+
mediaId: string;
|
|
1757
|
+
termIds: string[];
|
|
1758
|
+
}, {
|
|
1759
|
+
ok: true;
|
|
1760
|
+
count: number;
|
|
1761
|
+
}>;
|
|
1629
1762
|
/** Trash a media row. The R2 OBJECT IS KEPT — deleting the bytes here would make
|
|
1630
1763
|
* `restoreMedia` a lie, and a block still referencing the id would render a dead url
|
|
1631
1764
|
* with no way back. `purgeMedia` is what drops both — and `listTrash` is how you find
|
|
@@ -1787,6 +1920,7 @@ export declare function createCmsHandlers(opts?: CmsHandlerOpts): {
|
|
|
1787
1920
|
pagesByType: true;
|
|
1788
1921
|
siteFurniture: true;
|
|
1789
1922
|
codeDefinedTypes: true;
|
|
1923
|
+
mediaTerms: true;
|
|
1790
1924
|
canEdit: boolean;
|
|
1791
1925
|
}>;
|
|
1792
1926
|
/** Distinct locales present across all pages. NOTE: a DATA query — what is in the
|
|
@@ -2077,28 +2211,48 @@ export declare const cmsHandlers: {
|
|
|
2077
2211
|
}, {
|
|
2078
2212
|
ok: true;
|
|
2079
2213
|
}>;
|
|
2080
|
-
/** Every vocabulary
|
|
2081
|
-
* is a URL segment) and a front end routes on
|
|
2082
|
-
|
|
2214
|
+
/** Every vocabulary, or just the ones that classify `target`. PUBLIC — like content
|
|
2215
|
+
* types, a taxonomy's slug is structural (it is a URL segment) and a front end routes on
|
|
2216
|
+
* it.
|
|
2217
|
+
*
|
|
2218
|
+
* Narrowed HERE rather than in each caller, so the page panel, the media panel and the
|
|
2219
|
+
* write-side guard cannot disagree about what a vocabulary applies to. In memory, because
|
|
2220
|
+
* `appliesTo` is a `t.json()` column that `where` cannot see into — which costs nothing:
|
|
2221
|
+
* this handler already reads every taxonomy, and nothing pages by them.
|
|
2222
|
+
*
|
|
2223
|
+
* No `target` means EVERY vocabulary, which is what the Taxonomies screen needs: the one
|
|
2224
|
+
* place that edits `appliesTo` must be able to see a vocabulary it has narrowed away. */
|
|
2225
|
+
listTaxonomies: import("@pramen/server").Handler<{
|
|
2226
|
+
target?: TaxonomyTarget;
|
|
2227
|
+
}, Record<string, unknown>[]>;
|
|
2083
2228
|
createTaxonomy: import("@pramen/server").Handler<{
|
|
2084
2229
|
slug: string;
|
|
2085
2230
|
label: string;
|
|
2086
2231
|
pluralLabel?: string;
|
|
2087
2232
|
description?: string;
|
|
2088
2233
|
hierarchical?: boolean;
|
|
2234
|
+
appliesTo?: TaxonomyTarget[] | null;
|
|
2089
2235
|
}, Record<string, unknown>>;
|
|
2090
2236
|
/** Patch a vocabulary. `slug` is a URL segment and the key `listTerms` resolves, so it
|
|
2091
2237
|
* is not mutable — the same rule content types and block types already follow.
|
|
2092
2238
|
*
|
|
2093
2239
|
* Turning `hierarchical` OFF is refused while any term still has a parent. Allowing it
|
|
2094
2240
|
* would leave a stored hierarchy that no reader renders and no writer can clear, and
|
|
2095
|
-
* flattening the terms silently is a destructive edit behind a checkbox.
|
|
2241
|
+
* flattening the terms silently is a destructive edit behind a checkbox.
|
|
2242
|
+
*
|
|
2243
|
+
* NARROWING `appliesTo` is refused on exactly the same grounds, and it is the same bug:
|
|
2244
|
+
* dropping a target this vocabulary is already used for would strand those assignments —
|
|
2245
|
+
* still stored, still returned by `listPageTerms`/`listMediaTerms`, but invisible in the
|
|
2246
|
+
* panel that could remove them, because the panel only renders vocabularies that apply.
|
|
2247
|
+
* Unassign them first; then the narrowing is a settings change rather than a silent
|
|
2248
|
+
* orphaning. WIDENING is always fine — it strands nothing. */
|
|
2096
2249
|
updateTaxonomy: import("@pramen/server").Handler<{
|
|
2097
2250
|
id: string;
|
|
2098
2251
|
label?: string;
|
|
2099
2252
|
pluralLabel?: string | null;
|
|
2100
2253
|
description?: string | null;
|
|
2101
2254
|
hierarchical?: boolean;
|
|
2255
|
+
appliesTo?: TaxonomyTarget[] | null;
|
|
2102
2256
|
}, Record<string, unknown> | undefined>;
|
|
2103
2257
|
/** Delete a vocabulary. Its terms go with it, and their page assignments with those —
|
|
2104
2258
|
* both by real `ON DELETE CASCADE`, so the cleanup is the DB's and cannot be half-done
|
|
@@ -2216,6 +2370,10 @@ export declare const cmsHandlers: {
|
|
|
2216
2370
|
listMedia: import("@pramen/server").Handler<{
|
|
2217
2371
|
limit?: number;
|
|
2218
2372
|
offset?: number;
|
|
2373
|
+
sort?: MediaSort;
|
|
2374
|
+
kind?: MediaKind;
|
|
2375
|
+
q?: string;
|
|
2376
|
+
term?: string;
|
|
2219
2377
|
}, Record<string, unknown>[]>;
|
|
2220
2378
|
getMedia: import("@pramen/server").Handler<{
|
|
2221
2379
|
id: string;
|
|
@@ -2225,6 +2383,25 @@ export declare const cmsHandlers: {
|
|
|
2225
2383
|
id: string;
|
|
2226
2384
|
alt: string | null;
|
|
2227
2385
|
}, Record<string, unknown>>;
|
|
2386
|
+
/** A media asset's assigned terms.
|
|
2387
|
+
*
|
|
2388
|
+
* The media row is read FIRST, through `ctx.db`, so the caller's own scope decides
|
|
2389
|
+
* whether this answers — the junction and `cms_terms` are granted unscoped, so without
|
|
2390
|
+
* it anyone holding an id could read a TRASHED file's tags and the non-empty answer
|
|
2391
|
+
* would confirm the file exists. Same rule as `listPageTerms`, for the same reason. */
|
|
2392
|
+
listMediaTerms: import("@pramen/server").Handler<{
|
|
2393
|
+
mediaId: string;
|
|
2394
|
+
}, Term[]>;
|
|
2395
|
+
/** Replace a media asset's term assignments wholesale — set semantics, like
|
|
2396
|
+
* `setPageTerms`, and for the same reason: the panel holds the whole selection, and two
|
|
2397
|
+
* calls each patching one end of it race into a state neither asked for. */
|
|
2398
|
+
setMediaTerms: import("@pramen/server").Handler<{
|
|
2399
|
+
mediaId: string;
|
|
2400
|
+
termIds: string[];
|
|
2401
|
+
}, {
|
|
2402
|
+
ok: true;
|
|
2403
|
+
count: number;
|
|
2404
|
+
}>;
|
|
2228
2405
|
/** Trash a media row. The R2 OBJECT IS KEPT — deleting the bytes here would make
|
|
2229
2406
|
* `restoreMedia` a lie, and a block still referencing the id would render a dead url
|
|
2230
2407
|
* with no way back. `purgeMedia` is what drops both — and `listTrash` is how you find
|
|
@@ -2386,6 +2563,7 @@ export declare const cmsHandlers: {
|
|
|
2386
2563
|
pagesByType: true;
|
|
2387
2564
|
siteFurniture: true;
|
|
2388
2565
|
codeDefinedTypes: true;
|
|
2566
|
+
mediaTerms: true;
|
|
2389
2567
|
canEdit: boolean;
|
|
2390
2568
|
}>;
|
|
2391
2569
|
/** Distinct locales present across all pages. NOTE: a DATA query — what is in the
|
|
@@ -2916,6 +3094,18 @@ export declare function createCollectionTasks(collections: readonly CollectionDe
|
|
|
2916
3094
|
* between the revision insert and the page update leaves the page unpublished with an orphan
|
|
2917
3095
|
* revision until the next at-least-once redelivery re-runs (the token still matches, so it
|
|
2918
3096
|
* completes). Acceptable for a scheduled job; the interactive path is atomic. */
|
|
3097
|
+
/**
|
|
3098
|
+
* The CMS's own data migrations — spread into `app.migrations`.
|
|
3099
|
+
*
|
|
3100
|
+
* migrations: [...cmsMigrations]
|
|
3101
|
+
*
|
|
3102
|
+
* Opt-in like every other fragment this package ships (`cmsHandlers`, `cmsPolicies`,
|
|
3103
|
+
* `cmsTasks`), and with the same consequence for forgetting it: nothing breaks loudly. Media
|
|
3104
|
+
* rows written before the projection columns existed keep NULL `filename`/`contentType`, so
|
|
3105
|
+
* they sort together under a name sort and answer only the `other` type filter. New uploads
|
|
3106
|
+
* are unaffected — `createMedia` writes the columns itself.
|
|
3107
|
+
*/
|
|
3108
|
+
export declare const cmsMigrations: readonly DataMigration[];
|
|
2919
3109
|
export declare const cmsTasks: {
|
|
2920
3110
|
"cms:publish": (ctx: HandlerContext, payload: unknown) => Promise<void>;
|
|
2921
3111
|
"cms:unpublish": (ctx: HandlerContext, payload: unknown) => Promise<void>;
|