@wipzent/github-sync 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -0
  3. package/dist/cli/index.js +185 -0
  4. package/dist/cli/prompts.js +16 -0
  5. package/dist/config/defaults.js +5 -0
  6. package/dist/config/env.js +23 -0
  7. package/dist/config/octokit.js +14 -0
  8. package/dist/git/clone.js +15 -0
  9. package/dist/git/rewriteAuthor.js +37 -0
  10. package/dist/git/sync.js +141 -0
  11. package/dist/mcp/resources/github.js +13 -0
  12. package/dist/mcp/server.js +92 -0
  13. package/dist/mcp/tools/autoSync.js +130 -0
  14. package/dist/mcp/tools/createRepo.js +120 -0
  15. package/dist/mcp/tools/resolveConflict.js +4 -0
  16. package/dist/mcp/tools/syncRepo.js +21 -0
  17. package/dist/temp/Terred-Mujtaba-/apps/app/src/components/Toaster/toast.js +17 -0
  18. package/dist/temp/Terred-Mujtaba-/apps/app/src/config/fetch.js +76 -0
  19. package/dist/temp/Terred-Mujtaba-/apps/app/src/config/index.js +65 -0
  20. package/dist/temp/Terred-Mujtaba-/apps/app/src/data/data.js +574 -0
  21. package/dist/temp/Terred-Mujtaba-/apps/app/src/lib/utils.js +8 -0
  22. package/dist/temp/Terred-Mujtaba-/apps/app/src/redux/config.js +7 -0
  23. package/dist/temp/Terred-Mujtaba-/apps/app/src/redux/slices/AdminsSlice.js +20 -0
  24. package/dist/temp/Terred-Mujtaba-/apps/app/src/redux/slices/userSlice.js +19 -0
  25. package/dist/temp/Terred-Mujtaba-/apps/app/src/redux/store.js +15 -0
  26. package/dist/temp/Terred-Mujtaba-/apps/app/src/types/interfaces.js +3 -0
  27. package/dist/temp/Terred-Mujtaba-/apps/app/src/types/types.js +2 -0
  28. package/dist/temp/Terred-Mujtaba-/apps/app/src/utils/helperFunctions.js +150 -0
  29. package/dist/temp/Terred-Mujtaba-/apps/app/tailwind.config.js +85 -0
  30. package/dist/temp/Terred-Mujtaba-/apps/backend/src/admin/admin.controller.js +113 -0
  31. package/dist/temp/Terred-Mujtaba-/apps/backend/src/admin/admin.controller.spec.js +18 -0
  32. package/dist/temp/Terred-Mujtaba-/apps/backend/src/admin/admin.module.js +64 -0
  33. package/dist/temp/Terred-Mujtaba-/apps/backend/src/admin/admin.service.js +327 -0
  34. package/dist/temp/Terred-Mujtaba-/apps/backend/src/admin/admin.service.spec.js +16 -0
  35. package/dist/temp/Terred-Mujtaba-/apps/backend/src/admin/dto/admin.dto.js +214 -0
  36. package/dist/temp/Terred-Mujtaba-/apps/backend/src/app.controller.js +67 -0
  37. package/dist/temp/Terred-Mujtaba-/apps/backend/src/app.controller.spec.js +20 -0
  38. package/dist/temp/Terred-Mujtaba-/apps/backend/src/app.module.js +67 -0
  39. package/dist/temp/Terred-Mujtaba-/apps/backend/src/app.service.js +60 -0
  40. package/dist/temp/Terred-Mujtaba-/apps/backend/src/categories/categories.controller.js +89 -0
  41. package/dist/temp/Terred-Mujtaba-/apps/backend/src/categories/categories.controller.spec.js +18 -0
  42. package/dist/temp/Terred-Mujtaba-/apps/backend/src/categories/categories.module.js +64 -0
  43. package/dist/temp/Terred-Mujtaba-/apps/backend/src/categories/categories.service.js +173 -0
  44. package/dist/temp/Terred-Mujtaba-/apps/backend/src/categories/categories.service.spec.js +16 -0
  45. package/dist/temp/Terred-Mujtaba-/apps/backend/src/categories/dto/category.dto.js +128 -0
  46. package/dist/temp/Terred-Mujtaba-/apps/backend/src/cloudinary/cloudinary.controller.js +86 -0
  47. package/dist/temp/Terred-Mujtaba-/apps/backend/src/cloudinary/cloudinary.controller.spec.js +18 -0
  48. package/dist/temp/Terred-Mujtaba-/apps/backend/src/cloudinary/cloudinary.module.js +63 -0
  49. package/dist/temp/Terred-Mujtaba-/apps/backend/src/cloudinary/cloudinary.service.js +132 -0
  50. package/dist/temp/Terred-Mujtaba-/apps/backend/src/cloudinary/cloudinary.service.spec.js +16 -0
  51. package/dist/temp/Terred-Mujtaba-/apps/backend/src/config/index.js +2 -0
  52. package/dist/temp/Terred-Mujtaba-/apps/backend/src/main.js +30 -0
  53. package/dist/temp/Terred-Mujtaba-/apps/backend/src/prisma/prisma.module.js +60 -0
  54. package/dist/temp/Terred-Mujtaba-/apps/backend/src/prisma/prisma.service.js +58 -0
  55. package/dist/temp/Terred-Mujtaba-/apps/backend/src/prisma/prisma.service.spec.js +16 -0
  56. package/dist/temp/Terred-Mujtaba-/apps/backend/src/products/dto/product.dto.js +124 -0
  57. package/dist/temp/Terred-Mujtaba-/apps/backend/src/products/products.controller.js +89 -0
  58. package/dist/temp/Terred-Mujtaba-/apps/backend/src/products/products.controller.spec.js +18 -0
  59. package/dist/temp/Terred-Mujtaba-/apps/backend/src/products/products.module.js +64 -0
  60. package/dist/temp/Terred-Mujtaba-/apps/backend/src/products/products.service.js +170 -0
  61. package/dist/temp/Terred-Mujtaba-/apps/backend/src/products/products.service.spec.js +16 -0
  62. package/dist/temp/Terred-Mujtaba-/apps/backend/src/types/index.js +2 -0
  63. package/dist/temp/Terred-Mujtaba-/apps/backend/src/utils/func.js +79 -0
  64. package/dist/temp/Terred-Mujtaba-/apps/backend/src/utils/helper.js +31 -0
  65. package/dist/temp/Terred-Mujtaba-/apps/backend/test/app.e2e-spec.js +54 -0
  66. package/dist/temp/Terred-Mujtaba-/packages/ui/turbo/generators/config.js +30 -0
  67. package/package.json +47 -0
@@ -0,0 +1,130 @@
1
+ import { orgOctokit, personalOctokit } from '../../config/octokit.js';
2
+ import { config } from '../../config/env.js';
3
+ import { createRepo } from './createRepo.js';
4
+ import { syncRepo } from './syncRepo.js';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import chalk from 'chalk';
8
+ import ora from 'ora';
9
+ import boxen from 'boxen';
10
+ /**
11
+ * Automatically handles the check-create-sync workflow for personal or collaborator repos.
12
+ *
13
+ * @param fullRepoName The name of the repository (e.g., 'my-repo' or 'client-owner/client-repo')
14
+ */
15
+ export async function autoSync(fullRepoName, shouldRewrite = false, forcePush = false, pullChanges = false, overwriteSource = false, skipPush = false) {
16
+ const spinner = ora();
17
+ // Header for the action
18
+ console.log(boxen(chalk.cyan.bold('WIPZENT SYNC') + ' ' + chalk.gray('v1.0.1') + '\n' +
19
+ chalk.gray('Source: ') + chalk.white.bold(fullRepoName), { padding: 1, margin: { top: 1, bottom: 0 }, borderStyle: 'round', borderColor: 'cyan' }));
20
+ // Handle 'owner/repo' format for collaborators/clients
21
+ let owner;
22
+ let repoName;
23
+ const parts = fullRepoName.split('/').filter(p => p.length > 0);
24
+ if (parts.length >= 2) {
25
+ owner = parts[parts.length - 2];
26
+ repoName = parts[parts.length - 1];
27
+ }
28
+ else {
29
+ // Fallback to authenticated user if only name provided
30
+ const { data: user } = await personalOctokit.rest.users.getAuthenticated();
31
+ owner = user.login;
32
+ repoName = parts[0] || fullRepoName;
33
+ }
34
+ // Clean up repo name (remove .git or trailing slashes)
35
+ repoName = repoName.replace(/\.git$/, '').replace(/\/$/, '');
36
+ const orgRepoName = repoName;
37
+ const localDirName = repoName; // Local storage name
38
+ const localPath = path.join(process.cwd(), 'temp', localDirName);
39
+ try {
40
+ // 1. Check if repo exists in the organization
41
+ spinner.start(`Verifying ${chalk.bold(orgRepoName)} in ${chalk.bold(config.orgName)}...`);
42
+ let exists = false;
43
+ try {
44
+ await orgOctokit.rest.repos.get({
45
+ owner: config.orgName,
46
+ repo: orgRepoName,
47
+ });
48
+ exists = true;
49
+ spinner.succeed(chalk.green(`Repository found in organization.`));
50
+ }
51
+ catch (error) {
52
+ // If it's private and we don't have access, it might return 404
53
+ // but we'll try to proceed anyway and let createRepo handle the collision
54
+ spinner.info(chalk.yellow(`Could not verify existence (may be private or missing).`));
55
+ }
56
+ // 2. Create/Initialize if needed
57
+ if (!exists || !fs.existsSync(localPath)) {
58
+ spinner.start(`Initializing mirror: ${chalk.blue(owner + '/' + repoName)} → ${chalk.blue(config.orgName + '/' + orgRepoName)}...`);
59
+ const result = await createRepo(repoName, orgRepoName, owner, shouldRewrite, forcePush, pullChanges, skipPush);
60
+ if (!result.success && (result.isDiverged || result.requiresManualResolution)) {
61
+ spinner.warn(chalk.yellow(`Remote changes detected or conflicts found.`));
62
+ return result;
63
+ }
64
+ spinner.succeed(chalk.green(result.message));
65
+ // Re-sync if we just created it and skipPush is true
66
+ if (skipPush && result.success) {
67
+ const syncResult = await syncRepo(repoName, orgRepoName, shouldRewrite, forcePush, overwriteSource, skipPush);
68
+ // Deduplicate pushes by remote
69
+ const pushMap = new Map();
70
+ const allPushes = [...(result.pendingPushes || []), ...(syncResult.pendingPushes || [])];
71
+ for (const push of allPushes) {
72
+ const existing = pushMap.get(push.remote);
73
+ if (!existing) {
74
+ pushMap.set(push.remote, push);
75
+ }
76
+ else {
77
+ // Merge: prefer force: true and keep the most descriptive name
78
+ existing.force = existing.force || push.force;
79
+ if (push.description.length > existing.description.length) {
80
+ existing.description = push.description;
81
+ }
82
+ }
83
+ }
84
+ return { ...syncResult, pendingPushes: Array.from(pushMap.values()), localPath };
85
+ }
86
+ }
87
+ else {
88
+ spinner.info(chalk.blue(`Using existing local mirror for ${repoName}.`));
89
+ }
90
+ // 3. Perform the sync
91
+ spinner.start(`Performing two-way synchronization...`);
92
+ const syncResult = await syncRepo(repoName, orgRepoName, shouldRewrite, forcePush, overwriteSource, skipPush);
93
+ if (syncResult.success) {
94
+ spinner.succeed(chalk.green.bold(`Sync Success!`));
95
+ if (!skipPush) {
96
+ console.log(chalk.gray(`\nRemotes updated: origin (client) and upstream (org)\n`));
97
+ }
98
+ return { ...syncResult, localPath };
99
+ }
100
+ else if (syncResult.requiresManualResolution) {
101
+ spinner.warn(chalk.yellow.bold(`Action Required: Merge Conflicts!`));
102
+ console.log(chalk.red(`\nConflicts detected in:`));
103
+ syncResult.conflicts.forEach((f) => console.log(chalk.red(` • ${f}`)));
104
+ console.log(chalk.white(`\nPlease resolve manually in: `) + chalk.cyan(localPath));
105
+ return syncResult;
106
+ }
107
+ else {
108
+ spinner.fail(chalk.red(`Sync Failed: ${syncResult.message}`));
109
+ return syncResult;
110
+ }
111
+ }
112
+ catch (error) {
113
+ spinner.fail(chalk.red(`Error: ${error.message}`));
114
+ return {
115
+ success: false,
116
+ message: `Auto-sync failed: ${error.message}`,
117
+ isDiverged: false
118
+ };
119
+ }
120
+ finally {
121
+ if (fs.existsSync(localPath) && !skipPush) {
122
+ try {
123
+ fs.rmSync(localPath, { recursive: true, force: true });
124
+ }
125
+ catch (cleanupError) {
126
+ // Silently ignore cleanup errors or optionally log them
127
+ }
128
+ }
129
+ }
130
+ }
@@ -0,0 +1,120 @@
1
+ import { orgOctokit } from '../../config/octokit.js';
2
+ import { config } from '../../config/env.js';
3
+ import { cloneRepo } from '../../git/clone.js';
4
+ import { rewriteHistory } from '../../git/rewriteAuthor.js';
5
+ import { simpleGit } from 'simple-git';
6
+ import { pushToRemote } from '../../git/sync.js';
7
+ import path from 'path';
8
+ import fs from 'fs';
9
+ import chalk from 'chalk';
10
+ /**
11
+ * Creates an organization repository and pushes the content of a personal repository to it.
12
+ */
13
+ export async function createRepo(personalRepoName, orgRepoName, owner, shouldRewrite = false, forcePush = false, pullChanges = false, skipPush = false) {
14
+ // 1. Create the repository in the organization
15
+ let repoCreated = false;
16
+ try {
17
+ await orgOctokit.rest.repos.createInOrg({
18
+ org: config.orgName,
19
+ name: orgRepoName,
20
+ private: true,
21
+ });
22
+ repoCreated = true;
23
+ }
24
+ catch (error) {
25
+ // If it already exists, that's fine for our idempotency
26
+ if (error.status === 422 || (error.response?.data?.message && error.response.data.message.includes('already exists'))) {
27
+ repoCreated = true;
28
+ }
29
+ else {
30
+ throw new Error(`Repository creation failed: ${error.message}`);
31
+ }
32
+ }
33
+ // 2. Clone the personal repo locally
34
+ const tempDir = path.join(process.cwd(), 'temp', personalRepoName);
35
+ if (fs.existsSync(tempDir)) {
36
+ fs.rmSync(tempDir, { recursive: true, force: true });
37
+ }
38
+ await cloneRepo(owner, personalRepoName, tempDir);
39
+ // 3. Set local author identity for the initial push/clones
40
+ // This is ALWAYS set to ensure correct attribution for any new actions
41
+ const git = simpleGit(tempDir);
42
+ await git.addConfig('user.name', config.authorName);
43
+ await git.addConfig('user.email', config.authorEmail);
44
+ if (shouldRewrite) {
45
+ await rewriteHistory(tempDir, config.authorName, config.authorEmail);
46
+ // Force push is REQUIRED if we rewrite history as SHAs change
47
+ forcePush = true;
48
+ }
49
+ // 4. Add org repo as remote and push
50
+ const orgUrl = `https://${config.orgPat}@github.com/${config.orgName}/${orgRepoName}.git`;
51
+ try {
52
+ await git.addRemote('upstream', orgUrl);
53
+ if (pullChanges) {
54
+ console.log(chalk.blue(`Pulling and merging changes from ${orgUrl}...`));
55
+ await git.fetch('upstream', config.defaultBranch);
56
+ // Attempt a merge
57
+ try {
58
+ await git.merge([`upstream/${config.defaultBranch}`, '--no-commit', '--no-ff']);
59
+ const status = await git.status();
60
+ if (status.conflicted.length > 0) {
61
+ return {
62
+ success: false,
63
+ isDiverged: false,
64
+ requiresManualResolution: true,
65
+ conflicts: status.conflicted,
66
+ message: 'Conflicts detected during merge from organization. Please resolve manually.',
67
+ tempPath: tempDir,
68
+ pendingPushes: []
69
+ };
70
+ }
71
+ await git.commit(`Merge organization changes from ${orgRepoName} into personal mirror`);
72
+ }
73
+ catch (mergeError) {
74
+ const status = await git.status();
75
+ if (status.conflicted.length > 0) {
76
+ return {
77
+ success: false,
78
+ isDiverged: false,
79
+ requiresManualResolution: true,
80
+ conflicts: status.conflicted,
81
+ message: 'Conflicts detected during merge from organization. Please resolve manually.',
82
+ tempPath: tempDir,
83
+ pendingPushes: []
84
+ };
85
+ }
86
+ throw mergeError;
87
+ }
88
+ }
89
+ const pendingPushes = [];
90
+ pendingPushes.push({
91
+ remote: 'upstream',
92
+ branch: config.defaultBranch,
93
+ force: forcePush,
94
+ description: `Organization Repository (upstream)`
95
+ });
96
+ if (!skipPush) {
97
+ await pushToRemote(tempDir, 'upstream', config.defaultBranch, forcePush);
98
+ }
99
+ return {
100
+ success: true,
101
+ message: repoCreated ? `Successfully found/created ${orgRepoName} and synced content.` : `Created and synced ${orgRepoName}.`,
102
+ tempPath: tempDir,
103
+ isDiverged: false,
104
+ pendingPushes: skipPush ? pendingPushes : [],
105
+ localPath: tempDir
106
+ };
107
+ }
108
+ catch (error) {
109
+ if (error.message.includes('rejected') || error.message.includes('fetch first')) {
110
+ return {
111
+ success: false,
112
+ isDiverged: true,
113
+ message: 'Organization repository already contains changes. Please choose whether to merge or overwrite.',
114
+ tempPath: tempDir,
115
+ pendingPushes: []
116
+ };
117
+ }
118
+ throw error;
119
+ }
120
+ }
@@ -0,0 +1,4 @@
1
+ export async function resolveConflict(fileName, strategy) {
2
+ console.log(`Resolving conflict in ${fileName} using strategy: ${strategy}`);
3
+ // Conflict resolution logic
4
+ }
@@ -0,0 +1,21 @@
1
+ import { syncBetweenRemotes } from '../../git/sync.js';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ /**
5
+ * Syncs changes between a personal/client repository and an organization repository.
6
+ */
7
+ export async function syncRepo(personalRepoName, orgRepoName, shouldRewrite = false, forcePush = false, overwriteSource = false, skipPush = false) {
8
+ const localPath = path.join(process.cwd(), 'temp', personalRepoName);
9
+ if (!fs.existsSync(localPath)) {
10
+ return {
11
+ success: false,
12
+ message: `The repository ${personalRepoName} has not been set up yet.`,
13
+ requiresManualResolution: false,
14
+ conflicts: [],
15
+ isDiverged: false,
16
+ pendingPushes: []
17
+ };
18
+ }
19
+ // Perform the safe two-way sync
20
+ return await syncBetweenRemotes(localPath, 'origin', 'upstream', shouldRewrite, forcePush, overwriteSource, skipPush);
21
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = showToast;
4
+ const react_toastify_1 = require("react-toastify");
5
+ require("react-toastify/dist/ReactToastify.css");
6
+ function showToast(ToastType, message) {
7
+ react_toastify_1.toast[ToastType](message, {
8
+ position: 'top-right',
9
+ autoClose: 2000,
10
+ hideProgressBar: false,
11
+ closeOnClick: true,
12
+ pauseOnHover: true,
13
+ draggable: true,
14
+ progress: undefined,
15
+ theme: 'colored',
16
+ });
17
+ }
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getAllAdmins = exports.adminRecords = exports.fetchSubCategories = exports.fetchCategories = exports.fetchProductBySlug = exports.fetchBlogs = exports.fetchProducts = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const js_cookie_1 = __importDefault(require("js-cookie"));
9
+ const superAdmintoken = js_cookie_1.default.get('superAdminToken');
10
+ const token = js_cookie_1.default.get('adminAuth');
11
+ let Finaltoken = superAdmintoken ? superAdmintoken : token;
12
+ const headers = {
13
+ Authorization: `Bearer ${Finaltoken}`,
14
+ };
15
+ const fetchProducts = async () => {
16
+ console.log(`${process.env.NEXT_PUBLIC_BASE_URL}`);
17
+ const response = await axios_1.default.get(`${process.env.NEXT_PUBLIC_BASE_URL}/api/product/get-all`);
18
+ return response.data;
19
+ };
20
+ exports.fetchProducts = fetchProducts;
21
+ const fetchBlogs = async () => {
22
+ console.log(`${process.env.NEXT_PUBLIC_BASE_URL}`);
23
+ const response = await axios_1.default.get(`${process.env.NEXT_PUBLIC_BASE_URL}/api/blogs`);
24
+ return response.data;
25
+ };
26
+ exports.fetchBlogs = fetchBlogs;
27
+ const fetchProductBySlug = async (slug) => {
28
+ const response = await fetch(`/api/products?slug=${slug}`);
29
+ if (!response.ok) {
30
+ throw new Error('Error fetching product');
31
+ }
32
+ return response.json();
33
+ };
34
+ exports.fetchProductBySlug = fetchProductBySlug;
35
+ const fetchCategories = async () => {
36
+ const response = await axios_1.default.get(`${process.env.NEXT_PUBLIC_BASE_URL}/api/category/get-all`);
37
+ return response.data;
38
+ };
39
+ exports.fetchCategories = fetchCategories;
40
+ const fetchSubCategories = async () => {
41
+ const response = await axios_1.default.get(`${process.env.NEXT_PUBLIC_BASE_URL}/api/categories/get-all-subCategories`);
42
+ return response.data;
43
+ };
44
+ exports.fetchSubCategories = fetchSubCategories;
45
+ const adminRecords = async (token) => {
46
+ try {
47
+ if (!token)
48
+ throw new Error('token not found');
49
+ const response = await axios_1.default.get(`${process.env.NEXT_PUBLIC_BASE_URL}/api/admins/get_all_records`, {
50
+ headers: {
51
+ Authorization: `Bearer ${token}`,
52
+ },
53
+ });
54
+ return response.data;
55
+ }
56
+ catch (error) {
57
+ console.error('Error fetching admin records:', error);
58
+ throw error;
59
+ }
60
+ };
61
+ exports.adminRecords = adminRecords;
62
+ const getAllAdmins = async () => {
63
+ try {
64
+ const token = js_cookie_1.default.get('superAdminToken');
65
+ if (!token) {
66
+ return;
67
+ }
68
+ const response = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/admins/get_all_admin`, { headers: headers });
69
+ const admins = await response.json();
70
+ return admins;
71
+ }
72
+ catch (err) {
73
+ throw err;
74
+ }
75
+ };
76
+ exports.getAllAdmins = getAllAdmins;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateSlug = exports.removeImagesFromContent = exports.formatDateMonth = exports.getCategoryFromUrl = exports.formatDate = void 0;
4
+ exports.formatDateTime = formatDateTime;
5
+ const formatDate = (isoDate) => {
6
+ const date = new Date(isoDate);
7
+ return date.toLocaleDateString('en-GB', {
8
+ day: '2-digit',
9
+ month: 'short',
10
+ year: 'numeric',
11
+ });
12
+ };
13
+ exports.formatDate = formatDate;
14
+ const urlToCategoryMap = {
15
+ 'made-to-measure-blinds': 'Blinds',
16
+ 'shutters-range': 'Shutter',
17
+ 'made-to-measure-curtains': 'Curtains',
18
+ };
19
+ const getCategoryFromUrl = (urlParam) => {
20
+ return urlToCategoryMap[urlParam] || urlParam;
21
+ };
22
+ exports.getCategoryFromUrl = getCategoryFromUrl;
23
+ function formatDateTime(isoDate) {
24
+ const date = new Date(isoDate);
25
+ const formattedDate = date.toLocaleDateString('en-US', {
26
+ year: 'numeric',
27
+ month: 'long',
28
+ day: 'numeric',
29
+ });
30
+ const formattedTime = date.toLocaleTimeString('en-US', {
31
+ hour: 'numeric',
32
+ minute: '2-digit',
33
+ second: '2-digit',
34
+ hour12: true,
35
+ });
36
+ return `${formattedDate}, ${formattedTime}`;
37
+ }
38
+ const formatDateMonth = (dateString) => {
39
+ const date = new Date(dateString);
40
+ const options = {
41
+ day: 'numeric',
42
+ month: 'long',
43
+ year: 'numeric',
44
+ };
45
+ return date.toLocaleDateString(undefined, options);
46
+ };
47
+ exports.formatDateMonth = formatDateMonth;
48
+ const removeImagesFromContent = (content) => {
49
+ let updatedContent = content.replace(/<figure[^>]*>.*?<\/figure>/g, '');
50
+ updatedContent = updatedContent.replace(/\s*(<p>|<\/p>)\s*/g, '');
51
+ return updatedContent;
52
+ };
53
+ exports.removeImagesFromContent = removeImagesFromContent;
54
+ const generateSlug = (text) => {
55
+ if (!text)
56
+ return '';
57
+ return text
58
+ .toString()
59
+ .toLowerCase()
60
+ .trim()
61
+ .replace(/\s+/g, '-')
62
+ .replace(/[^\w\-]+/g, '')
63
+ .replace(/\-\-+/g, '-');
64
+ };
65
+ exports.generateSlug = generateSlug;