@terreno/api 0.13.3 → 0.14.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.
Files changed (172) hide show
  1. package/dist/__tests__/versionCheckPlugin.test.js +136 -3
  2. package/dist/api.arrayOperations.test.js +1 -0
  3. package/dist/api.d.ts +15 -4
  4. package/dist/api.errors.test.js +1 -0
  5. package/dist/api.hooks.test.js +1 -0
  6. package/dist/api.js +153 -104
  7. package/dist/api.query.test.js +1 -0
  8. package/dist/api.test.js +174 -0
  9. package/dist/auth.d.ts +10 -5
  10. package/dist/auth.js +163 -90
  11. package/dist/auth.test.js +159 -0
  12. package/dist/betterAuthApp.test.js +1 -0
  13. package/dist/betterAuthSetup.d.ts +5 -6
  14. package/dist/betterAuthSetup.js +30 -17
  15. package/dist/betterAuthSetup.test.js +1 -0
  16. package/dist/config.d.ts +48 -0
  17. package/dist/config.js +257 -0
  18. package/dist/config.test.d.ts +1 -0
  19. package/dist/config.test.js +328 -0
  20. package/dist/configuration.test.js +1 -0
  21. package/dist/configurationApp.d.ts +1 -1
  22. package/dist/configurationApp.js +17 -13
  23. package/dist/configurationPlugin.test.js +1 -0
  24. package/dist/consentApp.test.js +1 -0
  25. package/dist/envConfigurationPlugin.d.ts +2 -0
  26. package/dist/envConfigurationPlugin.js +173 -0
  27. package/dist/envConfigurationPlugin.test.d.ts +1 -0
  28. package/dist/envConfigurationPlugin.test.js +322 -0
  29. package/dist/errors.d.ts +18 -7
  30. package/dist/errors.js +111 -12
  31. package/dist/errors.test.js +16 -1
  32. package/dist/example.js +19 -7
  33. package/dist/expressServer.d.ts +10 -9
  34. package/dist/expressServer.js +62 -53
  35. package/dist/expressServer.test.js +165 -2
  36. package/dist/githubAuth.d.ts +2 -1
  37. package/dist/githubAuth.js +41 -26
  38. package/dist/githubAuth.test.js +1 -0
  39. package/dist/index.d.ts +4 -0
  40. package/dist/index.js +4 -0
  41. package/dist/logger.d.ts +1 -1
  42. package/dist/logger.js +42 -20
  43. package/dist/models/versionConfig.d.ts +2 -0
  44. package/dist/models/versionConfig.js +8 -0
  45. package/dist/notifiers/googleChatNotifier.js +14 -16
  46. package/dist/notifiers/googleChatNotifier.test.js +1 -0
  47. package/dist/notifiers/slackNotifier.js +16 -14
  48. package/dist/notifiers/slackNotifier.test.js +41 -3
  49. package/dist/notifiers/zoomNotifier.js +7 -10
  50. package/dist/notifiers/zoomNotifier.test.js +1 -0
  51. package/dist/openApi.d.ts +1 -1
  52. package/dist/openApi.test.js +1 -0
  53. package/dist/openApiBuilder.d.ts +39 -6
  54. package/dist/openApiBuilder.js +1 -31
  55. package/dist/openApiBuilder.test.js +1 -0
  56. package/dist/openApiValidator.js +1 -0
  57. package/dist/openApiValidator.test.js +1 -0
  58. package/dist/permissions.d.ts +4 -4
  59. package/dist/permissions.js +67 -65
  60. package/dist/permissions.middleware.test.js +1 -0
  61. package/dist/permissions.test.js +1 -0
  62. package/dist/plugins.d.ts +5 -5
  63. package/dist/plugins.js +18 -9
  64. package/dist/plugins.test.js +1 -1
  65. package/dist/populate.d.ts +15 -8
  66. package/dist/populate.js +23 -24
  67. package/dist/populate.test.js +1 -0
  68. package/dist/realtime/changeStreamWatcher.d.ts +73 -0
  69. package/dist/realtime/changeStreamWatcher.js +724 -0
  70. package/dist/realtime/index.d.ts +6 -0
  71. package/dist/realtime/index.js +27 -0
  72. package/dist/realtime/queryMatcher.d.ts +14 -0
  73. package/dist/realtime/queryMatcher.js +250 -0
  74. package/dist/realtime/queryStore.d.ts +37 -0
  75. package/dist/realtime/queryStore.js +195 -0
  76. package/dist/realtime/realtime.test.d.ts +10 -0
  77. package/dist/realtime/realtime.test.js +3066 -0
  78. package/dist/realtime/realtimeApp.d.ts +93 -0
  79. package/dist/realtime/realtimeApp.js +560 -0
  80. package/dist/realtime/registry.d.ts +40 -0
  81. package/dist/realtime/registry.js +38 -0
  82. package/dist/realtime/socketUser.d.ts +10 -0
  83. package/dist/realtime/socketUser.js +17 -0
  84. package/dist/realtime/types.d.ts +100 -0
  85. package/dist/realtime/types.js +2 -0
  86. package/dist/requestContext.d.ts +37 -0
  87. package/dist/requestContext.js +344 -0
  88. package/dist/requestContext.test.d.ts +1 -0
  89. package/dist/requestContext.test.js +384 -0
  90. package/dist/terrenoApp.d.ts +8 -0
  91. package/dist/terrenoApp.js +50 -13
  92. package/dist/terrenoApp.test.js +194 -21
  93. package/dist/terrenoPlugin.d.ts +11 -0
  94. package/dist/tests/bunSetup.js +1 -0
  95. package/dist/tests.js +1 -1
  96. package/dist/transformers.d.ts +2 -2
  97. package/dist/transformers.js +5 -3
  98. package/dist/transformers.test.js +90 -0
  99. package/dist/types/consentResponse.d.ts +6 -3
  100. package/dist/versionCheckPlugin.d.ts +2 -0
  101. package/dist/versionCheckPlugin.js +18 -12
  102. package/package.json +4 -2
  103. package/src/__tests__/versionCheckPlugin.test.ts +94 -3
  104. package/src/api.arrayOperations.test.ts +1 -0
  105. package/src/api.errors.test.ts +1 -0
  106. package/src/api.hooks.test.ts +1 -0
  107. package/src/api.query.test.ts +1 -0
  108. package/src/api.test.ts +132 -0
  109. package/src/api.ts +199 -84
  110. package/src/auth.test.ts +160 -0
  111. package/src/auth.ts +120 -50
  112. package/src/betterAuthApp.test.ts +1 -0
  113. package/src/betterAuthSetup.test.ts +1 -0
  114. package/src/betterAuthSetup.ts +59 -22
  115. package/src/config.test.ts +255 -0
  116. package/src/config.ts +216 -0
  117. package/src/configuration.test.ts +1 -0
  118. package/src/configurationApp.ts +59 -24
  119. package/src/configurationPlugin.test.ts +1 -0
  120. package/src/consentApp.test.ts +1 -0
  121. package/src/envConfigurationPlugin.test.ts +143 -0
  122. package/src/envConfigurationPlugin.ts +100 -0
  123. package/src/errors.test.ts +19 -1
  124. package/src/errors.ts +118 -38
  125. package/src/example.ts +49 -21
  126. package/src/express.d.ts +18 -1
  127. package/src/expressServer.test.ts +147 -2
  128. package/src/expressServer.ts +80 -50
  129. package/src/githubAuth.test.ts +1 -0
  130. package/src/githubAuth.ts +59 -38
  131. package/src/index.ts +4 -0
  132. package/src/logger.ts +47 -17
  133. package/src/models/versionConfig.ts +13 -2
  134. package/src/notifiers/googleChatNotifier.test.ts +1 -0
  135. package/src/notifiers/googleChatNotifier.ts +7 -9
  136. package/src/notifiers/slackNotifier.test.ts +29 -3
  137. package/src/notifiers/slackNotifier.ts +9 -7
  138. package/src/notifiers/zoomNotifier.test.ts +1 -0
  139. package/src/notifiers/zoomNotifier.ts +8 -11
  140. package/src/openApi.test.ts +1 -0
  141. package/src/openApi.ts +4 -4
  142. package/src/openApiBuilder.test.ts +1 -0
  143. package/src/openApiBuilder.ts +14 -11
  144. package/src/openApiValidator.test.ts +1 -0
  145. package/src/openApiValidator.ts +3 -2
  146. package/src/permissions.middleware.test.ts +1 -0
  147. package/src/permissions.test.ts +1 -0
  148. package/src/permissions.ts +30 -25
  149. package/src/plugins.test.ts +1 -1
  150. package/src/plugins.ts +21 -14
  151. package/src/populate.test.ts +1 -0
  152. package/src/populate.ts +44 -36
  153. package/src/realtime/changeStreamWatcher.ts +572 -0
  154. package/src/realtime/index.ts +34 -0
  155. package/src/realtime/queryMatcher.ts +179 -0
  156. package/src/realtime/queryStore.ts +132 -0
  157. package/src/realtime/realtime.test.ts +2465 -0
  158. package/src/realtime/realtimeApp.ts +478 -0
  159. package/src/realtime/registry.ts +64 -0
  160. package/src/realtime/socketUser.ts +25 -0
  161. package/src/realtime/types.ts +112 -0
  162. package/src/requestContext.test.ts +321 -0
  163. package/src/requestContext.ts +368 -0
  164. package/src/terrenoApp.test.ts +137 -11
  165. package/src/terrenoApp.ts +64 -17
  166. package/src/terrenoPlugin.ts +12 -0
  167. package/src/tests/bunSetup.ts +1 -0
  168. package/src/tests.ts +7 -2
  169. package/src/transformers.test.ts +70 -2
  170. package/src/transformers.ts +15 -7
  171. package/src/types/consentResponse.ts +8 -10
  172. package/src/versionCheckPlugin.ts +15 -7
package/dist/config.js ADDED
@@ -0,0 +1,257 @@
1
+ "use strict";
2
+ /**
3
+ * Runtime configuration registry with a fixed resolution order:
4
+ *
5
+ * 1. In-process override (Config.setOverride) — highest, for tests/bootstrap
6
+ * 2. Cached env map — typically loaded from an admin-editable Mongoose document
7
+ * 3. process.env
8
+ * 4. Registered default
9
+ *
10
+ * Why a registry: every key migrating off raw `process.env` declares its type
11
+ * and default in one place. That keeps the admin UI honest (no surprise keys),
12
+ * gives synchronous access without scattered `?? "default"` literals at call
13
+ * sites, and lets tests assert behavior against a single source of truth.
14
+ *
15
+ * Why sync: hundreds of call sites read configuration during request handling
16
+ * and module init; an async API would force enormous refactors. Callers load
17
+ * the env map once via `Config.refresh()` after Mongo connects, then read
18
+ * synchronously from cache.
19
+ *
20
+ * The mechanism is agnostic to where the env map comes from. Apps wire up
21
+ * their backing store with `Config.setEnvLoader(fn)`. The optional
22
+ * `envConfigurationPlugin` provides a drop-in Mongoose schema integration.
23
+ */
24
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
25
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
26
+ return new (P || (P = Promise))(function (resolve, reject) {
27
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
28
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
29
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
30
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
31
+ });
32
+ };
33
+ var __generator = (this && this.__generator) || function (thisArg, body) {
34
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
35
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
36
+ function verb(n) { return function (v) { return step([n, v]); }; }
37
+ function step(op) {
38
+ if (f) throw new TypeError("Generator is already executing.");
39
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
40
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
41
+ if (y = 0, t) op = [op[0] & 2, t.value];
42
+ switch (op[0]) {
43
+ case 0: case 1: t = op; break;
44
+ case 4: _.label++; return { value: op[1], done: false };
45
+ case 5: _.label++; y = op[1]; op = [0]; continue;
46
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
47
+ default:
48
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
49
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
50
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
51
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
52
+ if (t[2]) _.ops.pop();
53
+ _.trys.pop(); continue;
54
+ }
55
+ op = body.call(thisArg, _);
56
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
57
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
58
+ }
59
+ };
60
+ var __values = (this && this.__values) || function(o) {
61
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
62
+ if (m) return m.call(o);
63
+ if (o && typeof o.length === "number") return {
64
+ next: function () {
65
+ if (o && i >= o.length) o = void 0;
66
+ return { value: o && o[i++], done: !o };
67
+ }
68
+ };
69
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
70
+ };
71
+ Object.defineProperty(exports, "__esModule", { value: true });
72
+ exports.Config = void 0;
73
+ var errors_1 = require("./errors");
74
+ var overrides = new Map();
75
+ var cachedEnv = null;
76
+ var envLoader = null;
77
+ // Null-prototype object so lookups don't resolve inherited keys like
78
+ // `constructor` / `toString` as accidentally-registered entries.
79
+ var REGISTRY = Object.create(null);
80
+ /**
81
+ * Registers a configuration key, its default, and metadata. Re-registration
82
+ * of the same key throws so duplicates surface at boot.
83
+ */
84
+ var register = function (key, registration) {
85
+ if (registration === void 0) { registration = {}; }
86
+ if (REGISTRY[key]) {
87
+ throw new errors_1.APIError({ status: 500, title: "Config key \"".concat(key, "\" registered more than once") });
88
+ }
89
+ REGISTRY[key] = registration;
90
+ };
91
+ /**
92
+ * Returns the configured string value for `key`, applying the resolution
93
+ * order documented at the top of this file. Returns `undefined` if no
94
+ * source supplies a value and no default was registered.
95
+ */
96
+ var getString = function (key) {
97
+ var _a;
98
+ if (overrides.has(key)) {
99
+ return overrides.get(key);
100
+ }
101
+ if (cachedEnv && key in cachedEnv) {
102
+ var v = cachedEnv[key];
103
+ if (v !== undefined && v !== "") {
104
+ return v;
105
+ }
106
+ }
107
+ // Guard against process.env keys like "toString" / "constructor" inheriting
108
+ // a function from Object.prototype. Only accept string values.
109
+ var fromProcess = process.env[key];
110
+ if (typeof fromProcess === "string" && fromProcess !== "") {
111
+ return fromProcess;
112
+ }
113
+ return (_a = REGISTRY[key]) === null || _a === void 0 ? void 0 : _a.default;
114
+ };
115
+ /**
116
+ * Returns the configured value as a number. Throws if a value is present but
117
+ * not finite — silent NaN propagation has bitten apps before.
118
+ */
119
+ var getNumber = function (key) {
120
+ var raw = getString(key);
121
+ if (raw === undefined) {
122
+ return undefined;
123
+ }
124
+ // Number() rejects partially-numeric strings like "5000ms" (returns NaN)
125
+ // whereas parseFloat would silently truncate to 5000.
126
+ var parsed = Number(raw);
127
+ if (!Number.isFinite(parsed)) {
128
+ throw new errors_1.APIError({
129
+ error: new Error("Config key \"".concat(key, "\" is not a valid number: ").concat(JSON.stringify(raw))),
130
+ status: 500,
131
+ title: "Config key \"".concat(key, "\" is not a valid number"),
132
+ });
133
+ }
134
+ return parsed;
135
+ };
136
+ /**
137
+ * Returns true iff the string value equals "true" (case-insensitive). Mirrors
138
+ * the existing `process.env.X === "true"` idiom.
139
+ */
140
+ var getBoolean = function (key) {
141
+ var raw = getString(key);
142
+ return raw !== undefined && raw.toLowerCase() === "true";
143
+ };
144
+ /**
145
+ * Parses a JSON-encoded config value. Returns undefined if unset; throws on
146
+ * malformed JSON so misconfiguration fails loud at the call site rather than
147
+ * producing silent runtime errors later.
148
+ */
149
+ var getJSON = function (key) {
150
+ var raw = getString(key);
151
+ if (raw === undefined) {
152
+ return undefined;
153
+ }
154
+ try {
155
+ return JSON.parse(raw);
156
+ }
157
+ catch (error) {
158
+ throw new errors_1.APIError({
159
+ error: new Error("Config key \"".concat(key, "\" is not valid JSON: ").concat(error.message)),
160
+ status: 500,
161
+ title: "Config key \"".concat(key, "\" is not valid JSON"),
162
+ });
163
+ }
164
+ };
165
+ /**
166
+ * Registers a loader that returns the env map (typically backed by an
167
+ * admin-editable Mongoose document). Called once at app startup before
168
+ * the first `Config.refresh()`.
169
+ */
170
+ var setEnvLoader = function (loader) {
171
+ envLoader = loader;
172
+ };
173
+ /**
174
+ * Reloads the in-memory cache by invoking the registered env loader. No-op
175
+ * (clears cache) if no loader has been registered.
176
+ */
177
+ var refresh = function () { return __awaiter(void 0, void 0, void 0, function () {
178
+ return __generator(this, function (_a) {
179
+ switch (_a.label) {
180
+ case 0:
181
+ if (!envLoader) {
182
+ cachedEnv = {};
183
+ return [2 /*return*/];
184
+ }
185
+ return [4 /*yield*/, envLoader()];
186
+ case 1:
187
+ cachedEnv = _a.sent();
188
+ return [2 /*return*/];
189
+ }
190
+ });
191
+ }); };
192
+ /** Replaces the cache directly. Intended for the envConfigurationPlugin and tests. */
193
+ var setCachedEnv = function (env) {
194
+ cachedEnv = env;
195
+ };
196
+ /**
197
+ * Sets an in-process override for `key`. Highest precedence — wins over
198
+ * the cached env map. Intended for tests and bootstrap helpers.
199
+ */
200
+ var setOverride = function (key, value) {
201
+ overrides.set(key, value);
202
+ };
203
+ /** Clears every override. Call from afterEach in tests. */
204
+ var clearOverrides = function () {
205
+ overrides.clear();
206
+ };
207
+ /** Returns the registered default (if any) for `key`. */
208
+ var getDefault = function (key) {
209
+ var _a;
210
+ return (_a = REGISTRY[key]) === null || _a === void 0 ? void 0 : _a.default;
211
+ };
212
+ /** Returns the registration metadata for `key`, including secret/description. */
213
+ var getRegistration = function (key) {
214
+ return REGISTRY[key];
215
+ };
216
+ /** Returns the registered keys, sorted. Used by the admin UI. */
217
+ var getRegisteredKeys = function () {
218
+ return Object.keys(REGISTRY).sort();
219
+ };
220
+ /** Returns true if `key` was registered. */
221
+ var isRegistered = function (key) {
222
+ return key in REGISTRY;
223
+ };
224
+ /** Removes every registered key. Intended for tests. */
225
+ var clearRegistryForTesting = function () {
226
+ var e_1, _a;
227
+ try {
228
+ for (var _b = __values(Object.keys(REGISTRY)), _c = _b.next(); !_c.done; _c = _b.next()) {
229
+ var key = _c.value;
230
+ delete REGISTRY[key];
231
+ }
232
+ }
233
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
234
+ finally {
235
+ try {
236
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
237
+ }
238
+ finally { if (e_1) throw e_1.error; }
239
+ }
240
+ };
241
+ exports.Config = {
242
+ clearOverrides: clearOverrides,
243
+ clearRegistryForTesting: clearRegistryForTesting,
244
+ get: getString,
245
+ getBoolean: getBoolean,
246
+ getDefault: getDefault,
247
+ getJSON: getJSON,
248
+ getNumber: getNumber,
249
+ getRegisteredKeys: getRegisteredKeys,
250
+ getRegistration: getRegistration,
251
+ isRegistered: isRegistered,
252
+ refresh: refresh,
253
+ register: register,
254
+ setCachedEnv: setCachedEnv,
255
+ setEnvLoader: setEnvLoader,
256
+ setOverride: setOverride,
257
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,328 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __values = (this && this.__values) || function(o) {
39
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
40
+ if (m) return m.call(o);
41
+ if (o && typeof o.length === "number") return {
42
+ next: function () {
43
+ if (o && i >= o.length) o = void 0;
44
+ return { value: o && o[i++], done: !o };
45
+ }
46
+ };
47
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
48
+ };
49
+ var __read = (this && this.__read) || function (o, n) {
50
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
51
+ if (!m) return o;
52
+ var i = m.call(o), r, ar = [], e;
53
+ try {
54
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
55
+ }
56
+ catch (error) { e = { error: error }; }
57
+ finally {
58
+ try {
59
+ if (r && !r.done && (m = i["return"])) m.call(i);
60
+ }
61
+ finally { if (e) throw e.error; }
62
+ }
63
+ return ar;
64
+ };
65
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
66
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
67
+ if (ar || !(i in from)) {
68
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
69
+ ar[i] = from[i];
70
+ }
71
+ }
72
+ return to.concat(ar || Array.prototype.slice.call(from));
73
+ };
74
+ Object.defineProperty(exports, "__esModule", { value: true });
75
+ var bun_test_1 = require("bun:test");
76
+ var config_1 = require("./config");
77
+ var KEYS = [
78
+ "TERRENO_CFG_STRING",
79
+ "TERRENO_CFG_DEFAULTED",
80
+ "TERRENO_CFG_NUM",
81
+ "TERRENO_CFG_BOOL",
82
+ "TERRENO_CFG_JSON",
83
+ "TERRENO_CFG_UNDEFAULTED",
84
+ "TERRENO_CFG_UNREGISTERED",
85
+ ];
86
+ var resetEnv = function () {
87
+ var e_1, _a;
88
+ try {
89
+ for (var KEYS_1 = __values(KEYS), KEYS_1_1 = KEYS_1.next(); !KEYS_1_1.done; KEYS_1_1 = KEYS_1.next()) {
90
+ var key = KEYS_1_1.value;
91
+ Reflect.deleteProperty(process.env, key);
92
+ }
93
+ }
94
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
95
+ finally {
96
+ try {
97
+ if (KEYS_1_1 && !KEYS_1_1.done && (_a = KEYS_1.return)) _a.call(KEYS_1);
98
+ }
99
+ finally { if (e_1) throw e_1.error; }
100
+ }
101
+ };
102
+ (0, bun_test_1.describe)("Config", function () {
103
+ (0, bun_test_1.beforeEach)(function () {
104
+ config_1.Config.clearRegistryForTesting();
105
+ config_1.Config.clearOverrides();
106
+ config_1.Config.setCachedEnv(null);
107
+ config_1.Config.setEnvLoader(null);
108
+ resetEnv();
109
+ config_1.Config.register("TERRENO_CFG_STRING");
110
+ config_1.Config.register("TERRENO_CFG_DEFAULTED", { default: "fallback" });
111
+ config_1.Config.register("TERRENO_CFG_NUM", { default: "1000" });
112
+ config_1.Config.register("TERRENO_CFG_BOOL", { default: "false" });
113
+ config_1.Config.register("TERRENO_CFG_JSON", { default: "{}" });
114
+ config_1.Config.register("TERRENO_CFG_UNDEFAULTED");
115
+ });
116
+ (0, bun_test_1.afterEach)(function () {
117
+ resetEnv();
118
+ config_1.Config.clearRegistryForTesting();
119
+ config_1.Config.clearOverrides();
120
+ config_1.Config.setCachedEnv(null);
121
+ config_1.Config.setEnvLoader(null);
122
+ });
123
+ (0, bun_test_1.describe)("resolution order", function () {
124
+ (0, bun_test_1.it)("returns the registered default when nothing is set", function () {
125
+ (0, bun_test_1.expect)(config_1.Config.get("TERRENO_CFG_DEFAULTED")).toBe("fallback");
126
+ });
127
+ (0, bun_test_1.it)("returns process.env when set and no cache/override is present", function () {
128
+ process.env.TERRENO_CFG_STRING = "fromEnv";
129
+ (0, bun_test_1.expect)(config_1.Config.get("TERRENO_CFG_STRING")).toBe("fromEnv");
130
+ });
131
+ (0, bun_test_1.it)("cache wins over process.env", function () {
132
+ process.env.TERRENO_CFG_STRING = "fromEnv";
133
+ config_1.Config.setCachedEnv({ TERRENO_CFG_STRING: "fromCache" });
134
+ (0, bun_test_1.expect)(config_1.Config.get("TERRENO_CFG_STRING")).toBe("fromCache");
135
+ });
136
+ (0, bun_test_1.it)("override wins over cache and process.env", function () {
137
+ process.env.TERRENO_CFG_STRING = "fromEnv";
138
+ config_1.Config.setCachedEnv({ TERRENO_CFG_STRING: "fromCache" });
139
+ config_1.Config.setOverride("TERRENO_CFG_STRING", "fromOverride");
140
+ (0, bun_test_1.expect)(config_1.Config.get("TERRENO_CFG_STRING")).toBe("fromOverride");
141
+ });
142
+ (0, bun_test_1.it)("falls through empty-string cache values to process.env", function () {
143
+ process.env.TERRENO_CFG_STRING = "fromEnv";
144
+ config_1.Config.setCachedEnv({ TERRENO_CFG_STRING: "" });
145
+ (0, bun_test_1.expect)(config_1.Config.get("TERRENO_CFG_STRING")).toBe("fromEnv");
146
+ });
147
+ (0, bun_test_1.it)("falls through empty-string process.env to default", function () {
148
+ process.env.TERRENO_CFG_DEFAULTED = "";
149
+ (0, bun_test_1.expect)(config_1.Config.get("TERRENO_CFG_DEFAULTED")).toBe("fallback");
150
+ });
151
+ (0, bun_test_1.it)("returns undefined for an unregistered key with nothing set", function () {
152
+ (0, bun_test_1.expect)(config_1.Config.get("TERRENO_CFG_UNREGISTERED")).toBeUndefined();
153
+ });
154
+ (0, bun_test_1.it)("returns process.env for an unregistered key when set", function () {
155
+ process.env.TERRENO_CFG_UNREGISTERED = "value";
156
+ (0, bun_test_1.expect)(config_1.Config.get("TERRENO_CFG_UNREGISTERED")).toBe("value");
157
+ });
158
+ (0, bun_test_1.it)("setOverride(key, undefined) is treated as 'force unset'", function () {
159
+ process.env.TERRENO_CFG_STRING = "fromEnv";
160
+ config_1.Config.setOverride("TERRENO_CFG_STRING", undefined);
161
+ (0, bun_test_1.expect)(config_1.Config.get("TERRENO_CFG_STRING")).toBeUndefined();
162
+ });
163
+ });
164
+ (0, bun_test_1.describe)("getNumber", function () {
165
+ (0, bun_test_1.it)("parses numeric process.env values", function () {
166
+ process.env.TERRENO_CFG_NUM = "5000";
167
+ (0, bun_test_1.expect)(config_1.Config.getNumber("TERRENO_CFG_NUM")).toBe(5000);
168
+ });
169
+ (0, bun_test_1.it)("parses the registered default", function () {
170
+ (0, bun_test_1.expect)(config_1.Config.getNumber("TERRENO_CFG_NUM")).toBe(1000);
171
+ });
172
+ (0, bun_test_1.it)("returns undefined when no value is available", function () {
173
+ (0, bun_test_1.expect)(config_1.Config.getNumber("TERRENO_CFG_UNDEFAULTED")).toBeUndefined();
174
+ });
175
+ (0, bun_test_1.it)("throws on non-numeric values", function () {
176
+ process.env.TERRENO_CFG_NUM = "not-a-number";
177
+ (0, bun_test_1.expect)(function () { return config_1.Config.getNumber("TERRENO_CFG_NUM"); }).toThrow(/not a valid number/);
178
+ });
179
+ (0, bun_test_1.it)("throws on partially-numeric strings like '5000ms'", function () {
180
+ process.env.TERRENO_CFG_NUM = "5000ms";
181
+ (0, bun_test_1.expect)(function () { return config_1.Config.getNumber("TERRENO_CFG_NUM"); }).toThrow(/not a valid number/);
182
+ });
183
+ (0, bun_test_1.it)("supports floats", function () {
184
+ process.env.TERRENO_CFG_NUM = "3.14";
185
+ (0, bun_test_1.expect)(config_1.Config.getNumber("TERRENO_CFG_NUM")).toBe(3.14);
186
+ });
187
+ });
188
+ (0, bun_test_1.describe)("getBoolean", function () {
189
+ (0, bun_test_1.it)("returns true for 'true'", function () {
190
+ process.env.TERRENO_CFG_BOOL = "true";
191
+ (0, bun_test_1.expect)(config_1.Config.getBoolean("TERRENO_CFG_BOOL")).toBe(true);
192
+ });
193
+ (0, bun_test_1.it)("returns true for 'TRUE' (case-insensitive)", function () {
194
+ process.env.TERRENO_CFG_BOOL = "TRUE";
195
+ (0, bun_test_1.expect)(config_1.Config.getBoolean("TERRENO_CFG_BOOL")).toBe(true);
196
+ });
197
+ (0, bun_test_1.it)("returns false for 'false'", function () {
198
+ process.env.TERRENO_CFG_BOOL = "false";
199
+ (0, bun_test_1.expect)(config_1.Config.getBoolean("TERRENO_CFG_BOOL")).toBe(false);
200
+ });
201
+ (0, bun_test_1.it)("returns false when unset and default is 'false'", function () {
202
+ (0, bun_test_1.expect)(config_1.Config.getBoolean("TERRENO_CFG_BOOL")).toBe(false);
203
+ });
204
+ (0, bun_test_1.it)("returns false for any non-true string", function () {
205
+ process.env.TERRENO_CFG_BOOL = "yes";
206
+ (0, bun_test_1.expect)(config_1.Config.getBoolean("TERRENO_CFG_BOOL")).toBe(false);
207
+ });
208
+ });
209
+ (0, bun_test_1.describe)("getJSON", function () {
210
+ (0, bun_test_1.it)("parses valid JSON", function () {
211
+ process.env.TERRENO_CFG_JSON = JSON.stringify({ hook: "https://example.com" });
212
+ (0, bun_test_1.expect)(config_1.Config.getJSON("TERRENO_CFG_JSON")).toEqual({
213
+ hook: "https://example.com",
214
+ });
215
+ });
216
+ (0, bun_test_1.it)("parses the registered default", function () {
217
+ (0, bun_test_1.expect)(config_1.Config.getJSON("TERRENO_CFG_JSON")).toEqual({});
218
+ });
219
+ (0, bun_test_1.it)("returns undefined when nothing is set and no default exists", function () {
220
+ (0, bun_test_1.expect)(config_1.Config.getJSON("TERRENO_CFG_UNDEFAULTED")).toBeUndefined();
221
+ });
222
+ (0, bun_test_1.it)("throws on malformed JSON instead of silently returning undefined", function () {
223
+ process.env.TERRENO_CFG_JSON = "{not json";
224
+ (0, bun_test_1.expect)(function () { return config_1.Config.getJSON("TERRENO_CFG_JSON"); }).toThrow(/not valid JSON/);
225
+ });
226
+ });
227
+ (0, bun_test_1.describe)("registry", function () {
228
+ (0, bun_test_1.it)("exposes registered keys in sorted order", function () {
229
+ var keys = config_1.Config.getRegisteredKeys();
230
+ var sorted = __spreadArray([], __read(keys), false).sort();
231
+ (0, bun_test_1.expect)(keys).toEqual(sorted);
232
+ });
233
+ (0, bun_test_1.it)("isRegistered returns true for known keys", function () {
234
+ (0, bun_test_1.expect)(config_1.Config.isRegistered("TERRENO_CFG_STRING")).toBe(true);
235
+ });
236
+ (0, bun_test_1.it)("isRegistered returns false for unknown keys", function () {
237
+ (0, bun_test_1.expect)(config_1.Config.isRegistered("NOT_A_REAL_KEY")).toBe(false);
238
+ });
239
+ (0, bun_test_1.it)("getDefault returns the registered default", function () {
240
+ (0, bun_test_1.expect)(config_1.Config.getDefault("TERRENO_CFG_DEFAULTED")).toBe("fallback");
241
+ });
242
+ (0, bun_test_1.it)("getDefault returns undefined for keys with no default", function () {
243
+ (0, bun_test_1.expect)(config_1.Config.getDefault("TERRENO_CFG_STRING")).toBeUndefined();
244
+ });
245
+ (0, bun_test_1.it)("getRegistration exposes secret + description metadata", function () {
246
+ config_1.Config.register("TERRENO_CFG_WITH_META", {
247
+ description: "A secret thing",
248
+ secret: true,
249
+ });
250
+ var meta = config_1.Config.getRegistration("TERRENO_CFG_WITH_META");
251
+ (0, bun_test_1.expect)(meta === null || meta === void 0 ? void 0 : meta.secret).toBe(true);
252
+ (0, bun_test_1.expect)(meta === null || meta === void 0 ? void 0 : meta.description).toBe("A secret thing");
253
+ });
254
+ (0, bun_test_1.it)("re-registering the same key throws", function () {
255
+ (0, bun_test_1.expect)(function () { return config_1.Config.register("TERRENO_CFG_STRING"); }).toThrow(/registered more than once/);
256
+ });
257
+ (0, bun_test_1.it)("does not treat Object prototype keys as registered", function () {
258
+ (0, bun_test_1.expect)(config_1.Config.isRegistered("constructor")).toBe(false);
259
+ (0, bun_test_1.expect)(config_1.Config.isRegistered("toString")).toBe(false);
260
+ (0, bun_test_1.expect)(config_1.Config.isRegistered("hasOwnProperty")).toBe(false);
261
+ // And re-registering one of these names should still succeed.
262
+ (0, bun_test_1.expect)(function () { return config_1.Config.register("toString", { default: "x" }); }).not.toThrow();
263
+ (0, bun_test_1.expect)(config_1.Config.get("toString")).toBe("x");
264
+ });
265
+ });
266
+ (0, bun_test_1.describe)("clearOverrides", function () {
267
+ (0, bun_test_1.it)("removes all overrides", function () {
268
+ config_1.Config.setOverride("TERRENO_CFG_STRING", "x");
269
+ config_1.Config.setOverride("TERRENO_CFG_DEFAULTED", "y");
270
+ config_1.Config.clearOverrides();
271
+ (0, bun_test_1.expect)(config_1.Config.get("TERRENO_CFG_STRING")).toBeUndefined();
272
+ (0, bun_test_1.expect)(config_1.Config.get("TERRENO_CFG_DEFAULTED")).toBe("fallback");
273
+ });
274
+ });
275
+ (0, bun_test_1.describe)("refresh", function () {
276
+ (0, bun_test_1.it)("loads values via the registered env loader", function () { return __awaiter(void 0, void 0, void 0, function () {
277
+ return __generator(this, function (_a) {
278
+ switch (_a.label) {
279
+ case 0:
280
+ config_1.Config.setEnvLoader(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
281
+ return [2 /*return*/, ({ TERRENO_CFG_STRING: "fromLoader" })];
282
+ }); }); });
283
+ return [4 /*yield*/, config_1.Config.refresh()];
284
+ case 1:
285
+ _a.sent();
286
+ (0, bun_test_1.expect)(config_1.Config.get("TERRENO_CFG_STRING")).toBe("fromLoader");
287
+ return [2 /*return*/];
288
+ }
289
+ });
290
+ }); });
291
+ (0, bun_test_1.it)("clears the cache when no loader is registered", function () { return __awaiter(void 0, void 0, void 0, function () {
292
+ return __generator(this, function (_a) {
293
+ switch (_a.label) {
294
+ case 0:
295
+ config_1.Config.setCachedEnv({ TERRENO_CFG_STRING: "stale" });
296
+ config_1.Config.setEnvLoader(null);
297
+ return [4 /*yield*/, config_1.Config.refresh()];
298
+ case 1:
299
+ _a.sent();
300
+ (0, bun_test_1.expect)(config_1.Config.get("TERRENO_CFG_STRING")).toBeUndefined();
301
+ return [2 /*return*/];
302
+ }
303
+ });
304
+ }); });
305
+ (0, bun_test_1.it)("updates the cache on each refresh", function () { return __awaiter(void 0, void 0, void 0, function () {
306
+ var payload;
307
+ return __generator(this, function (_a) {
308
+ switch (_a.label) {
309
+ case 0:
310
+ payload = { TERRENO_CFG_STRING: "first" };
311
+ config_1.Config.setEnvLoader(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
312
+ return [2 /*return*/, payload];
313
+ }); }); });
314
+ return [4 /*yield*/, config_1.Config.refresh()];
315
+ case 1:
316
+ _a.sent();
317
+ (0, bun_test_1.expect)(config_1.Config.get("TERRENO_CFG_STRING")).toBe("first");
318
+ payload = { TERRENO_CFG_STRING: "second" };
319
+ return [4 /*yield*/, config_1.Config.refresh()];
320
+ case 2:
321
+ _a.sent();
322
+ (0, bun_test_1.expect)(config_1.Config.get("TERRENO_CFG_STRING")).toBe("second");
323
+ return [2 /*return*/];
324
+ }
325
+ });
326
+ }); });
327
+ });
328
+ });
@@ -72,6 +72,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
72
72
  return (mod && mod.__esModule) ? mod : { "default": mod };
73
73
  };
74
74
  Object.defineProperty(exports, "__esModule", { value: true });
75
+ // biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
75
76
  var bun_test_1 = require("bun:test");
76
77
  var mongoose_1 = __importStar(require("mongoose"));
77
78
  var supertest_1 = __importDefault(require("supertest"));
@@ -9,7 +9,7 @@ interface ConfigFieldMeta {
9
9
  required: boolean;
10
10
  description?: string;
11
11
  enum?: string[];
12
- default?: any;
12
+ default?: unknown;
13
13
  secret?: boolean;
14
14
  widget?: string;
15
15
  }