@careflair/common 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AvailabilitiesSchemaZod = void 0;
4
+ const zod_1 = require("zod");
5
+ // Regular expression for both 12-hour (with AM/PM) and 24-hour format
6
+ const timeRegex = /^(0?[1-9]|1[0-2]):([0-5][0-9]) (AM|PM)$/i; // 12-hour format with AM/PM
7
+ const timeRegex24Hour = /^(2[0-3]|[01]?[0-9]):([0-5][0-9])$/; // 24-hour format (00:00 to 23:59)
8
+ // Function to convert time string to minutes
9
+ const convertToMinutes = (time) => {
10
+ let hours, minutes;
11
+ // Check if it's a 12-hour format with AM/PM
12
+ if (timeRegex.test(time)) {
13
+ const [timeString, period] = time.split(' ');
14
+ const [hour, minute] = timeString.split(':').map(Number);
15
+ hours = hour;
16
+ minutes = minute;
17
+ if (period.toLowerCase() === 'pm' && hours !== 12) {
18
+ hours += 12; // Convert PM to 24-hour format
19
+ }
20
+ else if (period.toLowerCase() === 'am' && hours === 12) {
21
+ hours = 0; // Handle 12 AM as 0 hours
22
+ }
23
+ }
24
+ else if (timeRegex24Hour.test(time)) {
25
+ // Handle 24-hour format (e.g., "14:00")
26
+ const [hour, minute] = time.split(':').map(Number);
27
+ hours = hour;
28
+ minutes = minute;
29
+ }
30
+ else {
31
+ throw new Error('Invalid time format');
32
+ }
33
+ return hours * 60 + minutes; // Convert to total minutes since midnight
34
+ };
35
+ // Shift schema with time validation
36
+ const ShiftSchema = zod_1.z.object({
37
+ startTime: zod_1.z.string().refine(time => timeRegex.test(time) || timeRegex24Hour.test(time), 'Invalid start time format'),
38
+ endTime: zod_1.z.string().refine(time => timeRegex.test(time) || timeRegex24Hour.test(time), 'Invalid end time format'),
39
+ }).refine((data) => {
40
+ // Convert times to minutes for comparison
41
+ const startInMinutes = convertToMinutes(data.startTime);
42
+ const endInMinutes = convertToMinutes(data.endTime);
43
+ return startInMinutes < endInMinutes; // Ensure start time is before end time
44
+ }, {
45
+ message: 'Start time must be before end time',
46
+ path: ['startTime', 'endTime'],
47
+ });
48
+ // Availability schema with shift overlap validation
49
+ const AvailabilitySchemaZod = zod_1.z.object({
50
+ day: zod_1.z.string(),
51
+ isEnabled: zod_1.z.boolean().default(true),
52
+ shifts: zod_1.z.array(ShiftSchema).refine(shifts => {
53
+ // Sort shifts by start time (in minutes)
54
+ const sortedShifts = shifts.sort((a, b) => {
55
+ const startA = convertToMinutes(a.startTime);
56
+ const startB = convertToMinutes(b.startTime);
57
+ return startA - startB;
58
+ });
59
+ // Ensure no overlapping shifts
60
+ for (let i = 0; i < sortedShifts.length - 1; i++) {
61
+ const endCurrent = convertToMinutes(sortedShifts[i].endTime);
62
+ const startNext = convertToMinutes(sortedShifts[i + 1].startTime);
63
+ if (endCurrent > startNext) {
64
+ return false;
65
+ }
66
+ }
67
+ return true;
68
+ }, {
69
+ message: 'Shifts cannot overlap',
70
+ }),
71
+ });
72
+ // Schema for the entire availability array (the list of availabilities)
73
+ exports.AvailabilitiesSchemaZod = zod_1.z.array(AvailabilitySchemaZod).refine(availabilities => {
74
+ const days = availabilities.map(a => a.day);
75
+ const uniqueDays = new Set(days);
76
+ return days.length === uniqueDays.size; // Ensure no duplicate days
77
+ }, {
78
+ message: 'There should be no duplicate days',
79
+ });
@@ -0,0 +1,6 @@
1
+ import { z } from 'zod';
2
+ import { CategoryEnum, SupportWorkerService } from '../enums';
3
+ export declare const ServicesSchema: z.ZodEffects<z.ZodOptional<z.ZodArray<z.ZodNativeEnum<typeof CategoryEnum>, "many">>, CategoryEnum[] | undefined, CategoryEnum[] | undefined>;
4
+ export declare const validateServices: (services?: string[]) => boolean;
5
+ export declare const SupportWorkerServicesSchema: z.ZodEffects<z.ZodOptional<z.ZodArray<z.ZodNativeEnum<typeof SupportWorkerService>, "many">>, SupportWorkerService[] | undefined, SupportWorkerService[] | undefined>;
6
+ export declare const validateSupportWorkerServices: (services?: string[]) => boolean;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateSupportWorkerServices = exports.SupportWorkerServicesSchema = exports.validateServices = exports.ServicesSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ const enums_1 = require("../enums");
6
+ exports.ServicesSchema = zod_1.z
7
+ .array(zod_1.z.nativeEnum(enums_1.CategoryEnum))
8
+ .optional()
9
+ .refine((services) => !services || services.every((service) => Object.values(enums_1.CategoryEnum).includes(service)), { message: 'Invalid services provided.' });
10
+ const validateServices = (services) => {
11
+ const result = exports.ServicesSchema.safeParse(services);
12
+ return result.success;
13
+ };
14
+ exports.validateServices = validateServices;
15
+ exports.SupportWorkerServicesSchema = zod_1.z
16
+ .array(zod_1.z.nativeEnum(enums_1.SupportWorkerService))
17
+ .optional()
18
+ .refine((services) => !services || services.every((service) => Object.values(enums_1.SupportWorkerService).includes(service)), { message: 'Invalid services provided.' });
19
+ const validateSupportWorkerServices = (services) => {
20
+ const result = exports.SupportWorkerServicesSchema.safeParse(services);
21
+ return result.success;
22
+ };
23
+ exports.validateSupportWorkerServices = validateSupportWorkerServices;
@@ -0,0 +1,38 @@
1
+ import { z } from "zod";
2
+ export declare const EducationAndTrainingSchema: z.ZodEffects<z.ZodObject<{
3
+ school: z.ZodString;
4
+ type: z.ZodString;
5
+ degree: z.ZodString;
6
+ startDate: z.ZodDate;
7
+ endDate: z.ZodOptional<z.ZodDate>;
8
+ isCurrentlyStudying: z.ZodDefault<z.ZodBoolean>;
9
+ }, "strip", z.ZodTypeAny, {
10
+ type: string;
11
+ school: string;
12
+ degree: string;
13
+ startDate: Date;
14
+ isCurrentlyStudying: boolean;
15
+ endDate?: Date | undefined;
16
+ }, {
17
+ type: string;
18
+ school: string;
19
+ degree: string;
20
+ startDate: Date;
21
+ endDate?: Date | undefined;
22
+ isCurrentlyStudying?: boolean | undefined;
23
+ }>, {
24
+ type: string;
25
+ school: string;
26
+ degree: string;
27
+ startDate: Date;
28
+ isCurrentlyStudying: boolean;
29
+ endDate?: Date | undefined;
30
+ }, {
31
+ type: string;
32
+ school: string;
33
+ degree: string;
34
+ startDate: Date;
35
+ endDate?: Date | undefined;
36
+ isCurrentlyStudying?: boolean | undefined;
37
+ }>;
38
+ export type EducationAndTrainingInputValidation = z.infer<typeof EducationAndTrainingSchema>;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EducationAndTrainingSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ const limits_1 = require("../constants/limits");
6
+ exports.EducationAndTrainingSchema = zod_1.z
7
+ .object({
8
+ school: zod_1.z
9
+ .string()
10
+ .min(1, "School is required")
11
+ .max(limits_1.EDUTRAINING_INSTITUTION_MAX_LENGTH, `School name must be at most ${limits_1.EDUTRAINING_INSTITUTION_MAX_LENGTH} characters long`),
12
+ type: zod_1.z.string().min(1, "Type of education is required"),
13
+ degree: zod_1.z
14
+ .string()
15
+ .min(1, "Degree is required")
16
+ .max(limits_1.EDUTRAINING_DEGREE_MAX_LENGTH, `Degree must be at most ${limits_1.EDUTRAINING_DEGREE_MAX_LENGTH} characters long`),
17
+ startDate: zod_1.z.date(),
18
+ endDate: zod_1.z.date().optional(),
19
+ isCurrentlyStudying: zod_1.z.boolean().default(false),
20
+ })
21
+ .refine((data) => {
22
+ if (data.endDate) {
23
+ return !data.isCurrentlyStudying;
24
+ }
25
+ return data.isCurrentlyStudying || data.endDate !== undefined;
26
+ }, {
27
+ message: "If there is an endDate, is Currently Studying must be false. If is Currently Studying is false, endDate must be provided.",
28
+ path: ["endDate", "isCurrentlyStudying"],
29
+ });
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+ export declare const HourlyRateInputZod: z.ZodObject<{
3
+ service: z.ZodString;
4
+ dayRates: z.ZodEffects<z.ZodArray<z.ZodObject<{
5
+ day: z.ZodString;
6
+ price: z.ZodNumber;
7
+ }, "strip", z.ZodTypeAny, {
8
+ day: string;
9
+ price: number;
10
+ }, {
11
+ day: string;
12
+ price: number;
13
+ }>, "many">, {
14
+ day: string;
15
+ price: number;
16
+ }[], {
17
+ day: string;
18
+ price: number;
19
+ }[]>;
20
+ isNegotiable: z.ZodOptional<z.ZodBoolean>;
21
+ }, "strip", z.ZodTypeAny, {
22
+ service: string;
23
+ dayRates: {
24
+ day: string;
25
+ price: number;
26
+ }[];
27
+ isNegotiable?: boolean | undefined;
28
+ }, {
29
+ service: string;
30
+ dayRates: {
31
+ day: string;
32
+ price: number;
33
+ }[];
34
+ isNegotiable?: boolean | undefined;
35
+ }>;
36
+ export type HourlyRateInputZodType = z.infer<typeof HourlyRateInputZod>;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HourlyRateInputZod = void 0;
4
+ const zod_1 = require("zod");
5
+ const limits_1 = require("../constants/limits");
6
+ // DayRateInput validation: Check for duplicate days in the input
7
+ const DayRateInputZod = zod_1.z.object({
8
+ day: zod_1.z.string(),
9
+ price: zod_1.z
10
+ .number()
11
+ .min(limits_1.MIN_HOURLY_RATE, `Hourly rate must be at least $${limits_1.MIN_HOURLY_RATE}`)
12
+ .max(limits_1.MAX_HOURLY_RATE, `Hourly rate must be at most $${limits_1.MAX_HOURLY_RATE}`),
13
+ });
14
+ // HourlyRateInput validation: Validate dayRates for duplicates
15
+ exports.HourlyRateInputZod = zod_1.z.object({
16
+ service: zod_1.z.string(),
17
+ dayRates: zod_1.z.array(DayRateInputZod).refine((data) => {
18
+ const days = data.map((dayRate) => dayRate.day);
19
+ const uniqueDays = new Set(days);
20
+ return days.length === uniqueDays.size; // Ensure no duplicate days
21
+ }, {
22
+ message: "There should be no duplicate days in dayRates",
23
+ }),
24
+ isNegotiable: zod_1.z.boolean().optional(),
25
+ });
@@ -0,0 +1,6 @@
1
+ export { UserRegistrationSchema, UserRegistrationInput } from './userValiationSchema';
2
+ export { EducationAndTrainingSchema, EducationAndTrainingInputValidation } from './educationSchemas';
3
+ export { WorkHistorySchema, WorkHistoryInputValidation } from './workHistorySchema';
4
+ export { AvailabilitiesSchemaZod, AvailabilityZodInput } from './availabilitySchemaValidation';
5
+ export { HourlyRateInputZod, HourlyRateInputZodType } from './hourlyRateSchemaValidation';
6
+ export { validateServices, ServicesSchema, SupportWorkerServicesSchema, validateSupportWorkerServices } from './businessServicesValidation';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateSupportWorkerServices = exports.SupportWorkerServicesSchema = exports.ServicesSchema = exports.validateServices = exports.HourlyRateInputZod = exports.AvailabilitiesSchemaZod = exports.WorkHistorySchema = exports.EducationAndTrainingSchema = exports.UserRegistrationSchema = void 0;
4
+ var userValiationSchema_1 = require("./userValiationSchema");
5
+ Object.defineProperty(exports, "UserRegistrationSchema", { enumerable: true, get: function () { return userValiationSchema_1.UserRegistrationSchema; } });
6
+ var educationSchemas_1 = require("./educationSchemas");
7
+ Object.defineProperty(exports, "EducationAndTrainingSchema", { enumerable: true, get: function () { return educationSchemas_1.EducationAndTrainingSchema; } });
8
+ var workHistorySchema_1 = require("./workHistorySchema");
9
+ Object.defineProperty(exports, "WorkHistorySchema", { enumerable: true, get: function () { return workHistorySchema_1.WorkHistorySchema; } });
10
+ var availabilitySchemaValidation_1 = require("./availabilitySchemaValidation");
11
+ Object.defineProperty(exports, "AvailabilitiesSchemaZod", { enumerable: true, get: function () { return availabilitySchemaValidation_1.AvailabilitiesSchemaZod; } });
12
+ var hourlyRateSchemaValidation_1 = require("./hourlyRateSchemaValidation");
13
+ Object.defineProperty(exports, "HourlyRateInputZod", { enumerable: true, get: function () { return hourlyRateSchemaValidation_1.HourlyRateInputZod; } });
14
+ var businessServicesValidation_1 = require("./businessServicesValidation");
15
+ Object.defineProperty(exports, "validateServices", { enumerable: true, get: function () { return businessServicesValidation_1.validateServices; } });
16
+ Object.defineProperty(exports, "ServicesSchema", { enumerable: true, get: function () { return businessServicesValidation_1.ServicesSchema; } });
17
+ Object.defineProperty(exports, "SupportWorkerServicesSchema", { enumerable: true, get: function () { return businessServicesValidation_1.SupportWorkerServicesSchema; } });
18
+ Object.defineProperty(exports, "validateSupportWorkerServices", { enumerable: true, get: function () { return businessServicesValidation_1.validateSupportWorkerServices; } });
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ export declare const UserRegistrationSchema: z.ZodObject<{
3
+ email: z.ZodEffects<z.ZodString, string, string>;
4
+ username: z.ZodString;
5
+ password: z.ZodString;
6
+ role: z.ZodEnum<["business", "provider", "participant", "support_person", "support_worker"]>;
7
+ fingerPrint: z.ZodOptional<z.ZodString>;
8
+ firstName: z.ZodString;
9
+ lastName: z.ZodString;
10
+ isSmallProvider: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
11
+ }, "strip", z.ZodTypeAny, {
12
+ role: "business" | "provider" | "participant" | "support_person" | "support_worker";
13
+ username: string;
14
+ email: string;
15
+ password: string;
16
+ firstName: string;
17
+ lastName: string;
18
+ isSmallProvider: boolean;
19
+ fingerPrint?: string | undefined;
20
+ }, {
21
+ role: "business" | "provider" | "participant" | "support_person" | "support_worker";
22
+ username: string;
23
+ email: string;
24
+ password: string;
25
+ firstName: string;
26
+ lastName: string;
27
+ fingerPrint?: string | undefined;
28
+ isSmallProvider?: boolean | undefined;
29
+ }>;
30
+ export type UserRegistrationInput = z.infer<typeof UserRegistrationSchema>;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UserRegistrationSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ const limits_1 = require("../constants/limits");
6
+ exports.UserRegistrationSchema = zod_1.z.object({
7
+ email: zod_1.z
8
+ .string()
9
+ .email()
10
+ .max(limits_1.EMAIL_MAX_LENGTH, `Email must be at most ${limits_1.EMAIL_MAX_LENGTH} characters long`)
11
+ .refine((email) => !email.includes("+"), {
12
+ message: "Email aliasing with '+' is not allowed",
13
+ }),
14
+ username: zod_1.z
15
+ .string()
16
+ .min(limits_1.USERNAME_MIN_LENGTH, `Username must be at least ${limits_1.USERNAME_MIN_LENGTH} characters long`)
17
+ .max(limits_1.USERNAME_MAX_LENGTH, `Username must be at most ${limits_1.USERNAME_MAX_LENGTH} characters long`),
18
+ password: zod_1.z
19
+ .string()
20
+ .min(limits_1.PASSWORD_MIN_LENGTH, `Password must be at least ${limits_1.PASSWORD_MIN_LENGTH} characters long`)
21
+ .max(limits_1.PASSWORD_MAX_LENGTH, `Password must be at most ${limits_1.PASSWORD_MAX_LENGTH} characters long`),
22
+ role: zod_1.z.enum(["business", "provider", "participant", "support_person", "support_worker"], {
23
+ message: "Role must be one of: business, provider, participant, support_person, support_worker",
24
+ }),
25
+ fingerPrint: zod_1.z.string().optional(),
26
+ firstName: zod_1.z
27
+ .string()
28
+ .min(limits_1.NAME_MIN_LENGTH, `First name must be at least ${limits_1.NAME_MIN_LENGTH} characters long`)
29
+ .max(limits_1.NAME_MAX_LENGTH, `First name must be at most ${limits_1.NAME_MAX_LENGTH} characters long`),
30
+ lastName: zod_1.z
31
+ .string()
32
+ .min(limits_1.NAME_MIN_LENGTH, `Last name must be at least ${limits_1.NAME_MIN_LENGTH} characters long`)
33
+ .max(limits_1.NAME_MAX_LENGTH, `Last name must be at most ${limits_1.NAME_MAX_LENGTH} characters long`),
34
+ isSmallProvider: zod_1.z.boolean().optional().default(false),
35
+ // businessName: z.string().optional(),
36
+ // entityTypeCode: z.string().optional(),
37
+ // abn: z.string().optional(),
38
+ });
@@ -0,0 +1,33 @@
1
+ import { z } from "zod";
2
+ export declare const WorkHistorySchema: z.ZodEffects<z.ZodObject<{
3
+ jobTitle: z.ZodString;
4
+ startDate: z.ZodDate;
5
+ endDate: z.ZodNullable<z.ZodOptional<z.ZodDate>>;
6
+ organisation: z.ZodString;
7
+ isCurrentlyWorking: z.ZodDefault<z.ZodBoolean>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ startDate: Date;
10
+ jobTitle: string;
11
+ organisation: string;
12
+ isCurrentlyWorking: boolean;
13
+ endDate?: Date | null | undefined;
14
+ }, {
15
+ startDate: Date;
16
+ jobTitle: string;
17
+ organisation: string;
18
+ endDate?: Date | null | undefined;
19
+ isCurrentlyWorking?: boolean | undefined;
20
+ }>, {
21
+ startDate: Date;
22
+ jobTitle: string;
23
+ organisation: string;
24
+ isCurrentlyWorking: boolean;
25
+ endDate?: Date | null | undefined;
26
+ }, {
27
+ startDate: Date;
28
+ jobTitle: string;
29
+ organisation: string;
30
+ endDate?: Date | null | undefined;
31
+ isCurrentlyWorking?: boolean | undefined;
32
+ }>;
33
+ export type WorkHistoryInputValidation = z.infer<typeof WorkHistorySchema>;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WorkHistorySchema = void 0;
4
+ const zod_1 = require("zod");
5
+ const limits_1 = require("../constants/limits");
6
+ exports.WorkHistorySchema = zod_1.z
7
+ .object({
8
+ jobTitle: zod_1.z
9
+ .string()
10
+ .min(1, "Job title is required")
11
+ .max(limits_1.WORK_HISTORY_TITLE_MAX_LENGTH, `Job title must be at most ${limits_1.WORK_HISTORY_TITLE_MAX_LENGTH} characters long`),
12
+ startDate: zod_1.z.date(),
13
+ endDate: zod_1.z.date().optional().nullable(),
14
+ organisation: zod_1.z
15
+ .string()
16
+ .min(1, "Organisation is required")
17
+ .max(limits_1.WORK_HISTORY_ORGANIZATION_MAX_LENGTH, `Organisation must be at most ${limits_1.WORK_HISTORY_ORGANIZATION_MAX_LENGTH} characters long`),
18
+ isCurrentlyWorking: zod_1.z.boolean().default(false),
19
+ })
20
+ .refine((data) => {
21
+ if (data.endDate) {
22
+ return !data.isCurrentlyWorking;
23
+ }
24
+ return data.isCurrentlyWorking || data.endDate !== undefined;
25
+ }, {
26
+ message: "If there is an endDate, is Currently Working must be false. If is Currently Working is false, endDate must be provided.",
27
+ path: ["endDate", "isCurrentlyWorking"],
28
+ });
@@ -0,0 +1,6 @@
1
+ type Option = {
2
+ label: string;
3
+ value: string;
4
+ };
5
+ export declare function enumToOptions<T extends Record<string, string>>(enumObj: T): Option[];
6
+ export {};
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enumToOptions = enumToOptions;
4
+ function enumToOptions(enumObj) {
5
+ return Object.values(enumObj).map((value) => ({
6
+ label: value, // Use the enum value for the label
7
+ value: value, // Use the enum value for the value
8
+ }));
9
+ }
@@ -0,0 +1,31 @@
1
+ export type VideoProvider = "youtube" | "vimeo" | "loom" | "tiktok";
2
+ export interface VideoValidationResult {
3
+ isValid: boolean;
4
+ provider?: VideoProvider;
5
+ error?: string;
6
+ }
7
+ /**
8
+ * Unified video link validation function
9
+ * @param url - The video URL to validate
10
+ * @param allowedProviders - Array of allowed video providers
11
+ * @returns VideoValidationResult object
12
+ */
13
+ export declare function validateVideoLink(url: string, allowedProviders: VideoProvider[]): VideoValidationResult;
14
+ /**
15
+ * Detect video provider from URL
16
+ * @param url - The video URL
17
+ * @returns VideoProvider or null if not detected
18
+ */
19
+ export declare function detectVideoProvider(url: string): VideoProvider | null;
20
+ /**
21
+ * Convenience function for YouTube and Vimeo only (commonly used)
22
+ * @param url - The video URL to validate
23
+ * @returns boolean
24
+ */
25
+ export declare function isValidYouTubeOrVimeoUrl(url: string): boolean;
26
+ /**
27
+ * Convenience function for community posts (all providers)
28
+ * @param url - The video URL to validate
29
+ * @returns boolean
30
+ */
31
+ export declare function isValidCommunityVideoUrl(url: string): boolean;
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateVideoLink = validateVideoLink;
4
+ exports.detectVideoProvider = detectVideoProvider;
5
+ exports.isValidYouTubeOrVimeoUrl = isValidYouTubeOrVimeoUrl;
6
+ exports.isValidCommunityVideoUrl = isValidCommunityVideoUrl;
7
+ /**
8
+ * Unified video link validation function
9
+ * @param url - The video URL to validate
10
+ * @param allowedProviders - Array of allowed video providers
11
+ * @returns VideoValidationResult object
12
+ */
13
+ function validateVideoLink(url, allowedProviders) {
14
+ if (!url || url.trim() === "") {
15
+ return { isValid: true }; // Empty URL is valid (optional field)
16
+ }
17
+ const trimmedUrl = url.trim();
18
+ // Validate URL format
19
+ if (!isValidURL(trimmedUrl)) {
20
+ return { isValid: false, error: "Invalid URL format" };
21
+ }
22
+ const provider = detectVideoProvider(trimmedUrl);
23
+ if (!provider) {
24
+ return {
25
+ isValid: false,
26
+ error: `Unsupported video provider. Supported: ${allowedProviders.join(", ")}`,
27
+ };
28
+ }
29
+ if (!allowedProviders.includes(provider)) {
30
+ return {
31
+ isValid: false,
32
+ error: `${provider} is not allowed. Supported: ${allowedProviders.join(", ")}`,
33
+ };
34
+ }
35
+ return { isValid: true, provider };
36
+ }
37
+ /**
38
+ * Detect video provider from URL
39
+ * @param url - The video URL
40
+ * @returns VideoProvider or null if not detected
41
+ */
42
+ function detectVideoProvider(url) {
43
+ const normalizedUrl = url.toLowerCase();
44
+ // YouTube detection
45
+ if (normalizedUrl.includes("youtube.com") ||
46
+ normalizedUrl.includes("youtu.be")) {
47
+ const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/.+/;
48
+ if (youtubeRegex.test(url)) {
49
+ return "youtube";
50
+ }
51
+ }
52
+ // Vimeo detection
53
+ if (normalizedUrl.includes("vimeo.com")) {
54
+ const vimeoRegex = /^(https?:\/\/)?(www\.)?(vimeo\.com)\/.+/;
55
+ if (vimeoRegex.test(url)) {
56
+ return "vimeo";
57
+ }
58
+ }
59
+ // Loom detection
60
+ if (normalizedUrl.includes("loom.com")) {
61
+ const loomRegex = /^(https?:\/\/)?(www\.)?(loom\.com)\/share\/([a-zA-Z0-9]+)(\S+)?$/;
62
+ if (loomRegex.test(url)) {
63
+ return "loom";
64
+ }
65
+ }
66
+ // TikTok detection
67
+ if (normalizedUrl.includes("tiktok.com")) {
68
+ const tiktokRegex1 = /^(https?:\/\/)?(www\.)?(tiktok\.com)\/@([a-zA-Z0-9_.]+)\/video\/([0-9]+)(\S+)?$/;
69
+ const tiktokRegex2 = /^(https?:\/\/)?(vm\.)?(tiktok\.com)\/([a-zA-Z0-9]+)(\S+)?$/;
70
+ if (tiktokRegex1.test(url) || tiktokRegex2.test(url)) {
71
+ return "tiktok";
72
+ }
73
+ }
74
+ return null;
75
+ }
76
+ /**
77
+ * Basic URL validation
78
+ * @param url - The URL to validate
79
+ * @returns boolean
80
+ */
81
+ function isValidURL(url) {
82
+ try {
83
+ new URL(url);
84
+ return true;
85
+ }
86
+ catch {
87
+ // Try with protocol prefix
88
+ try {
89
+ new URL(`https://${url}`);
90
+ return true;
91
+ }
92
+ catch {
93
+ return false;
94
+ }
95
+ }
96
+ }
97
+ /**
98
+ * Convenience function for YouTube and Vimeo only (commonly used)
99
+ * @param url - The video URL to validate
100
+ * @returns boolean
101
+ */
102
+ function isValidYouTubeOrVimeoUrl(url) {
103
+ const result = validateVideoLink(url, ["youtube", "vimeo"]);
104
+ return result.isValid;
105
+ }
106
+ /**
107
+ * Convenience function for community posts (all providers)
108
+ * @param url - The video URL to validate
109
+ * @returns boolean
110
+ */
111
+ function isValidCommunityVideoUrl(url) {
112
+ const result = validateVideoLink(url, [
113
+ "youtube",
114
+ "vimeo",
115
+ "loom",
116
+ "tiktok",
117
+ ]);
118
+ return result.isValid;
119
+ }
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@careflair/common",
3
+ "version": "1.0.0",
4
+ "description": "A package containing multiple Zod-based validation schemas for CareFlair.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "prepublishOnly": "npm run build"
10
+ },
11
+ "devDependencies": {
12
+ "typescript": "^5.7.4"
13
+ },
14
+ "keywords": [
15
+ "pnp-zod"
16
+ ],
17
+ "author": "Farhan Hossain",
18
+ "license": "ISC",
19
+ "dependencies": {
20
+ "zod": "^3.23.8"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ]
25
+ }