New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@contentrain/query

Package Overview
Dependencies
Maintainers
0
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@contentrain/query - npm Package Compare versions

Comparing version 3.0.0 to 3.1.0

66

dist/index.d.ts
interface ContentrainConfig {
contentDir: string;
defaultLocale?: string;
models: {
[modelId: string]: {
localized?: boolean;
localized: boolean;
defaultLocale?: string;

@@ -79,15 +80,3 @@ locales?: string[];

}
interface AssetMetadata {
path: string;
size: number;
type: string;
createdAt: string;
updatedAt: string;
}
type ContentrainLocales = string;
interface QueryConfig<TFields extends BaseContentrainType, TLocales extends ContentrainLocales, TRelations extends Record<string, BaseContentrainType>> {
fields: TFields;
locales: TLocales;
relations: TRelations;
}

@@ -113,2 +102,16 @@ interface ContentLoaderOptions {

}
interface AssetMetadata {
path: string;
mimetype: string;
size: number;
alt: string;
meta: {
user: {
name: string;
email: string;
avatar: string;
};
createdAt: string;
};
}
interface LoaderResult<T extends BaseContentrainType = BaseContentrainType> {

@@ -119,2 +122,3 @@ model: ModelConfig;

};
assets?: AssetMetadata[];
}

@@ -138,8 +142,15 @@ interface RelationConfig {

}
interface MemoryCacheOptions {
maxSize?: number;
defaultTTL?: number;
}
type Operator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'startsWith' | 'endsWith';
interface Filter {
type StringOperator = 'eq' | 'ne' | 'contains' | 'startsWith' | 'endsWith';
type NumericOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte';
type ArrayOperator = 'in' | 'nin';
type Operator = StringOperator | NumericOperator | ArrayOperator;
interface Filter<T = any> {
field: string;
operator: Operator;
value: any;
value: T extends Array<infer U> ? (ArrayOperator extends 'in' | 'nin' ? U[] : U) : T;
}

@@ -174,2 +185,7 @@ interface Sort {

}
interface QueryConfig<TFields extends BaseContentrainType, TLocales extends ContentrainLocales = 'en' | 'tr', TRelations extends Record<string, BaseContentrainType> = Record<string, never>> {
fields: TFields;
locales: TLocales;
relations: TRelations;
}

@@ -190,2 +206,3 @@ declare class ContentLoader {

private loadRelations;
private getModelLocales;
load<T extends BaseContentrainType>(model: string): Promise<LoaderResult<T>>;

@@ -202,2 +219,3 @@ resolveRelation<T extends BaseContentrainType, R extends BaseContentrainType>(model: string, relationField: keyof T, data: T[], locale?: string): Promise<R[]>;

private resolveIncludes;
private applyStringOperation;
execute<T extends BaseContentrainType>({ model, data, filters, includes, sorting, pagination, options, }: {

@@ -238,9 +256,6 @@ model: string;

model: string;
filters: Filter[];
filters: Filter<any>[];
includes: Include;
sorting: Sort[];
pagination: {
limit?: number;
offset?: number;
};
pagination: Pagination;
options: QueryOptions;

@@ -253,6 +268,2 @@ };

interface MemoryCacheOptions {
maxSize?: number;
defaultTTL?: number;
}
declare class MemoryCache {

@@ -279,4 +290,7 @@ private cache;

load<T extends BaseContentrainType>(model: string): Promise<LoaderResult<T>>;
clearCache(): Promise<void>;
refreshCache(model: string): Promise<void>;
getCacheStats(): CacheStats;
}
export { type AssetMetadata, type BaseContentrainType, type CacheEntry, type CacheStats, type ContentFile, ContentLoader, type ContentLoaderOptions, type ContentrainComponentId, type ContentrainConfig, type ContentrainFieldType, type ContentrainLocales, ContentrainQueryBuilder, ContentrainSDK, type ContentrainStatus, type FieldMetadata, type FieldOptions, type FieldValidations, type Filter, type Include, type LoaderResult, MemoryCache, type MemoryCacheOptions, type ModelConfig, type ModelMetadata, type Operator, type Pagination, type QueryConfig, QueryExecutor, type QueryOptions, type QueryResult, type RelationConfig, type Sort };
export { type ArrayOperator, type AssetMetadata, type BaseContentrainType, type CacheEntry, type CacheStats, type ContentFile, ContentLoader, type ContentLoaderOptions, type ContentrainComponentId, type ContentrainConfig, type ContentrainFieldType, type ContentrainLocales, ContentrainQueryBuilder, ContentrainSDK, type ContentrainStatus, type FieldMetadata, type FieldOptions, type FieldValidations, type Filter, type Include, type LoaderResult, MemoryCache, type MemoryCacheOptions, type ModelConfig, type ModelMetadata, type NumericOperator, type Operator, type Pagination, type QueryConfig, QueryExecutor, type QueryOptions, type QueryResult, type RelationConfig, type Sort, type StringOperator };

@@ -177,10 +177,2 @@ 'use strict';

const modelFields = JSON.parse(modelContent);
if (!Array.isArray(modelFields)) {
throw new TypeError(`Invalid field configuration for model ${model}: Expected an array of fields`);
}
modelFields.forEach((field, index) => {
if (!field.fieldId || !field.fieldType || !field.componentId) {
throw new Error(`Invalid field at index ${index} for model ${model}: Missing required properties`);
}
});
return {

@@ -194,8 +186,18 @@ metadata: modelMetadata,

}
async loadContentFile(model, locale) {
async loadContentFile(model, locale = "default") {
try {
const modelConfig = await this.loadModelConfig(model);
let contentPath;
if (locale) {
if (modelConfig.metadata.localization) {
if (!locale || locale === "default") {
if (!this.options.defaultLocale) {
throw new Error(`Default locale is required for localized model "${model}"`);
}
locale = this.options.defaultLocale;
}
contentPath = path.join(this.options.contentDir, model, `${locale}.json`);
} else {
if (locale !== "default") {
console.warn(`Locale "${locale}" specified for non-localized model "${model}". This parameter will be ignored.`);
}
contentPath = path.join(this.options.contentDir, model, `${model}.json`);

@@ -208,3 +210,3 @@ }

model,
locale,
locale: modelConfig.metadata.localization ? locale : void 0,
data

@@ -249,4 +251,27 @@ };

}
async getModelLocales(model, modelConfig) {
try {
if (!modelConfig.metadata.localization) {
return ["default"];
}
const modelDir = path.join(this.options.contentDir, model);
const files = await promises.readdir(modelDir);
const locales = files.filter((file) => file.endsWith(".json")).map((file) => file.replace(".json", "")).filter((locale) => locale !== model);
if (locales.length === 0) {
if (!this.options.defaultLocale) {
throw new Error(`No locale files found for localized model "${model}" and no default locale specified`);
}
return [this.options.defaultLocale];
}
return locales;
} catch (error) {
if (!this.options.defaultLocale) {
throw new Error(`Failed to read locales for model ${model} and no default locale specified: ${error?.message}`);
}
console.warn(`Failed to read locales for model ${model}: ${error?.message}`);
return [this.options.defaultLocale];
}
}
async load(model) {
const cacheKey = this.getCacheKey(model);
const cacheKey = `${model}`;
if (this.options.cache) {

@@ -263,6 +288,13 @@ const cached = await this.cache.get(cacheKey);

if (modelConfig.metadata.localization) {
const locales = ["en", "tr"];
const locales = await this.getModelLocales(model, modelConfig);
for (const locale of locales) {
const file = await this.loadContentFile(model, locale);
content[locale] = file.data;
try {
const file = await this.loadContentFile(model, locale);
content[locale] = file.data;
} catch (error) {
console.warn(`Failed to load content for locale ${locale}: ${error?.message}`);
if (locale === this.options.defaultLocale) {
throw error;
}
}
}

@@ -273,5 +305,14 @@ } else {

}
let assets;
try {
const assetsPath = path.join(this.options.contentDir, "assets.json");
const assetsContent = await promises.readFile(assetsPath, "utf-8");
assets = JSON.parse(assetsContent);
} catch (error) {
console.warn("Assets file not found or cannot be read:", error);
}
const result = {
model: modelConfig,
content
content,
assets
};

@@ -306,10 +347,12 @@ if (this.options.cache) {

} else {
return data.flatMap((item) => {
const ids = Array.isArray(item[relationField]) ? item[relationField] : [item[relationField]];
const items = ids.map((id) => relatedData.find((r) => r.ID === id)).filter(Boolean);
if (items.length !== ids.length) {
throw new Error("Failed to resolve relation: Some related items not found");
}
return items;
});
const uniqueIds = new Set(
data.flatMap(
(item) => Array.isArray(item[relationField]) ? item[relationField] : [item[relationField]]
)
);
const items = Array.from(uniqueIds).map((id) => relatedData.find((r) => r.ID === id)).filter(Boolean);
if (items.length !== uniqueIds.size) {
throw new Error("Failed to resolve relation: Some related items not found");
}
return items;
}

@@ -400,3 +443,16 @@ } catch (error) {

const result = await this.loader.load(this.model);
const data = this.options.locale ? result.content[this.options.locale] : result.content.en;
const modelConfig = result.model;
let data;
if (modelConfig.metadata.localization) {
const locale = this.options.locale || "en";
data = result.content[locale];
if (!data) {
throw new Error(`Content not found for locale: ${locale}`);
}
} else {
if (!result.content.default) {
throw new Error(`Content not found for model: ${this.model}`);
}
data = result.content.default;
}
return this.executor.execute({

@@ -431,34 +487,48 @@ model: this.model,

return data.filter((item) => {
return filters.every((filter) => {
const value = item[filter.field];
return filters.every(({ field, operator, value }) => {
const itemValue = item[field];
const validOperators = ["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin", "contains", "startsWith", "endsWith"];
if (!validOperators.includes(filter.operator)) {
throw new Error(`Invalid operator: ${filter.operator}`);
if (!validOperators.includes(operator)) {
throw new Error(`Invalid operator: ${operator}`);
}
switch (filter.operator) {
case "eq":
return value === filter.value;
case "ne":
return value !== filter.value;
case "gt":
return value > filter.value;
case "gte":
return value >= filter.value;
case "lt":
return value < filter.value;
case "lte":
return value <= filter.value;
case "in":
return Array.isArray(filter.value) && filter.value.includes(value);
case "nin":
return Array.isArray(filter.value) && !filter.value.includes(value);
case "contains":
return typeof value === "string" && value.includes(filter.value);
case "startsWith":
return typeof value === "string" && value.startsWith(filter.value);
case "endsWith":
return typeof value === "string" && value.endsWith(filter.value);
default:
return false;
if (typeof itemValue === "string" && typeof value === "string") {
return this.applyStringOperation(itemValue, operator, value);
}
if (Array.isArray(value)) {
switch (operator) {
case "in":
return value.includes(itemValue);
case "nin":
return !value.includes(itemValue);
default:
throw new Error(`Invalid array operator: ${operator}`);
}
}
if (Array.isArray(itemValue)) {
switch (operator) {
case "in":
return value.some((v) => itemValue.includes(v));
case "nin":
return !value.some((v) => itemValue.includes(v));
default:
throw new Error(`Invalid array operator: ${operator}`);
}
}
if (typeof itemValue === "number" && typeof value === "number") {
switch (operator) {
case "eq":
return itemValue === value;
case "ne":
return itemValue !== value;
case "gt":
return itemValue > value;
case "gte":
return itemValue >= value;
case "lt":
return itemValue < value;
case "lte":
return itemValue <= value;
}
}
return false;
});

@@ -469,9 +539,12 @@ });

return [...data].sort((a, b) => {
for (const sort of sorting) {
const aValue = a[sort.field];
const bValue = b[sort.field];
for (const { field, direction } of sorting) {
if (!(field in a)) {
throw new Error(`Invalid sort field: ${field}`);
}
const aValue = a[field];
const bValue = b[field];
if (aValue === bValue)
continue;
const direction = sort.direction === "asc" ? 1 : -1;
return aValue > bValue ? direction : -direction;
const compareResult = aValue < bValue ? -1 : 1;
return direction === "asc" ? compareResult : -compareResult;
}

@@ -519,2 +592,20 @@ return 0;

}
applyStringOperation(value, operator, searchValue) {
switch (operator) {
case "eq":
return value === searchValue;
case "ne":
return value !== searchValue;
case "contains":
return value.toLowerCase().includes(searchValue.toLowerCase());
case "startsWith":
return value.toLowerCase().startsWith(searchValue.toLowerCase());
case "endsWith":
return value.toLowerCase().endsWith(searchValue.toLowerCase());
default: {
const _exhaustiveCheck = operator;
return _exhaustiveCheck;
}
}
}
async execute({

@@ -570,2 +661,11 @@ model,

}
async clearCache() {
return this.loader.clearCache();
}
async refreshCache(model) {
return this.loader.refreshCache(model);
}
getCacheStats() {
return this.loader.getCacheStats();
}
};

@@ -572,0 +672,0 @@ __name(_ContentrainSDK, "ContentrainSDK");

{
"name": "@contentrain/query",
"version": "3.0.0",
"version": "3.1.0",
"description": "Core package for Contentrain SDK, providing fundamental functionality and types",

@@ -44,2 +44,4 @@ "author": "Contentrain Inc.",

"build": "tsup && rm -rf dist/types dist/cache dist/loader dist/query",
"dev": "tsup --watch",
"dev:watch": "tsup --watch",
"test": "vitest watch",

@@ -46,0 +48,0 @@ "test:run": "vitest run",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc