@multiplatform.one/platform 7.7.0 → 7.7.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.
@@ -32,14 +32,15 @@ __export(runtimeConfig_exports, {
32
32
  serializeRuntimePublicConfig: () => serializeRuntimePublicConfig
33
33
  });
34
34
  module.exports = __toCommonJS(runtimeConfig_exports);
35
- const import_meta = {};
36
35
  const runtimePublicConfigKey = "__mp_public_config__";
37
36
  const publicConfigKeysEnvKey = "VITE_MP_PUBLIC_CONFIG_KEYS";
38
37
  const bakedConfigEnvKey = "VITE_MP_CONFIG";
39
- function importMetaEnv() {
38
+ function bakedEnv() {
40
39
  try {
41
- if (typeof import_meta === "undefined") return {};
42
- return import_meta.env || {};
40
+ return {
41
+ VITE_MP_CONFIG: process.env.VITE_MP_CONFIG,
42
+ VITE_MP_PUBLIC_CONFIG_KEYS: process.env.VITE_MP_PUBLIC_CONFIG_KEYS
43
+ };
43
44
  } catch {
44
45
  return {};
45
46
  }
@@ -53,7 +54,7 @@ function processEnv() {
53
54
  }
54
55
  }
55
56
  function readBuildEnv(key) {
56
- return importMetaEnv()[key] ?? processEnv()[key];
57
+ return bakedEnv()[key] ?? processEnv()[key];
57
58
  }
58
59
  function parseJson(raw) {
59
60
  if (!raw) return void 0;
@@ -1,10 +1,12 @@
1
1
  const runtimePublicConfigKey = "__mp_public_config__";
2
2
  const publicConfigKeysEnvKey = "VITE_MP_PUBLIC_CONFIG_KEYS";
3
3
  const bakedConfigEnvKey = "VITE_MP_CONFIG";
4
- function importMetaEnv() {
4
+ function bakedEnv() {
5
5
  try {
6
- if (typeof import.meta === "undefined") return {};
7
- return import.meta.env || {};
6
+ return {
7
+ VITE_MP_CONFIG: process.env.VITE_MP_CONFIG,
8
+ VITE_MP_PUBLIC_CONFIG_KEYS: process.env.VITE_MP_PUBLIC_CONFIG_KEYS
9
+ };
8
10
  } catch {
9
11
  return {};
10
12
  }
@@ -18,7 +20,7 @@ function processEnv() {
18
20
  }
19
21
  }
20
22
  function readBuildEnv(key) {
21
- return importMetaEnv()[key] ?? processEnv()[key];
23
+ return bakedEnv()[key] ?? processEnv()[key];
22
24
  }
23
25
  function parseJson(raw) {
24
26
  if (!raw) return void 0;
@@ -1 +1 @@
1
- {"version":3,"names":["runtimePublicConfigKey","publicConfigKeysEnvKey","bakedConfigEnvKey","importMetaEnv","import","meta","env","processEnv","process","readBuildEnv","key","parseJson","raw","JSON","parse","stringifyValues","source","Object","entries","reduce","acc","value","String","publicConfigKeys","parsed","Array","isArray","filter","length","bakedPublicConfig","resolvePublicConfig","baked","readRuntimePublicConfig","globalThis","safeJsonStringify","stringify","replace","serializeRuntimePublicConfig","config"],"sources":["../../../src/config/runtimeConfig.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["runtimePublicConfigKey","publicConfigKeysEnvKey","bakedConfigEnvKey","bakedEnv","VITE_MP_CONFIG","process","env","VITE_MP_PUBLIC_CONFIG_KEYS","processEnv","readBuildEnv","key","parseJson","raw","JSON","parse","stringifyValues","source","Object","entries","reduce","acc","value","String","publicConfigKeys","parsed","Array","isArray","filter","length","bakedPublicConfig","resolvePublicConfig","baked","readRuntimePublicConfig","globalThis","safeJsonStringify","stringify","replace","serializeRuntimePublicConfig","config"],"sources":["../../../src/config/runtimeConfig.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplatform.one/platform",
3
- "version": "7.7.0",
3
+ "version": "7.7.1",
4
4
  "description": "Platform detection flags and utilities for multiplatform.one",
5
5
  "keywords": [
6
6
  "detection",
@@ -55,7 +55,7 @@
55
55
  "react": "19.2.5",
56
56
  "typescript": "~5.9.3",
57
57
  "vitest": "^4.1.5",
58
- "@multiplatform.one/config": "7.7.0"
58
+ "@multiplatform.one/config": "7.7.1"
59
59
  },
60
60
  "peerDependencies": {
61
61
  "expo-constants": "~55.0.15",
@@ -126,3 +126,36 @@ describe("readRuntimePublicConfig", () => {
126
126
  expect(readRuntimePublicConfig()).toBeUndefined();
127
127
  });
128
128
  });
129
+
130
+ // ---------------------------------------------------------------------------
131
+ // Twin parity (MPO-192)
132
+ // ---------------------------------------------------------------------------
133
+ import { existsSync, readFileSync } from "node:fs";
134
+ import { resolve } from "node:path";
135
+ import * as esm from "./runtimeConfig";
136
+
137
+ describe("runtimeConfig has no import.meta and bakes through static process.env reads", () => {
138
+ // vitest runs this package with cwd at its root and a browser-like
139
+ // environment where import.meta.url is not a file: URL, so anchor on cwd.
140
+ const here = resolve(process.cwd(), "src/config");
141
+ const source = readFileSync(`${here}/runtimeConfig.ts`, "utf8").replace(/\/\*[\s\S]*?\*\//g, "");
142
+
143
+ it("contains no import.meta token, because a classic script (Expo DOM component, Hermes) cannot even parse one", () => {
144
+ expect(source).not.toMatch(/import\.meta/);
145
+ });
146
+
147
+ it("reads each baked key as a static process.env member expression, the only spelling bundlers inline", () => {
148
+ expect(source).toMatch(/process\.env\.VITE_MP_CONFIG\b/);
149
+ expect(source).toMatch(/process\.env\.VITE_MP_PUBLIC_CONFIG_KEYS\b/);
150
+ });
151
+
152
+ it("has no platform twin, because One's web build and tamagui-build resolve .web.ts like Metro and a twin replaced the bake once", () => {
153
+ for (const twin of [
154
+ "runtimeConfig.web.ts",
155
+ "runtimeConfig.native.ts",
156
+ "runtimeConfig.classic.ts",
157
+ ]) {
158
+ expect(existsSync(`${here}/${twin}`), twin).toBe(false);
159
+ }
160
+ });
161
+ });
@@ -1,42 +1,37 @@
1
1
  /**
2
- * RUNTIME public-config seam: SSR document -> browser.
2
+ * Build-time and runtime public config, with no `import.meta` anywhere.
3
3
  *
4
- * Why this exists: `VITE_MP_CONFIG` is resolved by `@multiplatform.one/config`
5
- * at BUILD time (createViteConfig sets `process.env.VITE_MP_CONFIG` from the
6
- * build environment, and vite bakes it into `import.meta.env`). A container
7
- * image built by CI therefore ships whatever the CI job happened to export —
8
- * usually nothing and no amount of runtime container env reaches the
9
- * browser, because `Config._lookupEnv` reads `process.env`, which does not
10
- * exist there. The measured failure: the marketplace storefront had
11
- * MARKETPLACE_API_URL on its container and still rendered "MARKETPLACE_API_URL
12
- * is not configured", issuing ZERO catalog requests.
4
+ * Why no `import.meta`: two bundles execute mpo as a CLASSIC script rather
5
+ * than an ES module, and in a classic script `import.meta` is a PARSE-time
6
+ * SyntaxError, so no `typeof import.meta` guard ever runs: the vxrn/rolldown
7
+ * native bundle for Hermes, and any Metro bundle for platform=web, which is
8
+ * what an Expo DOM component (`'use dom'`) and Expo web are (MPO-192: a
9
+ * knob-driven component inside a DOM component blanked the whole WebView with
10
+ * no diagnostics). A `.web.ts` / `.native.ts` twin is not a way out either:
11
+ * One's web build and tamagui-build both resolve `.web.ts` exactly as Metro
12
+ * does, so a web twin replaced this module in the Vite web build too and the
13
+ * bake vanished there. One file, one behaviour.
13
14
  *
14
- * The fix keeps the image environment-agnostic: the BUILD bakes only the
15
- * NAMES of the public keys (`VITE_MP_PUBLIC_CONFIG_KEYS`), and the SSR node
16
- * process resolves their VALUES from its own `process.env` at request time and
17
- * publishes them into the served document.
18
- *
19
- * Transport: one inline `<script>` in `<head>` assigning a `globalThis` key —
20
- * the SAME mechanism one uses for `__one_server_context__` (see one's
21
- * `server/ServerContextScript`), deliberately sitting ALONGSIDE it rather
22
- * than being a second, differently-shaped invention.
15
+ * Baking: `createViteConfig` sets `VITE_MP_CONFIG` (values) and
16
+ * `VITE_MP_PUBLIC_CONFIG_KEYS` (names) on `process.env` and defines both as
17
+ * `process.env.<KEY>` for the bundle; `bakedEnv` below reads exactly those two
18
+ * static expressions. The SSR document path (`readRuntimePublicConfig`) is
19
+ * unchanged: the node process resolves the allowlisted names from its own
20
+ * environment at request time and publishes them under
21
+ * `globalThis.__mp_public_config__`.
23
22
  *
24
23
  * Safety: `resolvePublicConfig` walks the BUILD-TIME PUBLIC ALLOWLIST, never
25
- * the environment. A key that is not on `config.json`'s `public` list can not
26
- * be serialized even if it is present in the SSR process env, so server-only
27
- * secrets never reach the document.
24
+ * the environment, so a private key present in `env` can not reach the
25
+ * returned object.
28
26
  */
29
27
 
30
- /**
31
- * Global key the SSR document assigns the resolved public config to.
32
- * Sibling of one's `__one_server_context__`.
33
- */
28
+ /** Global the SSR document publishes the resolved public config under. */
34
29
  export const runtimePublicConfigKey = "__mp_public_config__";
35
30
 
36
31
  /**
37
- * Build-time env key carrying the JSON array of PUBLIC config key names
38
- * (written by `createViteConfig` in `@multiplatform.one/config/vite`).
39
- * Only the NAMES are baked values stay environment-agnostic.
32
+ * Build-time env key carrying the JSON array of PUBLIC config key names. Names
33
+ * only, never a value: the SSR node process resolves values from its own
34
+ * environment at request time, so one build deploys to every environment.
40
35
  */
41
36
  export const publicConfigKeysEnvKey = "VITE_MP_PUBLIC_CONFIG_KEYS";
42
37
 
@@ -47,10 +42,23 @@ export const publicConfigKeysEnvKey = "VITE_MP_PUBLIC_CONFIG_KEYS";
47
42
  */
48
43
  export const bakedConfigEnvKey = "VITE_MP_CONFIG";
49
44
 
50
- function importMetaEnv(): Record<string, string | undefined> {
45
+ /**
46
+ * The two baked keys, read as STATIC `process.env.<KEY>` member expressions.
47
+ * That exact spelling is what every bundler on the way to a browser inlines:
48
+ * `createViteConfig` defines both keys (like `process.env.VITE_FRAPPE_ENABLED`),
49
+ * vxrn's loadEnv under One defines every public key the same way, and Metro's
50
+ * babel does the same for `EXPO_PUBLIC_*`. A dynamic `env[key]` read is never
51
+ * inlined, and One's client build folds `typeof process` to "undefined", so
52
+ * anything that consults the environment first returns `{}` before the bake
53
+ * is ever seen. Where nothing defined the keys and `process` does not exist,
54
+ * the reads throw and the catch returns `{}`.
55
+ */
56
+ function bakedEnv(): Record<string, string | undefined> {
51
57
  try {
52
- if (typeof import.meta === "undefined") return {};
53
- return (import.meta.env || {}) as Record<string, string | undefined>;
58
+ return {
59
+ VITE_MP_CONFIG: process.env.VITE_MP_CONFIG,
60
+ VITE_MP_PUBLIC_CONFIG_KEYS: process.env.VITE_MP_PUBLIC_CONFIG_KEYS,
61
+ };
54
62
  } catch {
55
63
  return {};
56
64
  }
@@ -68,7 +76,7 @@ function processEnv(): Record<string, string | undefined> {
68
76
  }
69
77
 
70
78
  function readBuildEnv(key: string): string | undefined {
71
- return importMetaEnv()[key] ?? processEnv()[key];
79
+ return bakedEnv()[key] ?? processEnv()[key];
72
80
  }
73
81
 
74
82
  function parseJson(raw: string | undefined): unknown {
@@ -89,7 +97,7 @@ function stringifyValues(source: Record<string, unknown>): Record<string, string
89
97
 
90
98
  /**
91
99
  * The PUBLIC key allowlist baked at build time. Empty when the app was not
92
- * built through `createViteConfig` (tests, storybook, plain node) in which
100
+ * built through `createViteConfig` (tests, storybook, plain node), in which
93
101
  * case nothing is publishable and the payload stays empty, which is the safe
94
102
  * direction to fail.
95
103
  */
@@ -109,8 +117,8 @@ export function bakedPublicConfig(): Record<string, string> {
109
117
  /**
110
118
  * Resolve the public config the SSR document should publish.
111
119
  *
112
- * Iterates the ALLOWLIST never the environment so a private key present
113
- * in `env` can not reach the returned object. Runtime env wins over the
120
+ * Iterates the ALLOWLIST, never the environment, so a private key present in
121
+ * `env` can not reach the returned object. Runtime env wins over the
114
122
  * build-time bake, so a redeployed container overrides a stale baked value.
115
123
  */
116
124
  export function resolvePublicConfig(
@@ -124,21 +132,13 @@ export function resolvePublicConfig(
124
132
  }, {});
125
133
  }
126
134
 
127
- /**
128
- * Read the payload the SSR document published. Returns undefined anywhere the
129
- * document never ran (native, SSR itself, tests).
130
- */
135
+ /** The config the served document published, when there is one. */
131
136
  export function readRuntimePublicConfig(): Record<string, string> | undefined {
132
137
  const raw = (globalThis as unknown as Record<string, unknown>)[runtimePublicConfigKey];
133
138
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return undefined;
134
139
  return stringifyValues(raw as Record<string, unknown>);
135
140
  }
136
141
 
137
- /**
138
- * HTML-safe JSON: an inline `<script>` body must not be able to close its own
139
- * tag, and U+2028/U+2029 are line terminators to a JS parser but legal inside
140
- * a JSON string.
141
- */
142
142
  function safeJsonStringify(value: unknown): string {
143
143
  return JSON.stringify(value ?? {})
144
144
  .replace(/</g, "\\u003c")
@@ -147,11 +147,7 @@ function safeJsonStringify(value: unknown): string {
147
147
  .replace(/\u2029/g, "\\u2029");
148
148
  }
149
149
 
150
- /**
151
- * The inline `<script>` body that publishes the public config to the browser.
152
- * Runs during document parse, so it lands before the deferred app bundle
153
- * constructs its `Config`.
154
- */
150
+ /** The inline script the SSR document carries so the client can read the config. */
155
151
  export function serializeRuntimePublicConfig(
156
152
  config: Record<string, string> = resolvePublicConfig(),
157
153
  ): string {
@@ -1,40 +1,35 @@
1
1
  /**
2
- * RUNTIME public-config seam: SSR document -> browser.
2
+ * Build-time and runtime public config, with no `import.meta` anywhere.
3
3
  *
4
- * Why this exists: `VITE_MP_CONFIG` is resolved by `@multiplatform.one/config`
5
- * at BUILD time (createViteConfig sets `process.env.VITE_MP_CONFIG` from the
6
- * build environment, and vite bakes it into `import.meta.env`). A container
7
- * image built by CI therefore ships whatever the CI job happened to export —
8
- * usually nothing and no amount of runtime container env reaches the
9
- * browser, because `Config._lookupEnv` reads `process.env`, which does not
10
- * exist there. The measured failure: the marketplace storefront had
11
- * MARKETPLACE_API_URL on its container and still rendered "MARKETPLACE_API_URL
12
- * is not configured", issuing ZERO catalog requests.
4
+ * Why no `import.meta`: two bundles execute mpo as a CLASSIC script rather
5
+ * than an ES module, and in a classic script `import.meta` is a PARSE-time
6
+ * SyntaxError, so no `typeof import.meta` guard ever runs: the vxrn/rolldown
7
+ * native bundle for Hermes, and any Metro bundle for platform=web, which is
8
+ * what an Expo DOM component (`'use dom'`) and Expo web are (MPO-192: a
9
+ * knob-driven component inside a DOM component blanked the whole WebView with
10
+ * no diagnostics). A `.web.ts` / `.native.ts` twin is not a way out either:
11
+ * One's web build and tamagui-build both resolve `.web.ts` exactly as Metro
12
+ * does, so a web twin replaced this module in the Vite web build too and the
13
+ * bake vanished there. One file, one behaviour.
13
14
  *
14
- * The fix keeps the image environment-agnostic: the BUILD bakes only the
15
- * NAMES of the public keys (`VITE_MP_PUBLIC_CONFIG_KEYS`), and the SSR node
16
- * process resolves their VALUES from its own `process.env` at request time and
17
- * publishes them into the served document.
18
- *
19
- * Transport: one inline `<script>` in `<head>` assigning a `globalThis` key —
20
- * the SAME mechanism one uses for `__one_server_context__` (see one's
21
- * `server/ServerContextScript`), deliberately sitting ALONGSIDE it rather
22
- * than being a second, differently-shaped invention.
15
+ * Baking: `createViteConfig` sets `VITE_MP_CONFIG` (values) and
16
+ * `VITE_MP_PUBLIC_CONFIG_KEYS` (names) on `process.env` and defines both as
17
+ * `process.env.<KEY>` for the bundle; `bakedEnv` below reads exactly those two
18
+ * static expressions. The SSR document path (`readRuntimePublicConfig`) is
19
+ * unchanged: the node process resolves the allowlisted names from its own
20
+ * environment at request time and publishes them under
21
+ * `globalThis.__mp_public_config__`.
23
22
  *
24
23
  * Safety: `resolvePublicConfig` walks the BUILD-TIME PUBLIC ALLOWLIST, never
25
- * the environment. A key that is not on `config.json`'s `public` list can not
26
- * be serialized even if it is present in the SSR process env, so server-only
27
- * secrets never reach the document.
28
- */
29
- /**
30
- * Global key the SSR document assigns the resolved public config to.
31
- * Sibling of one's `__one_server_context__`.
24
+ * the environment, so a private key present in `env` can not reach the
25
+ * returned object.
32
26
  */
27
+ /** Global the SSR document publishes the resolved public config under. */
33
28
  export declare const runtimePublicConfigKey = "__mp_public_config__";
34
29
  /**
35
- * Build-time env key carrying the JSON array of PUBLIC config key names
36
- * (written by `createViteConfig` in `@multiplatform.one/config/vite`).
37
- * Only the NAMES are baked values stay environment-agnostic.
30
+ * Build-time env key carrying the JSON array of PUBLIC config key names. Names
31
+ * only, never a value: the SSR node process resolves values from its own
32
+ * environment at request time, so one build deploys to every environment.
38
33
  */
39
34
  export declare const publicConfigKeysEnvKey = "VITE_MP_PUBLIC_CONFIG_KEYS";
40
35
  /**
@@ -45,7 +40,7 @@ export declare const publicConfigKeysEnvKey = "VITE_MP_PUBLIC_CONFIG_KEYS";
45
40
  export declare const bakedConfigEnvKey = "VITE_MP_CONFIG";
46
41
  /**
47
42
  * The PUBLIC key allowlist baked at build time. Empty when the app was not
48
- * built through `createViteConfig` (tests, storybook, plain node) in which
43
+ * built through `createViteConfig` (tests, storybook, plain node), in which
49
44
  * case nothing is publishable and the payload stays empty, which is the safe
50
45
  * direction to fail.
51
46
  */
@@ -55,20 +50,13 @@ export declare function bakedPublicConfig(): Record<string, string>;
55
50
  /**
56
51
  * Resolve the public config the SSR document should publish.
57
52
  *
58
- * Iterates the ALLOWLIST never the environment so a private key present
59
- * in `env` can not reach the returned object. Runtime env wins over the
53
+ * Iterates the ALLOWLIST, never the environment, so a private key present in
54
+ * `env` can not reach the returned object. Runtime env wins over the
60
55
  * build-time bake, so a redeployed container overrides a stale baked value.
61
56
  */
62
57
  export declare function resolvePublicConfig(env?: Record<string, string | undefined>): Record<string, string>;
63
- /**
64
- * Read the payload the SSR document published. Returns undefined anywhere the
65
- * document never ran (native, SSR itself, tests).
66
- */
58
+ /** The config the served document published, when there is one. */
67
59
  export declare function readRuntimePublicConfig(): Record<string, string> | undefined;
68
- /**
69
- * The inline `<script>` body that publishes the public config to the browser.
70
- * Runs during document parse, so it lands before the deferred app bundle
71
- * constructs its `Config`.
72
- */
60
+ /** The inline script the SSR document carries so the client can read the config. */
73
61
  export declare function serializeRuntimePublicConfig(config?: Record<string, string>): string;
74
62
  //# sourceMappingURL=runtimeConfig.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtimeConfig.d.ts","sourceRoot":"","sources":["../../src/config/runtimeConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH;;;GAGG;AACH,eAAO,MAAM,sBAAsB,yBAAyB,CAAC;AAE7D;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,+BAA+B,CAAC;AAEnE;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,mBAAmB,CAAC;AA0ClD;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAI3C;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAI1D;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAgB,GACrD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOxB;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAI5E;AAeD;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAyB,GACrD,MAAM,CAER"}
1
+ {"version":3,"file":"runtimeConfig.d.ts","sourceRoot":"","sources":["../../src/config/runtimeConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,0EAA0E;AAC1E,eAAO,MAAM,sBAAsB,yBAAyB,CAAC;AAE7D;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,+BAA+B,CAAC;AAEnE;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,mBAAmB,CAAC;AAuDlD;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAI3C;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAI1D;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAgB,GACrD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOxB;AAED,mEAAmE;AACnE,wBAAgB,uBAAuB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAI5E;AAUD,oFAAoF;AACpF,wBAAgB,4BAA4B,CAC1C,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAyB,GACrD,MAAM,CAER"}
@@ -1,84 +0,0 @@
1
- /**
2
- * Native twin of `runtimeConfig.ts`.
3
- *
4
- * Identical API, minus `import.meta`: the vxrn/rolldown native bundle is a
5
- * CLASSIC script for Hermes, where `import.meta` is a syntax error (the same
6
- * reason `config.native.ts` exists as a hand-written twin of `config.ts`).
7
- *
8
- * Native has no SSR document, so `readRuntimePublicConfig` normally returns
9
- * undefined and native keeps resolving through `expo-constants` +
10
- * `process.env` exactly as before. The exports are kept so that shared code
11
- * importing from `@multiplatform.one/platform` compiles on every target.
12
- */
13
-
14
- export const runtimePublicConfigKey = "__mp_public_config__";
15
- export const publicConfigKeysEnvKey = "VITE_MP_PUBLIC_CONFIG_KEYS";
16
- export const bakedConfigEnvKey = "VITE_MP_CONFIG";
17
-
18
- function processEnv(): Record<string, string | undefined> {
19
- try {
20
- if (typeof process === "undefined") return {};
21
- return (process.env || {}) as Record<string, string | undefined>;
22
- } catch {
23
- return {};
24
- }
25
- }
26
-
27
- function parseJson(raw: string | undefined): unknown {
28
- if (!raw) return undefined;
29
- try {
30
- return JSON.parse(raw);
31
- } catch {
32
- return undefined;
33
- }
34
- }
35
-
36
- function stringifyValues(source: Record<string, unknown>): Record<string, string> {
37
- return Object.entries(source).reduce<Record<string, string>>((acc, [key, value]) => {
38
- if (typeof value !== "undefined" && value !== null) acc[key] = String(value);
39
- return acc;
40
- }, {});
41
- }
42
-
43
- export function publicConfigKeys(): string[] {
44
- const parsed = parseJson(processEnv()[publicConfigKeysEnvKey]);
45
- if (!Array.isArray(parsed)) return [];
46
- return parsed.filter((key): key is string => typeof key === "string" && key.length > 0);
47
- }
48
-
49
- export function bakedPublicConfig(): Record<string, string> {
50
- const parsed = parseJson(processEnv()[bakedConfigEnvKey]);
51
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
52
- return stringifyValues(parsed as Record<string, unknown>);
53
- }
54
-
55
- export function resolvePublicConfig(
56
- env: Record<string, string | undefined> = processEnv(),
57
- ): Record<string, string> {
58
- const baked = bakedPublicConfig();
59
- return publicConfigKeys().reduce<Record<string, string>>((acc, key) => {
60
- const value = env[key] ?? baked[key];
61
- if (typeof value === "string" && value.length > 0) acc[key] = value;
62
- return acc;
63
- }, {});
64
- }
65
-
66
- export function readRuntimePublicConfig(): Record<string, string> | undefined {
67
- const raw = (globalThis as unknown as Record<string, unknown>)[runtimePublicConfigKey];
68
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return undefined;
69
- return stringifyValues(raw as Record<string, unknown>);
70
- }
71
-
72
- function safeJsonStringify(value: unknown): string {
73
- return JSON.stringify(value ?? {})
74
- .replace(/</g, "\\u003c")
75
- .replace(/>/g, "\\u003e")
76
- .replace(/\u2028/g, "\\u2028")
77
- .replace(/\u2029/g, "\\u2029");
78
- }
79
-
80
- export function serializeRuntimePublicConfig(
81
- config: Record<string, string> = resolvePublicConfig(),
82
- ): string {
83
- return `globalThis[${JSON.stringify(runtimePublicConfigKey)}]=${safeJsonStringify(config)};`;
84
- }
@@ -1,21 +0,0 @@
1
- /**
2
- * Native twin of `runtimeConfig.ts`.
3
- *
4
- * Identical API, minus `import.meta`: the vxrn/rolldown native bundle is a
5
- * CLASSIC script for Hermes, where `import.meta` is a syntax error (the same
6
- * reason `config.native.ts` exists as a hand-written twin of `config.ts`).
7
- *
8
- * Native has no SSR document, so `readRuntimePublicConfig` normally returns
9
- * undefined and native keeps resolving through `expo-constants` +
10
- * `process.env` exactly as before. The exports are kept so that shared code
11
- * importing from `@multiplatform.one/platform` compiles on every target.
12
- */
13
- export declare const runtimePublicConfigKey = "__mp_public_config__";
14
- export declare const publicConfigKeysEnvKey = "VITE_MP_PUBLIC_CONFIG_KEYS";
15
- export declare const bakedConfigEnvKey = "VITE_MP_CONFIG";
16
- export declare function publicConfigKeys(): string[];
17
- export declare function bakedPublicConfig(): Record<string, string>;
18
- export declare function resolvePublicConfig(env?: Record<string, string | undefined>): Record<string, string>;
19
- export declare function readRuntimePublicConfig(): Record<string, string> | undefined;
20
- export declare function serializeRuntimePublicConfig(config?: Record<string, string>): string;
21
- //# sourceMappingURL=runtimeConfig.native.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"runtimeConfig.native.d.ts","sourceRoot":"","sources":["../../src/config/runtimeConfig.native.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,sBAAsB,yBAAyB,CAAC;AAC7D,eAAO,MAAM,sBAAsB,+BAA+B,CAAC;AACnE,eAAO,MAAM,iBAAiB,mBAAmB,CAAC;AA2BlD,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAI3C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAI1D;AAED,wBAAgB,mBAAmB,CACjC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAgB,GACrD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOxB;AAED,wBAAgB,uBAAuB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAI5E;AAUD,wBAAgB,4BAA4B,CAC1C,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAyB,GACrD,MAAM,CAER"}