@leadcms/sdk 1.2.91-pre → 2.0.0-pre

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.
Files changed (56) hide show
  1. package/README.md +154 -7
  2. package/dist/cli/index.js +55 -62
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/index.d.ts +2 -2
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -18
  7. package/dist/index.js.map +1 -1
  8. package/dist/lib/cms.d.ts +24 -8
  9. package/dist/lib/cms.d.ts.map +1 -1
  10. package/dist/lib/cms.js +159 -90
  11. package/dist/lib/cms.js.map +1 -1
  12. package/dist/lib/config.d.ts +0 -8
  13. package/dist/lib/config.d.ts.map +1 -1
  14. package/dist/lib/config.js +13 -40
  15. package/dist/lib/config.js.map +1 -1
  16. package/dist/lib/console-colors.d.ts +49 -0
  17. package/dist/lib/console-colors.d.ts.map +1 -0
  18. package/dist/lib/console-colors.js +121 -0
  19. package/dist/lib/console-colors.js.map +1 -0
  20. package/dist/lib/content-transformation.d.ts +83 -0
  21. package/dist/lib/content-transformation.d.ts.map +1 -0
  22. package/dist/lib/content-transformation.js +305 -0
  23. package/dist/lib/content-transformation.js.map +1 -0
  24. package/dist/lib/data-service.d.ts +97 -0
  25. package/dist/lib/data-service.d.ts.map +1 -0
  26. package/dist/lib/data-service.js +389 -0
  27. package/dist/lib/data-service.js.map +1 -0
  28. package/dist/scripts/fetch-leadcms-content.d.ts +19 -0
  29. package/dist/scripts/fetch-leadcms-content.d.ts.map +1 -0
  30. package/dist/scripts/fetch-leadcms-content.js +301 -0
  31. package/dist/scripts/fetch-leadcms-content.js.map +1 -0
  32. package/dist/scripts/generate-env-js.d.ts +2 -0
  33. package/dist/scripts/generate-env-js.d.ts.map +1 -0
  34. package/dist/scripts/generate-env-js.js +22 -0
  35. package/dist/scripts/generate-env-js.js.map +1 -0
  36. package/dist/scripts/leadcms-helpers.d.ts +25 -0
  37. package/dist/scripts/leadcms-helpers.d.ts.map +1 -0
  38. package/dist/scripts/leadcms-helpers.js +78 -0
  39. package/dist/scripts/leadcms-helpers.js.map +1 -0
  40. package/dist/scripts/push-leadcms-content.d.ts +50 -0
  41. package/dist/scripts/push-leadcms-content.d.ts.map +1 -0
  42. package/dist/scripts/push-leadcms-content.js +1063 -0
  43. package/dist/scripts/push-leadcms-content.js.map +1 -0
  44. package/dist/scripts/sse-watcher.d.ts +20 -0
  45. package/dist/scripts/sse-watcher.d.ts.map +1 -0
  46. package/dist/scripts/sse-watcher.js +268 -0
  47. package/dist/scripts/sse-watcher.js.map +1 -0
  48. package/dist/scripts/status-leadcms-content.d.ts +4 -0
  49. package/dist/scripts/status-leadcms-content.d.ts.map +1 -0
  50. package/dist/scripts/status-leadcms-content.js +36 -0
  51. package/dist/scripts/status-leadcms-content.js.map +1 -0
  52. package/package.json +22 -13
  53. package/dist/scripts/fetch-leadcms-content.mjs +0 -367
  54. package/dist/scripts/generate-env-js.mjs +0 -24
  55. package/dist/scripts/leadcms-helpers.mjs +0 -208
  56. package/dist/scripts/sse-watcher.mjs +0 -300
package/dist/lib/cms.js CHANGED
@@ -1,33 +1,35 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getLeadCMSConfig = getLeadCMSConfig;
7
- exports.getAvailableLanguages = getAvailableLanguages;
8
- exports.getContentDirForLocale = getContentDirForLocale;
9
- exports.getAllContentSlugsForLocale = getAllContentSlugsForLocale;
10
- exports.getCMSContentBySlugForLocaleWithDraftSupport = getCMSContentBySlugForLocaleWithDraftSupport;
11
- exports.getCMSContentBySlugForLocale = getCMSContentBySlugForLocale;
12
- exports.getContentTranslations = getContentTranslations;
13
- exports.getAllContentRoutes = getAllContentRoutes;
14
- exports.extractUserUidFromSlug = extractUserUidFromSlug;
15
- exports.getAllContentSlugs = getAllContentSlugs;
16
- exports.getCMSContentBySlug = getCMSContentBySlug;
17
- exports.loadContentConfig = loadContentConfig;
18
- exports.getHeaderConfig = getHeaderConfig;
19
- exports.getFooterConfig = getFooterConfig;
20
- exports.getLocaleFromPath = getLocaleFromPath;
21
- exports.makeLocaleAwareLink = makeLocaleAwareLink;
22
- exports.loadContentConfigStrict = loadContentConfigStrict;
23
- const fs_1 = __importDefault(require("fs"));
24
- const path_1 = __importDefault(require("path"));
25
- const gray_matter_1 = __importDefault(require("gray-matter"));
26
- const config_1 = require("./config");
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import matter from "gray-matter";
4
+ import { getConfig } from "./config.js";
27
5
  // Default language - internal fallback only when configuration is not available
28
6
  const DEFAULT_LANGUAGE = "en";
29
7
  const contentCache = new Map();
30
8
  const CONTENT_CACHE_TTL = 30000; // 30 seconds cache TTL for content files
9
+ /**
10
+ * Check if content is a draft based on publishedAt field
11
+ * Content is considered draft if:
12
+ * - publishedAt is null/undefined
13
+ * - publishedAt is a future date (after current time)
14
+ *
15
+ * @param content - The content to check
16
+ * @returns true if content is draft, false otherwise
17
+ */
18
+ export function isContentDraft(content) {
19
+ if (!content.publishedAt) {
20
+ return true;
21
+ }
22
+ const publishedDate = content.publishedAt instanceof Date
23
+ ? content.publishedAt
24
+ : new Date(content.publishedAt);
25
+ const publishedTime = publishedDate.getTime();
26
+ // If the date is invalid (NaN), treat as draft
27
+ if (isNaN(publishedTime)) {
28
+ return true;
29
+ }
30
+ const now = Date.now();
31
+ return publishedTime > now;
32
+ }
31
33
  /**
32
34
  * Get LeadCMS configuration with fallbacks
33
35
  * This function attempts to load the LeadCMS configuration and falls back to environment variables
@@ -35,9 +37,9 @@ const CONTENT_CACHE_TTL = 30000; // 30 seconds cache TTL for content files
35
37
  *
36
38
  * @returns Complete LeadCMS configuration object with all required fields
37
39
  */
38
- function getLeadCMSConfig() {
40
+ export function getLeadCMSConfig() {
39
41
  try {
40
- return (0, config_1.getConfig)();
42
+ return getConfig();
41
43
  }
42
44
  catch (error) {
43
45
  // If config loading fails, return minimal config with environment fallbacks
@@ -59,7 +61,7 @@ function getAvailableLanguagesFromDir(contentDir) {
59
61
  const config = getLeadCMSConfig();
60
62
  const defaultLanguage = config.defaultLanguage || DEFAULT_LANGUAGE;
61
63
  try {
62
- const entries = fs_1.default.readdirSync(contentDir, { withFileTypes: true });
64
+ const entries = fs.readdirSync(contentDir, { withFileTypes: true });
63
65
  const languages = [defaultLanguage]; // Always include default language
64
66
  for (const entry of entries) {
65
67
  if (entry.isDirectory() && entry.name.length === 2) {
@@ -78,7 +80,7 @@ function getAvailableLanguagesFromDir(contentDir) {
78
80
  /**
79
81
  * Get all available languages from the content directory structure
80
82
  */
81
- function getAvailableLanguages() {
83
+ export function getAvailableLanguages() {
82
84
  const config = getLeadCMSConfig();
83
85
  const defaultLanguage = config.defaultLanguage || DEFAULT_LANGUAGE;
84
86
  const contentDir = config.contentDir;
@@ -90,14 +92,14 @@ function getAvailableLanguages() {
90
92
  /**
91
93
  * Get content directory for a specific locale
92
94
  */
93
- function getContentDirForLocale(contentDir, locale) {
95
+ export function getContentDirForLocale(contentDir, locale) {
94
96
  const config = getLeadCMSConfig();
95
97
  const defaultLanguage = config.defaultLanguage || DEFAULT_LANGUAGE;
96
98
  const actualLocale = locale || defaultLanguage;
97
99
  if (actualLocale === defaultLanguage) {
98
100
  return contentDir;
99
101
  }
100
- return path_1.default.join(contentDir, actualLocale);
102
+ return path.join(contentDir, actualLocale);
101
103
  }
102
104
  /**
103
105
  * Get all content slugs for a specific locale with draft filtering options (internal helper)
@@ -117,7 +119,7 @@ function getAllContentSlugsForLocaleFromDir(contentDir, locale, contentTypes, in
117
119
  slugs = getAllContentSlugsFromDir(localeContentDir, contentTypes);
118
120
  }
119
121
  // Apply draft filtering logic
120
- return applyDraftFiltering(slugs, includeDrafts, draftUserUid);
122
+ return applyDraftFiltering(slugs, includeDrafts, draftUserUid, contentDir, locale);
121
123
  }
122
124
  /**
123
125
  * Get all content slugs for a specific locale with draft filtering options
@@ -126,7 +128,7 @@ function getAllContentSlugsForLocaleFromDir(contentDir, locale, contentTypes, in
126
128
  * @param includeDrafts - Whether to include draft content (default: null = false)
127
129
  * @param draftUserUid - Specific user UID for draft content (only relevant if includeDrafts is true)
128
130
  */
129
- function getAllContentSlugsForLocale(locale, contentTypes, includeDrafts, draftUserUid) {
131
+ export function getAllContentSlugsForLocale(locale, contentTypes, includeDrafts, draftUserUid) {
130
132
  const config = getLeadCMSConfig();
131
133
  const contentDir = config.contentDir;
132
134
  if (!contentDir) {
@@ -140,19 +142,31 @@ function getAllContentSlugsForLocale(locale, contentTypes, includeDrafts, draftU
140
142
  * @param slugs - Array of all slugs
141
143
  * @param includeDrafts - Whether to include draft content (null/false = filter out drafts)
142
144
  * @param draftUserUid - Specific user UID for draft content (only relevant if includeDrafts is true)
145
+ * @param contentDir - Content directory to load content from for publishedAt checking
146
+ * @param locale - Locale for content loading
143
147
  */
144
- function applyDraftFiltering(slugs, includeDrafts, draftUserUid) {
148
+ function applyDraftFiltering(slugs, includeDrafts, draftUserUid, contentDir, locale) {
145
149
  // If includeDrafts is false or null, filter out all draft content
146
150
  if (!includeDrafts) {
147
- return filterOutDraftSlugs(slugs);
148
- }
149
- // If includeDrafts is true but no specific user UID, return all slugs as-is
150
- if (!draftUserUid) {
151
- return slugs;
151
+ let filteredSlugs = filterOutDraftSlugs(slugs);
152
+ // Also filter based on publishedAt if contentDir is available
153
+ if (contentDir) {
154
+ filteredSlugs = filteredSlugs.filter(slug => {
155
+ const localeContentDir = getContentDirForLocale(contentDir, locale);
156
+ const content = getCMSContentBySlugFromDir(slug, localeContentDir);
157
+ return content && !isContentDraft(content);
158
+ });
159
+ }
160
+ return filteredSlugs;
152
161
  }
153
162
  // If includeDrafts is true and draftUserUid is specified,
154
163
  // return base content with user's drafts overriding the originals
155
- return getBaseContentWithUserDraftOverrides(slugs, draftUserUid);
164
+ // User-specific drafts are always included regardless of publishedAt
165
+ if (draftUserUid) {
166
+ return getBaseContentWithUserDraftOverrides(slugs, draftUserUid);
167
+ }
168
+ // If includeDrafts is true but no specific user UID, return all slugs as-is
169
+ return slugs;
156
170
  }
157
171
  /**
158
172
  * Filter out draft slugs (those that have a corresponding base slug)
@@ -169,20 +183,20 @@ function filterOutDraftSlugs(slugs) {
169
183
  }
170
184
  /**
171
185
  * Get base content slugs with user-specific draft overrides
172
- * Returns only the user's draft versions when they exist, otherwise the base content
186
+ * Returns base slugs, but when content is fetched, user's version will be preferred
187
+ * Also includes user-only drafts (drafts that don't have a base version)
173
188
  */
174
189
  function getBaseContentWithUserDraftOverrides(slugs, draftUserUid) {
175
190
  // First, get all base slugs (non-draft)
176
191
  const baseSlugs = filterOutDraftSlugs(slugs);
177
- // For each base slug, check if user has a draft version and prefer it
178
- const result = [];
179
- for (const baseSlug of baseSlugs) {
180
- const userDraftSlug = `${baseSlug}-${draftUserUid}`;
181
- // If user has a draft version, use it; otherwise use the base version
182
- if (slugs.includes(userDraftSlug)) {
183
- result.push(userDraftSlug);
184
- }
185
- else {
192
+ const result = [...baseSlugs]; // Start with all base slugs
193
+ // Find user-only drafts (drafts that don't have a corresponding base slug)
194
+ const userDraftPattern = new RegExp(`-${draftUserUid}$`);
195
+ const userDrafts = slugs.filter(slug => userDraftPattern.test(slug));
196
+ for (const userDraft of userDrafts) {
197
+ const baseSlug = userDraft.replace(userDraftPattern, '');
198
+ // If this is a user-only draft (no base version exists), add the base slug
199
+ if (!baseSlugs.includes(baseSlug)) {
186
200
  result.push(baseSlug);
187
201
  }
188
202
  }
@@ -195,7 +209,7 @@ function getAllContentSlugsExcludingLanguageDirs(contentDir, contentTypes, rootC
195
209
  ? getAvailableLanguagesFromDir(rootContentDir)
196
210
  : [config.defaultLanguage];
197
211
  function walk(dir, prefix = "") {
198
- const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
212
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
199
213
  const slugs = [];
200
214
  for (const entry of entries) {
201
215
  if (entry.isDirectory()) {
@@ -206,7 +220,7 @@ function getAllContentSlugsExcludingLanguageDirs(contentDir, contentTypes, rootC
206
220
  entry.name !== defaultLanguage) {
207
221
  continue;
208
222
  }
209
- const subSlugs = walk(path_1.default.join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name);
223
+ const subSlugs = walk(path.join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name);
210
224
  slugs.push(...subSlugs);
211
225
  }
212
226
  else if (entry.isFile() && (entry.name.endsWith(".mdx") || entry.name.endsWith(".json"))) {
@@ -214,7 +228,7 @@ function getAllContentSlugsExcludingLanguageDirs(contentDir, contentTypes, rootC
214
228
  const slug = (prefix ? `${prefix}/` : "") + entry.name.replace(new RegExp(`\\${ext}$`), "");
215
229
  // If content types filter is provided, check the file's type
216
230
  if (contentTypes && contentTypes.length > 0) {
217
- const filePath = path_1.default.join(dir, entry.name);
231
+ const filePath = path.join(dir, entry.name);
218
232
  try {
219
233
  const fileType = getFileTypeOptimized(filePath, ext);
220
234
  // Only include if the type matches the filter
@@ -242,8 +256,9 @@ function getAllContentSlugsExcludingLanguageDirs(contentDir, contentTypes, rootC
242
256
  * @param slug - Content slug
243
257
  * @param locale - Locale code
244
258
  * @param userUid - Optional user UID for draft content
259
+ * @param includeDrafts - Whether to include content that is draft based on publishedAt (default: false)
245
260
  */
246
- function getCMSContentBySlugForLocaleWithDraftSupport(slug, locale, userUid) {
261
+ export function getCMSContentBySlugForLocaleWithDraftSupport(slug, locale, userUid, includeDrafts = false) {
247
262
  const config = getLeadCMSConfig();
248
263
  const contentDir = config.contentDir;
249
264
  if (!contentDir) {
@@ -255,11 +270,17 @@ function getCMSContentBySlugForLocaleWithDraftSupport(slug, locale, userUid) {
255
270
  const draftSlug = `${slug}-${userUid}`;
256
271
  const draftContent = getCMSContentBySlugForLocaleFromDir(draftSlug, contentDir, locale);
257
272
  if (draftContent) {
273
+ // Always return user-specific draft content when found, ignoring publishedAt logic
258
274
  return draftContent;
259
275
  }
260
276
  }
261
277
  // Fall back to regular content
262
- return getCMSContentBySlugForLocaleFromDir(slug, contentDir, locale);
278
+ const content = getCMSContentBySlugForLocaleFromDir(slug, contentDir, locale);
279
+ // Filter out drafts by default unless explicitly requested
280
+ if (content && !includeDrafts && isContentDraft(content)) {
281
+ return null;
282
+ }
283
+ return content;
263
284
  }
264
285
  /**
265
286
  * Get content by slug for a specific locale (internal helper)
@@ -277,19 +298,26 @@ function getCMSContentBySlugForLocaleFromDir(slug, contentDir, locale) {
277
298
  /**
278
299
  * Get content by slug for a specific locale
279
300
  */
280
- function getCMSContentBySlugForLocale(slug, locale) {
301
+ export function getCMSContentBySlugForLocale(slug, locale, includeDrafts = false) {
281
302
  const config = getLeadCMSConfig();
282
303
  const contentDir = config.contentDir;
283
304
  if (!contentDir) {
284
305
  console.warn('[LeadCMS] No contentDir configured. Please set up your LeadCMS configuration.');
285
306
  return null;
286
307
  }
287
- return getCMSContentBySlugForLocaleFromDir(slug, contentDir, locale);
308
+ const content = getCMSContentBySlugForLocaleFromDir(slug, contentDir, locale);
309
+ // Filter out drafts by default unless explicitly requested
310
+ if (content && !includeDrafts && isContentDraft(content)) {
311
+ return null;
312
+ }
313
+ return content;
288
314
  }
289
315
  /**
290
316
  * Get all translations of a content item by translationKey
317
+ * @param translationKey - The translation key to search for
318
+ * @param includeDrafts - Whether to include draft content (default: false)
291
319
  */
292
- function getContentTranslations(translationKey) {
320
+ export function getContentTranslations(translationKey, includeDrafts = false) {
293
321
  const config = getLeadCMSConfig();
294
322
  const contentDir = config.contentDir;
295
323
  if (!contentDir) {
@@ -302,10 +330,14 @@ function getContentTranslations(translationKey) {
302
330
  const localeContentDir = getContentDirForLocale(contentDir, locale);
303
331
  // Search for content with matching translationKey
304
332
  try {
305
- const slugs = getAllContentSlugsFromDir(localeContentDir);
333
+ const slugs = getAllContentSlugsForLocaleFromDir(contentDir, locale, undefined, includeDrafts);
306
334
  for (const slug of slugs) {
307
335
  const content = getCMSContentBySlugFromDir(slug, localeContentDir);
308
336
  if (content && content.translationKey === translationKey) {
337
+ // Filter out drafts by default unless explicitly requested
338
+ if (!includeDrafts && isContentDraft(content)) {
339
+ continue;
340
+ }
309
341
  content.language = content.language || locale;
310
342
  translations.push({ locale, content });
311
343
  break; // Found the translation for this locale
@@ -322,8 +354,11 @@ function getContentTranslations(translationKey) {
322
354
  /**
323
355
  * Get all content routes for all locales in a framework-agnostic format
324
356
  * Returns an array of route objects with locale and slug information
357
+ * @param contentTypes - Optional array of content types to filter
358
+ * @param includeDrafts - Whether to include draft content (default: false)
359
+ * @param draftUserUid - Specific user UID for draft content (only relevant if includeDrafts is true)
325
360
  */
326
- function getAllContentRoutes(contentTypes, includeDrafts, draftUserUid) {
361
+ export function getAllContentRoutes(contentTypes, includeDrafts = false, draftUserUid) {
327
362
  const config = getLeadCMSConfig();
328
363
  const contentDir = config.contentDir;
329
364
  const defaultLanguage = config.defaultLanguage;
@@ -355,28 +390,49 @@ function getAllContentRoutes(contentTypes, includeDrafts, draftUserUid) {
355
390
  * @param slug - The slug to check for userUid
356
391
  * @returns userUid if found, null otherwise
357
392
  */
358
- function extractUserUidFromSlug(slug) {
393
+ export function extractUserUidFromSlug(slug) {
359
394
  // GUID pattern: 8-4-4-4-12 hexadecimal characters with a preceding dash
360
395
  const draftSlugPattern = /-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i;
361
396
  const match = slug.match(draftSlugPattern);
362
397
  return match ? match[1] : null;
363
398
  }
364
- function getAllContentSlugs(contentTypes) {
399
+ export function getAllContentSlugs(contentTypes, includeDrafts = false) {
365
400
  const config = getLeadCMSConfig();
366
401
  const contentDir = config.contentDir;
367
402
  if (!contentDir) {
368
403
  console.warn('[LeadCMS] No contentDir configured. Please set up your LeadCMS configuration.');
369
404
  return [];
370
405
  }
371
- return getAllContentSlugsFromDir(contentDir, contentTypes);
406
+ const allSlugs = getAllContentSlugsFromDir(contentDir, contentTypes);
407
+ // Always filter out user-specific draft slugs (with GUID pattern) - these should never appear in general listings
408
+ let filteredSlugs = filterOutDraftSlugs(allSlugs);
409
+ // Filter out draft content based on publishedAt if includeDrafts is false
410
+ if (!includeDrafts) {
411
+ // Then filter based on publishedAt
412
+ return filteredSlugs.filter(slug => {
413
+ const content = getCMSContentBySlugFromDir(slug, contentDir);
414
+ return content && !isContentDraft(content);
415
+ });
416
+ }
417
+ return filteredSlugs;
372
418
  }
373
419
  function getAllContentSlugsFromDir(contentDir, contentTypes) {
374
420
  function walk(dir, prefix = "") {
375
- const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
421
+ let entries;
422
+ try {
423
+ entries = fs.readdirSync(dir, { withFileTypes: true });
424
+ }
425
+ catch (error) {
426
+ // If directory doesn't exist or can't be read, return empty array
427
+ if (error.code === 'ENOENT' || error.code === 'EACCES') {
428
+ return [];
429
+ }
430
+ throw error;
431
+ }
376
432
  const slugs = [];
377
433
  for (const entry of entries) {
378
434
  if (entry.isDirectory()) {
379
- const subSlugs = walk(path_1.default.join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name);
435
+ const subSlugs = walk(path.join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name);
380
436
  slugs.push(...subSlugs);
381
437
  }
382
438
  else if (entry.isFile() && (entry.name.endsWith(".mdx") || entry.name.endsWith(".json"))) {
@@ -384,7 +440,7 @@ function getAllContentSlugsFromDir(contentDir, contentTypes) {
384
440
  const slug = (prefix ? `${prefix}/` : "") + entry.name.replace(new RegExp(`\\${ext}$`), "");
385
441
  // If content types filter is provided, check the file's type
386
442
  if (contentTypes && contentTypes.length > 0) {
387
- const filePath = path_1.default.join(dir, entry.name);
443
+ const filePath = path.join(dir, entry.name);
388
444
  try {
389
445
  const fileType = getFileTypeOptimized(filePath, ext);
390
446
  // Only include if the type matches the filter
@@ -407,24 +463,33 @@ function getAllContentSlugsFromDir(contentDir, contentTypes) {
407
463
  }
408
464
  return walk(contentDir);
409
465
  }
410
- function getCMSContentBySlug(slug) {
466
+ export function getCMSContentBySlug(slug, includeDrafts = false) {
411
467
  const config = getLeadCMSConfig();
412
468
  const contentDir = config.contentDir;
413
469
  if (!contentDir) {
414
470
  console.warn('[LeadCMS] No contentDir configured. Please set up your LeadCMS configuration.');
415
471
  return null;
416
472
  }
417
- return getCMSContentBySlugFromDir(slug, contentDir);
473
+ const content = getCMSContentBySlugFromDir(slug, contentDir);
474
+ // Filter out drafts by default unless explicitly requested
475
+ if (content && !includeDrafts && isContentDraft(content)) {
476
+ return null;
477
+ }
478
+ return content;
418
479
  }
419
480
  function getCMSContentBySlugFromDir(slug, contentDir) {
420
481
  // Try both .mdx and .json extensions
421
- const mdxPath = path_1.default.join(contentDir, `${slug}.mdx`);
422
- const jsonPath = path_1.default.join(contentDir, `${slug}.json`);
482
+ const mdxPath = path.join(contentDir, `${slug}.mdx`);
483
+ const jsonPath = path.join(contentDir, `${slug}.json`);
423
484
  try {
424
485
  // Try MDX first - combine existence check with read operation
425
486
  try {
426
- const file = fs_1.default.readFileSync(mdxPath, "utf8");
427
- const { data, content } = (0, gray_matter_1.default)(file);
487
+ const file = fs.readFileSync(mdxPath, "utf8");
488
+ const { data, content } = matter(file);
489
+ // Convert publishedAt string to Date if present
490
+ if (data.publishedAt && typeof data.publishedAt === 'string') {
491
+ data.publishedAt = new Date(data.publishedAt);
492
+ }
428
493
  return {
429
494
  ...data,
430
495
  slug,
@@ -440,8 +505,12 @@ function getCMSContentBySlugFromDir(slug, contentDir) {
440
505
  }
441
506
  // Try JSON
442
507
  try {
443
- const file = fs_1.default.readFileSync(jsonPath, "utf8");
508
+ const file = fs.readFileSync(jsonPath, "utf8");
444
509
  const data = JSON.parse(file);
510
+ // Convert publishedAt string to Date if present
511
+ if (data.publishedAt && typeof data.publishedAt === 'string') {
512
+ data.publishedAt = new Date(data.publishedAt);
513
+ }
445
514
  return {
446
515
  ...data,
447
516
  slug,
@@ -480,7 +549,7 @@ function getFileTypeOptimized(filePath, ext) {
480
549
  */
481
550
  function extractTypeFromMDXFrontmatter(filePath) {
482
551
  try {
483
- const file = fs_1.default.readFileSync(filePath, "utf8");
552
+ const file = fs.readFileSync(filePath, "utf8");
484
553
  // Quick check if file starts with frontmatter
485
554
  if (!file.startsWith("---\n") && !file.startsWith("---\r\n")) {
486
555
  return undefined;
@@ -495,7 +564,7 @@ function extractTypeFromMDXFrontmatter(filePath) {
495
564
  }
496
565
  // Extract and parse only the frontmatter
497
566
  const frontmatterContent = file.slice(4, endIndex);
498
- const { data } = (0, gray_matter_1.default)(`---\n${frontmatterContent}\n---`);
567
+ const { data } = matter(`---\n${frontmatterContent}\n---`);
499
568
  return data.type;
500
569
  }
501
570
  catch {
@@ -508,7 +577,7 @@ function extractTypeFromMDXFrontmatter(filePath) {
508
577
  */
509
578
  function extractTypeFromJSON(filePath) {
510
579
  try {
511
- const file = fs_1.default.readFileSync(filePath, "utf8");
580
+ const file = fs.readFileSync(filePath, "utf8");
512
581
  // For small files, just parse normally
513
582
  if (file.length < 100) {
514
583
  const data = JSON.parse(file);
@@ -543,7 +612,7 @@ function loadConfigWithDraftSupport(contentDir, locale, configName, userUid) {
543
612
  let cacheKey;
544
613
  // If userUid is provided, try draft version first
545
614
  if (userUid) {
546
- const draftConfigPath = path_1.default.join(localeContentDir, `${configName}-${userUid}.json`);
615
+ const draftConfigPath = path.join(localeContentDir, `${configName}-${userUid}.json`);
547
616
  cacheKey = `${draftConfigPath}:${locale}:${configName}:${userUid}`;
548
617
  // Check cache first for draft
549
618
  const cached = contentCache.get(cacheKey);
@@ -551,8 +620,8 @@ function loadConfigWithDraftSupport(contentDir, locale, configName, userUid) {
551
620
  if (cached && (now - cached.timestamp) < CONTENT_CACHE_TTL) {
552
621
  return cached.content;
553
622
  }
554
- if (fs_1.default.existsSync(draftConfigPath)) {
555
- const draftConfigContent = fs_1.default.readFileSync(draftConfigPath, "utf-8");
623
+ if (fs.existsSync(draftConfigPath)) {
624
+ const draftConfigContent = fs.readFileSync(draftConfigPath, "utf-8");
556
625
  const parsed = JSON.parse(draftConfigContent);
557
626
  // Cache the result
558
627
  contentCache.set(cacheKey, {
@@ -564,7 +633,7 @@ function loadConfigWithDraftSupport(contentDir, locale, configName, userUid) {
564
633
  }
565
634
  }
566
635
  // Fall back to regular config file
567
- const configPath = path_1.default.join(localeContentDir, `${configName}.json`);
636
+ const configPath = path.join(localeContentDir, `${configName}.json`);
568
637
  cacheKey = `${configPath}:${locale}:${configName}`;
569
638
  // Check cache for regular config
570
639
  const cached = contentCache.get(cacheKey);
@@ -572,7 +641,7 @@ function loadConfigWithDraftSupport(contentDir, locale, configName, userUid) {
572
641
  if (cached && (now - cached.timestamp) < CONTENT_CACHE_TTL) {
573
642
  return cached.content;
574
643
  }
575
- if (!fs_1.default.existsSync(configPath)) {
644
+ if (!fs.existsSync(configPath)) {
576
645
  // Cache the null result too to avoid repeated file system checks
577
646
  contentCache.set(cacheKey, {
578
647
  content: null,
@@ -587,7 +656,7 @@ function loadConfigWithDraftSupport(contentDir, locale, configName, userUid) {
587
656
  error.filePath = configPath;
588
657
  throw error;
589
658
  }
590
- const configContent = fs_1.default.readFileSync(configPath, "utf-8");
659
+ const configContent = fs.readFileSync(configPath, "utf-8");
591
660
  const parsed = JSON.parse(configContent);
592
661
  // Cache the result
593
662
  contentCache.set(cacheKey, {
@@ -602,7 +671,7 @@ function loadConfigWithDraftSupport(contentDir, locale, configName, userUid) {
602
671
  if (error instanceof Error && error.name === 'MissingConfigurationFile') {
603
672
  console.error(`[LeadCMS] ${error.message}`);
604
673
  // Re-throw with more context for debugging
605
- const detailedError = new Error(`Missing configuration files - configName: '${configName}', locale: '${locale}', expected path: ${path_1.default.join(getContentDirForLocale(contentDir, locale), `${configName}.json`)}`);
674
+ const detailedError = new Error(`Missing configuration files - configName: '${configName}', locale: '${locale}', expected path: ${path.join(getContentDirForLocale(contentDir, locale), `${configName}.json`)}`);
606
675
  detailedError.name = 'MissingConfigurationFile';
607
676
  detailedError.configName = configName;
608
677
  detailedError.locale = locale;
@@ -627,7 +696,7 @@ function loadConfigWithDraftSupport(contentDir, locale, configName, userUid) {
627
696
  * @param userUid - Optional user UID for draft content
628
697
  * @returns Parsed config object or null if not found
629
698
  */
630
- function loadContentConfig(configName, locale, userUid) {
699
+ export function loadContentConfig(configName, locale, userUid) {
631
700
  const config = getLeadCMSConfig();
632
701
  const actualLocale = locale || config.defaultLanguage;
633
702
  const contentDir = config.contentDir;
@@ -697,7 +766,7 @@ function getFooterConfigInternal(contentDir, locale, userUid) {
697
766
  * @param locale - Locale code (optional, uses default language from config if not provided)
698
767
  * @param userUid - Optional user UID for draft content
699
768
  */
700
- function getHeaderConfig(locale, userUid) {
769
+ export function getHeaderConfig(locale, userUid) {
701
770
  return loadContentConfig('header', locale, userUid);
702
771
  }
703
772
  /**
@@ -705,7 +774,7 @@ function getHeaderConfig(locale, userUid) {
705
774
  * @param locale - Locale code (optional, uses default language from config if not provided)
706
775
  * @param userUid - Optional user UID for draft content
707
776
  */
708
- function getFooterConfig(locale, userUid) {
777
+ export function getFooterConfig(locale, userUid) {
709
778
  return loadContentConfig('footer', locale, userUid);
710
779
  }
711
780
  /**
@@ -713,7 +782,7 @@ function getFooterConfig(locale, userUid) {
713
782
  * @param pathname - The pathname to extract locale from
714
783
  * @param contentDir - Content directory path (optional, uses configured contentDir if not provided)
715
784
  */
716
- function getLocaleFromPath(pathname) {
785
+ export function getLocaleFromPath(pathname) {
717
786
  const config = getLeadCMSConfig();
718
787
  const contentDir = config.contentDir;
719
788
  const defaultLanguage = config.defaultLanguage;
@@ -752,7 +821,7 @@ function getLocaleFromPath(pathname) {
752
821
  * @param href - The href to make locale-aware
753
822
  * @param currentLocale - The current locale
754
823
  */
755
- function makeLocaleAwareLink(href, currentLocale) {
824
+ export function makeLocaleAwareLink(href, currentLocale) {
756
825
  // Don't modify external links or anchors
757
826
  if (href.startsWith("http") || href.startsWith("#")) {
758
827
  return href;
@@ -782,7 +851,7 @@ function makeLocaleAwareLink(href, currentLocale) {
782
851
  * @returns Parsed config object (never returns null)
783
852
  * @throws {Error} With detailed information about missing files including configName, locale, and expected path
784
853
  */
785
- function loadContentConfigStrict(configName, locale, userUid) {
854
+ export function loadContentConfigStrict(configName, locale, userUid) {
786
855
  const config = getLeadCMSConfig();
787
856
  const actualLocale = locale || config.defaultLanguage;
788
857
  const contentDir = config.contentDir;