@intlayer/config 9.4.0-canary.0 → 9.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -214,6 +214,7 @@ Explore our comprehensive documentation to get started with Intlayer and learn h
214
214
  <li><a href="https://intlayer.org/doc/environment/vite-and-vue" rel=''>Vite + Vue</a></li>
215
215
  <li><a href="https://intlayer.org/doc/environment/nuxt-and-vue" rel=''>Vite + Nuxt</a></li>
216
216
  <li><a href="https://intlayer.org/doc/environment/vite-and-solid" rel=''>Vite + Solid</a></li>
217
+ <li><a href="https://intlayer.org/doc/environment/solid-start" rel=''>Solid Start</a></li>
217
218
  <li><a href="https://intlayer.org/doc/environment/angular" rel=''>Angular</a></li>
218
219
  <li>
219
220
  <a href="https://intlayer.org/doc/environment/express" rel=''>Backend</a>
@@ -223,7 +224,9 @@ Explore our comprehensive documentation to get started with Intlayer and learn h
223
224
  <li><a href="https://intlayer.org/doc/environment/fastify" rel=''>Fastify</a></li>
224
225
  <li><a href="https://intlayer.org/doc/environment/adonisjs" rel=''>AdonisJS</a></li>
225
226
  <li><a href="https://intlayer.org/doc/environment/hono" rel=''>Hono</a></li>
227
+ <li><a href="https://intlayer.org/doc/environment/elysia" rel=''>Elysia</a></li>
226
228
  </ul>
229
+
227
230
  </li>
228
231
  </ul>
229
232
  </details>
@@ -2,8 +2,8 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
3
3
  const require_utils_alias = require('../utils/alias.cjs');
4
4
  const require_configFile_getConfiguration = require('../configFile/getConfiguration.cjs');
5
- const require_bundle_logBundle = require('./logBundle.cjs');
6
5
  const require_envVars_envVars = require('../envVars/envVars.cjs');
6
+ const require_bundle_logBundle = require('./logBundle.cjs');
7
7
  let node_path = require("node:path");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  let _intlayer_types_package_json = require("@intlayer/types/package.json");
@@ -7,34 +7,47 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
7
7
  * through member access (`content.title`) or destructuring
8
8
  * (`const { title } = useIntlayer('home')`).
9
9
  */
10
- const BASE_CALLERS = [{
11
- callerName: "useIntlayer",
12
- library: "intlayer",
13
- importSources: [
14
- "intlayer",
15
- "react-intlayer",
16
- "next-intlayer",
17
- "vue-intlayer",
18
- "svelte-intlayer",
19
- "solid-intlayer",
20
- "preact-intlayer",
21
- "angular-intlayer"
22
- ],
23
- namespaceSources: [{
24
- from: "argument",
25
- index: 0
26
- }],
27
- translationFunction: "content"
28
- }, {
29
- callerName: "getIntlayer",
30
- library: "intlayer",
31
- importSources: ["intlayer", "@intlayer/core"],
32
- namespaceSources: [{
33
- from: "argument",
34
- index: 0
35
- }],
36
- translationFunction: "content"
37
- }];
10
+ const BASE_CALLERS = [
11
+ {
12
+ callerName: "useIntlayer",
13
+ library: "intlayer",
14
+ importSources: [
15
+ "intlayer",
16
+ "react-intlayer",
17
+ "next-intlayer",
18
+ "vue-intlayer",
19
+ "svelte-intlayer",
20
+ "solid-intlayer",
21
+ "preact-intlayer",
22
+ "angular-intlayer"
23
+ ],
24
+ namespaceSources: [{
25
+ from: "argument",
26
+ index: 0
27
+ }],
28
+ translationFunction: "content"
29
+ },
30
+ {
31
+ callerName: "getIntlayer",
32
+ library: "intlayer",
33
+ importSources: ["intlayer", "@intlayer/core"],
34
+ namespaceSources: [{
35
+ from: "argument",
36
+ index: 0
37
+ }],
38
+ translationFunction: "content"
39
+ },
40
+ {
41
+ callerName: "getIntlayerAsync",
42
+ library: "intlayer",
43
+ importSources: ["intlayer", "@intlayer/core"],
44
+ namespaceSources: [{
45
+ from: "argument",
46
+ index: 0
47
+ }],
48
+ translationFunction: "content"
49
+ }
50
+ ];
38
51
 
39
52
  //#endregion
40
53
  exports.BASE_CALLERS = BASE_CALLERS;
@@ -1 +1 @@
1
- {"version":3,"file":"base.cjs","names":[],"sources":["../../../src/callers/base.ts"],"sourcesContent":["import type { CallerDescriptor } from './types';\n\n/**\n * Base intlayer getters — always active, independent of any compat library.\n *\n * Their result is the dictionary content object itself: fields are consumed\n * through member access (`content.title`) or destructuring\n * (`const { title } = useIntlayer('home')`).\n */\nexport const BASE_CALLERS: CallerDescriptor[] = [\n {\n callerName: 'useIntlayer',\n library: 'intlayer',\n importSources: [\n 'intlayer',\n 'react-intlayer',\n 'next-intlayer',\n 'vue-intlayer',\n 'svelte-intlayer',\n 'solid-intlayer',\n 'preact-intlayer',\n 'angular-intlayer',\n ],\n namespaceSources: [{ from: 'argument', index: 0 }],\n translationFunction: 'content',\n },\n {\n callerName: 'getIntlayer',\n library: 'intlayer',\n importSources: ['intlayer', '@intlayer/core'],\n namespaceSources: [{ from: 'argument', index: 0 }],\n translationFunction: 'content',\n },\n];\n"],"mappings":";;;;;;;;;AASA,MAAa,eAAmC,CAC9C;CACE,YAAY;CACZ,SAAS;CACT,eAAe;EACb;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CACA,kBAAkB,CAAC;EAAE,MAAM;EAAY,OAAO;CAAE,CAAC;CACjD,qBAAqB;AACvB,GACA;CACE,YAAY;CACZ,SAAS;CACT,eAAe,CAAC,YAAY,gBAAgB;CAC5C,kBAAkB,CAAC;EAAE,MAAM;EAAY,OAAO;CAAE,CAAC;CACjD,qBAAqB;AACvB,CACF"}
1
+ {"version":3,"file":"base.cjs","names":[],"sources":["../../../src/callers/base.ts"],"sourcesContent":["import type { CallerDescriptor } from './types';\n\n/**\n * Base intlayer getters — always active, independent of any compat library.\n *\n * Their result is the dictionary content object itself: fields are consumed\n * through member access (`content.title`) or destructuring\n * (`const { title } = useIntlayer('home')`).\n */\nexport const BASE_CALLERS: CallerDescriptor[] = [\n {\n callerName: 'useIntlayer',\n library: 'intlayer',\n importSources: [\n 'intlayer',\n 'react-intlayer',\n 'next-intlayer',\n 'vue-intlayer',\n 'svelte-intlayer',\n 'solid-intlayer',\n 'preact-intlayer',\n 'angular-intlayer',\n ],\n namespaceSources: [{ from: 'argument', index: 0 }],\n translationFunction: 'content',\n },\n {\n callerName: 'getIntlayer',\n library: 'intlayer',\n importSources: ['intlayer', '@intlayer/core'],\n namespaceSources: [{ from: 'argument', index: 0 }],\n translationFunction: 'content',\n },\n {\n // Async counterpart of `getIntlayer`: same content shape, reached through\n // an `await`.\n callerName: 'getIntlayerAsync',\n library: 'intlayer',\n importSources: ['intlayer', '@intlayer/core'],\n namespaceSources: [{ from: 'argument', index: 0 }],\n translationFunction: 'content',\n },\n];\n"],"mappings":";;;;;;;;;AASA,MAAa,eAAmC;CAC9C;EACE,YAAY;EACZ,SAAS;EACT,eAAe;GACb;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC;GAAE,MAAM;GAAY,OAAO;EAAE,CAAC;EACjD,qBAAqB;CACvB;CACA;EACE,YAAY;EACZ,SAAS;EACT,eAAe,CAAC,YAAY,gBAAgB;EAC5C,kBAAkB,CAAC;GAAE,MAAM;GAAY,OAAO;EAAE,CAAC;EACjD,qBAAqB;CACvB;CACA;EAGE,YAAY;EACZ,SAAS;EACT,eAAe,CAAC,YAAY,gBAAgB;EAC5C,kBAAkB,CAAC;GAAE,MAAM;GAAY,OAAO;EAAE,CAAC;EACjD,qBAAqB;CACvB;AACF"}
@@ -355,7 +355,8 @@ const buildLogFields = (customConfiguration, logFunctions) => ({
355
355
  *
356
356
  * Applies defaults for every field and strips all server-only or sensitive
357
357
  * information (`system`, `content`, `build`, `compiler`, `dictionary`, `ai`,
358
- * `editor.clientId`, `editor.clientSecret`).
358
+ * `editor.clientSecret`). The public `editor.clientId` is kept — see
359
+ * {@link BrowserIntlayerConfig}.
359
360
  *
360
361
  * This is the browser counterpart of `buildConfigurationFields`. It is safe
361
362
  * to call in browser environments because it has no Node.js dependencies.
@@ -375,7 +376,7 @@ const buildLogFields = (customConfiguration, logFunctions) => ({
375
376
  const buildBrowserConfiguration = (customConfig) => {
376
377
  const { locales, defaultLocale } = buildInternationalizationFields(customConfig?.internationalization);
377
378
  const routing = buildRoutingFields(customConfig?.routing);
378
- const { clientId: _clientId, clientSecret: _clientSecret, ...editorPublic } = buildEditorFields(customConfig?.editor);
379
+ const { clientSecret: _clientSecret, ...editorPublic } = buildEditorFields(customConfig?.editor);
379
380
  const analytics = buildAnalyticsFields(customConfig?.analytics);
380
381
  const { mode, prefix } = buildLogFields(customConfig?.log);
381
382
  return {
@@ -403,36 +404,28 @@ const buildBrowserConfiguration = (customConfig) => {
403
404
  * @param config - A fully-built server-side Intlayer configuration.
404
405
  * @returns The browser-safe subset of that configuration.
405
406
  */
406
- const extractBrowserConfiguration = (config) => ({
407
- internationalization: {
408
- locales: config.internationalization.locales,
409
- defaultLocale: config.internationalization.defaultLocale
410
- },
411
- routing: {
412
- mode: config.routing.mode,
413
- enableProxy: config.routing.enableProxy,
414
- storage: config.routing.storage,
415
- basePath: config.routing.basePath,
416
- rewrite: config.routing.rewrite
417
- },
418
- editor: {
419
- applicationURL: config.editor.applicationURL,
420
- editorURL: config.editor.editorURL,
421
- cmsURL: config.editor.cmsURL,
422
- backendURL: config.editor.backendURL,
423
- port: config.editor.port,
424
- enabled: config.editor.enabled,
425
- dictionaryPriorityStrategy: config.editor.dictionaryPriorityStrategy,
426
- liveSync: config.editor.liveSync,
427
- liveSyncPort: config.editor.liveSyncPort,
428
- liveSyncURL: config.editor.liveSyncURL
429
- },
430
- analytics: buildAnalyticsFields(config.analytics),
431
- log: {
432
- mode: config.log.mode,
433
- prefix: config.log.prefix
434
- }
435
- });
407
+ const extractBrowserConfiguration = (config) => {
408
+ const { clientSecret: _clientSecret, ...editorPublic } = config.editor;
409
+ return {
410
+ internationalization: {
411
+ locales: config.internationalization.locales,
412
+ defaultLocale: config.internationalization.defaultLocale
413
+ },
414
+ routing: {
415
+ mode: config.routing.mode,
416
+ enableProxy: config.routing.enableProxy,
417
+ storage: config.routing.storage,
418
+ basePath: config.routing.basePath,
419
+ rewrite: config.routing.rewrite
420
+ },
421
+ editor: editorPublic,
422
+ analytics: buildAnalyticsFields(config.analytics),
423
+ log: {
424
+ mode: config.log.mode,
425
+ prefix: config.log.prefix
426
+ }
427
+ };
428
+ };
436
429
 
437
430
  //#endregion
438
431
  exports.buildAnalyticsFields = buildAnalyticsFields;
@@ -1 +1 @@
1
- {"version":3,"file":"buildBrowserConfiguration.cjs","names":["LOCALES","REQUIRED_LOCALES","STORAGE","getStorageAttributes","PREFIX"],"sources":["../../../src/configFile/buildBrowserConfiguration.ts"],"sourcesContent":["import type {\n AnalyticsConfig,\n CustomIntlayerConfig,\n CustomRoutingConfig,\n EditorConfig,\n InternationalizationConfig,\n IntlayerConfig,\n LogConfig,\n LogFunctions,\n RoutingConfig,\n} from '@intlayer/types/config';\nimport {\n ANALYTICS_ENABLED,\n ANALYTICS_FLUSH_INTERVAL,\n ANALYTICS_SAMPLE_RATE,\n} from '../defaultValues/analytics';\nimport {\n APPLICATION_URL,\n BACKEND_URL,\n CMS_URL,\n DICTIONARY_PRIORITY_STRATEGY,\n EDITOR_URL,\n IS_ENABLED,\n LIVE_SYNC,\n LIVE_SYNC_PORT,\n PORT,\n} from '../defaultValues/editor';\nimport {\n DEFAULT_LOCALE,\n LOCALES,\n REQUIRED_LOCALES,\n STRICT_MODE,\n} from '../defaultValues/internationalization';\nimport { MODE, PREFIX } from '../defaultValues/log';\nimport {\n BASE_PATH,\n ENABLE_PROXY,\n ROUTING_MODE,\n STORAGE,\n} from '../defaultValues/routing';\nimport { getStorageAttributes } from '../utils/getStorageAttributes';\n\n// ---------------------------------------------------------------------------\n// Type\n// ---------------------------------------------------------------------------\n\n/**\n * Browser-safe subset of {@link IntlayerConfig}.\n *\n * Excludes server-only fields (`system`, `content`, `build`, `compiler`,\n * `dictionary`, `ai`) and sensitive editor credentials (`clientId`,\n * `clientSecret`) that must never be shipped to the browser.\n */\nexport type BrowserIntlayerConfig = {\n internationalization: Pick<\n InternationalizationConfig,\n 'locales' | 'defaultLocale'\n >;\n routing: RoutingConfig;\n editor: Omit<EditorConfig, 'clientId' | 'clientSecret'>;\n analytics: AnalyticsConfig;\n log: Pick<LogConfig, 'mode' | 'prefix'>;\n};\n\ndeclare global {\n interface Window {\n /** Browser-safe Intlayer configuration injected by a build plugin or `installIntlayer`. */\n INTLAYER_CONFIG?: BrowserIntlayerConfig;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared field builders (browser-safe — no Node.js APIs)\n//\n// These functions are re-used by both `buildBrowserConfiguration` (browser)\n// and `buildConfigurationFields` (server) to avoid duplication.\n// ---------------------------------------------------------------------------\n\n/**\n * Build the internationalization section of the Intlayer configuration.\n *\n * @param customConfiguration - Partial user-supplied internationalization config.\n * @returns A fully-defaulted {@link InternationalizationConfig}.\n */\nexport const buildInternationalizationFields = (\n customConfiguration?: Partial<InternationalizationConfig>\n): InternationalizationConfig => ({\n /**\n * Locales available in the application\n *\n * Default: ['en']\n *\n */\n locales: customConfiguration?.locales ?? LOCALES,\n\n /**\n * Locales required by TypeScript to ensure strong implementations of internationalized content using typescript.\n *\n * Default: []\n *\n * If empty, all locales are required in `strict` mode.\n *\n * Ensure required locales are also defined in the `locales` field.\n */\n requiredLocales:\n customConfiguration?.requiredLocales ??\n customConfiguration?.locales ??\n REQUIRED_LOCALES,\n\n /**\n * Ensure strong implementations of internationalized content using typescript.\n * - If set to \"strict\", the translation `t` function will require each declared locales to be defined. If one locale is missing, or if a locale is not declared in your config, it will throw an error.\n * - If set to \"inclusive\", the translation `t` function will require each declared locales to be defined. If one locale is missing, it will throw a warning. But will accept if a locale is not declared in your config, but exist.\n * - If set to \"loose\", the translation `t` function will accept any existing locale.\n *\n * Default: \"inclusive\"\n */\n strictMode: customConfiguration?.strictMode ?? STRICT_MODE,\n\n /**\n * Default locale of the application for fallback\n *\n * Default: 'en'\n */\n defaultLocale: customConfiguration?.defaultLocale ?? DEFAULT_LOCALE,\n});\n\n/**\n * Build the routing section of the Intlayer configuration.\n *\n * @param customConfiguration - Partial user-supplied routing config.\n * @returns A fully-defaulted {@link RoutingConfig}.\n */\nexport const buildRoutingFields = (\n customConfiguration?: Partial<CustomRoutingConfig>\n): RoutingConfig => {\n const storage = customConfiguration?.storage ?? STORAGE;\n\n return {\n /**\n * URL routing mode for locale handling\n *\n * Controls how locales are represented in application URLs:\n * - 'prefix-no-default': Prefix all locales except the default locale (default)\n * - en → /dashboard\n * - fr → /fr/dashboard\n *\n * - 'prefix-all': Prefix all locales including the default locale\n * - en → /en/dashboard\n * - fr → /fr/dashboard\n *\n * - 'search-params': Use search parameters for locale handling\n * - en → /dashboard?locale=en\n * - fr → /fr/dashboard?locale=fr\n *\n * - 'no-prefix': No locale prefixing in URLs\n * - en → /dashboard\n * - fr → /dashboard\n *\n * Default: 'prefix-no-default'\n */\n mode: customConfiguration?.mode ?? ROUTING_MODE,\n\n /**\n * Enables the Intlayer locale-routing proxy (middleware).\n *\n * When enabled, the build-tool integration (e.g. the `intlayer()` Vite plugin)\n * automatically wires the locale-detection / redirect / rewrite middleware in\n * development, preview, and production SSR.\n *\n * - `undefined` (auto, default): the proxy runs, but development and preview\n * servers ignore the stored locale (cookie / header) as a redirect source.\n * - `true`: full proxy behaviour in every environment.\n * - `false`: no locale routing; the proxy is not registered.\n *\n * Default: undefined (auto)\n */\n enableProxy: customConfiguration?.enableProxy ?? ENABLE_PROXY,\n\n /**\n * Configuration for storing the locale in the client (localStorage or sessionStorage)\n *\n * If false, the locale will not be stored by the middleware.\n * If true, the locale storage will consider all default values. (cookie and header)\n *\n * Default: ['cookie', 'header']\n *\n */\n storage: getStorageAttributes(storage),\n\n /**\n * Base path of the application URL\n *\n * Default: ''\n *\n * Example:\n * - If the application is hosted at https://example.com/my-app\n * - The base path is '/my-app'\n * - The URL will be https://example.com/my-app/en\n * - If the base path is not set, the URL will be https://example.com/en\n */\n basePath: customConfiguration?.basePath ?? BASE_PATH,\n\n /**\n * Custom URL rewriting rules that override the default routing mode for specific paths.\n * Allows you to define locale-specific paths that differ from the standard routing behavior.\n * Supports dynamic route parameters using `[param]` syntax.\n *\n * Default: undefined\n *\n * Example:\n * ```typescript\n * rewrite: {\n * \"/about\": {\n * en: \"/about\",\n * fr: \"/a-propos\",\n * },\n * \"/product/[slug]\": {\n * en: \"/product/[slug]\",\n * fr: \"/produit/[slug]\",\n * },\n * }\n * ```\n *\n * Note:\n * - The rewrite rules take precedence over the default `mode` behavior.\n * - If a path matches a rewrite rule, the localized path from the rewrite configuration will be used.\n * - Dynamic route parameters are supported using bracket notation (e.g., `[slug]`, `[id]`).\n * - Works with both Next.js and Vite applications.\n */\n rewrite: customConfiguration?.rewrite,\n\n /**\n * Maps locales to specific domain hostnames for domain-based routing.\n *\n * Default: undefined\n */\n domains: customConfiguration?.domains,\n };\n};\n\n/**\n * Build the editor section of the Intlayer configuration.\n *\n * Returns the **full** {@link EditorConfig} including sensitive fields\n * (`clientId`, `clientSecret`). The browser-safe {@link BrowserIntlayerConfig}\n * omits those fields when exposing config to the client.\n *\n * `clientId` / `clientSecret` are never inferred from the environment: the\n * configuration file is the single source of truth. A project that wants to\n * authenticate from its `.env` file must wire the variables explicitly, e.g.\n * `clientId: process.env.INTLAYER_CLIENT_ID`. Commenting those lines out is\n * therefore enough to disable every credentialed behaviour (remote dictionary\n * fetching, CMS access) even when the variables remain defined in the\n * environment.\n *\n * @param customConfiguration - Partial user-supplied editor config.\n * @returns A fully-defaulted {@link EditorConfig}.\n */\nexport const buildEditorFields = (\n customConfiguration?: Partial<EditorConfig>\n): EditorConfig => {\n const liveSyncPort = customConfiguration?.liveSyncPort ?? LIVE_SYNC_PORT;\n return {\n /**\n * URL of the application. Used to restrict the origin of the editor for security reasons.\n *\n * > '*' means that the editor is accessible from any origin\n *\n * Default: '*'\n */\n applicationURL: customConfiguration?.applicationURL || APPLICATION_URL,\n\n /**\n * URL of the editor server. Used to restrict the origin of the editor for security reasons.\n *\n * > '*' means that the editor is accessible from any origin\n *\n * Default: '*'\n */\n editorURL: customConfiguration?.editorURL || EDITOR_URL,\n\n /**\n * URL of the CMS server. Used to restrict the origin of the editor for security reasons.\n */\n cmsURL: customConfiguration?.cmsURL || CMS_URL,\n\n /**\n * URL of the editor server\n *\n * Default: 'https://back.intlayer.org'\n */\n backendURL: customConfiguration?.backendURL || BACKEND_URL,\n\n /** Port of the editor server\n *\n * Default: 8000\n */\n port: customConfiguration?.port ?? PORT,\n\n /**\n * Indicates if the application interact with the visual editor\n *\n * Default: false;\n *\n * If true, the editor will be able to interact with the application.\n * If false, the editor will not be able to interact with the application.\n * In any case, the editor can only be enabled by the visual editor.\n * Disabling the editor for specific environments is a way to enforce the security.\n *\n * Usage:\n * ```js\n * {\n * // Other configurations\n * editor: {\n * enabled: process.env.NODE_ENV !== 'production',\n * }\n * };\n * ```\n */\n enabled: customConfiguration?.enabled ?? IS_ENABLED,\n\n /**\n * clientId and clientSecret allow the intlayer packages to authenticate with the backend using oAuth2 authentication.\n * An access token is use to authenticate the user related to the project.\n * To get an access token, go to https://app.intlayer.org/project and create an account.\n *\n * Default: undefined\n *\n * > Important: The clientId and clientSecret should be kept secret and not shared publicly. Please ensure to keep them in a secure location, such as environment variables.\n */\n clientId: customConfiguration?.clientId,\n\n /**\n * clientId and clientSecret allow the intlayer packages to authenticate with the backend using oAuth2 authentication.\n * An access token is use to authenticate the user related to the project.\n * To get an access token, go to https://app.intlayer.org/project and create an account.\n *\n * Default: undefined\n *\n * > Important: The clientId and clientSecret should be kept secret and not shared publicly. Please ensure to keep them in a secure location, such as environment variables.\n */\n clientSecret: customConfiguration?.clientSecret,\n\n /**\n * Strategy for prioritizing dictionaries. If a dictionary is both present online and locally, the content will be merge.\n * However, is a field is defined in both dictionary, this setting determines which fields takes the priority over the other.\n *\n * Default: 'local_first'\n *\n * The strategy for prioritizing dictionaries. It can be either 'local_first' or 'distant_first'.\n * - 'local_first': The first dictionary found in the locale is used.\n * - 'distant_first': The first dictionary found in the distant locales is used.\n */\n dictionaryPriorityStrategy:\n customConfiguration?.dictionaryPriorityStrategy ??\n DICTIONARY_PRIORITY_STRATEGY,\n\n /**\n * Indicates if the application should hot reload the locale configurations when a change is detected.\n * For example, when a new dictionary is added or updated, the application will update the content tu display in the page.\n *\n * The hot reload is only available for clients of the `enterprise` plan.\n *\n * Default: false\n */\n liveSync: customConfiguration?.liveSync ?? LIVE_SYNC,\n\n /**\n * Port of the live sync server\n *\n * Default: 4000\n */\n liveSyncPort,\n\n /**\n * URL of the live sync server in case of remote live sync server\n *\n * Default: `http://localhost:${LIVE_SYNC_PORT}`\n */\n liveSyncURL:\n customConfiguration?.liveSyncURL ?? `http://localhost:${liveSyncPort}`,\n };\n};\n\n/**\n * Build the analytics section of the Intlayer configuration.\n *\n * Analytics is opt-out: `enabled` defaults to `true`, so installing the\n * optional `@intlayer/analytics` package is enough to turn it on. The runtime\n * additionally requires a project key (`editor.clientId`) for attribution\n * before collecting anything.\n *\n * An explicit `enabled` value always wins, so a project whose package manager\n * hides the package from the project root can still opt back in.\n *\n * @param customConfiguration - Partial user-supplied analytics config.\n * @param isPackageInstalled - Whether `@intlayer/analytics` is installed in the\n * project. Browser callers cannot resolve packages and pass `true`, letting\n * the runtime dynamic `import()` fail silently when it is absent.\n * @returns A fully-defaulted {@link AnalyticsConfig}.\n */\nexport const buildAnalyticsFields = (\n customConfiguration?: Partial<AnalyticsConfig>,\n isPackageInstalled = true\n): AnalyticsConfig => ({\n /**\n * Enables analytics collection (page views, content exposures, A/B events).\n *\n * Default: true, as soon as `@intlayer/analytics` is installed.\n */\n enabled:\n customConfiguration?.enabled ?? (ANALYTICS_ENABLED && isPackageInstalled),\n\n /**\n * Milliseconds between automatic batched flushes to the backend.\n *\n * Default: 20000\n */\n flushInterval: customConfiguration?.flushInterval ?? ANALYTICS_FLUSH_INTERVAL,\n\n /**\n * Fraction of sessions to record, from 0 (none) to 1 (all).\n *\n * Default: 1\n */\n sampleRate: customConfiguration?.sampleRate ?? ANALYTICS_SAMPLE_RATE,\n});\n\n/**\n * Build the log section of the Intlayer configuration.\n *\n * @param customConfiguration - Partial user-supplied log config.\n * @param logFunctions - Optional custom log function overrides (server-only).\n * @returns A fully-defaulted {@link LogConfig}.\n */\nexport const buildLogFields = (\n customConfiguration?: Partial<LogConfig>,\n logFunctions?: LogFunctions\n): LogConfig => ({\n /**\n * Indicates if the logger is enabled\n *\n * Default: 'prefix-no-default'\n *\n * If 'default', the logger is enabled and can be used.\n * If 'verbose', the logger will be enabled and can be used, but will log more information.\n * If 'disabled', the logger is disabled and cannot be used.\n */\n mode: customConfiguration?.mode ?? MODE,\n\n /**\n * Prefix of the logger\n *\n * Default: '[intlayer]'\n *\n * The prefix of the logger.\n */\n prefix: customConfiguration?.prefix ?? PREFIX,\n\n /**\n * Functions to log\n */\n error: logFunctions?.error,\n log: logFunctions?.log,\n info: logFunctions?.info,\n warn: logFunctions?.warn,\n});\n\n// ---------------------------------------------------------------------------\n// Browser configuration builders\n// ---------------------------------------------------------------------------\n\n/**\n * Build a browser-safe {@link BrowserIntlayerConfig} from a raw user config.\n *\n * Applies defaults for every field and strips all server-only or sensitive\n * information (`system`, `content`, `build`, `compiler`, `dictionary`, `ai`,\n * `editor.clientId`, `editor.clientSecret`).\n *\n * This is the browser counterpart of `buildConfigurationFields`. It is safe\n * to call in browser environments because it has no Node.js dependencies.\n *\n * @param customConfig - Optional partial user-supplied Intlayer config.\n * @returns A browser-safe configuration object ready for `window.INTLAYER_CONFIG`.\n *\n * @example\n * ```ts\n * import { buildBrowserConfiguration } from '@intlayer/config/client';\n *\n * window.INTLAYER_CONFIG = buildBrowserConfiguration({\n * internationalization: { locales: ['en', 'fr'], defaultLocale: 'en' },\n * });\n * ```\n */\nexport const buildBrowserConfiguration = (\n customConfig?: CustomIntlayerConfig\n): BrowserIntlayerConfig => {\n const { locales, defaultLocale } = buildInternationalizationFields(\n customConfig?.internationalization\n );\n const routing = buildRoutingFields(customConfig?.routing);\n const {\n clientId: _clientId,\n clientSecret: _clientSecret,\n ...editorPublic\n } = buildEditorFields(customConfig?.editor);\n const analytics = buildAnalyticsFields(customConfig?.analytics);\n const { mode, prefix } = buildLogFields(customConfig?.log);\n\n return {\n internationalization: { locales, defaultLocale },\n routing,\n editor: editorPublic,\n analytics,\n log: { mode, prefix },\n };\n};\n\n/**\n * Extract a {@link BrowserIntlayerConfig} from an already-built full\n * {@link IntlayerConfig}.\n *\n * Used by build plugins (`vite-intlayer`, `withIntlayer`) which already hold\n * the full server-side config and need to inject the browser-safe subset at\n * compile time via a bundler `define`.\n *\n * @param config - A fully-built server-side Intlayer configuration.\n * @returns The browser-safe subset of that configuration.\n */\nexport const extractBrowserConfiguration = (\n config: IntlayerConfig\n): BrowserIntlayerConfig => ({\n internationalization: {\n locales: config.internationalization.locales,\n defaultLocale: config.internationalization.defaultLocale,\n },\n routing: {\n mode: config.routing.mode,\n enableProxy: config.routing.enableProxy,\n storage: config.routing.storage,\n basePath: config.routing.basePath,\n rewrite: config.routing.rewrite,\n },\n editor: {\n applicationURL: config.editor.applicationURL,\n editorURL: config.editor.editorURL,\n cmsURL: config.editor.cmsURL,\n backendURL: config.editor.backendURL,\n port: config.editor.port,\n enabled: config.editor.enabled,\n dictionaryPriorityStrategy: config.editor.dictionaryPriorityStrategy,\n liveSync: config.editor.liveSync,\n liveSyncPort: config.editor.liveSyncPort,\n liveSyncURL: config.editor.liveSyncURL,\n },\n analytics: buildAnalyticsFields(config.analytics),\n log: {\n mode: config.log.mode,\n prefix: config.log.prefix,\n },\n});\n"],"mappings":";;;;;;;;;;;;;;;AAoFA,MAAa,mCACX,yBACgC;;;;;;;CAOhC,SAAS,qBAAqB,WAAWA;;;;;;;;;;CAWzC,iBACE,qBAAqB,mBACrB,qBAAqB,WACrBC;;;;;;;;;CAUF,YAAY,qBAAqB;;;;;;CAOjC,eAAe,qBAAqB;AACtC;;;;;;;AAQA,MAAa,sBACX,wBACkB;CAClB,MAAM,UAAU,qBAAqB,WAAWC;CAEhD,OAAO;;;;;;;;;;;;;;;;;;;;;;;EAuBL,MAAM,qBAAqB;;;;;;;;;;;;;;;EAgB3B,aAAa,qBAAqB;;;;;;;;;;EAWlC,SAASC,wDAAqB,OAAO;;;;;;;;;;;;EAarC,UAAU,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6B/B,SAAS,qBAAqB;;;;;;EAO9B,SAAS,qBAAqB;CAChC;AACF;;;;;;;;;;;;;;;;;;;AAoBA,MAAa,qBACX,wBACiB;CACjB,MAAM,eAAe,qBAAqB;CAC1C,OAAO;;;;;;;;EAQL,gBAAgB,qBAAqB;;;;;;;;EASrC,WAAW,qBAAqB;;;;EAKhC,QAAQ,qBAAqB;;;;;;EAO7B,YAAY,qBAAqB;;;;;EAMjC,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;EAsB3B,SAAS,qBAAqB;;;;;;;;;;EAW9B,UAAU,qBAAqB;;;;;;;;;;EAW/B,cAAc,qBAAqB;;;;;;;;;;;EAYnC,4BACE,qBAAqB;;;;;;;;;EAWvB,UAAU,qBAAqB;;;;;;EAO/B;;;;;;EAOA,aACE,qBAAqB,eAAe,oBAAoB;CAC5D;AACF;;;;;;;;;;;;;;;;;;AAmBA,MAAa,wBACX,qBACA,qBAAqB,UACA;;;;;;CAMrB,SACE,qBAAqB,oBAAiC;;;;;;CAOxD,eAAe,qBAAqB;;;;;;CAOpC,YAAY,qBAAqB;AACnC;;;;;;;;AASA,MAAa,kBACX,qBACA,kBACe;;;;;;;;;;CAUf,MAAM,qBAAqB;;;;;;;;CAS3B,QAAQ,qBAAqB,UAAUC;;;;CAKvC,OAAO,cAAc;CACrB,KAAK,cAAc;CACnB,MAAM,cAAc;CACpB,MAAM,cAAc;AACtB;;;;;;;;;;;;;;;;;;;;;;;AA4BA,MAAa,6BACX,iBAC0B;CAC1B,MAAM,EAAE,SAAS,kBAAkB,gCACjC,cAAc,oBAChB;CACA,MAAM,UAAU,mBAAmB,cAAc,OAAO;CACxD,MAAM,EACJ,UAAU,WACV,cAAc,eACd,GAAG,iBACD,kBAAkB,cAAc,MAAM;CAC1C,MAAM,YAAY,qBAAqB,cAAc,SAAS;CAC9D,MAAM,EAAE,MAAM,WAAW,eAAe,cAAc,GAAG;CAEzD,OAAO;EACL,sBAAsB;GAAE;GAAS;EAAc;EAC/C;EACA,QAAQ;EACR;EACA,KAAK;GAAE;GAAM;EAAO;CACtB;AACF;;;;;;;;;;;;AAaA,MAAa,+BACX,YAC2B;CAC3B,sBAAsB;EACpB,SAAS,OAAO,qBAAqB;EACrC,eAAe,OAAO,qBAAqB;CAC7C;CACA,SAAS;EACP,MAAM,OAAO,QAAQ;EACrB,aAAa,OAAO,QAAQ;EAC5B,SAAS,OAAO,QAAQ;EACxB,UAAU,OAAO,QAAQ;EACzB,SAAS,OAAO,QAAQ;CAC1B;CACA,QAAQ;EACN,gBAAgB,OAAO,OAAO;EAC9B,WAAW,OAAO,OAAO;EACzB,QAAQ,OAAO,OAAO;EACtB,YAAY,OAAO,OAAO;EAC1B,MAAM,OAAO,OAAO;EACpB,SAAS,OAAO,OAAO;EACvB,4BAA4B,OAAO,OAAO;EAC1C,UAAU,OAAO,OAAO;EACxB,cAAc,OAAO,OAAO;EAC5B,aAAa,OAAO,OAAO;CAC7B;CACA,WAAW,qBAAqB,OAAO,SAAS;CAChD,KAAK;EACH,MAAM,OAAO,IAAI;EACjB,QAAQ,OAAO,IAAI;CACrB;AACF"}
1
+ {"version":3,"file":"buildBrowserConfiguration.cjs","names":["LOCALES","REQUIRED_LOCALES","STORAGE","getStorageAttributes","PREFIX"],"sources":["../../../src/configFile/buildBrowserConfiguration.ts"],"sourcesContent":["import type {\n AnalyticsConfig,\n CustomIntlayerConfig,\n CustomRoutingConfig,\n EditorConfig,\n InternationalizationConfig,\n IntlayerConfig,\n LogConfig,\n LogFunctions,\n RoutingConfig,\n} from '@intlayer/types/config';\nimport {\n ANALYTICS_ENABLED,\n ANALYTICS_FLUSH_INTERVAL,\n ANALYTICS_SAMPLE_RATE,\n} from '../defaultValues/analytics';\nimport {\n APPLICATION_URL,\n BACKEND_URL,\n CMS_URL,\n DICTIONARY_PRIORITY_STRATEGY,\n EDITOR_URL,\n IS_ENABLED,\n LIVE_SYNC,\n LIVE_SYNC_PORT,\n PORT,\n} from '../defaultValues/editor';\nimport {\n DEFAULT_LOCALE,\n LOCALES,\n REQUIRED_LOCALES,\n STRICT_MODE,\n} from '../defaultValues/internationalization';\nimport { MODE, PREFIX } from '../defaultValues/log';\nimport {\n BASE_PATH,\n ENABLE_PROXY,\n ROUTING_MODE,\n STORAGE,\n} from '../defaultValues/routing';\nimport { getStorageAttributes } from '../utils/getStorageAttributes';\n\n// ---------------------------------------------------------------------------\n// Type\n// ---------------------------------------------------------------------------\n\n/**\n * Browser-safe subset of {@link IntlayerConfig}.\n *\n * Excludes server-only fields (`system`, `content`, `build`, `compiler`,\n * `dictionary`, `ai`) and the `editor.clientSecret`, which must never be\n * shipped to the browser.\n *\n * `editor.clientId` is kept: it is the **public** project key, used by\n * `@intlayer/analytics` to identify the project when exchanging it for a\n * short-lived ingest token. It grants nothing on its own — every credentialed\n * backend call needs the secret, which only ever exists server-side.\n */\nexport type BrowserIntlayerConfig = {\n internationalization: Pick<\n InternationalizationConfig,\n 'locales' | 'defaultLocale'\n >;\n routing: RoutingConfig;\n editor: Omit<EditorConfig, 'clientSecret'>;\n analytics: AnalyticsConfig;\n log: Pick<LogConfig, 'mode' | 'prefix'>;\n};\n\ndeclare global {\n interface Window {\n /** Browser-safe Intlayer configuration injected by a build plugin or `installIntlayer`. */\n INTLAYER_CONFIG?: BrowserIntlayerConfig;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared field builders (browser-safe — no Node.js APIs)\n//\n// These functions are re-used by both `buildBrowserConfiguration` (browser)\n// and `buildConfigurationFields` (server) to avoid duplication.\n// ---------------------------------------------------------------------------\n\n/**\n * Build the internationalization section of the Intlayer configuration.\n *\n * @param customConfiguration - Partial user-supplied internationalization config.\n * @returns A fully-defaulted {@link InternationalizationConfig}.\n */\nexport const buildInternationalizationFields = (\n customConfiguration?: Partial<InternationalizationConfig>\n): InternationalizationConfig => ({\n /**\n * Locales available in the application\n *\n * Default: ['en']\n *\n */\n locales: customConfiguration?.locales ?? LOCALES,\n\n /**\n * Locales required by TypeScript to ensure strong implementations of internationalized content using typescript.\n *\n * Default: []\n *\n * If empty, all locales are required in `strict` mode.\n *\n * Ensure required locales are also defined in the `locales` field.\n */\n requiredLocales:\n customConfiguration?.requiredLocales ??\n customConfiguration?.locales ??\n REQUIRED_LOCALES,\n\n /**\n * Ensure strong implementations of internationalized content using typescript.\n * - If set to \"strict\", the translation `t` function will require each declared locales to be defined. If one locale is missing, or if a locale is not declared in your config, it will throw an error.\n * - If set to \"inclusive\", the translation `t` function will require each declared locales to be defined. If one locale is missing, it will throw a warning. But will accept if a locale is not declared in your config, but exist.\n * - If set to \"loose\", the translation `t` function will accept any existing locale.\n *\n * Default: \"inclusive\"\n */\n strictMode: customConfiguration?.strictMode ?? STRICT_MODE,\n\n /**\n * Default locale of the application for fallback\n *\n * Default: 'en'\n */\n defaultLocale: customConfiguration?.defaultLocale ?? DEFAULT_LOCALE,\n});\n\n/**\n * Build the routing section of the Intlayer configuration.\n *\n * @param customConfiguration - Partial user-supplied routing config.\n * @returns A fully-defaulted {@link RoutingConfig}.\n */\nexport const buildRoutingFields = (\n customConfiguration?: Partial<CustomRoutingConfig>\n): RoutingConfig => {\n const storage = customConfiguration?.storage ?? STORAGE;\n\n return {\n /**\n * URL routing mode for locale handling\n *\n * Controls how locales are represented in application URLs:\n * - 'prefix-no-default': Prefix all locales except the default locale (default)\n * - en → /dashboard\n * - fr → /fr/dashboard\n *\n * - 'prefix-all': Prefix all locales including the default locale\n * - en → /en/dashboard\n * - fr → /fr/dashboard\n *\n * - 'search-params': Use search parameters for locale handling\n * - en → /dashboard?locale=en\n * - fr → /fr/dashboard?locale=fr\n *\n * - 'no-prefix': No locale prefixing in URLs\n * - en → /dashboard\n * - fr → /dashboard\n *\n * Default: 'prefix-no-default'\n */\n mode: customConfiguration?.mode ?? ROUTING_MODE,\n\n /**\n * Enables the Intlayer locale-routing proxy (middleware).\n *\n * When enabled, the build-tool integration (e.g. the `intlayer()` Vite plugin)\n * automatically wires the locale-detection / redirect / rewrite middleware in\n * development, preview, and production SSR.\n *\n * - `undefined` (auto, default): the proxy runs, but development and preview\n * servers ignore the stored locale (cookie / header) as a redirect source.\n * - `true`: full proxy behaviour in every environment.\n * - `false`: no locale routing; the proxy is not registered.\n *\n * Default: undefined (auto)\n */\n enableProxy: customConfiguration?.enableProxy ?? ENABLE_PROXY,\n\n /**\n * Configuration for storing the locale in the client (localStorage or sessionStorage)\n *\n * If false, the locale will not be stored by the middleware.\n * If true, the locale storage will consider all default values. (cookie and header)\n *\n * Default: ['cookie', 'header']\n *\n */\n storage: getStorageAttributes(storage),\n\n /**\n * Base path of the application URL\n *\n * Default: ''\n *\n * Example:\n * - If the application is hosted at https://example.com/my-app\n * - The base path is '/my-app'\n * - The URL will be https://example.com/my-app/en\n * - If the base path is not set, the URL will be https://example.com/en\n */\n basePath: customConfiguration?.basePath ?? BASE_PATH,\n\n /**\n * Custom URL rewriting rules that override the default routing mode for specific paths.\n * Allows you to define locale-specific paths that differ from the standard routing behavior.\n * Supports dynamic route parameters using `[param]` syntax.\n *\n * Default: undefined\n *\n * Example:\n * ```typescript\n * rewrite: {\n * \"/about\": {\n * en: \"/about\",\n * fr: \"/a-propos\",\n * },\n * \"/product/[slug]\": {\n * en: \"/product/[slug]\",\n * fr: \"/produit/[slug]\",\n * },\n * }\n * ```\n *\n * Note:\n * - The rewrite rules take precedence over the default `mode` behavior.\n * - If a path matches a rewrite rule, the localized path from the rewrite configuration will be used.\n * - Dynamic route parameters are supported using bracket notation (e.g., `[slug]`, `[id]`).\n * - Works with both Next.js and Vite applications.\n */\n rewrite: customConfiguration?.rewrite,\n\n /**\n * Maps locales to specific domain hostnames for domain-based routing.\n *\n * Default: undefined\n */\n domains: customConfiguration?.domains,\n };\n};\n\n/**\n * Build the editor section of the Intlayer configuration.\n *\n * Returns the **full** {@link EditorConfig} including sensitive fields\n * (`clientId`, `clientSecret`). The browser-safe {@link BrowserIntlayerConfig}\n * omits those fields when exposing config to the client.\n *\n * `clientId` / `clientSecret` are never inferred from the environment: the\n * configuration file is the single source of truth. A project that wants to\n * authenticate from its `.env` file must wire the variables explicitly, e.g.\n * `clientId: process.env.INTLAYER_CLIENT_ID`. Commenting those lines out is\n * therefore enough to disable every credentialed behaviour (remote dictionary\n * fetching, CMS access) even when the variables remain defined in the\n * environment.\n *\n * @param customConfiguration - Partial user-supplied editor config.\n * @returns A fully-defaulted {@link EditorConfig}.\n */\nexport const buildEditorFields = (\n customConfiguration?: Partial<EditorConfig>\n): EditorConfig => {\n const liveSyncPort = customConfiguration?.liveSyncPort ?? LIVE_SYNC_PORT;\n return {\n /**\n * URL of the application. Used to restrict the origin of the editor for security reasons.\n *\n * > '*' means that the editor is accessible from any origin\n *\n * Default: '*'\n */\n applicationURL: customConfiguration?.applicationURL || APPLICATION_URL,\n\n /**\n * URL of the editor server. Used to restrict the origin of the editor for security reasons.\n *\n * > '*' means that the editor is accessible from any origin\n *\n * Default: '*'\n */\n editorURL: customConfiguration?.editorURL || EDITOR_URL,\n\n /**\n * URL of the CMS server. Used to restrict the origin of the editor for security reasons.\n */\n cmsURL: customConfiguration?.cmsURL || CMS_URL,\n\n /**\n * URL of the editor server\n *\n * Default: 'https://back.intlayer.org'\n */\n backendURL: customConfiguration?.backendURL || BACKEND_URL,\n\n /** Port of the editor server\n *\n * Default: 8000\n */\n port: customConfiguration?.port ?? PORT,\n\n /**\n * Indicates if the application interact with the visual editor\n *\n * Default: false;\n *\n * If true, the editor will be able to interact with the application.\n * If false, the editor will not be able to interact with the application.\n * In any case, the editor can only be enabled by the visual editor.\n * Disabling the editor for specific environments is a way to enforce the security.\n *\n * Usage:\n * ```js\n * {\n * // Other configurations\n * editor: {\n * enabled: process.env.NODE_ENV !== 'production',\n * }\n * };\n * ```\n */\n enabled: customConfiguration?.enabled ?? IS_ENABLED,\n\n /**\n * clientId and clientSecret allow the intlayer packages to authenticate with the backend using oAuth2 authentication.\n * An access token is use to authenticate the user related to the project.\n * To get an access token, go to https://app.intlayer.org/project and create an account.\n *\n * Default: undefined\n *\n * > Important: The clientId and clientSecret should be kept secret and not shared publicly. Please ensure to keep them in a secure location, such as environment variables.\n */\n clientId: customConfiguration?.clientId,\n\n /**\n * clientId and clientSecret allow the intlayer packages to authenticate with the backend using oAuth2 authentication.\n * An access token is use to authenticate the user related to the project.\n * To get an access token, go to https://app.intlayer.org/project and create an account.\n *\n * Default: undefined\n *\n * > Important: The clientId and clientSecret should be kept secret and not shared publicly. Please ensure to keep them in a secure location, such as environment variables.\n */\n clientSecret: customConfiguration?.clientSecret,\n\n /**\n * Strategy for prioritizing dictionaries. If a dictionary is both present online and locally, the content will be merge.\n * However, is a field is defined in both dictionary, this setting determines which fields takes the priority over the other.\n *\n * Default: 'local_first'\n *\n * The strategy for prioritizing dictionaries. It can be either 'local_first' or 'distant_first'.\n * - 'local_first': The first dictionary found in the locale is used.\n * - 'distant_first': The first dictionary found in the distant locales is used.\n */\n dictionaryPriorityStrategy:\n customConfiguration?.dictionaryPriorityStrategy ??\n DICTIONARY_PRIORITY_STRATEGY,\n\n /**\n * Indicates if the application should hot reload the locale configurations when a change is detected.\n * For example, when a new dictionary is added or updated, the application will update the content tu display in the page.\n *\n * The hot reload is only available for clients of the `enterprise` plan.\n *\n * Default: false\n */\n liveSync: customConfiguration?.liveSync ?? LIVE_SYNC,\n\n /**\n * Port of the live sync server\n *\n * Default: 4000\n */\n liveSyncPort,\n\n /**\n * URL of the live sync server in case of remote live sync server\n *\n * Default: `http://localhost:${LIVE_SYNC_PORT}`\n */\n liveSyncURL:\n customConfiguration?.liveSyncURL ?? `http://localhost:${liveSyncPort}`,\n };\n};\n\n/**\n * Build the analytics section of the Intlayer configuration.\n *\n * Analytics is opt-out: `enabled` defaults to `true`, so installing the\n * optional `@intlayer/analytics` package is enough to turn it on. The runtime\n * additionally requires a project key (`editor.clientId`) for attribution\n * before collecting anything.\n *\n * An explicit `enabled` value always wins, so a project whose package manager\n * hides the package from the project root can still opt back in.\n *\n * @param customConfiguration - Partial user-supplied analytics config.\n * @param isPackageInstalled - Whether `@intlayer/analytics` is installed in the\n * project. Browser callers cannot resolve packages and pass `true`, letting\n * the runtime dynamic `import()` fail silently when it is absent.\n * @returns A fully-defaulted {@link AnalyticsConfig}.\n */\nexport const buildAnalyticsFields = (\n customConfiguration?: Partial<AnalyticsConfig>,\n isPackageInstalled = true\n): AnalyticsConfig => ({\n /**\n * Enables analytics collection (page views, content exposures, A/B events).\n *\n * Default: true, as soon as `@intlayer/analytics` is installed.\n */\n enabled:\n customConfiguration?.enabled ?? (ANALYTICS_ENABLED && isPackageInstalled),\n\n /**\n * Milliseconds between automatic batched flushes to the backend.\n *\n * Default: 20000\n */\n flushInterval: customConfiguration?.flushInterval ?? ANALYTICS_FLUSH_INTERVAL,\n\n /**\n * Fraction of sessions to record, from 0 (none) to 1 (all).\n *\n * Default: 1\n */\n sampleRate: customConfiguration?.sampleRate ?? ANALYTICS_SAMPLE_RATE,\n});\n\n/**\n * Build the log section of the Intlayer configuration.\n *\n * @param customConfiguration - Partial user-supplied log config.\n * @param logFunctions - Optional custom log function overrides (server-only).\n * @returns A fully-defaulted {@link LogConfig}.\n */\nexport const buildLogFields = (\n customConfiguration?: Partial<LogConfig>,\n logFunctions?: LogFunctions\n): LogConfig => ({\n /**\n * Indicates if the logger is enabled\n *\n * Default: 'prefix-no-default'\n *\n * If 'default', the logger is enabled and can be used.\n * If 'verbose', the logger will be enabled and can be used, but will log more information.\n * If 'disabled', the logger is disabled and cannot be used.\n */\n mode: customConfiguration?.mode ?? MODE,\n\n /**\n * Prefix of the logger\n *\n * Default: '[intlayer]'\n *\n * The prefix of the logger.\n */\n prefix: customConfiguration?.prefix ?? PREFIX,\n\n /**\n * Functions to log\n */\n error: logFunctions?.error,\n log: logFunctions?.log,\n info: logFunctions?.info,\n warn: logFunctions?.warn,\n});\n\n// ---------------------------------------------------------------------------\n// Browser configuration builders\n// ---------------------------------------------------------------------------\n\n/**\n * Build a browser-safe {@link BrowserIntlayerConfig} from a raw user config.\n *\n * Applies defaults for every field and strips all server-only or sensitive\n * information (`system`, `content`, `build`, `compiler`, `dictionary`, `ai`,\n * `editor.clientSecret`). The public `editor.clientId` is kept — see\n * {@link BrowserIntlayerConfig}.\n *\n * This is the browser counterpart of `buildConfigurationFields`. It is safe\n * to call in browser environments because it has no Node.js dependencies.\n *\n * @param customConfig - Optional partial user-supplied Intlayer config.\n * @returns A browser-safe configuration object ready for `window.INTLAYER_CONFIG`.\n *\n * @example\n * ```ts\n * import { buildBrowserConfiguration } from '@intlayer/config/client';\n *\n * window.INTLAYER_CONFIG = buildBrowserConfiguration({\n * internationalization: { locales: ['en', 'fr'], defaultLocale: 'en' },\n * });\n * ```\n */\nexport const buildBrowserConfiguration = (\n customConfig?: CustomIntlayerConfig\n): BrowserIntlayerConfig => {\n const { locales, defaultLocale } = buildInternationalizationFields(\n customConfig?.internationalization\n );\n const routing = buildRoutingFields(customConfig?.routing);\n const { clientSecret: _clientSecret, ...editorPublic } = buildEditorFields(\n customConfig?.editor\n );\n const analytics = buildAnalyticsFields(customConfig?.analytics);\n const { mode, prefix } = buildLogFields(customConfig?.log);\n\n return {\n internationalization: { locales, defaultLocale },\n routing,\n editor: editorPublic,\n analytics,\n log: { mode, prefix },\n };\n};\n\n/**\n * Extract a {@link BrowserIntlayerConfig} from an already-built full\n * {@link IntlayerConfig}.\n *\n * Used by build plugins (`vite-intlayer`, `withIntlayer`) which already hold\n * the full server-side config and need to inject the browser-safe subset at\n * compile time via a bundler `define`.\n *\n * @param config - A fully-built server-side Intlayer configuration.\n * @returns The browser-safe subset of that configuration.\n */\nexport const extractBrowserConfiguration = (\n config: IntlayerConfig\n): BrowserIntlayerConfig => {\n // Destructured rather than picked field by field so that any future addition\n // to `EditorConfig` is carried over automatically, while `clientSecret` stays\n // excluded by construction.\n const { clientSecret: _clientSecret, ...editorPublic } = config.editor;\n\n return {\n internationalization: {\n locales: config.internationalization.locales,\n defaultLocale: config.internationalization.defaultLocale,\n },\n routing: {\n mode: config.routing.mode,\n enableProxy: config.routing.enableProxy,\n storage: config.routing.storage,\n basePath: config.routing.basePath,\n rewrite: config.routing.rewrite,\n },\n editor: editorPublic,\n analytics: buildAnalyticsFields(config.analytics),\n log: {\n mode: config.log.mode,\n prefix: config.log.prefix,\n },\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;AAyFA,MAAa,mCACX,yBACgC;;;;;;;CAOhC,SAAS,qBAAqB,WAAWA;;;;;;;;;;CAWzC,iBACE,qBAAqB,mBACrB,qBAAqB,WACrBC;;;;;;;;;CAUF,YAAY,qBAAqB;;;;;;CAOjC,eAAe,qBAAqB;AACtC;;;;;;;AAQA,MAAa,sBACX,wBACkB;CAClB,MAAM,UAAU,qBAAqB,WAAWC;CAEhD,OAAO;;;;;;;;;;;;;;;;;;;;;;;EAuBL,MAAM,qBAAqB;;;;;;;;;;;;;;;EAgB3B,aAAa,qBAAqB;;;;;;;;;;EAWlC,SAASC,wDAAqB,OAAO;;;;;;;;;;;;EAarC,UAAU,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6B/B,SAAS,qBAAqB;;;;;;EAO9B,SAAS,qBAAqB;CAChC;AACF;;;;;;;;;;;;;;;;;;;AAoBA,MAAa,qBACX,wBACiB;CACjB,MAAM,eAAe,qBAAqB;CAC1C,OAAO;;;;;;;;EAQL,gBAAgB,qBAAqB;;;;;;;;EASrC,WAAW,qBAAqB;;;;EAKhC,QAAQ,qBAAqB;;;;;;EAO7B,YAAY,qBAAqB;;;;;EAMjC,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;EAsB3B,SAAS,qBAAqB;;;;;;;;;;EAW9B,UAAU,qBAAqB;;;;;;;;;;EAW/B,cAAc,qBAAqB;;;;;;;;;;;EAYnC,4BACE,qBAAqB;;;;;;;;;EAWvB,UAAU,qBAAqB;;;;;;EAO/B;;;;;;EAOA,aACE,qBAAqB,eAAe,oBAAoB;CAC5D;AACF;;;;;;;;;;;;;;;;;;AAmBA,MAAa,wBACX,qBACA,qBAAqB,UACA;;;;;;CAMrB,SACE,qBAAqB,oBAAiC;;;;;;CAOxD,eAAe,qBAAqB;;;;;;CAOpC,YAAY,qBAAqB;AACnC;;;;;;;;AASA,MAAa,kBACX,qBACA,kBACe;;;;;;;;;;CAUf,MAAM,qBAAqB;;;;;;;;CAS3B,QAAQ,qBAAqB,UAAUC;;;;CAKvC,OAAO,cAAc;CACrB,KAAK,cAAc;CACnB,MAAM,cAAc;CACpB,MAAM,cAAc;AACtB;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAa,6BACX,iBAC0B;CAC1B,MAAM,EAAE,SAAS,kBAAkB,gCACjC,cAAc,oBAChB;CACA,MAAM,UAAU,mBAAmB,cAAc,OAAO;CACxD,MAAM,EAAE,cAAc,eAAe,GAAG,iBAAiB,kBACvD,cAAc,MAChB;CACA,MAAM,YAAY,qBAAqB,cAAc,SAAS;CAC9D,MAAM,EAAE,MAAM,WAAW,eAAe,cAAc,GAAG;CAEzD,OAAO;EACL,sBAAsB;GAAE;GAAS;EAAc;EAC/C;EACA,QAAQ;EACR;EACA,KAAK;GAAE;GAAM;EAAO;CACtB;AACF;;;;;;;;;;;;AAaA,MAAa,+BACX,WAC0B;CAI1B,MAAM,EAAE,cAAc,eAAe,GAAG,iBAAiB,OAAO;CAEhE,OAAO;EACL,sBAAsB;GACpB,SAAS,OAAO,qBAAqB;GACrC,eAAe,OAAO,qBAAqB;EAC7C;EACA,SAAS;GACP,MAAM,OAAO,QAAQ;GACrB,aAAa,OAAO,QAAQ;GAC5B,SAAS,OAAO,QAAQ;GACxB,UAAU,OAAO,QAAQ;GACzB,SAAS,OAAO,QAAQ;EAC1B;EACA,QAAQ;EACR,WAAW,qBAAqB,OAAO,SAAS;EAChD,KAAK;GACH,MAAM,OAAO,IAAI;GACjB,QAAQ,OAAO,IAAI;EACrB;CACF;AACF"}
@@ -0,0 +1,21 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ //#region src/secrets.browser.ts
3
+ /**
4
+ * Browser stub for {@link file://./secrets.ts}.
5
+ *
6
+ * Selected through the `browser` export condition of
7
+ * `@intlayer/config/secrets`, so a client bundle resolves the credential
8
+ * accessor to an inert function instead of pulling the configuration loader —
9
+ * and the secret itself — into a public asset.
10
+ */
11
+ /**
12
+ * Always `undefined` in the browser: the CMS client secret grants project-wide
13
+ * API access and must never leave the server.
14
+ *
15
+ * @returns `undefined`.
16
+ */
17
+ const getEditorClientSecret = () => void 0;
18
+
19
+ //#endregion
20
+ exports.getEditorClientSecret = getEditorClientSecret;
21
+ //# sourceMappingURL=secrets.browser.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.browser.cjs","names":[],"sources":["../../src/secrets.browser.ts"],"sourcesContent":["/**\n * Browser stub for {@link file://./secrets.ts}.\n *\n * Selected through the `browser` export condition of\n * `@intlayer/config/secrets`, so a client bundle resolves the credential\n * accessor to an inert function instead of pulling the configuration loader —\n * and the secret itself — into a public asset.\n */\n\n/**\n * Always `undefined` in the browser: the CMS client secret grants project-wide\n * API access and must never leave the server.\n *\n * @returns `undefined`.\n */\nexport const getEditorClientSecret = (): string | undefined => undefined;\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,MAAa,8BAAkD"}
@@ -0,0 +1,47 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ //#region src/secrets.ts
3
+ /**
4
+ * Server-only access to the sensitive parts of the Intlayer configuration.
5
+ *
6
+ * `@intlayer/config/built` is aliased by every framework integration to a
7
+ * generated file that the bundle **inlines**, and the same alias is installed
8
+ * for the client and the server build. Anything it carries that browser code
9
+ * imports therefore ends up in a public asset, which is why the generated file
10
+ * never contains `editor.clientSecret`.
11
+ *
12
+ * This module is the server-side counterpart. It is deliberately *not* aliased:
13
+ * it resolves through the package's export map, whose `browser` condition
14
+ * points at {@link file://./secrets.browser.ts} — an inert stub. Every
15
+ * major bundler (webpack, Rspack, Turbopack, Vite, Rollup, esbuild, Metro)
16
+ * honours that condition, so a browser build resolves to the stub without any
17
+ * integration-specific configuration.
18
+ *
19
+ * It intentionally imports nothing: pulling the configuration loader in here
20
+ * would drag the whole resolved configuration — and the machinery that builds
21
+ * it — into every server bundle that touches a credentialed API.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { getEditorClientSecret } from '@intlayer/config/secrets';
26
+ *
27
+ * const clientSecret = config.editor.clientSecret ?? getEditorClientSecret();
28
+ * ```
29
+ */
30
+ /**
31
+ * Returns the CMS client secret from the environment, or `undefined` when it is
32
+ * not set.
33
+ *
34
+ * The configuration file stays the source of truth for *whether* credentials
35
+ * are active — a project opts in by wiring `clientId` (conventionally
36
+ * `clientId: process.env.INTLAYER_CLIENT_ID`), and callers gate on the public
37
+ * `editor.clientId` from `@intlayer/config/built` before asking for the secret.
38
+ * This function only supplies the confidential half, which the generated
39
+ * configuration deliberately does not carry.
40
+ *
41
+ * @returns The OAuth2 client secret, or `undefined`.
42
+ */
43
+ const getEditorClientSecret = () => typeof process === "undefined" ? void 0 : process.env?.INTLAYER_CLIENT_SECRET;
44
+
45
+ //#endregion
46
+ exports.getEditorClientSecret = getEditorClientSecret;
47
+ //# sourceMappingURL=secrets.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.cjs","names":[],"sources":["../../src/secrets.ts"],"sourcesContent":["/**\n * Server-only access to the sensitive parts of the Intlayer configuration.\n *\n * `@intlayer/config/built` is aliased by every framework integration to a\n * generated file that the bundle **inlines**, and the same alias is installed\n * for the client and the server build. Anything it carries that browser code\n * imports therefore ends up in a public asset, which is why the generated file\n * never contains `editor.clientSecret`.\n *\n * This module is the server-side counterpart. It is deliberately *not* aliased:\n * it resolves through the package's export map, whose `browser` condition\n * points at {@link file://./secrets.browser.ts} — an inert stub. Every\n * major bundler (webpack, Rspack, Turbopack, Vite, Rollup, esbuild, Metro)\n * honours that condition, so a browser build resolves to the stub without any\n * integration-specific configuration.\n *\n * It intentionally imports nothing: pulling the configuration loader in here\n * would drag the whole resolved configuration — and the machinery that builds\n * it — into every server bundle that touches a credentialed API.\n *\n * @example\n * ```ts\n * import { getEditorClientSecret } from '@intlayer/config/secrets';\n *\n * const clientSecret = config.editor.clientSecret ?? getEditorClientSecret();\n * ```\n */\n\n/**\n * Returns the CMS client secret from the environment, or `undefined` when it is\n * not set.\n *\n * The configuration file stays the source of truth for *whether* credentials\n * are active — a project opts in by wiring `clientId` (conventionally\n * `clientId: process.env.INTLAYER_CLIENT_ID`), and callers gate on the public\n * `editor.clientId` from `@intlayer/config/built` before asking for the secret.\n * This function only supplies the confidential half, which the generated\n * configuration deliberately does not carry.\n *\n * @returns The OAuth2 client secret, or `undefined`.\n */\nexport const getEditorClientSecret = (): string | undefined =>\n typeof process === 'undefined'\n ? undefined\n : process.env?.INTLAYER_CLIENT_SECRET;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,MAAa,8BACX,OAAO,YAAY,cACf,SACA,QAAQ,KAAK"}
@@ -1,7 +1,7 @@
1
1
  import { getAlias } from "../utils/alias.mjs";
2
2
  import { getConfiguration } from "../configFile/getConfiguration.mjs";
3
- import { BundleLogger } from "./logBundle.mjs";
4
3
  import { getConfigEnvVars } from "../envVars/envVars.mjs";
4
+ import { BundleLogger } from "./logBundle.mjs";
5
5
  import { isAbsolute, join, resolve } from "node:path";
6
6
  import { mkdir, rm, writeFile } from "node:fs/promises";
7
7
  import configPackageJson from "@intlayer/types/package.json" with { type: "json" };
@@ -6,34 +6,47 @@
6
6
  * through member access (`content.title`) or destructuring
7
7
  * (`const { title } = useIntlayer('home')`).
8
8
  */
9
- const BASE_CALLERS = [{
10
- callerName: "useIntlayer",
11
- library: "intlayer",
12
- importSources: [
13
- "intlayer",
14
- "react-intlayer",
15
- "next-intlayer",
16
- "vue-intlayer",
17
- "svelte-intlayer",
18
- "solid-intlayer",
19
- "preact-intlayer",
20
- "angular-intlayer"
21
- ],
22
- namespaceSources: [{
23
- from: "argument",
24
- index: 0
25
- }],
26
- translationFunction: "content"
27
- }, {
28
- callerName: "getIntlayer",
29
- library: "intlayer",
30
- importSources: ["intlayer", "@intlayer/core"],
31
- namespaceSources: [{
32
- from: "argument",
33
- index: 0
34
- }],
35
- translationFunction: "content"
36
- }];
9
+ const BASE_CALLERS = [
10
+ {
11
+ callerName: "useIntlayer",
12
+ library: "intlayer",
13
+ importSources: [
14
+ "intlayer",
15
+ "react-intlayer",
16
+ "next-intlayer",
17
+ "vue-intlayer",
18
+ "svelte-intlayer",
19
+ "solid-intlayer",
20
+ "preact-intlayer",
21
+ "angular-intlayer"
22
+ ],
23
+ namespaceSources: [{
24
+ from: "argument",
25
+ index: 0
26
+ }],
27
+ translationFunction: "content"
28
+ },
29
+ {
30
+ callerName: "getIntlayer",
31
+ library: "intlayer",
32
+ importSources: ["intlayer", "@intlayer/core"],
33
+ namespaceSources: [{
34
+ from: "argument",
35
+ index: 0
36
+ }],
37
+ translationFunction: "content"
38
+ },
39
+ {
40
+ callerName: "getIntlayerAsync",
41
+ library: "intlayer",
42
+ importSources: ["intlayer", "@intlayer/core"],
43
+ namespaceSources: [{
44
+ from: "argument",
45
+ index: 0
46
+ }],
47
+ translationFunction: "content"
48
+ }
49
+ ];
37
50
 
38
51
  //#endregion
39
52
  export { BASE_CALLERS };
@@ -1 +1 @@
1
- {"version":3,"file":"base.mjs","names":[],"sources":["../../../src/callers/base.ts"],"sourcesContent":["import type { CallerDescriptor } from './types';\n\n/**\n * Base intlayer getters — always active, independent of any compat library.\n *\n * Their result is the dictionary content object itself: fields are consumed\n * through member access (`content.title`) or destructuring\n * (`const { title } = useIntlayer('home')`).\n */\nexport const BASE_CALLERS: CallerDescriptor[] = [\n {\n callerName: 'useIntlayer',\n library: 'intlayer',\n importSources: [\n 'intlayer',\n 'react-intlayer',\n 'next-intlayer',\n 'vue-intlayer',\n 'svelte-intlayer',\n 'solid-intlayer',\n 'preact-intlayer',\n 'angular-intlayer',\n ],\n namespaceSources: [{ from: 'argument', index: 0 }],\n translationFunction: 'content',\n },\n {\n callerName: 'getIntlayer',\n library: 'intlayer',\n importSources: ['intlayer', '@intlayer/core'],\n namespaceSources: [{ from: 'argument', index: 0 }],\n translationFunction: 'content',\n },\n];\n"],"mappings":";;;;;;;;AASA,MAAa,eAAmC,CAC9C;CACE,YAAY;CACZ,SAAS;CACT,eAAe;EACb;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CACA,kBAAkB,CAAC;EAAE,MAAM;EAAY,OAAO;CAAE,CAAC;CACjD,qBAAqB;AACvB,GACA;CACE,YAAY;CACZ,SAAS;CACT,eAAe,CAAC,YAAY,gBAAgB;CAC5C,kBAAkB,CAAC;EAAE,MAAM;EAAY,OAAO;CAAE,CAAC;CACjD,qBAAqB;AACvB,CACF"}
1
+ {"version":3,"file":"base.mjs","names":[],"sources":["../../../src/callers/base.ts"],"sourcesContent":["import type { CallerDescriptor } from './types';\n\n/**\n * Base intlayer getters — always active, independent of any compat library.\n *\n * Their result is the dictionary content object itself: fields are consumed\n * through member access (`content.title`) or destructuring\n * (`const { title } = useIntlayer('home')`).\n */\nexport const BASE_CALLERS: CallerDescriptor[] = [\n {\n callerName: 'useIntlayer',\n library: 'intlayer',\n importSources: [\n 'intlayer',\n 'react-intlayer',\n 'next-intlayer',\n 'vue-intlayer',\n 'svelte-intlayer',\n 'solid-intlayer',\n 'preact-intlayer',\n 'angular-intlayer',\n ],\n namespaceSources: [{ from: 'argument', index: 0 }],\n translationFunction: 'content',\n },\n {\n callerName: 'getIntlayer',\n library: 'intlayer',\n importSources: ['intlayer', '@intlayer/core'],\n namespaceSources: [{ from: 'argument', index: 0 }],\n translationFunction: 'content',\n },\n {\n // Async counterpart of `getIntlayer`: same content shape, reached through\n // an `await`.\n callerName: 'getIntlayerAsync',\n library: 'intlayer',\n importSources: ['intlayer', '@intlayer/core'],\n namespaceSources: [{ from: 'argument', index: 0 }],\n translationFunction: 'content',\n },\n];\n"],"mappings":";;;;;;;;AASA,MAAa,eAAmC;CAC9C;EACE,YAAY;EACZ,SAAS;EACT,eAAe;GACb;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC;GAAE,MAAM;GAAY,OAAO;EAAE,CAAC;EACjD,qBAAqB;CACvB;CACA;EACE,YAAY;EACZ,SAAS;EACT,eAAe,CAAC,YAAY,gBAAgB;EAC5C,kBAAkB,CAAC;GAAE,MAAM;GAAY,OAAO;EAAE,CAAC;EACjD,qBAAqB;CACvB;CACA;EAGE,YAAY;EACZ,SAAS;EACT,eAAe,CAAC,YAAY,gBAAgB;EAC5C,kBAAkB,CAAC;GAAE,MAAM;GAAY,OAAO;EAAE,CAAC;EACjD,qBAAqB;CACvB;AACF"}
@@ -354,7 +354,8 @@ const buildLogFields = (customConfiguration, logFunctions) => ({
354
354
  *
355
355
  * Applies defaults for every field and strips all server-only or sensitive
356
356
  * information (`system`, `content`, `build`, `compiler`, `dictionary`, `ai`,
357
- * `editor.clientId`, `editor.clientSecret`).
357
+ * `editor.clientSecret`). The public `editor.clientId` is kept — see
358
+ * {@link BrowserIntlayerConfig}.
358
359
  *
359
360
  * This is the browser counterpart of `buildConfigurationFields`. It is safe
360
361
  * to call in browser environments because it has no Node.js dependencies.
@@ -374,7 +375,7 @@ const buildLogFields = (customConfiguration, logFunctions) => ({
374
375
  const buildBrowserConfiguration = (customConfig) => {
375
376
  const { locales, defaultLocale } = buildInternationalizationFields(customConfig?.internationalization);
376
377
  const routing = buildRoutingFields(customConfig?.routing);
377
- const { clientId: _clientId, clientSecret: _clientSecret, ...editorPublic } = buildEditorFields(customConfig?.editor);
378
+ const { clientSecret: _clientSecret, ...editorPublic } = buildEditorFields(customConfig?.editor);
378
379
  const analytics = buildAnalyticsFields(customConfig?.analytics);
379
380
  const { mode, prefix } = buildLogFields(customConfig?.log);
380
381
  return {
@@ -402,36 +403,28 @@ const buildBrowserConfiguration = (customConfig) => {
402
403
  * @param config - A fully-built server-side Intlayer configuration.
403
404
  * @returns The browser-safe subset of that configuration.
404
405
  */
405
- const extractBrowserConfiguration = (config) => ({
406
- internationalization: {
407
- locales: config.internationalization.locales,
408
- defaultLocale: config.internationalization.defaultLocale
409
- },
410
- routing: {
411
- mode: config.routing.mode,
412
- enableProxy: config.routing.enableProxy,
413
- storage: config.routing.storage,
414
- basePath: config.routing.basePath,
415
- rewrite: config.routing.rewrite
416
- },
417
- editor: {
418
- applicationURL: config.editor.applicationURL,
419
- editorURL: config.editor.editorURL,
420
- cmsURL: config.editor.cmsURL,
421
- backendURL: config.editor.backendURL,
422
- port: config.editor.port,
423
- enabled: config.editor.enabled,
424
- dictionaryPriorityStrategy: config.editor.dictionaryPriorityStrategy,
425
- liveSync: config.editor.liveSync,
426
- liveSyncPort: config.editor.liveSyncPort,
427
- liveSyncURL: config.editor.liveSyncURL
428
- },
429
- analytics: buildAnalyticsFields(config.analytics),
430
- log: {
431
- mode: config.log.mode,
432
- prefix: config.log.prefix
433
- }
434
- });
406
+ const extractBrowserConfiguration = (config) => {
407
+ const { clientSecret: _clientSecret, ...editorPublic } = config.editor;
408
+ return {
409
+ internationalization: {
410
+ locales: config.internationalization.locales,
411
+ defaultLocale: config.internationalization.defaultLocale
412
+ },
413
+ routing: {
414
+ mode: config.routing.mode,
415
+ enableProxy: config.routing.enableProxy,
416
+ storage: config.routing.storage,
417
+ basePath: config.routing.basePath,
418
+ rewrite: config.routing.rewrite
419
+ },
420
+ editor: editorPublic,
421
+ analytics: buildAnalyticsFields(config.analytics),
422
+ log: {
423
+ mode: config.log.mode,
424
+ prefix: config.log.prefix
425
+ }
426
+ };
427
+ };
435
428
 
436
429
  //#endregion
437
430
  export { buildAnalyticsFields, buildBrowserConfiguration, buildEditorFields, buildInternationalizationFields, buildLogFields, buildRoutingFields, extractBrowserConfiguration };
@@ -1 +1 @@
1
- {"version":3,"file":"buildBrowserConfiguration.mjs","names":[],"sources":["../../../src/configFile/buildBrowserConfiguration.ts"],"sourcesContent":["import type {\n AnalyticsConfig,\n CustomIntlayerConfig,\n CustomRoutingConfig,\n EditorConfig,\n InternationalizationConfig,\n IntlayerConfig,\n LogConfig,\n LogFunctions,\n RoutingConfig,\n} from '@intlayer/types/config';\nimport {\n ANALYTICS_ENABLED,\n ANALYTICS_FLUSH_INTERVAL,\n ANALYTICS_SAMPLE_RATE,\n} from '../defaultValues/analytics';\nimport {\n APPLICATION_URL,\n BACKEND_URL,\n CMS_URL,\n DICTIONARY_PRIORITY_STRATEGY,\n EDITOR_URL,\n IS_ENABLED,\n LIVE_SYNC,\n LIVE_SYNC_PORT,\n PORT,\n} from '../defaultValues/editor';\nimport {\n DEFAULT_LOCALE,\n LOCALES,\n REQUIRED_LOCALES,\n STRICT_MODE,\n} from '../defaultValues/internationalization';\nimport { MODE, PREFIX } from '../defaultValues/log';\nimport {\n BASE_PATH,\n ENABLE_PROXY,\n ROUTING_MODE,\n STORAGE,\n} from '../defaultValues/routing';\nimport { getStorageAttributes } from '../utils/getStorageAttributes';\n\n// ---------------------------------------------------------------------------\n// Type\n// ---------------------------------------------------------------------------\n\n/**\n * Browser-safe subset of {@link IntlayerConfig}.\n *\n * Excludes server-only fields (`system`, `content`, `build`, `compiler`,\n * `dictionary`, `ai`) and sensitive editor credentials (`clientId`,\n * `clientSecret`) that must never be shipped to the browser.\n */\nexport type BrowserIntlayerConfig = {\n internationalization: Pick<\n InternationalizationConfig,\n 'locales' | 'defaultLocale'\n >;\n routing: RoutingConfig;\n editor: Omit<EditorConfig, 'clientId' | 'clientSecret'>;\n analytics: AnalyticsConfig;\n log: Pick<LogConfig, 'mode' | 'prefix'>;\n};\n\ndeclare global {\n interface Window {\n /** Browser-safe Intlayer configuration injected by a build plugin or `installIntlayer`. */\n INTLAYER_CONFIG?: BrowserIntlayerConfig;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared field builders (browser-safe — no Node.js APIs)\n//\n// These functions are re-used by both `buildBrowserConfiguration` (browser)\n// and `buildConfigurationFields` (server) to avoid duplication.\n// ---------------------------------------------------------------------------\n\n/**\n * Build the internationalization section of the Intlayer configuration.\n *\n * @param customConfiguration - Partial user-supplied internationalization config.\n * @returns A fully-defaulted {@link InternationalizationConfig}.\n */\nexport const buildInternationalizationFields = (\n customConfiguration?: Partial<InternationalizationConfig>\n): InternationalizationConfig => ({\n /**\n * Locales available in the application\n *\n * Default: ['en']\n *\n */\n locales: customConfiguration?.locales ?? LOCALES,\n\n /**\n * Locales required by TypeScript to ensure strong implementations of internationalized content using typescript.\n *\n * Default: []\n *\n * If empty, all locales are required in `strict` mode.\n *\n * Ensure required locales are also defined in the `locales` field.\n */\n requiredLocales:\n customConfiguration?.requiredLocales ??\n customConfiguration?.locales ??\n REQUIRED_LOCALES,\n\n /**\n * Ensure strong implementations of internationalized content using typescript.\n * - If set to \"strict\", the translation `t` function will require each declared locales to be defined. If one locale is missing, or if a locale is not declared in your config, it will throw an error.\n * - If set to \"inclusive\", the translation `t` function will require each declared locales to be defined. If one locale is missing, it will throw a warning. But will accept if a locale is not declared in your config, but exist.\n * - If set to \"loose\", the translation `t` function will accept any existing locale.\n *\n * Default: \"inclusive\"\n */\n strictMode: customConfiguration?.strictMode ?? STRICT_MODE,\n\n /**\n * Default locale of the application for fallback\n *\n * Default: 'en'\n */\n defaultLocale: customConfiguration?.defaultLocale ?? DEFAULT_LOCALE,\n});\n\n/**\n * Build the routing section of the Intlayer configuration.\n *\n * @param customConfiguration - Partial user-supplied routing config.\n * @returns A fully-defaulted {@link RoutingConfig}.\n */\nexport const buildRoutingFields = (\n customConfiguration?: Partial<CustomRoutingConfig>\n): RoutingConfig => {\n const storage = customConfiguration?.storage ?? STORAGE;\n\n return {\n /**\n * URL routing mode for locale handling\n *\n * Controls how locales are represented in application URLs:\n * - 'prefix-no-default': Prefix all locales except the default locale (default)\n * - en → /dashboard\n * - fr → /fr/dashboard\n *\n * - 'prefix-all': Prefix all locales including the default locale\n * - en → /en/dashboard\n * - fr → /fr/dashboard\n *\n * - 'search-params': Use search parameters for locale handling\n * - en → /dashboard?locale=en\n * - fr → /fr/dashboard?locale=fr\n *\n * - 'no-prefix': No locale prefixing in URLs\n * - en → /dashboard\n * - fr → /dashboard\n *\n * Default: 'prefix-no-default'\n */\n mode: customConfiguration?.mode ?? ROUTING_MODE,\n\n /**\n * Enables the Intlayer locale-routing proxy (middleware).\n *\n * When enabled, the build-tool integration (e.g. the `intlayer()` Vite plugin)\n * automatically wires the locale-detection / redirect / rewrite middleware in\n * development, preview, and production SSR.\n *\n * - `undefined` (auto, default): the proxy runs, but development and preview\n * servers ignore the stored locale (cookie / header) as a redirect source.\n * - `true`: full proxy behaviour in every environment.\n * - `false`: no locale routing; the proxy is not registered.\n *\n * Default: undefined (auto)\n */\n enableProxy: customConfiguration?.enableProxy ?? ENABLE_PROXY,\n\n /**\n * Configuration for storing the locale in the client (localStorage or sessionStorage)\n *\n * If false, the locale will not be stored by the middleware.\n * If true, the locale storage will consider all default values. (cookie and header)\n *\n * Default: ['cookie', 'header']\n *\n */\n storage: getStorageAttributes(storage),\n\n /**\n * Base path of the application URL\n *\n * Default: ''\n *\n * Example:\n * - If the application is hosted at https://example.com/my-app\n * - The base path is '/my-app'\n * - The URL will be https://example.com/my-app/en\n * - If the base path is not set, the URL will be https://example.com/en\n */\n basePath: customConfiguration?.basePath ?? BASE_PATH,\n\n /**\n * Custom URL rewriting rules that override the default routing mode for specific paths.\n * Allows you to define locale-specific paths that differ from the standard routing behavior.\n * Supports dynamic route parameters using `[param]` syntax.\n *\n * Default: undefined\n *\n * Example:\n * ```typescript\n * rewrite: {\n * \"/about\": {\n * en: \"/about\",\n * fr: \"/a-propos\",\n * },\n * \"/product/[slug]\": {\n * en: \"/product/[slug]\",\n * fr: \"/produit/[slug]\",\n * },\n * }\n * ```\n *\n * Note:\n * - The rewrite rules take precedence over the default `mode` behavior.\n * - If a path matches a rewrite rule, the localized path from the rewrite configuration will be used.\n * - Dynamic route parameters are supported using bracket notation (e.g., `[slug]`, `[id]`).\n * - Works with both Next.js and Vite applications.\n */\n rewrite: customConfiguration?.rewrite,\n\n /**\n * Maps locales to specific domain hostnames for domain-based routing.\n *\n * Default: undefined\n */\n domains: customConfiguration?.domains,\n };\n};\n\n/**\n * Build the editor section of the Intlayer configuration.\n *\n * Returns the **full** {@link EditorConfig} including sensitive fields\n * (`clientId`, `clientSecret`). The browser-safe {@link BrowserIntlayerConfig}\n * omits those fields when exposing config to the client.\n *\n * `clientId` / `clientSecret` are never inferred from the environment: the\n * configuration file is the single source of truth. A project that wants to\n * authenticate from its `.env` file must wire the variables explicitly, e.g.\n * `clientId: process.env.INTLAYER_CLIENT_ID`. Commenting those lines out is\n * therefore enough to disable every credentialed behaviour (remote dictionary\n * fetching, CMS access) even when the variables remain defined in the\n * environment.\n *\n * @param customConfiguration - Partial user-supplied editor config.\n * @returns A fully-defaulted {@link EditorConfig}.\n */\nexport const buildEditorFields = (\n customConfiguration?: Partial<EditorConfig>\n): EditorConfig => {\n const liveSyncPort = customConfiguration?.liveSyncPort ?? LIVE_SYNC_PORT;\n return {\n /**\n * URL of the application. Used to restrict the origin of the editor for security reasons.\n *\n * > '*' means that the editor is accessible from any origin\n *\n * Default: '*'\n */\n applicationURL: customConfiguration?.applicationURL || APPLICATION_URL,\n\n /**\n * URL of the editor server. Used to restrict the origin of the editor for security reasons.\n *\n * > '*' means that the editor is accessible from any origin\n *\n * Default: '*'\n */\n editorURL: customConfiguration?.editorURL || EDITOR_URL,\n\n /**\n * URL of the CMS server. Used to restrict the origin of the editor for security reasons.\n */\n cmsURL: customConfiguration?.cmsURL || CMS_URL,\n\n /**\n * URL of the editor server\n *\n * Default: 'https://back.intlayer.org'\n */\n backendURL: customConfiguration?.backendURL || BACKEND_URL,\n\n /** Port of the editor server\n *\n * Default: 8000\n */\n port: customConfiguration?.port ?? PORT,\n\n /**\n * Indicates if the application interact with the visual editor\n *\n * Default: false;\n *\n * If true, the editor will be able to interact with the application.\n * If false, the editor will not be able to interact with the application.\n * In any case, the editor can only be enabled by the visual editor.\n * Disabling the editor for specific environments is a way to enforce the security.\n *\n * Usage:\n * ```js\n * {\n * // Other configurations\n * editor: {\n * enabled: process.env.NODE_ENV !== 'production',\n * }\n * };\n * ```\n */\n enabled: customConfiguration?.enabled ?? IS_ENABLED,\n\n /**\n * clientId and clientSecret allow the intlayer packages to authenticate with the backend using oAuth2 authentication.\n * An access token is use to authenticate the user related to the project.\n * To get an access token, go to https://app.intlayer.org/project and create an account.\n *\n * Default: undefined\n *\n * > Important: The clientId and clientSecret should be kept secret and not shared publicly. Please ensure to keep them in a secure location, such as environment variables.\n */\n clientId: customConfiguration?.clientId,\n\n /**\n * clientId and clientSecret allow the intlayer packages to authenticate with the backend using oAuth2 authentication.\n * An access token is use to authenticate the user related to the project.\n * To get an access token, go to https://app.intlayer.org/project and create an account.\n *\n * Default: undefined\n *\n * > Important: The clientId and clientSecret should be kept secret and not shared publicly. Please ensure to keep them in a secure location, such as environment variables.\n */\n clientSecret: customConfiguration?.clientSecret,\n\n /**\n * Strategy for prioritizing dictionaries. If a dictionary is both present online and locally, the content will be merge.\n * However, is a field is defined in both dictionary, this setting determines which fields takes the priority over the other.\n *\n * Default: 'local_first'\n *\n * The strategy for prioritizing dictionaries. It can be either 'local_first' or 'distant_first'.\n * - 'local_first': The first dictionary found in the locale is used.\n * - 'distant_first': The first dictionary found in the distant locales is used.\n */\n dictionaryPriorityStrategy:\n customConfiguration?.dictionaryPriorityStrategy ??\n DICTIONARY_PRIORITY_STRATEGY,\n\n /**\n * Indicates if the application should hot reload the locale configurations when a change is detected.\n * For example, when a new dictionary is added or updated, the application will update the content tu display in the page.\n *\n * The hot reload is only available for clients of the `enterprise` plan.\n *\n * Default: false\n */\n liveSync: customConfiguration?.liveSync ?? LIVE_SYNC,\n\n /**\n * Port of the live sync server\n *\n * Default: 4000\n */\n liveSyncPort,\n\n /**\n * URL of the live sync server in case of remote live sync server\n *\n * Default: `http://localhost:${LIVE_SYNC_PORT}`\n */\n liveSyncURL:\n customConfiguration?.liveSyncURL ?? `http://localhost:${liveSyncPort}`,\n };\n};\n\n/**\n * Build the analytics section of the Intlayer configuration.\n *\n * Analytics is opt-out: `enabled` defaults to `true`, so installing the\n * optional `@intlayer/analytics` package is enough to turn it on. The runtime\n * additionally requires a project key (`editor.clientId`) for attribution\n * before collecting anything.\n *\n * An explicit `enabled` value always wins, so a project whose package manager\n * hides the package from the project root can still opt back in.\n *\n * @param customConfiguration - Partial user-supplied analytics config.\n * @param isPackageInstalled - Whether `@intlayer/analytics` is installed in the\n * project. Browser callers cannot resolve packages and pass `true`, letting\n * the runtime dynamic `import()` fail silently when it is absent.\n * @returns A fully-defaulted {@link AnalyticsConfig}.\n */\nexport const buildAnalyticsFields = (\n customConfiguration?: Partial<AnalyticsConfig>,\n isPackageInstalled = true\n): AnalyticsConfig => ({\n /**\n * Enables analytics collection (page views, content exposures, A/B events).\n *\n * Default: true, as soon as `@intlayer/analytics` is installed.\n */\n enabled:\n customConfiguration?.enabled ?? (ANALYTICS_ENABLED && isPackageInstalled),\n\n /**\n * Milliseconds between automatic batched flushes to the backend.\n *\n * Default: 20000\n */\n flushInterval: customConfiguration?.flushInterval ?? ANALYTICS_FLUSH_INTERVAL,\n\n /**\n * Fraction of sessions to record, from 0 (none) to 1 (all).\n *\n * Default: 1\n */\n sampleRate: customConfiguration?.sampleRate ?? ANALYTICS_SAMPLE_RATE,\n});\n\n/**\n * Build the log section of the Intlayer configuration.\n *\n * @param customConfiguration - Partial user-supplied log config.\n * @param logFunctions - Optional custom log function overrides (server-only).\n * @returns A fully-defaulted {@link LogConfig}.\n */\nexport const buildLogFields = (\n customConfiguration?: Partial<LogConfig>,\n logFunctions?: LogFunctions\n): LogConfig => ({\n /**\n * Indicates if the logger is enabled\n *\n * Default: 'prefix-no-default'\n *\n * If 'default', the logger is enabled and can be used.\n * If 'verbose', the logger will be enabled and can be used, but will log more information.\n * If 'disabled', the logger is disabled and cannot be used.\n */\n mode: customConfiguration?.mode ?? MODE,\n\n /**\n * Prefix of the logger\n *\n * Default: '[intlayer]'\n *\n * The prefix of the logger.\n */\n prefix: customConfiguration?.prefix ?? PREFIX,\n\n /**\n * Functions to log\n */\n error: logFunctions?.error,\n log: logFunctions?.log,\n info: logFunctions?.info,\n warn: logFunctions?.warn,\n});\n\n// ---------------------------------------------------------------------------\n// Browser configuration builders\n// ---------------------------------------------------------------------------\n\n/**\n * Build a browser-safe {@link BrowserIntlayerConfig} from a raw user config.\n *\n * Applies defaults for every field and strips all server-only or sensitive\n * information (`system`, `content`, `build`, `compiler`, `dictionary`, `ai`,\n * `editor.clientId`, `editor.clientSecret`).\n *\n * This is the browser counterpart of `buildConfigurationFields`. It is safe\n * to call in browser environments because it has no Node.js dependencies.\n *\n * @param customConfig - Optional partial user-supplied Intlayer config.\n * @returns A browser-safe configuration object ready for `window.INTLAYER_CONFIG`.\n *\n * @example\n * ```ts\n * import { buildBrowserConfiguration } from '@intlayer/config/client';\n *\n * window.INTLAYER_CONFIG = buildBrowserConfiguration({\n * internationalization: { locales: ['en', 'fr'], defaultLocale: 'en' },\n * });\n * ```\n */\nexport const buildBrowserConfiguration = (\n customConfig?: CustomIntlayerConfig\n): BrowserIntlayerConfig => {\n const { locales, defaultLocale } = buildInternationalizationFields(\n customConfig?.internationalization\n );\n const routing = buildRoutingFields(customConfig?.routing);\n const {\n clientId: _clientId,\n clientSecret: _clientSecret,\n ...editorPublic\n } = buildEditorFields(customConfig?.editor);\n const analytics = buildAnalyticsFields(customConfig?.analytics);\n const { mode, prefix } = buildLogFields(customConfig?.log);\n\n return {\n internationalization: { locales, defaultLocale },\n routing,\n editor: editorPublic,\n analytics,\n log: { mode, prefix },\n };\n};\n\n/**\n * Extract a {@link BrowserIntlayerConfig} from an already-built full\n * {@link IntlayerConfig}.\n *\n * Used by build plugins (`vite-intlayer`, `withIntlayer`) which already hold\n * the full server-side config and need to inject the browser-safe subset at\n * compile time via a bundler `define`.\n *\n * @param config - A fully-built server-side Intlayer configuration.\n * @returns The browser-safe subset of that configuration.\n */\nexport const extractBrowserConfiguration = (\n config: IntlayerConfig\n): BrowserIntlayerConfig => ({\n internationalization: {\n locales: config.internationalization.locales,\n defaultLocale: config.internationalization.defaultLocale,\n },\n routing: {\n mode: config.routing.mode,\n enableProxy: config.routing.enableProxy,\n storage: config.routing.storage,\n basePath: config.routing.basePath,\n rewrite: config.routing.rewrite,\n },\n editor: {\n applicationURL: config.editor.applicationURL,\n editorURL: config.editor.editorURL,\n cmsURL: config.editor.cmsURL,\n backendURL: config.editor.backendURL,\n port: config.editor.port,\n enabled: config.editor.enabled,\n dictionaryPriorityStrategy: config.editor.dictionaryPriorityStrategy,\n liveSync: config.editor.liveSync,\n liveSyncPort: config.editor.liveSyncPort,\n liveSyncURL: config.editor.liveSyncURL,\n },\n analytics: buildAnalyticsFields(config.analytics),\n log: {\n mode: config.log.mode,\n prefix: config.log.prefix,\n },\n});\n"],"mappings":";;;;;;;;;;;;;;AAoFA,MAAa,mCACX,yBACgC;;;;;;;CAOhC,SAAS,qBAAqB,WAAW;;;;;;;;;;CAWzC,iBACE,qBAAqB,mBACrB,qBAAqB,WACrB;;;;;;;;;CAUF,YAAY,qBAAqB;;;;;;CAOjC,eAAe,qBAAqB;AACtC;;;;;;;AAQA,MAAa,sBACX,wBACkB;CAClB,MAAM,UAAU,qBAAqB,WAAW;CAEhD,OAAO;;;;;;;;;;;;;;;;;;;;;;;EAuBL,MAAM,qBAAqB;;;;;;;;;;;;;;;EAgB3B,aAAa,qBAAqB;;;;;;;;;;EAWlC,SAAS,qBAAqB,OAAO;;;;;;;;;;;;EAarC,UAAU,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6B/B,SAAS,qBAAqB;;;;;;EAO9B,SAAS,qBAAqB;CAChC;AACF;;;;;;;;;;;;;;;;;;;AAoBA,MAAa,qBACX,wBACiB;CACjB,MAAM,eAAe,qBAAqB;CAC1C,OAAO;;;;;;;;EAQL,gBAAgB,qBAAqB;;;;;;;;EASrC,WAAW,qBAAqB;;;;EAKhC,QAAQ,qBAAqB;;;;;;EAO7B,YAAY,qBAAqB;;;;;EAMjC,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;EAsB3B,SAAS,qBAAqB;;;;;;;;;;EAW9B,UAAU,qBAAqB;;;;;;;;;;EAW/B,cAAc,qBAAqB;;;;;;;;;;;EAYnC,4BACE,qBAAqB;;;;;;;;;EAWvB,UAAU,qBAAqB;;;;;;EAO/B;;;;;;EAOA,aACE,qBAAqB,eAAe,oBAAoB;CAC5D;AACF;;;;;;;;;;;;;;;;;;AAmBA,MAAa,wBACX,qBACA,qBAAqB,UACA;;;;;;CAMrB,SACE,qBAAqB,oBAAiC;;;;;;CAOxD,eAAe,qBAAqB;;;;;;CAOpC,YAAY,qBAAqB;AACnC;;;;;;;;AASA,MAAa,kBACX,qBACA,kBACe;;;;;;;;;;CAUf,MAAM,qBAAqB;;;;;;;;CAS3B,QAAQ,qBAAqB,UAAU;;;;CAKvC,OAAO,cAAc;CACrB,KAAK,cAAc;CACnB,MAAM,cAAc;CACpB,MAAM,cAAc;AACtB;;;;;;;;;;;;;;;;;;;;;;;AA4BA,MAAa,6BACX,iBAC0B;CAC1B,MAAM,EAAE,SAAS,kBAAkB,gCACjC,cAAc,oBAChB;CACA,MAAM,UAAU,mBAAmB,cAAc,OAAO;CACxD,MAAM,EACJ,UAAU,WACV,cAAc,eACd,GAAG,iBACD,kBAAkB,cAAc,MAAM;CAC1C,MAAM,YAAY,qBAAqB,cAAc,SAAS;CAC9D,MAAM,EAAE,MAAM,WAAW,eAAe,cAAc,GAAG;CAEzD,OAAO;EACL,sBAAsB;GAAE;GAAS;EAAc;EAC/C;EACA,QAAQ;EACR;EACA,KAAK;GAAE;GAAM;EAAO;CACtB;AACF;;;;;;;;;;;;AAaA,MAAa,+BACX,YAC2B;CAC3B,sBAAsB;EACpB,SAAS,OAAO,qBAAqB;EACrC,eAAe,OAAO,qBAAqB;CAC7C;CACA,SAAS;EACP,MAAM,OAAO,QAAQ;EACrB,aAAa,OAAO,QAAQ;EAC5B,SAAS,OAAO,QAAQ;EACxB,UAAU,OAAO,QAAQ;EACzB,SAAS,OAAO,QAAQ;CAC1B;CACA,QAAQ;EACN,gBAAgB,OAAO,OAAO;EAC9B,WAAW,OAAO,OAAO;EACzB,QAAQ,OAAO,OAAO;EACtB,YAAY,OAAO,OAAO;EAC1B,MAAM,OAAO,OAAO;EACpB,SAAS,OAAO,OAAO;EACvB,4BAA4B,OAAO,OAAO;EAC1C,UAAU,OAAO,OAAO;EACxB,cAAc,OAAO,OAAO;EAC5B,aAAa,OAAO,OAAO;CAC7B;CACA,WAAW,qBAAqB,OAAO,SAAS;CAChD,KAAK;EACH,MAAM,OAAO,IAAI;EACjB,QAAQ,OAAO,IAAI;CACrB;AACF"}
1
+ {"version":3,"file":"buildBrowserConfiguration.mjs","names":[],"sources":["../../../src/configFile/buildBrowserConfiguration.ts"],"sourcesContent":["import type {\n AnalyticsConfig,\n CustomIntlayerConfig,\n CustomRoutingConfig,\n EditorConfig,\n InternationalizationConfig,\n IntlayerConfig,\n LogConfig,\n LogFunctions,\n RoutingConfig,\n} from '@intlayer/types/config';\nimport {\n ANALYTICS_ENABLED,\n ANALYTICS_FLUSH_INTERVAL,\n ANALYTICS_SAMPLE_RATE,\n} from '../defaultValues/analytics';\nimport {\n APPLICATION_URL,\n BACKEND_URL,\n CMS_URL,\n DICTIONARY_PRIORITY_STRATEGY,\n EDITOR_URL,\n IS_ENABLED,\n LIVE_SYNC,\n LIVE_SYNC_PORT,\n PORT,\n} from '../defaultValues/editor';\nimport {\n DEFAULT_LOCALE,\n LOCALES,\n REQUIRED_LOCALES,\n STRICT_MODE,\n} from '../defaultValues/internationalization';\nimport { MODE, PREFIX } from '../defaultValues/log';\nimport {\n BASE_PATH,\n ENABLE_PROXY,\n ROUTING_MODE,\n STORAGE,\n} from '../defaultValues/routing';\nimport { getStorageAttributes } from '../utils/getStorageAttributes';\n\n// ---------------------------------------------------------------------------\n// Type\n// ---------------------------------------------------------------------------\n\n/**\n * Browser-safe subset of {@link IntlayerConfig}.\n *\n * Excludes server-only fields (`system`, `content`, `build`, `compiler`,\n * `dictionary`, `ai`) and the `editor.clientSecret`, which must never be\n * shipped to the browser.\n *\n * `editor.clientId` is kept: it is the **public** project key, used by\n * `@intlayer/analytics` to identify the project when exchanging it for a\n * short-lived ingest token. It grants nothing on its own — every credentialed\n * backend call needs the secret, which only ever exists server-side.\n */\nexport type BrowserIntlayerConfig = {\n internationalization: Pick<\n InternationalizationConfig,\n 'locales' | 'defaultLocale'\n >;\n routing: RoutingConfig;\n editor: Omit<EditorConfig, 'clientSecret'>;\n analytics: AnalyticsConfig;\n log: Pick<LogConfig, 'mode' | 'prefix'>;\n};\n\ndeclare global {\n interface Window {\n /** Browser-safe Intlayer configuration injected by a build plugin or `installIntlayer`. */\n INTLAYER_CONFIG?: BrowserIntlayerConfig;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared field builders (browser-safe — no Node.js APIs)\n//\n// These functions are re-used by both `buildBrowserConfiguration` (browser)\n// and `buildConfigurationFields` (server) to avoid duplication.\n// ---------------------------------------------------------------------------\n\n/**\n * Build the internationalization section of the Intlayer configuration.\n *\n * @param customConfiguration - Partial user-supplied internationalization config.\n * @returns A fully-defaulted {@link InternationalizationConfig}.\n */\nexport const buildInternationalizationFields = (\n customConfiguration?: Partial<InternationalizationConfig>\n): InternationalizationConfig => ({\n /**\n * Locales available in the application\n *\n * Default: ['en']\n *\n */\n locales: customConfiguration?.locales ?? LOCALES,\n\n /**\n * Locales required by TypeScript to ensure strong implementations of internationalized content using typescript.\n *\n * Default: []\n *\n * If empty, all locales are required in `strict` mode.\n *\n * Ensure required locales are also defined in the `locales` field.\n */\n requiredLocales:\n customConfiguration?.requiredLocales ??\n customConfiguration?.locales ??\n REQUIRED_LOCALES,\n\n /**\n * Ensure strong implementations of internationalized content using typescript.\n * - If set to \"strict\", the translation `t` function will require each declared locales to be defined. If one locale is missing, or if a locale is not declared in your config, it will throw an error.\n * - If set to \"inclusive\", the translation `t` function will require each declared locales to be defined. If one locale is missing, it will throw a warning. But will accept if a locale is not declared in your config, but exist.\n * - If set to \"loose\", the translation `t` function will accept any existing locale.\n *\n * Default: \"inclusive\"\n */\n strictMode: customConfiguration?.strictMode ?? STRICT_MODE,\n\n /**\n * Default locale of the application for fallback\n *\n * Default: 'en'\n */\n defaultLocale: customConfiguration?.defaultLocale ?? DEFAULT_LOCALE,\n});\n\n/**\n * Build the routing section of the Intlayer configuration.\n *\n * @param customConfiguration - Partial user-supplied routing config.\n * @returns A fully-defaulted {@link RoutingConfig}.\n */\nexport const buildRoutingFields = (\n customConfiguration?: Partial<CustomRoutingConfig>\n): RoutingConfig => {\n const storage = customConfiguration?.storage ?? STORAGE;\n\n return {\n /**\n * URL routing mode for locale handling\n *\n * Controls how locales are represented in application URLs:\n * - 'prefix-no-default': Prefix all locales except the default locale (default)\n * - en → /dashboard\n * - fr → /fr/dashboard\n *\n * - 'prefix-all': Prefix all locales including the default locale\n * - en → /en/dashboard\n * - fr → /fr/dashboard\n *\n * - 'search-params': Use search parameters for locale handling\n * - en → /dashboard?locale=en\n * - fr → /fr/dashboard?locale=fr\n *\n * - 'no-prefix': No locale prefixing in URLs\n * - en → /dashboard\n * - fr → /dashboard\n *\n * Default: 'prefix-no-default'\n */\n mode: customConfiguration?.mode ?? ROUTING_MODE,\n\n /**\n * Enables the Intlayer locale-routing proxy (middleware).\n *\n * When enabled, the build-tool integration (e.g. the `intlayer()` Vite plugin)\n * automatically wires the locale-detection / redirect / rewrite middleware in\n * development, preview, and production SSR.\n *\n * - `undefined` (auto, default): the proxy runs, but development and preview\n * servers ignore the stored locale (cookie / header) as a redirect source.\n * - `true`: full proxy behaviour in every environment.\n * - `false`: no locale routing; the proxy is not registered.\n *\n * Default: undefined (auto)\n */\n enableProxy: customConfiguration?.enableProxy ?? ENABLE_PROXY,\n\n /**\n * Configuration for storing the locale in the client (localStorage or sessionStorage)\n *\n * If false, the locale will not be stored by the middleware.\n * If true, the locale storage will consider all default values. (cookie and header)\n *\n * Default: ['cookie', 'header']\n *\n */\n storage: getStorageAttributes(storage),\n\n /**\n * Base path of the application URL\n *\n * Default: ''\n *\n * Example:\n * - If the application is hosted at https://example.com/my-app\n * - The base path is '/my-app'\n * - The URL will be https://example.com/my-app/en\n * - If the base path is not set, the URL will be https://example.com/en\n */\n basePath: customConfiguration?.basePath ?? BASE_PATH,\n\n /**\n * Custom URL rewriting rules that override the default routing mode for specific paths.\n * Allows you to define locale-specific paths that differ from the standard routing behavior.\n * Supports dynamic route parameters using `[param]` syntax.\n *\n * Default: undefined\n *\n * Example:\n * ```typescript\n * rewrite: {\n * \"/about\": {\n * en: \"/about\",\n * fr: \"/a-propos\",\n * },\n * \"/product/[slug]\": {\n * en: \"/product/[slug]\",\n * fr: \"/produit/[slug]\",\n * },\n * }\n * ```\n *\n * Note:\n * - The rewrite rules take precedence over the default `mode` behavior.\n * - If a path matches a rewrite rule, the localized path from the rewrite configuration will be used.\n * - Dynamic route parameters are supported using bracket notation (e.g., `[slug]`, `[id]`).\n * - Works with both Next.js and Vite applications.\n */\n rewrite: customConfiguration?.rewrite,\n\n /**\n * Maps locales to specific domain hostnames for domain-based routing.\n *\n * Default: undefined\n */\n domains: customConfiguration?.domains,\n };\n};\n\n/**\n * Build the editor section of the Intlayer configuration.\n *\n * Returns the **full** {@link EditorConfig} including sensitive fields\n * (`clientId`, `clientSecret`). The browser-safe {@link BrowserIntlayerConfig}\n * omits those fields when exposing config to the client.\n *\n * `clientId` / `clientSecret` are never inferred from the environment: the\n * configuration file is the single source of truth. A project that wants to\n * authenticate from its `.env` file must wire the variables explicitly, e.g.\n * `clientId: process.env.INTLAYER_CLIENT_ID`. Commenting those lines out is\n * therefore enough to disable every credentialed behaviour (remote dictionary\n * fetching, CMS access) even when the variables remain defined in the\n * environment.\n *\n * @param customConfiguration - Partial user-supplied editor config.\n * @returns A fully-defaulted {@link EditorConfig}.\n */\nexport const buildEditorFields = (\n customConfiguration?: Partial<EditorConfig>\n): EditorConfig => {\n const liveSyncPort = customConfiguration?.liveSyncPort ?? LIVE_SYNC_PORT;\n return {\n /**\n * URL of the application. Used to restrict the origin of the editor for security reasons.\n *\n * > '*' means that the editor is accessible from any origin\n *\n * Default: '*'\n */\n applicationURL: customConfiguration?.applicationURL || APPLICATION_URL,\n\n /**\n * URL of the editor server. Used to restrict the origin of the editor for security reasons.\n *\n * > '*' means that the editor is accessible from any origin\n *\n * Default: '*'\n */\n editorURL: customConfiguration?.editorURL || EDITOR_URL,\n\n /**\n * URL of the CMS server. Used to restrict the origin of the editor for security reasons.\n */\n cmsURL: customConfiguration?.cmsURL || CMS_URL,\n\n /**\n * URL of the editor server\n *\n * Default: 'https://back.intlayer.org'\n */\n backendURL: customConfiguration?.backendURL || BACKEND_URL,\n\n /** Port of the editor server\n *\n * Default: 8000\n */\n port: customConfiguration?.port ?? PORT,\n\n /**\n * Indicates if the application interact with the visual editor\n *\n * Default: false;\n *\n * If true, the editor will be able to interact with the application.\n * If false, the editor will not be able to interact with the application.\n * In any case, the editor can only be enabled by the visual editor.\n * Disabling the editor for specific environments is a way to enforce the security.\n *\n * Usage:\n * ```js\n * {\n * // Other configurations\n * editor: {\n * enabled: process.env.NODE_ENV !== 'production',\n * }\n * };\n * ```\n */\n enabled: customConfiguration?.enabled ?? IS_ENABLED,\n\n /**\n * clientId and clientSecret allow the intlayer packages to authenticate with the backend using oAuth2 authentication.\n * An access token is use to authenticate the user related to the project.\n * To get an access token, go to https://app.intlayer.org/project and create an account.\n *\n * Default: undefined\n *\n * > Important: The clientId and clientSecret should be kept secret and not shared publicly. Please ensure to keep them in a secure location, such as environment variables.\n */\n clientId: customConfiguration?.clientId,\n\n /**\n * clientId and clientSecret allow the intlayer packages to authenticate with the backend using oAuth2 authentication.\n * An access token is use to authenticate the user related to the project.\n * To get an access token, go to https://app.intlayer.org/project and create an account.\n *\n * Default: undefined\n *\n * > Important: The clientId and clientSecret should be kept secret and not shared publicly. Please ensure to keep them in a secure location, such as environment variables.\n */\n clientSecret: customConfiguration?.clientSecret,\n\n /**\n * Strategy for prioritizing dictionaries. If a dictionary is both present online and locally, the content will be merge.\n * However, is a field is defined in both dictionary, this setting determines which fields takes the priority over the other.\n *\n * Default: 'local_first'\n *\n * The strategy for prioritizing dictionaries. It can be either 'local_first' or 'distant_first'.\n * - 'local_first': The first dictionary found in the locale is used.\n * - 'distant_first': The first dictionary found in the distant locales is used.\n */\n dictionaryPriorityStrategy:\n customConfiguration?.dictionaryPriorityStrategy ??\n DICTIONARY_PRIORITY_STRATEGY,\n\n /**\n * Indicates if the application should hot reload the locale configurations when a change is detected.\n * For example, when a new dictionary is added or updated, the application will update the content tu display in the page.\n *\n * The hot reload is only available for clients of the `enterprise` plan.\n *\n * Default: false\n */\n liveSync: customConfiguration?.liveSync ?? LIVE_SYNC,\n\n /**\n * Port of the live sync server\n *\n * Default: 4000\n */\n liveSyncPort,\n\n /**\n * URL of the live sync server in case of remote live sync server\n *\n * Default: `http://localhost:${LIVE_SYNC_PORT}`\n */\n liveSyncURL:\n customConfiguration?.liveSyncURL ?? `http://localhost:${liveSyncPort}`,\n };\n};\n\n/**\n * Build the analytics section of the Intlayer configuration.\n *\n * Analytics is opt-out: `enabled` defaults to `true`, so installing the\n * optional `@intlayer/analytics` package is enough to turn it on. The runtime\n * additionally requires a project key (`editor.clientId`) for attribution\n * before collecting anything.\n *\n * An explicit `enabled` value always wins, so a project whose package manager\n * hides the package from the project root can still opt back in.\n *\n * @param customConfiguration - Partial user-supplied analytics config.\n * @param isPackageInstalled - Whether `@intlayer/analytics` is installed in the\n * project. Browser callers cannot resolve packages and pass `true`, letting\n * the runtime dynamic `import()` fail silently when it is absent.\n * @returns A fully-defaulted {@link AnalyticsConfig}.\n */\nexport const buildAnalyticsFields = (\n customConfiguration?: Partial<AnalyticsConfig>,\n isPackageInstalled = true\n): AnalyticsConfig => ({\n /**\n * Enables analytics collection (page views, content exposures, A/B events).\n *\n * Default: true, as soon as `@intlayer/analytics` is installed.\n */\n enabled:\n customConfiguration?.enabled ?? (ANALYTICS_ENABLED && isPackageInstalled),\n\n /**\n * Milliseconds between automatic batched flushes to the backend.\n *\n * Default: 20000\n */\n flushInterval: customConfiguration?.flushInterval ?? ANALYTICS_FLUSH_INTERVAL,\n\n /**\n * Fraction of sessions to record, from 0 (none) to 1 (all).\n *\n * Default: 1\n */\n sampleRate: customConfiguration?.sampleRate ?? ANALYTICS_SAMPLE_RATE,\n});\n\n/**\n * Build the log section of the Intlayer configuration.\n *\n * @param customConfiguration - Partial user-supplied log config.\n * @param logFunctions - Optional custom log function overrides (server-only).\n * @returns A fully-defaulted {@link LogConfig}.\n */\nexport const buildLogFields = (\n customConfiguration?: Partial<LogConfig>,\n logFunctions?: LogFunctions\n): LogConfig => ({\n /**\n * Indicates if the logger is enabled\n *\n * Default: 'prefix-no-default'\n *\n * If 'default', the logger is enabled and can be used.\n * If 'verbose', the logger will be enabled and can be used, but will log more information.\n * If 'disabled', the logger is disabled and cannot be used.\n */\n mode: customConfiguration?.mode ?? MODE,\n\n /**\n * Prefix of the logger\n *\n * Default: '[intlayer]'\n *\n * The prefix of the logger.\n */\n prefix: customConfiguration?.prefix ?? PREFIX,\n\n /**\n * Functions to log\n */\n error: logFunctions?.error,\n log: logFunctions?.log,\n info: logFunctions?.info,\n warn: logFunctions?.warn,\n});\n\n// ---------------------------------------------------------------------------\n// Browser configuration builders\n// ---------------------------------------------------------------------------\n\n/**\n * Build a browser-safe {@link BrowserIntlayerConfig} from a raw user config.\n *\n * Applies defaults for every field and strips all server-only or sensitive\n * information (`system`, `content`, `build`, `compiler`, `dictionary`, `ai`,\n * `editor.clientSecret`). The public `editor.clientId` is kept — see\n * {@link BrowserIntlayerConfig}.\n *\n * This is the browser counterpart of `buildConfigurationFields`. It is safe\n * to call in browser environments because it has no Node.js dependencies.\n *\n * @param customConfig - Optional partial user-supplied Intlayer config.\n * @returns A browser-safe configuration object ready for `window.INTLAYER_CONFIG`.\n *\n * @example\n * ```ts\n * import { buildBrowserConfiguration } from '@intlayer/config/client';\n *\n * window.INTLAYER_CONFIG = buildBrowserConfiguration({\n * internationalization: { locales: ['en', 'fr'], defaultLocale: 'en' },\n * });\n * ```\n */\nexport const buildBrowserConfiguration = (\n customConfig?: CustomIntlayerConfig\n): BrowserIntlayerConfig => {\n const { locales, defaultLocale } = buildInternationalizationFields(\n customConfig?.internationalization\n );\n const routing = buildRoutingFields(customConfig?.routing);\n const { clientSecret: _clientSecret, ...editorPublic } = buildEditorFields(\n customConfig?.editor\n );\n const analytics = buildAnalyticsFields(customConfig?.analytics);\n const { mode, prefix } = buildLogFields(customConfig?.log);\n\n return {\n internationalization: { locales, defaultLocale },\n routing,\n editor: editorPublic,\n analytics,\n log: { mode, prefix },\n };\n};\n\n/**\n * Extract a {@link BrowserIntlayerConfig} from an already-built full\n * {@link IntlayerConfig}.\n *\n * Used by build plugins (`vite-intlayer`, `withIntlayer`) which already hold\n * the full server-side config and need to inject the browser-safe subset at\n * compile time via a bundler `define`.\n *\n * @param config - A fully-built server-side Intlayer configuration.\n * @returns The browser-safe subset of that configuration.\n */\nexport const extractBrowserConfiguration = (\n config: IntlayerConfig\n): BrowserIntlayerConfig => {\n // Destructured rather than picked field by field so that any future addition\n // to `EditorConfig` is carried over automatically, while `clientSecret` stays\n // excluded by construction.\n const { clientSecret: _clientSecret, ...editorPublic } = config.editor;\n\n return {\n internationalization: {\n locales: config.internationalization.locales,\n defaultLocale: config.internationalization.defaultLocale,\n },\n routing: {\n mode: config.routing.mode,\n enableProxy: config.routing.enableProxy,\n storage: config.routing.storage,\n basePath: config.routing.basePath,\n rewrite: config.routing.rewrite,\n },\n editor: editorPublic,\n analytics: buildAnalyticsFields(config.analytics),\n log: {\n mode: config.log.mode,\n prefix: config.log.prefix,\n },\n };\n};\n"],"mappings":";;;;;;;;;;;;;;AAyFA,MAAa,mCACX,yBACgC;;;;;;;CAOhC,SAAS,qBAAqB,WAAW;;;;;;;;;;CAWzC,iBACE,qBAAqB,mBACrB,qBAAqB,WACrB;;;;;;;;;CAUF,YAAY,qBAAqB;;;;;;CAOjC,eAAe,qBAAqB;AACtC;;;;;;;AAQA,MAAa,sBACX,wBACkB;CAClB,MAAM,UAAU,qBAAqB,WAAW;CAEhD,OAAO;;;;;;;;;;;;;;;;;;;;;;;EAuBL,MAAM,qBAAqB;;;;;;;;;;;;;;;EAgB3B,aAAa,qBAAqB;;;;;;;;;;EAWlC,SAAS,qBAAqB,OAAO;;;;;;;;;;;;EAarC,UAAU,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6B/B,SAAS,qBAAqB;;;;;;EAO9B,SAAS,qBAAqB;CAChC;AACF;;;;;;;;;;;;;;;;;;;AAoBA,MAAa,qBACX,wBACiB;CACjB,MAAM,eAAe,qBAAqB;CAC1C,OAAO;;;;;;;;EAQL,gBAAgB,qBAAqB;;;;;;;;EASrC,WAAW,qBAAqB;;;;EAKhC,QAAQ,qBAAqB;;;;;;EAO7B,YAAY,qBAAqB;;;;;EAMjC,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;EAsB3B,SAAS,qBAAqB;;;;;;;;;;EAW9B,UAAU,qBAAqB;;;;;;;;;;EAW/B,cAAc,qBAAqB;;;;;;;;;;;EAYnC,4BACE,qBAAqB;;;;;;;;;EAWvB,UAAU,qBAAqB;;;;;;EAO/B;;;;;;EAOA,aACE,qBAAqB,eAAe,oBAAoB;CAC5D;AACF;;;;;;;;;;;;;;;;;;AAmBA,MAAa,wBACX,qBACA,qBAAqB,UACA;;;;;;CAMrB,SACE,qBAAqB,oBAAiC;;;;;;CAOxD,eAAe,qBAAqB;;;;;;CAOpC,YAAY,qBAAqB;AACnC;;;;;;;;AASA,MAAa,kBACX,qBACA,kBACe;;;;;;;;;;CAUf,MAAM,qBAAqB;;;;;;;;CAS3B,QAAQ,qBAAqB,UAAU;;;;CAKvC,OAAO,cAAc;CACrB,KAAK,cAAc;CACnB,MAAM,cAAc;CACpB,MAAM,cAAc;AACtB;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAa,6BACX,iBAC0B;CAC1B,MAAM,EAAE,SAAS,kBAAkB,gCACjC,cAAc,oBAChB;CACA,MAAM,UAAU,mBAAmB,cAAc,OAAO;CACxD,MAAM,EAAE,cAAc,eAAe,GAAG,iBAAiB,kBACvD,cAAc,MAChB;CACA,MAAM,YAAY,qBAAqB,cAAc,SAAS;CAC9D,MAAM,EAAE,MAAM,WAAW,eAAe,cAAc,GAAG;CAEzD,OAAO;EACL,sBAAsB;GAAE;GAAS;EAAc;EAC/C;EACA,QAAQ;EACR;EACA,KAAK;GAAE;GAAM;EAAO;CACtB;AACF;;;;;;;;;;;;AAaA,MAAa,+BACX,WAC0B;CAI1B,MAAM,EAAE,cAAc,eAAe,GAAG,iBAAiB,OAAO;CAEhE,OAAO;EACL,sBAAsB;GACpB,SAAS,OAAO,qBAAqB;GACrC,eAAe,OAAO,qBAAqB;EAC7C;EACA,SAAS;GACP,MAAM,OAAO,QAAQ;GACrB,aAAa,OAAO,QAAQ;GAC5B,SAAS,OAAO,QAAQ;GACxB,UAAU,OAAO,QAAQ;GACzB,SAAS,OAAO,QAAQ;EAC1B;EACA,QAAQ;EACR,WAAW,qBAAqB,OAAO,SAAS;EAChD,KAAK;GACH,MAAM,OAAO,IAAI;GACjB,QAAQ,OAAO,IAAI;EACrB;CACF;AACF"}
@@ -0,0 +1,20 @@
1
+ //#region src/secrets.browser.ts
2
+ /**
3
+ * Browser stub for {@link file://./secrets.ts}.
4
+ *
5
+ * Selected through the `browser` export condition of
6
+ * `@intlayer/config/secrets`, so a client bundle resolves the credential
7
+ * accessor to an inert function instead of pulling the configuration loader —
8
+ * and the secret itself — into a public asset.
9
+ */
10
+ /**
11
+ * Always `undefined` in the browser: the CMS client secret grants project-wide
12
+ * API access and must never leave the server.
13
+ *
14
+ * @returns `undefined`.
15
+ */
16
+ const getEditorClientSecret = () => void 0;
17
+
18
+ //#endregion
19
+ export { getEditorClientSecret };
20
+ //# sourceMappingURL=secrets.browser.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.browser.mjs","names":[],"sources":["../../src/secrets.browser.ts"],"sourcesContent":["/**\n * Browser stub for {@link file://./secrets.ts}.\n *\n * Selected through the `browser` export condition of\n * `@intlayer/config/secrets`, so a client bundle resolves the credential\n * accessor to an inert function instead of pulling the configuration loader —\n * and the secret itself — into a public asset.\n */\n\n/**\n * Always `undefined` in the browser: the CMS client secret grants project-wide\n * API access and must never leave the server.\n *\n * @returns `undefined`.\n */\nexport const getEditorClientSecret = (): string | undefined => undefined;\n"],"mappings":";;;;;;;;;;;;;;;AAeA,MAAa,8BAAkD"}
@@ -0,0 +1,46 @@
1
+ //#region src/secrets.ts
2
+ /**
3
+ * Server-only access to the sensitive parts of the Intlayer configuration.
4
+ *
5
+ * `@intlayer/config/built` is aliased by every framework integration to a
6
+ * generated file that the bundle **inlines**, and the same alias is installed
7
+ * for the client and the server build. Anything it carries that browser code
8
+ * imports therefore ends up in a public asset, which is why the generated file
9
+ * never contains `editor.clientSecret`.
10
+ *
11
+ * This module is the server-side counterpart. It is deliberately *not* aliased:
12
+ * it resolves through the package's export map, whose `browser` condition
13
+ * points at {@link file://./secrets.browser.ts} — an inert stub. Every
14
+ * major bundler (webpack, Rspack, Turbopack, Vite, Rollup, esbuild, Metro)
15
+ * honours that condition, so a browser build resolves to the stub without any
16
+ * integration-specific configuration.
17
+ *
18
+ * It intentionally imports nothing: pulling the configuration loader in here
19
+ * would drag the whole resolved configuration — and the machinery that builds
20
+ * it — into every server bundle that touches a credentialed API.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * import { getEditorClientSecret } from '@intlayer/config/secrets';
25
+ *
26
+ * const clientSecret = config.editor.clientSecret ?? getEditorClientSecret();
27
+ * ```
28
+ */
29
+ /**
30
+ * Returns the CMS client secret from the environment, or `undefined` when it is
31
+ * not set.
32
+ *
33
+ * The configuration file stays the source of truth for *whether* credentials
34
+ * are active — a project opts in by wiring `clientId` (conventionally
35
+ * `clientId: process.env.INTLAYER_CLIENT_ID`), and callers gate on the public
36
+ * `editor.clientId` from `@intlayer/config/built` before asking for the secret.
37
+ * This function only supplies the confidential half, which the generated
38
+ * configuration deliberately does not carry.
39
+ *
40
+ * @returns The OAuth2 client secret, or `undefined`.
41
+ */
42
+ const getEditorClientSecret = () => typeof process === "undefined" ? void 0 : process.env?.INTLAYER_CLIENT_SECRET;
43
+
44
+ //#endregion
45
+ export { getEditorClientSecret };
46
+ //# sourceMappingURL=secrets.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.mjs","names":[],"sources":["../../src/secrets.ts"],"sourcesContent":["/**\n * Server-only access to the sensitive parts of the Intlayer configuration.\n *\n * `@intlayer/config/built` is aliased by every framework integration to a\n * generated file that the bundle **inlines**, and the same alias is installed\n * for the client and the server build. Anything it carries that browser code\n * imports therefore ends up in a public asset, which is why the generated file\n * never contains `editor.clientSecret`.\n *\n * This module is the server-side counterpart. It is deliberately *not* aliased:\n * it resolves through the package's export map, whose `browser` condition\n * points at {@link file://./secrets.browser.ts} — an inert stub. Every\n * major bundler (webpack, Rspack, Turbopack, Vite, Rollup, esbuild, Metro)\n * honours that condition, so a browser build resolves to the stub without any\n * integration-specific configuration.\n *\n * It intentionally imports nothing: pulling the configuration loader in here\n * would drag the whole resolved configuration — and the machinery that builds\n * it — into every server bundle that touches a credentialed API.\n *\n * @example\n * ```ts\n * import { getEditorClientSecret } from '@intlayer/config/secrets';\n *\n * const clientSecret = config.editor.clientSecret ?? getEditorClientSecret();\n * ```\n */\n\n/**\n * Returns the CMS client secret from the environment, or `undefined` when it is\n * not set.\n *\n * The configuration file stays the source of truth for *whether* credentials\n * are active — a project opts in by wiring `clientId` (conventionally\n * `clientId: process.env.INTLAYER_CLIENT_ID`), and callers gate on the public\n * `editor.clientId` from `@intlayer/config/built` before asking for the secret.\n * This function only supplies the confidential half, which the generated\n * configuration deliberately does not carry.\n *\n * @returns The OAuth2 client secret, or `undefined`.\n */\nexport const getEditorClientSecret = (): string | undefined =>\n typeof process === 'undefined'\n ? undefined\n : process.env?.INTLAYER_CLIENT_SECRET;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,MAAa,8BACX,OAAO,YAAY,cACf,SACA,QAAQ,KAAK"}
@@ -4,13 +4,18 @@ import { AnalyticsConfig, CustomIntlayerConfig, CustomRoutingConfig, EditorConfi
4
4
  * Browser-safe subset of {@link IntlayerConfig}.
5
5
  *
6
6
  * Excludes server-only fields (`system`, `content`, `build`, `compiler`,
7
- * `dictionary`, `ai`) and sensitive editor credentials (`clientId`,
8
- * `clientSecret`) that must never be shipped to the browser.
7
+ * `dictionary`, `ai`) and the `editor.clientSecret`, which must never be
8
+ * shipped to the browser.
9
+ *
10
+ * `editor.clientId` is kept: it is the **public** project key, used by
11
+ * `@intlayer/analytics` to identify the project when exchanging it for a
12
+ * short-lived ingest token. It grants nothing on its own — every credentialed
13
+ * backend call needs the secret, which only ever exists server-side.
9
14
  */
10
15
  type BrowserIntlayerConfig = {
11
16
  internationalization: Pick<InternationalizationConfig, 'locales' | 'defaultLocale'>;
12
17
  routing: RoutingConfig;
13
- editor: Omit<EditorConfig, 'clientId' | 'clientSecret'>;
18
+ editor: Omit<EditorConfig, 'clientSecret'>;
14
19
  analytics: AnalyticsConfig;
15
20
  log: Pick<LogConfig, 'mode' | 'prefix'>;
16
21
  };
@@ -84,7 +89,8 @@ declare const buildLogFields: (customConfiguration?: Partial<LogConfig>, logFunc
84
89
  *
85
90
  * Applies defaults for every field and strips all server-only or sensitive
86
91
  * information (`system`, `content`, `build`, `compiler`, `dictionary`, `ai`,
87
- * `editor.clientId`, `editor.clientSecret`).
92
+ * `editor.clientSecret`). The public `editor.clientId` is kept — see
93
+ * {@link BrowserIntlayerConfig}.
88
94
  *
89
95
  * This is the browser counterpart of `buildConfigurationFields`. It is safe
90
96
  * to call in browser environments because it has no Node.js dependencies.
@@ -1 +1 @@
1
- {"version":3,"file":"buildBrowserConfiguration.d.ts","names":[],"sources":["../../../src/configFile/buildBrowserConfiguration.ts"],"mappings":";;;;;;;;;KAqDY;EACV,sBAAsB,KACpB;EAGF,SAAS;EACT,QAAQ,KAAK;EACb,WAAW;EACX,KAAK,KAAK;;QAGJ;YACI;;IAER,kBAAkB;;;;;;;;;cAiBT,kCAA+B,sBACpB,QAAQ,gCAC7B;;;;;;;cA+CU,qBAAkB,sBACP,QAAQ,yBAC7B;;;;;;;;;;;;;;;;;;;cA4HU,oBAAiB,sBACN,QAAQ,kBAC7B;;;;;;;;;;;;;;;;;;cA6IU,uBAAoB,sBACT,QAAQ,kBAAgB,iCAE7C;;;;;;;;cA+BU,iBAAc,sBACH,QAAQ,YAAU,eACzB,iBACd;;;;;;;;;;;;;;;;;;;;;;;cAwDU,4BAAyB,eACrB,yBACd;;;;;;;;;;;;cAiCU,8BAA2B,QAC9B,mBACP"}
1
+ {"version":3,"file":"buildBrowserConfiguration.d.ts","names":[],"sources":["../../../src/configFile/buildBrowserConfiguration.ts"],"mappings":";;;;;;;;;;;;;;KA0DY;EACV,sBAAsB,KACpB;EAGF,SAAS;EACT,QAAQ,KAAK;EACb,WAAW;EACX,KAAK,KAAK;;QAGJ;YACI;;IAER,kBAAkB;;;;;;;;;cAiBT,kCAA+B,sBACpB,QAAQ,gCAC7B;;;;;;;cA+CU,qBAAkB,sBACP,QAAQ,yBAC7B;;;;;;;;;;;;;;;;;;;cA4HU,oBAAiB,sBACN,QAAQ,kBAC7B;;;;;;;;;;;;;;;;;;cA6IU,uBAAoB,sBACT,QAAQ,kBAAgB,iCAE7C;;;;;;;;cA+BU,iBAAc,sBACH,QAAQ,YAAU,eACzB,iBACd;;;;;;;;;;;;;;;;;;;;;;;;cAyDU,4BAAyB,eACrB,yBACd;;;;;;;;;;;;cA+BU,8BAA2B,QAC9B,mBACP"}
@@ -0,0 +1,19 @@
1
+ //#region src/secrets.browser.d.ts
2
+ /**
3
+ * Browser stub for {@link file://./secrets.ts}.
4
+ *
5
+ * Selected through the `browser` export condition of
6
+ * `@intlayer/config/secrets`, so a client bundle resolves the credential
7
+ * accessor to an inert function instead of pulling the configuration loader —
8
+ * and the secret itself — into a public asset.
9
+ */
10
+ /**
11
+ * Always `undefined` in the browser: the CMS client secret grants project-wide
12
+ * API access and must never leave the server.
13
+ *
14
+ * @returns `undefined`.
15
+ */
16
+ declare const getEditorClientSecret: () => string | undefined;
17
+ //#endregion
18
+ export { getEditorClientSecret };
19
+ //# sourceMappingURL=secrets.browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.browser.d.ts","names":[],"sources":["../../src/secrets.browser.ts"],"mappings":";;;;;;;;;;;;;;;cAea"}
@@ -0,0 +1,45 @@
1
+ //#region src/secrets.d.ts
2
+ /**
3
+ * Server-only access to the sensitive parts of the Intlayer configuration.
4
+ *
5
+ * `@intlayer/config/built` is aliased by every framework integration to a
6
+ * generated file that the bundle **inlines**, and the same alias is installed
7
+ * for the client and the server build. Anything it carries that browser code
8
+ * imports therefore ends up in a public asset, which is why the generated file
9
+ * never contains `editor.clientSecret`.
10
+ *
11
+ * This module is the server-side counterpart. It is deliberately *not* aliased:
12
+ * it resolves through the package's export map, whose `browser` condition
13
+ * points at {@link file://./secrets.browser.ts} — an inert stub. Every
14
+ * major bundler (webpack, Rspack, Turbopack, Vite, Rollup, esbuild, Metro)
15
+ * honours that condition, so a browser build resolves to the stub without any
16
+ * integration-specific configuration.
17
+ *
18
+ * It intentionally imports nothing: pulling the configuration loader in here
19
+ * would drag the whole resolved configuration — and the machinery that builds
20
+ * it — into every server bundle that touches a credentialed API.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * import { getEditorClientSecret } from '@intlayer/config/secrets';
25
+ *
26
+ * const clientSecret = config.editor.clientSecret ?? getEditorClientSecret();
27
+ * ```
28
+ */
29
+ /**
30
+ * Returns the CMS client secret from the environment, or `undefined` when it is
31
+ * not set.
32
+ *
33
+ * The configuration file stays the source of truth for *whether* credentials
34
+ * are active — a project opts in by wiring `clientId` (conventionally
35
+ * `clientId: process.env.INTLAYER_CLIENT_ID`), and callers gate on the public
36
+ * `editor.clientId` from `@intlayer/config/built` before asking for the secret.
37
+ * This function only supplies the confidential half, which the generated
38
+ * configuration deliberately does not carry.
39
+ *
40
+ * @returns The OAuth2 client secret, or `undefined`.
41
+ */
42
+ declare const getEditorClientSecret: () => string | undefined;
43
+ //#endregion
44
+ export { getEditorClientSecret };
45
+ //# sourceMappingURL=secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.d.ts","names":[],"sources":["../../src/secrets.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAyCa"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/config",
3
- "version": "9.4.0-canary.0",
3
+ "version": "9.4.1",
4
4
  "private": false,
5
5
  "description": "Retrieve Intlayer configurations and manage environment variables for both server-side and client-side environments.",
6
6
  "keywords": [
@@ -52,6 +52,15 @@
52
52
  "require": "./dist/cjs/built.cjs",
53
53
  "import": "./dist/esm/built.mjs"
54
54
  },
55
+ "./secrets": {
56
+ "types": "./dist/types/secrets.d.ts",
57
+ "browser": {
58
+ "require": "./dist/cjs/secrets.browser.cjs",
59
+ "import": "./dist/esm/secrets.browser.mjs"
60
+ },
61
+ "require": "./dist/cjs/secrets.cjs",
62
+ "import": "./dist/esm/secrets.mjs"
63
+ },
55
64
  "./callers": {
56
65
  "types": "./dist/types/callers/index.d.ts",
57
66
  "import": "./dist/esm/callers/index.mjs",
@@ -120,6 +129,9 @@
120
129
  "built": [
121
130
  "./dist/types/built.d.ts"
122
131
  ],
132
+ "secrets": [
133
+ "./dist/types/secrets.d.ts"
134
+ ],
123
135
  "node": [
124
136
  "./dist/types/node.d.ts"
125
137
  ],
@@ -177,7 +189,7 @@
177
189
  "typecheck": "tsc --noEmit --project tsconfig.types.json"
178
190
  },
179
191
  "dependencies": {
180
- "@intlayer/types": "9.4.0-canary.0",
192
+ "@intlayer/types": "9.4.1",
181
193
  "defu": "6.1.7",
182
194
  "dotenv": "17.4.2",
183
195
  "esbuild": "0.28.2",
@@ -185,7 +197,7 @@
185
197
  "zod": "4.4.3"
186
198
  },
187
199
  "devDependencies": {
188
- "@types/node": "26.2.0",
200
+ "@types/node": "26.3.0",
189
201
  "@utils/ts-config": "1.0.4",
190
202
  "@utils/ts-config-types": "1.0.4",
191
203
  "@utils/tsdown-config": "1.0.4",