fastify-intlayer 9.5.12 → 9.7.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/cjs/index.cjs +72 -87
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.mjs +73 -88
- package/dist/esm/index.mjs.map +1 -1
- package/dist/types/index.d.ts +28 -11
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +7 -9
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,25 +1,65 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
3
|
+
let node_async_hooks = require("node:async_hooks");
|
|
4
|
+
let _intlayer_config_built = require("@intlayer/config/built");
|
|
3
5
|
let _intlayer_config_node = require("@intlayer/config/node");
|
|
4
6
|
let _intlayer_core_interpreter = require("@intlayer/core/interpreter");
|
|
5
7
|
let _intlayer_core_localization = require("@intlayer/core/localization");
|
|
6
8
|
let _intlayer_core_utils = require("@intlayer/core/utils");
|
|
7
9
|
let _intlayer_engine_build = require("@intlayer/engine/build");
|
|
8
|
-
let cls_hooked = require("cls-hooked");
|
|
9
10
|
let fastify_plugin = require("fastify-plugin");
|
|
10
11
|
fastify_plugin = require_runtime.__toESM(fastify_plugin);
|
|
11
12
|
|
|
12
13
|
//#region src/index.ts
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Request-scoped Intlayer context, holding the `req.intlayer` object itself.
|
|
16
|
+
*/
|
|
17
|
+
const intlayerStorage = new node_async_hooks.AsyncLocalStorage();
|
|
18
|
+
(0, _intlayer_core_interpreter.registerAmbientLocaleResolver)(() => intlayerStorage.getStore()?.locale);
|
|
15
19
|
let debug = () => {};
|
|
16
20
|
/**
|
|
21
|
+
* Translates `content` into `locale`, falling back to the default locale.
|
|
22
|
+
*/
|
|
23
|
+
const translate = (content, locale) => typeof content === "string" ? content : (0, _intlayer_core_interpreter.getTranslation)(content, locale, _intlayer_config_built.internationalization.defaultLocale);
|
|
24
|
+
/**
|
|
25
|
+
* Reads a single-valued request header.
|
|
26
|
+
*/
|
|
27
|
+
const getHeader = (request, name) => {
|
|
28
|
+
const value = request.headers[name];
|
|
29
|
+
return Array.isArray(value) ? value.join(",") : value;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Builds the Intlayer state of a request.
|
|
33
|
+
*/
|
|
34
|
+
const createRequestContext = (request) => {
|
|
35
|
+
const { locales, defaultLocale } = _intlayer_config_built.internationalization;
|
|
36
|
+
const parsedCookies = request.cookies;
|
|
37
|
+
const localeFromStorage = (0, _intlayer_core_utils.getLocaleFromStorageServer)({
|
|
38
|
+
getCookie: (name) => parsedCookies ? parsedCookies[name] : (0, _intlayer_core_utils.getCookie)(name, request.headers.cookie ?? ""),
|
|
39
|
+
getHeader: (name) => getHeader(request, name)
|
|
40
|
+
});
|
|
41
|
+
const localeDetected = (0, _intlayer_core_localization.localeDetector)({ "accept-language": getHeader(request, "accept-language") }, locales, defaultLocale);
|
|
42
|
+
const locale = localeFromStorage ?? localeDetected;
|
|
43
|
+
return {
|
|
44
|
+
locale_storage: localeFromStorage,
|
|
45
|
+
locale_detected: localeDetected,
|
|
46
|
+
locale,
|
|
47
|
+
defaultLocale,
|
|
48
|
+
t: (content, localeArg) => translate(content, localeArg ?? locale),
|
|
49
|
+
getIntlayer: (key, localeArg, ...props) => (0, _intlayer_core_interpreter.getIntlayer)(key, localeArg ?? locale, ...props),
|
|
50
|
+
getDictionary: (key, localeArg, ...props) => (0, _intlayer_core_interpreter.getDictionary)(key, localeArg ?? locale, ...props)
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
17
54
|
* Fastify Plugin that integrates Intlayer into your Fastify application.
|
|
18
55
|
*
|
|
19
56
|
* It handles:
|
|
20
|
-
* 1. Locale detection from storage (cookies, headers)
|
|
57
|
+
* 1. Locale detection from storage (cookies, headers) then from `Accept-Language`.
|
|
21
58
|
* 2. Decorating the request object with `intlayer` data containing `t`, `getIntlayer`, and `getDictionary`.
|
|
22
|
-
* 3. Setting up
|
|
59
|
+
* 3. Setting up an `AsyncLocalStorage` context for programmatic access during the request lifecycle.
|
|
60
|
+
*
|
|
61
|
+
* The context is set in `onRequest`, so it also covers the app hooks, schema
|
|
62
|
+
* validation errors and error handlers.
|
|
23
63
|
*
|
|
24
64
|
* @example
|
|
25
65
|
* ```ts
|
|
@@ -30,63 +70,14 @@ let debug = () => {};
|
|
|
30
70
|
* fastify.register(intlayer);
|
|
31
71
|
* ```
|
|
32
72
|
*/
|
|
33
|
-
const fastifyIntlayer = async (fastify
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (process.env.NODE_ENV === "development") debug = (msg) => fastify.log.debug(msg);
|
|
37
|
-
/**
|
|
38
|
-
* Retrieves the locale from storage (cookies, headers).
|
|
39
|
-
* Note: req.cookies requires @fastify/cookie to be registered.
|
|
40
|
-
* We cast req to any to avoid hard dependency on @fastify/cookie types.
|
|
41
|
-
*/
|
|
42
|
-
const getStorageLocale = (req) => (0, _intlayer_core_utils.getLocaleFromStorageServer)({
|
|
43
|
-
getCookie: (name) => req.cookies?.[name],
|
|
44
|
-
getHeader: (name) => req.headers?.[name]
|
|
45
|
-
});
|
|
46
|
-
(0, _intlayer_engine_build.prepareIntlayerServer)(configuration, { label: "fastify-intlayer" });
|
|
47
|
-
const translateFunction = (req) => (content, locale) => {
|
|
48
|
-
const { locale: currentLocale, defaultLocale } = req.intlayer;
|
|
49
|
-
const targetLocale = locale ?? currentLocale;
|
|
50
|
-
if (typeof content === "undefined") return "";
|
|
51
|
-
if (typeof content === "string") return content;
|
|
52
|
-
if (typeof content?.[targetLocale] === "undefined") {
|
|
53
|
-
if (typeof content?.[defaultLocale] === "undefined") return content;
|
|
54
|
-
else return (0, _intlayer_core_interpreter.getTranslation)(content, defaultLocale);
|
|
55
|
-
}
|
|
56
|
-
return (0, _intlayer_core_interpreter.getTranslation)(content, targetLocale);
|
|
57
|
-
};
|
|
73
|
+
const fastifyIntlayer = async (fastify) => {
|
|
74
|
+
if (process.env.NODE_ENV === "development") debug = (message) => fastify.log.debug(message);
|
|
75
|
+
(0, _intlayer_engine_build.prepareIntlayerServer)((0, _intlayer_config_node.getConfiguration)({ logFunctions: fastify.log }), { label: "fastify-intlayer" });
|
|
58
76
|
if (!fastify.hasRequestDecorator("intlayer")) fastify.decorateRequest("intlayer", null);
|
|
59
|
-
fastify.addHook("
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const value = req.headers[key];
|
|
64
|
-
if (typeof value === "string") negotiatorHeaders[key] = value;
|
|
65
|
-
else if (Array.isArray(value)) negotiatorHeaders[key] = value.join(",");
|
|
66
|
-
}
|
|
67
|
-
const localeDetected = (0, _intlayer_core_localization.localeDetector)(negotiatorHeaders, internationalization.locales, internationalization.defaultLocale);
|
|
68
|
-
const locale = localeFromStorage ?? localeDetected;
|
|
69
|
-
const defaultLocale = internationalization.defaultLocale;
|
|
70
|
-
const getIntlayerWrapped = (key, localeArg, ...props) => (0, _intlayer_core_interpreter.getIntlayer)(key, localeArg ?? locale, ...props);
|
|
71
|
-
const getDictionaryWrapped = (key, localeArg, ...props) => (0, _intlayer_core_interpreter.getDictionary)(key, localeArg ?? locale, ...props);
|
|
72
|
-
req.intlayer = {
|
|
73
|
-
locale_storage: localeFromStorage,
|
|
74
|
-
locale_detected: localeDetected,
|
|
75
|
-
locale,
|
|
76
|
-
defaultLocale,
|
|
77
|
-
getIntlayer: getIntlayerWrapped,
|
|
78
|
-
getDictionary: getDictionaryWrapped,
|
|
79
|
-
t: void 0
|
|
80
|
-
};
|
|
81
|
-
const t = translateFunction(req);
|
|
82
|
-
req.intlayer.t = t;
|
|
83
|
-
appNamespace.run(() => {
|
|
84
|
-
appNamespace.set("locale", locale);
|
|
85
|
-
appNamespace.set("t", t);
|
|
86
|
-
appNamespace.set("getIntlayer", getIntlayerWrapped);
|
|
87
|
-
appNamespace.set("getDictionary", getDictionaryWrapped);
|
|
88
|
-
done();
|
|
89
|
-
});
|
|
77
|
+
fastify.addHook("onRequest", (request, _reply, done) => {
|
|
78
|
+
const context = createRequestContext(request);
|
|
79
|
+
request.intlayer = context;
|
|
80
|
+
intlayerStorage.run(context, done);
|
|
90
81
|
});
|
|
91
82
|
};
|
|
92
83
|
const intlayer = (0, fastify_plugin.default)(fastifyIntlayer, {
|
|
@@ -96,8 +87,8 @@ const intlayer = (0, fastify_plugin.default)(fastifyIntlayer, {
|
|
|
96
87
|
/**
|
|
97
88
|
* Global translation function that retrieves content for the current locale in Fastify.
|
|
98
89
|
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
90
|
+
* Falls back to the configured default locale when called outside of a request
|
|
91
|
+
* handled by the `intlayer` plugin.
|
|
101
92
|
*
|
|
102
93
|
* @param content - A map of locales to content.
|
|
103
94
|
* @param locale - Optional locale override.
|
|
@@ -117,34 +108,28 @@ const intlayer = (0, fastify_plugin.default)(fastifyIntlayer, {
|
|
|
117
108
|
* ```
|
|
118
109
|
*/
|
|
119
110
|
const t = (content, locale) => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
} catch (error) {
|
|
125
|
-
debug(error.message);
|
|
126
|
-
return (0, _intlayer_core_interpreter.getTranslation)(content, locale ?? "en");
|
|
127
|
-
}
|
|
111
|
+
const context = intlayerStorage.getStore();
|
|
112
|
+
if (context) return context.t(content, locale);
|
|
113
|
+
debug("Using the import { t } from \"fastify-intlayer\" outside of a request context. Use req.intlayer.t instead.");
|
|
114
|
+
return (0, _intlayer_core_interpreter.getTranslation)(content, locale ?? _intlayer_config_built.internationalization.defaultLocale);
|
|
128
115
|
};
|
|
116
|
+
/**
|
|
117
|
+
* Retrieves a dictionary by key, using the locale of the current request by default.
|
|
118
|
+
*/
|
|
129
119
|
const getIntlayer = (...args) => {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
} catch (error) {
|
|
135
|
-
debug(error.message);
|
|
136
|
-
return (0, _intlayer_core_interpreter.getIntlayer)(...args);
|
|
137
|
-
}
|
|
120
|
+
const context = intlayerStorage.getStore();
|
|
121
|
+
if (context) return context.getIntlayer(...args);
|
|
122
|
+
debug("Context not found. Ensure you are inside a request handling flow.");
|
|
123
|
+
return (0, _intlayer_core_interpreter.getIntlayer)(...args);
|
|
138
124
|
};
|
|
125
|
+
/**
|
|
126
|
+
* Retrieves an imported dictionary, using the locale of the current request by default.
|
|
127
|
+
*/
|
|
139
128
|
const getDictionary = (...args) => {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
} catch (error) {
|
|
145
|
-
debug(error.message);
|
|
146
|
-
return (0, _intlayer_core_interpreter.getDictionary)(...args);
|
|
147
|
-
}
|
|
129
|
+
const context = intlayerStorage.getStore();
|
|
130
|
+
if (context) return context.getDictionary(...args);
|
|
131
|
+
debug("Context not found. Ensure you are inside a request handling flow.");
|
|
132
|
+
return (0, _intlayer_core_interpreter.getDictionary)(...args);
|
|
148
133
|
};
|
|
149
134
|
|
|
150
135
|
//#endregion
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["createNamespace","registerAmbientLocaleResolver","getConfiguration","getLocaleFromStorageServer","getTranslation","localeDetector","getIntlayerFunction","getDictionaryFunction","fp"],"sources":["../../src/index.ts"],"sourcesContent":["import { getConfiguration } from '@intlayer/config/node';\nimport {\n getDictionary as getDictionaryFunction,\n getIntlayer as getIntlayerFunction,\n getTranslation,\n registerAmbientLocaleResolver,\n} from '@intlayer/core/interpreter';\nimport { localeDetector } from '@intlayer/core/localization';\nimport { getLocaleFromStorageServer } from '@intlayer/core/utils';\nimport { prepareIntlayerServer } from '@intlayer/engine/build';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { StrictModeLocaleMap } from '@intlayer/types/module_augmentation';\nimport { createNamespace } from 'cls-hooked';\nimport type { FastifyPluginAsync, FastifyRequest } from 'fastify';\nimport fp from 'fastify-plugin';\n\n// Module augmentation to type the request decoration\ndeclare module 'fastify' {\n interface FastifyRequest {\n intlayer: {\n locale: Locale;\n defaultLocale: Locale;\n locale_storage?: Locale;\n locale_detected?: Locale;\n t: <T extends string>(\n content: StrictModeLocaleMap<T> | string,\n locale?: Locale\n ) => T;\n getIntlayer: typeof getIntlayerFunction;\n getDictionary: typeof getDictionaryFunction;\n };\n }\n}\n\nconst appNamespace = createNamespace('app');\n\n// Lets a bare `getIntlayer` / `getDictionary` from any Intlayer package resolve\n// to the locale of the request being handled.\nregisterAmbientLocaleResolver(() => appNamespace.get('locale'));\n\n// Zero-cost fallback, will be updated with fastify logger in dev mode\nlet debug: (message: string) => void = () => {};\n\n/**\n * Fastify Plugin that integrates Intlayer into your Fastify application.\n *\n * It handles:\n * 1. Locale detection from storage (cookies, headers).\n * 2. Decorating the request object with `intlayer` data containing `t`, `getIntlayer`, and `getDictionary`.\n * 3. Setting up a `cls-hooked` namespace for programmatic access during the request lifecycle.\n *\n * @example\n * ```ts\n * import Fastify from 'fastify';\n * import { intlayer } from 'fastify-intlayer';\n *\n * const fastify = Fastify();\n * fastify.register(intlayer);\n * ```\n */\nconst fastifyIntlayer: FastifyPluginAsync = async (fastify, _opts) => {\n const configuration = getConfiguration({\n logFunctions: fastify.log, // Req not defined yet\n });\n const { internationalization } = configuration;\n\n // In dev mode, use fastify logger to debug messages\n if (process.env['NODE_ENV'] === 'development') {\n debug = (msg: string) => fastify.log.debug(msg);\n }\n\n /**\n * Retrieves the locale from storage (cookies, headers).\n * Note: req.cookies requires @fastify/cookie to be registered.\n * We cast req to any to avoid hard dependency on @fastify/cookie types.\n */\n const getStorageLocale = (req: FastifyRequest): Locale | undefined =>\n getLocaleFromStorageServer({\n getCookie: (name: string) => (req as any).cookies?.[name],\n getHeader: (name: string) => req.headers?.[name] as string | undefined,\n });\n\n prepareIntlayerServer(configuration, { label: 'fastify-intlayer' });\n\n const translateFunction =\n (req: FastifyRequest) =>\n <T extends string>(\n content: StrictModeLocaleMap<T> | string,\n locale?: Locale\n ): T => {\n // Access the decorated state from the request\n const { locale: currentLocale, defaultLocale } = req.intlayer;\n\n const targetLocale = locale ?? currentLocale;\n\n if (typeof content === 'undefined') {\n return '' as unknown as T;\n }\n\n if (typeof content === 'string') {\n return content as unknown as T;\n }\n\n if (\n typeof content?.[\n targetLocale as unknown as keyof StrictModeLocaleMap<T>\n ] === 'undefined'\n ) {\n if (\n typeof content?.[\n defaultLocale as unknown as keyof StrictModeLocaleMap<T>\n ] === 'undefined'\n ) {\n return content as unknown as T;\n } else {\n return getTranslation(content, defaultLocale);\n }\n }\n\n return getTranslation(content, targetLocale);\n };\n\n // Decorate the request object to ensure types are stable.\n // We use 'null as any' to bypass the initial type check, knowing\n // the preHandler will populate it before any route handler runs.\n if (!fastify.hasRequestDecorator('intlayer')) {\n fastify.decorateRequest('intlayer', null as any);\n }\n\n fastify.addHook('preHandler', (req, _reply, done) => {\n // Detect if locale is set by intlayer frontend lib in the headers\n const localeFromStorage = getStorageLocale(req);\n\n const negotiatorHeaders: Record<string, string> = {};\n\n // Copy all headers from the request to negotiatorHeaders\n if (req && typeof req.headers === 'object') {\n for (const key in req.headers) {\n const value = req.headers[key];\n\n if (typeof value === 'string') {\n negotiatorHeaders[key] = value;\n } else if (Array.isArray(value)) {\n // Handle array headers (unlikely for accept-language but possible in Fastify)\n negotiatorHeaders[key] = value.join(',');\n }\n }\n }\n\n const localeDetected = localeDetector(\n negotiatorHeaders,\n internationalization.locales,\n internationalization.defaultLocale\n );\n\n const locale = localeFromStorage ?? localeDetected;\n const defaultLocale = internationalization.defaultLocale;\n\n // Helper functions bound to the current request context\n const getIntlayerWrapped: typeof getIntlayerFunction = (\n key,\n localeArg,\n ...props\n ) =>\n getIntlayerFunction(\n key,\n (localeArg ?? locale) as typeof localeArg,\n ...props\n );\n\n const getDictionaryWrapped: typeof getDictionaryFunction = (\n key,\n localeArg,\n ...props\n ) =>\n getDictionaryFunction(\n key,\n (localeArg ?? locale) as typeof localeArg,\n ...props\n );\n\n // Assign data to request decoration\n req.intlayer = {\n locale_storage: localeFromStorage,\n locale_detected: localeDetected,\n locale,\n defaultLocale,\n getIntlayer: getIntlayerWrapped,\n getDictionary: getDictionaryWrapped,\n t: undefined as unknown as any, // Placeholder\n };\n\n // Now bind t using the updated req\n const t = translateFunction(req);\n req.intlayer.t = t;\n\n // Run CLS context\n appNamespace.run(() => {\n appNamespace.set('locale', locale);\n appNamespace.set('t', t);\n appNamespace.set('getIntlayer', getIntlayerWrapped);\n appNamespace.set('getDictionary', getDictionaryWrapped);\n\n done();\n });\n });\n};\n\n// Export as a Fastify Plugin (wrapped in fp to skip encapsulation)\nexport const intlayer = fp(fastifyIntlayer as any, {\n name: 'fastify-intlayer',\n fastify: '5.x',\n}) as unknown as FastifyPluginAsync;\n\n/**\n * Global translation function that retrieves content for the current locale in Fastify.\n *\n * This function utilizes CLS (Async Local Storage) and must be used within a request context\n * managed by the `intlayer` plugin.\n *\n * @param content - A map of locales to content.\n * @param locale - Optional locale override.\n * @returns The translated content.\n *\n * @example\n * ```ts\n * import { t } from 'fastify-intlayer';\n *\n * fastify.get('/', async (req, reply) => {\n * const greeting = t({\n * en: 'Hello',\n * fr: 'Bonjour',\n * });\n * return greeting;\n * });\n * ```\n */\nexport const t = <Content = string>(\n content: StrictModeLocaleMap<Content>,\n locale?: Locale\n): Content => {\n try {\n if (typeof appNamespace === 'undefined') {\n throw new Error(\n 'Intlayer is not initialized. Register the plugin `fastify.register(intlayer)`.'\n );\n }\n\n if (typeof appNamespace.get('t') !== 'function') {\n throw new Error(\n 'Using the import { t } from \"fastify-intlayer\" is not supported in your environment outside of a request context or proper setup. Use req.intlayer.t instead.'\n );\n }\n\n return appNamespace.get('t')(content, locale);\n } catch (error) {\n debug((error as Error).message);\n\n return getTranslation(content, locale ?? 'en');\n }\n};\n\nexport const getIntlayer: typeof getIntlayerFunction = (...args) => {\n try {\n if (typeof appNamespace === 'undefined') {\n throw new Error(\n 'Intlayer is not initialized. Register the plugin `fastify.register(intlayer)`.'\n );\n }\n\n if (typeof appNamespace.get('getIntlayer') !== 'function') {\n throw new Error(\n 'Context not found. Ensure you are inside a request handling flow.'\n );\n }\n\n return appNamespace.get('getIntlayer')(...args);\n } catch (error) {\n debug((error as Error).message);\n\n return getIntlayerFunction(...args);\n }\n};\n\nexport const getDictionary: typeof getDictionaryFunction = (...args) => {\n try {\n if (typeof appNamespace === 'undefined') {\n throw new Error(\n 'Intlayer is not initialized. Register the plugin `fastify.register(intlayer)`.'\n );\n }\n\n if (typeof appNamespace.get('getDictionary') !== 'function') {\n throw new Error(\n 'Context not found. Ensure you are inside a request handling flow.'\n );\n }\n\n return appNamespace.get('getDictionary')(...args);\n } catch (error) {\n debug((error as Error).message);\n\n return getDictionaryFunction(...args);\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["AsyncLocalStorage","registerAmbientLocaleResolver","getTranslation","internationalization","getLocaleFromStorageServer","getCookie","localeDetector","getIntlayerFunction","getDictionaryFunction","getConfiguration","fp"],"sources":["../../src/index.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport { internationalization } from '@intlayer/config/built';\nimport { getConfiguration } from '@intlayer/config/node';\nimport {\n getDictionary as getDictionaryFunction,\n getIntlayer as getIntlayerFunction,\n getTranslation,\n registerAmbientLocaleResolver,\n} from '@intlayer/core/interpreter';\nimport { localeDetector } from '@intlayer/core/localization';\nimport { getCookie, getLocaleFromStorageServer } from '@intlayer/core/utils';\nimport { prepareIntlayerServer } from '@intlayer/engine/build';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { StrictModeLocaleMap } from '@intlayer/types/module_augmentation';\nimport type { FastifyPluginAsync, FastifyRequest } from 'fastify';\nimport fp from 'fastify-plugin';\n\n/**\n * Intlayer state of the request being handled.\n */\nexport type IntlayerRequestContext = {\n /** Locale to use for this request, `locale_storage` taking precedence. */\n locale: Locale;\n /** Locale configured as fallback in `intlayer.config.ts`. */\n defaultLocale: Locale;\n /** Locale explicitly requested by the client through a cookie or a header. */\n locale_storage?: Locale;\n /** Locale negotiated from the `Accept-Language` header. */\n locale_detected?: Locale;\n /** Translates an inline locale map. */\n t: <T extends string>(\n content: StrictModeLocaleMap<T> | string,\n locale?: Locale\n ) => T;\n /** Reads a dictionary by key, defaulting to the request locale. */\n getIntlayer: typeof getIntlayerFunction;\n /** Reads an imported dictionary, defaulting to the request locale. */\n getDictionary: typeof getDictionaryFunction;\n};\n\n// Module augmentation to type the request decoration\ndeclare module 'fastify' {\n interface FastifyRequest {\n intlayer: IntlayerRequestContext;\n }\n}\n\n/**\n * Request-scoped Intlayer context, holding the `req.intlayer` object itself.\n */\nconst intlayerStorage = new AsyncLocalStorage<IntlayerRequestContext>();\n\n// Lets a bare `getIntlayer` / `getDictionary` from any Intlayer package resolve\n// to the locale of the request being handled.\nregisterAmbientLocaleResolver(() => intlayerStorage.getStore()?.locale);\n\n// Zero-cost fallback, will be updated with fastify logger in dev mode\nlet debug: (message: string) => void = () => {};\n\n/**\n * Translates `content` into `locale`, falling back to the default locale.\n */\nconst translate = <T extends string>(\n content: StrictModeLocaleMap<T> | string,\n locale: Locale\n): T =>\n typeof content === 'string'\n ? (content as T)\n : getTranslation(content, locale, internationalization.defaultLocale);\n\n/**\n * Reads a single-valued request header.\n */\nconst getHeader = (\n request: FastifyRequest,\n name: string\n): string | undefined => {\n const value = request.headers[name];\n\n return Array.isArray(value) ? value.join(',') : value;\n};\n\n/**\n * Builds the Intlayer state of a request.\n */\nconst createRequestContext = (\n request: FastifyRequest\n): IntlayerRequestContext => {\n const { locales, defaultLocale } = internationalization;\n\n // Parsed cookies need `@fastify/cookie`: read the raw header otherwise\n const parsedCookies = (\n request as FastifyRequest & { cookies?: Record<string, string | undefined> }\n ).cookies;\n\n const localeFromStorage = getLocaleFromStorageServer({\n getCookie: (name: string) =>\n parsedCookies\n ? parsedCookies[name]\n : getCookie(name, request.headers.cookie ?? ''),\n getHeader: (name: string) => getHeader(request, name),\n });\n\n const localeDetected = localeDetector(\n { 'accept-language': getHeader(request, 'accept-language') },\n locales,\n defaultLocale\n );\n\n const locale = localeFromStorage ?? localeDetected;\n\n return {\n locale_storage: localeFromStorage,\n locale_detected: localeDetected,\n locale,\n defaultLocale,\n t: (content, localeArg) => translate(content, localeArg ?? locale),\n getIntlayer: (key, localeArg, ...props) =>\n getIntlayerFunction(\n key,\n (localeArg ?? locale) as typeof localeArg,\n ...props\n ),\n getDictionary: (key, localeArg, ...props) =>\n getDictionaryFunction(\n key,\n (localeArg ?? locale) as typeof localeArg,\n ...props\n ),\n };\n};\n\n/**\n * Fastify Plugin that integrates Intlayer into your Fastify application.\n *\n * It handles:\n * 1. Locale detection from storage (cookies, headers) then from `Accept-Language`.\n * 2. Decorating the request object with `intlayer` data containing `t`, `getIntlayer`, and `getDictionary`.\n * 3. Setting up an `AsyncLocalStorage` context for programmatic access during the request lifecycle.\n *\n * The context is set in `onRequest`, so it also covers the app hooks, schema\n * validation errors and error handlers.\n *\n * @example\n * ```ts\n * import Fastify from 'fastify';\n * import { intlayer } from 'fastify-intlayer';\n *\n * const fastify = Fastify();\n * fastify.register(intlayer);\n * ```\n */\nconst fastifyIntlayer: FastifyPluginAsync = async (fastify) => {\n // In dev mode, use fastify logger to debug messages\n if (process.env['NODE_ENV'] === 'development') {\n debug = (message: string) => fastify.log.debug(message);\n }\n\n prepareIntlayerServer(getConfiguration({ logFunctions: fastify.log }), {\n label: 'fastify-intlayer',\n });\n\n // Declared upfront so every request object keeps the same shape\n if (!fastify.hasRequestDecorator('intlayer')) {\n fastify.decorateRequest(\n 'intlayer',\n null as unknown as IntlayerRequestContext\n );\n }\n\n fastify.addHook('onRequest', (request, _reply, done) => {\n const context = createRequestContext(request);\n\n request.intlayer = context;\n\n // Run the rest of the request lifecycle inside the Intlayer context\n intlayerStorage.run(context, done);\n });\n};\n\n// Export as a Fastify Plugin (wrapped in fp to skip encapsulation)\nexport const intlayer = fp(fastifyIntlayer, {\n name: 'fastify-intlayer',\n fastify: '5.x',\n});\n\n/**\n * Global translation function that retrieves content for the current locale in Fastify.\n *\n * Falls back to the configured default locale when called outside of a request\n * handled by the `intlayer` plugin.\n *\n * @param content - A map of locales to content.\n * @param locale - Optional locale override.\n * @returns The translated content.\n *\n * @example\n * ```ts\n * import { t } from 'fastify-intlayer';\n *\n * fastify.get('/', async (req, reply) => {\n * const greeting = t({\n * en: 'Hello',\n * fr: 'Bonjour',\n * });\n * return greeting;\n * });\n * ```\n */\nexport const t = <Content = string>(\n content: StrictModeLocaleMap<Content>,\n locale?: Locale\n): Content => {\n const context = intlayerStorage.getStore();\n\n if (context) {\n return context.t(content as StrictModeLocaleMap<string>, locale) as Content;\n }\n\n debug(\n 'Using the import { t } from \"fastify-intlayer\" outside of a request context. Use req.intlayer.t instead.'\n );\n\n return getTranslation(content, locale ?? internationalization.defaultLocale);\n};\n\n/**\n * Retrieves a dictionary by key, using the locale of the current request by default.\n */\nexport const getIntlayer: typeof getIntlayerFunction = (...args) => {\n const context = intlayerStorage.getStore();\n\n if (context) {\n return context.getIntlayer(...args);\n }\n\n debug('Context not found. Ensure you are inside a request handling flow.');\n\n return getIntlayerFunction(...args);\n};\n\n/**\n * Retrieves an imported dictionary, using the locale of the current request by default.\n */\nexport const getDictionary: typeof getDictionaryFunction = (...args) => {\n const context = intlayerStorage.getStore();\n\n if (context) {\n return context.getDictionary(...args);\n }\n\n debug('Context not found. Ensure you are inside a request handling flow.');\n\n return getDictionaryFunction(...args);\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAkDA,MAAM,kBAAkB,IAAIA,mCAA0C;IAItEC,gEAAoC,gBAAgB,SAAS,CAAC,EAAE,MAAM;AAGtE,IAAI,cAAyC,CAAC;;;;AAK9C,MAAM,aACJ,SACA,WAEA,OAAO,YAAY,WACd,cACDC,2CAAe,SAAS,QAAQC,4CAAqB,aAAa;;;;AAKxE,MAAM,aACJ,SACA,SACuB;CACvB,MAAM,QAAQ,QAAQ,QAAQ;CAE9B,OAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,GAAG,IAAI;AAClD;;;;AAKA,MAAM,wBACJ,YAC2B;CAC3B,MAAM,EAAE,SAAS,kBAAkBA;CAGnC,MAAM,gBACJ,QACA;CAEF,MAAM,wBAAoBC,iDAA2B;EACnD,YAAY,SACV,gBACI,cAAc,YACdC,gCAAU,MAAM,QAAQ,QAAQ,UAAU,EAAE;EAClD,YAAY,SAAiB,UAAU,SAAS,IAAI;CACtD,CAAC;CAED,MAAM,qBAAiBC,4CACrB,EAAE,mBAAmB,UAAU,SAAS,iBAAiB,EAAE,GAC3D,SACA,aACF;CAEA,MAAM,SAAS,qBAAqB;CAEpC,OAAO;EACL,gBAAgB;EAChB,iBAAiB;EACjB;EACA;EACA,IAAI,SAAS,cAAc,UAAU,SAAS,aAAa,MAAM;EACjE,cAAc,KAAK,WAAW,GAAG,cAC/BC,wCACE,KACC,aAAa,QACd,GAAG,KACL;EACF,gBAAgB,KAAK,WAAW,GAAG,cACjCC,0CACE,KACC,aAAa,QACd,GAAG,KACL;CACJ;AACF;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAM,kBAAsC,OAAO,YAAY;CAE7D,6BAAgC,eAC9B,SAAS,YAAoB,QAAQ,IAAI,MAAM,OAAO;CAGxD,sDAAsBC,wCAAiB,EAAE,cAAc,QAAQ,IAAI,CAAC,GAAG,EACrE,OAAO,mBACT,CAAC;CAGD,IAAI,CAAC,QAAQ,oBAAoB,UAAU,GACzC,QAAQ,gBACN,YACA,IACF;CAGF,QAAQ,QAAQ,cAAc,SAAS,QAAQ,SAAS;EACtD,MAAM,UAAU,qBAAqB,OAAO;EAE5C,QAAQ,WAAW;EAGnB,gBAAgB,IAAI,SAAS,IAAI;CACnC,CAAC;AACH;AAGA,MAAa,eAAWC,wBAAG,iBAAiB;CAC1C,MAAM;CACN,SAAS;AACX,CAAC;;;;;;;;;;;;;;;;;;;;;;;;AAyBD,MAAa,KACX,SACA,WACY;CACZ,MAAM,UAAU,gBAAgB,SAAS;CAEzC,IAAI,SACF,OAAO,QAAQ,EAAE,SAAwC,MAAM;CAGjE,MACE,4GACF;CAEA,WAAOR,2CAAe,SAAS,UAAUC,4CAAqB,aAAa;AAC7E;;;;AAKA,MAAa,eAA2C,GAAG,SAAS;CAClE,MAAM,UAAU,gBAAgB,SAAS;CAEzC,IAAI,SACF,OAAO,QAAQ,YAAY,GAAG,IAAI;CAGpC,MAAM,mEAAmE;CAEzE,WAAOI,wCAAoB,GAAG,IAAI;AACpC;;;;AAKA,MAAa,iBAA+C,GAAG,SAAS;CACtE,MAAM,UAAU,gBAAgB,SAAS;CAEzC,IAAI,SACF,OAAO,QAAQ,cAAc,GAAG,IAAI;CAGtC,MAAM,mEAAmE;CAEzE,WAAOC,0CAAsB,GAAG,IAAI;AACtC"}
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1,22 +1,62 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { internationalization } from "@intlayer/config/built";
|
|
1
3
|
import { getConfiguration } from "@intlayer/config/node";
|
|
2
4
|
import { getDictionary as getDictionary$1, getIntlayer as getIntlayer$1, getTranslation, registerAmbientLocaleResolver } from "@intlayer/core/interpreter";
|
|
3
5
|
import { localeDetector } from "@intlayer/core/localization";
|
|
4
|
-
import { getLocaleFromStorageServer } from "@intlayer/core/utils";
|
|
6
|
+
import { getCookie, getLocaleFromStorageServer } from "@intlayer/core/utils";
|
|
5
7
|
import { prepareIntlayerServer } from "@intlayer/engine/build";
|
|
6
|
-
import { createNamespace } from "cls-hooked";
|
|
7
8
|
import fp from "fastify-plugin";
|
|
8
9
|
|
|
9
10
|
//#region src/index.ts
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Request-scoped Intlayer context, holding the `req.intlayer` object itself.
|
|
13
|
+
*/
|
|
14
|
+
const intlayerStorage = new AsyncLocalStorage();
|
|
15
|
+
registerAmbientLocaleResolver(() => intlayerStorage.getStore()?.locale);
|
|
12
16
|
let debug = () => {};
|
|
13
17
|
/**
|
|
18
|
+
* Translates `content` into `locale`, falling back to the default locale.
|
|
19
|
+
*/
|
|
20
|
+
const translate = (content, locale) => typeof content === "string" ? content : getTranslation(content, locale, internationalization.defaultLocale);
|
|
21
|
+
/**
|
|
22
|
+
* Reads a single-valued request header.
|
|
23
|
+
*/
|
|
24
|
+
const getHeader = (request, name) => {
|
|
25
|
+
const value = request.headers[name];
|
|
26
|
+
return Array.isArray(value) ? value.join(",") : value;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Builds the Intlayer state of a request.
|
|
30
|
+
*/
|
|
31
|
+
const createRequestContext = (request) => {
|
|
32
|
+
const { locales, defaultLocale } = internationalization;
|
|
33
|
+
const parsedCookies = request.cookies;
|
|
34
|
+
const localeFromStorage = getLocaleFromStorageServer({
|
|
35
|
+
getCookie: (name) => parsedCookies ? parsedCookies[name] : getCookie(name, request.headers.cookie ?? ""),
|
|
36
|
+
getHeader: (name) => getHeader(request, name)
|
|
37
|
+
});
|
|
38
|
+
const localeDetected = localeDetector({ "accept-language": getHeader(request, "accept-language") }, locales, defaultLocale);
|
|
39
|
+
const locale = localeFromStorage ?? localeDetected;
|
|
40
|
+
return {
|
|
41
|
+
locale_storage: localeFromStorage,
|
|
42
|
+
locale_detected: localeDetected,
|
|
43
|
+
locale,
|
|
44
|
+
defaultLocale,
|
|
45
|
+
t: (content, localeArg) => translate(content, localeArg ?? locale),
|
|
46
|
+
getIntlayer: (key, localeArg, ...props) => getIntlayer$1(key, localeArg ?? locale, ...props),
|
|
47
|
+
getDictionary: (key, localeArg, ...props) => getDictionary$1(key, localeArg ?? locale, ...props)
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
14
51
|
* Fastify Plugin that integrates Intlayer into your Fastify application.
|
|
15
52
|
*
|
|
16
53
|
* It handles:
|
|
17
|
-
* 1. Locale detection from storage (cookies, headers)
|
|
54
|
+
* 1. Locale detection from storage (cookies, headers) then from `Accept-Language`.
|
|
18
55
|
* 2. Decorating the request object with `intlayer` data containing `t`, `getIntlayer`, and `getDictionary`.
|
|
19
|
-
* 3. Setting up
|
|
56
|
+
* 3. Setting up an `AsyncLocalStorage` context for programmatic access during the request lifecycle.
|
|
57
|
+
*
|
|
58
|
+
* The context is set in `onRequest`, so it also covers the app hooks, schema
|
|
59
|
+
* validation errors and error handlers.
|
|
20
60
|
*
|
|
21
61
|
* @example
|
|
22
62
|
* ```ts
|
|
@@ -27,63 +67,14 @@ let debug = () => {};
|
|
|
27
67
|
* fastify.register(intlayer);
|
|
28
68
|
* ```
|
|
29
69
|
*/
|
|
30
|
-
const fastifyIntlayer = async (fastify
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (process.env.NODE_ENV === "development") debug = (msg) => fastify.log.debug(msg);
|
|
34
|
-
/**
|
|
35
|
-
* Retrieves the locale from storage (cookies, headers).
|
|
36
|
-
* Note: req.cookies requires @fastify/cookie to be registered.
|
|
37
|
-
* We cast req to any to avoid hard dependency on @fastify/cookie types.
|
|
38
|
-
*/
|
|
39
|
-
const getStorageLocale = (req) => getLocaleFromStorageServer({
|
|
40
|
-
getCookie: (name) => req.cookies?.[name],
|
|
41
|
-
getHeader: (name) => req.headers?.[name]
|
|
42
|
-
});
|
|
43
|
-
prepareIntlayerServer(configuration, { label: "fastify-intlayer" });
|
|
44
|
-
const translateFunction = (req) => (content, locale) => {
|
|
45
|
-
const { locale: currentLocale, defaultLocale } = req.intlayer;
|
|
46
|
-
const targetLocale = locale ?? currentLocale;
|
|
47
|
-
if (typeof content === "undefined") return "";
|
|
48
|
-
if (typeof content === "string") return content;
|
|
49
|
-
if (typeof content?.[targetLocale] === "undefined") {
|
|
50
|
-
if (typeof content?.[defaultLocale] === "undefined") return content;
|
|
51
|
-
else return getTranslation(content, defaultLocale);
|
|
52
|
-
}
|
|
53
|
-
return getTranslation(content, targetLocale);
|
|
54
|
-
};
|
|
70
|
+
const fastifyIntlayer = async (fastify) => {
|
|
71
|
+
if (process.env.NODE_ENV === "development") debug = (message) => fastify.log.debug(message);
|
|
72
|
+
prepareIntlayerServer(getConfiguration({ logFunctions: fastify.log }), { label: "fastify-intlayer" });
|
|
55
73
|
if (!fastify.hasRequestDecorator("intlayer")) fastify.decorateRequest("intlayer", null);
|
|
56
|
-
fastify.addHook("
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const value = req.headers[key];
|
|
61
|
-
if (typeof value === "string") negotiatorHeaders[key] = value;
|
|
62
|
-
else if (Array.isArray(value)) negotiatorHeaders[key] = value.join(",");
|
|
63
|
-
}
|
|
64
|
-
const localeDetected = localeDetector(negotiatorHeaders, internationalization.locales, internationalization.defaultLocale);
|
|
65
|
-
const locale = localeFromStorage ?? localeDetected;
|
|
66
|
-
const defaultLocale = internationalization.defaultLocale;
|
|
67
|
-
const getIntlayerWrapped = (key, localeArg, ...props) => getIntlayer$1(key, localeArg ?? locale, ...props);
|
|
68
|
-
const getDictionaryWrapped = (key, localeArg, ...props) => getDictionary$1(key, localeArg ?? locale, ...props);
|
|
69
|
-
req.intlayer = {
|
|
70
|
-
locale_storage: localeFromStorage,
|
|
71
|
-
locale_detected: localeDetected,
|
|
72
|
-
locale,
|
|
73
|
-
defaultLocale,
|
|
74
|
-
getIntlayer: getIntlayerWrapped,
|
|
75
|
-
getDictionary: getDictionaryWrapped,
|
|
76
|
-
t: void 0
|
|
77
|
-
};
|
|
78
|
-
const t = translateFunction(req);
|
|
79
|
-
req.intlayer.t = t;
|
|
80
|
-
appNamespace.run(() => {
|
|
81
|
-
appNamespace.set("locale", locale);
|
|
82
|
-
appNamespace.set("t", t);
|
|
83
|
-
appNamespace.set("getIntlayer", getIntlayerWrapped);
|
|
84
|
-
appNamespace.set("getDictionary", getDictionaryWrapped);
|
|
85
|
-
done();
|
|
86
|
-
});
|
|
74
|
+
fastify.addHook("onRequest", (request, _reply, done) => {
|
|
75
|
+
const context = createRequestContext(request);
|
|
76
|
+
request.intlayer = context;
|
|
77
|
+
intlayerStorage.run(context, done);
|
|
87
78
|
});
|
|
88
79
|
};
|
|
89
80
|
const intlayer = fp(fastifyIntlayer, {
|
|
@@ -93,8 +84,8 @@ const intlayer = fp(fastifyIntlayer, {
|
|
|
93
84
|
/**
|
|
94
85
|
* Global translation function that retrieves content for the current locale in Fastify.
|
|
95
86
|
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
87
|
+
* Falls back to the configured default locale when called outside of a request
|
|
88
|
+
* handled by the `intlayer` plugin.
|
|
98
89
|
*
|
|
99
90
|
* @param content - A map of locales to content.
|
|
100
91
|
* @param locale - Optional locale override.
|
|
@@ -114,34 +105,28 @@ const intlayer = fp(fastifyIntlayer, {
|
|
|
114
105
|
* ```
|
|
115
106
|
*/
|
|
116
107
|
const t = (content, locale) => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
} catch (error) {
|
|
122
|
-
debug(error.message);
|
|
123
|
-
return getTranslation(content, locale ?? "en");
|
|
124
|
-
}
|
|
108
|
+
const context = intlayerStorage.getStore();
|
|
109
|
+
if (context) return context.t(content, locale);
|
|
110
|
+
debug("Using the import { t } from \"fastify-intlayer\" outside of a request context. Use req.intlayer.t instead.");
|
|
111
|
+
return getTranslation(content, locale ?? internationalization.defaultLocale);
|
|
125
112
|
};
|
|
113
|
+
/**
|
|
114
|
+
* Retrieves a dictionary by key, using the locale of the current request by default.
|
|
115
|
+
*/
|
|
126
116
|
const getIntlayer = (...args) => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
} catch (error) {
|
|
132
|
-
debug(error.message);
|
|
133
|
-
return getIntlayer$1(...args);
|
|
134
|
-
}
|
|
117
|
+
const context = intlayerStorage.getStore();
|
|
118
|
+
if (context) return context.getIntlayer(...args);
|
|
119
|
+
debug("Context not found. Ensure you are inside a request handling flow.");
|
|
120
|
+
return getIntlayer$1(...args);
|
|
135
121
|
};
|
|
122
|
+
/**
|
|
123
|
+
* Retrieves an imported dictionary, using the locale of the current request by default.
|
|
124
|
+
*/
|
|
136
125
|
const getDictionary = (...args) => {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
} catch (error) {
|
|
142
|
-
debug(error.message);
|
|
143
|
-
return getDictionary$1(...args);
|
|
144
|
-
}
|
|
126
|
+
const context = intlayerStorage.getStore();
|
|
127
|
+
if (context) return context.getDictionary(...args);
|
|
128
|
+
debug("Context not found. Ensure you are inside a request handling flow.");
|
|
129
|
+
return getDictionary$1(...args);
|
|
145
130
|
};
|
|
146
131
|
|
|
147
132
|
//#endregion
|
package/dist/esm/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["getIntlayerFunction","getDictionaryFunction"],"sources":["../../src/index.ts"],"sourcesContent":["import { getConfiguration } from '@intlayer/config/node';\nimport {\n getDictionary as getDictionaryFunction,\n getIntlayer as getIntlayerFunction,\n getTranslation,\n registerAmbientLocaleResolver,\n} from '@intlayer/core/interpreter';\nimport { localeDetector } from '@intlayer/core/localization';\nimport { getLocaleFromStorageServer } from '@intlayer/core/utils';\nimport { prepareIntlayerServer } from '@intlayer/engine/build';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { StrictModeLocaleMap } from '@intlayer/types/module_augmentation';\nimport { createNamespace } from 'cls-hooked';\nimport type { FastifyPluginAsync, FastifyRequest } from 'fastify';\nimport fp from 'fastify-plugin';\n\n// Module augmentation to type the request decoration\ndeclare module 'fastify' {\n interface FastifyRequest {\n intlayer: {\n locale: Locale;\n defaultLocale: Locale;\n locale_storage?: Locale;\n locale_detected?: Locale;\n t: <T extends string>(\n content: StrictModeLocaleMap<T> | string,\n locale?: Locale\n ) => T;\n getIntlayer: typeof getIntlayerFunction;\n getDictionary: typeof getDictionaryFunction;\n };\n }\n}\n\nconst appNamespace = createNamespace('app');\n\n// Lets a bare `getIntlayer` / `getDictionary` from any Intlayer package resolve\n// to the locale of the request being handled.\nregisterAmbientLocaleResolver(() => appNamespace.get('locale'));\n\n// Zero-cost fallback, will be updated with fastify logger in dev mode\nlet debug: (message: string) => void = () => {};\n\n/**\n * Fastify Plugin that integrates Intlayer into your Fastify application.\n *\n * It handles:\n * 1. Locale detection from storage (cookies, headers).\n * 2. Decorating the request object with `intlayer` data containing `t`, `getIntlayer`, and `getDictionary`.\n * 3. Setting up a `cls-hooked` namespace for programmatic access during the request lifecycle.\n *\n * @example\n * ```ts\n * import Fastify from 'fastify';\n * import { intlayer } from 'fastify-intlayer';\n *\n * const fastify = Fastify();\n * fastify.register(intlayer);\n * ```\n */\nconst fastifyIntlayer: FastifyPluginAsync = async (fastify, _opts) => {\n const configuration = getConfiguration({\n logFunctions: fastify.log, // Req not defined yet\n });\n const { internationalization } = configuration;\n\n // In dev mode, use fastify logger to debug messages\n if (process.env['NODE_ENV'] === 'development') {\n debug = (msg: string) => fastify.log.debug(msg);\n }\n\n /**\n * Retrieves the locale from storage (cookies, headers).\n * Note: req.cookies requires @fastify/cookie to be registered.\n * We cast req to any to avoid hard dependency on @fastify/cookie types.\n */\n const getStorageLocale = (req: FastifyRequest): Locale | undefined =>\n getLocaleFromStorageServer({\n getCookie: (name: string) => (req as any).cookies?.[name],\n getHeader: (name: string) => req.headers?.[name] as string | undefined,\n });\n\n prepareIntlayerServer(configuration, { label: 'fastify-intlayer' });\n\n const translateFunction =\n (req: FastifyRequest) =>\n <T extends string>(\n content: StrictModeLocaleMap<T> | string,\n locale?: Locale\n ): T => {\n // Access the decorated state from the request\n const { locale: currentLocale, defaultLocale } = req.intlayer;\n\n const targetLocale = locale ?? currentLocale;\n\n if (typeof content === 'undefined') {\n return '' as unknown as T;\n }\n\n if (typeof content === 'string') {\n return content as unknown as T;\n }\n\n if (\n typeof content?.[\n targetLocale as unknown as keyof StrictModeLocaleMap<T>\n ] === 'undefined'\n ) {\n if (\n typeof content?.[\n defaultLocale as unknown as keyof StrictModeLocaleMap<T>\n ] === 'undefined'\n ) {\n return content as unknown as T;\n } else {\n return getTranslation(content, defaultLocale);\n }\n }\n\n return getTranslation(content, targetLocale);\n };\n\n // Decorate the request object to ensure types are stable.\n // We use 'null as any' to bypass the initial type check, knowing\n // the preHandler will populate it before any route handler runs.\n if (!fastify.hasRequestDecorator('intlayer')) {\n fastify.decorateRequest('intlayer', null as any);\n }\n\n fastify.addHook('preHandler', (req, _reply, done) => {\n // Detect if locale is set by intlayer frontend lib in the headers\n const localeFromStorage = getStorageLocale(req);\n\n const negotiatorHeaders: Record<string, string> = {};\n\n // Copy all headers from the request to negotiatorHeaders\n if (req && typeof req.headers === 'object') {\n for (const key in req.headers) {\n const value = req.headers[key];\n\n if (typeof value === 'string') {\n negotiatorHeaders[key] = value;\n } else if (Array.isArray(value)) {\n // Handle array headers (unlikely for accept-language but possible in Fastify)\n negotiatorHeaders[key] = value.join(',');\n }\n }\n }\n\n const localeDetected = localeDetector(\n negotiatorHeaders,\n internationalization.locales,\n internationalization.defaultLocale\n );\n\n const locale = localeFromStorage ?? localeDetected;\n const defaultLocale = internationalization.defaultLocale;\n\n // Helper functions bound to the current request context\n const getIntlayerWrapped: typeof getIntlayerFunction = (\n key,\n localeArg,\n ...props\n ) =>\n getIntlayerFunction(\n key,\n (localeArg ?? locale) as typeof localeArg,\n ...props\n );\n\n const getDictionaryWrapped: typeof getDictionaryFunction = (\n key,\n localeArg,\n ...props\n ) =>\n getDictionaryFunction(\n key,\n (localeArg ?? locale) as typeof localeArg,\n ...props\n );\n\n // Assign data to request decoration\n req.intlayer = {\n locale_storage: localeFromStorage,\n locale_detected: localeDetected,\n locale,\n defaultLocale,\n getIntlayer: getIntlayerWrapped,\n getDictionary: getDictionaryWrapped,\n t: undefined as unknown as any, // Placeholder\n };\n\n // Now bind t using the updated req\n const t = translateFunction(req);\n req.intlayer.t = t;\n\n // Run CLS context\n appNamespace.run(() => {\n appNamespace.set('locale', locale);\n appNamespace.set('t', t);\n appNamespace.set('getIntlayer', getIntlayerWrapped);\n appNamespace.set('getDictionary', getDictionaryWrapped);\n\n done();\n });\n });\n};\n\n// Export as a Fastify Plugin (wrapped in fp to skip encapsulation)\nexport const intlayer = fp(fastifyIntlayer as any, {\n name: 'fastify-intlayer',\n fastify: '5.x',\n}) as unknown as FastifyPluginAsync;\n\n/**\n * Global translation function that retrieves content for the current locale in Fastify.\n *\n * This function utilizes CLS (Async Local Storage) and must be used within a request context\n * managed by the `intlayer` plugin.\n *\n * @param content - A map of locales to content.\n * @param locale - Optional locale override.\n * @returns The translated content.\n *\n * @example\n * ```ts\n * import { t } from 'fastify-intlayer';\n *\n * fastify.get('/', async (req, reply) => {\n * const greeting = t({\n * en: 'Hello',\n * fr: 'Bonjour',\n * });\n * return greeting;\n * });\n * ```\n */\nexport const t = <Content = string>(\n content: StrictModeLocaleMap<Content>,\n locale?: Locale\n): Content => {\n try {\n if (typeof appNamespace === 'undefined') {\n throw new Error(\n 'Intlayer is not initialized. Register the plugin `fastify.register(intlayer)`.'\n );\n }\n\n if (typeof appNamespace.get('t') !== 'function') {\n throw new Error(\n 'Using the import { t } from \"fastify-intlayer\" is not supported in your environment outside of a request context or proper setup. Use req.intlayer.t instead.'\n );\n }\n\n return appNamespace.get('t')(content, locale);\n } catch (error) {\n debug((error as Error).message);\n\n return getTranslation(content, locale ?? 'en');\n }\n};\n\nexport const getIntlayer: typeof getIntlayerFunction = (...args) => {\n try {\n if (typeof appNamespace === 'undefined') {\n throw new Error(\n 'Intlayer is not initialized. Register the plugin `fastify.register(intlayer)`.'\n );\n }\n\n if (typeof appNamespace.get('getIntlayer') !== 'function') {\n throw new Error(\n 'Context not found. Ensure you are inside a request handling flow.'\n );\n }\n\n return appNamespace.get('getIntlayer')(...args);\n } catch (error) {\n debug((error as Error).message);\n\n return getIntlayerFunction(...args);\n }\n};\n\nexport const getDictionary: typeof getDictionaryFunction = (...args) => {\n try {\n if (typeof appNamespace === 'undefined') {\n throw new Error(\n 'Intlayer is not initialized. Register the plugin `fastify.register(intlayer)`.'\n );\n }\n\n if (typeof appNamespace.get('getDictionary') !== 'function') {\n throw new Error(\n 'Context not found. Ensure you are inside a request handling flow.'\n );\n }\n\n return appNamespace.get('getDictionary')(...args);\n } catch (error) {\n debug((error as Error).message);\n\n return getDictionaryFunction(...args);\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["getIntlayerFunction","getDictionaryFunction"],"sources":["../../src/index.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport { internationalization } from '@intlayer/config/built';\nimport { getConfiguration } from '@intlayer/config/node';\nimport {\n getDictionary as getDictionaryFunction,\n getIntlayer as getIntlayerFunction,\n getTranslation,\n registerAmbientLocaleResolver,\n} from '@intlayer/core/interpreter';\nimport { localeDetector } from '@intlayer/core/localization';\nimport { getCookie, getLocaleFromStorageServer } from '@intlayer/core/utils';\nimport { prepareIntlayerServer } from '@intlayer/engine/build';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { StrictModeLocaleMap } from '@intlayer/types/module_augmentation';\nimport type { FastifyPluginAsync, FastifyRequest } from 'fastify';\nimport fp from 'fastify-plugin';\n\n/**\n * Intlayer state of the request being handled.\n */\nexport type IntlayerRequestContext = {\n /** Locale to use for this request, `locale_storage` taking precedence. */\n locale: Locale;\n /** Locale configured as fallback in `intlayer.config.ts`. */\n defaultLocale: Locale;\n /** Locale explicitly requested by the client through a cookie or a header. */\n locale_storage?: Locale;\n /** Locale negotiated from the `Accept-Language` header. */\n locale_detected?: Locale;\n /** Translates an inline locale map. */\n t: <T extends string>(\n content: StrictModeLocaleMap<T> | string,\n locale?: Locale\n ) => T;\n /** Reads a dictionary by key, defaulting to the request locale. */\n getIntlayer: typeof getIntlayerFunction;\n /** Reads an imported dictionary, defaulting to the request locale. */\n getDictionary: typeof getDictionaryFunction;\n};\n\n// Module augmentation to type the request decoration\ndeclare module 'fastify' {\n interface FastifyRequest {\n intlayer: IntlayerRequestContext;\n }\n}\n\n/**\n * Request-scoped Intlayer context, holding the `req.intlayer` object itself.\n */\nconst intlayerStorage = new AsyncLocalStorage<IntlayerRequestContext>();\n\n// Lets a bare `getIntlayer` / `getDictionary` from any Intlayer package resolve\n// to the locale of the request being handled.\nregisterAmbientLocaleResolver(() => intlayerStorage.getStore()?.locale);\n\n// Zero-cost fallback, will be updated with fastify logger in dev mode\nlet debug: (message: string) => void = () => {};\n\n/**\n * Translates `content` into `locale`, falling back to the default locale.\n */\nconst translate = <T extends string>(\n content: StrictModeLocaleMap<T> | string,\n locale: Locale\n): T =>\n typeof content === 'string'\n ? (content as T)\n : getTranslation(content, locale, internationalization.defaultLocale);\n\n/**\n * Reads a single-valued request header.\n */\nconst getHeader = (\n request: FastifyRequest,\n name: string\n): string | undefined => {\n const value = request.headers[name];\n\n return Array.isArray(value) ? value.join(',') : value;\n};\n\n/**\n * Builds the Intlayer state of a request.\n */\nconst createRequestContext = (\n request: FastifyRequest\n): IntlayerRequestContext => {\n const { locales, defaultLocale } = internationalization;\n\n // Parsed cookies need `@fastify/cookie`: read the raw header otherwise\n const parsedCookies = (\n request as FastifyRequest & { cookies?: Record<string, string | undefined> }\n ).cookies;\n\n const localeFromStorage = getLocaleFromStorageServer({\n getCookie: (name: string) =>\n parsedCookies\n ? parsedCookies[name]\n : getCookie(name, request.headers.cookie ?? ''),\n getHeader: (name: string) => getHeader(request, name),\n });\n\n const localeDetected = localeDetector(\n { 'accept-language': getHeader(request, 'accept-language') },\n locales,\n defaultLocale\n );\n\n const locale = localeFromStorage ?? localeDetected;\n\n return {\n locale_storage: localeFromStorage,\n locale_detected: localeDetected,\n locale,\n defaultLocale,\n t: (content, localeArg) => translate(content, localeArg ?? locale),\n getIntlayer: (key, localeArg, ...props) =>\n getIntlayerFunction(\n key,\n (localeArg ?? locale) as typeof localeArg,\n ...props\n ),\n getDictionary: (key, localeArg, ...props) =>\n getDictionaryFunction(\n key,\n (localeArg ?? locale) as typeof localeArg,\n ...props\n ),\n };\n};\n\n/**\n * Fastify Plugin that integrates Intlayer into your Fastify application.\n *\n * It handles:\n * 1. Locale detection from storage (cookies, headers) then from `Accept-Language`.\n * 2. Decorating the request object with `intlayer` data containing `t`, `getIntlayer`, and `getDictionary`.\n * 3. Setting up an `AsyncLocalStorage` context for programmatic access during the request lifecycle.\n *\n * The context is set in `onRequest`, so it also covers the app hooks, schema\n * validation errors and error handlers.\n *\n * @example\n * ```ts\n * import Fastify from 'fastify';\n * import { intlayer } from 'fastify-intlayer';\n *\n * const fastify = Fastify();\n * fastify.register(intlayer);\n * ```\n */\nconst fastifyIntlayer: FastifyPluginAsync = async (fastify) => {\n // In dev mode, use fastify logger to debug messages\n if (process.env['NODE_ENV'] === 'development') {\n debug = (message: string) => fastify.log.debug(message);\n }\n\n prepareIntlayerServer(getConfiguration({ logFunctions: fastify.log }), {\n label: 'fastify-intlayer',\n });\n\n // Declared upfront so every request object keeps the same shape\n if (!fastify.hasRequestDecorator('intlayer')) {\n fastify.decorateRequest(\n 'intlayer',\n null as unknown as IntlayerRequestContext\n );\n }\n\n fastify.addHook('onRequest', (request, _reply, done) => {\n const context = createRequestContext(request);\n\n request.intlayer = context;\n\n // Run the rest of the request lifecycle inside the Intlayer context\n intlayerStorage.run(context, done);\n });\n};\n\n// Export as a Fastify Plugin (wrapped in fp to skip encapsulation)\nexport const intlayer = fp(fastifyIntlayer, {\n name: 'fastify-intlayer',\n fastify: '5.x',\n});\n\n/**\n * Global translation function that retrieves content for the current locale in Fastify.\n *\n * Falls back to the configured default locale when called outside of a request\n * handled by the `intlayer` plugin.\n *\n * @param content - A map of locales to content.\n * @param locale - Optional locale override.\n * @returns The translated content.\n *\n * @example\n * ```ts\n * import { t } from 'fastify-intlayer';\n *\n * fastify.get('/', async (req, reply) => {\n * const greeting = t({\n * en: 'Hello',\n * fr: 'Bonjour',\n * });\n * return greeting;\n * });\n * ```\n */\nexport const t = <Content = string>(\n content: StrictModeLocaleMap<Content>,\n locale?: Locale\n): Content => {\n const context = intlayerStorage.getStore();\n\n if (context) {\n return context.t(content as StrictModeLocaleMap<string>, locale) as Content;\n }\n\n debug(\n 'Using the import { t } from \"fastify-intlayer\" outside of a request context. Use req.intlayer.t instead.'\n );\n\n return getTranslation(content, locale ?? internationalization.defaultLocale);\n};\n\n/**\n * Retrieves a dictionary by key, using the locale of the current request by default.\n */\nexport const getIntlayer: typeof getIntlayerFunction = (...args) => {\n const context = intlayerStorage.getStore();\n\n if (context) {\n return context.getIntlayer(...args);\n }\n\n debug('Context not found. Ensure you are inside a request handling flow.');\n\n return getIntlayerFunction(...args);\n};\n\n/**\n * Retrieves an imported dictionary, using the locale of the current request by default.\n */\nexport const getDictionary: typeof getDictionaryFunction = (...args) => {\n const context = intlayerStorage.getStore();\n\n if (context) {\n return context.getDictionary(...args);\n }\n\n debug('Context not found. Ensure you are inside a request handling flow.');\n\n return getDictionaryFunction(...args);\n};\n"],"mappings":";;;;;;;;;;;;;AAkDA,MAAM,kBAAkB,IAAI,kBAA0C;AAItE,oCAAoC,gBAAgB,SAAS,CAAC,EAAE,MAAM;AAGtE,IAAI,cAAyC,CAAC;;;;AAK9C,MAAM,aACJ,SACA,WAEA,OAAO,YAAY,WACd,UACD,eAAe,SAAS,QAAQ,qBAAqB,aAAa;;;;AAKxE,MAAM,aACJ,SACA,SACuB;CACvB,MAAM,QAAQ,QAAQ,QAAQ;CAE9B,OAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,GAAG,IAAI;AAClD;;;;AAKA,MAAM,wBACJ,YAC2B;CAC3B,MAAM,EAAE,SAAS,kBAAkB;CAGnC,MAAM,gBACJ,QACA;CAEF,MAAM,oBAAoB,2BAA2B;EACnD,YAAY,SACV,gBACI,cAAc,QACd,UAAU,MAAM,QAAQ,QAAQ,UAAU,EAAE;EAClD,YAAY,SAAiB,UAAU,SAAS,IAAI;CACtD,CAAC;CAED,MAAM,iBAAiB,eACrB,EAAE,mBAAmB,UAAU,SAAS,iBAAiB,EAAE,GAC3D,SACA,aACF;CAEA,MAAM,SAAS,qBAAqB;CAEpC,OAAO;EACL,gBAAgB;EAChB,iBAAiB;EACjB;EACA;EACA,IAAI,SAAS,cAAc,UAAU,SAAS,aAAa,MAAM;EACjE,cAAc,KAAK,WAAW,GAAG,UAC/BA,cACE,KACC,aAAa,QACd,GAAG,KACL;EACF,gBAAgB,KAAK,WAAW,GAAG,UACjCC,gBACE,KACC,aAAa,QACd,GAAG,KACL;CACJ;AACF;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAM,kBAAsC,OAAO,YAAY;CAE7D,6BAAgC,eAC9B,SAAS,YAAoB,QAAQ,IAAI,MAAM,OAAO;CAGxD,sBAAsB,iBAAiB,EAAE,cAAc,QAAQ,IAAI,CAAC,GAAG,EACrE,OAAO,mBACT,CAAC;CAGD,IAAI,CAAC,QAAQ,oBAAoB,UAAU,GACzC,QAAQ,gBACN,YACA,IACF;CAGF,QAAQ,QAAQ,cAAc,SAAS,QAAQ,SAAS;EACtD,MAAM,UAAU,qBAAqB,OAAO;EAE5C,QAAQ,WAAW;EAGnB,gBAAgB,IAAI,SAAS,IAAI;CACnC,CAAC;AACH;AAGA,MAAa,WAAW,GAAG,iBAAiB;CAC1C,MAAM;CACN,SAAS;AACX,CAAC;;;;;;;;;;;;;;;;;;;;;;;;AAyBD,MAAa,KACX,SACA,WACY;CACZ,MAAM,UAAU,gBAAgB,SAAS;CAEzC,IAAI,SACF,OAAO,QAAQ,EAAE,SAAwC,MAAM;CAGjE,MACE,4GACF;CAEA,OAAO,eAAe,SAAS,UAAU,qBAAqB,aAAa;AAC7E;;;;AAKA,MAAa,eAA2C,GAAG,SAAS;CAClE,MAAM,UAAU,gBAAgB,SAAS;CAEzC,IAAI,SACF,OAAO,QAAQ,YAAY,GAAG,IAAI;CAGpC,MAAM,mEAAmE;CAEzE,OAAOD,cAAoB,GAAG,IAAI;AACpC;;;;AAKA,MAAa,iBAA+C,GAAG,SAAS;CACtE,MAAM,UAAU,gBAAgB,SAAS;CAEzC,IAAI,SACF,OAAO,QAAQ,cAAc,GAAG,IAAI;CAGtC,MAAM,mEAAmE;CAEzE,OAAOC,gBAAsB,GAAG,IAAI;AACtC"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -3,25 +3,36 @@ import { Locale } from "@intlayer/types/allLocales";
|
|
|
3
3
|
import { StrictModeLocaleMap } from "@intlayer/types/module_augmentation";
|
|
4
4
|
import { FastifyPluginAsync } from "fastify";
|
|
5
5
|
//#region src/index.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Intlayer state of the request being handled.
|
|
8
|
+
*/
|
|
9
|
+
export type IntlayerRequestContext = {
|
|
10
|
+
/** Locale to use for this request, `locale_storage` taking precedence. */
|
|
11
|
+
locale: Locale;
|
|
12
|
+
/** Locale configured as fallback in `intlayer.config.ts`. */
|
|
13
|
+
defaultLocale: Locale;
|
|
14
|
+
/** Locale explicitly requested by the client through a cookie or a header. */
|
|
15
|
+
locale_storage?: Locale;
|
|
16
|
+
/** Locale negotiated from the `Accept-Language` header. */
|
|
17
|
+
locale_detected?: Locale;
|
|
18
|
+
/** Translates an inline locale map. */
|
|
19
|
+
t: <T extends string>(content: StrictModeLocaleMap<T> | string, locale?: Locale) => T;
|
|
20
|
+
/** Reads a dictionary by key, defaulting to the request locale. */
|
|
21
|
+
getIntlayer: typeof getIntlayer$1;
|
|
22
|
+
/** Reads an imported dictionary, defaulting to the request locale. */
|
|
23
|
+
getDictionary: typeof getDictionary$1;
|
|
24
|
+
};
|
|
6
25
|
declare module 'fastify' {
|
|
7
26
|
interface FastifyRequest {
|
|
8
|
-
intlayer:
|
|
9
|
-
locale: Locale;
|
|
10
|
-
defaultLocale: Locale;
|
|
11
|
-
locale_storage?: Locale;
|
|
12
|
-
locale_detected?: Locale;
|
|
13
|
-
t: <T extends string>(content: StrictModeLocaleMap<T> | string, locale?: Locale) => T;
|
|
14
|
-
getIntlayer: typeof getIntlayer$1;
|
|
15
|
-
getDictionary: typeof getDictionary$1;
|
|
16
|
-
};
|
|
27
|
+
intlayer: IntlayerRequestContext;
|
|
17
28
|
}
|
|
18
29
|
}
|
|
19
30
|
export declare const intlayer: FastifyPluginAsync;
|
|
20
31
|
/**
|
|
21
32
|
* Global translation function that retrieves content for the current locale in Fastify.
|
|
22
33
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
34
|
+
* Falls back to the configured default locale when called outside of a request
|
|
35
|
+
* handled by the `intlayer` plugin.
|
|
25
36
|
*
|
|
26
37
|
* @param content - A map of locales to content.
|
|
27
38
|
* @param locale - Optional locale override.
|
|
@@ -41,7 +52,13 @@ export declare const intlayer: FastifyPluginAsync;
|
|
|
41
52
|
* ```
|
|
42
53
|
*/
|
|
43
54
|
export declare const t: <Content = string>(content: StrictModeLocaleMap<Content>, locale?: Locale) => Content;
|
|
55
|
+
/**
|
|
56
|
+
* Retrieves a dictionary by key, using the locale of the current request by default.
|
|
57
|
+
*/
|
|
44
58
|
export declare const getIntlayer: typeof getIntlayer$1;
|
|
59
|
+
/**
|
|
60
|
+
* Retrieves an imported dictionary, using the locale of the current request by default.
|
|
61
|
+
*/
|
|
45
62
|
export declare const getDictionary: typeof getDictionary$1;
|
|
46
63
|
//#endregion
|
|
47
64
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/index.ts"],"mappings":";;;;;;;;YAoBY;;EAEV,QAAQ;;EAER,eAAe;;EAEf,iBAAiB;;EAEjB,kBAAkB;;EAElB,IAAI,kBACF,SAAS,oBAAoB,aAC7B,SAAS,WACN;;EAEL,oBAAoB;;EAEpB,sBAAsB;;;YAKZ;IACR,UAAU;;;qBA0ID,UAAQ;;;;;;;;;;;;;;;;;;;;;;;;qBA4BR,IAAK,kBAAgB,SACvB,oBAAoB,UAAQ,SAC5B,WACR;;;;qBAiBU,oBAAoB;;;;qBAepB,sBAAsB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify-intlayer",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.7.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Manage internationalization i18n in a simple way for Fastify applications.",
|
|
6
6
|
"keywords": [
|
|
@@ -76,16 +76,14 @@
|
|
|
76
76
|
"typecheck": "tsc --noEmit --project tsconfig.types.json"
|
|
77
77
|
},
|
|
78
78
|
"dependencies": {
|
|
79
|
-
"@intlayer/config": "9.
|
|
80
|
-
"@intlayer/core": "9.
|
|
81
|
-
"@intlayer/engine": "9.
|
|
82
|
-
"@intlayer/types": "9.
|
|
83
|
-
"cls-hooked": "4.2.2",
|
|
79
|
+
"@intlayer/config": "9.7.0",
|
|
80
|
+
"@intlayer/core": "9.7.0",
|
|
81
|
+
"@intlayer/engine": "9.7.0",
|
|
82
|
+
"@intlayer/types": "9.7.0",
|
|
84
83
|
"fastify-plugin": "6.0.0"
|
|
85
84
|
},
|
|
86
85
|
"devDependencies": {
|
|
87
|
-
"@types/
|
|
88
|
-
"@types/node": "^22",
|
|
86
|
+
"@types/node": "^26",
|
|
89
87
|
"@utils/ts-config": "1.0.4",
|
|
90
88
|
"@utils/ts-config-types": "1.0.4",
|
|
91
89
|
"@utils/tsdown-config": "1.0.4",
|
|
@@ -93,7 +91,7 @@
|
|
|
93
91
|
"rimraf": "6.1.3",
|
|
94
92
|
"tsdown": "0.23.0",
|
|
95
93
|
"typescript": "7.0.2",
|
|
96
|
-
"vitest": "5.0.
|
|
94
|
+
"vitest": "5.0.3"
|
|
97
95
|
},
|
|
98
96
|
"engines": {
|
|
99
97
|
"node": ">=14.18"
|