@intlayer/backend 7.0.6 → 7.0.8-canary.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.
- package/dist/assets/utils/AI/askDocQuestion/embeddings/blog/en/intlayer_with_i18next.json +5122 -4096
- package/dist/assets/utils/AI/askDocQuestion/embeddings/blog/en/intlayer_with_next-i18next.json +5122 -4096
- package/dist/assets/utils/AI/askDocQuestion/embeddings/blog/en/intlayer_with_next-intl.json +5122 -4096
- package/dist/assets/utils/AI/askDocQuestion/embeddings/blog/en/intlayer_with_react-i18next.json +5122 -4096
- package/dist/assets/utils/AI/askDocQuestion/embeddings/blog/en/intlayer_with_react-intl.json +5122 -4096
- package/dist/assets/utils/AI/askDocQuestion/embeddings/blog/en/intlayer_with_vue-i18n.json +5122 -4096
- package/dist/assets/utils/AI/askDocQuestion/embeddings/blog/en/react-i18next_vs_react-intl_vs_intlayer.json +8194 -7168
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/intlayer_with_nextjs_16.json +1 -1
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/intlayer_with_nuxt.json +11266 -10240
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/intlayer_with_tanstack.json +9218 -8192
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/intlayer_with_vite+react.json +1 -1
- package/dist/cjs/_virtual/_utils_asset.cjs +0 -3
- package/dist/cjs/controllers/audit.controller.cjs +0 -1
- package/dist/cjs/controllers/audit.controller.cjs.map +1 -1
- package/dist/cjs/controllers/dictionary.controller.cjs +0 -1
- package/dist/cjs/controllers/dictionary.controller.cjs.map +1 -1
- package/dist/cjs/controllers/newsletter.controller.cjs +0 -1
- package/dist/cjs/controllers/newsletter.controller.cjs.map +1 -1
- package/dist/cjs/controllers/oAuth2.controller.cjs +0 -1
- package/dist/cjs/controllers/oAuth2.controller.cjs.map +1 -1
- package/dist/cjs/controllers/organization.controller.cjs +0 -2
- package/dist/cjs/controllers/organization.controller.cjs.map +1 -1
- package/dist/cjs/controllers/project.controller.cjs +0 -1
- package/dist/cjs/controllers/project.controller.cjs.map +1 -1
- package/dist/cjs/controllers/projectAccessKey.controller.cjs +0 -1
- package/dist/cjs/controllers/projectAccessKey.controller.cjs.map +1 -1
- package/dist/cjs/controllers/stripe.controller.cjs +0 -2
- package/dist/cjs/controllers/stripe.controller.cjs.map +1 -1
- package/dist/cjs/controllers/tag.controller.cjs +0 -1
- package/dist/cjs/controllers/tag.controller.cjs.map +1 -1
- package/dist/cjs/controllers/user.controller.cjs +0 -1
- package/dist/cjs/controllers/user.controller.cjs.map +1 -1
- package/dist/cjs/emails/InviteUserEmail.cjs +0 -2
- package/dist/cjs/emails/InviteUserEmail.cjs.map +1 -1
- package/dist/cjs/emails/OAuthTokenCreatedEmail.cjs +0 -2
- package/dist/cjs/emails/OAuthTokenCreatedEmail.cjs.map +1 -1
- package/dist/cjs/emails/PasswordChangeConfirmation.cjs +0 -2
- package/dist/cjs/emails/PasswordChangeConfirmation.cjs.map +1 -1
- package/dist/cjs/emails/ResetUserPassword.cjs +0 -2
- package/dist/cjs/emails/ResetUserPassword.cjs.map +1 -1
- package/dist/cjs/emails/SubscriptionPaymentCancellation.cjs +0 -2
- package/dist/cjs/emails/SubscriptionPaymentCancellation.cjs.map +1 -1
- package/dist/cjs/emails/SubscriptionPaymentError.cjs +0 -2
- package/dist/cjs/emails/SubscriptionPaymentError.cjs.map +1 -1
- package/dist/cjs/emails/SubscriptionPaymentSuccess.cjs +0 -2
- package/dist/cjs/emails/SubscriptionPaymentSuccess.cjs.map +1 -1
- package/dist/cjs/emails/ValidateUserEmail.cjs +0 -2
- package/dist/cjs/emails/ValidateUserEmail.cjs.map +1 -1
- package/dist/cjs/emails/Welcome.cjs +0 -2
- package/dist/cjs/emails/Welcome.cjs.map +1 -1
- package/dist/cjs/index.cjs +0 -2
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/logger/index.cjs +0 -1
- package/dist/cjs/logger/index.cjs.map +1 -1
- package/dist/cjs/middlewares/sessionAuth.middleware.cjs +0 -1
- package/dist/cjs/middlewares/sessionAuth.middleware.cjs.map +1 -1
- package/dist/cjs/models/audit.model.cjs +0 -2
- package/dist/cjs/models/audit.model.cjs.map +1 -1
- package/dist/cjs/models/dictionary.model.cjs +0 -1
- package/dist/cjs/models/dictionary.model.cjs.map +1 -1
- package/dist/cjs/models/discussion.model.cjs +0 -1
- package/dist/cjs/models/discussion.model.cjs.map +1 -1
- package/dist/cjs/models/oAuth2.model.cjs +0 -1
- package/dist/cjs/models/oAuth2.model.cjs.map +1 -1
- package/dist/cjs/models/organization.model.cjs +0 -1
- package/dist/cjs/models/organization.model.cjs.map +1 -1
- package/dist/cjs/models/project.model.cjs +0 -1
- package/dist/cjs/models/project.model.cjs.map +1 -1
- package/dist/cjs/models/session.model.cjs +0 -1
- package/dist/cjs/models/session.model.cjs.map +1 -1
- package/dist/cjs/models/tag.model.cjs +0 -1
- package/dist/cjs/models/tag.model.cjs.map +1 -1
- package/dist/cjs/models/user.model.cjs +0 -1
- package/dist/cjs/models/user.model.cjs.map +1 -1
- package/dist/cjs/routes/ai.routes.cjs +0 -1
- package/dist/cjs/routes/ai.routes.cjs.map +1 -1
- package/dist/cjs/routes/dictionary.routes.cjs +0 -1
- package/dist/cjs/routes/dictionary.routes.cjs.map +1 -1
- package/dist/cjs/routes/eventListener.routes.cjs +0 -1
- package/dist/cjs/routes/eventListener.routes.cjs.map +1 -1
- package/dist/cjs/routes/newsletter.routes.cjs +0 -1
- package/dist/cjs/routes/newsletter.routes.cjs.map +1 -1
- package/dist/cjs/routes/organization.routes.cjs +0 -1
- package/dist/cjs/routes/organization.routes.cjs.map +1 -1
- package/dist/cjs/routes/project.routes.cjs +0 -1
- package/dist/cjs/routes/project.routes.cjs.map +1 -1
- package/dist/cjs/routes/search.routes.cjs +0 -1
- package/dist/cjs/routes/search.routes.cjs.map +1 -1
- package/dist/cjs/routes/stripe.routes.cjs +0 -1
- package/dist/cjs/routes/stripe.routes.cjs.map +1 -1
- package/dist/cjs/routes/tags.routes.cjs +0 -1
- package/dist/cjs/routes/tags.routes.cjs.map +1 -1
- package/dist/cjs/routes/user.routes.cjs +0 -1
- package/dist/cjs/routes/user.routes.cjs.map +1 -1
- package/dist/cjs/schemas/dictionary.schema.cjs +0 -1
- package/dist/cjs/schemas/dictionary.schema.cjs.map +1 -1
- package/dist/cjs/schemas/discussion.schema.cjs +0 -1
- package/dist/cjs/schemas/discussion.schema.cjs.map +1 -1
- package/dist/cjs/schemas/oAuth2.schema.cjs +0 -1
- package/dist/cjs/schemas/oAuth2.schema.cjs.map +1 -1
- package/dist/cjs/schemas/organization.schema.cjs +0 -1
- package/dist/cjs/schemas/organization.schema.cjs.map +1 -1
- package/dist/cjs/schemas/plans.schema.cjs +0 -1
- package/dist/cjs/schemas/plans.schema.cjs.map +1 -1
- package/dist/cjs/schemas/project.schema.cjs +0 -2
- package/dist/cjs/schemas/project.schema.cjs.map +1 -1
- package/dist/cjs/schemas/session.schema.cjs +0 -1
- package/dist/cjs/schemas/session.schema.cjs.map +1 -1
- package/dist/cjs/schemas/tag.schema.cjs +0 -1
- package/dist/cjs/schemas/tag.schema.cjs.map +1 -1
- package/dist/cjs/schemas/user.schema.cjs +0 -1
- package/dist/cjs/schemas/user.schema.cjs.map +1 -1
- package/dist/cjs/services/dictionary.service.cjs +0 -1
- package/dist/cjs/services/dictionary.service.cjs.map +1 -1
- package/dist/cjs/services/email.service.cjs +0 -3
- package/dist/cjs/services/email.service.cjs.map +1 -1
- package/dist/cjs/services/oAuth2.service.cjs +0 -1
- package/dist/cjs/services/oAuth2.service.cjs.map +1 -1
- package/dist/cjs/services/projectAccessKey.service.cjs +0 -1
- package/dist/cjs/services/projectAccessKey.service.cjs.map +1 -1
- package/dist/cjs/utils/AI/aiSdk.cjs +0 -5
- package/dist/cjs/utils/AI/aiSdk.cjs.map +1 -1
- package/dist/cjs/utils/AI/askDocQuestion/askDocQuestion.cjs +0 -4
- package/dist/cjs/utils/AI/askDocQuestion/askDocQuestion.cjs.map +1 -1
- package/dist/cjs/utils/AI/askDocQuestion/indexMarkdownFiles.cjs +0 -6
- package/dist/cjs/utils/AI/askDocQuestion/indexMarkdownFiles.cjs.map +1 -1
- package/dist/cjs/utils/AI/auditDictionary/index.cjs +0 -3
- package/dist/cjs/utils/AI/auditDictionary/index.cjs.map +1 -1
- package/dist/cjs/utils/AI/auditDictionaryField/index.cjs +0 -3
- package/dist/cjs/utils/AI/auditDictionaryField/index.cjs.map +1 -1
- package/dist/cjs/utils/AI/auditDictionaryMetadata/index.cjs +0 -1
- package/dist/cjs/utils/AI/auditDictionaryMetadata/index.cjs.map +1 -1
- package/dist/cjs/utils/AI/auditTag/index.cjs +0 -1
- package/dist/cjs/utils/AI/auditTag/index.cjs.map +1 -1
- package/dist/cjs/utils/AI/autocomplete/index.cjs +0 -1
- package/dist/cjs/utils/AI/autocomplete/index.cjs.map +1 -1
- package/dist/cjs/utils/AI/customQuery/index.cjs +0 -1
- package/dist/cjs/utils/AI/customQuery/index.cjs.map +1 -1
- package/dist/cjs/utils/AI/translateJSON/index.cjs +0 -3
- package/dist/cjs/utils/AI/translateJSON/index.cjs.map +1 -1
- package/dist/cjs/utils/accessControl.cjs +1 -1
- package/dist/cjs/utils/accessControl.cjs.map +1 -1
- package/dist/cjs/utils/auth/getAuth.cjs +0 -4
- package/dist/cjs/utils/auth/getAuth.cjs.map +1 -1
- package/dist/cjs/utils/errors/ErrorHandler.cjs +0 -2
- package/dist/cjs/utils/errors/ErrorHandler.cjs.map +1 -1
- package/dist/cjs/utils/errors/ErrorsClass.cjs +0 -1
- package/dist/cjs/utils/errors/ErrorsClass.cjs.map +1 -1
- package/dist/cjs/utils/mongoDB/connectDB.cjs +1 -2
- package/dist/cjs/utils/mongoDB/connectDB.cjs.map +1 -1
- package/dist/cjs/webhooks/stripe.webhook.cjs +0 -1
- package/dist/cjs/webhooks/stripe.webhook.cjs.map +1 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/utils/accessControl.mjs +1 -1
- package/dist/esm/utils/accessControl.mjs.map +1 -1
- package/dist/esm/utils/mongoDB/connectDB.mjs +2 -2
- package/dist/esm/utils/mongoDB/connectDB.mjs.map +1 -1
- package/dist/types/controllers/dictionary.controller.d.ts.map +1 -1
- package/dist/types/emails/InviteUserEmail.d.ts +4 -4
- package/dist/types/emails/OAuthTokenCreatedEmail.d.ts +4 -4
- package/dist/types/emails/OAuthTokenCreatedEmail.d.ts.map +1 -1
- package/dist/types/emails/PasswordChangeConfirmation.d.ts +4 -4
- package/dist/types/emails/PasswordChangeConfirmation.d.ts.map +1 -1
- package/dist/types/emails/SubscriptionPaymentCancellation.d.ts +4 -4
- package/dist/types/emails/SubscriptionPaymentCancellation.d.ts.map +1 -1
- package/dist/types/emails/SubscriptionPaymentError.d.ts +4 -4
- package/dist/types/emails/SubscriptionPaymentSuccess.d.ts +4 -4
- package/dist/types/emails/ValidateUserEmail.d.ts +4 -4
- package/dist/types/emails/Welcome.d.ts +4 -4
- package/dist/types/emails/Welcome.d.ts.map +1 -1
- package/dist/types/export.d.ts +3 -1
- package/dist/types/models/dictionary.model.d.ts +4 -4
- package/dist/types/models/dictionary.model.d.ts.map +1 -1
- package/dist/types/models/discussion.model.d.ts +2 -2
- package/dist/types/models/discussion.model.d.ts.map +1 -1
- package/dist/types/models/oAuth2.model.d.ts +4 -3
- package/dist/types/models/oAuth2.model.d.ts.map +1 -1
- package/dist/types/schemas/dictionary.schema.d.ts +6 -6
- package/dist/types/schemas/dictionary.schema.d.ts.map +1 -1
- package/dist/types/schemas/discussion.schema.d.ts +6 -6
- package/dist/types/schemas/oAuth2.schema.d.ts +5 -5
- package/dist/types/schemas/organization.schema.d.ts +6 -6
- package/dist/types/schemas/organization.schema.d.ts.map +1 -1
- package/dist/types/schemas/plans.schema.d.ts +6 -6
- package/dist/types/schemas/project.schema.d.ts +6 -6
- package/dist/types/schemas/session.schema.d.ts +6 -6
- package/dist/types/schemas/tag.schema.d.ts +6 -6
- package/dist/types/schemas/user.schema.d.ts +6 -6
- package/dist/types/services/email.service.d.ts +10 -10
- package/dist/types/utils/accessControl.d.ts +1 -1
- package/dist/types/utils/accessControl.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getDictionaryFiltersAndPagination.d.ts +2 -2
- package/dist/types/utils/filtersAndPagination/getOrganizationFiltersAndPagination.d.ts +2 -2
- package/dist/types/utils/filtersAndPagination/getProjectFiltersAndPagination.d.ts +2 -2
- package/dist/types/utils/filtersAndPagination/getTagFiltersAndPagination.d.ts +2 -2
- package/dist/types/utils/mongoDB/connectDB.d.ts +2 -2
- package/dist/types/utils/mongoDB/connectDB.d.ts.map +1 -1
- package/package.json +15 -16
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
const require_rolldown_runtime = require('../../../_virtual/rolldown_runtime.cjs');
|
|
2
2
|
const require_utils_AI_aiSdk = require('../aiSdk.cjs');
|
|
3
3
|
let node_fs = require("node:fs");
|
|
4
|
-
node_fs = require_rolldown_runtime.__toESM(node_fs);
|
|
5
4
|
let node_path = require("node:path");
|
|
6
|
-
node_path = require_rolldown_runtime.__toESM(node_path);
|
|
7
5
|
let node_url = require("node:url");
|
|
8
|
-
node_url = require_rolldown_runtime.__toESM(node_url);
|
|
9
6
|
let __intlayer_core = require("@intlayer/core");
|
|
10
|
-
__intlayer_core = require_rolldown_runtime.__toESM(__intlayer_core);
|
|
11
7
|
let __intlayer_docs = require("@intlayer/docs");
|
|
12
|
-
__intlayer_docs = require_rolldown_runtime.__toESM(__intlayer_docs);
|
|
13
8
|
let openai = require("openai");
|
|
14
|
-
openai = require_rolldown_runtime.__toESM(openai);
|
|
15
9
|
let dotenv = require("dotenv");
|
|
16
10
|
dotenv = require_rolldown_runtime.__toESM(dotenv);
|
|
17
11
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexMarkdownFiles.cjs","names":["__dirname","vectorStore: VectorStoreEl[]","MODEL: AIOptions['model']","MODEL_TEMPERATURE: AIOptions['temperature']","aiDefaultOptions: AIOptions","AIProvider","EMBEDDING_MODEL: OpenAI.EmbeddingModel","OVERLAP_TOKENS: number","MAX_CHUNK_TOKENS: number","CHAR_BY_TOKEN: number","MAX_CHARS: number","OVERLAP_CHARS: number","chunks: string[]","OpenAI","resultForFile: Record<string, number[]>"],"sources":["../../../../../src/utils/AI/askDocQuestion/indexMarkdownFiles.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { getMarkdownMetadata } from '@intlayer/core';\nimport { getBlogs, getDocs, getFrequentQuestions } from '@intlayer/docs';\nimport dotenv from 'dotenv';\nimport { OpenAI } from 'openai';\nimport { type AIOptions, AIProvider } from '../aiSdk';\n\nconst OUTPUT_EMBEDDINGS_DIR = 'src/utils/AI/askDocQuestion/embeddings';\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nconst readEmbeddingsForFile = (fileKey: string): Record<string, number[]> => {\n try {\n return JSON.parse(\n readFileSync(\n `${__dirname}/embeddings/${fileKey.replace('.md', '.json')}`,\n 'utf-8'\n )\n ) as Record<string, number[]>;\n } catch {\n return {};\n }\n};\n\nconst writeEmbeddingsForFile = (\n fileKey: string,\n data: Record<string, number[]>\n): void => {\n const filePath = join(\n OUTPUT_EMBEDDINGS_DIR,\n `${fileKey.replace('.md', '.json')}`\n );\n const dir = dirname(filePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(filePath, JSON.stringify(data));\n};\n\ntype VectorStoreEl = {\n fileKey: string;\n chunkNumber: number;\n content: string;\n embedding: number[];\n docUrl: string;\n docName: string;\n};\n\n/**\n * Simple in-memory vector store to hold document embeddings and their content.\n * Each entry contains:\n * - fileKey: A unique key identifying the file\n * - chunkNumber: The number of the chunk within the document\n * - content: The chunk content\n * - embedding: The numerical embedding vector for the chunk\n */\nconst vectorStore: VectorStoreEl[] = [];\n\n/*\n * Ask question AI configuration\n */\nconst MODEL: AIOptions['model'] = 'chatgpt-4o-latest'; // Model to use for chat completions\nconst MODEL_TEMPERATURE: AIOptions['temperature'] = 0.1; // Temperature to use for chat completions\n\nexport const aiDefaultOptions: AIOptions = {\n provider: AIProvider.OPENAI,\n model: MODEL,\n temperature: MODEL_TEMPERATURE,\n};\n\n/*\n * Embedding model configuration\n */\nconst EMBEDDING_MODEL: OpenAI.EmbeddingModel = 'text-embedding-3-large'; // Model to use for embedding generation\nconst OVERLAP_TOKENS: number = 200; // Number of tokens to overlap between chunks\nconst MAX_CHUNK_TOKENS: number = 800; // Maximum number of tokens per chunk\nconst CHAR_BY_TOKEN: number = 4.15; // Approximate pessimistically the number of characters per token // Can use `tiktoken` or other tokenizers to calculate it more precisely\nconst MAX_CHARS: number = MAX_CHUNK_TOKENS * CHAR_BY_TOKEN;\nconst OVERLAP_CHARS: number = OVERLAP_TOKENS * CHAR_BY_TOKEN;\n\nconst skipDocEmbeddingsIndex = process.env.SKIP_DOC_EMBEDDINGS_INDEX === 'true';\n\n/**\n * Splits a given text into chunks ensuring each chunk does not exceed MAX_CHARS.\n * @param text - The input text to split.\n * @returns - Array of text chunks.\n */\nconst chunkText = (text: string): string[] => {\n const chunks: string[] = [];\n let start = 0;\n\n while (start < text.length) {\n let end = Math.min(start + MAX_CHARS, text.length);\n\n // Ensure we don't cut words in the middle (find nearest space)\n if (end < text.length) {\n const lastSpace = text.lastIndexOf(' ', end);\n if (lastSpace > start) {\n end = lastSpace;\n }\n }\n\n chunks.push(text.substring(start, end));\n\n // Move start forward correctly\n const nextStart = end - OVERLAP_CHARS;\n if (nextStart <= start) {\n // Prevent infinite loop if overlap is too large\n start = end;\n } else {\n start = nextStart;\n }\n }\n\n return chunks;\n};\n\n/**\n * Generates an embedding for a given text using OpenAI's embedding API.\n * Trims the text if it exceeds the maximum allowed characters.\n *\n * @param text - The input text to generate an embedding for\n * @returns The embedding vector as a number array\n */\nconst generateEmbedding = async (text: string): Promise<number[]> => {\n try {\n const openaiClient = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });\n\n const response = await openaiClient.embeddings.create({\n model: EMBEDDING_MODEL,\n input: text,\n });\n\n return response.data[0].embedding;\n } catch (error) {\n console.error('Error generating embedding:', error);\n return [];\n }\n};\n\n/**\n * Indexes all Markdown documents by generating embeddings for each chunk and storing them in memory.\n * Persists per-document embeddings under `embeddings/<fileKey>.json`.\n * Handles cases where files have been updated and chunk counts have changed.\n */\nexport const indexMarkdownFiles = async (): Promise<void> => {\n const env = process.env.NODE_ENV;\n dotenv.config({\n path: [`.env.${env}.local`, `.env.${env}`, '.env.local', '.env'],\n });\n\n // Retrieve documentation and blog posts in English locale\n const frequentQuestions = await getFrequentQuestions();\n const docs = await getDocs();\n const blogs = await getBlogs();\n\n const files = { ...docs, ...blogs, ...frequentQuestions }; // Combine docs and blogs into a single object\n\n // Iterate over each file key (identifier) in the combined files\n for await (const fileKey of Object.keys(files)) {\n // Get the metadata of the file\n const fileMetadata = getMarkdownMetadata(\n files[fileKey as keyof typeof files] as string\n );\n\n // Split the document into chunks based on headings\n const fileChunks = chunkText(\n files[fileKey as keyof typeof files] as string\n );\n\n // Read existing embeddings for this file\n const existingEmbeddings = readEmbeddingsForFile(fileKey);\n\n // Check if the number of chunks has changed for this file\n const existingChunksForFile = Object.keys(existingEmbeddings);\n const currentChunkCount = fileChunks.length;\n const previousChunkCount = existingChunksForFile.length;\n\n let shouldRegenerateFileEmbeddings = false;\n\n // If chunk count differs, we need to regenerate embeddings for this file\n if (currentChunkCount !== previousChunkCount) {\n console.info(\n `File \"${fileKey}\" chunk count changed: ${previousChunkCount} -> ${currentChunkCount}. Regenerating embeddings.`\n );\n\n shouldRegenerateFileEmbeddings = !skipDocEmbeddingsIndex;\n }\n\n // Iterate over each chunk within the current file\n let resultForFile: Record<string, number[]> = {};\n for await (const chunkIndex of Object.keys(fileChunks)) {\n const chunkNumber = Number(chunkIndex) + 1; // Chunk number starts at 1\n const chunksNumber = fileChunks.length;\n\n const fileChunk = fileChunks[\n chunkIndex as keyof typeof fileChunks\n ] as string;\n\n const chunkKeyName = `chunk_${chunkNumber}`; // Unique key for the chunk within the file\n\n // Retrieve precomputed embedding if available and file hasn't changed\n const docEmbedding = !shouldRegenerateFileEmbeddings\n ? (existingEmbeddings[\n chunkKeyName as keyof typeof existingEmbeddings\n ] as number[] | undefined)\n : undefined;\n\n let embedding = docEmbedding; // Use existing embedding if available and valid\n\n if (!embedding) {\n embedding = await generateEmbedding(fileChunk); // Generate embedding if not present or file changed\n console.info(`- Generated new embedding: ${fileKey}/${chunkKeyName}`);\n }\n\n // Update the file-scoped result object with the embedding\n resultForFile = { ...resultForFile, [chunkKeyName]: embedding };\n\n // Store the embedding and content in the in-memory vector store\n vectorStore.push({\n fileKey,\n chunkNumber,\n embedding,\n content: fileChunk,\n docUrl: fileMetadata.url,\n docName: fileMetadata.title,\n });\n\n console.info(`- Indexed: ${fileKey}/${chunkKeyName}/${chunksNumber}`);\n }\n\n // Persist per-file embeddings if changed\n try {\n if (\n JSON.stringify(resultForFile) !== JSON.stringify(existingEmbeddings)\n ) {\n writeEmbeddingsForFile(fileKey, resultForFile);\n }\n } catch (error) {\n console.error(error);\n }\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"indexMarkdownFiles.cjs","names":["__dirname","vectorStore: VectorStoreEl[]","MODEL: AIOptions['model']","MODEL_TEMPERATURE: AIOptions['temperature']","aiDefaultOptions: AIOptions","AIProvider","EMBEDDING_MODEL: OpenAI.EmbeddingModel","OVERLAP_TOKENS: number","MAX_CHUNK_TOKENS: number","CHAR_BY_TOKEN: number","MAX_CHARS: number","OVERLAP_CHARS: number","chunks: string[]","OpenAI","resultForFile: Record<string, number[]>"],"sources":["../../../../../src/utils/AI/askDocQuestion/indexMarkdownFiles.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { getMarkdownMetadata } from '@intlayer/core';\nimport { getBlogs, getDocs, getFrequentQuestions } from '@intlayer/docs';\nimport dotenv from 'dotenv';\nimport { OpenAI } from 'openai';\nimport { type AIOptions, AIProvider } from '../aiSdk';\n\nconst OUTPUT_EMBEDDINGS_DIR = 'src/utils/AI/askDocQuestion/embeddings';\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nconst readEmbeddingsForFile = (fileKey: string): Record<string, number[]> => {\n try {\n return JSON.parse(\n readFileSync(\n `${__dirname}/embeddings/${fileKey.replace('.md', '.json')}`,\n 'utf-8'\n )\n ) as Record<string, number[]>;\n } catch {\n return {};\n }\n};\n\nconst writeEmbeddingsForFile = (\n fileKey: string,\n data: Record<string, number[]>\n): void => {\n const filePath = join(\n OUTPUT_EMBEDDINGS_DIR,\n `${fileKey.replace('.md', '.json')}`\n );\n const dir = dirname(filePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(filePath, JSON.stringify(data));\n};\n\ntype VectorStoreEl = {\n fileKey: string;\n chunkNumber: number;\n content: string;\n embedding: number[];\n docUrl: string;\n docName: string;\n};\n\n/**\n * Simple in-memory vector store to hold document embeddings and their content.\n * Each entry contains:\n * - fileKey: A unique key identifying the file\n * - chunkNumber: The number of the chunk within the document\n * - content: The chunk content\n * - embedding: The numerical embedding vector for the chunk\n */\nconst vectorStore: VectorStoreEl[] = [];\n\n/*\n * Ask question AI configuration\n */\nconst MODEL: AIOptions['model'] = 'chatgpt-4o-latest'; // Model to use for chat completions\nconst MODEL_TEMPERATURE: AIOptions['temperature'] = 0.1; // Temperature to use for chat completions\n\nexport const aiDefaultOptions: AIOptions = {\n provider: AIProvider.OPENAI,\n model: MODEL,\n temperature: MODEL_TEMPERATURE,\n};\n\n/*\n * Embedding model configuration\n */\nconst EMBEDDING_MODEL: OpenAI.EmbeddingModel = 'text-embedding-3-large'; // Model to use for embedding generation\nconst OVERLAP_TOKENS: number = 200; // Number of tokens to overlap between chunks\nconst MAX_CHUNK_TOKENS: number = 800; // Maximum number of tokens per chunk\nconst CHAR_BY_TOKEN: number = 4.15; // Approximate pessimistically the number of characters per token // Can use `tiktoken` or other tokenizers to calculate it more precisely\nconst MAX_CHARS: number = MAX_CHUNK_TOKENS * CHAR_BY_TOKEN;\nconst OVERLAP_CHARS: number = OVERLAP_TOKENS * CHAR_BY_TOKEN;\n\nconst skipDocEmbeddingsIndex = process.env.SKIP_DOC_EMBEDDINGS_INDEX === 'true';\n\n/**\n * Splits a given text into chunks ensuring each chunk does not exceed MAX_CHARS.\n * @param text - The input text to split.\n * @returns - Array of text chunks.\n */\nconst chunkText = (text: string): string[] => {\n const chunks: string[] = [];\n let start = 0;\n\n while (start < text.length) {\n let end = Math.min(start + MAX_CHARS, text.length);\n\n // Ensure we don't cut words in the middle (find nearest space)\n if (end < text.length) {\n const lastSpace = text.lastIndexOf(' ', end);\n if (lastSpace > start) {\n end = lastSpace;\n }\n }\n\n chunks.push(text.substring(start, end));\n\n // Move start forward correctly\n const nextStart = end - OVERLAP_CHARS;\n if (nextStart <= start) {\n // Prevent infinite loop if overlap is too large\n start = end;\n } else {\n start = nextStart;\n }\n }\n\n return chunks;\n};\n\n/**\n * Generates an embedding for a given text using OpenAI's embedding API.\n * Trims the text if it exceeds the maximum allowed characters.\n *\n * @param text - The input text to generate an embedding for\n * @returns The embedding vector as a number array\n */\nconst generateEmbedding = async (text: string): Promise<number[]> => {\n try {\n const openaiClient = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });\n\n const response = await openaiClient.embeddings.create({\n model: EMBEDDING_MODEL,\n input: text,\n });\n\n return response.data[0].embedding;\n } catch (error) {\n console.error('Error generating embedding:', error);\n return [];\n }\n};\n\n/**\n * Indexes all Markdown documents by generating embeddings for each chunk and storing them in memory.\n * Persists per-document embeddings under `embeddings/<fileKey>.json`.\n * Handles cases where files have been updated and chunk counts have changed.\n */\nexport const indexMarkdownFiles = async (): Promise<void> => {\n const env = process.env.NODE_ENV;\n dotenv.config({\n path: [`.env.${env}.local`, `.env.${env}`, '.env.local', '.env'],\n });\n\n // Retrieve documentation and blog posts in English locale\n const frequentQuestions = await getFrequentQuestions();\n const docs = await getDocs();\n const blogs = await getBlogs();\n\n const files = { ...docs, ...blogs, ...frequentQuestions }; // Combine docs and blogs into a single object\n\n // Iterate over each file key (identifier) in the combined files\n for await (const fileKey of Object.keys(files)) {\n // Get the metadata of the file\n const fileMetadata = getMarkdownMetadata(\n files[fileKey as keyof typeof files] as string\n );\n\n // Split the document into chunks based on headings\n const fileChunks = chunkText(\n files[fileKey as keyof typeof files] as string\n );\n\n // Read existing embeddings for this file\n const existingEmbeddings = readEmbeddingsForFile(fileKey);\n\n // Check if the number of chunks has changed for this file\n const existingChunksForFile = Object.keys(existingEmbeddings);\n const currentChunkCount = fileChunks.length;\n const previousChunkCount = existingChunksForFile.length;\n\n let shouldRegenerateFileEmbeddings = false;\n\n // If chunk count differs, we need to regenerate embeddings for this file\n if (currentChunkCount !== previousChunkCount) {\n console.info(\n `File \"${fileKey}\" chunk count changed: ${previousChunkCount} -> ${currentChunkCount}. Regenerating embeddings.`\n );\n\n shouldRegenerateFileEmbeddings = !skipDocEmbeddingsIndex;\n }\n\n // Iterate over each chunk within the current file\n let resultForFile: Record<string, number[]> = {};\n for await (const chunkIndex of Object.keys(fileChunks)) {\n const chunkNumber = Number(chunkIndex) + 1; // Chunk number starts at 1\n const chunksNumber = fileChunks.length;\n\n const fileChunk = fileChunks[\n chunkIndex as keyof typeof fileChunks\n ] as string;\n\n const chunkKeyName = `chunk_${chunkNumber}`; // Unique key for the chunk within the file\n\n // Retrieve precomputed embedding if available and file hasn't changed\n const docEmbedding = !shouldRegenerateFileEmbeddings\n ? (existingEmbeddings[\n chunkKeyName as keyof typeof existingEmbeddings\n ] as number[] | undefined)\n : undefined;\n\n let embedding = docEmbedding; // Use existing embedding if available and valid\n\n if (!embedding) {\n embedding = await generateEmbedding(fileChunk); // Generate embedding if not present or file changed\n console.info(`- Generated new embedding: ${fileKey}/${chunkKeyName}`);\n }\n\n // Update the file-scoped result object with the embedding\n resultForFile = { ...resultForFile, [chunkKeyName]: embedding };\n\n // Store the embedding and content in the in-memory vector store\n vectorStore.push({\n fileKey,\n chunkNumber,\n embedding,\n content: fileChunk,\n docUrl: fileMetadata.url,\n docName: fileMetadata.title,\n });\n\n console.info(`- Indexed: ${fileKey}/${chunkKeyName}/${chunksNumber}`);\n }\n\n // Persist per-file embeddings if changed\n try {\n if (\n JSON.stringify(resultForFile) !== JSON.stringify(existingEmbeddings)\n ) {\n writeEmbeddingsForFile(fileKey, resultForFile);\n }\n } catch (error) {\n console.error(error);\n }\n }\n};\n"],"mappings":"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"}
|
|
@@ -3,11 +3,8 @@ const require_logger_index = require('../../../logger/index.cjs');
|
|
|
3
3
|
const require__utils_asset = require('../../../_virtual/_utils_asset.cjs');
|
|
4
4
|
const require_utils_extractJSON = require('../../extractJSON.cjs');
|
|
5
5
|
let __intlayer_types = require("@intlayer/types");
|
|
6
|
-
__intlayer_types = require_rolldown_runtime.__toESM(__intlayer_types);
|
|
7
6
|
let __intlayer_core = require("@intlayer/core");
|
|
8
|
-
__intlayer_core = require_rolldown_runtime.__toESM(__intlayer_core);
|
|
9
7
|
let ai = require("ai");
|
|
10
|
-
ai = require_rolldown_runtime.__toESM(ai);
|
|
11
8
|
|
|
12
9
|
//#region src/utils/AI/auditDictionary/index.ts
|
|
13
10
|
const CHAT_GPT_PROMPT = require__utils_asset.readAsset("./PROMPT.md");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","Locales","extractJson"],"sources":["../../../../../src/utils/AI/auditDictionary/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core';\nimport { type Locale, Locales } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { extractJson } from '@utils/extractJSON';\nimport { generateText } from 'ai';\nimport type { Tag } from '@/types/tag.types';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\nexport type AuditOptions = {\n fileContent: string;\n filePath?: string;\n locales: Locale[];\n defaultLocale: Locale;\n tags: Tag[];\n aiConfig: AIConfig;\n applicationContext?: string;\n};\n\nexport type AuditFileResultData = {\n fileContent: {\n title: string;\n description: string;\n tags: string[];\n };\n tokenUsed: number;\n};\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\n\n/**\n * Format a locale with its name.\n *\n * @param locale - The locale to format.\n * @returns A string in the format \"locale: name\", e.g. \"en: English\".\n */\nconst formatLocaleWithName = (locale: Locale): string => {\n return `${locale}: ${getLocaleName(locale, Locales.ENGLISH)}`;\n};\n\n/**\n * Formats tag instructions for the AI prompt.\n * Creates a string with all available tags and their descriptions.\n *\n * @param tags - The list of tags to format.\n * @returns A formatted string with tag instructions.\n */\nconst formatTagInstructions = (tags: Tag[]): string => {\n if (!tags || tags.length === 0) return '';\n\n // Prepare the tag instructions.\n return [\n `Based on the dictionary content, identify specific tags from the list below that would be relevant:`,\n tags.map(({ key, description }) => `- ${key}: ${description}`).join('\\n\\n'),\n ].join('\\n\\n');\n};\n\n/**\n * Audits a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const auditDictionary = async ({\n fileContent,\n filePath,\n locales,\n defaultLocale,\n tags,\n aiConfig,\n applicationContext,\n}: AuditOptions): Promise<AuditFileResultData | undefined> => {\n const otherLocales = locales.filter((locale) => locale !== defaultLocale);\n\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{defaultLocale}}',\n formatLocaleWithName(defaultLocale)\n )\n .replace(\n '{{otherLocales}}',\n `{${otherLocales.map(formatLocaleWithName).join(', ')}}`\n )\n .replace('{{filePath}}', filePath ?? '')\n .replace('{{fileContent}}', fileContent)\n .replace('{{applicationContext}}', applicationContext ?? '')\n .replace('{{tagsInstructions}}', formatTagInstructions(tags));\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages: [{ role: 'system', content: prompt }],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: extractJson(newContent),\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","Locales","extractJson"],"sources":["../../../../../src/utils/AI/auditDictionary/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core';\nimport { type Locale, Locales } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { extractJson } from '@utils/extractJSON';\nimport { generateText } from 'ai';\nimport type { Tag } from '@/types/tag.types';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\nexport type AuditOptions = {\n fileContent: string;\n filePath?: string;\n locales: Locale[];\n defaultLocale: Locale;\n tags: Tag[];\n aiConfig: AIConfig;\n applicationContext?: string;\n};\n\nexport type AuditFileResultData = {\n fileContent: {\n title: string;\n description: string;\n tags: string[];\n };\n tokenUsed: number;\n};\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\n\n/**\n * Format a locale with its name.\n *\n * @param locale - The locale to format.\n * @returns A string in the format \"locale: name\", e.g. \"en: English\".\n */\nconst formatLocaleWithName = (locale: Locale): string => {\n return `${locale}: ${getLocaleName(locale, Locales.ENGLISH)}`;\n};\n\n/**\n * Formats tag instructions for the AI prompt.\n * Creates a string with all available tags and their descriptions.\n *\n * @param tags - The list of tags to format.\n * @returns A formatted string with tag instructions.\n */\nconst formatTagInstructions = (tags: Tag[]): string => {\n if (!tags || tags.length === 0) return '';\n\n // Prepare the tag instructions.\n return [\n `Based on the dictionary content, identify specific tags from the list below that would be relevant:`,\n tags.map(({ key, description }) => `- ${key}: ${description}`).join('\\n\\n'),\n ].join('\\n\\n');\n};\n\n/**\n * Audits a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const auditDictionary = async ({\n fileContent,\n filePath,\n locales,\n defaultLocale,\n tags,\n aiConfig,\n applicationContext,\n}: AuditOptions): Promise<AuditFileResultData | undefined> => {\n const otherLocales = locales.filter((locale) => locale !== defaultLocale);\n\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{defaultLocale}}',\n formatLocaleWithName(defaultLocale)\n )\n .replace(\n '{{otherLocales}}',\n `{${otherLocales.map(formatLocaleWithName).join(', ')}}`\n )\n .replace('{{filePath}}', filePath ?? '')\n .replace('{{fileContent}}', fileContent)\n .replace('{{applicationContext}}', applicationContext ?? '')\n .replace('{{tagsInstructions}}', formatTagInstructions(tags));\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages: [{ role: 'system', content: prompt }],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: extractJson(newContent),\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;;;;;AA6BA,MAAM,kBAAkBA,+BAAU,cAAc;AAEhD,MAAaC,mBAA8B,EAE1C;;;;;;;AAQD,MAAM,wBAAwB,WAA2B;AACvD,QAAO,GAAG,OAAO,uCAAkB,QAAQC,yBAAQ,QAAQ;;;;;;;;;AAU7D,MAAM,yBAAyB,SAAwB;AACrD,KAAI,CAAC,QAAQ,KAAK,WAAW,EAAG,QAAO;AAGvC,QAAO,CACL,uGACA,KAAK,KAAK,EAAE,KAAK,kBAAkB,KAAK,IAAI,IAAI,cAAc,CAAC,KAAK,OAAO,CAC5E,CAAC,KAAK,OAAO;;;;;;;AAQhB,MAAa,kBAAkB,OAAO,EACpC,aACA,UACA,SACA,eACA,MACA,UACA,yBAC4D;CAC5D,MAAM,eAAe,QAAQ,QAAQ,WAAW,WAAW,cAAc;CAGzE,MAAM,SAAS,gBAAgB,QAC7B,qBACA,qBAAqB,cAAc,CACpC,CACE,QACC,oBACA,IAAI,aAAa,IAAI,qBAAqB,CAAC,KAAK,KAAK,CAAC,GACvD,CACA,QAAQ,gBAAgB,YAAY,GAAG,CACvC,QAAQ,mBAAmB,YAAY,CACvC,QAAQ,0BAA0B,sBAAsB,GAAG,CAC3D,QAAQ,wBAAwB,sBAAsB,KAAK,CAAC;CAG/D,MAAM,EAAE,MAAM,YAAY,UAAU,2BAAmB;EACrD,GAAG;EACH,UAAU,CAAC;GAAE,MAAM;GAAU,SAAS;GAAQ,CAAC;EAChD,CAAC;AAEF,6BAAO,KAAK,GAAG,OAAO,eAAe,EAAE,6BAA6B;AAEpE,QAAO;EACL,aAAaC,sCAAY,WAAW;EACpC,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -2,11 +2,8 @@ const require_rolldown_runtime = require('../../../_virtual/rolldown_runtime.cjs
|
|
|
2
2
|
const require_logger_index = require('../../../logger/index.cjs');
|
|
3
3
|
const require__utils_asset = require('../../../_virtual/_utils_asset.cjs');
|
|
4
4
|
let __intlayer_types = require("@intlayer/types");
|
|
5
|
-
__intlayer_types = require_rolldown_runtime.__toESM(__intlayer_types);
|
|
6
5
|
let __intlayer_core = require("@intlayer/core");
|
|
7
|
-
__intlayer_core = require_rolldown_runtime.__toESM(__intlayer_core);
|
|
8
6
|
let ai = require("ai");
|
|
9
|
-
ai = require_rolldown_runtime.__toESM(ai);
|
|
10
7
|
|
|
11
8
|
//#region src/utils/AI/auditDictionaryField/index.ts
|
|
12
9
|
const CHAT_GPT_PROMPT = require__utils_asset.readAsset("./PROMPT.md");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","Locales"],"sources":["../../../../../src/utils/AI/auditDictionaryField/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core';\nimport type { KeyPath } from '@intlayer/types';\nimport { type Locale, Locales } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { generateText } from 'ai';\nimport type { Tag } from '@/types/tag.types';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\nexport type AuditDictionaryFieldOptions = {\n fileContent: string;\n locales: Locale[];\n keyPath: KeyPath[];\n tags: Tag[];\n aiConfig: AIConfig;\n applicationContext?: string;\n};\n\nexport type AuditDictionaryFieldResultData = {\n fileContent: string;\n tokenUsed: number;\n};\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\n\n/**\n * Format a locale with its name.\n *\n * @param locale - The locale to format.\n * @returns A string in the format \"locale: name\", e.g. \"en: English\".\n */\nconst formatLocaleWithName = (locale: Locale): string => {\n return `${locale}: ${getLocaleName(locale, Locales.ENGLISH)}`;\n};\n\n/**\n * Formats tag instructions for the AI prompt.\n *\n * @param tags - Array of tags to format\n * @returns A formatted string with tag instructions\n */\nconst formatTagInstructions = (tags: Tag[]): string => {\n if (!tags || tags.length === 0) {\n return '';\n }\n\n return `Based on the dictionary content, identify specific tags from the list below that would be relevant:\n \n${tags.map(({ key, description }) => `- ${key}: ${description}`).join('\\n\\n')}`;\n};\n\n/**\n * Audits a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const auditDictionaryField = async ({\n fileContent,\n applicationContext,\n locales,\n keyPath,\n tags,\n aiConfig,\n}: AuditDictionaryFieldOptions): Promise<\n AuditDictionaryFieldResultData | undefined\n> => {\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{otherLocales}}',\n `{${locales.map(formatLocaleWithName).join(', ')}}`\n )\n .replace('{{keyPath}}', JSON.stringify(keyPath))\n .replace('{{fileContent}}', fileContent)\n .replace('{{applicationContext}}', applicationContext ?? '')\n .replace('{{tagsInstructions}}', formatTagInstructions(tags));\n\n if (!aiConfig) {\n logger.error('Failed to configure AI model');\n return undefined;\n }\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages: [{ role: 'system', content: prompt }],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: newContent,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","Locales"],"sources":["../../../../../src/utils/AI/auditDictionaryField/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core';\nimport type { KeyPath } from '@intlayer/types';\nimport { type Locale, Locales } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { generateText } from 'ai';\nimport type { Tag } from '@/types/tag.types';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\nexport type AuditDictionaryFieldOptions = {\n fileContent: string;\n locales: Locale[];\n keyPath: KeyPath[];\n tags: Tag[];\n aiConfig: AIConfig;\n applicationContext?: string;\n};\n\nexport type AuditDictionaryFieldResultData = {\n fileContent: string;\n tokenUsed: number;\n};\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\n\n/**\n * Format a locale with its name.\n *\n * @param locale - The locale to format.\n * @returns A string in the format \"locale: name\", e.g. \"en: English\".\n */\nconst formatLocaleWithName = (locale: Locale): string => {\n return `${locale}: ${getLocaleName(locale, Locales.ENGLISH)}`;\n};\n\n/**\n * Formats tag instructions for the AI prompt.\n *\n * @param tags - Array of tags to format\n * @returns A formatted string with tag instructions\n */\nconst formatTagInstructions = (tags: Tag[]): string => {\n if (!tags || tags.length === 0) {\n return '';\n }\n\n return `Based on the dictionary content, identify specific tags from the list below that would be relevant:\n \n${tags.map(({ key, description }) => `- ${key}: ${description}`).join('\\n\\n')}`;\n};\n\n/**\n * Audits a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const auditDictionaryField = async ({\n fileContent,\n applicationContext,\n locales,\n keyPath,\n tags,\n aiConfig,\n}: AuditDictionaryFieldOptions): Promise<\n AuditDictionaryFieldResultData | undefined\n> => {\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{otherLocales}}',\n `{${locales.map(formatLocaleWithName).join(', ')}}`\n )\n .replace('{{keyPath}}', JSON.stringify(keyPath))\n .replace('{{fileContent}}', fileContent)\n .replace('{{applicationContext}}', applicationContext ?? '')\n .replace('{{tagsInstructions}}', formatTagInstructions(tags));\n\n if (!aiConfig) {\n logger.error('Failed to configure AI model');\n return undefined;\n }\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages: [{ role: 'system', content: prompt }],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: newContent,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;;;;AAwBA,MAAM,kBAAkBA,+BAAU,cAAc;AAEhD,MAAaC,mBAA8B,EAE1C;;;;;;;AAQD,MAAM,wBAAwB,WAA2B;AACvD,QAAO,GAAG,OAAO,uCAAkB,QAAQC,yBAAQ,QAAQ;;;;;;;;AAS7D,MAAM,yBAAyB,SAAwB;AACrD,KAAI,CAAC,QAAQ,KAAK,WAAW,EAC3B,QAAO;AAGT,QAAO;;EAEP,KAAK,KAAK,EAAE,KAAK,kBAAkB,KAAK,IAAI,IAAI,cAAc,CAAC,KAAK,OAAO;;;;;;;AAQ7E,MAAa,uBAAuB,OAAO,EACzC,aACA,oBACA,SACA,SACA,MACA,eAGG;CAEH,MAAM,SAAS,gBAAgB,QAC7B,oBACA,IAAI,QAAQ,IAAI,qBAAqB,CAAC,KAAK,KAAK,CAAC,GAClD,CACE,QAAQ,eAAe,KAAK,UAAU,QAAQ,CAAC,CAC/C,QAAQ,mBAAmB,YAAY,CACvC,QAAQ,0BAA0B,sBAAsB,GAAG,CAC3D,QAAQ,wBAAwB,sBAAsB,KAAK,CAAC;AAE/D,KAAI,CAAC,UAAU;AACb,8BAAO,MAAM,+BAA+B;AAC5C;;CAIF,MAAM,EAAE,MAAM,YAAY,UAAU,2BAAmB;EACrD,GAAG;EACH,UAAU,CAAC;GAAE,MAAM;GAAU,SAAS;GAAQ,CAAC;EAChD,CAAC;AAEF,6BAAO,KAAK,GAAG,OAAO,eAAe,EAAE,6BAA6B;AAEpE,QAAO;EACL,aAAa;EACb,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -3,7 +3,6 @@ const require_logger_index = require('../../../logger/index.cjs');
|
|
|
3
3
|
const require__utils_asset = require('../../../_virtual/_utils_asset.cjs');
|
|
4
4
|
const require_utils_extractJSON = require('../../extractJSON.cjs');
|
|
5
5
|
let ai = require("ai");
|
|
6
|
-
ai = require_rolldown_runtime.__toESM(ai);
|
|
7
6
|
|
|
8
7
|
//#region src/utils/AI/auditDictionaryMetadata/index.ts
|
|
9
8
|
const CHAT_GPT_PROMPT = require__utils_asset.readAsset("./PROMPT.md");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","extractJson"],"sources":["../../../../../src/utils/AI/auditDictionaryMetadata/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { logger } from '@logger';\nimport { extractJson } from '@utils/extractJSON';\nimport { generateText } from 'ai';\nimport type { Tag } from '@/types/tag.types';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\nexport type AuditOptions = {\n fileContent: string;\n tags: Tag[];\n aiConfig: AIConfig;\n applicationContext?: string;\n};\n\nexport type AuditFileResultData = {\n fileContent: {\n title: string;\n description: string;\n tags: string[];\n };\n tokenUsed: number;\n};\n\n// The prompt template to send to AI models\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\n\n/**\n * Audits a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const auditDictionaryMetadata = async ({\n tags,\n fileContent,\n applicationContext,\n aiConfig,\n}: AuditOptions): Promise<AuditFileResultData | undefined> => {\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{tags}}',\n `${JSON.stringify(\n tags\n .map(({ key, description }) => `- ${key}: ${description}`)\n .join('\\n\\n'),\n null,\n 2\n )}`\n )\n .replace('{{contentDeclaration}}', fileContent)\n .replace('{{applicationContext}}', applicationContext ?? '');\n\n if (!aiConfig) {\n logger.error('Failed to configure AI model');\n return undefined;\n }\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages: [{ role: 'system', content: prompt }],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: extractJson(newContent),\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","extractJson"],"sources":["../../../../../src/utils/AI/auditDictionaryMetadata/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { logger } from '@logger';\nimport { extractJson } from '@utils/extractJSON';\nimport { generateText } from 'ai';\nimport type { Tag } from '@/types/tag.types';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\nexport type AuditOptions = {\n fileContent: string;\n tags: Tag[];\n aiConfig: AIConfig;\n applicationContext?: string;\n};\n\nexport type AuditFileResultData = {\n fileContent: {\n title: string;\n description: string;\n tags: string[];\n };\n tokenUsed: number;\n};\n\n// The prompt template to send to AI models\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\n\n/**\n * Audits a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const auditDictionaryMetadata = async ({\n tags,\n fileContent,\n applicationContext,\n aiConfig,\n}: AuditOptions): Promise<AuditFileResultData | undefined> => {\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{tags}}',\n `${JSON.stringify(\n tags\n .map(({ key, description }) => `- ${key}: ${description}`)\n .join('\\n\\n'),\n null,\n 2\n )}`\n )\n .replace('{{contentDeclaration}}', fileContent)\n .replace('{{applicationContext}}', applicationContext ?? '');\n\n if (!aiConfig) {\n logger.error('Failed to configure AI model');\n return undefined;\n }\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages: [{ role: 'system', content: prompt }],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: extractJson(newContent),\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;;;AAwBA,MAAM,kBAAkBA,+BAAU,cAAc;AAEhD,MAAaC,mBAA8B,EAE1C;;;;;;AAOD,MAAa,0BAA0B,OAAO,EAC5C,MACA,aACA,oBACA,eAC4D;CAE5D,MAAM,SAAS,gBAAgB,QAC7B,YACA,GAAG,KAAK,UACN,KACG,KAAK,EAAE,KAAK,kBAAkB,KAAK,IAAI,IAAI,cAAc,CACzD,KAAK,OAAO,EACf,MACA,EACD,GACF,CACE,QAAQ,0BAA0B,YAAY,CAC9C,QAAQ,0BAA0B,sBAAsB,GAAG;AAE9D,KAAI,CAAC,UAAU;AACb,8BAAO,MAAM,+BAA+B;AAC5C;;CAIF,MAAM,EAAE,MAAM,YAAY,UAAU,2BAAmB;EACrD,GAAG;EACH,UAAU,CAAC;GAAE,MAAM;GAAU,SAAS;GAAQ,CAAC;EAChD,CAAC;AAEF,6BAAO,KAAK,GAAG,OAAO,eAAe,EAAE,6BAA6B;AAEpE,QAAO;EACL,aAAaC,sCAAY,WAAW;EACpC,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -3,7 +3,6 @@ const require_logger_index = require('../../../logger/index.cjs');
|
|
|
3
3
|
const require__utils_asset = require('../../../_virtual/_utils_asset.cjs');
|
|
4
4
|
const require_utils_extractJSON = require('../../extractJSON.cjs');
|
|
5
5
|
let ai = require("ai");
|
|
6
|
-
ai = require_rolldown_runtime.__toESM(ai);
|
|
7
6
|
|
|
8
7
|
//#region src/utils/AI/auditTag/index.ts
|
|
9
8
|
const CHAT_GPT_PROMPT = require__utils_asset.readAsset("./PROMPT.md");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","extractJson"],"sources":["../../../../../src/utils/AI/auditTag/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { logger } from '@logger';\nimport { extractJson } from '@utils/extractJSON';\nimport { generateText } from 'ai';\nimport type { Dictionary } from '@/types/dictionary.types';\nimport type { TagAPI } from '@/types/tag.types';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\nexport type AuditOptions = {\n dictionaries: Dictionary[];\n tag: TagAPI;\n aiConfig: AIConfig;\n applicationContext?: string;\n};\n\nexport type TranslateJSONResultData = {\n fileContent: string;\n tokenUsed: number;\n};\n\n// The prompt template to send to AI models\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\n\n/**\n * Audits a tag by constructing a prompt for AI models.\n * The prompt includes details about the tag and related dictionaries.\n */\nexport const auditTag = async ({\n dictionaries,\n tag,\n aiConfig,\n applicationContext,\n}: AuditOptions): Promise<TranslateJSONResultData | undefined> => {\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{tag.description}}',\n tag.description ?? ''\n )\n .replace('{{tag.key}}', tag.key)\n .replace('{{dictionaries}}', JSON.stringify(dictionaries, null, 2))\n .replace('{{applicationContext}}', applicationContext ?? '');\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages: [{ role: 'system', content: prompt }],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: extractJson(newContent),\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","extractJson"],"sources":["../../../../../src/utils/AI/auditTag/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { logger } from '@logger';\nimport { extractJson } from '@utils/extractJSON';\nimport { generateText } from 'ai';\nimport type { Dictionary } from '@/types/dictionary.types';\nimport type { TagAPI } from '@/types/tag.types';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\nexport type AuditOptions = {\n dictionaries: Dictionary[];\n tag: TagAPI;\n aiConfig: AIConfig;\n applicationContext?: string;\n};\n\nexport type TranslateJSONResultData = {\n fileContent: string;\n tokenUsed: number;\n};\n\n// The prompt template to send to AI models\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\n\n/**\n * Audits a tag by constructing a prompt for AI models.\n * The prompt includes details about the tag and related dictionaries.\n */\nexport const auditTag = async ({\n dictionaries,\n tag,\n aiConfig,\n applicationContext,\n}: AuditOptions): Promise<TranslateJSONResultData | undefined> => {\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{tag.description}}',\n tag.description ?? ''\n )\n .replace('{{tag.key}}', tag.key)\n .replace('{{dictionaries}}', JSON.stringify(dictionaries, null, 2))\n .replace('{{applicationContext}}', applicationContext ?? '');\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages: [{ role: 'system', content: prompt }],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: extractJson(newContent),\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;;;AAqBA,MAAM,kBAAkBA,+BAAU,cAAc;AAEhD,MAAaC,mBAA8B,EAE1C;;;;;AAMD,MAAa,WAAW,OAAO,EAC7B,cACA,KACA,UACA,yBACgE;CAEhE,MAAM,SAAS,gBAAgB,QAC7B,uBACA,IAAI,eAAe,GACpB,CACE,QAAQ,eAAe,IAAI,IAAI,CAC/B,QAAQ,oBAAoB,KAAK,UAAU,cAAc,MAAM,EAAE,CAAC,CAClE,QAAQ,0BAA0B,sBAAsB,GAAG;CAG9D,MAAM,EAAE,MAAM,YAAY,UAAU,2BAAmB;EACrD,GAAG;EACH,UAAU,CAAC;GAAE,MAAM;GAAU,SAAS;GAAQ,CAAC;EAChD,CAAC;AAEF,6BAAO,KAAK,GAAG,OAAO,eAAe,EAAE,6BAA6B;AAEpE,QAAO;EACL,aAAaC,sCAAY,WAAW;EACpC,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -3,7 +3,6 @@ const require_logger_index = require('../../../logger/index.cjs');
|
|
|
3
3
|
const require_utils_AI_aiSdk = require('../aiSdk.cjs');
|
|
4
4
|
const require__utils_asset = require('../../../_virtual/_utils_asset.cjs');
|
|
5
5
|
let ai = require("ai");
|
|
6
|
-
ai = require_rolldown_runtime.__toESM(ai);
|
|
7
6
|
|
|
8
7
|
//#region src/utils/AI/autocomplete/index.ts
|
|
9
8
|
const CHAT_GPT_PROMPT = require__utils_asset.readAsset("./PROMPT.md");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","AIProvider"],"sources":["../../../../../src/utils/AI/autocomplete/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { logger } from '@logger';\nimport { generateText } from 'ai';\nimport { type AIConfig, type AIOptions, AIProvider } from '../aiSdk';\n\nexport type AutocompleteOptions = {\n text: string;\n aiConfig: AIConfig;\n applicationContext?: string;\n contextBefore?: string;\n currentLine?: string;\n contextAfter?: string;\n};\n\nexport type AutocompleteFileResultData = {\n autocompletion: string;\n tokenUsed: number;\n};\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n provider: AIProvider.OPENAI,\n model: 'gpt-4o-mini',\n temperature: 0.7,\n};\n\n/**\n * Autocompletes a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const autocomplete = async ({\n text,\n aiConfig,\n applicationContext,\n contextBefore,\n currentLine,\n contextAfter,\n}: AutocompleteOptions): Promise<AutocompleteFileResultData | undefined> => {\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{applicationContext}}',\n applicationContext ?? ''\n )\n .replace('{{contextBefore}}', contextBefore ?? '')\n .replace('{{currentLine}}', currentLine ?? '')\n .replace('{{contextAfter}}', contextAfter ?? '');\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages: [\n { role: 'system', content: prompt },\n { role: 'assistant', content: text },\n ],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n autocompletion: newContent,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","AIProvider"],"sources":["../../../../../src/utils/AI/autocomplete/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { logger } from '@logger';\nimport { generateText } from 'ai';\nimport { type AIConfig, type AIOptions, AIProvider } from '../aiSdk';\n\nexport type AutocompleteOptions = {\n text: string;\n aiConfig: AIConfig;\n applicationContext?: string;\n contextBefore?: string;\n currentLine?: string;\n contextAfter?: string;\n};\n\nexport type AutocompleteFileResultData = {\n autocompletion: string;\n tokenUsed: number;\n};\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n provider: AIProvider.OPENAI,\n model: 'gpt-4o-mini',\n temperature: 0.7,\n};\n\n/**\n * Autocompletes a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const autocomplete = async ({\n text,\n aiConfig,\n applicationContext,\n contextBefore,\n currentLine,\n contextAfter,\n}: AutocompleteOptions): Promise<AutocompleteFileResultData | undefined> => {\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{applicationContext}}',\n applicationContext ?? ''\n )\n .replace('{{contextBefore}}', contextBefore ?? '')\n .replace('{{currentLine}}', currentLine ?? '')\n .replace('{{contextAfter}}', contextAfter ?? '');\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages: [\n { role: 'system', content: prompt },\n { role: 'assistant', content: text },\n ],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n autocompletion: newContent,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;;;AAoBA,MAAM,kBAAkBA,+BAAU,cAAc;AAEhD,MAAaC,mBAA8B;CACzC,UAAUC,kCAAW;CACrB,OAAO;CACP,aAAa;CACd;;;;;;AAOD,MAAa,eAAe,OAAO,EACjC,MACA,UACA,oBACA,eACA,aACA,mBAC0E;CAE1E,MAAM,SAAS,gBAAgB,QAC7B,0BACA,sBAAsB,GACvB,CACE,QAAQ,qBAAqB,iBAAiB,GAAG,CACjD,QAAQ,mBAAmB,eAAe,GAAG,CAC7C,QAAQ,oBAAoB,gBAAgB,GAAG;CAGlD,MAAM,EAAE,MAAM,YAAY,UAAU,2BAAmB;EACrD,GAAG;EACH,UAAU,CACR;GAAE,MAAM;GAAU,SAAS;GAAQ,EACnC;GAAE,MAAM;GAAa,SAAS;GAAM,CACrC;EACF,CAAC;AAEF,6BAAO,KAAK,GAAG,OAAO,eAAe,EAAE,6BAA6B;AAEpE,QAAO;EACL,gBAAgB;EAChB,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const require_rolldown_runtime = require('../../../_virtual/rolldown_runtime.cjs');
|
|
2
2
|
const require_logger_index = require('../../../logger/index.cjs');
|
|
3
3
|
let ai = require("ai");
|
|
4
|
-
ai = require_rolldown_runtime.__toESM(ai);
|
|
5
4
|
|
|
6
5
|
//#region src/utils/AI/customQuery/index.ts
|
|
7
6
|
const aiDefaultOptions = { model: "gpt-4o-mini" };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["aiDefaultOptions: AIOptions"],"sources":["../../../../../src/utils/AI/customQuery/index.ts"],"sourcesContent":["import { logger } from '@logger';\nimport { generateText } from 'ai';\nimport type { AIConfig, AIOptions, Messages } from '../aiSdk';\n\nexport type CustomQueryOptions = {\n messages: Messages;\n aiConfig: AIConfig;\n};\n\nexport type CustomQueryResultData = {\n fileContent: string;\n tokenUsed: number;\n};\n\nexport const aiDefaultOptions: AIOptions = {\n model: 'gpt-4o-mini',\n // Keep default options\n};\n\n/**\n * CustomQuerys a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const customQuery = async ({\n messages,\n aiConfig,\n}: CustomQueryOptions): Promise<CustomQueryResultData | undefined> => {\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages,\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: newContent,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["aiDefaultOptions: AIOptions"],"sources":["../../../../../src/utils/AI/customQuery/index.ts"],"sourcesContent":["import { logger } from '@logger';\nimport { generateText } from 'ai';\nimport type { AIConfig, AIOptions, Messages } from '../aiSdk';\n\nexport type CustomQueryOptions = {\n messages: Messages;\n aiConfig: AIConfig;\n};\n\nexport type CustomQueryResultData = {\n fileContent: string;\n tokenUsed: number;\n};\n\nexport const aiDefaultOptions: AIOptions = {\n model: 'gpt-4o-mini',\n // Keep default options\n};\n\n/**\n * CustomQuerys a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const customQuery = async ({\n messages,\n aiConfig,\n}: CustomQueryOptions): Promise<CustomQueryResultData | undefined> => {\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages,\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: newContent,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;AAcA,MAAaA,mBAA8B,EACzC,OAAO,eAER;;;;;;AAOD,MAAa,cAAc,OAAO,EAChC,UACA,eACoE;CAEpE,MAAM,EAAE,MAAM,YAAY,UAAU,2BAAmB;EACrD,GAAG;EACH;EACD,CAAC;AAEF,6BAAO,KAAK,GAAG,OAAO,eAAe,EAAE,6BAA6B;AAEpE,QAAO;EACL,aAAa;EACb,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -4,11 +4,8 @@ const require_utils_AI_aiSdk = require('../aiSdk.cjs');
|
|
|
4
4
|
const require__utils_asset = require('../../../_virtual/_utils_asset.cjs');
|
|
5
5
|
const require_utils_extractJSON = require('../../extractJSON.cjs');
|
|
6
6
|
let __intlayer_types = require("@intlayer/types");
|
|
7
|
-
__intlayer_types = require_rolldown_runtime.__toESM(__intlayer_types);
|
|
8
7
|
let __intlayer_core = require("@intlayer/core");
|
|
9
|
-
__intlayer_core = require_rolldown_runtime.__toESM(__intlayer_core);
|
|
10
8
|
let ai = require("ai");
|
|
11
|
-
ai = require_rolldown_runtime.__toESM(ai);
|
|
12
9
|
|
|
13
10
|
//#region src/utils/AI/translateJSON/index.ts
|
|
14
11
|
const CHAT_GPT_PROMPT = require__utils_asset.readAsset("./PROMPT.md");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","AIProvider","Locales","extractJson"],"sources":["../../../../../src/utils/AI/translateJSON/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core';\nimport { type Locale, Locales } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { extractJson } from '@utils/extractJSON';\nimport { generateText } from 'ai';\nimport type { Tag } from '@/types/tag.types';\nimport { type AIConfig, type AIOptions, AIProvider } from '../aiSdk';\n\nexport type TranslateJSONOptions = {\n entryFileContent: JSON;\n presetOutputContent: JSON;\n dictionaryDescription?: string;\n entryLocale: Locale;\n outputLocale: Locale;\n tags: Tag[];\n aiConfig: AIConfig;\n mode: 'complete' | 'review';\n applicationContext?: string;\n};\n\nexport type TranslateJSONResultData = {\n fileContent: string;\n tokenUsed: number;\n};\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n provider: AIProvider.OPENAI,\n model: 'gpt-5-mini',\n};\n\n/**\n * Format a locale with its name.\n *\n * @param locale - The locale to format.\n * @returns A string in the format \"locale: name\", e.g. \"en: English\".\n */\nconst formatLocaleWithName = (locale: Locale): string =>\n `${locale}: ${getLocaleName(locale, Locales.ENGLISH)}`;\n\n/**\n * Formats tag instructions for the AI prompt.\n * Creates a string with all available tags and their descriptions.\n *\n * @param tags - The list of tags to format.\n * @returns A formatted string with tag instructions.\n */\nconst formatTagInstructions = (tags: Tag[]): string => {\n if (!tags || tags.length === 0) {\n return '';\n }\n\n // Prepare the tag instructions.\n return `Based on the dictionary content, identify specific tags from the list below that would be relevant:\n \n${tags.map(({ key, description }) => `- ${key}: ${description}`).join('\\n\\n')}`;\n};\n\nconst getModeInstructions = (mode: 'complete' | 'review'): string => {\n if (mode === 'complete') {\n return 'Mode: \"Complete\" - Enrich the preset content with the missing keys and values in the output locale. Do not update existing keys. Everything should be returned in the output.';\n }\n\n return 'Mode: \"Review\" - Fill missing content and review existing keys from the preset content. If a key from the entry is missing in the output, it must be translated to the target language and added. If you detect misspelled content, or content that should be reformulated, correct it. If a translation is not coherent with the desired language, translate it.';\n};\n\n/**\n * TranslateJSONs a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const translateJSON = async ({\n entryFileContent,\n presetOutputContent,\n dictionaryDescription,\n aiConfig,\n entryLocale,\n outputLocale,\n tags,\n mode,\n applicationContext,\n}: TranslateJSONOptions): Promise<TranslateJSONResultData | undefined> => {\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{entryLocale}}',\n formatLocaleWithName(entryLocale)\n )\n .replace('{{outputLocale}}', formatLocaleWithName(outputLocale))\n .replace('{{entryFileContent}}', JSON.stringify(entryFileContent))\n .replace('{{presetOutputContent}}', JSON.stringify(presetOutputContent))\n .replace('{{dictionaryDescription}}', dictionaryDescription ?? '')\n .replace('{{applicationContext}}', applicationContext ?? '')\n .replace('{{tagsInstructions}}', formatTagInstructions(tags))\n .replace('{{modeInstructions}}', getModeInstructions(mode));\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages: [{ role: 'system', content: prompt }],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: extractJson(newContent),\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","AIProvider","Locales","extractJson"],"sources":["../../../../../src/utils/AI/translateJSON/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core';\nimport { type Locale, Locales } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { extractJson } from '@utils/extractJSON';\nimport { generateText } from 'ai';\nimport type { Tag } from '@/types/tag.types';\nimport { type AIConfig, type AIOptions, AIProvider } from '../aiSdk';\n\nexport type TranslateJSONOptions = {\n entryFileContent: JSON;\n presetOutputContent: JSON;\n dictionaryDescription?: string;\n entryLocale: Locale;\n outputLocale: Locale;\n tags: Tag[];\n aiConfig: AIConfig;\n mode: 'complete' | 'review';\n applicationContext?: string;\n};\n\nexport type TranslateJSONResultData = {\n fileContent: string;\n tokenUsed: number;\n};\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n provider: AIProvider.OPENAI,\n model: 'gpt-5-mini',\n};\n\n/**\n * Format a locale with its name.\n *\n * @param locale - The locale to format.\n * @returns A string in the format \"locale: name\", e.g. \"en: English\".\n */\nconst formatLocaleWithName = (locale: Locale): string =>\n `${locale}: ${getLocaleName(locale, Locales.ENGLISH)}`;\n\n/**\n * Formats tag instructions for the AI prompt.\n * Creates a string with all available tags and their descriptions.\n *\n * @param tags - The list of tags to format.\n * @returns A formatted string with tag instructions.\n */\nconst formatTagInstructions = (tags: Tag[]): string => {\n if (!tags || tags.length === 0) {\n return '';\n }\n\n // Prepare the tag instructions.\n return `Based on the dictionary content, identify specific tags from the list below that would be relevant:\n \n${tags.map(({ key, description }) => `- ${key}: ${description}`).join('\\n\\n')}`;\n};\n\nconst getModeInstructions = (mode: 'complete' | 'review'): string => {\n if (mode === 'complete') {\n return 'Mode: \"Complete\" - Enrich the preset content with the missing keys and values in the output locale. Do not update existing keys. Everything should be returned in the output.';\n }\n\n return 'Mode: \"Review\" - Fill missing content and review existing keys from the preset content. If a key from the entry is missing in the output, it must be translated to the target language and added. If you detect misspelled content, or content that should be reformulated, correct it. If a translation is not coherent with the desired language, translate it.';\n};\n\n/**\n * TranslateJSONs a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const translateJSON = async ({\n entryFileContent,\n presetOutputContent,\n dictionaryDescription,\n aiConfig,\n entryLocale,\n outputLocale,\n tags,\n mode,\n applicationContext,\n}: TranslateJSONOptions): Promise<TranslateJSONResultData | undefined> => {\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{entryLocale}}',\n formatLocaleWithName(entryLocale)\n )\n .replace('{{outputLocale}}', formatLocaleWithName(outputLocale))\n .replace('{{entryFileContent}}', JSON.stringify(entryFileContent))\n .replace('{{presetOutputContent}}', JSON.stringify(presetOutputContent))\n .replace('{{dictionaryDescription}}', dictionaryDescription ?? '')\n .replace('{{applicationContext}}', applicationContext ?? '')\n .replace('{{tagsInstructions}}', formatTagInstructions(tags))\n .replace('{{modeInstructions}}', getModeInstructions(mode));\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages: [{ role: 'system', content: prompt }],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: extractJson(newContent),\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;;;;;;AA2BA,MAAM,kBAAkBA,+BAAU,cAAc;AAEhD,MAAaC,mBAA8B;CACzC,UAAUC,kCAAW;CACrB,OAAO;CACR;;;;;;;AAQD,MAAM,wBAAwB,WAC5B,GAAG,OAAO,uCAAkB,QAAQC,yBAAQ,QAAQ;;;;;;;;AAStD,MAAM,yBAAyB,SAAwB;AACrD,KAAI,CAAC,QAAQ,KAAK,WAAW,EAC3B,QAAO;AAIT,QAAO;;EAEP,KAAK,KAAK,EAAE,KAAK,kBAAkB,KAAK,IAAI,IAAI,cAAc,CAAC,KAAK,OAAO;;AAG7E,MAAM,uBAAuB,SAAwC;AACnE,KAAI,SAAS,WACX,QAAO;AAGT,QAAO;;;;;;;AAQT,MAAa,gBAAgB,OAAO,EAClC,kBACA,qBACA,uBACA,UACA,aACA,cACA,MACA,MACA,yBACwE;CAExE,MAAM,SAAS,gBAAgB,QAC7B,mBACA,qBAAqB,YAAY,CAClC,CACE,QAAQ,oBAAoB,qBAAqB,aAAa,CAAC,CAC/D,QAAQ,wBAAwB,KAAK,UAAU,iBAAiB,CAAC,CACjE,QAAQ,2BAA2B,KAAK,UAAU,oBAAoB,CAAC,CACvE,QAAQ,6BAA6B,yBAAyB,GAAG,CACjE,QAAQ,0BAA0B,sBAAsB,GAAG,CAC3D,QAAQ,wBAAwB,sBAAsB,KAAK,CAAC,CAC5D,QAAQ,wBAAwB,oBAAoB,KAAK,CAAC;CAG7D,MAAM,EAAE,MAAM,YAAY,UAAU,2BAAmB;EACrD,GAAG;EACH,UAAU,CAAC;GAAE,MAAM;GAAU,SAAS;GAAQ,CAAC;EAChD,CAAC;AAEF,6BAAO,KAAK,GAAG,OAAO,eAAe,EAAE,6BAA6B;AAEpE,QAAO;EACL,aAAaC,sCAAY,WAAW;EACpC,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -6,7 +6,7 @@ let AccessRule = /* @__PURE__ */ function(AccessRule$1) {
|
|
|
6
6
|
AccessRule$1["none"] = "none";
|
|
7
7
|
AccessRule$1["authenticated"] = "authenticated";
|
|
8
8
|
AccessRule$1["admin"] = "admin";
|
|
9
|
-
AccessRule$1["noneAuthenticated"] = "
|
|
9
|
+
AccessRule$1["noneAuthenticated"] = "not-authenticated";
|
|
10
10
|
AccessRule$1["hasOrganization"] = "has-organization";
|
|
11
11
|
AccessRule$1["hasProject"] = "has-project";
|
|
12
12
|
AccessRule$1["hasBearer"] = "has-bearer";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accessControl.cjs","names":["accessRuleArray: AccessRule[]","messages: string[]","HttpStatusCodes"],"sources":["../../../src/utils/accessControl.ts"],"sourcesContent":["import { logger } from '@logger';\n\nimport type { NextFunction, Request, Response } from 'express';\nimport { HttpStatusCodes } from './httpStatusCodes';\n\nexport enum AccessRule {\n none = 'none',\n authenticated = 'authenticated',\n admin = 'admin',\n noneAuthenticated = '
|
|
1
|
+
{"version":3,"file":"accessControl.cjs","names":["accessRuleArray: AccessRule[]","messages: string[]","HttpStatusCodes"],"sources":["../../../src/utils/accessControl.ts"],"sourcesContent":["import { logger } from '@logger';\n\nimport type { NextFunction, Request, Response } from 'express';\nimport { HttpStatusCodes } from './httpStatusCodes';\n\nexport enum AccessRule {\n none = 'none',\n authenticated = 'authenticated',\n admin = 'admin',\n noneAuthenticated = 'not-authenticated',\n hasOrganization = 'has-organization',\n hasProject = 'has-project',\n hasBearer = 'has-bearer',\n oauth2 = 'oauth2',\n}\n\nexport const accessControl = <R extends AccessRule | AccessRule[]>(\n res: Response,\n accessRule: R\n) => {\n const accessRuleArray: AccessRule[] = Array.isArray(accessRule)\n ? accessRule\n : [accessRule];\n\n const localsAuthInformation = res.locals;\n const { user, organization, project, authType } = localsAuthInformation;\n\n // If 'none' access rule is present, immediately return success\n if (accessRuleArray.includes(AccessRule.none)) {\n return {\n success: true,\n message: null,\n data: { user, organization, project, authType },\n };\n }\n\n let success = true;\n const messages: string[] = [];\n\n // Check for 'authenticated' access rule\n if (accessRuleArray.includes(AccessRule.authenticated)) {\n if (!user) {\n success = false;\n messages.push('User is not authenticated');\n }\n }\n\n // Check for 'admin' access rule\n // if (accessRuleArray.includes(AccessRule.admin)) {\n // if (!user?.role.includes('admin')) {\n // success = false;\n // messages.push('User is not an admin');\n // }\n // }\n\n // Check for 'not-authenticated' access rule\n if (accessRuleArray.includes(AccessRule.noneAuthenticated)) {\n if (user) {\n success = false;\n messages.push('User is authenticated');\n }\n }\n\n // Check for 'has-organization' access rule\n if (accessRuleArray.includes(AccessRule.hasOrganization)) {\n if (!organization) {\n success = false;\n messages.push('Organization is not set');\n }\n }\n\n // Check for 'has-project' access rule\n if (accessRuleArray.includes(AccessRule.hasProject)) {\n if (!project) {\n success = false;\n messages.push('Project is not set');\n }\n }\n\n // Check for 'oauth2' access rule\n if (accessRuleArray.includes(AccessRule.oauth2)) {\n if (authType !== 'oauth2') {\n success = false;\n messages.push('OAuth2 authentication is required');\n }\n }\n\n // Handle unknown access rules\n const knownRules = Object.values(AccessRule);\n const unknownRules = accessRuleArray.filter(\n (rule) => !knownRules.includes(rule)\n );\n if (unknownRules.length > 0) {\n success = false;\n messages.push(`Unknown access rules: ${unknownRules.join(', ')}`);\n }\n\n return {\n success,\n message: messages.join(', '),\n data: { user, organization, project, authType },\n };\n};\n\n/**\n * Middleware to control API access based on access rules.\n *\n * This middleware allows for multiple access rules to be passed, either as individual `AccessRule` or\n * an array of `AccessRule` groups. Access is granted if at least one of the following conditions is met:\n *\n * - The user satisfies all `AccessRule` within any group of rules passed as an array.\n * - The user satisfies any single `AccessRule` passed individually.\n *\n * Example usage:\n *\n * ```typescript\n * // Allow access if the user has both `hasProject` and `hasOrganization`, or if they have `admin` rights\n * app.use('/protected-route', apiAccessControlMiddleWare([AccessRule.hasProject, AccessRule.hasOrganization], AccessRule.admin));\n * ```\n *\n * In this example:\n * - The user will be granted access if they have both `hasProject` and `hasOrganization`.\n * - Alternatively, the user will also be granted access if they have `admin` privileges.\n *\n * @param {...(AccessRule | AccessRule[])[]} accessRules - One or more access rules or groups of access rules.\n * - Single `AccessRule`: The user must satisfy this rule for access to be granted.\n * - Array of `AccessRule`: The user must satisfy all rules in the array for access to be granted.\n * @returns {Function} Express middleware function that checks if the user has the required access.\n *\n * If the user does not meet any of the provided access rules, a 403 Forbidden status is returned.\n *\n * @example\n * // Example 1: Require admin privileges\n * app.use('/admin', apiAccessControlMiddleWare(AccessRule.admin));\n *\n * @example\n * // Example 2: Require both project and organization access, or admin privileges\n * app.use('/dashboard', apiAccessControlMiddleWare([AccessRule.hasProject, AccessRule.hasOrganization], AccessRule.admin));\n */\nexport const accessControlMiddleWare =\n (...accessRules: (AccessRule | AccessRule[])[]) =>\n (_req: Request<unknown>, res: Response, next: NextFunction): void => {\n let hasAccess = false;\n\n // Iterate over each access rule group (either single AccessRule or an array of AccessRules)\n for (const ruleGroup of accessRules) {\n if (Array.isArray(ruleGroup)) {\n // If ruleGroup is an array, check if all rules in the group are satisfied\n const accessResults = ruleGroup.map(\n (rule) => accessControl(res, rule).success\n );\n hasAccess = accessResults.every((result) => result); // All rules must be satisfied in this case\n } else {\n // Single rule: just check this one\n const accessResult = accessControl(res, ruleGroup);\n if (accessResult.success) {\n hasAccess = true;\n }\n }\n\n // If access is granted at any point, stop further checks\n if (hasAccess) {\n break;\n }\n }\n\n // If no access rule group was satisfied, deny access\n if (!hasAccess) {\n logger.error('Access denied');\n\n const errorStatusCode = HttpStatusCodes.FORBIDDEN_403;\n res.sendStatus(errorStatusCode);\n return;\n }\n\n next();\n };\n"],"mappings":";;;;AAKA,IAAY,oDAAL;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAGF,MAAa,iBACX,KACA,eACG;CACH,MAAMA,kBAAgC,MAAM,QAAQ,WAAW,GAC3D,aACA,CAAC,WAAW;CAGhB,MAAM,EAAE,MAAM,cAAc,SAAS,aADP,IAAI;AAIlC,KAAI,gBAAgB,SAAS,WAAW,KAAK,CAC3C,QAAO;EACL,SAAS;EACT,SAAS;EACT,MAAM;GAAE;GAAM;GAAc;GAAS;GAAU;EAChD;CAGH,IAAI,UAAU;CACd,MAAMC,WAAqB,EAAE;AAG7B,KAAI,gBAAgB,SAAS,WAAW,cAAc,EACpD;MAAI,CAAC,MAAM;AACT,aAAU;AACV,YAAS,KAAK,4BAA4B;;;AAa9C,KAAI,gBAAgB,SAAS,WAAW,kBAAkB,EACxD;MAAI,MAAM;AACR,aAAU;AACV,YAAS,KAAK,wBAAwB;;;AAK1C,KAAI,gBAAgB,SAAS,WAAW,gBAAgB,EACtD;MAAI,CAAC,cAAc;AACjB,aAAU;AACV,YAAS,KAAK,0BAA0B;;;AAK5C,KAAI,gBAAgB,SAAS,WAAW,WAAW,EACjD;MAAI,CAAC,SAAS;AACZ,aAAU;AACV,YAAS,KAAK,qBAAqB;;;AAKvC,KAAI,gBAAgB,SAAS,WAAW,OAAO,EAC7C;MAAI,aAAa,UAAU;AACzB,aAAU;AACV,YAAS,KAAK,oCAAoC;;;CAKtD,MAAM,aAAa,OAAO,OAAO,WAAW;CAC5C,MAAM,eAAe,gBAAgB,QAClC,SAAS,CAAC,WAAW,SAAS,KAAK,CACrC;AACD,KAAI,aAAa,SAAS,GAAG;AAC3B,YAAU;AACV,WAAS,KAAK,yBAAyB,aAAa,KAAK,KAAK,GAAG;;AAGnE,QAAO;EACL;EACA,SAAS,SAAS,KAAK,KAAK;EAC5B,MAAM;GAAE;GAAM;GAAc;GAAS;GAAU;EAChD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCH,MAAa,2BACV,GAAG,iBACH,MAAwB,KAAe,SAA6B;CACnE,IAAI,YAAY;AAGhB,MAAK,MAAM,aAAa,aAAa;AACnC,MAAI,MAAM,QAAQ,UAAU,CAK1B,aAHsB,UAAU,KAC7B,SAAS,cAAc,KAAK,KAAK,CAAC,QACpC,CACyB,OAAO,WAAW,OAAO;WAG9B,cAAc,KAAK,UAAU,CACjC,QACf,aAAY;AAKhB,MAAI,UACF;;AAKJ,KAAI,CAAC,WAAW;AACd,8BAAO,MAAM,gBAAgB;EAE7B,MAAM,kBAAkBC,8CAAgB;AACxC,MAAI,WAAW,gBAAgB;AAC/B;;AAGF,OAAM"}
|
|
@@ -11,13 +11,9 @@ const require_utils_mapper_project = require('../mapper/project.cjs');
|
|
|
11
11
|
const require_controllers_user_controller = require('../../controllers/user.controller.cjs');
|
|
12
12
|
const require_utils_mapper_session = require('../mapper/session.cjs');
|
|
13
13
|
let better_auth = require("better-auth");
|
|
14
|
-
better_auth = require_rolldown_runtime.__toESM(better_auth);
|
|
15
14
|
let better_auth_adapters_mongodb = require("better-auth/adapters/mongodb");
|
|
16
|
-
better_auth_adapters_mongodb = require_rolldown_runtime.__toESM(better_auth_adapters_mongodb);
|
|
17
15
|
let better_auth_api = require("better-auth/api");
|
|
18
|
-
better_auth_api = require_rolldown_runtime.__toESM(better_auth_api);
|
|
19
16
|
let better_auth_plugins = require("better-auth/plugins");
|
|
20
|
-
better_auth_plugins = require_rolldown_runtime.__toESM(better_auth_plugins);
|
|
21
17
|
|
|
22
18
|
//#region src/utils/auth/getAuth.ts
|
|
23
19
|
const formatSession = (session) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getAuth.cjs","names":["getSessionRoles","computeEffectivePermission","intersectPermissions","sendEmail","userAPI: UserAPI | null","organizationAPI: OrganizationAPI | null","projectAPI: ProjectAPI | null","getUserById","mapUserToAPI","getOrganizationById","mapOrganizationToAPI","getProjectById","mapProjectToAPI","mapSessionToAPI","logger"],"sources":["../../../../src/utils/auth/getAuth.ts"],"sourcesContent":["import { sendVerificationUpdate } from '@controllers/user.controller';\nimport { logger } from '@logger';\nimport { sendEmail } from '@services/email.service';\nimport { getOrganizationById } from '@services/organization.service';\nimport { getProjectById } from '@services/project.service';\nimport { getUserById } from '@services/user.service';\nimport { mapOrganizationToAPI } from '@utils/mapper/organization';\nimport { mapProjectToAPI } from '@utils/mapper/project';\nimport { mapSessionToAPI } from '@utils/mapper/session';\nimport { mapUserToAPI } from '@utils/mapper/user';\nimport {\n computeEffectivePermission,\n getSessionRoles,\n intersectPermissions,\n} from '@utils/permissions';\nimport { betterAuth, type OmitId } from 'better-auth';\nimport { mongodbAdapter } from 'better-auth/adapters/mongodb';\nimport { createAuthMiddleware } from 'better-auth/api';\nimport { customSession } from 'better-auth/plugins';\nimport type { MongoClient } from 'mongodb';\nimport type { OrganizationAPI } from '@/types/organization.types';\nimport type { ProjectAPI } from '@/types/project.types';\nimport type {\n Session,\n SessionContext,\n SessionDataApi,\n} from '@/types/session.types';\nimport type { User, UserAPI } from '@/types/user.types';\n\nexport type Auth = ReturnType<typeof betterAuth>;\n\nexport const formatSession = (session: SessionContext): OmitId<Session> => {\n const roles = getSessionRoles(session);\n let permissions = computeEffectivePermission(roles);\n\n // Intersect in the case a Access Token try to override the permissions\n if (session.permissions) {\n permissions = intersectPermissions(permissions, session.permissions);\n }\n\n const resultSession = {\n session: session.session,\n user: session.user,\n organization: session.organization,\n project: session.project,\n authType: 'session',\n permissions,\n roles,\n } as OmitId<Session>;\n\n return resultSession;\n};\n\nexport const getAuth = (dbClient: MongoClient): Auth => {\n if (!dbClient) {\n throw new Error('MongoDB connection not established');\n }\n\n const auth = betterAuth({\n appName: 'Intlayer',\n\n database: mongodbAdapter(dbClient.db()),\n\n /**\n * User model\n */\n user: {\n modelName: 'users',\n },\n\n databaseHooks: {\n user: {\n create: {\n // Runs once, immediately after the INSERT\n after: async (user) => {\n if (!user?.emailVerified) return;\n\n await sendEmail({\n type: 'welcome',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n loginLink: `${process.env.CLIENT_URL}/auth/login`,\n locale: (user as any).lang,\n });\n logger.info('Welcome e‑mail delivered', {\n email: user.email,\n });\n },\n },\n },\n },\n\n hooks: {\n after: createAuthMiddleware(async (ctx) => {\n const { path, context } = ctx;\n\n const newUser = context.newSession?.user;\n const existingUser = context.session?.user;\n const user = newUser ?? existingUser;\n\n if (!user) return;\n\n if (['/verify-email'].includes(path)) {\n sendVerificationUpdate(user as unknown as User);\n logger.info('SSE verification update sent', {\n email: user.email,\n userId: user.id,\n });\n\n await sendEmail({\n type: 'welcome',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n loginLink: `${process.env.CLIENT_URL}/auth/login`,\n locale: (user as any).lang,\n });\n logger.info('Welcome e‑mail delivered', {\n email: user.email,\n });\n }\n }),\n },\n\n advanced: {\n // 1️⃣ Change or drop the global prefix\n // cookiePrefix: \"intlayer\", // => intlayer.session_token\n cookiePrefix: 'intlayer', // => session_token (no prefix)\n\n // 2️⃣ Override just the session‑token cookie\n cookies: {\n session_token: {\n // name: 'intlayer_session_token', // final name depends on the prefix above\n // attributes: { sameSite: \"lax\", maxAge: 60 * 60 * 24 } // optional\n },\n },\n\n // 3️⃣ (optional) turn off the automatic __Secure‑ prefix in non‑prod\n // useSecureCookies: false,\n },\n\n secret: process.env.AUTH_SECRET as string,\n session: {\n modelName: 'sessions',\n id: 'id',\n\n additionalFields: {\n activeOrganizationId: { type: 'string', nullable: true, input: false },\n activeProjectId: { type: 'string', nullable: true, input: false },\n },\n },\n\n plugins: [\n customSession(async ({ session }) => {\n const typedSession = session as unknown as SessionDataApi;\n\n let userAPI: UserAPI | null = null;\n let organizationAPI: OrganizationAPI | null = null;\n let projectAPI: ProjectAPI | null = null;\n\n if (typedSession.userId) {\n const userData = await getUserById(typedSession.userId);\n\n if (userData) {\n userAPI = mapUserToAPI(userData);\n }\n }\n\n if (typedSession.activeOrganizationId) {\n const orgData = await getOrganizationById(\n typedSession.activeOrganizationId\n );\n\n if (orgData) {\n organizationAPI = mapOrganizationToAPI(orgData);\n }\n }\n if (typedSession.activeProjectId) {\n const projectData = await getProjectById(\n typedSession.activeProjectId\n );\n\n if (projectData) {\n projectAPI = mapProjectToAPI(projectData);\n }\n }\n\n const sessionWithNoPermission: SessionContext = {\n session: typedSession,\n user: userAPI!,\n organization: organizationAPI ?? null,\n project: projectAPI ?? null,\n authType: 'session',\n };\n\n const formattedSession = formatSession(sessionWithNoPermission);\n\n return mapSessionToAPI(formattedSession);\n }),\n ],\n\n emailAndPassword: {\n enabled: true,\n disableSignUp: false,\n requireEmailVerification: true,\n minPasswordLength: 8,\n maxPasswordLength: 128,\n autoSignIn: true,\n sendResetPassword: async ({ user, token }) => {\n logger.info('sending reset password email', { email: user.email });\n await sendEmail({\n type: 'resetPassword',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n resetLink: `${process.env.CLIENT_URL}/auth/password/reset?token=${token}`,\n });\n },\n resetPasswordTokenExpiresIn: 3600,\n },\n accountLinking: {\n enabled: true, // allow linking in general\n trustedProviders: ['google', 'github'], // optional: auto‑link when Google verifies the e‑mail\n },\n emailVerification: {\n autoSignInAfterVerification: true,\n sendOnSignIn: true,\n sendVerificationEmail: async ({ user, url }) => {\n logger.info('sending verification email', { email: user.email });\n await sendEmail({\n type: 'validate',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n validationLink: url,\n });\n },\n },\n\n crossSubDomainCookies: {\n enabled: true,\n additionalCookies: ['session_token'],\n domain: process.env.CLIENT_URL as string,\n },\n cookiePrefix: 'intlayer',\n cookies: {\n session_token: {\n name: 'session_token',\n attributes: {\n httpOnly: true,\n secure: true,\n },\n },\n },\n\n trustedOrigins: [process.env.CLIENT_URL as string],\n\n socialProviders: {\n google: {\n clientId: process.env.GOOGLE_CLIENT_ID as string,\n clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,\n },\n github: {\n clientId: process.env.GITHUB_CLIENT_ID as string,\n clientSecret: process.env.GITHUB_CLIENT_SECRET as string,\n },\n },\n\n logger: {\n log: (level, message, ...args) => logger[level](message, ...args),\n },\n });\n\n return auth;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"getAuth.cjs","names":["getSessionRoles","computeEffectivePermission","intersectPermissions","sendEmail","userAPI: UserAPI | null","organizationAPI: OrganizationAPI | null","projectAPI: ProjectAPI | null","getUserById","mapUserToAPI","getOrganizationById","mapOrganizationToAPI","getProjectById","mapProjectToAPI","mapSessionToAPI","logger"],"sources":["../../../../src/utils/auth/getAuth.ts"],"sourcesContent":["import { sendVerificationUpdate } from '@controllers/user.controller';\nimport { logger } from '@logger';\nimport { sendEmail } from '@services/email.service';\nimport { getOrganizationById } from '@services/organization.service';\nimport { getProjectById } from '@services/project.service';\nimport { getUserById } from '@services/user.service';\nimport { mapOrganizationToAPI } from '@utils/mapper/organization';\nimport { mapProjectToAPI } from '@utils/mapper/project';\nimport { mapSessionToAPI } from '@utils/mapper/session';\nimport { mapUserToAPI } from '@utils/mapper/user';\nimport {\n computeEffectivePermission,\n getSessionRoles,\n intersectPermissions,\n} from '@utils/permissions';\nimport { betterAuth, type OmitId } from 'better-auth';\nimport { mongodbAdapter } from 'better-auth/adapters/mongodb';\nimport { createAuthMiddleware } from 'better-auth/api';\nimport { customSession } from 'better-auth/plugins';\nimport type { MongoClient } from 'mongodb';\nimport type { OrganizationAPI } from '@/types/organization.types';\nimport type { ProjectAPI } from '@/types/project.types';\nimport type {\n Session,\n SessionContext,\n SessionDataApi,\n} from '@/types/session.types';\nimport type { User, UserAPI } from '@/types/user.types';\n\nexport type Auth = ReturnType<typeof betterAuth>;\n\nexport const formatSession = (session: SessionContext): OmitId<Session> => {\n const roles = getSessionRoles(session);\n let permissions = computeEffectivePermission(roles);\n\n // Intersect in the case a Access Token try to override the permissions\n if (session.permissions) {\n permissions = intersectPermissions(permissions, session.permissions);\n }\n\n const resultSession = {\n session: session.session,\n user: session.user,\n organization: session.organization,\n project: session.project,\n authType: 'session',\n permissions,\n roles,\n } as OmitId<Session>;\n\n return resultSession;\n};\n\nexport const getAuth = (dbClient: MongoClient): Auth => {\n if (!dbClient) {\n throw new Error('MongoDB connection not established');\n }\n\n const auth = betterAuth({\n appName: 'Intlayer',\n\n database: mongodbAdapter(dbClient.db()),\n\n /**\n * User model\n */\n user: {\n modelName: 'users',\n },\n\n databaseHooks: {\n user: {\n create: {\n // Runs once, immediately after the INSERT\n after: async (user) => {\n if (!user?.emailVerified) return;\n\n await sendEmail({\n type: 'welcome',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n loginLink: `${process.env.CLIENT_URL}/auth/login`,\n locale: (user as any).lang,\n });\n logger.info('Welcome e‑mail delivered', {\n email: user.email,\n });\n },\n },\n },\n },\n\n hooks: {\n after: createAuthMiddleware(async (ctx) => {\n const { path, context } = ctx;\n\n const newUser = context.newSession?.user;\n const existingUser = context.session?.user;\n const user = newUser ?? existingUser;\n\n if (!user) return;\n\n if (['/verify-email'].includes(path)) {\n sendVerificationUpdate(user as unknown as User);\n logger.info('SSE verification update sent', {\n email: user.email,\n userId: user.id,\n });\n\n await sendEmail({\n type: 'welcome',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n loginLink: `${process.env.CLIENT_URL}/auth/login`,\n locale: (user as any).lang,\n });\n logger.info('Welcome e‑mail delivered', {\n email: user.email,\n });\n }\n }),\n },\n\n advanced: {\n // 1️⃣ Change or drop the global prefix\n // cookiePrefix: \"intlayer\", // => intlayer.session_token\n cookiePrefix: 'intlayer', // => session_token (no prefix)\n\n // 2️⃣ Override just the session‑token cookie\n cookies: {\n session_token: {\n // name: 'intlayer_session_token', // final name depends on the prefix above\n // attributes: { sameSite: \"lax\", maxAge: 60 * 60 * 24 } // optional\n },\n },\n\n // 3️⃣ (optional) turn off the automatic __Secure‑ prefix in non‑prod\n // useSecureCookies: false,\n },\n\n secret: process.env.AUTH_SECRET as string,\n session: {\n modelName: 'sessions',\n id: 'id',\n\n additionalFields: {\n activeOrganizationId: { type: 'string', nullable: true, input: false },\n activeProjectId: { type: 'string', nullable: true, input: false },\n },\n },\n\n plugins: [\n customSession(async ({ session }) => {\n const typedSession = session as unknown as SessionDataApi;\n\n let userAPI: UserAPI | null = null;\n let organizationAPI: OrganizationAPI | null = null;\n let projectAPI: ProjectAPI | null = null;\n\n if (typedSession.userId) {\n const userData = await getUserById(typedSession.userId);\n\n if (userData) {\n userAPI = mapUserToAPI(userData);\n }\n }\n\n if (typedSession.activeOrganizationId) {\n const orgData = await getOrganizationById(\n typedSession.activeOrganizationId\n );\n\n if (orgData) {\n organizationAPI = mapOrganizationToAPI(orgData);\n }\n }\n if (typedSession.activeProjectId) {\n const projectData = await getProjectById(\n typedSession.activeProjectId\n );\n\n if (projectData) {\n projectAPI = mapProjectToAPI(projectData);\n }\n }\n\n const sessionWithNoPermission: SessionContext = {\n session: typedSession,\n user: userAPI!,\n organization: organizationAPI ?? null,\n project: projectAPI ?? null,\n authType: 'session',\n };\n\n const formattedSession = formatSession(sessionWithNoPermission);\n\n return mapSessionToAPI(formattedSession);\n }),\n ],\n\n emailAndPassword: {\n enabled: true,\n disableSignUp: false,\n requireEmailVerification: true,\n minPasswordLength: 8,\n maxPasswordLength: 128,\n autoSignIn: true,\n sendResetPassword: async ({ user, token }) => {\n logger.info('sending reset password email', { email: user.email });\n await sendEmail({\n type: 'resetPassword',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n resetLink: `${process.env.CLIENT_URL}/auth/password/reset?token=${token}`,\n });\n },\n resetPasswordTokenExpiresIn: 3600,\n },\n accountLinking: {\n enabled: true, // allow linking in general\n trustedProviders: ['google', 'github'], // optional: auto‑link when Google verifies the e‑mail\n },\n emailVerification: {\n autoSignInAfterVerification: true,\n sendOnSignIn: true,\n sendVerificationEmail: async ({ user, url }) => {\n logger.info('sending verification email', { email: user.email });\n await sendEmail({\n type: 'validate',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n validationLink: url,\n });\n },\n },\n\n crossSubDomainCookies: {\n enabled: true,\n additionalCookies: ['session_token'],\n domain: process.env.CLIENT_URL as string,\n },\n cookiePrefix: 'intlayer',\n cookies: {\n session_token: {\n name: 'session_token',\n attributes: {\n httpOnly: true,\n secure: true,\n },\n },\n },\n\n trustedOrigins: [process.env.CLIENT_URL as string],\n\n socialProviders: {\n google: {\n clientId: process.env.GOOGLE_CLIENT_ID as string,\n clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,\n },\n github: {\n clientId: process.env.GITHUB_CLIENT_ID as string,\n clientSecret: process.env.GITHUB_CLIENT_SECRET as string,\n },\n },\n\n logger: {\n log: (level, message, ...args) => logger[level](message, ...args),\n },\n });\n\n return auth;\n};\n"],"mappings":"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"}
|
|
@@ -4,9 +4,7 @@ const require_utils_httpStatusCodes = require('../httpStatusCodes.cjs');
|
|
|
4
4
|
const require_utils_responseData = require('../responseData.cjs');
|
|
5
5
|
const require_utils_errors_errorCodes = require('./errorCodes.cjs');
|
|
6
6
|
let __intlayer_types = require("@intlayer/types");
|
|
7
|
-
__intlayer_types = require_rolldown_runtime.__toESM(__intlayer_types);
|
|
8
7
|
let express_intlayer = require("express-intlayer");
|
|
9
|
-
express_intlayer = require_rolldown_runtime.__toESM(express_intlayer);
|
|
10
8
|
|
|
11
9
|
//#region src/utils/errors/ErrorHandler.ts
|
|
12
10
|
var ErrorHandler = class ErrorHandler {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorHandler.cjs","names":["errorData","HttpStatusCodes","Locales","responseData","formatPaginatedResponse","formatResponse"],"sources":["../../../../src/utils/errors/ErrorHandler.ts"],"sourcesContent":["// Import required modules and types from their respective locations.\n\nimport { Locales, type StrictModeLocaleMap } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { formatPaginatedResponse, formatResponse } from '@utils/responseData';\nimport type { Response } from 'express';\nimport { t } from 'express-intlayer';\nimport type { UserAPI } from '@/types/user.types';\nimport { HttpStatusCodes } from '@/utils/httpStatusCodes';\nimport type { AppError } from './ErrorsClass';\nimport { type ErrorCodes, errorData } from './errorCodes';\n\n// Define a class named 'ErrorHandler' to encapsulate error handling logic.\nexport class ErrorHandler {\n /**\n * Handles generic error responses by formatting and sending a JSON response.\n * @param res - The response object provided by Express.js.\n * @param errorKey - A key representing the specific error.\n * @param statusCode - (Optional) A specific HTTP status code to use for the response.\n * @param isPaginatedResponse - Flag to determine if the response should be paginated.\n */\n static handleGenericErrorResponse(\n res: Response,\n errorKey: ErrorCodes,\n errorDetails?: object,\n statusCode?: HttpStatusCodes,\n isPaginatedResponse: boolean = false\n ) {\n const error = errorData[errorKey];\n const status = statusCode ?? error.statusCode; // Use the provided status code or default to the one in errorData.\n\n // Delegate to a more customizable error response handler.\n ErrorHandler.handleCustomErrorResponse(\n res,\n errorKey,\n error.title,\n error.message,\n errorDetails,\n status,\n isPaginatedResponse\n );\n }\n\n /**\n * Handles application-specific error responses by formatting and sending a JSON response.\n * @param res - The response object provided by Express.js.\n * @param error - The error object.\n * @param messageDetails - (Optional) Additional message details to include in the response.\n * @param isPaginatedResponse - (Optional) Flag to determine if the response should be paginated.\n */\n static handleAppErrorResponse(\n res: Response,\n error: AppError,\n messageDetails?: object,\n isPaginatedResponse: boolean = false\n ) {\n if (!error.isAppError) {\n ErrorHandler.handleCustomErrorResponse(\n res,\n error.errorKey ?? 'UNKNOWN_ERROR',\n 'Error',\n error.message ?? JSON.stringify(error),\n undefined,\n error.httpStatusCode ?? HttpStatusCodes.INTERNAL_SERVER_ERROR_500,\n isPaginatedResponse\n );\n }\n\n const isMultilingual = error.isMultilingual ?? false;\n // Delegate to a more customizable error response handler.\n ErrorHandler.handleCustomErrorResponse(\n res,\n error.errorKey,\n isMultilingual ? error.multilingualTitle : error.title,\n isMultilingual ? error.multilingualMessage : error.message,\n error.messageDetails ?? messageDetails,\n error.httpStatusCode,\n isPaginatedResponse\n );\n }\n\n /**\n * Handles more customizable error responses with detailed error messages and codes.\n * @param res - The response object.\n * @param errorKey - Error code key used to fetch the corresponding message and default status.\n * @param message - The localized error message object.\n * @param messageDetails - (Optional) Additional message details to include in the response.\n * @param statusCode - (Optional) HTTP status code, defaults to 500 if not specified.\n * @param isPaginatedResponse - Determines if the error should be part of a paginated response.\n */\n static handleCustomErrorResponse<T>(\n res: Response,\n errorKey: ErrorCodes | string,\n title: StrictModeLocaleMap<string> | string,\n message: StrictModeLocaleMap<string> | string,\n messageDetails?: object,\n statusCode?: HttpStatusCodes,\n isPaginatedResponse: boolean = false\n ) {\n const errorTitle = t(title as StrictModeLocaleMap<string>, Locales.ENGLISH);\n const errorMessage = t(\n message as StrictModeLocaleMap<string>,\n Locales.ENGLISH\n );\n logger.error(errorMessage, messageDetails); // Log the English version of the error message.\n const status = statusCode ?? HttpStatusCodes.INTERNAL_SERVER_ERROR_500; // Default to 500 if no status code is provided.\n\n if (isPaginatedResponse) {\n // Format the response as a paginated error response if requested.\n const responseData = formatPaginatedResponse<T>({\n error: {\n code: errorKey,\n title: errorTitle,\n message: errorMessage,\n },\n status,\n });\n res.status(status).json(responseData);\n return;\n }\n\n // Format the response as a standard non-paginated error response.\n const responseData = formatResponse<UserAPI>({\n error: {\n code: errorKey,\n title: errorTitle,\n message: errorMessage,\n ...messageDetails,\n },\n status,\n });\n\n res.status(status).json(responseData);\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"ErrorHandler.cjs","names":["errorData","HttpStatusCodes","Locales","responseData","formatPaginatedResponse","formatResponse"],"sources":["../../../../src/utils/errors/ErrorHandler.ts"],"sourcesContent":["// Import required modules and types from their respective locations.\n\nimport { Locales, type StrictModeLocaleMap } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { formatPaginatedResponse, formatResponse } from '@utils/responseData';\nimport type { Response } from 'express';\nimport { t } from 'express-intlayer';\nimport type { UserAPI } from '@/types/user.types';\nimport { HttpStatusCodes } from '@/utils/httpStatusCodes';\nimport type { AppError } from './ErrorsClass';\nimport { type ErrorCodes, errorData } from './errorCodes';\n\n// Define a class named 'ErrorHandler' to encapsulate error handling logic.\nexport class ErrorHandler {\n /**\n * Handles generic error responses by formatting and sending a JSON response.\n * @param res - The response object provided by Express.js.\n * @param errorKey - A key representing the specific error.\n * @param statusCode - (Optional) A specific HTTP status code to use for the response.\n * @param isPaginatedResponse - Flag to determine if the response should be paginated.\n */\n static handleGenericErrorResponse(\n res: Response,\n errorKey: ErrorCodes,\n errorDetails?: object,\n statusCode?: HttpStatusCodes,\n isPaginatedResponse: boolean = false\n ) {\n const error = errorData[errorKey];\n const status = statusCode ?? error.statusCode; // Use the provided status code or default to the one in errorData.\n\n // Delegate to a more customizable error response handler.\n ErrorHandler.handleCustomErrorResponse(\n res,\n errorKey,\n error.title,\n error.message,\n errorDetails,\n status,\n isPaginatedResponse\n );\n }\n\n /**\n * Handles application-specific error responses by formatting and sending a JSON response.\n * @param res - The response object provided by Express.js.\n * @param error - The error object.\n * @param messageDetails - (Optional) Additional message details to include in the response.\n * @param isPaginatedResponse - (Optional) Flag to determine if the response should be paginated.\n */\n static handleAppErrorResponse(\n res: Response,\n error: AppError,\n messageDetails?: object,\n isPaginatedResponse: boolean = false\n ) {\n if (!error.isAppError) {\n ErrorHandler.handleCustomErrorResponse(\n res,\n error.errorKey ?? 'UNKNOWN_ERROR',\n 'Error',\n error.message ?? JSON.stringify(error),\n undefined,\n error.httpStatusCode ?? HttpStatusCodes.INTERNAL_SERVER_ERROR_500,\n isPaginatedResponse\n );\n }\n\n const isMultilingual = error.isMultilingual ?? false;\n // Delegate to a more customizable error response handler.\n ErrorHandler.handleCustomErrorResponse(\n res,\n error.errorKey,\n isMultilingual ? error.multilingualTitle : error.title,\n isMultilingual ? error.multilingualMessage : error.message,\n error.messageDetails ?? messageDetails,\n error.httpStatusCode,\n isPaginatedResponse\n );\n }\n\n /**\n * Handles more customizable error responses with detailed error messages and codes.\n * @param res - The response object.\n * @param errorKey - Error code key used to fetch the corresponding message and default status.\n * @param message - The localized error message object.\n * @param messageDetails - (Optional) Additional message details to include in the response.\n * @param statusCode - (Optional) HTTP status code, defaults to 500 if not specified.\n * @param isPaginatedResponse - Determines if the error should be part of a paginated response.\n */\n static handleCustomErrorResponse<T>(\n res: Response,\n errorKey: ErrorCodes | string,\n title: StrictModeLocaleMap<string> | string,\n message: StrictModeLocaleMap<string> | string,\n messageDetails?: object,\n statusCode?: HttpStatusCodes,\n isPaginatedResponse: boolean = false\n ) {\n const errorTitle = t(title as StrictModeLocaleMap<string>, Locales.ENGLISH);\n const errorMessage = t(\n message as StrictModeLocaleMap<string>,\n Locales.ENGLISH\n );\n logger.error(errorMessage, messageDetails); // Log the English version of the error message.\n const status = statusCode ?? HttpStatusCodes.INTERNAL_SERVER_ERROR_500; // Default to 500 if no status code is provided.\n\n if (isPaginatedResponse) {\n // Format the response as a paginated error response if requested.\n const responseData = formatPaginatedResponse<T>({\n error: {\n code: errorKey,\n title: errorTitle,\n message: errorMessage,\n },\n status,\n });\n res.status(status).json(responseData);\n return;\n }\n\n // Format the response as a standard non-paginated error response.\n const responseData = formatResponse<UserAPI>({\n error: {\n code: errorKey,\n title: errorTitle,\n message: errorMessage,\n ...messageDetails,\n },\n status,\n });\n\n res.status(status).json(responseData);\n }\n}\n"],"mappings":";;;;;;;;;AAaA,IAAa,eAAb,MAAa,aAAa;;;;;;;;CAQxB,OAAO,2BACL,KACA,UACA,cACA,YACA,sBAA+B,OAC/B;EACA,MAAM,QAAQA,0CAAU;EACxB,MAAM,SAAS,cAAc,MAAM;AAGnC,eAAa,0BACX,KACA,UACA,MAAM,OACN,MAAM,SACN,cACA,QACA,oBACD;;;;;;;;;CAUH,OAAO,uBACL,KACA,OACA,gBACA,sBAA+B,OAC/B;AACA,MAAI,CAAC,MAAM,WACT,cAAa,0BACX,KACA,MAAM,YAAY,iBAClB,SACA,MAAM,WAAW,KAAK,UAAU,MAAM,EACtC,QACA,MAAM,kBAAkBC,8CAAgB,2BACxC,oBACD;EAGH,MAAM,iBAAiB,MAAM,kBAAkB;AAE/C,eAAa,0BACX,KACA,MAAM,UACN,iBAAiB,MAAM,oBAAoB,MAAM,OACjD,iBAAiB,MAAM,sBAAsB,MAAM,SACnD,MAAM,kBAAkB,gBACxB,MAAM,gBACN,oBACD;;;;;;;;;;;CAYH,OAAO,0BACL,KACA,UACA,OACA,SACA,gBACA,YACA,sBAA+B,OAC/B;EACA,MAAM,qCAAe,OAAsCC,yBAAQ,QAAQ;EAC3E,MAAM,uCACJ,SACAA,yBAAQ,QACT;AACD,8BAAO,MAAM,cAAc,eAAe;EAC1C,MAAM,SAAS,cAAcD,8CAAgB;AAE7C,MAAI,qBAAqB;GAEvB,MAAME,iBAAeC,mDAA2B;IAC9C,OAAO;KACL,MAAM;KACN,OAAO;KACP,SAAS;KACV;IACD;IACD,CAAC;AACF,OAAI,OAAO,OAAO,CAAC,KAAKD,eAAa;AACrC;;EAIF,MAAM,eAAeE,0CAAwB;GAC3C,OAAO;IACL,MAAM;IACN,OAAO;IACP,SAAS;IACT,GAAG;IACJ;GACD;GACD,CAAC;AAEF,MAAI,OAAO,OAAO,CAAC,KAAK,aAAa"}
|
|
@@ -2,7 +2,6 @@ const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
|
|
|
2
2
|
const require_utils_httpStatusCodes = require('../httpStatusCodes.cjs');
|
|
3
3
|
const require_utils_errors_errorCodes = require('./errorCodes.cjs');
|
|
4
4
|
let express_intlayer = require("express-intlayer");
|
|
5
|
-
express_intlayer = require_rolldown_runtime.__toESM(express_intlayer);
|
|
6
5
|
|
|
7
6
|
//#region src/utils/errors/ErrorsClass.ts
|
|
8
7
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorsClass.cjs","names":["HttpStatusCodes","errorData"],"sources":["../../../../src/utils/errors/ErrorsClass.ts"],"sourcesContent":["import { HttpStatusCodes } from '@utils/httpStatusCodes';\n// @ts-ignore express-intlayer not build yet\nimport { type StrictModeLocaleMap, t } from 'express-intlayer';\nimport { type ErrorCodes, errorData } from './errorCodes';\n\n/**\n * Custom error class that extends the native JavaScript Error class.\n * This class supports multilingual error messages and HTTP status codes.\n */\nexport class AppError extends Error {\n public isAppError: boolean = true; // Flag to identify AppError instances.\n public name: string;\n public isMultilingual: boolean = true;\n public errorKey: string;\n public title: string;\n public multilingualTitle: StrictModeLocaleMap<string>;\n public message: string;\n public multilingualMessage: StrictModeLocaleMap<string>;\n public httpStatusCode: HttpStatusCodes;\n public messageDetails?: object;\n\n /**\n * Constructor for the custom error class.\n * @param multilingualMessage - The error message which can be a simple string or a multilingual object.\n * @param httpStatusCode - Optional HTTP status code, defaults to 500 Internal Server Error.\n */\n constructor(\n multilingualTitle: StrictModeLocaleMap<string>,\n multilingualMessage: StrictModeLocaleMap<string>,\n errorKey: string,\n httpStatusCode: HttpStatusCodes = HttpStatusCodes.INTERNAL_SERVER_ERROR_500,\n messageDetails?: object\n ) {\n const title = t(multilingualTitle); // Translate title based on current locale\n const message = t(multilingualMessage); // Translate message based on current locale.\n\n super(message); // Use translated message for the superclass constructor.\n this.title = title;\n this.multilingualTitle = multilingualTitle;\n this.message = message;\n this.multilingualMessage = multilingualMessage; // Store original message format for potential use.\n this.name = 'AppError';\n this.errorKey = errorKey;\n this.httpStatusCode = httpStatusCode; // Set the HTTP status code.\n this.messageDetails = messageDetails; // Store any additional message details.\n\n // Capture the stack trace to exclude the constructor call.\n Error.captureStackTrace(this, this.constructor);\n }\n}\n\nexport class GenericError extends AppError {\n constructor(errorKey: ErrorCodes, messageDetails?: object) {\n const multilingualTitle = errorData[errorKey].title;\n const multilingualMessage = errorData[errorKey].message;\n const httpStatusCode = errorData[errorKey].statusCode;\n\n super(\n multilingualTitle,\n multilingualMessage,\n errorKey,\n httpStatusCode,\n messageDetails\n );\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"ErrorsClass.cjs","names":["HttpStatusCodes","errorData"],"sources":["../../../../src/utils/errors/ErrorsClass.ts"],"sourcesContent":["import { HttpStatusCodes } from '@utils/httpStatusCodes';\n// @ts-ignore express-intlayer not build yet\nimport { type StrictModeLocaleMap, t } from 'express-intlayer';\nimport { type ErrorCodes, errorData } from './errorCodes';\n\n/**\n * Custom error class that extends the native JavaScript Error class.\n * This class supports multilingual error messages and HTTP status codes.\n */\nexport class AppError extends Error {\n public isAppError: boolean = true; // Flag to identify AppError instances.\n public name: string;\n public isMultilingual: boolean = true;\n public errorKey: string;\n public title: string;\n public multilingualTitle: StrictModeLocaleMap<string>;\n public message: string;\n public multilingualMessage: StrictModeLocaleMap<string>;\n public httpStatusCode: HttpStatusCodes;\n public messageDetails?: object;\n\n /**\n * Constructor for the custom error class.\n * @param multilingualMessage - The error message which can be a simple string or a multilingual object.\n * @param httpStatusCode - Optional HTTP status code, defaults to 500 Internal Server Error.\n */\n constructor(\n multilingualTitle: StrictModeLocaleMap<string>,\n multilingualMessage: StrictModeLocaleMap<string>,\n errorKey: string,\n httpStatusCode: HttpStatusCodes = HttpStatusCodes.INTERNAL_SERVER_ERROR_500,\n messageDetails?: object\n ) {\n const title = t(multilingualTitle); // Translate title based on current locale\n const message = t(multilingualMessage); // Translate message based on current locale.\n\n super(message); // Use translated message for the superclass constructor.\n this.title = title;\n this.multilingualTitle = multilingualTitle;\n this.message = message;\n this.multilingualMessage = multilingualMessage; // Store original message format for potential use.\n this.name = 'AppError';\n this.errorKey = errorKey;\n this.httpStatusCode = httpStatusCode; // Set the HTTP status code.\n this.messageDetails = messageDetails; // Store any additional message details.\n\n // Capture the stack trace to exclude the constructor call.\n Error.captureStackTrace(this, this.constructor);\n }\n}\n\nexport class GenericError extends AppError {\n constructor(errorKey: ErrorCodes, messageDetails?: object) {\n const multilingualTitle = errorData[errorKey].title;\n const multilingualMessage = errorData[errorKey].message;\n const httpStatusCode = errorData[errorKey].statusCode;\n\n super(\n multilingualTitle,\n multilingualMessage,\n errorKey,\n httpStatusCode,\n messageDetails\n );\n }\n}\n"],"mappings":";;;;;;;;;;AASA,IAAa,WAAb,cAA8B,MAAM;CAClC,AAAO,aAAsB;CAC7B,AAAO;CACP,AAAO,iBAA0B;CACjC,AAAO;CACP,AAAO;CACP,AAAO;CACP,AAAO;CACP,AAAO;CACP,AAAO;CACP,AAAO;;;;;;CAOP,YACE,mBACA,qBACA,UACA,iBAAkCA,8CAAgB,2BAClD,gBACA;EACA,MAAM,gCAAU,kBAAkB;EAClC,MAAM,kCAAY,oBAAoB;AAEtC,QAAM,QAAQ;AACd,OAAK,QAAQ;AACb,OAAK,oBAAoB;AACzB,OAAK,UAAU;AACf,OAAK,sBAAsB;AAC3B,OAAK,OAAO;AACZ,OAAK,WAAW;AAChB,OAAK,iBAAiB;AACtB,OAAK,iBAAiB;AAGtB,QAAM,kBAAkB,MAAM,KAAK,YAAY;;;AAInD,IAAa,eAAb,cAAkC,SAAS;CACzC,YAAY,UAAsB,gBAAyB;EACzD,MAAM,oBAAoBC,0CAAU,UAAU;EAC9C,MAAM,sBAAsBA,0CAAU,UAAU;EAChD,MAAM,iBAAiBA,0CAAU,UAAU;AAE3C,QACE,mBACA,qBACA,UACA,gBACA,eACD"}
|
|
@@ -7,12 +7,11 @@ const require_models_tag_model = require('../../models/tag.model.cjs');
|
|
|
7
7
|
const require_models_user_model = require('../../models/user.model.cjs');
|
|
8
8
|
const require_models_oAuth2_model = require('../../models/oAuth2.model.cjs');
|
|
9
9
|
let mongoose = require("mongoose");
|
|
10
|
-
mongoose = require_rolldown_runtime.__toESM(mongoose);
|
|
11
10
|
|
|
12
11
|
//#region src/utils/mongoDB/connectDB.ts
|
|
13
12
|
const connectDB = async () => {
|
|
14
13
|
try {
|
|
15
|
-
const client = await mongoose.
|
|
14
|
+
const client = await (0, mongoose.connect)(`mongodb+srv://${process.env.DB_ID}:${process.env.DB_MDP}@${process.env.DB_CLUSTER}/?retryWrites=true&w=majority&appName=Cluster0`);
|
|
16
15
|
require_logger_index.logger.info("MongoDB connected");
|
|
17
16
|
await require_models_project_model.ProjectModel.syncIndexes();
|
|
18
17
|
await require_models_user_model.UserModel.createIndexes();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connectDB.cjs","names":["ProjectModel","UserModel","OAuth2AccessTokenModel","TagModel","DictionaryModel","OrganizationModel"],"sources":["../../../../src/utils/mongoDB/connectDB.ts"],"sourcesContent":["import { logger } from '@logger';\nimport { DictionaryModel } from '@models/dictionary.model';\nimport { OAuth2AccessTokenModel } from '@models/oAuth2.model';\nimport { OrganizationModel } from '@models/organization.model';\nimport { ProjectModel } from '@models/project.model';\nimport { TagModel } from '@models/tag.model';\nimport { UserModel } from '@models/user.model';\nimport
|
|
1
|
+
{"version":3,"file":"connectDB.cjs","names":["ProjectModel","UserModel","OAuth2AccessTokenModel","TagModel","DictionaryModel","OrganizationModel"],"sources":["../../../../src/utils/mongoDB/connectDB.ts"],"sourcesContent":["import { logger } from '@logger';\nimport { DictionaryModel } from '@models/dictionary.model';\nimport { OAuth2AccessTokenModel } from '@models/oAuth2.model';\nimport { OrganizationModel } from '@models/organization.model';\nimport { ProjectModel } from '@models/project.model';\nimport { TagModel } from '@models/tag.model';\nimport { UserModel } from '@models/user.model';\nimport { connect } from 'mongoose';\n\nexport const connectDB = async () => {\n try {\n const client = await connect(\n `mongodb+srv://${process.env.DB_ID}:${process.env.DB_MDP}@${process.env.DB_CLUSTER}/?retryWrites=true&w=majority&appName=Cluster0`\n );\n\n logger.info('MongoDB connected');\n\n // Recreate indexes for models\n await ProjectModel.syncIndexes();\n await UserModel.createIndexes();\n await OAuth2AccessTokenModel.createIndexes();\n await TagModel.createIndexes();\n await DictionaryModel.createIndexes();\n await OrganizationModel.createIndexes();\n\n // Return the underlying MongoDB client for better-auth\n return client.connection.getClient();\n } catch (error) {\n const errorMessage = `MongoDB connection error - ${(error as Error).message}`;\n\n logger.error(errorMessage);\n throw new Error(errorMessage);\n }\n};\n"],"mappings":";;;;;;;;;;;AASA,MAAa,YAAY,YAAY;AACnC,KAAI;EACF,MAAM,SAAS,4BACb,iBAAiB,QAAQ,IAAI,MAAM,GAAG,QAAQ,IAAI,OAAO,GAAG,QAAQ,IAAI,WAAW,gDACpF;AAED,8BAAO,KAAK,oBAAoB;AAGhC,QAAMA,0CAAa,aAAa;AAChC,QAAMC,oCAAU,eAAe;AAC/B,QAAMC,mDAAuB,eAAe;AAC5C,QAAMC,kCAAS,eAAe;AAC9B,QAAMC,gDAAgB,eAAe;AACrC,QAAMC,oDAAkB,eAAe;AAGvC,SAAO,OAAO,WAAW,WAAW;UAC7B,OAAO;EACd,MAAM,eAAe,8BAA+B,MAAgB;AAEpE,8BAAO,MAAM,aAAa;AAC1B,QAAM,IAAI,MAAM,aAAa"}
|
|
@@ -6,7 +6,6 @@ const require_services_user_service = require('../services/user.service.cjs');
|
|
|
6
6
|
const require_services_email_service = require('../services/email.service.cjs');
|
|
7
7
|
const require_services_subscription_service = require('../services/subscription.service.cjs');
|
|
8
8
|
let stripe = require("stripe");
|
|
9
|
-
stripe = require_rolldown_runtime.__toESM(stripe);
|
|
10
9
|
|
|
11
10
|
//#region src/webhooks/stripe.webhook.ts
|
|
12
11
|
/**
|