@vicacha-devs/strapi-plugin-notes 0.0.0-stage → 1.1.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/LICENSE +22 -0
- package/README.md +20 -2
- package/dist/admin/NotesHomePage-CIWSOu3n.js +457 -0
- package/dist/admin/NotesHomePage-Da5Eu2o9.cjs +475 -0
- package/dist/admin/ar-NJ-XJhAU.cjs +64 -0
- package/dist/admin/ar-uSBvoP4-.js +64 -0
- package/dist/admin/de-DlFY4yCr.js +64 -0
- package/dist/admin/de-d-M42GQs.cjs +64 -0
- package/dist/admin/en-C8MGI5Fp.cjs +64 -0
- package/dist/admin/en-DQUtJt3K.js +64 -0
- package/dist/admin/es-BZY_kUPd.cjs +64 -0
- package/dist/admin/es-DcTVGahO.js +64 -0
- package/dist/admin/fr-D6Ism7Y3.cjs +64 -0
- package/dist/admin/fr-QW7vMjBs.js +64 -0
- package/dist/admin/index-BD0jLH1y.cjs +1458 -0
- package/dist/admin/index-ClQtejgN.js +1442 -0
- package/dist/admin/index.cjs +4 -0
- package/dist/admin/index.js +4 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/admin/it-DyBVYzjD.cjs +64 -0
- package/dist/admin/it-F24NTVne.js +64 -0
- package/dist/admin/pt-B4OG4ZiY.js +64 -0
- package/dist/admin/pt-BOHJueLP.cjs +64 -0
- package/dist/admin/ru-DGiT4YZI.js +64 -0
- package/dist/admin/ru-DhigZM8k.cjs +64 -0
- package/dist/admin/src/components/DeleteNoteDialog.d.ts +10 -0
- package/dist/admin/src/components/Initializer.d.ts +5 -0
- package/dist/admin/src/components/NoteListItem.d.ts +10 -0
- package/dist/admin/src/components/NoteModal.d.ts +18 -0
- package/dist/admin/src/components/NotesHubFilterBar.d.ts +14 -0
- package/dist/admin/src/components/NotesSidePanel.d.ts +13 -0
- package/dist/admin/src/components/PluginIcon.d.ts +2 -0
- package/dist/admin/src/hooks/useNotes.d.ts +12 -0
- package/dist/admin/src/index.d.ts +21 -0
- package/dist/admin/src/pages/NotesHomePage.d.ts +3 -0
- package/dist/admin/src/pluginId.d.ts +4 -0
- package/dist/admin/src/utils/getTrad.d.ts +2 -0
- package/dist/admin/src/utils/index.d.ts +1 -0
- package/dist/admin/tr-CNFZeutu.cjs +64 -0
- package/dist/admin/tr-CwzORNP2.js +64 -0
- package/dist/admin/zh-BriU4KiK.cjs +64 -0
- package/dist/admin/zh-C1EjzGrj.js +64 -0
- package/dist/package.json +3 -0
- package/dist/server/index.cjs +1212 -0
- package/dist/server/index.js +1212 -0
- package/dist/server/index.mjs +1212 -0
- package/dist/server/src/bootstrap.d.ts +5 -0
- package/dist/server/src/config/index.d.ts +8 -0
- package/dist/server/src/content-types/index.d.ts +70 -0
- package/dist/server/src/content-types/note/schema.json.d.ts +68 -0
- package/dist/server/src/controllers/index.d.ts +2 -0
- package/dist/server/src/controllers/notesController.d.ts +27 -0
- package/dist/server/src/destroy.d.ts +5 -0
- package/dist/server/src/index.d.ts +2 -0
- package/dist/server/src/register.d.ts +5 -0
- package/dist/server/src/routes/admin.d.ts +17 -0
- package/dist/server/src/routes/content-api.d.ts +12 -0
- package/dist/server/src/routes/index.d.ts +30 -0
- package/dist/server/src/services/index.d.ts +2 -0
- package/dist/server/src/services/notesQueryHelpers.d.ts +11 -0
- package/dist/server/src/services/notesService.d.ts +25 -0
- package/dist/shared/index.cjs +525 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +525 -0
- package/dist/shared/index.mjs +525 -0
- package/package.json +102 -3
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
const PLUGIN_ID = "notes";
|
|
2
|
+
const NOTE_CONTENT_TYPE_UID = "plugin::notes.note";
|
|
3
|
+
const NOTE_CATEGORIES = ["general", "todo", "blocker", "idea"];
|
|
4
|
+
const MAX_NOTE_TITLE_LENGTH = 255;
|
|
5
|
+
const MAX_NOTE_CONTENT_LENGTH = 2e4;
|
|
6
|
+
const MAX_ENTITY_SLUG_LENGTH = 160;
|
|
7
|
+
const MAX_ENTITY_ID_LENGTH = 128;
|
|
8
|
+
const MAX_LOCALE_LENGTH = 32;
|
|
9
|
+
const NOTE_CATEGORY_META = {
|
|
10
|
+
general: {
|
|
11
|
+
labelKey: "category.general",
|
|
12
|
+
defaultLabel: "General",
|
|
13
|
+
badgeBackground: "neutral150",
|
|
14
|
+
badgeTextColor: "neutral800"
|
|
15
|
+
},
|
|
16
|
+
todo: {
|
|
17
|
+
labelKey: "category.todo",
|
|
18
|
+
defaultLabel: "Todo",
|
|
19
|
+
badgeBackground: "primary100",
|
|
20
|
+
badgeTextColor: "primary700"
|
|
21
|
+
},
|
|
22
|
+
blocker: {
|
|
23
|
+
labelKey: "category.blocker",
|
|
24
|
+
defaultLabel: "Blocker",
|
|
25
|
+
badgeBackground: "danger100",
|
|
26
|
+
badgeTextColor: "danger700"
|
|
27
|
+
},
|
|
28
|
+
idea: {
|
|
29
|
+
labelKey: "category.idea",
|
|
30
|
+
defaultLabel: "Idea",
|
|
31
|
+
badgeBackground: "success100",
|
|
32
|
+
badgeTextColor: "success700"
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const ENTITY_SLUG_PATTERN = /^[a-zA-Z0-9_:.-]{1,160}$/;
|
|
36
|
+
const SAFE_ID_PATTERN = /^[a-zA-Z0-9_-]{1,128}$/;
|
|
37
|
+
const LOCALE_PATTERN = /^[a-zA-Z]{2,3}(?:-[a-zA-Z0-9]{2,8}){0,4}$/;
|
|
38
|
+
const FORBIDDEN_IDENTIFIER_TOKENS = /* @__PURE__ */ new Set([
|
|
39
|
+
"__proto__",
|
|
40
|
+
"constructor",
|
|
41
|
+
"prototype"
|
|
42
|
+
]);
|
|
43
|
+
function isForbiddenIdentifier(value) {
|
|
44
|
+
const lower = value.toLowerCase();
|
|
45
|
+
if (FORBIDDEN_IDENTIFIER_TOKENS.has(lower)) return true;
|
|
46
|
+
if (value === "." || value === ".." || value.includes("..") || value.startsWith(".") || value.endsWith(".")) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
return lower.split(/[:./]+/).some((segment) => FORBIDDEN_IDENTIFIER_TOKENS.has(segment));
|
|
50
|
+
}
|
|
51
|
+
function toPositiveSafeInt(input) {
|
|
52
|
+
if (typeof input !== "number" || !Number.isFinite(input)) return null;
|
|
53
|
+
const truncated = Math.trunc(input);
|
|
54
|
+
return Number.isSafeInteger(truncated) && truncated >= 1 ? truncated : null;
|
|
55
|
+
}
|
|
56
|
+
function isValidNoteCategory(value) {
|
|
57
|
+
return typeof value === "string" && NOTE_CATEGORIES.includes(value);
|
|
58
|
+
}
|
|
59
|
+
function normalizeNoteCategory(value) {
|
|
60
|
+
if (typeof value === "string") {
|
|
61
|
+
const normalized = value.trim().toLowerCase();
|
|
62
|
+
if (isValidNoteCategory(normalized)) return normalized;
|
|
63
|
+
}
|
|
64
|
+
return "general";
|
|
65
|
+
}
|
|
66
|
+
function sanitizePlainText(input, maxLength) {
|
|
67
|
+
if (typeof input !== "string") return "";
|
|
68
|
+
const cleaned = input.replace(/\0/g, "").replace(/\r\n/g, "\n").trim();
|
|
69
|
+
return cleaned.length > maxLength ? cleaned.slice(0, maxLength) : cleaned;
|
|
70
|
+
}
|
|
71
|
+
function sanitizeEntitySlug(input) {
|
|
72
|
+
if (typeof input !== "string" || input.includes("\0")) return "";
|
|
73
|
+
const cleaned = input.trim();
|
|
74
|
+
if (!cleaned || cleaned.length > MAX_ENTITY_SLUG_LENGTH || isForbiddenIdentifier(cleaned) || !ENTITY_SLUG_PATTERN.test(cleaned)) {
|
|
75
|
+
return "";
|
|
76
|
+
}
|
|
77
|
+
return cleaned;
|
|
78
|
+
}
|
|
79
|
+
function sanitizeEntityDocumentId(input) {
|
|
80
|
+
const numeric = toPositiveSafeInt(input);
|
|
81
|
+
if (numeric !== null) return String(numeric);
|
|
82
|
+
if (typeof input !== "string" || input.includes("\0")) return "";
|
|
83
|
+
const cleaned = input.trim();
|
|
84
|
+
if (!cleaned || cleaned.length > MAX_ENTITY_ID_LENGTH || isForbiddenIdentifier(cleaned) || !SAFE_ID_PATTERN.test(cleaned)) {
|
|
85
|
+
return "";
|
|
86
|
+
}
|
|
87
|
+
return cleaned;
|
|
88
|
+
}
|
|
89
|
+
function parseStringEntityId(input) {
|
|
90
|
+
if (input.includes("\0")) return null;
|
|
91
|
+
const trimmed = input.trim();
|
|
92
|
+
if (!trimmed || trimmed.length > MAX_ENTITY_ID_LENGTH || isForbiddenIdentifier(trimmed)) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
if (/^\d+$/.test(trimmed)) {
|
|
96
|
+
const parsed = Number.parseInt(trimmed, 10);
|
|
97
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
|
|
98
|
+
}
|
|
99
|
+
return SAFE_ID_PATTERN.test(trimmed) ? trimmed : null;
|
|
100
|
+
}
|
|
101
|
+
function sanitizeEntityId(input) {
|
|
102
|
+
const numeric = toPositiveSafeInt(input);
|
|
103
|
+
if (numeric !== null) {
|
|
104
|
+
return numeric;
|
|
105
|
+
}
|
|
106
|
+
if (typeof input === "string") {
|
|
107
|
+
return parseStringEntityId(input);
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
function isWildcardOrInvalidLocale(cleaned) {
|
|
112
|
+
if (!cleaned || cleaned === "all" || cleaned === "*") {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
return cleaned.length > MAX_LOCALE_LENGTH || isForbiddenIdentifier(cleaned);
|
|
116
|
+
}
|
|
117
|
+
function sanitizeEntityLocale(input) {
|
|
118
|
+
if (typeof input !== "string" || input.includes("\0")) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
const cleaned = input.trim();
|
|
122
|
+
if (isWildcardOrInvalidLocale(cleaned)) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
return LOCALE_PATTERN.test(cleaned) ? cleaned : null;
|
|
126
|
+
}
|
|
127
|
+
function extractPrimitiveAuthorId(rawUser) {
|
|
128
|
+
const numeric = toPositiveSafeInt(rawUser);
|
|
129
|
+
if (numeric !== null) {
|
|
130
|
+
return numeric;
|
|
131
|
+
}
|
|
132
|
+
if (typeof rawUser === "string" && rawUser.trim()) {
|
|
133
|
+
return sanitizeEntityDocumentId(rawUser) || null;
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
function sanitizePrimitiveAuthor(rawUser, fallback) {
|
|
138
|
+
const primitiveId = extractPrimitiveAuthorId(rawUser);
|
|
139
|
+
if (!fallback && primitiveId === null) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
id: primitiveId,
|
|
144
|
+
firstname: "",
|
|
145
|
+
lastname: "",
|
|
146
|
+
username: "",
|
|
147
|
+
displayName: fallback || `#${String(primitiveId)}`
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function asPlainObject(value, fallback) {
|
|
151
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : fallback;
|
|
152
|
+
}
|
|
153
|
+
function unwrapAuthorRecord(rawUser) {
|
|
154
|
+
const nestedData = asPlainObject(rawUser.data, rawUser);
|
|
155
|
+
const attrs = asPlainObject(nestedData.attributes, nestedData);
|
|
156
|
+
const id = extractPrimitiveAuthorId(nestedData.id ?? rawUser.id);
|
|
157
|
+
return { attrs, id };
|
|
158
|
+
}
|
|
159
|
+
function sanitizeNoteAuthor(rawUser, fallbackDisplayName) {
|
|
160
|
+
const fallback = sanitizePlainText(fallbackDisplayName, 120);
|
|
161
|
+
if (!rawUser || typeof rawUser !== "object" || Array.isArray(rawUser)) {
|
|
162
|
+
return sanitizePrimitiveAuthor(rawUser, fallback);
|
|
163
|
+
}
|
|
164
|
+
const { attrs, id } = unwrapAuthorRecord(rawUser);
|
|
165
|
+
const firstname = sanitizePlainText(attrs.firstname, 80);
|
|
166
|
+
const lastname = sanitizePlainText(attrs.lastname, 80);
|
|
167
|
+
const username = sanitizePlainText(attrs.username, 80);
|
|
168
|
+
const explicitDisplayName = sanitizePlainText(attrs.displayName, 120);
|
|
169
|
+
const fullName = [firstname, lastname].filter(Boolean).join(" ").trim();
|
|
170
|
+
const displayName = fullName || username || explicitDisplayName || fallback || "";
|
|
171
|
+
if (!displayName && id === null) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
id,
|
|
176
|
+
firstname,
|
|
177
|
+
lastname,
|
|
178
|
+
username,
|
|
179
|
+
displayName: displayName || `#${String(id)}`
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function formatNoteAuthorDisplay(author, emptyFallback = "-") {
|
|
183
|
+
if (!author) {
|
|
184
|
+
return emptyFallback;
|
|
185
|
+
}
|
|
186
|
+
const fullName = [author.firstname, author.lastname].filter(Boolean).join(" ").trim();
|
|
187
|
+
if (fullName) {
|
|
188
|
+
return fullName;
|
|
189
|
+
}
|
|
190
|
+
if (author.username && author.username.trim()) {
|
|
191
|
+
return author.username.trim();
|
|
192
|
+
}
|
|
193
|
+
if (author.displayName && author.displayName.trim()) {
|
|
194
|
+
return author.displayName.trim();
|
|
195
|
+
}
|
|
196
|
+
return emptyFallback;
|
|
197
|
+
}
|
|
198
|
+
function formatNoteTimestamp(isoString, locale = "en", emptyFallback = "-") {
|
|
199
|
+
if (!isoString || typeof isoString !== "string") {
|
|
200
|
+
return emptyFallback;
|
|
201
|
+
}
|
|
202
|
+
const date = new Date(isoString);
|
|
203
|
+
if (Number.isNaN(date.getTime())) {
|
|
204
|
+
return emptyFallback;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
return new Intl.DateTimeFormat(locale || "en", {
|
|
208
|
+
dateStyle: "medium",
|
|
209
|
+
timeStyle: "short"
|
|
210
|
+
}).format(date);
|
|
211
|
+
} catch {
|
|
212
|
+
return date.toISOString();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function extractRecordId(root, attrs) {
|
|
216
|
+
if (typeof root.id === "number" && Number.isFinite(root.id) || typeof root.id === "string") {
|
|
217
|
+
return root.id;
|
|
218
|
+
}
|
|
219
|
+
if (typeof attrs.id === "number" && Number.isFinite(attrs.id) || typeof attrs.id === "string") {
|
|
220
|
+
return attrs.id;
|
|
221
|
+
}
|
|
222
|
+
return "";
|
|
223
|
+
}
|
|
224
|
+
function sanitizeOptionalIsoString(value) {
|
|
225
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
226
|
+
}
|
|
227
|
+
function unwrapRawNoteAttributes(raw) {
|
|
228
|
+
const root = asPlainObject(raw, {});
|
|
229
|
+
const rawAttrs = asPlainObject(root.attributes, {});
|
|
230
|
+
const attrs = { ...root, ...rawAttrs };
|
|
231
|
+
return { root, attrs };
|
|
232
|
+
}
|
|
233
|
+
function normalizeNoteRecord(raw) {
|
|
234
|
+
const { root, attrs } = unwrapRawNoteAttributes(raw);
|
|
235
|
+
const id = extractRecordId(root, attrs);
|
|
236
|
+
const documentId = sanitizeEntityDocumentId(root.documentId ?? attrs.documentId) || String(id || "");
|
|
237
|
+
const entityId = sanitizeEntityId(attrs.entityId);
|
|
238
|
+
const entityDocumentId = sanitizeEntityDocumentId(attrs.entityDocumentId) || (entityId !== null ? String(entityId) : "");
|
|
239
|
+
const createdBy = sanitizeNoteAuthor(
|
|
240
|
+
attrs.createdBy,
|
|
241
|
+
attrs.createdByName ?? attrs.authorName
|
|
242
|
+
);
|
|
243
|
+
const updatedBy = sanitizeNoteAuthor(attrs.updatedBy, attrs.updatedByName) ?? createdBy;
|
|
244
|
+
const createdAt = sanitizeOptionalIsoString(attrs.createdAt);
|
|
245
|
+
const updatedAt = sanitizeOptionalIsoString(attrs.updatedAt) ?? createdAt;
|
|
246
|
+
const normalizedAttributes = {
|
|
247
|
+
title: sanitizePlainText(attrs.title, MAX_NOTE_TITLE_LENGTH),
|
|
248
|
+
content: sanitizePlainText(attrs.content, MAX_NOTE_CONTENT_LENGTH),
|
|
249
|
+
category: normalizeNoteCategory(attrs.category),
|
|
250
|
+
isPinned: Boolean(attrs.isPinned),
|
|
251
|
+
entitySlug: sanitizeEntitySlug(attrs.entitySlug),
|
|
252
|
+
entityId,
|
|
253
|
+
entityDocumentId,
|
|
254
|
+
entityLocale: sanitizeEntityLocale(attrs.entityLocale),
|
|
255
|
+
createdAt,
|
|
256
|
+
updatedAt,
|
|
257
|
+
createdBy,
|
|
258
|
+
updatedBy
|
|
259
|
+
};
|
|
260
|
+
return {
|
|
261
|
+
id,
|
|
262
|
+
documentId,
|
|
263
|
+
...normalizedAttributes,
|
|
264
|
+
attributes: normalizedAttributes
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function validateNoteCreatePayload(input) {
|
|
268
|
+
const raw = unwrapDataEnvelope(input);
|
|
269
|
+
const errors = [];
|
|
270
|
+
const title = sanitizePlainText(raw.title, MAX_NOTE_TITLE_LENGTH);
|
|
271
|
+
const content = sanitizePlainText(raw.content, MAX_NOTE_CONTENT_LENGTH);
|
|
272
|
+
const category = normalizeNoteCategory(raw.category);
|
|
273
|
+
const isPinned = Boolean(raw.isPinned);
|
|
274
|
+
const entitySlug = sanitizeEntitySlug(raw.entitySlug);
|
|
275
|
+
const entityId = sanitizeEntityId(raw.entityId);
|
|
276
|
+
const entityDocumentId = sanitizeEntityDocumentId(raw.entityDocumentId) || (entityId !== null ? String(entityId) : "");
|
|
277
|
+
const entityLocale = sanitizeEntityLocale(raw.entityLocale);
|
|
278
|
+
if (!title) {
|
|
279
|
+
errors.push("Note title is required.");
|
|
280
|
+
}
|
|
281
|
+
if (!entitySlug) {
|
|
282
|
+
errors.push("A valid target entitySlug is required.");
|
|
283
|
+
}
|
|
284
|
+
if (!entityDocumentId && entityId === null) {
|
|
285
|
+
errors.push("Either entityDocumentId or entityId is required.");
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
valid: errors.length === 0,
|
|
289
|
+
errors,
|
|
290
|
+
data: {
|
|
291
|
+
title,
|
|
292
|
+
content,
|
|
293
|
+
category,
|
|
294
|
+
isPinned,
|
|
295
|
+
entitySlug,
|
|
296
|
+
entityId,
|
|
297
|
+
entityDocumentId,
|
|
298
|
+
entityLocale
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function applyEntityTargetUpdates(raw, hasOwn, data, errors) {
|
|
303
|
+
if (hasOwn("entityDocumentId")) {
|
|
304
|
+
const docId = sanitizeEntityDocumentId(raw.entityDocumentId);
|
|
305
|
+
if (docId) data.entityDocumentId = docId;
|
|
306
|
+
}
|
|
307
|
+
if (hasOwn("entityId")) {
|
|
308
|
+
const entityId = sanitizeEntityId(raw.entityId);
|
|
309
|
+
if (entityId !== null) data.entityId = entityId;
|
|
310
|
+
}
|
|
311
|
+
if (hasOwn("entitySlug")) {
|
|
312
|
+
const slug = sanitizeEntitySlug(raw.entitySlug);
|
|
313
|
+
if (slug) {
|
|
314
|
+
data.entitySlug = slug;
|
|
315
|
+
} else if (raw.entitySlug !== void 0 && raw.entitySlug !== null) {
|
|
316
|
+
errors.push("A valid target entitySlug is required.");
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function validateNoteUpdatePayload(input) {
|
|
321
|
+
const raw = unwrapDataEnvelope(input);
|
|
322
|
+
const errors = [];
|
|
323
|
+
const data = {};
|
|
324
|
+
const hasOwn = (key) => Object.prototype.hasOwnProperty.call(raw, key);
|
|
325
|
+
if (hasOwn("title")) {
|
|
326
|
+
const title = sanitizePlainText(raw.title, MAX_NOTE_TITLE_LENGTH);
|
|
327
|
+
if (!title) {
|
|
328
|
+
errors.push("Note title cannot be empty.");
|
|
329
|
+
} else {
|
|
330
|
+
data.title = title;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (hasOwn("content")) {
|
|
334
|
+
data.content = sanitizePlainText(raw.content, MAX_NOTE_CONTENT_LENGTH);
|
|
335
|
+
}
|
|
336
|
+
if (hasOwn("category")) {
|
|
337
|
+
data.category = normalizeNoteCategory(raw.category);
|
|
338
|
+
}
|
|
339
|
+
if (hasOwn("isPinned")) {
|
|
340
|
+
data.isPinned = Boolean(raw.isPinned);
|
|
341
|
+
}
|
|
342
|
+
if (hasOwn("entityLocale")) {
|
|
343
|
+
data.entityLocale = sanitizeEntityLocale(raw.entityLocale);
|
|
344
|
+
}
|
|
345
|
+
applyEntityTargetUpdates(raw, hasOwn, data, errors);
|
|
346
|
+
if (Object.keys(data).length === 0 && errors.length === 0) {
|
|
347
|
+
errors.push("At least one valid note field must be provided for update.");
|
|
348
|
+
}
|
|
349
|
+
return { valid: errors.length === 0, errors, data };
|
|
350
|
+
}
|
|
351
|
+
function unwrapDataEnvelope(input) {
|
|
352
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
353
|
+
return {};
|
|
354
|
+
}
|
|
355
|
+
const obj = input;
|
|
356
|
+
if (obj.data && typeof obj.data === "object" && !Array.isArray(obj.data)) {
|
|
357
|
+
return obj.data;
|
|
358
|
+
}
|
|
359
|
+
if (obj.body && typeof obj.body === "object" && !Array.isArray(obj.body)) {
|
|
360
|
+
return obj.body;
|
|
361
|
+
}
|
|
362
|
+
return obj;
|
|
363
|
+
}
|
|
364
|
+
function sortNotesPinnedFirst(notes, direction = "DESC") {
|
|
365
|
+
return [...notes].sort((a, b) => {
|
|
366
|
+
if (a.isPinned !== b.isPinned) {
|
|
367
|
+
return a.isPinned ? -1 : 1;
|
|
368
|
+
}
|
|
369
|
+
const timeA = a.createdAt ? Date.parse(a.createdAt) : 0;
|
|
370
|
+
const timeB = b.createdAt ? Date.parse(b.createdAt) : 0;
|
|
371
|
+
if (timeA !== timeB && !Number.isNaN(timeA) && !Number.isNaN(timeB)) {
|
|
372
|
+
return direction === "ASC" ? timeA - timeB : timeB - timeA;
|
|
373
|
+
}
|
|
374
|
+
return String(a.id).localeCompare(String(b.id));
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
function matchesEntityIdentifier(note, docIdFilter, entityIdFilter) {
|
|
378
|
+
if (!docIdFilter && entityIdFilter === null) {
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
const noteIdStr = note.entityId !== null ? String(note.entityId) : "";
|
|
382
|
+
if (docIdFilter) {
|
|
383
|
+
if (note.entityDocumentId === docIdFilter || noteIdStr === docIdFilter) {
|
|
384
|
+
return true;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (entityIdFilter !== null) {
|
|
388
|
+
const filterStr = String(entityIdFilter);
|
|
389
|
+
if (noteIdStr === filterStr || note.entityDocumentId === filterStr) {
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
function matchesLocaleFilter(note, localeFilter, includeAllLocales) {
|
|
396
|
+
if (!localeFilter || includeAllLocales) {
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
return !note.entityLocale || note.entityLocale === localeFilter;
|
|
400
|
+
}
|
|
401
|
+
function matchesSearchFilter(note, searchFilter) {
|
|
402
|
+
if (!searchFilter) {
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
const haystack = [
|
|
406
|
+
note.title,
|
|
407
|
+
note.content,
|
|
408
|
+
note.entitySlug,
|
|
409
|
+
note.createdBy?.displayName ?? "",
|
|
410
|
+
note.updatedBy?.displayName ?? ""
|
|
411
|
+
].join(" ").toLowerCase();
|
|
412
|
+
return haystack.includes(searchFilter);
|
|
413
|
+
}
|
|
414
|
+
function matchesAllNoteFilters(note, filters) {
|
|
415
|
+
if (filters.slugFilter && note.entitySlug !== filters.slugFilter) {
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
if (!matchesEntityIdentifier(note, filters.docIdFilter, filters.entityIdFilter)) {
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
if (!matchesLocaleFilter(note, filters.localeFilter, filters.includeAllLocales)) {
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
if (filters.categoryFilter && note.category !== filters.categoryFilter) {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
if (typeof filters.isPinned === "boolean" && note.isPinned !== filters.isPinned) {
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
return matchesSearchFilter(note, filters.searchFilter);
|
|
431
|
+
}
|
|
432
|
+
function filterNotesList(notes, params = {}) {
|
|
433
|
+
const filters = {
|
|
434
|
+
slugFilter: params.entitySlug ? sanitizeEntitySlug(params.entitySlug) : "",
|
|
435
|
+
docIdFilter: params.entityDocumentId ? sanitizeEntityDocumentId(params.entityDocumentId) : "",
|
|
436
|
+
entityIdFilter: params.entityId !== void 0 && params.entityId !== null ? sanitizeEntityId(params.entityId) : null,
|
|
437
|
+
localeFilter: sanitizeEntityLocale(params.entityLocale),
|
|
438
|
+
includeAllLocales: params.includeAllLocales,
|
|
439
|
+
categoryFilter: params.category && params.category !== "all" ? normalizeNoteCategory(params.category) : null,
|
|
440
|
+
isPinned: params.isPinned,
|
|
441
|
+
searchFilter: sanitizePlainText(params.search, 160).toLowerCase()
|
|
442
|
+
};
|
|
443
|
+
const filtered = notes.filter((note) => matchesAllNoteFilters(note, filters));
|
|
444
|
+
const sorted = sortNotesPinnedFirst(filtered, params.sort ?? "DESC");
|
|
445
|
+
const offset = typeof params.offset === "number" && Number.isFinite(params.offset) && params.offset > 0 ? Math.trunc(params.offset) : 0;
|
|
446
|
+
const limit = typeof params.limit === "number" && Number.isFinite(params.limit) && params.limit > 0 ? Math.trunc(params.limit) : void 0;
|
|
447
|
+
if (offset > 0 || limit !== void 0) {
|
|
448
|
+
return sorted.slice(
|
|
449
|
+
offset,
|
|
450
|
+
limit !== void 0 ? offset + limit : void 0
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
return sorted;
|
|
454
|
+
}
|
|
455
|
+
function computeNotesSummaryStats(notes) {
|
|
456
|
+
const byCategory = {
|
|
457
|
+
general: 0,
|
|
458
|
+
todo: 0,
|
|
459
|
+
blocker: 0,
|
|
460
|
+
idea: 0
|
|
461
|
+
};
|
|
462
|
+
let pinned = 0;
|
|
463
|
+
const slugSet = /* @__PURE__ */ new Set();
|
|
464
|
+
for (const note of notes) {
|
|
465
|
+
if (note.isPinned) {
|
|
466
|
+
pinned += 1;
|
|
467
|
+
}
|
|
468
|
+
const cat = normalizeNoteCategory(note.category);
|
|
469
|
+
byCategory[cat] += 1;
|
|
470
|
+
if (note.entitySlug) {
|
|
471
|
+
slugSet.add(note.entitySlug);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return {
|
|
475
|
+
total: notes.length,
|
|
476
|
+
pinned,
|
|
477
|
+
byCategory,
|
|
478
|
+
entitySlugs: Array.from(slugSet).sort()
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
function buildContentManagerNoteLink(note) {
|
|
482
|
+
const slug = sanitizeEntitySlug(note.entitySlug);
|
|
483
|
+
const targetId = sanitizeEntityDocumentId(note.entityDocumentId) || (note.entityId !== null && note.entityId !== void 0 ? sanitizeEntityDocumentId(String(note.entityId)) : "");
|
|
484
|
+
const isSingleType = note.collectionType === "single-types" || note.collectionType === "singleType" || targetId === "single";
|
|
485
|
+
if (!slug || !isSingleType && !targetId) {
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
const basePath = isSingleType ? `/content-manager/single-types/${encodeURIComponent(slug)}` : `/content-manager/collection-types/${encodeURIComponent(
|
|
489
|
+
slug
|
|
490
|
+
)}/${encodeURIComponent(targetId)}`;
|
|
491
|
+
const locale = sanitizeEntityLocale(note.entityLocale);
|
|
492
|
+
if (locale) {
|
|
493
|
+
return `${basePath}?plugins[i18n][locale]=${encodeURIComponent(locale)}`;
|
|
494
|
+
}
|
|
495
|
+
return basePath;
|
|
496
|
+
}
|
|
497
|
+
export {
|
|
498
|
+
MAX_ENTITY_ID_LENGTH,
|
|
499
|
+
MAX_ENTITY_SLUG_LENGTH,
|
|
500
|
+
MAX_LOCALE_LENGTH,
|
|
501
|
+
MAX_NOTE_CONTENT_LENGTH,
|
|
502
|
+
MAX_NOTE_TITLE_LENGTH,
|
|
503
|
+
NOTE_CATEGORIES,
|
|
504
|
+
NOTE_CATEGORY_META,
|
|
505
|
+
NOTE_CONTENT_TYPE_UID,
|
|
506
|
+
PLUGIN_ID,
|
|
507
|
+
buildContentManagerNoteLink,
|
|
508
|
+
computeNotesSummaryStats,
|
|
509
|
+
filterNotesList,
|
|
510
|
+
formatNoteAuthorDisplay,
|
|
511
|
+
formatNoteTimestamp,
|
|
512
|
+
isValidNoteCategory,
|
|
513
|
+
normalizeNoteCategory,
|
|
514
|
+
normalizeNoteRecord,
|
|
515
|
+
sanitizeEntityDocumentId,
|
|
516
|
+
sanitizeEntityId,
|
|
517
|
+
sanitizeEntityLocale,
|
|
518
|
+
sanitizeEntitySlug,
|
|
519
|
+
sanitizeNoteAuthor,
|
|
520
|
+
sanitizePlainText,
|
|
521
|
+
sortNotesPinnedFirst,
|
|
522
|
+
unwrapDataEnvelope,
|
|
523
|
+
validateNoteCreatePayload,
|
|
524
|
+
validateNoteUpdatePayload
|
|
525
|
+
};
|