🚀. Socket Launch Week Day 2:Introducing Manifest Alerts.Learn more
Sign In

@deepsel/cms-utils

Package Overview
Dependencies
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@deepsel/cms-utils - npm Package Compare versions

Comparing version
1.0.0
to
1.1.0
+3
dist/menus/isActiveMenu.d.ts
import type { MenuItem } from './types';
import type { PageData } from '../page';
export declare const isActiveMenu: (menuItem: MenuItem, pageData: PageData) => boolean;
// Check if a menu item should be marked as active
export const isActiveMenu = (menuItem, pageData) => {
// return if not browser
if (typeof window === 'undefined') {
return false;
}
const location = window.location;
const currentLang = pageData.lang;
let result;
if (menuItem.url === '/') {
result =
location.pathname === '/' ||
location.pathname === `/${currentLang}` ||
location.pathname === `/${currentLang}/`;
}
else {
result =
location.pathname === menuItem?.url ||
location.pathname === `/${currentLang}${menuItem.url}` ||
location.pathname === `/${currentLang}${menuItem.url}/`;
}
return result;
};
+2
-1

@@ -1,1 +0,2 @@

export * from './getCurrentLangMenus';
export * from './isActiveMenu';
export * from './types';

@@ -1,1 +0,2 @@

export * from './getCurrentLangMenus';
export * from './isActiveMenu';
export * from './types';
export interface MenuItem {
id: number;
parent_id: number | null;
position: number;
translations: Record<string, {
open_in_new_tab: boolean;
page_content_id: number;
title: string;
url: string | null;
use_custom_url: boolean;
use_page_title: boolean;
}>;
children: MenuItem[];
}
export interface ProcessedMenuItem {
id: number;
position: number;
title: string;
url: string | null;
open_in_new_tab: boolean;
children: ProcessedMenuItem[];
children: MenuItem[];
}

@@ -1,5 +0,4 @@

import type { ApiResponse } from './types';
/**
* Fetches Form data from the backend by language and slug
*/
export declare function fetchFormData(lang: string, slug: string, backendHost?: string): Promise<ApiResponse>;
export declare function fetchFormData(lang: string, slug: string, backendHost?: string): Promise<Record<string, unknown>>;

@@ -1,5 +0,5 @@

import type { ApiResponse } from './types';
import type { PageData } from './types';
/**
* Fetches page data from the backend by language and slug
*/
export declare function fetchPageData(lang: string | null, slug: string, isPreview?: boolean, authToken?: string | null, astroRequest?: Request | null, backendHost?: string): Promise<ApiResponse>;
export declare function fetchPageData(lang: string | null, slug: string, isPreview?: boolean, authToken?: string | null, astroRequest?: Request | null, backendHost?: string): Promise<PageData>;

@@ -49,17 +49,5 @@ import { fetchPublicSettings } from './fetchPublicSettings';

// Add authentication headers if token exists (for both preview and protected content)
let token = authToken;
// If no token provided and we're in browser environment, try Capacitor Preferences
if (!token && typeof window !== 'undefined') {
try {
const { Preferences } = await import('@capacitor/preferences');
const tokenResult = await Preferences.get({ key: 'token' });
token = tokenResult.value;
}
catch (e) {
console.warn('Could not get token from Preferences:', e);
}
if (authToken) {
fetchOptions.headers['Authorization'] = `Bearer ${authToken}`;
}
if (token) {
fetchOptions.headers['Authorization'] = `Bearer ${token}`;
}
// Fetch the page data from the backend

@@ -69,3 +57,3 @@ const response = await fetch(url, fetchOptions);

if (response.status === 401) {
return { error: true, status: 401, message: 'Authentication required' };
throw new Error('Authentication required');
}

@@ -84,3 +72,3 @@ // Only treat actual 404 as not found

public_settings: siteSettings,
lang: lang || siteSettings?.default_language?.iso_code || 'en',
lang: lang || siteSettings.default_language?.iso_code || 'en',
};

@@ -90,3 +78,3 @@ }

console.warn('Could not fetch site settings for 404 page:', settingsError);
return { notFound: true, status: 404, detail };
throw new Error(`Page not found: ${detail}`);
}

@@ -101,3 +89,3 @@ }

console.error(`Failed to parse response: ${parseError.message}`);
return { error: true, parseError: parseError.message };
throw new Error(`Failed to parse response: ${parseError.message}`);
}

@@ -107,4 +95,4 @@ }

console.error('Error fetching page data:', error);
return { error: true, message: error.message };
throw error;
}
}

@@ -5,2 +5,2 @@ import type { SiteSettings } from '../types';

*/
export declare function fetchPublicSettings(orgId?: number | null, astroRequest?: Request | null, lang?: string | null, backendHost?: string): Promise<SiteSettings | null>;
export declare function fetchPublicSettings(orgId?: number | null, astroRequest?: Request | null, lang?: string | null, backendHost?: string): Promise<SiteSettings>;

@@ -61,4 +61,4 @@ /**

console.error('Error fetching public settings:', error);
return null;
throw error;
}
}
/**
* Extracts the authentication token from cookies or URL parameter (for iframe preview)
*/
export declare function getAuthToken(astro: any): any;
export declare function getAuthToken(astro: any): string | null;

@@ -1,8 +0,6 @@

export * from './fetchBlogListData.js';
export * from './fetchBlogPostData.js';
export * from './fetchFormData.js';
export * from './fetchPageData.js';
export * from './fetchPublicSettings.js';
export * from './getAuthToken.js';
export * from './parseSlugForLangAndPath.js';
export * from './types.js';
export * from './fetchFormData';
export * from './fetchPageData';
export * from './fetchPublicSettings';
export * from './getAuthToken';
export * from './parseSlugForLangAndPath';
export * from './types';

@@ -1,8 +0,6 @@

export * from './fetchBlogListData.js';
export * from './fetchBlogPostData.js';
export * from './fetchFormData.js';
export * from './fetchPageData.js';
export * from './fetchPublicSettings.js';
export * from './getAuthToken.js';
export * from './parseSlugForLangAndPath.js';
export * from './types.js';
export * from './fetchFormData';
export * from './fetchPageData';
export * from './fetchPublicSettings';
export * from './getAuthToken';
export * from './parseSlugForLangAndPath';
export * from './types';

@@ -0,1 +1,3 @@

import type { SiteSettings } from '../types';
import type { MenuItem } from '../menus/types';
export interface SlugParseResult {

@@ -5,18 +7,70 @@ lang: string | null;

}
export interface ErrorResponse {
error: true;
status?: number;
message?: string;
parseError?: string;
export interface Language {
id: number;
name: string;
iso_code: string;
svg_flag: string;
}
export interface NotFoundResponse {
notFound: true;
status: number;
detail: string;
public_settings?: Record<string, unknown>;
lang?: string;
export interface SpecialTemplate {
name: string;
html?: string;
component_name?: string;
}
export interface BlogListResponse {
posts: Record<string, unknown>[];
export type Menus = MenuItem[];
export interface ContentField {
'ds-label': string;
'ds-type': string;
'ds-value': string;
}
export type ApiResponse = Record<string, unknown> | ErrorResponse | NotFoundResponse | BlogListResponse;
export interface Content {
main: ContentField;
}
export interface SeoMetadata {
title: string;
description: string | null;
featured_image_id: number | null;
featured_image_name: string | null;
allow_indexing: boolean;
}
export interface LanguageAlternative {
slug: string;
locale: Language;
}
export interface BlogPostAuthor {
id: number;
display_name?: string;
username: string;
image?: string;
}
export interface BlogPostListItem {
id: number;
title: string;
slug: string;
excerpt?: string;
featured_image_id?: number;
publish_date?: string;
author?: BlogPostAuthor;
lang: string;
}
export interface PageData {
id?: number;
title?: string;
content?: Content;
lang: string;
public_settings: SiteSettings;
seo_metadata?: SeoMetadata;
language_alternatives?: LanguageAlternative[];
is_frontend_page?: boolean | null;
string_id?: string | null;
contents?: unknown;
page_custom_code?: string | null;
custom_code?: string | null;
require_login?: boolean;
blog_posts?: BlogPostListItem[];
featured_image_id?: number;
publish_date?: string;
author?: BlogPostAuthor;
notFound?: boolean;
status?: number;
detail?: string;
}
import type { MenuItem } from './menus/types';
import type { SpecialTemplate } from './page/types';
export interface SiteSettings {
id: number;
name: string;
domains: string[];

@@ -25,2 +28,13 @@ available_languages: Array<{

menus: MenuItem[];
access_token_expire_minutes: number;
require_2fa_all_users: boolean;
allow_public_signup: boolean;
is_enabled_google_sign_in: boolean;
is_enabled_saml: boolean;
saml_sp_entity_id: string | null;
auto_translate_components: boolean;
has_openai_api_key: boolean;
ai_default_writing_model_id: number;
special_templates: Record<string, SpecialTemplate>;
selected_theme: string;
}
{
"name": "@deepsel/cms-utils",
"version": "1.0.0",
"description": "Helper utilities for DeepCMS",
"version": "1.1.0",
"description": "Helper utilities for Deepsel CMS",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/DeepselSystems/cms-utils.git"
},
"author": "Tim Tran <tim.tran@deepsel.com> (https://deepsel.com)",
"license": "MIT",
"bugs": {
"url": "https://github.com/DeepselSystems/cms-utils/issues"
"url": "https://github.com/DeepselSystems/deepsel-cms/issues"
},
"homepage": "https://github.com/DeepselSystems/cms-utils#readme",
"homepage": "https://github.com/DeepselSystems/deepsel-cms/tree/main/packages/cms-utils#readme",
"type": "module",

@@ -22,2 +18,73 @@ "main": "dist/index.js",

"types": "dist/index.d.ts",
"sideEffects": false,
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./types": {
"import": "./dist/types.js",
"types": "./dist/types.d.ts"
},
"./language": {
"import": "./dist/language/index.js",
"types": "./dist/language/index.d.ts"
},
"./language/isValidLanguageCode": {
"import": "./dist/language/isValidLanguageCode.js",
"types": "./dist/language/isValidLanguageCode.d.ts"
},
"./menus": {
"import": "./dist/menus/index.js",
"types": "./dist/menus/index.d.ts"
},
"./menus/isActiveMenu": {
"import": "./dist/menus/isActiveMenu.js",
"types": "./dist/menus/isActiveMenu.d.ts"
},
"./menus/types": {
"import": "./dist/menus/types.js",
"types": "./dist/menus/types.d.ts"
},
"./page": {
"import": "./dist/page/index.js",
"types": "./dist/page/index.d.ts"
},
"./page/constants": {
"import": "./dist/page/constants.js",
"types": "./dist/page/constants.d.ts"
},
"./page/types": {
"import": "./dist/page/types.js",
"types": "./dist/page/types.d.ts"
},
"./page/fetchBlogListData": {
"import": "./dist/page/fetchBlogListData.js",
"types": "./dist/page/fetchBlogListData.d.ts"
},
"./page/fetchBlogPostData": {
"import": "./dist/page/fetchBlogPostData.js",
"types": "./dist/page/fetchBlogPostData.d.ts"
},
"./page/fetchFormData": {
"import": "./dist/page/fetchFormData.js",
"types": "./dist/page/fetchFormData.d.ts"
},
"./page/fetchPageData": {
"import": "./dist/page/fetchPageData.js",
"types": "./dist/page/fetchPageData.d.ts"
},
"./page/fetchPublicSettings": {
"import": "./dist/page/fetchPublicSettings.js",
"types": "./dist/page/fetchPublicSettings.d.ts"
},
"./page/getAuthToken": {
"import": "./dist/page/getAuthToken.js",
"types": "./dist/page/getAuthToken.d.ts"
},
"./page/parseSlugForLangAndPath": {
"import": "./dist/page/parseSlugForLangAndPath.js",
"types": "./dist/page/parseSlugForLangAndPath.d.ts"
}
},
"files": [

@@ -24,0 +91,0 @@ "dist"

# @deepsel/cms-utils
A collection of light-weight helper functions for building DeepCMS features in JavaScript/TypeScript apps.
Framework-agnostic utilities for building DeepCMS themes in any JavaScript framework.
`@deepsel/cms-utils` is designed to be:
- **Framework-agnostic** – works with Node, React, Astro, Next.js, etc.
- **Framework-agnostic** – works with React, Vue, Angular, Astro, Next.js, etc.
- **TypeScript-friendly** – fully typed helpers.
- **CMS-oriented** – utilities for menus, slugs, URLs, localization, content trees, etc.
- **CMS-oriented** – utilities for menus, slugs, URLs, localization, page data fetching, etc.
> **Note:** For React-specific hooks and components, use [`@deepsel/deep-cms-react`](https://github.com/DeepselSystems/deep-cms-react)
---

@@ -24,7 +26,7 @@

```typescript
import { getMenusForCurrentLang } from '@deepsel/cms-utils';
import { isValidLanguageCode } from '@deepsel/cms-utils';
const menus = getMenusForCurrentLang();
const isValidLanguageCode = isValidLanguageCode('en');
console.log(menus);
console.log({ isValidLanguageCode });
```

@@ -93,5 +95,7 @@

// inside my-app
import { getMenusForCurrentLang } from '@deepsel/cms-utils';
import { isValidLanguageCode } from '@deepsel/cms-utils';
const menus = getMenusForCurrentLang();
const isValidLanguageCode = isValidLanguageCode('en');
console.log({ isValidLanguageCode });
```

@@ -98,0 +102,0 @@

import { ProcessedMenuItem } from './types';
import type { SiteSettings } from '../types';
export declare const getCurrentLangMenus: (settings: SiteSettings) => ProcessedMenuItem[] | null;
/*
Get menus based on the current selected language
*/
export const getCurrentLangMenus = (settings) => {
if (!settings || !settings.menus || !settings.menus.length)
return null;
let currentLang = localStorage.getItem('i18nextLng');
// If no language is selected, use the default language
if (!currentLang) {
currentLang = settings.default_language.iso_code;
}
return settings.menus
.map((menu) => processMenuItem(menu, currentLang))
.filter((item) => item !== null);
};
/*
Process a menu item and its children recursively:
- Remove null items
- Only keep fields that are needed from the translation for the current language
- Remove other translations
*/
function processMenuItem(menuItem, currentLang) {
const translation = menuItem.translations[currentLang];
if (!translation) {
return null;
}
// Process children recursively and filter out null items
const children = menuItem.children && menuItem.children.length > 0
? menuItem.children
.map((child) => processMenuItem(child, currentLang))
.filter((item) => item !== null)
: [];
return {
id: menuItem.id,
position: menuItem.position,
title: translation.title,
url: translation.url,
open_in_new_tab: translation.open_in_new_tab,
children,
};
}
import type { ApiResponse } from './types';
/**
* Fetches blog list data from the backend by language
*/
export declare function fetchBlogListData(lang: string, authToken?: string | null, astroRequest?: Request | null, backendHost?: string): Promise<ApiResponse>;
/**
* Fetches blog list data from the backend by language
*/
export async function fetchBlogListData(lang, authToken = null, astroRequest = null, backendHost = 'http://localhost:8000') {
try {
// Determine the URL based on whether a language is provided
const url = lang && lang !== 'default'
? `${backendHost}/blog_post/website/${lang}`
: `${backendHost}/blog_post/website/default`;
// Prepare fetch options
const fetchOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
};
// Send the current hostname to the backend for proper domain detection
let hostname = null;
// Server-side: Extract hostname from Astro request
if (astroRequest) {
const url = new URL(astroRequest.url);
hostname = url.hostname;
}
// Client-side: Extract hostname from window
else if (typeof window !== 'undefined') {
hostname = window.location.hostname;
}
if (hostname) {
fetchOptions.headers['X-Original-Host'] = hostname;
fetchOptions.headers['X-Frontend-Host'] = hostname;
// Note: Cannot override Host header due to browser security restrictions
}
// Add authentication headers if token exists (for both preview and protected content)
let token = authToken;
// If no token provided and we're in browser environment, try Capacitor Preferences
if (!token && typeof window !== 'undefined') {
try {
const { Preferences } = await import('@capacitor/preferences');
const tokenResult = await Preferences.get({ key: 'token' });
token = tokenResult.value;
}
catch (e) {
console.warn('Could not get token from Preferences:', e);
}
}
if (token) {
fetchOptions.headers['Authorization'] = `Bearer ${token}`;
}
// Fetch the blog list data from the backend
const response = await fetch(url, fetchOptions);
// Handle authentication errors
if (response.status === 401) {
return { error: true, status: 401, message: 'Authentication required' };
}
// Only treat actual 404 as not found
if (response.status === 404) {
const { detail } = await response.json();
console.warn('404', url, { detail });
return { notFound: true, status: 404, detail };
}
try {
// Parse the JSON
const posts = await response.json();
return { posts };
}
catch (parseError) {
console.error(`Failed to parse response: ${parseError.message}`);
return { error: true, parseError: parseError.message };
}
}
catch (error) {
console.error('Error fetching blog list data:', error);
return { error: true, message: error.message };
}
}
import type { ApiResponse } from './types';
/**
* Fetches Blog-Post-Content data from the backend by language and slug
*/
export declare function fetchBlogPostData(lang: string, slug: string, authToken?: string | null, astroRequest?: Request | null, backendHost?: string): Promise<ApiResponse>;
/**
* Fetches Blog-Post-Content data from the backend by language and slug
*/
export async function fetchBlogPostData(lang, slug, authToken = null, astroRequest = null, backendHost = 'http://localhost:8000') {
try {
// Format the slug properly
const formattedSlug = slug.replace(/^\/blog\//, '');
// Determine the URL based on whether a language is provided
const url = lang && lang !== 'default'
? `${backendHost}/blog_post/website/${lang}/${formattedSlug}`
: `${backendHost}/blog_post/website/default/${formattedSlug}`;
// Prepare fetch options
const fetchOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
};
// Send the current hostname to the backend for proper domain detection
let hostname = null;
// Server-side: Extract hostname from Astro request
if (astroRequest) {
const url = new URL(astroRequest.url);
hostname = url.hostname;
}
// Client-side: Extract hostname from window
else if (typeof window !== 'undefined') {
hostname = window.location.hostname;
}
if (hostname) {
fetchOptions.headers['X-Original-Host'] = hostname;
fetchOptions.headers['X-Frontend-Host'] = hostname;
// Note: Cannot override Host header due to browser security restrictions
}
// Add authentication headers if token exists (for both preview and protected content)
let token = authToken;
// If no token provided and we're in browser environment, try Capacitor Preferences
if (!token && typeof window !== 'undefined') {
try {
const { Preferences } = await import('@capacitor/preferences');
const tokenResult = await Preferences.get({ key: 'token' });
token = tokenResult.value;
}
catch (e) {
console.warn('Could not get token from Preferences:', e);
}
}
if (token) {
fetchOptions.headers['Authorization'] = `Bearer ${token}`;
}
// Fetch the page data from the backend
const response = await fetch(url, fetchOptions);
// Handle authentication errors
if (response.status === 401) {
return { error: true, status: 401, message: 'Authentication required' };
}
// Only treat actual 404 as not found
if (response.status === 404) {
const { detail } = await response.json();
console.warn('404', url, { detail });
return { notFound: true, status: 404, detail };
}
try {
// Parse the JSON
return await response.json();
}
catch (parseError) {
console.error(`Failed to parse response: ${parseError.message}`);
return { error: true, parseError: parseError.message };
}
}
catch (error) {
console.error('Error fetching page data:', error);
return { error: true, message: error.message };
}
}