@antelopejs/dms-frontend 0.6.4 → 0.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.
@@ -49,15 +49,33 @@ function readJsonFile(url, fallback) {
49
49
  }
50
50
  }
51
51
 
52
- /** Sets `value` at `path` when the object holding it exists but lacks it. */
53
- function fillPath(target, path, value) {
52
+ const UNSAFE_KEYS = new Set(["__proto__", "constructor", "prototype"]);
53
+
54
+ /** The object holding `path`'s last key, when every key up to it is own. */
55
+ function holderOf(target, path) {
54
56
  let node = target;
55
57
  for (const key of path.slice(0, -1)) {
56
- if (typeof node[key] !== "object" || node[key] === null) return;
58
+ if (UNSAFE_KEYS.has(key) || !Object.hasOwn(node, key)) return undefined;
57
59
  node = node[key];
60
+ if (typeof node !== "object" || node === null) return undefined;
58
61
  }
62
+ return UNSAFE_KEYS.has(path.at(-1)) ? undefined : node;
63
+ }
64
+
65
+ /** Sets `value` at `path` when the object holding it exists but lacks it. */
66
+ function fillPath(target, path, value) {
67
+ const holder = holderOf(target, path);
68
+ const key = path.at(-1);
69
+ if (holder && (holder[key] === undefined || holder[key] === ""))
70
+ holder[key] = value;
71
+ }
72
+
73
+ /** Sets `value` at `path` only where the build left an empty string. */
74
+ function fillEmptied(target, path, value) {
75
+ const holder = holderOf(target, path);
59
76
  const key = path.at(-1);
60
- if (typeof node[key] !== "string" || node[key] === "") node[key] = value;
77
+ if (holder && Object.hasOwn(holder, key) && holder[key] === "")
78
+ holder[key] = value;
61
79
  }
62
80
 
63
81
  /**
@@ -77,12 +95,56 @@ export function privateOptionsWithFallbacks(modules, environment, env) {
77
95
  return modules;
78
96
  }
79
97
 
98
+ /** Each string leaf of `value`, array items included, with its path. */
99
+ function* stringLeaves(value, path = []) {
100
+ if (typeof value === "string") yield [path, value];
101
+ else if (typeof value === "object" && value !== null)
102
+ for (const [key, child] of Object.entries(value))
103
+ yield* stringLeaves(child, [...path, key]);
104
+ }
105
+
106
+ /**
107
+ * Fills the private values a build left empty from `DMS_PRIVATE_OPTIONS`, a
108
+ * JSON object of private options by module id
109
+ * (`{"acme__shop-frontend-vue":{"apiKey":"…"}}`): any module's secrets, where
110
+ * `DMS_HTML_RENDER_SECRET` and `DMS_OAUTH_RELAY_SECRET` name two of them. Only
111
+ * a value the build emptied is filled, array items included: a value the
112
+ * manifest carries wins, and no option or section is added or replaced.
113
+ *
114
+ * @param modules The private options by module id
115
+ * @param json The variable's value; nothing is filled when unset
116
+ * @param warn Reports a value that is not a JSON object, never its content
117
+ */
118
+ export function privateOptionsFromJson(modules, json, warn) {
119
+ if (!json) return modules;
120
+ let given;
121
+ try {
122
+ given = JSON.parse(json);
123
+ } catch {
124
+ given = undefined;
125
+ }
126
+ if (typeof given !== "object" || given === null || Array.isArray(given)) {
127
+ warn("DMS_PRIVATE_OPTIONS is not a JSON object of options by module id");
128
+ return modules;
129
+ }
130
+ for (const [id, options] of Object.entries(given)) {
131
+ if (UNSAFE_KEYS.has(id) || !Object.hasOwn(modules, id)) continue;
132
+ for (const [path, value] of stringLeaves(options))
133
+ if (path.length > 0) fillEmptied(modules[id], path, value);
134
+ }
135
+ return modules;
136
+ }
137
+
80
138
  function readPrivateOptions(env = process.env) {
81
139
  const { modules = {}, environment = {} } = readJsonFile(
82
140
  SERVER_OPTIONS_FILE,
83
141
  {},
84
142
  );
85
- return privateOptionsWithFallbacks(modules, environment, env);
143
+ return privateOptionsFromJson(
144
+ privateOptionsWithFallbacks(modules, environment, env),
145
+ env.DMS_PRIVATE_OPTIONS,
146
+ (message) => console.warn(message),
147
+ );
86
148
  }
87
149
 
88
150
  function readPublicOptions() {