Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@plasius/entity-manager

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@plasius/entity-manager - npm Package Compare versions

Comparing version
1.0.12
to
1.0.13
+388
src/user/profile.validation.ts
import {
createSchema,
field,
validateEmail,
validateName,
type Infer,
type ValidationIssue,
type ValidationIssueInput,
type ValidationResult,
} from "@plasius/schema";
import { UserEmailPreferences } from "./user.entity.js";
import { PreferredDisplayOrder } from "./user.name.js";
const PROFILE_PROFANITY_LEXICON = {
en: [
"arse",
"asshole",
"bastard",
"bitch",
"bollocks",
"bullshit",
"cunt",
"damn",
"fuck",
"motherfucker",
"shit",
"twat",
],
} as const;
export const PROFILE_PROFANITY_SUPPORTED_LOCALES = Object.freeze(
Object.keys(PROFILE_PROFANITY_LEXICON),
) as readonly string[];
export const PROFILE_DEFAULT_PROFANITY_LOCALE = "en";
export type EditableUserProfileFieldName =
| "name.firstName"
| "name.middleName"
| "name.lastName"
| "name.displayName"
| "name.preferredDisplayOrder"
| "email"
| "emailPreferences";
export type EditableUserProfileFieldErrors = Partial<
Record<EditableUserProfileFieldName, string>
>;
export interface EditableUserProfileValidationIssue extends ValidationIssue {
field?: EditableUserProfileFieldName;
}
const EDITABLE_PROFILE_FIELD_NAMES = new Set<EditableUserProfileFieldName>([
"name.firstName",
"name.middleName",
"name.lastName",
"name.displayName",
"name.preferredDisplayOrder",
"email",
"emailPreferences",
]);
const EDITABLE_PROFILE_FIELD_LABELS: Record<EditableUserProfileFieldName, string> = {
"name.firstName": "First name",
"name.middleName": "Middle name",
"name.lastName": "Last name",
"name.displayName": "Display name",
"name.preferredDisplayOrder": "Preferred name display",
email: "Email",
emailPreferences: "Email preferences",
};
type ProfileTextFieldRule = {
path: string;
label: string;
maxLength: number;
required: boolean;
format: "name" | "email";
};
function toIssue(
path: string,
code: string,
message: string,
): ValidationIssueInput {
return {
path,
code,
message,
};
}
function normalizeToken(value: string): string {
return value.trim().toLowerCase();
}
function isEditableUserProfileFieldName(
value: string,
): value is EditableUserProfileFieldName {
return EDITABLE_PROFILE_FIELD_NAMES.has(value as EditableUserProfileFieldName);
}
function readFieldNameFromTextError(
error: string,
): EditableUserProfileFieldName | null {
const fieldPath = error.match(/:\s*([a-zA-Z]+(?:\.[a-zA-Z]+)*)$/)?.[1] ?? "";
if (!isEditableUserProfileFieldName(fieldPath)) {
return null;
}
return fieldPath;
}
function createLegacyIssueFromError(
error: string,
): EditableUserProfileValidationIssue | null {
const field = readFieldNameFromTextError(error);
if (!field) {
return null;
}
if (error.startsWith("Missing required field:")) {
return {
field,
path: field,
code: `${field}.required`,
message: `${EDITABLE_PROFILE_FIELD_LABELS[field]} is required.`,
};
}
if (error.startsWith("Field is immutable:")) {
return {
field,
path: field,
code: `${field}.immutable`,
message: `${EDITABLE_PROFILE_FIELD_LABELS[field]} cannot be changed.`,
};
}
if (error.startsWith("Field ")) {
return {
field,
path: field,
code: `${field}.invalid_type`,
message: error,
};
}
return {
field,
path: field,
code: `${field}.invalid_value`,
message: error,
};
}
function normalizeValidationIssue(
issue: ValidationIssue,
): EditableUserProfileValidationIssue {
const field = isEditableUserProfileFieldName(issue.path)
? issue.path
: undefined;
return {
...issue,
field,
};
}
function findProfanityMatch(value: string, locale: string): string | null {
const lexicon =
PROFILE_PROFANITY_LEXICON[locale as keyof typeof PROFILE_PROFANITY_LEXICON]
?? PROFILE_PROFANITY_LEXICON[PROFILE_DEFAULT_PROFANITY_LOCALE];
const normalizedValue = ` ${normalizeToken(value).replace(/[^a-z0-9]+/g, " ")} `;
for (const token of lexicon) {
if (normalizedValue.includes(` ${token} `)) {
return token;
}
}
return null;
}
function validateProfileTextField(
value: unknown,
rule: ProfileTextFieldRule,
): true | ValidationIssueInput {
if (typeof value !== "string") {
return true;
}
const normalizedValue = value.trim();
if (normalizedValue.length === 0) {
if (!rule.required) {
return true;
}
return toIssue(
rule.path,
`${rule.path}.required`,
`${rule.label} is required.`,
);
}
if (normalizedValue.length > rule.maxLength) {
return toIssue(
rule.path,
`${rule.path}.too_long`,
`${rule.label} must be ${rule.maxLength} characters or fewer.`,
);
}
if (rule.format === "name" && !validateName(normalizedValue)) {
return toIssue(
rule.path,
`${rule.path}.invalid_format`,
`${rule.label} contains unsupported characters.`,
);
}
if (rule.format === "email" && !validateEmail(normalizedValue)) {
return toIssue(
rule.path,
`${rule.path}.invalid_format`,
`${rule.label} must be a valid email address.`,
);
}
const profanityToken = findProfanityMatch(normalizedValue, PROFILE_DEFAULT_PROFANITY_LOCALE);
if (profanityToken) {
return toIssue(
rule.path,
`${rule.path}.profanity`,
`${rule.label} contains blocked language.`,
);
}
return true;
}
export const editableProfileNameShape = {
firstName: field
.string()
.validator((value) =>
validateProfileTextField(value, {
path: "name.firstName",
label: "First name",
maxLength: 64,
required: true,
format: "name",
})
),
middleName: field
.string()
.optional()
.validator((value) =>
validateProfileTextField(value, {
path: "name.middleName",
label: "Middle name",
maxLength: 64,
required: false,
format: "name",
})
),
lastName: field
.string()
.validator((value) =>
validateProfileTextField(value, {
path: "name.lastName",
label: "Last name",
maxLength: 64,
required: true,
format: "name",
})
),
displayName: field
.string()
.validator((value) =>
validateProfileTextField(value, {
path: "name.displayName",
label: "Display name",
maxLength: 80,
required: true,
format: "name",
})
),
preferredDisplayOrder: field
.string()
.enum([...Object.values(PreferredDisplayOrder)]),
};
export const editableUserProfileSchema = createSchema(
{
email: field
.string()
.validator((value) =>
validateProfileTextField(value, {
path: "email",
label: "Email",
maxLength: 254,
required: true,
format: "email",
})
),
name: field.object(editableProfileNameShape),
emailPreferences: field
.array(
field
.string()
.enum([...Object.values(UserEmailPreferences)])
.as<UserEmailPreferences>()
)
.optional()
.as<UserEmailPreferences[]>(),
},
"userProfileEditable",
{
version: "1.0.0",
piiEnforcement: "strict",
table: "",
},
);
export type EditableUserProfile = Infer<(typeof editableUserProfileSchema)["_shape"]>;
export function validateEditableUserProfile(
input: unknown,
): ValidationResult<EditableUserProfile> {
return editableUserProfileSchema.validate(input);
}
export function mapEditableUserProfileValidationErrors(
validation: Pick<ValidationResult<EditableUserProfile>, "errors" | "issues">,
): {
fieldErrors: EditableUserProfileFieldErrors;
formErrors: string[];
issues: EditableUserProfileValidationIssue[];
} {
const issues: EditableUserProfileValidationIssue[] = (validation.issues ?? []).map(
normalizeValidationIssue,
);
const fieldErrors: EditableUserProfileFieldErrors = {};
const formErrors: string[] = [];
const seenMessages = new Set(issues.map((issue) => issue.message));
for (const issue of issues) {
if (issue.field) {
fieldErrors[issue.field] ??= issue.message;
continue;
}
formErrors.push(issue.message);
}
for (const rawError of validation.errors ?? []) {
const error = String(rawError);
if (seenMessages.has(error)) {
continue;
}
const issue = createLegacyIssueFromError(error);
if (!issue) {
formErrors.push(error);
continue;
}
issues.push(issue);
if (issue.field) {
fieldErrors[issue.field] ??= issue.message;
} else {
formErrors.push(issue.message);
}
}
return {
fieldErrors,
formErrors,
issues,
};
}
+15
-0

@@ -24,2 +24,16 @@

## [1.0.13] - 2026-03-27
- **Added**
- (placeholder)
- **Changed**
- (placeholder)
- **Fixed**
- (placeholder)
- **Security**
- (placeholder)
## [1.0.12] - 2026-03-09

@@ -177,1 +191,2 @@

[1.0.12]: https://github.com/Plasius-LTD/entity-manager/releases/tag/v1.0.12
[1.0.13]: https://github.com/Plasius-LTD/entity-manager/releases/tag/v1.0.13
+291
-19

@@ -26,2 +26,4 @@ "use strict";

EntityTypes: () => EntityTypes,
PROFILE_DEFAULT_PROFANITY_LOCALE: () => PROFILE_DEFAULT_PROFANITY_LOCALE,
PROFILE_PROFANITY_SUPPORTED_LOCALES: () => PROFILE_PROFANITY_SUPPORTED_LOCALES,
PreferredDisplayOrder: () => PreferredDisplayOrder,

@@ -42,2 +44,3 @@ Role: () => Role,

baseEntityShape: () => baseEntityShape,
editableUserProfileSchema: () => editableUserProfileSchema,
featureFlagEntitySchema: () => featureFlagEntitySchema,

@@ -48,2 +51,3 @@ featureFlagEntityShape: () => featureFlagEntityShape,

isValidEntityType: () => isValidEntityType,
mapEditableUserProfileValidationErrors: () => mapEditableUserProfileValidationErrors,
modelAssetEntitySchema: () => modelAssetEntitySchema,

@@ -62,2 +66,3 @@ objectAssetEntitySchema: () => objectAssetEntitySchema,

validateAssetSchema: () => validateAssetSchema,
validateEditableUserProfile: () => validateEditableUserProfile,
validateFeatureFlagValue: () => validateFeatureFlagValue,

@@ -651,4 +656,266 @@ validateSettingValue: () => validateSettingValue

// src/user/profile.validation.ts
var import_schema24 = require("@plasius/schema");
var PROFILE_PROFANITY_LEXICON = {
en: [
"arse",
"asshole",
"bastard",
"bitch",
"bollocks",
"bullshit",
"cunt",
"damn",
"fuck",
"motherfucker",
"shit",
"twat"
]
};
var PROFILE_PROFANITY_SUPPORTED_LOCALES = Object.freeze(
Object.keys(PROFILE_PROFANITY_LEXICON)
);
var PROFILE_DEFAULT_PROFANITY_LOCALE = "en";
var EDITABLE_PROFILE_FIELD_NAMES = /* @__PURE__ */ new Set([
"name.firstName",
"name.middleName",
"name.lastName",
"name.displayName",
"name.preferredDisplayOrder",
"email",
"emailPreferences"
]);
var EDITABLE_PROFILE_FIELD_LABELS = {
"name.firstName": "First name",
"name.middleName": "Middle name",
"name.lastName": "Last name",
"name.displayName": "Display name",
"name.preferredDisplayOrder": "Preferred name display",
email: "Email",
emailPreferences: "Email preferences"
};
function toIssue(path, code, message) {
return {
path,
code,
message
};
}
function normalizeToken(value) {
return value.trim().toLowerCase();
}
function isEditableUserProfileFieldName(value) {
return EDITABLE_PROFILE_FIELD_NAMES.has(value);
}
function readFieldNameFromTextError(error) {
const fieldPath = error.match(/:\s*([a-zA-Z]+(?:\.[a-zA-Z]+)*)$/)?.[1] ?? "";
if (!isEditableUserProfileFieldName(fieldPath)) {
return null;
}
return fieldPath;
}
function createLegacyIssueFromError(error) {
const field22 = readFieldNameFromTextError(error);
if (!field22) {
return null;
}
if (error.startsWith("Missing required field:")) {
return {
field: field22,
path: field22,
code: `${field22}.required`,
message: `${EDITABLE_PROFILE_FIELD_LABELS[field22]} is required.`
};
}
if (error.startsWith("Field is immutable:")) {
return {
field: field22,
path: field22,
code: `${field22}.immutable`,
message: `${EDITABLE_PROFILE_FIELD_LABELS[field22]} cannot be changed.`
};
}
if (error.startsWith("Field ")) {
return {
field: field22,
path: field22,
code: `${field22}.invalid_type`,
message: error
};
}
return {
field: field22,
path: field22,
code: `${field22}.invalid_value`,
message: error
};
}
function normalizeValidationIssue(issue) {
const field22 = isEditableUserProfileFieldName(issue.path) ? issue.path : void 0;
return {
...issue,
field: field22
};
}
function findProfanityMatch(value, locale) {
const lexicon = PROFILE_PROFANITY_LEXICON[locale] ?? PROFILE_PROFANITY_LEXICON[PROFILE_DEFAULT_PROFANITY_LOCALE];
const normalizedValue = ` ${normalizeToken(value).replace(/[^a-z0-9]+/g, " ")} `;
for (const token of lexicon) {
if (normalizedValue.includes(` ${token} `)) {
return token;
}
}
return null;
}
function validateProfileTextField(value, rule) {
if (typeof value !== "string") {
return true;
}
const normalizedValue = value.trim();
if (normalizedValue.length === 0) {
if (!rule.required) {
return true;
}
return toIssue(
rule.path,
`${rule.path}.required`,
`${rule.label} is required.`
);
}
if (normalizedValue.length > rule.maxLength) {
return toIssue(
rule.path,
`${rule.path}.too_long`,
`${rule.label} must be ${rule.maxLength} characters or fewer.`
);
}
if (rule.format === "name" && !(0, import_schema24.validateName)(normalizedValue)) {
return toIssue(
rule.path,
`${rule.path}.invalid_format`,
`${rule.label} contains unsupported characters.`
);
}
if (rule.format === "email" && !(0, import_schema24.validateEmail)(normalizedValue)) {
return toIssue(
rule.path,
`${rule.path}.invalid_format`,
`${rule.label} must be a valid email address.`
);
}
const profanityToken = findProfanityMatch(normalizedValue, PROFILE_DEFAULT_PROFANITY_LOCALE);
if (profanityToken) {
return toIssue(
rule.path,
`${rule.path}.profanity`,
`${rule.label} contains blocked language.`
);
}
return true;
}
var editableProfileNameShape = {
firstName: import_schema24.field.string().validator(
(value) => validateProfileTextField(value, {
path: "name.firstName",
label: "First name",
maxLength: 64,
required: true,
format: "name"
})
),
middleName: import_schema24.field.string().optional().validator(
(value) => validateProfileTextField(value, {
path: "name.middleName",
label: "Middle name",
maxLength: 64,
required: false,
format: "name"
})
),
lastName: import_schema24.field.string().validator(
(value) => validateProfileTextField(value, {
path: "name.lastName",
label: "Last name",
maxLength: 64,
required: true,
format: "name"
})
),
displayName: import_schema24.field.string().validator(
(value) => validateProfileTextField(value, {
path: "name.displayName",
label: "Display name",
maxLength: 80,
required: true,
format: "name"
})
),
preferredDisplayOrder: import_schema24.field.string().enum([...Object.values(PreferredDisplayOrder)])
};
var editableUserProfileSchema = (0, import_schema24.createSchema)(
{
email: import_schema24.field.string().validator(
(value) => validateProfileTextField(value, {
path: "email",
label: "Email",
maxLength: 254,
required: true,
format: "email"
})
),
name: import_schema24.field.object(editableProfileNameShape),
emailPreferences: import_schema24.field.array(
import_schema24.field.string().enum([...Object.values(UserEmailPreferences)]).as()
).optional().as()
},
"userProfileEditable",
{
version: "1.0.0",
piiEnforcement: "strict",
table: ""
}
);
function validateEditableUserProfile(input) {
return editableUserProfileSchema.validate(input);
}
function mapEditableUserProfileValidationErrors(validation) {
const issues = (validation.issues ?? []).map(
normalizeValidationIssue
);
const fieldErrors = {};
const formErrors = [];
const seenMessages = new Set(issues.map((issue) => issue.message));
for (const issue of issues) {
if (issue.field) {
fieldErrors[issue.field] ??= issue.message;
continue;
}
formErrors.push(issue.message);
}
for (const rawError of validation.errors ?? []) {
const error = String(rawError);
if (seenMessages.has(error)) {
continue;
}
const issue = createLegacyIssueFromError(error);
if (!issue) {
formErrors.push(error);
continue;
}
issues.push(issue);
if (issue.field) {
fieldErrors[issue.field] ??= issue.message;
} else {
formErrors.push(issue.message);
}
}
return {
fieldErrors,
formErrors,
issues
};
}
// src/auth/authenticatedUser.ts
var import_schema24 = require("@plasius/schema");
var import_schema25 = require("@plasius/schema");
var AuthProvider = /* @__PURE__ */ ((AuthProvider2) => {

@@ -662,4 +929,4 @@ AuthProvider2["GOOGLE"] = "google";

var authenticatedUserShape = {
sub: import_schema24.field.string().description("Unique user identifier").version("1.0"),
name: import_schema24.field.string().description("User's full name").version("1.0").PID({
sub: import_schema25.field.string().description("Unique user identifier").version("1.0"),
name: import_schema25.field.string().description("User's full name").version("1.0").PID({
classification: "high",

@@ -670,3 +937,3 @@ action: "encrypt",

}),
email: import_schema24.field.string().optional().description("User's email address").version("1.0").PID({
email: import_schema25.field.string().optional().description("User's email address").version("1.0").PID({
classification: "high",

@@ -677,4 +944,4 @@ action: "encrypt",

}),
groups: import_schema24.field.array(
import_schema24.field.string().optional().description("User group").version("1.0").PID({
groups: import_schema25.field.array(
import_schema25.field.string().optional().description("User group").version("1.0").PID({
classification: "low",

@@ -691,5 +958,5 @@ action: "encrypt",

}),
provider: import_schema24.field.string().enum([...Object.values(AuthProvider)]).description("Authentication provider used by the user")
provider: import_schema25.field.string().enum([...Object.values(AuthProvider)]).description("Authentication provider used by the user")
};
var authenticatedUserSchema = (0, import_schema24.createSchema)(
var authenticatedUserSchema = (0, import_schema25.createSchema)(
authenticatedUserShape,

@@ -705,9 +972,9 @@ "AuthenticatedUser",

// src/translations/translatable.ts
var import_schema25 = require("@plasius/schema");
var translatableSchema = (0, import_schema25.createSchema)(
var import_schema26 = require("@plasius/schema");
var translatableSchema = (0, import_schema26.createSchema)(
{
index: import_schema25.field.string().required().immutable().description("Unique string index for the translation"),
text: import_schema25.field.string().optional().validator(import_schema25.validateRichText),
translated: import_schema25.field.string().required().validator(import_schema25.validateRichText),
context: import_schema25.field.string().optional().validator(import_schema25.validateSafeText)
index: import_schema26.field.string().required().immutable().description("Unique string index for the translation"),
text: import_schema26.field.string().optional().validator(import_schema26.validateRichText),
translated: import_schema26.field.string().required().validator(import_schema26.validateRichText),
context: import_schema26.field.string().optional().validator(import_schema26.validateSafeText)
},

@@ -723,8 +990,8 @@ "translatable",

// src/translations/supported.languages.ts
var import_schema26 = require("@plasius/schema");
var supportedLanguagesSchema = (0, import_schema26.createSchema)(
var import_schema27 = require("@plasius/schema");
var supportedLanguagesSchema = (0, import_schema27.createSchema)(
{
code: import_schema26.field.string().description("Language code, e.g. 'en', 'fr-FR'").immutable(),
label: import_schema26.field.string().description("Human-readable name for the language").immutable(),
direction: import_schema26.field.string().enum(["ltr", "rtl"]).description("Text direction").immutable()
code: import_schema27.field.string().description("Language code, e.g. 'en', 'fr-FR'").immutable(),
label: import_schema27.field.string().description("Human-readable name for the language").immutable(),
direction: import_schema27.field.string().enum(["ltr", "rtl"]).description("Text direction").immutable()
},

@@ -738,2 +1005,4 @@ "supportedLanguages"

EntityTypes,
PROFILE_DEFAULT_PROFANITY_LOCALE,
PROFILE_PROFANITY_SUPPORTED_LOCALES,
PreferredDisplayOrder,

@@ -754,2 +1023,3 @@ Role,

baseEntityShape,
editableUserProfileSchema,
featureFlagEntitySchema,

@@ -760,2 +1030,3 @@ featureFlagEntityShape,

isValidEntityType,
mapEditableUserProfileValidationErrors,
modelAssetEntitySchema,

@@ -774,2 +1045,3 @@ objectAssetEntitySchema,

validateAssetSchema,
validateEditableUserProfile,
validateFeatureFlagValue,

@@ -776,0 +1048,0 @@ validateSettingValue

import * as _plasius_schema from '@plasius/schema';
import { Infer, SchemaShape } from '@plasius/schema';
import { Infer, SchemaShape, ValidationIssue, ValidationResult } from '@plasius/schema';

@@ -163,2 +163,34 @@ declare const baseEntityShape: SchemaShape;

declare const PROFILE_PROFANITY_SUPPORTED_LOCALES: readonly string[];
declare const PROFILE_DEFAULT_PROFANITY_LOCALE = "en";
type EditableUserProfileFieldName = "name.firstName" | "name.middleName" | "name.lastName" | "name.displayName" | "name.preferredDisplayOrder" | "email" | "emailPreferences";
type EditableUserProfileFieldErrors = Partial<Record<EditableUserProfileFieldName, string>>;
interface EditableUserProfileValidationIssue extends ValidationIssue {
field?: EditableUserProfileFieldName;
}
declare const editableUserProfileSchema: _plasius_schema.Schema<{
email: _plasius_schema.FieldBuilder<string, string>;
name: _plasius_schema.FieldBuilder<{
firstName: _plasius_schema.FieldBuilder<string, string>;
middleName: _plasius_schema.FieldBuilder<string, string>;
lastName: _plasius_schema.FieldBuilder<string, string>;
displayName: _plasius_schema.FieldBuilder<string, string>;
preferredDisplayOrder: _plasius_schema.FieldBuilder<PreferredDisplayOrder, PreferredDisplayOrder>;
}, {
firstName: _plasius_schema.FieldBuilder<string, string>;
middleName: _plasius_schema.FieldBuilder<string, string>;
lastName: _plasius_schema.FieldBuilder<string, string>;
displayName: _plasius_schema.FieldBuilder<string, string>;
preferredDisplayOrder: _plasius_schema.FieldBuilder<PreferredDisplayOrder, PreferredDisplayOrder>;
}>;
emailPreferences: _plasius_schema.FieldBuilder<UserEmailPreferences[], unknown>;
}>;
type EditableUserProfile = Infer<(typeof editableUserProfileSchema)["_shape"]>;
declare function validateEditableUserProfile(input: unknown): ValidationResult<EditableUserProfile>;
declare function mapEditableUserProfileValidationErrors(validation: Pick<ValidationResult<EditableUserProfile>, "errors" | "issues">): {
fieldErrors: EditableUserProfileFieldErrors;
formErrors: string[];
issues: EditableUserProfileValidationIssue[];
};
declare const assetEntityShape: SchemaShape;

@@ -269,2 +301,2 @@ declare const assetEntitySchema: _plasius_schema.Schema<SchemaShape>;

export { type AnimationComponent, type AnyAssetEntity, type AssetEntity, type AudioAssetEntity, AuthProvider, type AuthenticatedUser, type BaseComponent, type BaseEntity, ComponentTypes, EntityTypes, type FeatureFlagEntity, type ImageAssetEntity, type ModelAssetEntity, type ObjectAssetEntity, type PermissionsEntity, type PhysicsComponent, PreferredDisplayOrder, Role, type RoleEntity, Scope, type SettingsEntity, type ShadowComponent, type SupportedLanguage, type Translatable, type UserAvatarEntity, UserEmailPreferences, type UserEntity, type UserName, UserNotificationPreferences, animationComponentSchema, assetEntitySchema, assetEntityShape, audioAssetEntitySchema, authenticatedUserSchema, authenticatedUserShape, baseComponentSchema, baseComponentShape, baseEntitySchema, baseEntityShape, featureFlagEntitySchema, featureFlagEntityShape, imageAssetEntitySchema, isValidAzureTableKey, isValidEntityType, modelAssetEntitySchema, objectAssetEntitySchema, permissionsEntitySchema, physicsComponentSchema, roleEntitySchema, settingsEntitySchema, shadowComponentSchema, supportedLanguagesSchema, translatableSchema, userAvatarSchema, userEntitySchema, userNameSchema, validateAssetSchema, validateFeatureFlagValue, validateSettingValue };
export { type AnimationComponent, type AnyAssetEntity, type AssetEntity, type AudioAssetEntity, AuthProvider, type AuthenticatedUser, type BaseComponent, type BaseEntity, ComponentTypes, type EditableUserProfile, type EditableUserProfileFieldErrors, type EditableUserProfileFieldName, type EditableUserProfileValidationIssue, EntityTypes, type FeatureFlagEntity, type ImageAssetEntity, type ModelAssetEntity, type ObjectAssetEntity, PROFILE_DEFAULT_PROFANITY_LOCALE, PROFILE_PROFANITY_SUPPORTED_LOCALES, type PermissionsEntity, type PhysicsComponent, PreferredDisplayOrder, Role, type RoleEntity, Scope, type SettingsEntity, type ShadowComponent, type SupportedLanguage, type Translatable, type UserAvatarEntity, UserEmailPreferences, type UserEntity, type UserName, UserNotificationPreferences, animationComponentSchema, assetEntitySchema, assetEntityShape, audioAssetEntitySchema, authenticatedUserSchema, authenticatedUserShape, baseComponentSchema, baseComponentShape, baseEntitySchema, baseEntityShape, editableUserProfileSchema, featureFlagEntitySchema, featureFlagEntityShape, imageAssetEntitySchema, isValidAzureTableKey, isValidEntityType, mapEditableUserProfileValidationErrors, modelAssetEntitySchema, objectAssetEntitySchema, permissionsEntitySchema, physicsComponentSchema, roleEntitySchema, settingsEntitySchema, shadowComponentSchema, supportedLanguagesSchema, translatableSchema, userAvatarSchema, userEntitySchema, userNameSchema, validateAssetSchema, validateEditableUserProfile, validateFeatureFlagValue, validateSettingValue };
import * as _plasius_schema from '@plasius/schema';
import { Infer, SchemaShape } from '@plasius/schema';
import { Infer, SchemaShape, ValidationIssue, ValidationResult } from '@plasius/schema';

@@ -163,2 +163,34 @@ declare const baseEntityShape: SchemaShape;

declare const PROFILE_PROFANITY_SUPPORTED_LOCALES: readonly string[];
declare const PROFILE_DEFAULT_PROFANITY_LOCALE = "en";
type EditableUserProfileFieldName = "name.firstName" | "name.middleName" | "name.lastName" | "name.displayName" | "name.preferredDisplayOrder" | "email" | "emailPreferences";
type EditableUserProfileFieldErrors = Partial<Record<EditableUserProfileFieldName, string>>;
interface EditableUserProfileValidationIssue extends ValidationIssue {
field?: EditableUserProfileFieldName;
}
declare const editableUserProfileSchema: _plasius_schema.Schema<{
email: _plasius_schema.FieldBuilder<string, string>;
name: _plasius_schema.FieldBuilder<{
firstName: _plasius_schema.FieldBuilder<string, string>;
middleName: _plasius_schema.FieldBuilder<string, string>;
lastName: _plasius_schema.FieldBuilder<string, string>;
displayName: _plasius_schema.FieldBuilder<string, string>;
preferredDisplayOrder: _plasius_schema.FieldBuilder<PreferredDisplayOrder, PreferredDisplayOrder>;
}, {
firstName: _plasius_schema.FieldBuilder<string, string>;
middleName: _plasius_schema.FieldBuilder<string, string>;
lastName: _plasius_schema.FieldBuilder<string, string>;
displayName: _plasius_schema.FieldBuilder<string, string>;
preferredDisplayOrder: _plasius_schema.FieldBuilder<PreferredDisplayOrder, PreferredDisplayOrder>;
}>;
emailPreferences: _plasius_schema.FieldBuilder<UserEmailPreferences[], unknown>;
}>;
type EditableUserProfile = Infer<(typeof editableUserProfileSchema)["_shape"]>;
declare function validateEditableUserProfile(input: unknown): ValidationResult<EditableUserProfile>;
declare function mapEditableUserProfileValidationErrors(validation: Pick<ValidationResult<EditableUserProfile>, "errors" | "issues">): {
fieldErrors: EditableUserProfileFieldErrors;
formErrors: string[];
issues: EditableUserProfileValidationIssue[];
};
declare const assetEntityShape: SchemaShape;

@@ -269,2 +301,2 @@ declare const assetEntitySchema: _plasius_schema.Schema<SchemaShape>;

export { type AnimationComponent, type AnyAssetEntity, type AssetEntity, type AudioAssetEntity, AuthProvider, type AuthenticatedUser, type BaseComponent, type BaseEntity, ComponentTypes, EntityTypes, type FeatureFlagEntity, type ImageAssetEntity, type ModelAssetEntity, type ObjectAssetEntity, type PermissionsEntity, type PhysicsComponent, PreferredDisplayOrder, Role, type RoleEntity, Scope, type SettingsEntity, type ShadowComponent, type SupportedLanguage, type Translatable, type UserAvatarEntity, UserEmailPreferences, type UserEntity, type UserName, UserNotificationPreferences, animationComponentSchema, assetEntitySchema, assetEntityShape, audioAssetEntitySchema, authenticatedUserSchema, authenticatedUserShape, baseComponentSchema, baseComponentShape, baseEntitySchema, baseEntityShape, featureFlagEntitySchema, featureFlagEntityShape, imageAssetEntitySchema, isValidAzureTableKey, isValidEntityType, modelAssetEntitySchema, objectAssetEntitySchema, permissionsEntitySchema, physicsComponentSchema, roleEntitySchema, settingsEntitySchema, shadowComponentSchema, supportedLanguagesSchema, translatableSchema, userAvatarSchema, userEntitySchema, userNameSchema, validateAssetSchema, validateFeatureFlagValue, validateSettingValue };
export { type AnimationComponent, type AnyAssetEntity, type AssetEntity, type AudioAssetEntity, AuthProvider, type AuthenticatedUser, type BaseComponent, type BaseEntity, ComponentTypes, type EditableUserProfile, type EditableUserProfileFieldErrors, type EditableUserProfileFieldName, type EditableUserProfileValidationIssue, EntityTypes, type FeatureFlagEntity, type ImageAssetEntity, type ModelAssetEntity, type ObjectAssetEntity, PROFILE_DEFAULT_PROFANITY_LOCALE, PROFILE_PROFANITY_SUPPORTED_LOCALES, type PermissionsEntity, type PhysicsComponent, PreferredDisplayOrder, Role, type RoleEntity, Scope, type SettingsEntity, type ShadowComponent, type SupportedLanguage, type Translatable, type UserAvatarEntity, UserEmailPreferences, type UserEntity, type UserName, UserNotificationPreferences, animationComponentSchema, assetEntitySchema, assetEntityShape, audioAssetEntitySchema, authenticatedUserSchema, authenticatedUserShape, baseComponentSchema, baseComponentShape, baseEntitySchema, baseEntityShape, editableUserProfileSchema, featureFlagEntitySchema, featureFlagEntityShape, imageAssetEntitySchema, isValidAzureTableKey, isValidEntityType, mapEditableUserProfileValidationErrors, modelAssetEntitySchema, objectAssetEntitySchema, permissionsEntitySchema, physicsComponentSchema, roleEntitySchema, settingsEntitySchema, shadowComponentSchema, supportedLanguagesSchema, translatableSchema, userAvatarSchema, userEntitySchema, userNameSchema, validateAssetSchema, validateEditableUserProfile, validateFeatureFlagValue, validateSettingValue };

@@ -610,4 +610,271 @@ // src/base.entity.ts

// src/user/profile.validation.ts
import {
createSchema as createSchema15,
field as field18,
validateEmail as validateEmail2,
validateName as validateName2
} from "@plasius/schema";
var PROFILE_PROFANITY_LEXICON = {
en: [
"arse",
"asshole",
"bastard",
"bitch",
"bollocks",
"bullshit",
"cunt",
"damn",
"fuck",
"motherfucker",
"shit",
"twat"
]
};
var PROFILE_PROFANITY_SUPPORTED_LOCALES = Object.freeze(
Object.keys(PROFILE_PROFANITY_LEXICON)
);
var PROFILE_DEFAULT_PROFANITY_LOCALE = "en";
var EDITABLE_PROFILE_FIELD_NAMES = /* @__PURE__ */ new Set([
"name.firstName",
"name.middleName",
"name.lastName",
"name.displayName",
"name.preferredDisplayOrder",
"email",
"emailPreferences"
]);
var EDITABLE_PROFILE_FIELD_LABELS = {
"name.firstName": "First name",
"name.middleName": "Middle name",
"name.lastName": "Last name",
"name.displayName": "Display name",
"name.preferredDisplayOrder": "Preferred name display",
email: "Email",
emailPreferences: "Email preferences"
};
function toIssue(path, code, message) {
return {
path,
code,
message
};
}
function normalizeToken(value) {
return value.trim().toLowerCase();
}
function isEditableUserProfileFieldName(value) {
return EDITABLE_PROFILE_FIELD_NAMES.has(value);
}
function readFieldNameFromTextError(error) {
const fieldPath = error.match(/:\s*([a-zA-Z]+(?:\.[a-zA-Z]+)*)$/)?.[1] ?? "";
if (!isEditableUserProfileFieldName(fieldPath)) {
return null;
}
return fieldPath;
}
function createLegacyIssueFromError(error) {
const field22 = readFieldNameFromTextError(error);
if (!field22) {
return null;
}
if (error.startsWith("Missing required field:")) {
return {
field: field22,
path: field22,
code: `${field22}.required`,
message: `${EDITABLE_PROFILE_FIELD_LABELS[field22]} is required.`
};
}
if (error.startsWith("Field is immutable:")) {
return {
field: field22,
path: field22,
code: `${field22}.immutable`,
message: `${EDITABLE_PROFILE_FIELD_LABELS[field22]} cannot be changed.`
};
}
if (error.startsWith("Field ")) {
return {
field: field22,
path: field22,
code: `${field22}.invalid_type`,
message: error
};
}
return {
field: field22,
path: field22,
code: `${field22}.invalid_value`,
message: error
};
}
function normalizeValidationIssue(issue) {
const field22 = isEditableUserProfileFieldName(issue.path) ? issue.path : void 0;
return {
...issue,
field: field22
};
}
function findProfanityMatch(value, locale) {
const lexicon = PROFILE_PROFANITY_LEXICON[locale] ?? PROFILE_PROFANITY_LEXICON[PROFILE_DEFAULT_PROFANITY_LOCALE];
const normalizedValue = ` ${normalizeToken(value).replace(/[^a-z0-9]+/g, " ")} `;
for (const token of lexicon) {
if (normalizedValue.includes(` ${token} `)) {
return token;
}
}
return null;
}
function validateProfileTextField(value, rule) {
if (typeof value !== "string") {
return true;
}
const normalizedValue = value.trim();
if (normalizedValue.length === 0) {
if (!rule.required) {
return true;
}
return toIssue(
rule.path,
`${rule.path}.required`,
`${rule.label} is required.`
);
}
if (normalizedValue.length > rule.maxLength) {
return toIssue(
rule.path,
`${rule.path}.too_long`,
`${rule.label} must be ${rule.maxLength} characters or fewer.`
);
}
if (rule.format === "name" && !validateName2(normalizedValue)) {
return toIssue(
rule.path,
`${rule.path}.invalid_format`,
`${rule.label} contains unsupported characters.`
);
}
if (rule.format === "email" && !validateEmail2(normalizedValue)) {
return toIssue(
rule.path,
`${rule.path}.invalid_format`,
`${rule.label} must be a valid email address.`
);
}
const profanityToken = findProfanityMatch(normalizedValue, PROFILE_DEFAULT_PROFANITY_LOCALE);
if (profanityToken) {
return toIssue(
rule.path,
`${rule.path}.profanity`,
`${rule.label} contains blocked language.`
);
}
return true;
}
var editableProfileNameShape = {
firstName: field18.string().validator(
(value) => validateProfileTextField(value, {
path: "name.firstName",
label: "First name",
maxLength: 64,
required: true,
format: "name"
})
),
middleName: field18.string().optional().validator(
(value) => validateProfileTextField(value, {
path: "name.middleName",
label: "Middle name",
maxLength: 64,
required: false,
format: "name"
})
),
lastName: field18.string().validator(
(value) => validateProfileTextField(value, {
path: "name.lastName",
label: "Last name",
maxLength: 64,
required: true,
format: "name"
})
),
displayName: field18.string().validator(
(value) => validateProfileTextField(value, {
path: "name.displayName",
label: "Display name",
maxLength: 80,
required: true,
format: "name"
})
),
preferredDisplayOrder: field18.string().enum([...Object.values(PreferredDisplayOrder)])
};
var editableUserProfileSchema = createSchema15(
{
email: field18.string().validator(
(value) => validateProfileTextField(value, {
path: "email",
label: "Email",
maxLength: 254,
required: true,
format: "email"
})
),
name: field18.object(editableProfileNameShape),
emailPreferences: field18.array(
field18.string().enum([...Object.values(UserEmailPreferences)]).as()
).optional().as()
},
"userProfileEditable",
{
version: "1.0.0",
piiEnforcement: "strict",
table: ""
}
);
function validateEditableUserProfile(input) {
return editableUserProfileSchema.validate(input);
}
function mapEditableUserProfileValidationErrors(validation) {
const issues = (validation.issues ?? []).map(
normalizeValidationIssue
);
const fieldErrors = {};
const formErrors = [];
const seenMessages = new Set(issues.map((issue) => issue.message));
for (const issue of issues) {
if (issue.field) {
fieldErrors[issue.field] ??= issue.message;
continue;
}
formErrors.push(issue.message);
}
for (const rawError of validation.errors ?? []) {
const error = String(rawError);
if (seenMessages.has(error)) {
continue;
}
const issue = createLegacyIssueFromError(error);
if (!issue) {
formErrors.push(error);
continue;
}
issues.push(issue);
if (issue.field) {
fieldErrors[issue.field] ??= issue.message;
} else {
formErrors.push(issue.message);
}
}
return {
fieldErrors,
formErrors,
issues
};
}
// src/auth/authenticatedUser.ts
import { createSchema as createSchema15, field as field18 } from "@plasius/schema";
import { createSchema as createSchema16, field as field19 } from "@plasius/schema";
var AuthProvider = /* @__PURE__ */ ((AuthProvider2) => {

@@ -621,4 +888,4 @@ AuthProvider2["GOOGLE"] = "google";

var authenticatedUserShape = {
sub: field18.string().description("Unique user identifier").version("1.0"),
name: field18.string().description("User's full name").version("1.0").PID({
sub: field19.string().description("Unique user identifier").version("1.0"),
name: field19.string().description("User's full name").version("1.0").PID({
classification: "high",

@@ -629,3 +896,3 @@ action: "encrypt",

}),
email: field18.string().optional().description("User's email address").version("1.0").PID({
email: field19.string().optional().description("User's email address").version("1.0").PID({
classification: "high",

@@ -636,4 +903,4 @@ action: "encrypt",

}),
groups: field18.array(
field18.string().optional().description("User group").version("1.0").PID({
groups: field19.array(
field19.string().optional().description("User group").version("1.0").PID({
classification: "low",

@@ -650,5 +917,5 @@ action: "encrypt",

}),
provider: field18.string().enum([...Object.values(AuthProvider)]).description("Authentication provider used by the user")
provider: field19.string().enum([...Object.values(AuthProvider)]).description("Authentication provider used by the user")
};
var authenticatedUserSchema = createSchema15(
var authenticatedUserSchema = createSchema16(
authenticatedUserShape,

@@ -665,13 +932,13 @@ "AuthenticatedUser",

import {
createSchema as createSchema16,
field as field19,
createSchema as createSchema17,
field as field20,
validateRichText,
validateSafeText as validateSafeText5
} from "@plasius/schema";
var translatableSchema = createSchema16(
var translatableSchema = createSchema17(
{
index: field19.string().required().immutable().description("Unique string index for the translation"),
text: field19.string().optional().validator(validateRichText),
translated: field19.string().required().validator(validateRichText),
context: field19.string().optional().validator(validateSafeText5)
index: field20.string().required().immutable().description("Unique string index for the translation"),
text: field20.string().optional().validator(validateRichText),
translated: field20.string().required().validator(validateRichText),
context: field20.string().optional().validator(validateSafeText5)
},

@@ -687,8 +954,8 @@ "translatable",

// src/translations/supported.languages.ts
import { createSchema as createSchema17, field as field20 } from "@plasius/schema";
var supportedLanguagesSchema = createSchema17(
import { createSchema as createSchema18, field as field21 } from "@plasius/schema";
var supportedLanguagesSchema = createSchema18(
{
code: field20.string().description("Language code, e.g. 'en', 'fr-FR'").immutable(),
label: field20.string().description("Human-readable name for the language").immutable(),
direction: field20.string().enum(["ltr", "rtl"]).description("Text direction").immutable()
code: field21.string().description("Language code, e.g. 'en', 'fr-FR'").immutable(),
label: field21.string().description("Human-readable name for the language").immutable(),
direction: field21.string().enum(["ltr", "rtl"]).description("Text direction").immutable()
},

@@ -701,2 +968,4 @@ "supportedLanguages"

EntityTypes,
PROFILE_DEFAULT_PROFANITY_LOCALE,
PROFILE_PROFANITY_SUPPORTED_LOCALES,
PreferredDisplayOrder,

@@ -717,2 +986,3 @@ Role,

baseEntityShape,
editableUserProfileSchema,
featureFlagEntitySchema,

@@ -723,2 +993,3 @@ featureFlagEntityShape,

isValidEntityType,
mapEditableUserProfileValidationErrors,
modelAssetEntitySchema,

@@ -737,2 +1008,3 @@ objectAssetEntitySchema,

validateAssetSchema,
validateEditableUserProfile,
validateFeatureFlagValue,

@@ -739,0 +1011,0 @@ validateSettingValue

+5
-2
{
"name": "@plasius/entity-manager",
"version": "1.0.12",
"version": "1.0.13",
"description": "Entity definition & validation helpers for Plasius ecosystem",

@@ -19,3 +19,3 @@ "type": "module",

"dependencies": {
"@plasius/schema": "^1.2.6"
"@plasius/schema": "^1.2.7"
},

@@ -74,3 +74,6 @@ "exports": {

"access": "public"
},
"engines": {
"node": ">=24"
}
}

@@ -29,2 +29,13 @@ export {

export { type UserName, userNameSchema } from "./user.name.js";
export { type UserName, userNameSchema } from "./user.name.js";
export {
PROFILE_DEFAULT_PROFANITY_LOCALE,
PROFILE_PROFANITY_SUPPORTED_LOCALES,
editableUserProfileSchema,
type EditableUserProfileFieldErrors,
type EditableUserProfileFieldName,
type EditableUserProfileValidationIssue,
type EditableUserProfile,
mapEditableUserProfileValidationErrors,
validateEditableUserProfile,
} from "./profile.validation.js";

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display