@aduptive/strapi-blockscene 2.0.0-alpha.7 → 2.0.0-alpha.8
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 +146 -21
- package/dist/admin.cjs +1803 -1155
- package/dist/admin.mjs +1766 -1118
- package/dist/server.cjs +145 -12
- package/package.json +1 -1
package/dist/server.cjs
CHANGED
|
@@ -106,7 +106,41 @@ var require_groups = __commonJS({
|
|
|
106
106
|
const [, end] = groupRange(rows, start, g);
|
|
107
107
|
return [...rows.slice(0, start), ...rows.slice(end + 1)];
|
|
108
108
|
}
|
|
109
|
-
|
|
109
|
+
var MAX_COLUMNS = 12;
|
|
110
|
+
var parseColumns = (value) => {
|
|
111
|
+
const n = typeof value === "number" ? value : typeof value === "string" && /^\s*\d{1,3}\s*$/.test(value) ? Number(value) : NaN;
|
|
112
|
+
return Number.isInteger(n) && n >= 1 ? n : null;
|
|
113
|
+
};
|
|
114
|
+
var columnsAttribute = (attr) => Boolean(attr) && typeof attr === "object" && !attr.private && (["integer", "biginteger", "float", "decimal", "string"].includes(attr.type) || attr.type === "enumeration" && Array.isArray(attr.enum) && attr.enum.length > 0 && attr.enum.every((value) => parseColumns(value) !== null));
|
|
115
|
+
var layoutFields2 = (schema) => Object.entries(schema?.attributes || {}).filter(([, attr]) => columnsAttribute(attr)).map(([name]) => name);
|
|
116
|
+
function validLayout(value, attributes) {
|
|
117
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
118
|
+
if (!Object.keys(value).every((key) => ["columnsField", "mobileColumnsField", "maxColumns"].includes(key))) return false;
|
|
119
|
+
const field = (name) => typeof name === "string" && /^[A-Za-z_][\w-]{0,63}$/.test(name) && (!attributes || columnsAttribute(attributes[name]));
|
|
120
|
+
return field(value.columnsField) && (value.mobileColumnsField === void 0 || field(value.mobileColumnsField)) && (value.maxColumns === void 0 || Number.isInteger(value.maxColumns) && value.maxColumns >= 1 && value.maxColumns <= MAX_COLUMNS);
|
|
121
|
+
}
|
|
122
|
+
function layoutColumns(row, layout, schema) {
|
|
123
|
+
const max = layout.maxColumns || MAX_COLUMNS;
|
|
124
|
+
const read = (name) => Math.min(max, parseColumns(row?.[name]) ?? parseColumns(schema?.attributes?.[name]?.default) ?? 1);
|
|
125
|
+
return { desktop: read(layout.columnsField), mobile: layout.mobileColumnsField ? read(layout.mobileColumnsField) : null };
|
|
126
|
+
}
|
|
127
|
+
module2.exports = {
|
|
128
|
+
safeGroups: safeGroups2,
|
|
129
|
+
validateGroups: validateGroups2,
|
|
130
|
+
groupRange,
|
|
131
|
+
groupRows,
|
|
132
|
+
topLevelRanges,
|
|
133
|
+
moveGroup,
|
|
134
|
+
removeGroup,
|
|
135
|
+
blockKey,
|
|
136
|
+
isOpener,
|
|
137
|
+
isClose,
|
|
138
|
+
MAX_COLUMNS,
|
|
139
|
+
parseColumns,
|
|
140
|
+
layoutFields: layoutFields2,
|
|
141
|
+
validLayout,
|
|
142
|
+
layoutColumns
|
|
143
|
+
};
|
|
110
144
|
}
|
|
111
145
|
});
|
|
112
146
|
|
|
@@ -237,7 +271,7 @@ var require_settings = __commonJS({
|
|
|
237
271
|
return [...facets];
|
|
238
272
|
}
|
|
239
273
|
var safeUrl2 = (value) => typeof value === "string" && (/^\/(?!\/)/.test(value) || /^https?:\/\//.test(value)) ? value : void 0;
|
|
240
|
-
var { safeGroups: safeGroups2 } = require_groups();
|
|
274
|
+
var { safeGroups: safeGroups2, validLayout } = require_groups();
|
|
241
275
|
var safeVersion = (value) => typeof value === "string" && /^[\w.-]{1,32}$/.test(value) ? value : void 0;
|
|
242
276
|
var fail2 = (message, details) => {
|
|
243
277
|
let ValidationError;
|
|
@@ -282,7 +316,7 @@ var require_settings = __commonJS({
|
|
|
282
316
|
groups: safeGroups2(config.groups, config.componentUids)
|
|
283
317
|
};
|
|
284
318
|
}
|
|
285
|
-
function validateSettings2(input, componentUids2, contentTypeUids2 = [], historyTypes = null) {
|
|
319
|
+
function validateSettings2(input, componentUids2, contentTypeUids2 = [], historyTypes = null, schemas = null) {
|
|
286
320
|
const types = typeMap(contentTypeUids2);
|
|
287
321
|
if (!input || typeof input !== "object" || Array.isArray(input)) fail2("Settings must be an object");
|
|
288
322
|
if (JSON.stringify(input).length > 64 * 1024) fail2("Settings payload too large");
|
|
@@ -318,6 +352,9 @@ var require_settings = __commonJS({
|
|
|
318
352
|
} else if (key === "mediaId") {
|
|
319
353
|
if (!Number.isInteger(entry.mediaId) || entry.mediaId <= 0) fail2(`Invalid media for "${uid}"`);
|
|
320
354
|
clean.mediaId = entry.mediaId;
|
|
355
|
+
} else if (key === "layout") {
|
|
356
|
+
if (!validLayout(entry.layout, schemas && (schemas[uid]?.attributes || {}))) fail2(`Invalid layout for "${uid}": columnsField (and mobileColumnsField) must name a number, string or numeric enumeration attribute of the component; maxColumns 1 to 12`);
|
|
357
|
+
clean.layout = structuredClone(entry.layout);
|
|
321
358
|
} else fail2(`Unknown component setting "${key}"`);
|
|
322
359
|
}
|
|
323
360
|
if (Object.keys(clean).length) out.components[uid] = clean;
|
|
@@ -343,7 +380,7 @@ var require_settings = __commonJS({
|
|
|
343
380
|
if (out.history.eventDays < out.history.retentionDays) fail2("history.eventDays must be at least history.retentionDays");
|
|
344
381
|
return out;
|
|
345
382
|
}
|
|
346
|
-
function mergeSaved2(saved, componentUids2, contentTypeUids2 = [], historyTypes = null) {
|
|
383
|
+
function mergeSaved2(saved, componentUids2, contentTypeUids2 = [], historyTypes = null, schemas = null) {
|
|
347
384
|
const types = typeMap(contentTypeUids2);
|
|
348
385
|
const out = structuredClone(DEFAULTS2);
|
|
349
386
|
if (!saved || typeof saved !== "object") return out;
|
|
@@ -373,6 +410,7 @@ var require_settings = __commonJS({
|
|
|
373
410
|
clean.tags = cleanTags(clean.tags);
|
|
374
411
|
if (!clean.tags.length) delete clean.tags;
|
|
375
412
|
}
|
|
413
|
+
if ("layout" in clean && !validLayout(clean.layout, schemas && (schemas[uid]?.attributes || {}))) delete clean.layout;
|
|
376
414
|
if (Object.keys(clean).length) out.components[uid] = clean;
|
|
377
415
|
}
|
|
378
416
|
for (const [uid, entry] of Object.entries(saved.contentTypes || {})) {
|
|
@@ -411,7 +449,7 @@ var require_settings = __commonJS({
|
|
|
411
449
|
var FIELD_TEXT = { label: 80, description: 300, placeholder: 120, help: 500 };
|
|
412
450
|
var LOCALE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
|
|
413
451
|
var plain = (value) => value && typeof value === "object" && !Array.isArray(value);
|
|
414
|
-
var fieldText = (value, max) => typeof value === "string" ? value.trim() !== "" && value.length <= max : plain(value) && Object.keys(value).length > 0 && Object.keys(value).length <= 30 && Object.entries(value).every(([locale,
|
|
452
|
+
var fieldText = (value, max) => typeof value === "string" ? value.trim() !== "" && value.length <= max : plain(value) && Object.keys(value).length > 0 && Object.keys(value).length <= 30 && Object.entries(value).every(([locale, text2]) => LOCALE.test(locale) && fieldText(text2, max));
|
|
415
453
|
function validateFields2(input, schemas = {}, stale = []) {
|
|
416
454
|
if (input === null || input === void 0) return {};
|
|
417
455
|
if (!plain(input)) fail2("fields must be an object");
|
|
@@ -435,6 +473,87 @@ var require_settings = __commonJS({
|
|
|
435
473
|
}
|
|
436
474
|
return out;
|
|
437
475
|
}
|
|
476
|
+
var VARIANT = { id: /^[a-z0-9][a-z0-9_-]{0,39}$/, perComponent: 12, bytes: 16 * 1024, total: 256 * 1024, items: 100 };
|
|
477
|
+
var text = (value) => typeof value === "string";
|
|
478
|
+
var finite = (value) => typeof value === "number" && Number.isFinite(value);
|
|
479
|
+
var SCALARS = {
|
|
480
|
+
string: text,
|
|
481
|
+
text,
|
|
482
|
+
richtext: text,
|
|
483
|
+
email: text,
|
|
484
|
+
uid: text,
|
|
485
|
+
date: text,
|
|
486
|
+
datetime: text,
|
|
487
|
+
time: text,
|
|
488
|
+
timestamp: text,
|
|
489
|
+
integer: Number.isInteger,
|
|
490
|
+
biginteger: (value) => Number.isInteger(value) || text(value) && /^-?\d{1,19}$/.test(value),
|
|
491
|
+
float: finite,
|
|
492
|
+
decimal: finite,
|
|
493
|
+
boolean: (value) => typeof value === "boolean",
|
|
494
|
+
json: () => true,
|
|
495
|
+
customField: () => true,
|
|
496
|
+
blocks: Array.isArray,
|
|
497
|
+
enumeration: (value, attr) => (attr.enum || []).includes(value)
|
|
498
|
+
};
|
|
499
|
+
function variantValues(values, schema, schemas, path, skipped) {
|
|
500
|
+
if (!plain(values)) throw new Error(`${path} must be an object`);
|
|
501
|
+
const out = {};
|
|
502
|
+
for (const [name, value] of Object.entries(values)) {
|
|
503
|
+
if (name === "id" || name === "__component") continue;
|
|
504
|
+
const attr = schema.attributes?.[name], at = `${path}.${name}`;
|
|
505
|
+
if (!attr) throw new Error(`${at} is not an attribute of the component`);
|
|
506
|
+
if (attr.type === "media" || attr.type === "relation") {
|
|
507
|
+
skipped.push(at);
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
if (attr.type === "component") {
|
|
511
|
+
const nested = schemas[attr.component], max = Math.min(attr.max ?? Infinity, VARIANT.items);
|
|
512
|
+
if (!nested) throw new Error(`${at}: unknown component "${attr.component}"`);
|
|
513
|
+
if (!attr.repeatable) out[name] = value === null ? null : variantValues(value, nested, schemas, at, skipped);
|
|
514
|
+
else if (!Array.isArray(value) || value.length > max) throw new Error(`${at} must be a list of at most ${max} items`);
|
|
515
|
+
else out[name] = value.map((item, index) => variantValues(item, nested, schemas, `${at}[${index}]`, skipped));
|
|
516
|
+
} else if (value === null || SCALARS[attr.type]?.(value, attr)) out[name] = structuredClone(value);
|
|
517
|
+
else throw new Error(`${at}: ${SCALARS[attr.type] ? `not a valid ${attr.type}` : `${attr.type} values are not supported`}`);
|
|
518
|
+
}
|
|
519
|
+
return out;
|
|
520
|
+
}
|
|
521
|
+
function validateVariants2(config, schemas = {}, notes = []) {
|
|
522
|
+
const out = {};
|
|
523
|
+
let total = 0;
|
|
524
|
+
for (const [uid, entry] of Object.entries(plain(config) ? config : {})) {
|
|
525
|
+
if (!plain(entry) || entry.variants === void 0) continue;
|
|
526
|
+
if (!schemas[uid]) {
|
|
527
|
+
notes.push(`${uid}: unknown component`);
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
if (!Array.isArray(entry.variants) || entry.variants.length > VARIANT.perComponent) {
|
|
531
|
+
notes.push(`${uid}: variants must be a list of at most ${VARIANT.perComponent}`);
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
const ids = /* @__PURE__ */ new Set();
|
|
535
|
+
entry.variants.forEach((variant, index) => {
|
|
536
|
+
const name = `${uid} variant "${variant?.id ?? index}"`;
|
|
537
|
+
try {
|
|
538
|
+
if (!plain(variant) || Object.keys(variant).some((key) => !["id", "label", "values"].includes(key))) throw new Error("expected { id, label, values }");
|
|
539
|
+
if (!text(variant.id) || !VARIANT.id.test(variant.id) || ids.has(variant.id)) throw new Error('id must be 1 to 40 lowercase letters, digits, "-" or "_", unique per component');
|
|
540
|
+
if (!fieldText(variant.label, 60)) throw new Error('label must be a string of 1 to 60 characters, or { "<locale>": string }');
|
|
541
|
+
const skipped = [];
|
|
542
|
+
const values = variantValues(variant.values ?? {}, schemas[uid], schemas, "values", skipped);
|
|
543
|
+
const size = JSON.stringify(values).length;
|
|
544
|
+
if (size > VARIANT.bytes) throw new Error(`values over ${VARIANT.bytes / 1024} KB`);
|
|
545
|
+
if (total + size > VARIANT.total) throw new Error(`all variants together over ${VARIANT.total / 1024} KB`);
|
|
546
|
+
if (skipped.length) notes.push(`${name}: media and relation values are not inserted (${skipped.slice(0, 5).join(", ")}${skipped.length > 5 ? ", ..." : ""})`);
|
|
547
|
+
ids.add(variant.id);
|
|
548
|
+
total += size;
|
|
549
|
+
(out[uid] ||= []).push({ id: variant.id, label: structuredClone(variant.label), values });
|
|
550
|
+
} catch (error) {
|
|
551
|
+
notes.push(`${name} ignored: ${error.message}`);
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
return out;
|
|
556
|
+
}
|
|
438
557
|
var PREFS = { starred: 200, recent: 20 };
|
|
439
558
|
function validatePrefs2(input, componentUids2) {
|
|
440
559
|
if (!input || typeof input !== "object" || Array.isArray(input)) fail2("Preferences must be an object");
|
|
@@ -447,7 +566,7 @@ var require_settings = __commonJS({
|
|
|
447
566
|
return out;
|
|
448
567
|
}
|
|
449
568
|
var mergePrefs2 = (saved, componentUids2) => Object.fromEntries(Object.entries(PREFS).map(([key, max]) => [key, Array.isArray(saved?.[key]) ? [...new Set(saved[key].filter((uid) => componentUids2.includes(uid)))].slice(0, max) : []]));
|
|
450
|
-
module2.exports = { PLUGIN: PLUGIN2, HIDDEN_MODES, TEMPLATES: TEMPLATES2, TYPOLOGIES: TYPOLOGIES2, guessTypology, facetsOf, validatePrefs: validatePrefs2, mergePrefs: mergePrefs2, ICONS, DEFAULTS: DEFAULTS2, catalog: catalog2, validateSettings: validateSettings2, validateFields: validateFields2, mergeSaved: mergeSaved2, layer: layer2, overrides: overrides2, safeUrl: safeUrl2, fail: fail2 };
|
|
569
|
+
module2.exports = { PLUGIN: PLUGIN2, HIDDEN_MODES, TEMPLATES: TEMPLATES2, TYPOLOGIES: TYPOLOGIES2, guessTypology, facetsOf, validatePrefs: validatePrefs2, mergePrefs: mergePrefs2, ICONS, DEFAULTS: DEFAULTS2, catalog: catalog2, validateSettings: validateSettings2, validateFields: validateFields2, validateVariants: validateVariants2, mergeSaved: mergeSaved2, layer: layer2, overrides: overrides2, safeUrl: safeUrl2, fail: fail2 };
|
|
451
570
|
}
|
|
452
571
|
});
|
|
453
572
|
|
|
@@ -1485,8 +1604,8 @@ var require_history = __commonJS({
|
|
|
1485
1604
|
});
|
|
1486
1605
|
|
|
1487
1606
|
// server/index.js
|
|
1488
|
-
var { PLUGIN, TEMPLATES, TYPOLOGIES, DEFAULTS, catalog, validateSettings, validateFields, mergeSaved, layer, overrides, validatePrefs, mergePrefs, safeUrl, fail } = require_settings();
|
|
1489
|
-
var { safeGroups, validateGroups } = require_groups();
|
|
1607
|
+
var { PLUGIN, TEMPLATES, TYPOLOGIES, DEFAULTS, catalog, validateSettings, validateFields, validateVariants, mergeSaved, layer, overrides, validatePrefs, mergePrefs, safeUrl, fail } = require_settings();
|
|
1608
|
+
var { safeGroups, validateGroups, layoutFields } = require_groups();
|
|
1490
1609
|
var { hiddenName, injectHidden, registerHiddenStrip } = require_hidden();
|
|
1491
1610
|
var { CRON, covers, eventContentType, historyService, historyController, registerHistory, registerPurge } = require_history();
|
|
1492
1611
|
var { trashContentType, trashService, trashController } = require_trash();
|
|
@@ -1639,6 +1758,7 @@ module.exports = {
|
|
|
1639
1758
|
]);
|
|
1640
1759
|
strapi.plugin(PLUGIN).service("settings").projectDefaults();
|
|
1641
1760
|
strapi.plugin(PLUGIN).service("settings").fields();
|
|
1761
|
+
strapi.plugin(PLUGIN).service("settings").variants();
|
|
1642
1762
|
registerPublishGuard(strapi);
|
|
1643
1763
|
registerHiddenStrip(strapi, hiddenName(strapi.plugin(PLUGIN).config("hiddenAttribute")), strapi.plugin(PLUGIN));
|
|
1644
1764
|
if (STRAPI5) {
|
|
@@ -1658,7 +1778,7 @@ module.exports = {
|
|
|
1658
1778
|
const code = strapi.plugin(PLUGIN).config("settings");
|
|
1659
1779
|
project = null;
|
|
1660
1780
|
if (code) try {
|
|
1661
|
-
project = validateSettings(code, componentUids(strapi), contentTypeUids(strapi), apiUids(strapi));
|
|
1781
|
+
project = validateSettings(code, componentUids(strapi), contentTypeUids(strapi), apiUids(strapi), strapi.components);
|
|
1662
1782
|
} catch (error) {
|
|
1663
1783
|
strapi.log?.warn(`[${PLUGIN}] "settings" config ignored: ${error.message}. Using the built-in defaults under the saved settings.`);
|
|
1664
1784
|
}
|
|
@@ -1678,13 +1798,22 @@ module.exports = {
|
|
|
1678
1798
|
if (stale.length) strapi.log?.warn(`[${PLUGIN}] "fields" entries for fields the schema no longer has were skipped: ${stale.slice(0, 20).join(", ")}${stale.length > 20 ? ` and ${stale.length - 20} more` : ""}.`);
|
|
1679
1799
|
return fields;
|
|
1680
1800
|
};
|
|
1681
|
-
|
|
1801
|
+
let variants;
|
|
1802
|
+
const insertVariants = () => {
|
|
1803
|
+
if (variants !== void 0) return variants;
|
|
1804
|
+
const notes = [];
|
|
1805
|
+
variants = validateVariants(strapi.plugin(PLUGIN).config("components"), strapi.components || {}, notes);
|
|
1806
|
+
for (const note of notes) strapi.log?.warn(`[${PLUGIN}] components variants: ${note}.`);
|
|
1807
|
+
return variants;
|
|
1808
|
+
};
|
|
1809
|
+
const get = async () => mergeSaved(layer(projectDefaults(), await readSettings(strapi)), componentUids(strapi), contentTypeUids(strapi), apiUids(strapi), strapi.components);
|
|
1682
1810
|
return {
|
|
1683
1811
|
projectDefaults,
|
|
1684
1812
|
get,
|
|
1685
1813
|
fields: fieldTexts,
|
|
1814
|
+
variants: insertVariants,
|
|
1686
1815
|
async set(value) {
|
|
1687
|
-
const next = validateSettings(value, componentUids(strapi), contentTypeUids(strapi), apiUids(strapi));
|
|
1816
|
+
const next = validateSettings(value, componentUids(strapi), contentTypeUids(strapi), apiUids(strapi), strapi.components);
|
|
1688
1817
|
for (const [uid, entry] of Object.entries(next.components)) {
|
|
1689
1818
|
if (entry.mediaId && !await findMedia(strapi, entry.mediaId).catch(() => null)) fail(`Media for "${uid}" does not exist`);
|
|
1690
1819
|
}
|
|
@@ -1715,6 +1844,7 @@ module.exports = {
|
|
|
1715
1844
|
});
|
|
1716
1845
|
const settings = await plugin.service("settings").get();
|
|
1717
1846
|
const media = await resolveMedia(strapi, settings);
|
|
1847
|
+
for (const [uid, list] of Object.entries(plugin.service("settings").variants())) base.components[uid] = { ...base.components[uid], variants: list };
|
|
1718
1848
|
for (const [uid, entry] of Object.entries(settings.components)) {
|
|
1719
1849
|
base.components[uid] = {
|
|
1720
1850
|
...base.components[uid],
|
|
@@ -1727,8 +1857,10 @@ module.exports = {
|
|
|
1727
1857
|
const hidden = hiddenName(plugin.config("hiddenAttribute")) || null;
|
|
1728
1858
|
const types = Object.fromEntries(Object.entries(strapi.contentTypes || {}).filter(([uid]) => !uid.startsWith("admin::")).map(([uid, schema]) => [uid, [schema.info?.displayName || "", ...Object.keys(schema.attributes || {})]]));
|
|
1729
1859
|
const history = STRAPI5 && strapi.documents?.use && !base.disabled && settings.history.enabled ? { contentTypes: apiUids(strapi).filter((uid) => covers(settings.history, uid)) } : null;
|
|
1860
|
+
const layouts = Object.fromEntries(Object.entries(settings.components).filter(([uid, entry]) => entry.layout && base.groups?.[uid]).map(([uid, entry]) => [uid, entry.layout]));
|
|
1730
1861
|
ctx.body = {
|
|
1731
1862
|
...base,
|
|
1863
|
+
layouts,
|
|
1732
1864
|
palette: settings.palette,
|
|
1733
1865
|
contentTypes: settings.contentTypes,
|
|
1734
1866
|
hiddenAttribute: hidden,
|
|
@@ -1747,7 +1879,8 @@ module.exports = {
|
|
|
1747
1879
|
uid,
|
|
1748
1880
|
displayName: schema.info?.displayName || uid,
|
|
1749
1881
|
category: schema.category || uid.split(".")[0],
|
|
1750
|
-
typology: auto[uid]?.typology
|
|
1882
|
+
typology: auto[uid]?.typology,
|
|
1883
|
+
layoutFields: layoutFields(schema)
|
|
1751
1884
|
}));
|
|
1752
1885
|
const contentTypes = Object.keys(contentTypeUids(strapi)).map((uid) => ({
|
|
1753
1886
|
uid,
|
package/package.json
CHANGED