@intlayer/backend 8.7.12 → 8.7.14
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/README.md +27 -1
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/benchmark/solid.json +4990 -0
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/benchmark/svelte.json +4987 -0
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/benchmark/vue.json +4975 -0
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/intlayer_with_astro_lit.json +9936 -8930
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/intlayer_with_astro_vanilla.json +7963 -6949
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/readme.json +12908 -12288
- package/dist/esm/controllers/oAuth2.controller.mjs +21 -1
- package/dist/esm/controllers/oAuth2.controller.mjs.map +1 -1
- package/dist/esm/controllers/stripe.controller.mjs +86 -2
- package/dist/esm/controllers/stripe.controller.mjs.map +1 -1
- package/dist/esm/controllers/translation.controller.mjs +2 -0
- package/dist/esm/controllers/translation.controller.mjs.map +1 -1
- package/dist/esm/emails/InviteUserEmail.mjs +1541 -1
- package/dist/esm/emails/InviteUserEmail.mjs.map +1 -1
- package/dist/esm/emails/MagicLinkEmail.mjs +1128 -1
- package/dist/esm/emails/MagicLinkEmail.mjs.map +1 -1
- package/dist/esm/emails/OAuthTokenCreatedEmail.mjs +1389 -1
- package/dist/esm/emails/OAuthTokenCreatedEmail.mjs.map +1 -1
- package/dist/esm/emails/PasswordChangeConfirmation.mjs +814 -1
- package/dist/esm/emails/PasswordChangeConfirmation.mjs.map +1 -1
- package/dist/esm/emails/ResetUserPassword.mjs +1132 -1
- package/dist/esm/emails/ResetUserPassword.mjs.map +1 -1
- package/dist/esm/emails/SubscriptionPaymentCancellation.mjs +913 -1
- package/dist/esm/emails/SubscriptionPaymentCancellation.mjs.map +1 -1
- package/dist/esm/emails/SubscriptionPaymentError.mjs +908 -1
- package/dist/esm/emails/SubscriptionPaymentError.mjs.map +1 -1
- package/dist/esm/emails/SubscriptionPaymentSuccess.mjs +935 -1
- package/dist/esm/emails/SubscriptionPaymentSuccess.mjs.map +1 -1
- package/dist/esm/emails/ValidateUserEmail.mjs +1111 -1
- package/dist/esm/emails/ValidateUserEmail.mjs.map +1 -1
- package/dist/esm/emails/Welcome.mjs +1004 -1
- package/dist/esm/emails/Welcome.mjs.map +1 -1
- package/dist/esm/emails/index.mjs +7 -7
- package/dist/esm/index.mjs +8 -2
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/middlewares/oAuth2.middleware.mjs +2 -2
- package/dist/esm/middlewares/oAuth2.middleware.mjs.map +1 -1
- package/dist/esm/routes/audit.routes.mjs +5 -4
- package/dist/esm/routes/audit.routes.mjs.map +1 -1
- package/dist/esm/routes/dictionary.routes.mjs +4 -3
- package/dist/esm/routes/dictionary.routes.mjs.map +1 -1
- package/dist/esm/routes/organization.routes.mjs +3 -2
- package/dist/esm/routes/organization.routes.mjs.map +1 -1
- package/dist/esm/routes/paramsSchemas.mjs +67 -0
- package/dist/esm/routes/paramsSchemas.mjs.map +1 -0
- package/dist/esm/routes/project.routes.mjs +2 -1
- package/dist/esm/routes/project.routes.mjs.map +1 -1
- package/dist/esm/routes/showcaseProject.routes.mjs +5 -4
- package/dist/esm/routes/showcaseProject.routes.mjs.map +1 -1
- package/dist/esm/routes/stripe.routes.mjs +19 -1
- package/dist/esm/routes/stripe.routes.mjs.map +1 -1
- package/dist/esm/routes/tags.routes.mjs +3 -2
- package/dist/esm/routes/tags.routes.mjs.map +1 -1
- package/dist/esm/routes/translate.routes.mjs +6 -5
- package/dist/esm/routes/translate.routes.mjs.map +1 -1
- package/dist/esm/routes/user.routes.mjs +5 -4
- package/dist/esm/routes/user.routes.mjs.map +1 -1
- package/dist/esm/schemas/oAuth2.schema.mjs +1 -1
- package/dist/esm/schemas/oAuth2.schema.mjs.map +1 -1
- package/dist/esm/services/email.service.mjs +338 -38
- package/dist/esm/services/email.service.mjs.map +1 -1
- package/dist/esm/services/oAuth2.service.mjs +20 -2
- package/dist/esm/services/oAuth2.service.mjs.map +1 -1
- package/dist/esm/services/subscription.service.mjs +5 -2
- package/dist/esm/services/subscription.service.mjs.map +1 -1
- package/dist/esm/utils/AI/askDocQuestion/embeddings/docs/en/benchmark/solid.json +4990 -0
- package/dist/esm/utils/AI/askDocQuestion/embeddings/docs/en/benchmark/svelte.json +4987 -0
- package/dist/esm/utils/AI/askDocQuestion/embeddings/docs/en/benchmark/vue.json +4975 -0
- package/dist/esm/utils/AI/askDocQuestion/embeddings/docs/en/intlayer_with_astro_lit.json +9936 -8930
- package/dist/esm/utils/AI/askDocQuestion/embeddings/docs/en/intlayer_with_astro_vanilla.json +7963 -6949
- package/dist/esm/utils/AI/askDocQuestion/embeddings/docs/en/readme.json +12908 -12288
- package/dist/esm/utils/AI/auditDictionary/index.mjs +1 -3
- package/dist/esm/utils/AI/auditDictionary/index.mjs.map +1 -1
- package/dist/esm/utils/AI/auditDictionaryField/index.mjs +1 -3
- package/dist/esm/utils/AI/auditDictionaryField/index.mjs.map +1 -1
- package/dist/esm/utils/AI/auditTag/index.mjs +1 -3
- package/dist/esm/utils/AI/auditTag/index.mjs.map +1 -1
- package/dist/esm/utils/AI/autocomplete/index.mjs +1 -3
- package/dist/esm/utils/AI/autocomplete/index.mjs.map +1 -1
- package/dist/esm/utils/auth/getAuth.mjs +6 -0
- package/dist/esm/utils/auth/getAuth.mjs.map +1 -1
- package/dist/esm/utils/errors/errorCodes.mjs +3917 -287
- package/dist/esm/utils/errors/errorCodes.mjs.map +1 -1
- package/dist/esm/utils/mongoDB/connectDB.mjs +5 -0
- package/dist/esm/utils/mongoDB/connectDB.mjs.map +1 -1
- package/dist/esm/utils/oAuth2.mjs +6 -2
- package/dist/esm/utils/oAuth2.mjs.map +1 -1
- package/dist/esm/utils/plan.mjs +13 -1
- package/dist/esm/utils/plan.mjs.map +1 -1
- package/dist/types/controllers/oAuth2.controller.d.ts +11 -1
- package/dist/types/controllers/oAuth2.controller.d.ts.map +1 -1
- package/dist/types/controllers/stripe.controller.d.ts +22 -2
- package/dist/types/controllers/stripe.controller.d.ts.map +1 -1
- package/dist/types/controllers/translation.controller.d.ts.map +1 -1
- package/dist/types/emails/InviteUserEmail.d.ts +181 -1
- package/dist/types/emails/InviteUserEmail.d.ts.map +1 -1
- package/dist/types/emails/MagicLinkEmail.d.ts +106 -1
- package/dist/types/emails/MagicLinkEmail.d.ts.map +1 -1
- package/dist/types/emails/OAuthTokenCreatedEmail.d.ts +166 -1
- package/dist/types/emails/OAuthTokenCreatedEmail.d.ts.map +1 -1
- package/dist/types/emails/PasswordChangeConfirmation.d.ts +91 -1
- package/dist/types/emails/PasswordChangeConfirmation.d.ts.map +1 -1
- package/dist/types/emails/ResetUserPassword.d.ts +106 -1
- package/dist/types/emails/ResetUserPassword.d.ts.map +1 -1
- package/dist/types/emails/SubscriptionPaymentCancellation.d.ts +151 -1
- package/dist/types/emails/SubscriptionPaymentCancellation.d.ts.map +1 -1
- package/dist/types/emails/SubscriptionPaymentError.d.ts +151 -1
- package/dist/types/emails/SubscriptionPaymentError.d.ts.map +1 -1
- package/dist/types/emails/SubscriptionPaymentSuccess.d.ts +151 -1
- package/dist/types/emails/SubscriptionPaymentSuccess.d.ts.map +1 -1
- package/dist/types/emails/ValidateUserEmail.d.ts +106 -1
- package/dist/types/emails/ValidateUserEmail.d.ts.map +1 -1
- package/dist/types/emails/Welcome.d.ts +106 -1
- package/dist/types/emails/Welcome.d.ts.map +1 -1
- package/dist/types/emails/index.d.ts +7 -7
- package/dist/types/export.d.ts +3 -3
- package/dist/types/middlewares/oAuth2.middleware.d.ts.map +1 -1
- package/dist/types/routes/audit.routes.d.ts.map +1 -1
- package/dist/types/routes/dictionary.routes.d.ts.map +1 -1
- package/dist/types/routes/organization.routes.d.ts.map +1 -1
- package/dist/types/routes/paramsSchemas.d.ts +102 -0
- package/dist/types/routes/paramsSchemas.d.ts.map +1 -0
- package/dist/types/routes/project.routes.d.ts.map +1 -1
- package/dist/types/routes/showcaseProject.routes.d.ts.map +1 -1
- package/dist/types/routes/stripe.routes.d.ts +15 -0
- package/dist/types/routes/stripe.routes.d.ts.map +1 -1
- package/dist/types/routes/tags.routes.d.ts.map +1 -1
- package/dist/types/routes/translate.routes.d.ts.map +1 -1
- package/dist/types/routes/user.routes.d.ts.map +1 -1
- package/dist/types/schemas/dictionary.schema.d.ts +5 -5
- package/dist/types/schemas/discussion.schema.d.ts +8 -8
- package/dist/types/schemas/organization.schema.d.ts +6 -6
- package/dist/types/schemas/plans.schema.d.ts +9 -9
- package/dist/types/schemas/project.schema.d.ts +12 -12
- package/dist/types/schemas/session.schema.d.ts +3 -3
- package/dist/types/schemas/showcaseProject.schema.d.ts +7 -7
- package/dist/types/schemas/tag.schema.d.ts +8 -8
- package/dist/types/services/email.service.d.ts.map +1 -1
- package/dist/types/services/oAuth2.service.d.ts +6 -1
- package/dist/types/services/oAuth2.service.d.ts.map +1 -1
- package/dist/types/types/plan.types.d.ts +2 -2
- package/dist/types/utils/errors/errorCodes.d.ts +3634 -4
- package/dist/types/utils/errors/errorCodes.d.ts.map +1 -1
- package/dist/types/utils/mongoDB/connectDB.d.ts.map +1 -1
- package/dist/types/utils/oAuth2.d.ts +3 -1
- package/dist/types/utils/oAuth2.d.ts.map +1 -1
- package/dist/types/utils/plan.d.ts +2 -1
- package/dist/types/utils/plan.d.ts.map +1 -1
- package/package.json +14 -13
|
@@ -39,10 +39,8 @@ const auditDictionary = async ({ fileContent, filePath, locales, defaultLocale,
|
|
|
39
39
|
const prompt = CHAT_GPT_PROMPT.replace("{{defaultLocale}}", formatLocaleWithName(defaultLocale)).replace("{{otherLocales}}", `{${otherLocales.map(formatLocaleWithName).join(", ")}}`).replace("{{filePath}}", filePath ?? "").replace("{{applicationContext}}", applicationContext ?? "").replace("{{tagsInstructions}}", formatTagInstructions(tags));
|
|
40
40
|
const { text: newContent, usage } = await generateText({
|
|
41
41
|
...aiConfig,
|
|
42
|
+
system: prompt,
|
|
42
43
|
messages: [{
|
|
43
|
-
role: "system",
|
|
44
|
-
content: prompt
|
|
45
|
-
}, {
|
|
46
44
|
role: "user",
|
|
47
45
|
content: ["**File to Audit:**", fileContent].join("\n")
|
|
48
46
|
}]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../src/utils/AI/auditDictionary/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n type AIConfig,\n type AIOptions,\n extractJson,\n generateText,\n} from '@intlayer/ai';\nimport { DEFAULT_LOCALE } from '@intlayer/config/defaultValues';\nimport { getLocaleName } from '@intlayer/core/localization';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport { logger } from '@logger';\nimport type { Tag } from '@/types/tag.types';\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\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = readFileSync(join(__dirname, './PROMPT.md'), 'utf-8');\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, DEFAULT_LOCALE)}`;\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('{{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: [\n {
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../src/utils/AI/auditDictionary/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n type AIConfig,\n type AIOptions,\n extractJson,\n generateText,\n} from '@intlayer/ai';\nimport { DEFAULT_LOCALE } from '@intlayer/config/defaultValues';\nimport { getLocaleName } from '@intlayer/core/localization';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport { logger } from '@logger';\nimport type { Tag } from '@/types/tag.types';\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\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = readFileSync(join(__dirname, './PROMPT.md'), 'utf-8');\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, DEFAULT_LOCALE)}`;\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('{{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 system: prompt,\n messages: [\n {\n role: 'user',\n content: ['**File to Audit:**', fileContent].join('\\n'),\n },\n ],\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":";;;;;;;;;AAsCA,MAAM,kBAAkB,aAAa,KAHnB,QADC,cAAc,OAAO,KAAK,IACT,CAGe,EAAE,cAAc,EAAE,QAAQ;AAE7E,MAAa,mBAA8B,EAE1C;;;;;;;AAQD,MAAM,wBAAwB,WAA2B;AACvD,QAAO,GAAG,OAAO,IAAI,cAAc,QAAQ,eAAe;;;;;;;;;AAU5D,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,0BAA0B,sBAAsB,GAAG,CAC3D,QAAQ,wBAAwB,sBAAsB,KAAK,CAAC;CAG/D,MAAM,EAAE,MAAM,YAAY,UAAU,MAAM,aAAa;EACrD,GAAG;EACH,QAAQ;EACR,UAAU,CACR;GACE,MAAM;GACN,SAAS,CAAC,sBAAsB,YAAY,CAAC,KAAK,KAAK;GACxD,CACF;EACF,CAAC;AAEF,QAAO,KAAK,GAAG,OAAO,eAAe,EAAE,6BAA6B;AAEpE,QAAO;EACL,aAAa,YAAY,WAAW;EACpC,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -43,10 +43,8 @@ const auditDictionaryField = async ({ fileContent, applicationContext, locales,
|
|
|
43
43
|
}
|
|
44
44
|
const { text: newContent, usage } = await generateText({
|
|
45
45
|
...aiConfig,
|
|
46
|
+
system: prompt,
|
|
46
47
|
messages: [{
|
|
47
|
-
role: "system",
|
|
48
|
-
content: prompt
|
|
49
|
-
}, {
|
|
50
48
|
role: "user",
|
|
51
49
|
content: ["**File to Audit:**", fileContent].join("\n")
|
|
52
50
|
}]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../src/utils/AI/auditDictionaryField/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { AIConfig, AIOptions } from '@intlayer/ai';\nimport { generateText } from '@intlayer/ai';\nimport { getLocaleName } from '@intlayer/core/localization';\nimport type {
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../src/utils/AI/auditDictionaryField/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { AIConfig, AIOptions } from '@intlayer/ai';\nimport { generateText } from '@intlayer/ai';\nimport { getLocaleName } from '@intlayer/core/localization';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport * as Locales from '@intlayer/types/locales';\nimport { logger } from '@logger';\nimport type { Tag } from '@/types/tag.types';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\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 = readFileSync(join(__dirname, './PROMPT.md'), 'utf-8');\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('{{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 system: prompt,\n messages: [\n {\n role: 'user',\n content: ['**File to Audit:**', fileContent].join('\\n'),\n },\n ],\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":";;;;;;;;;AA8BA,MAAM,kBAAkB,aAAa,KAjBnB,QADC,cAAc,OAAO,KAAK,IACT,CAiBe,EAAE,cAAc,EAAE,QAAQ;AAE7E,MAAa,mBAA8B,EAE1C;;;;;;;AAQD,MAAM,wBAAwB,WAA2B;AACvD,QAAO,GAAG,OAAO,IAAI,cAAc,QAAQ,QAAQ,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,0BAA0B,sBAAsB,GAAG,CAC3D,QAAQ,wBAAwB,sBAAsB,KAAK,CAAC;AAE/D,KAAI,CAAC,UAAU;AACb,SAAO,MAAM,+BAA+B;AAC5C;;CAIF,MAAM,EAAE,MAAM,YAAY,UAAU,MAAM,aAAa;EACrD,GAAG;EACH,QAAQ;EACR,UAAU,CACR;GACE,MAAM;GACN,SAAS,CAAC,sBAAsB,YAAY,CAAC,KAAK,KAAK;GACxD,CACF;EACF,CAAC;AAEF,QAAO,KAAK,GAAG,OAAO,eAAe,EAAE,6BAA6B;AAEpE,QAAO;EACL,aAAa;EACb,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -15,10 +15,8 @@ const auditTag = async ({ dictionaries, tag, aiConfig, applicationContext }) =>
|
|
|
15
15
|
const prompt = CHAT_GPT_PROMPT.replace("{{tag.description}}", tag.description ?? "").replace("{{tag.key}}", tag.key).replace("{{applicationContext}}", applicationContext ?? "");
|
|
16
16
|
const { text: newContent, usage } = await generateText({
|
|
17
17
|
...aiConfig,
|
|
18
|
+
system: prompt,
|
|
18
19
|
messages: [{
|
|
19
|
-
role: "system",
|
|
20
|
-
content: prompt
|
|
21
|
-
}, {
|
|
22
20
|
role: "user",
|
|
23
21
|
content: [
|
|
24
22
|
"**Tag to audit:**",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../src/utils/AI/auditTag/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n type AIConfig,\n type AIOptions,\n extractJson,\n generateText,\n} from '@intlayer/ai';\nimport { logger } from '@logger';\nimport type { Dictionary } from '@/types/dictionary.types';\nimport type { TagAPI } from '@/types/tag.types';\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\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n// The prompt template to send to AI models\nconst CHAT_GPT_PROMPT = readFileSync(join(__dirname, './PROMPT.md'), 'utf-8');\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('{{applicationContext}}', applicationContext ?? '');\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages: [\n {
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../src/utils/AI/auditTag/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n type AIConfig,\n type AIOptions,\n extractJson,\n generateText,\n} from '@intlayer/ai';\nimport { logger } from '@logger';\nimport type { Dictionary } from '@/types/dictionary.types';\nimport type { TagAPI } from '@/types/tag.types';\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\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n// The prompt template to send to AI models\nconst CHAT_GPT_PROMPT = readFileSync(join(__dirname, './PROMPT.md'), 'utf-8');\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('{{applicationContext}}', applicationContext ?? '');\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n system: prompt,\n messages: [\n {\n role: 'user',\n content: [\n '**Tag to audit:**',\n 'This is the tag that you should consider to audit:',\n JSON.stringify(tag),\n ].join('\\n'),\n },\n ],\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,kBAAkB,aAAa,KAHnB,QADC,cAAc,OAAO,KAAK,IACT,CAGe,EAAE,cAAc,EAAE,QAAQ;AAE7E,MAAa,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,0BAA0B,sBAAsB,GAAG;CAG9D,MAAM,EAAE,MAAM,YAAY,UAAU,MAAM,aAAa;EACrD,GAAG;EACH,QAAQ;EACR,UAAU,CACR;GACE,MAAM;GACN,SAAS;IACP;IACA;IACA,KAAK,UAAU,IAAI;IACpB,CAAC,KAAK,KAAK;GACb,CACF;EACF,CAAC;AAEF,QAAO,KAAK,GAAG,OAAO,eAAe,EAAE,6BAA6B;AAEpE,QAAO;EACL,aAAa,YAAY,WAAW;EACpC,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -20,10 +20,8 @@ const autocomplete = async ({ text, aiConfig, applicationContext, contextBefore,
|
|
|
20
20
|
const prompt = CHAT_GPT_PROMPT.replace("{{applicationContext}}", applicationContext ?? "").replace("{{contextBefore}}", contextBefore ?? "").replace("{{currentLine}}", currentLine ?? "").replace("{{contextAfter}}", contextAfter ?? "");
|
|
21
21
|
const { text: newContent, usage } = await generateText({
|
|
22
22
|
...aiConfig,
|
|
23
|
+
system: prompt,
|
|
23
24
|
messages: [{
|
|
24
|
-
role: "system",
|
|
25
|
-
content: prompt
|
|
26
|
-
}, {
|
|
27
25
|
role: "assistant",
|
|
28
26
|
content: text
|
|
29
27
|
}]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../src/utils/AI/autocomplete/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n type AIConfig,\n type AIOptions,\n AIProvider,\n generateText,\n} from '@intlayer/ai';\nimport { logger } from '@logger';\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\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = readFileSync(join(__dirname, './PROMPT.md'), 'utf-8');\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
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../src/utils/AI/autocomplete/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n type AIConfig,\n type AIOptions,\n AIProvider,\n generateText,\n} from '@intlayer/ai';\nimport { logger } from '@logger';\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\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = readFileSync(join(__dirname, './PROMPT.md'), 'utf-8');\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 system: prompt,\n messages: [{ role: 'assistant', content: text }],\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":";;;;;;;AA6BA,MAAM,kBAAkB,aAAa,KAHnB,QADC,cAAc,OAAO,KAAK,IACT,CAGe,EAAE,cAAc,EAAE,QAAQ;AAE7E,MAAa,mBAA8B;CACzC,UAAU,WAAW;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,MAAM,aAAa;EACrD,GAAG;EACH,QAAQ;EACR,UAAU,CAAC;GAAE,MAAM;GAAa,SAAS;GAAM,CAAC;EACjD,CAAC;AAEF,QAAO,KAAK,GAAG,OAAO,eAAe,EAAE,6BAA6B;AAEpE,QAAO;EACL,gBAAgB;EAChB,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -96,6 +96,12 @@ const getAuth = (dbClient) => {
|
|
|
96
96
|
session: {
|
|
97
97
|
modelName: "sessions",
|
|
98
98
|
id: "id",
|
|
99
|
+
expiresIn: 3600 * 24 * 30,
|
|
100
|
+
updateAge: 3600 * 24,
|
|
101
|
+
cookieCache: {
|
|
102
|
+
enabled: false,
|
|
103
|
+
maxAge: 300
|
|
104
|
+
},
|
|
99
105
|
additionalFields: {
|
|
100
106
|
activeOrganizationId: {
|
|
101
107
|
type: "string",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getAuth.mjs","names":[],"sources":["../../../../src/utils/auth/getAuth.ts"],"sourcesContent":["import { passkey } from '@better-auth/passkey';\nimport { sso } from '@better-auth/sso';\nimport { 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 type { OmitId } from '@utils/mongoDB/types';\nimport {\n computeEffectivePermission,\n getSessionRoles,\n intersectPermissions,\n} from '@utils/permissions';\nimport { betterAuth } from 'better-auth';\nimport { mongodbAdapter } from 'better-auth/adapters/mongodb';\nimport { createAuthMiddleware } from 'better-auth/api';\nimport { customSession, lastLoginMethod, twoFactor } from 'better-auth/plugins';\nimport { magicLink } from 'better-auth/plugins/magic-link';\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\n// Check if we are in production based on the domain or NODE_ENV\nconst isProd = process.env.DOMAIN !== 'localhost';\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 baseURL: process.env.BACKEND_URL,\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.APP_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 (path.includes('/verify-email')) {\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.APP_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 crossSubDomainCookies: {\n enabled: isProd,\n domain: isProd ? process.env.DOMAIN : undefined,\n additionalCookies: ['session_token'],\n },\n cookiePrefix: 'intlayer',\n cookies: {\n session_token: {\n name: 'session_token',\n attributes: {\n httpOnly: true,\n secure: isProd,\n sameSite: 'lax',\n },\n },\n },\n },\n\n secret: process.env.BETTER_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 // activeOrganizationId may be stored as a BSON ObjectId in MongoDB\n // (legacy data), so we normalize it to a string before querying.\n const rawOrgId = typedSession.activeOrganizationId as any;\n const orgIdStr: string | null =\n typeof rawOrgId === 'string'\n ? rawOrgId\n : rawOrgId.buffer instanceof Uint8Array\n ? Buffer.from(rawOrgId.buffer).toString('hex')\n : null;\n\n if (!orgIdStr) {\n typedSession.activeOrganizationId = undefined;\n typedSession.activeProjectId = undefined;\n }\n\n const orgData = orgIdStr ? await getOrganizationById(orgIdStr) : null;\n\n if (orgData) {\n organizationAPI = mapOrganizationToAPI(orgData);\n } else {\n // Organization ID is present in session but not found in DB (Zombie session)\n // We detach the organization to prevent login errors\n await dbClient\n .db()\n .collection('sessions')\n .updateOne(\n { id: typedSession.id },\n {\n $set: { activeOrganizationId: null, activeProjectId: null },\n }\n );\n\n // Clear it locally so the current request proceeds without error\n typedSession.activeOrganizationId = undefined;\n typedSession.activeProjectId = undefined;\n }\n }\n if (typedSession.activeProjectId) {\n const rawProjectId = typedSession.activeProjectId as any;\n const projectIdStr: string | null =\n typeof rawProjectId === 'string'\n ? rawProjectId\n : rawProjectId.buffer instanceof Uint8Array\n ? Buffer.from(rawProjectId.buffer).toString('hex')\n : null;\n const projectData = projectIdStr\n ? await getProjectById(projectIdStr)\n : null;\n\n if (projectData) {\n projectAPI = mapProjectToAPI(projectData);\n } else {\n // Organization ID is present in session but not found in DB (Zombie session)\n // We detach the organization to prevent login errors\n await dbClient\n .db()\n .collection('sessions')\n .updateOne(\n { id: typedSession.id },\n {\n $set: { activeProjectId: null },\n }\n );\n\n // Clear it locally so the current request proceeds without error\n typedSession.activeProjectId = undefined;\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 lastLoginMethod({\n storeInDatabase: true, // adds user.lastLoginMethod in DB and session\n schema: {\n user: {\n lastLoginMethod: 'lastLoginMethod', // Custom field name\n },\n },\n customResolveMethod: (context) => {\n // When user clicks the magic link\n if (context.path === '/magic-link/verify') {\n return 'magic-link';\n }\n\n // Fallback to default behavior for everything else\n return null;\n },\n }),\n passkey({\n rpID: process.env.DOMAIN,\n rpName: 'Intlayer',\n }),\n twoFactor(),\n magicLink({\n sendMagicLink: async ({ email, url }) => {\n logger.info('sending magic link', { email, url });\n await sendEmail({\n type: 'magicLink',\n to: email,\n username: email.split('@')[0],\n magicLink: url,\n });\n },\n }),\n sso({\n organizationProvisioning: {},\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.APP_URL}/auth/password/reset?token=${token}`,\n });\n },\n resetPasswordTokenExpiresIn: 3600,\n },\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 trustedOrigins: [\n process.env.WEBSITE_URL,\n process.env.APP_URL,\n process.env.SHOWCASE_URL,\n ].filter(Boolean) as string[],\n\n accountLinking: {\n enabled: true, // allow linking in general\n trustedProviders: [\n 'google',\n 'github',\n 'linkedin',\n 'gitlab',\n 'atlassian',\n 'microsoft',\n 'email-password',\n 'magic-link',\n 'passkey',\n ],\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 atlassian: {\n clientId: process.env.ATLASSIAN_CLIENT_ID as string,\n clientSecret: process.env.ATLASSIAN_CLIENT_SECRET as string,\n },\n gitlab: {\n clientId: process.env.GITLAB_CLIENT_ID as string,\n clientSecret: process.env.GITLAB_CLIENT_SECRET as string,\n },\n linkedin: {\n clientId: process.env.LINKEDIN_CLIENT_ID as string,\n clientSecret: process.env.LINKEDIN_CLIENT_SECRET as string,\n },\n microsoft: {\n clientId: process.env.MICROSOFT_CLIENT_ID as string,\n clientSecret: process.env.MICROSOFT_CLIENT_SECRET as string,\n },\n // socialProviders: {\n // apple: {\n // clientId: process.env.APPLE_CLIENT_ID as string,\n // clientSecret: process.env.APPLE_CLIENT_SECRET as string,\n // // Optional\n // appBundleIdentifier: process.env\n // .APPLE_APP_BUNDLE_IDENTIFIER as string,\n // },\n // },\n // // Add appleid.apple.com to trustedOrigins for Sign In with Apple flows\n // trustedOrigins: ['https://appleid.apple.com'],\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.mjs","names":[],"sources":["../../../../src/utils/auth/getAuth.ts"],"sourcesContent":["import { passkey } from '@better-auth/passkey';\nimport { sso } from '@better-auth/sso';\nimport { 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 type { OmitId } from '@utils/mongoDB/types';\nimport {\n computeEffectivePermission,\n getSessionRoles,\n intersectPermissions,\n} from '@utils/permissions';\nimport { betterAuth } from 'better-auth';\nimport { mongodbAdapter } from 'better-auth/adapters/mongodb';\nimport { createAuthMiddleware } from 'better-auth/api';\nimport { customSession, lastLoginMethod, twoFactor } from 'better-auth/plugins';\nimport { magicLink } from 'better-auth/plugins/magic-link';\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\n// Check if we are in production based on the domain or NODE_ENV\nconst isProd = process.env.DOMAIN !== 'localhost';\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 baseURL: process.env.BACKEND_URL,\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.APP_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 (path.includes('/verify-email')) {\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.APP_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 crossSubDomainCookies: {\n enabled: isProd,\n domain: isProd ? process.env.DOMAIN : undefined,\n additionalCookies: ['session_token'],\n },\n cookiePrefix: 'intlayer',\n cookies: {\n session_token: {\n name: 'session_token',\n attributes: {\n httpOnly: true,\n secure: isProd,\n sameSite: 'lax',\n },\n },\n },\n },\n\n secret: process.env.BETTER_AUTH_SECRET as string,\n session: {\n modelName: 'sessions',\n id: 'id',\n\n // Session lives for 30 days; each access made more than 1 day after the\n // last refresh slides the expiry forward, so an active user effectively\n // stays signed in indefinitely.\n expiresIn: 60 * 60 * 24 * 30,\n updateAge: 60 * 60 * 24,\n\n // Cache the session in a signed cookie for 5 minutes to avoid hitting\n // Mongo on every request while still picking up updateAge refreshes.\n cookieCache: {\n enabled: false,\n maxAge: 5 * 60,\n },\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 // activeOrganizationId may be stored as a BSON ObjectId in MongoDB\n // (legacy data), so we normalize it to a string before querying.\n const rawOrgId = typedSession.activeOrganizationId as any;\n const orgIdStr: string | null =\n typeof rawOrgId === 'string'\n ? rawOrgId\n : rawOrgId.buffer instanceof Uint8Array\n ? Buffer.from(rawOrgId.buffer).toString('hex')\n : null;\n\n if (!orgIdStr) {\n typedSession.activeOrganizationId = undefined;\n typedSession.activeProjectId = undefined;\n }\n\n const orgData = orgIdStr ? await getOrganizationById(orgIdStr) : null;\n\n if (orgData) {\n organizationAPI = mapOrganizationToAPI(orgData);\n } else {\n // Organization ID is present in session but not found in DB (Zombie session)\n // We detach the organization to prevent login errors\n await dbClient\n .db()\n .collection('sessions')\n .updateOne(\n { id: typedSession.id },\n {\n $set: { activeOrganizationId: null, activeProjectId: null },\n }\n );\n\n // Clear it locally so the current request proceeds without error\n typedSession.activeOrganizationId = undefined;\n typedSession.activeProjectId = undefined;\n }\n }\n if (typedSession.activeProjectId) {\n const rawProjectId = typedSession.activeProjectId as any;\n const projectIdStr: string | null =\n typeof rawProjectId === 'string'\n ? rawProjectId\n : rawProjectId.buffer instanceof Uint8Array\n ? Buffer.from(rawProjectId.buffer).toString('hex')\n : null;\n const projectData = projectIdStr\n ? await getProjectById(projectIdStr)\n : null;\n\n if (projectData) {\n projectAPI = mapProjectToAPI(projectData);\n } else {\n // Organization ID is present in session but not found in DB (Zombie session)\n // We detach the organization to prevent login errors\n await dbClient\n .db()\n .collection('sessions')\n .updateOne(\n { id: typedSession.id },\n {\n $set: { activeProjectId: null },\n }\n );\n\n // Clear it locally so the current request proceeds without error\n typedSession.activeProjectId = undefined;\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 lastLoginMethod({\n storeInDatabase: true, // adds user.lastLoginMethod in DB and session\n schema: {\n user: {\n lastLoginMethod: 'lastLoginMethod', // Custom field name\n },\n },\n customResolveMethod: (context) => {\n // When user clicks the magic link\n if (context.path === '/magic-link/verify') {\n return 'magic-link';\n }\n\n // Fallback to default behavior for everything else\n return null;\n },\n }),\n passkey({\n rpID: process.env.DOMAIN,\n rpName: 'Intlayer',\n }),\n twoFactor(),\n magicLink({\n sendMagicLink: async ({ email, url }) => {\n logger.info('sending magic link', { email, url });\n await sendEmail({\n type: 'magicLink',\n to: email,\n username: email.split('@')[0],\n magicLink: url,\n });\n },\n }),\n sso({\n organizationProvisioning: {},\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.APP_URL}/auth/password/reset?token=${token}`,\n });\n },\n resetPasswordTokenExpiresIn: 3600,\n },\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 trustedOrigins: [\n process.env.WEBSITE_URL,\n process.env.APP_URL,\n process.env.SHOWCASE_URL,\n ].filter(Boolean) as string[],\n\n accountLinking: {\n enabled: true, // allow linking in general\n trustedProviders: [\n 'google',\n 'github',\n 'linkedin',\n 'gitlab',\n 'atlassian',\n 'microsoft',\n 'email-password',\n 'magic-link',\n 'passkey',\n ],\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 atlassian: {\n clientId: process.env.ATLASSIAN_CLIENT_ID as string,\n clientSecret: process.env.ATLASSIAN_CLIENT_SECRET as string,\n },\n gitlab: {\n clientId: process.env.GITLAB_CLIENT_ID as string,\n clientSecret: process.env.GITLAB_CLIENT_SECRET as string,\n },\n linkedin: {\n clientId: process.env.LINKEDIN_CLIENT_ID as string,\n clientSecret: process.env.LINKEDIN_CLIENT_SECRET as string,\n },\n microsoft: {\n clientId: process.env.MICROSOFT_CLIENT_ID as string,\n clientSecret: process.env.MICROSOFT_CLIENT_SECRET as string,\n },\n // socialProviders: {\n // apple: {\n // clientId: process.env.APPLE_CLIENT_ID as string,\n // clientSecret: process.env.APPLE_CLIENT_SECRET as string,\n // // Optional\n // appBundleIdentifier: process.env\n // .APPLE_APP_BUNDLE_IDENTIFIER as string,\n // },\n // },\n // // Add appleid.apple.com to trustedOrigins for Sign In with Apple flows\n // trustedOrigins: ['https://appleid.apple.com'],\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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"}
|