@dxos/config 0.8.4-main.fffef41 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/browser/chunk-VQRJY4AQ.mjs +11 -0
  4. package/dist/lib/browser/chunk-VQRJY4AQ.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-ZY4D4E3Q.mjs +146 -0
  6. package/dist/lib/browser/chunk-ZY4D4E3Q.mjs.map +7 -0
  7. package/dist/lib/browser/index.mjs +117 -211
  8. package/dist/lib/browser/index.mjs.map +4 -4
  9. package/dist/lib/browser/loaders/browser.mjs +73 -0
  10. package/dist/lib/browser/loaders/browser.mjs.map +7 -0
  11. package/dist/lib/browser/loaders/index.mjs +66 -0
  12. package/dist/lib/browser/loaders/index.mjs.map +7 -0
  13. package/dist/lib/browser/meta.json +1 -1
  14. package/dist/lib/browser/plugin/browser.mjs +8 -0
  15. package/dist/lib/browser/plugin/browser.mjs.map +7 -0
  16. package/dist/lib/browser/plugin/index.mjs +98 -0
  17. package/dist/lib/browser/plugin/index.mjs.map +7 -0
  18. package/dist/lib/browser/savers/browser.mjs +25 -0
  19. package/dist/lib/browser/savers/browser.mjs.map +7 -0
  20. package/dist/lib/browser/savers/index.mjs +7 -0
  21. package/dist/lib/browser/savers/index.mjs.map +7 -0
  22. package/dist/lib/node-esm/chunk-T6NFH3CC.mjs +13 -0
  23. package/dist/lib/node-esm/chunk-T6NFH3CC.mjs.map +7 -0
  24. package/dist/lib/node-esm/chunk-WIJROCKQ.mjs +148 -0
  25. package/dist/lib/node-esm/chunk-WIJROCKQ.mjs.map +7 -0
  26. package/dist/lib/node-esm/index.mjs +115 -303
  27. package/dist/lib/node-esm/index.mjs.map +4 -4
  28. package/dist/lib/node-esm/loaders/browser.mjs +75 -0
  29. package/dist/lib/node-esm/loaders/browser.mjs.map +7 -0
  30. package/dist/lib/node-esm/loaders/index.mjs +67 -0
  31. package/dist/lib/node-esm/loaders/index.mjs.map +7 -0
  32. package/dist/lib/node-esm/meta.json +1 -1
  33. package/dist/lib/node-esm/plugin/browser.mjs +10 -0
  34. package/dist/lib/node-esm/plugin/browser.mjs.map +7 -0
  35. package/dist/lib/node-esm/plugin/index.mjs +99 -0
  36. package/dist/lib/node-esm/plugin/index.mjs.map +7 -0
  37. package/dist/lib/node-esm/savers/browser.mjs +27 -0
  38. package/dist/lib/node-esm/savers/browser.mjs.map +7 -0
  39. package/dist/lib/node-esm/savers/index.mjs +9 -0
  40. package/dist/lib/node-esm/savers/index.mjs.map +7 -0
  41. package/dist/plugin/node-esm/{chunk-C4YHC2RN.mjs → chunk-XCZNYAQT.mjs} +17 -30
  42. package/dist/plugin/node-esm/chunk-XCZNYAQT.mjs.map +7 -0
  43. package/dist/plugin/node-esm/esbuild-plugin.mjs +1 -1
  44. package/dist/plugin/node-esm/meta.json +1 -1
  45. package/dist/plugin/node-esm/rollup-plugin.mjs +1 -1
  46. package/dist/plugin/node-esm/vite-plugin.mjs +1 -1
  47. package/dist/plugin/node-esm/vite-plugin.mjs.map +2 -2
  48. package/dist/types/src/config-service.d.ts +19 -0
  49. package/dist/types/src/config-service.d.ts.map +1 -0
  50. package/dist/types/src/config.d.ts +0 -1
  51. package/dist/types/src/config.d.ts.map +1 -1
  52. package/dist/types/src/index.d.ts +5 -3
  53. package/dist/types/src/index.d.ts.map +1 -1
  54. package/dist/types/src/loaders/browser.d.ts +11 -15
  55. package/dist/types/src/loaders/browser.d.ts.map +1 -1
  56. package/dist/types/src/loaders/index.d.ts.map +1 -1
  57. package/dist/types/src/plugin/browser.d.ts +3 -0
  58. package/dist/types/src/plugin/browser.d.ts.map +1 -0
  59. package/dist/types/src/plugin/definitions.d.ts.map +1 -1
  60. package/dist/types/src/plugin/esbuild-plugin.d.ts.map +1 -1
  61. package/dist/types/src/plugin/rollup-plugin.d.ts.map +1 -1
  62. package/dist/types/src/plugin/vite-plugin.d.ts.map +1 -1
  63. package/dist/types/src/preset.d.ts +1 -1
  64. package/dist/types/src/preset.d.ts.map +1 -1
  65. package/dist/types/src/savers/browser.d.ts +2 -1
  66. package/dist/types/src/savers/browser.d.ts.map +1 -1
  67. package/dist/types/src/savers/index.d.ts.map +1 -1
  68. package/dist/types/src/telemetry.d.ts +17 -0
  69. package/dist/types/src/telemetry.d.ts.map +1 -0
  70. package/dist/types/src/testing/defaults.d.ts +6 -6
  71. package/dist/types/src/testing/defaults.d.ts.map +1 -1
  72. package/dist/types/src/testing/envs-map.d.ts +12 -14
  73. package/dist/types/src/testing/envs-map.d.ts.map +1 -1
  74. package/dist/types/tsconfig.tsbuildinfo +1 -1
  75. package/package.json +50 -36
  76. package/src/config-service.ts +116 -0
  77. package/src/config.ts +4 -7
  78. package/src/index.ts +5 -3
  79. package/src/loaders/{browser.js → browser.ts} +15 -9
  80. package/src/loaders/index.ts +2 -3
  81. package/src/plugin/browser.ts +11 -0
  82. package/src/plugin/definitions.ts +2 -4
  83. package/src/plugin/vite-plugin.ts +0 -1
  84. package/src/preset.ts +2 -1
  85. package/src/savers/{browser.js → browser.ts} +3 -3
  86. package/src/telemetry.ts +25 -0
  87. package/src/testing/build.js +2 -1
  88. package/dist/plugin/node-esm/chunk-C4YHC2RN.mjs.map +0 -7
package/package.json CHANGED
@@ -1,13 +1,46 @@
1
1
  {
2
2
  "name": "@dxos/config",
3
- "version": "0.8.4-main.fffef41",
3
+ "version": "0.9.0",
4
4
  "description": "Config utilities",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
- "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
11
+ "license": "FSL-1.1-Apache-2.0",
8
12
  "author": "DXOS.org",
9
- "sideEffects": true,
13
+ "sideEffects": false,
10
14
  "type": "module",
15
+ "imports": {
16
+ "#loaders": {
17
+ "source": {
18
+ "browser": "./src/loaders/browser.ts",
19
+ "default": "./src/loaders/index.ts"
20
+ },
21
+ "types": "./dist/types/src/loaders/index.d.ts",
22
+ "browser": "./dist/lib/browser/loaders/browser.mjs",
23
+ "default": "./dist/lib/node-esm/loaders/index.mjs"
24
+ },
25
+ "#savers": {
26
+ "source": {
27
+ "browser": "./src/savers/browser.ts",
28
+ "default": "./src/savers/index.ts"
29
+ },
30
+ "types": "./dist/types/src/savers/index.d.ts",
31
+ "browser": "./dist/lib/browser/savers/browser.mjs",
32
+ "default": "./dist/lib/node-esm/savers/index.mjs"
33
+ },
34
+ "#plugin": {
35
+ "source": {
36
+ "browser": "./src/plugin/browser.ts",
37
+ "default": "./src/plugin/index.ts"
38
+ },
39
+ "types": "./dist/types/src/plugin/index.d.ts",
40
+ "browser": "./dist/lib/browser/plugin/browser.mjs",
41
+ "default": "./dist/lib/node-esm/plugin/index.mjs"
42
+ }
43
+ },
11
44
  "exports": {
12
45
  ".": {
13
46
  "source": "./src/index.ts",
@@ -19,69 +52,50 @@
19
52
  }
20
53
  },
21
54
  "./esbuild-plugin": {
22
- "source": "./src/esbuild-plugin/index.ts",
55
+ "source": "./src/plugin/esbuild-plugin.ts",
23
56
  "types": "./dist/types/src/esbuild-plugin/index.d.ts",
24
57
  "import": "./dist/plugin/node-esm/esbuild-plugin.mjs",
25
58
  "require": "./dist/plugin/node/esbuild-plugin.cjs"
26
59
  },
27
60
  "./rollup-plugin": {
28
- "source": "./src/rollup-plugin/index.ts",
61
+ "source": "./src/plugin/rollup-plugin.ts",
29
62
  "types": "./dist/types/src/rollup-plugin/index.d.ts",
30
63
  "import": "./dist/plugin/node-esm/rollup-plugin.mjs",
31
64
  "require": "./dist/plugin/node/rollup-plugin.cjs"
32
65
  },
33
66
  "./vite-plugin": {
34
- "source": "./src/vite-plugin/index.ts",
67
+ "source": "./src/plugin/vite-plugin.ts",
35
68
  "types": "./dist/types/src/vite-plugin/index.d.ts",
36
69
  "import": "./dist/plugin/node-esm/vite-plugin.mjs",
37
70
  "require": "./dist/plugin/node/vite-plugin.cjs"
38
71
  }
39
72
  },
40
73
  "main": "dist/lib/node/index.cjs",
41
- "browser": {
42
- "./src/loaders/index.ts": "./src/loaders/browser.js",
43
- "./src/savers/index.ts": "./src/savers/browser.js",
44
- "./src/plugin/index.ts": false,
45
- "./dist/lib/node/index.cjs": "./dist/lib/browser/index.mjs"
46
- },
47
74
  "types": "dist/types/src/index.d.ts",
48
- "typesVersions": {
49
- "*": {
50
- "esbuild-plugin": [
51
- "dist/types/src/esbuild-plugin/index.d.ts"
52
- ],
53
- "rollup-plugin": [
54
- "dist/types/src/rollup-plugin/index.d.ts"
55
- ],
56
- "vite-plugin": [
57
- "dist/types/src/vite-plugin/index.d.ts"
58
- ]
59
- }
60
- },
61
75
  "files": [
62
76
  "dist",
63
77
  "src"
64
78
  ],
65
79
  "dependencies": {
66
- "boolean": "^3.0.1",
67
- "effect": "3.18.3",
68
- "js-yaml": "^4.1.0",
80
+ "@effect/platform": "0.96.1",
81
+ "effect": "3.21.3",
69
82
  "localforage": "^1.10.0",
70
83
  "lodash.defaultsdeep": "^4.6.1",
71
84
  "lodash.ismatch": "^4.4.0",
72
85
  "pkg-up": "^3.1.0",
73
- "@dxos/invariant": "0.8.4-main.fffef41",
74
- "@dxos/log": "0.8.4-main.fffef41",
75
- "@dxos/node-std": "0.8.4-main.fffef41",
76
- "@dxos/protocols": "0.8.4-main.fffef41",
77
- "@dxos/tracing": "0.8.4-main.fffef41",
78
- "@dxos/util": "0.8.4-main.fffef41"
86
+ "yaml": "^2.8.2",
87
+ "@dxos/invariant": "0.9.0",
88
+ "@dxos/log": "0.9.0",
89
+ "@dxos/node-std": "0.9.0",
90
+ "@dxos/client-protocol": "0.9.0",
91
+ "@dxos/protocols": "0.9.0",
92
+ "@dxos/tracing": "0.9.0",
93
+ "@dxos/util": "0.9.0"
79
94
  },
80
95
  "devDependencies": {
81
- "@types/js-yaml": "^4.0.5",
82
96
  "@types/lodash.defaultsdeep": "^4.6.6",
83
97
  "@types/lodash.ismatch": "^4.4.9",
84
- "webpack": "^5.88.2"
98
+ "webpack": "^5.104.1"
85
99
  },
86
100
  "publishConfig": {
87
101
  "access": "public"
@@ -0,0 +1,116 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as FileSystem from '@effect/platform/FileSystem';
6
+ import * as Context from 'effect/Context';
7
+ import * as Effect from 'effect/Effect';
8
+ import * as Layer from 'effect/Layer';
9
+ import * as Option from 'effect/Option';
10
+ import { dirname } from 'node:path';
11
+ import * as Yaml from 'yaml';
12
+
13
+ import { DX_CONFIG, DX_DATA, getProfileConfigPath, getProfilePath } from '@dxos/client-protocol';
14
+ import { invariant } from '@dxos/invariant';
15
+
16
+ import { Config } from './config';
17
+
18
+ export const memoryConfig = new Config({
19
+ runtime: {
20
+ client: {
21
+ edgeFeatures: {
22
+ echoReplicator: true,
23
+ feedReplicator: true,
24
+ signaling: true,
25
+ agents: true,
26
+ },
27
+ },
28
+ },
29
+ });
30
+
31
+ export const defaultConfig = new Config({
32
+ runtime: {
33
+ client: {
34
+ edgeFeatures: {
35
+ echoReplicator: true,
36
+ feedReplicator: true,
37
+ signaling: true,
38
+ agents: true,
39
+ },
40
+ storage: {
41
+ persistent: true,
42
+ },
43
+ },
44
+ services: {
45
+ edge: {
46
+ url: 'wss://edge-production.dxos.workers.dev/',
47
+ },
48
+ iceProviders: [
49
+ {
50
+ urls: 'https://edge-production.dxos.workers.dev/ice',
51
+ },
52
+ ],
53
+ ai: {
54
+ server: 'https://ai-service.dxos.workers.dev',
55
+ },
56
+ ipfs: {
57
+ server: 'https://api.ipfs.dxos.network/api/v0',
58
+ gateway: 'https://gateway.ipfs.dxos.network/ipfs',
59
+ },
60
+ },
61
+ },
62
+ });
63
+
64
+ export class ConfigService extends Context.Tag('ConfigService')<ConfigService, Config>() {
65
+ static layerMemory = Layer.effect(ConfigService, Effect.succeed(memoryConfig));
66
+
67
+ static fromConfig = (config: Config) => Layer.succeed(ConfigService, config);
68
+
69
+ static load = (args: { config: Option.Option<string>; profile: string }) => {
70
+ const defaultConfigPath = getProfileConfigPath(DX_CONFIG, args.profile);
71
+ return Effect.gen(function* () {
72
+ const fs = yield* FileSystem.FileSystem;
73
+ const configPath = Option.getOrElse(args.config, () => defaultConfigPath);
74
+ const configContent = yield* fs.readFileString(configPath);
75
+ const configValues = Yaml.parse(configContent);
76
+ return ConfigService.of(new Config(configValues, profileBuiltinDefaults(args.profile).values));
77
+ }).pipe(
78
+ // If the config file doesn't exist, create it.
79
+ Effect.catchTag('SystemError', () =>
80
+ Effect.gen(function* () {
81
+ const configValues = defaultConfig.values;
82
+ const fs = yield* FileSystem.FileSystem;
83
+ const pathToCreate = Option.getOrElse(args.config, () => defaultConfigPath);
84
+ yield* fs.makeDirectory(dirname(pathToCreate), { recursive: true });
85
+ yield* fs.writeFileString(pathToCreate, Yaml.stringify(configValues));
86
+ return ConfigService.of(new Config(configValues));
87
+ }),
88
+ ),
89
+ );
90
+ };
91
+ }
92
+
93
+ /**
94
+ * Default config for a profile.
95
+ * Always merged with the default config.
96
+ */
97
+ const profileBuiltinDefaults = (profile: string) => {
98
+ invariant(!profile.endsWith('.yml'));
99
+
100
+ return new Config({
101
+ runtime: {
102
+ client: {
103
+ edgeFeatures: {
104
+ echoReplicator: true,
105
+ feedReplicator: true,
106
+ signaling: true,
107
+ agents: true,
108
+ },
109
+ storage: {
110
+ persistent: true,
111
+ dataRoot: getProfilePath(DX_DATA, profile),
112
+ },
113
+ },
114
+ },
115
+ });
116
+ };
package/src/config.ts CHANGED
@@ -2,7 +2,6 @@
2
2
  // Copyright 2021 DXOS.org
3
3
  //
4
4
 
5
- import { boolean } from 'boolean';
6
5
  import defaultsDeep from 'lodash.defaultsdeep';
7
6
  import isMatch from 'lodash.ismatch';
8
7
 
@@ -19,7 +18,6 @@ const configRootType = schema.getCodecForType('dxos.config.Config');
19
18
 
20
19
  /**
21
20
  * Maps the given objects onto a flattened set of (key x values).
22
- *
23
21
  * Expects parsed yaml content of the form:
24
22
  *
25
23
  * ```
@@ -36,12 +34,11 @@ export const mapFromKeyValues = (spec: MappingSpec, values: Record<string, any>)
36
34
 
37
35
  for (const [key, { path, type }] of Object.entries(spec)) {
38
36
  let value = values[key];
39
-
40
37
  if (value !== undefined) {
41
38
  if (type) {
42
39
  switch (type) {
43
40
  case 'boolean': {
44
- value = boolean(value);
41
+ value = String(value).toLowerCase() === 'true' || value === '1';
45
42
  break;
46
43
  }
47
44
 
@@ -101,16 +98,16 @@ export const mapToKeyValues = (spec: MappingSpec, values: any) => {
101
98
  */
102
99
  export const validateConfig = (config: ConfigProto): ConfigProto => {
103
100
  if (!('version' in config)) {
104
- throw new InvalidConfigError('Version not specified');
101
+ throw new InvalidConfigError({ message: 'Version not specified' });
105
102
  }
106
103
 
107
104
  if (config?.version !== 1) {
108
- throw new InvalidConfigError(`Invalid config version: ${config.version}`);
105
+ throw new InvalidConfigError({ message: `Invalid config version: ${config.version}` });
109
106
  }
110
107
 
111
108
  const error = configRootType.protoType.verify(config);
112
109
  if (error) {
113
- throw new InvalidConfigError(error);
110
+ throw new InvalidConfigError({ message: String(error) });
114
111
  }
115
112
 
116
113
  return config;
package/src/index.ts CHANGED
@@ -8,8 +8,10 @@ export * as defs from '@dxos/protocols/proto/dxos/config';
8
8
  export { type Config as ConfigProto } from '@dxos/protocols/proto/dxos/config';
9
9
 
10
10
  export * from './config';
11
- export * from './loaders';
12
- export * from './savers';
13
- export * from './plugin';
11
+ export * from './config-service';
12
+ export * from '#loaders';
13
+ export * from '#savers';
14
+ export * from '#plugin';
15
+ export * from './telemetry';
14
16
  export * from './types';
15
17
  export * from './preset';
@@ -4,19 +4,25 @@
4
4
 
5
5
  /* THIS FILE WILL BE LOADED BY CONTEXT REPLACEMENT PLUGIN IN BROWSER ENVS. */
6
6
 
7
- /* global __DXOS_CONFIG__ __CONFIG_ENVS__ __CONFIG_DEFAULTS__ __CONFIG_LOCAL__ */
8
-
9
7
  import localforage from 'localforage';
10
8
 
11
9
  import { log } from '@dxos/log';
10
+ import { type Config as ConfigProto } from '@dxos/protocols/proto/dxos/config';
11
+
12
+ declare const __DXOS_CONFIG__: { publicUrl?: string; dynamic?: boolean };
13
+ declare const __CONFIG_ENVS__: Partial<ConfigProto> | undefined;
14
+ declare const __CONFIG_DEFAULTS__: Partial<ConfigProto> | undefined;
15
+ declare const __CONFIG_LOCAL__: Partial<ConfigProto> | undefined;
12
16
 
13
17
  const CONFIG_ENDPOINT = '/.well-known/dx/config';
14
18
 
15
- export const Local = () => {
19
+ export const Profile = (_profile = 'default'): Partial<ConfigProto> => ({});
20
+
21
+ export const Local = (): Partial<ConfigProto> => {
16
22
  return typeof __CONFIG_LOCAL__ !== 'undefined' ? __CONFIG_LOCAL__ : {};
17
23
  };
18
24
 
19
- export const Dynamics = async () => {
25
+ export const Dynamics = async (): Promise<Partial<ConfigProto>> => {
20
26
  const { publicUrl = '', dynamic } = __DXOS_CONFIG__;
21
27
  if (!dynamic) {
22
28
  log('dynamics disabled');
@@ -32,20 +38,20 @@ export const Dynamics = async () => {
32
38
  });
33
39
  };
34
40
 
35
- export const Envs = () => {
41
+ export const Envs = (_basePath?: string): Partial<ConfigProto> => {
36
42
  return typeof __CONFIG_ENVS__ !== 'undefined' ? __CONFIG_ENVS__ : {};
37
43
  };
38
44
 
39
- export const Defaults = () => {
45
+ export const Defaults = (_basePath?: string): Partial<ConfigProto> => {
40
46
  return typeof __CONFIG_DEFAULTS__ !== 'undefined' ? __CONFIG_DEFAULTS__ : {};
41
47
  };
42
48
 
43
49
  /**
44
50
  * Settings config from browser storage.
45
51
  */
46
- export const Storage = async () => {
52
+ export const Storage = async (): Promise<Partial<ConfigProto>> => {
47
53
  try {
48
- const config = await localforage.getItem('dxos.org/settings/config');
54
+ const config = await localforage.getItem<Partial<ConfigProto>>('org.dxos.settings.config');
49
55
  if (config) {
50
56
  return config;
51
57
  }
@@ -55,7 +61,7 @@ export const Storage = async () => {
55
61
  return {};
56
62
  };
57
63
 
58
- export const Remote = (target, authenticationToken) => {
64
+ export const Remote = (target: string | undefined, authenticationToken?: string): Partial<ConfigProto> => {
59
65
  if (!target) {
60
66
  return {};
61
67
  }
@@ -4,8 +4,7 @@
4
4
 
5
5
  import fs from 'node:fs';
6
6
  import path from 'node:path';
7
-
8
- import yaml from 'js-yaml';
7
+ import { parse } from 'yaml';
9
8
 
10
9
  import { log } from '@dxos/log';
11
10
  import { type Config as ConfigProto } from '@dxos/protocols/proto/dxos/config';
@@ -19,7 +18,7 @@ const DEFAULT_BASE_PATH = path.resolve(process.cwd(), 'config');
19
18
 
20
19
  const maybeLoadFile = (file: string): any => {
21
20
  try {
22
- return yaml.load(fs.readFileSync(file, { encoding: 'utf8' }));
21
+ return parse(fs.readFileSync(file, { encoding: 'utf8' }));
23
22
  } catch (err: any) {
24
23
  // Ignored.
25
24
  }
@@ -0,0 +1,11 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ // Browser stub for plugin module - plugins are build-time only.
6
+
7
+ export type * from './types';
8
+
9
+ export const definitions = (): Record<string, unknown> => {
10
+ throw new Error('Config plugin definitions are not available in the browser');
11
+ };
@@ -5,16 +5,14 @@
5
5
  import { execSync } from 'node:child_process';
6
6
  import { readFileSync } from 'node:fs';
7
7
  import { resolve } from 'node:path';
8
-
9
- import yaml from 'js-yaml';
10
8
  import pkgUp from 'pkg-up';
9
+ import { parse } from 'yaml';
11
10
 
12
11
  import { invariant } from '@dxos/invariant';
13
12
  import { log } from '@dxos/log';
14
13
  import { setDeep } from '@dxos/util';
15
14
 
16
15
  import { mapFromKeyValues } from '../config';
17
-
18
16
  import { type ConfigPluginOpts } from './types';
19
17
 
20
18
  const CWD = process.cwd();
@@ -40,7 +38,7 @@ export const definitions = ({
40
38
  invariant(key);
41
39
  let content = {};
42
40
  try {
43
- content = yaml.load(readFileSync(value, 'utf-8')) as any;
41
+ content = parse(readFileSync(value, 'utf-8')) as any;
44
42
 
45
43
  // Map environment variables to config values.
46
44
  if (key === '__CONFIG_ENVS__') {
@@ -3,7 +3,6 @@
3
3
  //
4
4
 
5
5
  import { resolve } from 'node:path';
6
-
7
6
  import { type Plugin } from 'vite';
8
7
 
9
8
  import { definitions } from './definitions';
package/src/preset.ts CHANGED
@@ -11,7 +11,7 @@ export type ConfigPresetOptions = {
11
11
  * Edge service.
12
12
  * @default main
13
13
  */
14
- edge?: 'local' | 'dev' | 'main';
14
+ edge?: 'local' | 'dev' | 'main' | 'production';
15
15
  };
16
16
 
17
17
  export const configPreset = ({ edge = 'main' }: ConfigPresetOptions = {}) =>
@@ -31,6 +31,7 @@ export const configPreset = ({ edge = 'main' }: ConfigPresetOptions = {}) =>
31
31
  Match.when('local', () => 'http://localhost:8787'),
32
32
  Match.when('dev', () => 'https://edge.dxos.workers.dev'),
33
33
  Match.when('main', () => 'https://edge-main.dxos.workers.dev'),
34
+ Match.when('production', () => 'https://edge-production.dxos.workers.dev'),
34
35
  Match.exhaustive,
35
36
  ),
36
37
  },
@@ -7,10 +7,11 @@
7
7
  import localforage from 'localforage';
8
8
 
9
9
  import { log } from '@dxos/log';
10
+ import { type Config as ConfigProto } from '@dxos/protocols/proto/dxos/config';
10
11
 
11
12
  let PERFORMING_CONFIG_SAVE = false;
12
13
 
13
- export const SaveConfig = async (config) => {
14
+ export const SaveConfig = async (config: ConfigProto): Promise<void> => {
14
15
  if (PERFORMING_CONFIG_SAVE) {
15
16
  log.warn('Already performing config save');
16
17
  return;
@@ -18,10 +19,9 @@ export const SaveConfig = async (config) => {
18
19
  PERFORMING_CONFIG_SAVE = true;
19
20
 
20
21
  try {
21
- await localforage.setItem('dxos.org/settings/config', config);
22
+ await localforage.setItem('org.dxos.settings.config', config);
22
23
  } catch (err) {
23
24
  log.warn('Failed to save config', { err });
24
- return {};
25
25
  } finally {
26
26
  PERFORMING_CONFIG_SAVE = false;
27
27
  }
@@ -0,0 +1,25 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { isNode } from '@dxos/util';
6
+
7
+ import { type Config } from './config';
8
+
9
+ /**
10
+ * Resolves the telemetry tag used as the `X-DXOS-Client-Tag` header on edge
11
+ * requests AND as the `ctx.tag` attribute / resource on observability traces.
12
+ *
13
+ * Keeping a single resolver ensures the same tag identifies a session across
14
+ * edge and telemetry tiers. Precedence:
15
+ *
16
+ * 1. `config.runtime.app.env.DX_TELEMETRY_TAG` — the explicit, deterministic
17
+ * path. Populated from `.env`/build-time by the app's config loaders.
18
+ * 2. `process.env.DX_TELEMETRY_TAG` — Node-only, for dev/CI convenience.
19
+ *
20
+ * Returns `undefined` when unset; callers should no-op on undefined rather
21
+ * than stamping empty strings.
22
+ */
23
+ export const resolveTelemetryTag = (config?: Config): string | undefined => {
24
+ return config?.get('runtime.app.env.DX_TELEMETRY_TAG') ?? (isNode() ? process.env.DX_TELEMETRY_TAG : undefined);
25
+ };
@@ -5,9 +5,10 @@
5
5
  // This script is used to test whether the esbuild config plugin is properly injecting config constants.
6
6
  // TODO(wittjosiah): Automate this test.
7
7
 
8
- const { build } = require('esbuild');
9
8
  const { join } = require('node:path');
10
9
 
10
+ const { build } = require('esbuild');
11
+
11
12
  // eslint-disable-next-line
12
13
  // @ts-ignore
13
14
  const { ConfigPlugin } = require('../../dist/plugin/node/esbuild-plugin.cjs');
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/plugin/definitions.ts", "../../../src/config.ts"],
4
- "sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport yaml from 'js-yaml';\nimport pkgUp from 'pkg-up';\n\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\nimport { setDeep } from '@dxos/util';\n\nimport { mapFromKeyValues } from '../config';\n\nimport { type ConfigPluginOpts } from './types';\n\nconst CWD = process.cwd();\n\nexport const definitions = ({\n configPath,\n envPath,\n devPath,\n mode = process.env.NODE_ENV,\n publicUrl = '',\n env,\n}: ConfigPluginOpts) => {\n const KEYS_TO_FILE = {\n __CONFIG_DEFAULTS__: configPath?.length ? configPath : resolve(CWD, 'dx.yml'),\n __CONFIG_ENVS__: envPath?.length ? envPath : resolve(CWD, 'dx-env.yml'),\n } as { [key: string]: string };\n if (mode !== 'production') {\n KEYS_TO_FILE.__CONFIG_LOCAL__ = devPath ?? resolve(CWD, 'dx-local.yml');\n }\n\n return Object.entries(KEYS_TO_FILE).reduce(\n (prev, [key, value]) => {\n invariant(key);\n let content = {};\n try {\n content = yaml.load(readFileSync(value, 'utf-8')) as any;\n\n // Map environment variables to config values.\n if (key === '__CONFIG_ENVS__') {\n content = mapFromKeyValues(content, process.env);\n }\n\n if (key === '__CONFIG_DEFAULTS__') {\n // Load app environment variables into default config.\n Object.entries(process.env).forEach(([key, value]) => {\n if (key.startsWith('DX_') || env?.includes(key)) {\n setDeep(content, ['runtime', 'app', 'env', key], value);\n }\n });\n\n // Set build info automatically if available.\n try {\n const timestamp = new Date().toISOString();\n const commitHash =\n process.env.DX_COMMIT_HASH ??\n execSync('git rev-parse --short HEAD', { encoding: 'utf-8' }).replace('\\n', '');\n const packagePath = pkgUp.sync();\n const packageJson = packagePath && JSON.parse(readFileSync(packagePath, 'utf-8'));\n setDeep(content, ['runtime', 'app', 'build', 'timestamp'], timestamp);\n setDeep(content, ['runtime', 'app', 'build', 'commitHash'], commitHash);\n setDeep(content, ['runtime', 'app', 'build', 'version'], packageJson?.version);\n } catch {}\n }\n } catch (err: any) {\n if (err.message.includes('YAMLException')) {\n log.error(`Failed to parse file ${value}:`, err);\n } else {\n log(`Failed to load file ${value}:`, err);\n }\n\n if (key === '__CONFIG_DEFAULTS__') {\n // Default config is required.\n throw new Error(`Failed to load default config file from ${value}`);\n }\n }\n\n return {\n ...prev,\n [key]: content,\n };\n },\n {\n __CONFIG_DEFAULTS__: {},\n __CONFIG_ENVS__: {},\n __CONFIG_LOCAL__: {},\n __DXOS_CONFIG__: { dynamic: mode === 'production', publicUrl },\n },\n );\n};\n", "//\n// Copyright 2021 DXOS.org\n//\n\nimport { boolean } from 'boolean';\nimport defaultsDeep from 'lodash.defaultsdeep';\nimport isMatch from 'lodash.ismatch';\n\nimport { InvalidConfigError } from '@dxos/protocols';\nimport { schema } from '@dxos/protocols/proto';\nimport { type Config as ConfigProto } from '@dxos/protocols/proto/dxos/config';\nimport { trace } from '@dxos/tracing';\nimport { getDeep, setDeep } from '@dxos/util';\n\nimport { type ConfigKey, type DeepIndex, type ParseKey } from './types';\n\ntype MappingSpec = Record<string, { path: string; type?: string }>;\nconst configRootType = schema.getCodecForType('dxos.config.Config');\n\n/**\n * Maps the given objects onto a flattened set of (key x values).\n *\n * Expects parsed yaml content of the form:\n *\n * ```\n * ENV_VAR:\n * path: config.selector.path\n * ```\n *\n * @param {object} spec\n * @param {object} values\n * @return {object}\n */\nexport const mapFromKeyValues = (spec: MappingSpec, values: Record<string, any>) => {\n const config = {};\n\n for (const [key, { path, type }] of Object.entries(spec)) {\n let value = values[key];\n\n if (value !== undefined) {\n if (type) {\n switch (type) {\n case 'boolean': {\n value = boolean(value);\n break;\n }\n\n case 'number': {\n value = Number(value);\n break;\n }\n\n case 'string': {\n break;\n }\n\n case 'json': {\n value = value ? JSON.parse(value) : null;\n break;\n }\n\n default: {\n throw new Error(`Invalid type: ${type}`);\n }\n }\n }\n\n setDeep(config, path.split('.'), value);\n }\n }\n\n return config;\n};\n\n/**\n * Maps the given flattend set of (key x values) onto a JSON object.\n * @param {object} spec\n * @param {object} values\n */\nexport const mapToKeyValues = (spec: MappingSpec, values: any) => {\n const config: Record<string, any> = {};\n\n for (const [key, { path, type }] of Object.entries(spec)) {\n const value = getDeep(values, path.split('.'));\n if (value !== undefined) {\n switch (type) {\n case 'json':\n config[key] = JSON.stringify(value);\n break;\n default:\n config[key] = value;\n }\n }\n }\n\n return config;\n};\n\n/**\n * Validate config object.\n */\nexport const validateConfig = (config: ConfigProto): ConfigProto => {\n if (!('version' in config)) {\n throw new InvalidConfigError('Version not specified');\n }\n\n if (config?.version !== 1) {\n throw new InvalidConfigError(`Invalid config version: ${config.version}`);\n }\n\n const error = configRootType.protoType.verify(config);\n if (error) {\n throw new InvalidConfigError(error);\n }\n\n return config;\n};\n\nexport const ConfigResource = Symbol.for('dxos.resource.Config');\n\n/**\n * Global configuration object.\n * NOTE: Config objects are immutable.\n */\n@trace.resource({ annotation: ConfigResource })\nexport class Config {\n private readonly _config: any;\n\n /**\n * Creates an immutable instance.\n * @constructor\n */\n constructor(config: ConfigProto = {}, ...objects: ConfigProto[]) {\n this._config = validateConfig(defaultsDeep(config, ...objects, { version: 1 }));\n }\n\n /**\n * Returns an immutable config JSON object.\n */\n get values(): ConfigProto {\n return this._config;\n }\n\n /**\n * Returns the given config property.\n *\n * @param key A key in the config object. Can be a nested property with keys separated by dots: 'services.signal.server'.\n * @param defaultValue Default value to return if option is not present in the config.\n * @returns The config value or undefined if the option is not present.\n */\n get<K extends ConfigKey>(\n key: K,\n defaultValue?: DeepIndex<ConfigProto, ParseKey<K>>,\n ): DeepIndex<ConfigProto, ParseKey<K>> | undefined {\n return getDeep(this._config, key.split('.')) ?? defaultValue;\n }\n\n /**\n * Get unique key.\n */\n find<T = any>(path: string, test: object): T | undefined {\n const values = getDeep(this._config, path.split('.'));\n if (!Array.isArray(values)) {\n return;\n }\n\n return values.find((value) => isMatch(value, test));\n }\n\n /**\n * Returns the given config property or throw if it doesn't exist.\n *\n * @param key A key in the config object. Can be a nested property with keys separated by dots: 'services.signal.server'.\n */\n getOrThrow<K extends ConfigKey>(key: K): Exclude<DeepIndex<ConfigProto, ParseKey<K>>, undefined> {\n const value: DeepIndex<ConfigProto, ParseKey<K>> | undefined = getDeep(this._config, key.split('.'));\n if (!value) {\n throw new Error(`Config option not present: ${key}`);\n }\n return value;\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["execSync", "readFileSync", "resolve", "yaml", "pkgUp", "invariant", "log", "setDeep", "boolean", "defaultsDeep", "isMatch", "InvalidConfigError", "schema", "trace", "getDeep", "setDeep", "configRootType", "schema", "getCodecForType", "mapFromKeyValues", "spec", "values", "config", "key", "path", "type", "Object", "entries", "value", "undefined", "boolean", "Number", "JSON", "parse", "Error", "setDeep", "split", "validateConfig", "config", "InvalidConfigError", "version", "error", "configRootType", "protoType", "verify", "ConfigResource", "Symbol", "for", "Config", "_config", "objects", "defaultsDeep", "values", "get", "key", "defaultValue", "getDeep", "split", "find", "path", "test", "Array", "isArray", "value", "isMatch", "getOrThrow", "Error", "resource", "annotation", "CWD", "process", "cwd", "definitions", "configPath", "envPath", "devPath", "mode", "env", "NODE_ENV", "publicUrl", "KEYS_TO_FILE", "__CONFIG_DEFAULTS__", "length", "resolve", "__CONFIG_ENVS__", "__CONFIG_LOCAL__", "Object", "entries", "reduce", "prev", "key", "value", "invariant", "content", "yaml", "load", "readFileSync", "mapFromKeyValues", "forEach", "startsWith", "includes", "setDeep", "timestamp", "Date", "toISOString", "commitHash", "DX_COMMIT_HASH", "execSync", "encoding", "replace", "packagePath", "pkgUp", "sync", "packageJson", "JSON", "parse", "version", "err", "message", "log", "error", "Error", "__DXOS_CONFIG__", "dynamic"]
7
- }