@voxgig/station 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +88 -0
- package/dist/src/Station.d.ts +238 -0
- package/dist/src/Station.js +1213 -0
- package/dist/src/Station.js.map +1 -0
- package/dist/src/adapter.d.ts +9 -0
- package/dist/src/adapter.js +171 -0
- package/dist/src/adapter.js.map +1 -0
- package/dist/src/config-shape.d.ts +1 -0
- package/dist/src/config-shape.js +329 -0
- package/dist/src/config-shape.js.map +1 -0
- package/dist/src/descriptor.d.ts +8 -0
- package/dist/src/descriptor.js +170 -0
- package/dist/src/descriptor.js.map +1 -0
- package/dist/src/error.d.ts +8 -0
- package/dist/src/error.js +60 -0
- package/dist/src/error.js.map +1 -0
- package/dist/src/events.d.ts +15 -0
- package/dist/src/events.js +46 -0
- package/dist/src/events.js.map +1 -0
- package/dist/src/factory.d.ts +24 -0
- package/dist/src/factory.js +79 -0
- package/dist/src/factory.js.map +1 -0
- package/dist/src/feature.d.ts +106 -0
- package/dist/src/feature.js +324 -0
- package/dist/src/feature.js.map +1 -0
- package/dist/src/index.browser.d.ts +14 -0
- package/dist/src/index.browser.js +64 -0
- package/dist/src/index.browser.js.map +1 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.js +56 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/loader.d.ts +45 -0
- package/dist/src/loader.js +181 -0
- package/dist/src/loader.js.map +1 -0
- package/dist/src/profile.d.ts +6 -0
- package/dist/src/profile.js +77 -0
- package/dist/src/profile.js.map +1 -0
- package/dist/src/profilecore.d.ts +10 -0
- package/dist/src/profilecore.js +180 -0
- package/dist/src/profilecore.js.map +1 -0
- package/dist/src/secrets.d.ts +12 -0
- package/dist/src/secrets.js +93 -0
- package/dist/src/secrets.js.map +1 -0
- package/dist/src/shape.d.ts +42 -0
- package/dist/src/shape.js +444 -0
- package/dist/src/shape.js.map +1 -0
- package/dist/src/types.d.ts +138 -0
- package/dist/src/types.js +5 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +60 -0
- package/src/Station.ts +1310 -0
- package/src/adapter.ts +183 -0
- package/src/config-shape.ts +326 -0
- package/src/descriptor.ts +179 -0
- package/src/error.ts +64 -0
- package/src/events.ts +45 -0
- package/src/factory.ts +93 -0
- package/src/feature.ts +321 -0
- package/src/index.browser.ts +56 -0
- package/src/index.ts +50 -0
- package/src/loader.ts +188 -0
- package/src/profile.ts +67 -0
- package/src/profilecore.ts +201 -0
- package/src/secrets.ts +96 -0
- package/src/shape.ts +435 -0
- package/src/types.ts +185 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* The config grammar, as data (design §4).
|
|
3
|
+
*
|
|
4
|
+
* TWO STEPS, AND THE FIRST IS WHAT MAKES THE SECOND HONEST.
|
|
5
|
+
*
|
|
6
|
+
* struct drops the unexpected-key check for a map whose spec node ends
|
|
7
|
+
* up empty - "an empty spec object means the object can be open". An
|
|
8
|
+
* optional key is `['$ONE','$NIL', spec]`, and when the data does not
|
|
9
|
+
* carry that key the validator REMOVES it from the spec node. So a
|
|
10
|
+
* block whose keys are all optional degenerates into an open map
|
|
11
|
+
* exactly when the data has none of them, and `{"solar": {"bass": 1}}`
|
|
12
|
+
* validates clean - the one property the whole exercise is for,
|
|
13
|
+
* silently absent in the one case that matters.
|
|
14
|
+
*
|
|
15
|
+
* So: normalizeConfig materializes every documented default, and
|
|
16
|
+
* validateConfig then runs a shape WITH NO OPTIONAL CONTAINERS AT ALL.
|
|
17
|
+
* After normalization every container is present, so the shape can
|
|
18
|
+
* require them, so unexpected-key detection is live at every level and
|
|
19
|
+
* every error names its path. */
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.MERGE_SENSITIVE = exports.BLOCK_DEFAULTS = exports.PROFILE_DEFAULTS = void 0;
|
|
22
|
+
exports.normalizeConfig = normalizeConfig;
|
|
23
|
+
exports.configShape = configShape;
|
|
24
|
+
exports.validateConfig = validateConfig;
|
|
25
|
+
const struct_1 = require("@voxgig/struct");
|
|
26
|
+
const sekreto_1 = require("@voxgig/sekreto");
|
|
27
|
+
const config_shape_1 = require("./config-shape");
|
|
28
|
+
const error_1 = require("./error");
|
|
29
|
+
const descriptor_1 = require("./descriptor");
|
|
30
|
+
// ---------------------------------------------------------------------
|
|
31
|
+
// The defaults table - ONE table, two callers
|
|
32
|
+
// ---------------------------------------------------------------------
|
|
33
|
+
/** Profile-level containers. Safe to materialize early either way:
|
|
34
|
+
* they are containers, and a missing one merges as empty regardless. */
|
|
35
|
+
exports.PROFILE_DEFAULTS = {
|
|
36
|
+
secrets: () => ({ providers: [{ kind: 'env' }] }),
|
|
37
|
+
api: () => ({}),
|
|
38
|
+
sdk: () => ({}),
|
|
39
|
+
feature: () => ({}),
|
|
40
|
+
};
|
|
41
|
+
/** Block-level. `feature` is a container and safe early.
|
|
42
|
+
*
|
|
43
|
+
* `active` IS NOT, and that is the whole timing rule: a default
|
|
44
|
+
* synthesized into an OVERLAY block overwrites the base's real value
|
|
45
|
+
* and silently reactivates an integration the base deliberately barred
|
|
46
|
+
* (§3.3). So the two consumers read this same table at different
|
|
47
|
+
* moments - validateConfig before, to every block, because a block with
|
|
48
|
+
* no present keys is an open map; the resolver AFTER, to the merged
|
|
49
|
+
* instance, because an absent key must stay absent through the merge. */
|
|
50
|
+
exports.BLOCK_DEFAULTS = {
|
|
51
|
+
active: () => true,
|
|
52
|
+
feature: () => ({}),
|
|
53
|
+
};
|
|
54
|
+
/** The one block key carrying the timing rule. Named rather than
|
|
55
|
+
* inferred, so a reader does not have to work out which of the two it
|
|
56
|
+
* is, and so a port can assert it. */
|
|
57
|
+
exports.MERGE_SENSITIVE = ['active'];
|
|
58
|
+
// ---------------------------------------------------------------------
|
|
59
|
+
// normalizeConfig
|
|
60
|
+
// ---------------------------------------------------------------------
|
|
61
|
+
/** Materialize every documented default, DEFENSIVELY: a node that is
|
|
62
|
+
* not the kind it expects is left alone for validate to reject with a
|
|
63
|
+
* proper message. Pure data-in/data-out, which is what makes it
|
|
64
|
+
* portable to 22 languages and expressible in the corpus.
|
|
65
|
+
*
|
|
66
|
+
* THE NORMALIZED FORM IS AN INPUT TO VALIDATION AND TO NOTHING ELSE. */
|
|
67
|
+
function normalizeConfig(raw) {
|
|
68
|
+
if (!ismap(raw)) {
|
|
69
|
+
return raw;
|
|
70
|
+
}
|
|
71
|
+
const out = { ...raw };
|
|
72
|
+
if (undefined === out.station) {
|
|
73
|
+
out.station = 1;
|
|
74
|
+
}
|
|
75
|
+
if (undefined === out.profiles) {
|
|
76
|
+
out.profiles = {};
|
|
77
|
+
}
|
|
78
|
+
if (!ismap(out.profiles)) {
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
const profiles = {};
|
|
82
|
+
for (const pname of Object.keys(out.profiles)) {
|
|
83
|
+
const p = out.profiles[pname];
|
|
84
|
+
if (!ismap(p)) {
|
|
85
|
+
profiles[pname] = p;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const prof = { ...p };
|
|
89
|
+
for (const k of Object.keys(exports.PROFILE_DEFAULTS)) {
|
|
90
|
+
if (undefined === prof[k]) {
|
|
91
|
+
prof[k] = exports.PROFILE_DEFAULTS[k]();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// A `secrets` written without `providers` still gets the chain.
|
|
95
|
+
if (ismap(prof.secrets) && undefined === prof.secrets.providers) {
|
|
96
|
+
prof.secrets = { ...prof.secrets, providers: [{ kind: 'env' }] };
|
|
97
|
+
}
|
|
98
|
+
prof.feature = normfeatures(prof.feature);
|
|
99
|
+
for (const bkey of ['api', 'sdk']) {
|
|
100
|
+
if (!ismap(prof[bkey])) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const blocks = {};
|
|
104
|
+
for (const ref of Object.keys(prof[bkey])) {
|
|
105
|
+
const b = prof[bkey][ref];
|
|
106
|
+
if (!ismap(b)) {
|
|
107
|
+
blocks[ref] = b;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const block = { ...b };
|
|
111
|
+
for (const k of Object.keys(exports.BLOCK_DEFAULTS)) {
|
|
112
|
+
if (undefined === block[k]) {
|
|
113
|
+
block[k] = exports.BLOCK_DEFAULTS[k]();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
block.feature = normfeatures(block.feature);
|
|
117
|
+
blocks[ref] = block;
|
|
118
|
+
}
|
|
119
|
+
prof[bkey] = blocks;
|
|
120
|
+
}
|
|
121
|
+
profiles[pname] = prof;
|
|
122
|
+
}
|
|
123
|
+
out.profiles = profiles;
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
/** Per feature entry, at every level: `active` -> true.
|
|
127
|
+
*
|
|
128
|
+
* A FEATURE NAMED IN THE CONFIG IS ONE YOU ARE ASKING FOR. The SDK's
|
|
129
|
+
* own default is `active: false` for all but `log`, and
|
|
130
|
+
* `{"retry": {"retries": 3}}` plainly means "retry, with three
|
|
131
|
+
* attempts". It also keeps the feature map closed, for the same reason
|
|
132
|
+
* every other block needs one present key.
|
|
133
|
+
*
|
|
134
|
+
* Defensive like the rest: a non-map is returned untouched for validate
|
|
135
|
+
* to reject by path. */
|
|
136
|
+
function normfeatures(f) {
|
|
137
|
+
if (!ismap(f)) {
|
|
138
|
+
return f;
|
|
139
|
+
}
|
|
140
|
+
const out = {};
|
|
141
|
+
for (const name of Object.keys(f)) {
|
|
142
|
+
const e = f[name];
|
|
143
|
+
out[name] = ismap(e) && undefined === e.active ? { ...e, active: true } : e;
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
// ---------------------------------------------------------------------
|
|
148
|
+
// validateConfig
|
|
149
|
+
// ---------------------------------------------------------------------
|
|
150
|
+
/** `spec/config-shape.json`, §4.3 verbatim, through the shipped mirror.
|
|
151
|
+
* Every validate gets a CLONE: struct's validate consumes the spec it
|
|
152
|
+
* walks (it deletes satisfied `$ONE` branches as it goes), so handing it
|
|
153
|
+
* the module constant twice would validate the second config against a
|
|
154
|
+
* spec the first had already eaten. */
|
|
155
|
+
function configShape() {
|
|
156
|
+
return (0, struct_1.clone)(config_shape_1.CONFIG_SHAPE);
|
|
157
|
+
}
|
|
158
|
+
/** Credential-shaped keys (§5.2). `secret` is here AND is the one
|
|
159
|
+
* exempt key - see secretvalue below; a blanket deny would reject the
|
|
160
|
+
* very mechanism that keeps values out of the file. */
|
|
161
|
+
const CREDENTIAL_KEYS = [
|
|
162
|
+
'apikey', 'auth', 'authorization', 'token',
|
|
163
|
+
'secret', 'password', 'credential', 'bearer',
|
|
164
|
+
];
|
|
165
|
+
/** The suffix rule catches `access_key`, `X-Api-Token` and friends in
|
|
166
|
+
* one rule rather than a growing list of spellings. */
|
|
167
|
+
const CREDENTIAL_SUFFIX = ['_KEY', '_TOKEN', '_SECRET', '_PASSWORD'];
|
|
168
|
+
/** §5.2's backstop, and it is stated as one rather than as a grammar.
|
|
169
|
+
* `validname()` is a NAME grammar, not a credential filter: it rejects
|
|
170
|
+
* uppercase, hyphens, `+`, `/` and `=`, so it excludes most real
|
|
171
|
+
* credential formats - but a lowercase hex token passes it cleanly. A
|
|
172
|
+
* character class cannot tell a name from a secret.
|
|
173
|
+
*
|
|
174
|
+
* Derived names break on every separator (`voxgig_solardemo.apikey`
|
|
175
|
+
* runs 6/9/6) and a hand-written name for a human to read does too; a
|
|
176
|
+
* 24-character unbroken run is not a name anybody writes. Note this is
|
|
177
|
+
* a RUN bound, not a length bound: `acme_internal_billing_service.apikey`
|
|
178
|
+
* is 36 characters and passes, which is the false positive a naive
|
|
179
|
+
* length bound would produce. */
|
|
180
|
+
const RUN_BOUND = 24;
|
|
181
|
+
const UNBROKEN_RUN = new RegExp('[A-Za-z0-9]{' + RUN_BOUND + ',}');
|
|
182
|
+
/** Normalize, then validate (§4.2). Raises `station_config_invalid`
|
|
183
|
+
* with EVERY struct error at once - an eighteen-instance config that
|
|
184
|
+
* touches three of them must not die because the eighteenth has a
|
|
185
|
+
* typo'd package name - then the §5.2 scans.
|
|
186
|
+
*
|
|
187
|
+
* Takes the NORMALIZED form. Handing it a raw config is the mistake
|
|
188
|
+
* §4.2 exists to prevent, so callers go through open()/loadConfig. */
|
|
189
|
+
function validateConfig(normalized) {
|
|
190
|
+
const errs = [];
|
|
191
|
+
(0, struct_1.validate)(normalized, configShape(), { errs });
|
|
192
|
+
if (0 < errs.length) {
|
|
193
|
+
throw new error_1.StationError('station_config_invalid', errs.join('; ') + renamehint(normalized));
|
|
194
|
+
}
|
|
195
|
+
scanConfig(normalized);
|
|
196
|
+
return normalized;
|
|
197
|
+
}
|
|
198
|
+
/** `plugin` is REMOVED, not aliased (§3.4) - a deprecated alias would
|
|
199
|
+
* be a second grammar for one concept in 17 ports. The shape already
|
|
200
|
+
* rejects it as an unexpected key; this says what to rename, because
|
|
201
|
+
* "unexpected key: plugin" alone does not, and the migration for a
|
|
202
|
+
* single-instance project is exactly this one rename. */
|
|
203
|
+
function renamehint(cfg) {
|
|
204
|
+
const profiles = ismap(cfg) && ismap(cfg.profiles) ? cfg.profiles : {};
|
|
205
|
+
const hit = Object.keys(profiles)
|
|
206
|
+
.filter((p) => ismap(profiles[p]) && undefined !== profiles[p].plugin);
|
|
207
|
+
if (0 === hit.length) {
|
|
208
|
+
return '';
|
|
209
|
+
}
|
|
210
|
+
return '; rename `plugin` to `sdk` in ' +
|
|
211
|
+
hit.map((p) => 'profiles.' + p).join(', ') +
|
|
212
|
+
' - the keys are unchanged, an untagged ref IS an api slug (§3.4)';
|
|
213
|
+
}
|
|
214
|
+
/** The §5.2 scans, over the parts of the grammar that hold arbitrary
|
|
215
|
+
* data. Everything else is closed by construction and needs no scan. */
|
|
216
|
+
function scanConfig(cfg) {
|
|
217
|
+
const secrets = [];
|
|
218
|
+
const reserved = [];
|
|
219
|
+
const invalid = [];
|
|
220
|
+
const profiles = ismap(cfg) && ismap(cfg.profiles) ? cfg.profiles : {};
|
|
221
|
+
for (const pname of Object.keys(profiles)) {
|
|
222
|
+
const prof = profiles[pname];
|
|
223
|
+
if (!ismap(prof)) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
const ppath = 'profiles.' + pname;
|
|
227
|
+
checkfeatures(prof.feature, ppath + '.feature', secrets, reserved, invalid);
|
|
228
|
+
for (const bkey of ['api', 'sdk']) {
|
|
229
|
+
if (!ismap(prof[bkey])) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
for (const ref of Object.keys(prof[bkey])) {
|
|
233
|
+
const block = prof[bkey][ref];
|
|
234
|
+
if (!ismap(block)) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
const bpath = ppath + '.' + bkey + '.' + ref;
|
|
238
|
+
// The block's own `secret` holds a NAME. resolveProfile checks
|
|
239
|
+
// it again per instance (station_secret_name); this catches it
|
|
240
|
+
// at open(), for the whole file at once.
|
|
241
|
+
if (undefined !== block.secret) {
|
|
242
|
+
secretvalue(block.secret, bpath + '.secret', secrets);
|
|
243
|
+
}
|
|
244
|
+
// `options` is passthrough to a generated constructor, so it is
|
|
245
|
+
// the one place a value can hide.
|
|
246
|
+
scan(block.options, bpath + '.options', secrets, reserved);
|
|
247
|
+
checkfeatures(block.feature, bpath + '.feature', secrets, reserved, invalid);
|
|
248
|
+
// §4.4's explicit checks, applied where the shape cannot reach,
|
|
249
|
+
// raising the same code the shape would - and pinned in the
|
|
250
|
+
// corpus so each workaround is removed deliberately when struct
|
|
251
|
+
// is fixed rather than forgotten.
|
|
252
|
+
checkpolicy(block.policy, bpath + '.policy', invalid);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (0 < invalid.length) {
|
|
257
|
+
throw new error_1.StationError('station_config_invalid', invalid.join('; '));
|
|
258
|
+
}
|
|
259
|
+
if (0 < reserved.length) {
|
|
260
|
+
throw new error_1.StationError('station_feature_reserved', reserved.join('; '));
|
|
261
|
+
}
|
|
262
|
+
if (0 < secrets.length) {
|
|
263
|
+
throw new error_1.StationError('station_config_secret', secrets.join('; '));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/** A feature map at any level. `station` is reserved: station composes
|
|
267
|
+
* its own wrap and a config that reconfigures it is asking for a state
|
|
268
|
+
* the ordering rules cannot express (§8.4). */
|
|
269
|
+
function checkfeatures(f, path, secrets, reserved, invalid) {
|
|
270
|
+
if (!ismap(f)) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
for (const name of Object.keys(f)) {
|
|
274
|
+
const fpath = path + '.' + name;
|
|
275
|
+
if ('station' === name) {
|
|
276
|
+
reserved.push(path + '.station is reserved: station composes its own ' +
|
|
277
|
+
'wrap and it cannot be configured from station.json');
|
|
278
|
+
}
|
|
279
|
+
const order = ismap(f[name]) ? f[name].order : undefined;
|
|
280
|
+
if (ismap(order)) {
|
|
281
|
+
firstelement(order.before, fpath + '.order.before', invalid);
|
|
282
|
+
firstelement(order.after, fpath + '.order.after', invalid);
|
|
283
|
+
}
|
|
284
|
+
scan(f[name], fpath, secrets, reserved);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/** The policy block's §4.4 workarounds, in one place because they are
|
|
288
|
+
* one class of gap: struct cannot check what its own defects hide.
|
|
289
|
+
*
|
|
290
|
+
* - `hosts`, `allow.op` and `allow.method` are `$CHILD` string lists,
|
|
291
|
+
* so element 0 escapes the shape (see firstelement below).
|
|
292
|
+
* - `budget` is a map whose keys are ALL optional scalars, and struct
|
|
293
|
+
* removes an unsatisfied optional key from the spec node - so
|
|
294
|
+
* `budget: {rp: 1}` degenerates the spec into an open map and the
|
|
295
|
+
* typo passes. `allow` does not have this problem (its `$CHILD` keys
|
|
296
|
+
* stay in the spec whether or not the data carries them, keeping the
|
|
297
|
+
* map closed), and neither does `policy` itself (`hosts` anchors it);
|
|
298
|
+
* `budget` alone needs the explicit unexpected-key check, phrased as
|
|
299
|
+
* struct would phrase it. */
|
|
300
|
+
const BUDGET_KEYS = ['concurrency', 'rps'];
|
|
301
|
+
function checkpolicy(policy, path, invalid) {
|
|
302
|
+
if (!ismap(policy)) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
firstelement(policy.hosts, path + '.hosts', invalid);
|
|
306
|
+
const allow = policy.allow;
|
|
307
|
+
if (ismap(allow)) {
|
|
308
|
+
firstelement(allow.op, path + '.allow.op', invalid);
|
|
309
|
+
firstelement(allow.method, path + '.allow.method', invalid);
|
|
310
|
+
}
|
|
311
|
+
const budget = policy.budget;
|
|
312
|
+
if (ismap(budget)) {
|
|
313
|
+
const unknown = Object.keys(budget)
|
|
314
|
+
.filter((k) => !BUDGET_KEYS.includes(k)).sort();
|
|
315
|
+
if (0 < unknown.length) {
|
|
316
|
+
invalid.push('Unexpected keys at field ' + path + '.budget: ' +
|
|
317
|
+
unknown.join(', '));
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/** §4.4: `$CHILD` in list mode DOES NOT VALIDATE ELEMENT 0. Verified:
|
|
322
|
+
* `["a", 1]` fails at index 1, `[1]` passes. validate_CHILD installs the
|
|
323
|
+
* template across the data's indices, sets keyI = 0 and returns element
|
|
324
|
+
* 0 as the slot value, and the injection loop resumes past it. An
|
|
325
|
+
* upstream struct defect; filing it is a deliverable of this plan.
|
|
326
|
+
*
|
|
327
|
+
* It reaches EVERY string list in the shape. §4.4 says the only one left
|
|
328
|
+
* is `policy.hosts` because the profile-level `order` list is gone - but
|
|
329
|
+
* §8.4's per-feature `order` carries `before`/`after` string lists, so
|
|
330
|
+
* the reach is three lists, not one, and §4.5 pins `order: [7]`
|
|
331
|
+
* accordingly. Applied where the shape cannot reach, raising the same
|
|
332
|
+
* code the shape would, and pinned in the corpus so the workaround is
|
|
333
|
+
* removed deliberately when struct is fixed rather than forgotten. */
|
|
334
|
+
function firstelement(list, path, invalid) {
|
|
335
|
+
if (!Array.isArray(list) || 0 === list.length) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if ('string' === typeof list[0]) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
invalid.push('Expected field ' + path + '.0 to be string, but found ' +
|
|
342
|
+
kindof(list[0]) + ': ' + JSON.stringify(list[0]));
|
|
343
|
+
}
|
|
344
|
+
/** Recursive over EVERY nested map and list, not just the top level -
|
|
345
|
+
* a credential one level down is the case a top-level scan misses. */
|
|
346
|
+
function scan(node, path, secrets, reserved) {
|
|
347
|
+
if (Array.isArray(node)) {
|
|
348
|
+
for (let i = 0; i < node.length; i++) {
|
|
349
|
+
scan(node[i], path + '.' + i, secrets, reserved);
|
|
350
|
+
}
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if ('string' === typeof node) {
|
|
354
|
+
userinfo(node, path, secrets);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (!ismap(node)) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
for (const key of Object.keys(node)) {
|
|
361
|
+
const kpath = path + '.' + key;
|
|
362
|
+
const val = node[key];
|
|
363
|
+
// §8.6: station owns feature composition, so an `options.feature`
|
|
364
|
+
// in a declarative config is a second, unreconciled ordering input.
|
|
365
|
+
if ('feature' === key) {
|
|
366
|
+
reserved.push(kpath + ' is reserved: configure features under the ' +
|
|
367
|
+
'block\'s own `feature` key, not through `options`');
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if ('secret' === key.toLowerCase()) {
|
|
371
|
+
secretvalue(val, kpath, secrets);
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (credentialkey(key)) {
|
|
375
|
+
secrets.push(kpath + ' is a credential-shaped key: station.json ' +
|
|
376
|
+
'holds secret NAMES, never values (§5.2)');
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
scan(val, kpath, secrets, reserved);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
function credentialkey(key) {
|
|
383
|
+
const low = String(key).toLowerCase().replace(/[^a-z0-9]+/g, '');
|
|
384
|
+
if (CREDENTIAL_KEYS.includes(low)) {
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
const tok = (0, descriptor_1.envtoken)(key);
|
|
388
|
+
return CREDENTIAL_SUFFIX.some((s) => tok.endsWith(s));
|
|
389
|
+
}
|
|
390
|
+
/** A `secret`-named key holds a NAME, and that exemption is not a
|
|
391
|
+
* loophole - it is the whole design. Two checks, not one, and they live
|
|
392
|
+
* in the same three lines precisely so a port cannot implement only the
|
|
393
|
+
* first and inherit the gap the second exists to close. */
|
|
394
|
+
function secretvalue(val, path, secrets) {
|
|
395
|
+
if ('string' !== typeof val) {
|
|
396
|
+
secrets.push(path + ' must be a secret name (a string), but found ' +
|
|
397
|
+
kindof(val));
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (!(0, sekreto_1.validname)(val)) {
|
|
401
|
+
secrets.push(path + ' is not a valid sekreto name, so it cannot be a ' +
|
|
402
|
+
'name and must not be a value: ' + JSON.stringify(val));
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
if (UNBROKEN_RUN.test(val)) {
|
|
406
|
+
secrets.push(path + ' contains an unbroken alphanumeric run of ' +
|
|
407
|
+
RUN_BOUND + ' or more characters, which is not a name anybody writes');
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
/** One rule about values rather than keys, because the `proxy` feature
|
|
411
|
+
* makes it concrete: `http://user:pass@proxy.internal:8080`. */
|
|
412
|
+
function userinfo(val, path, secrets) {
|
|
413
|
+
if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(val)) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
let u;
|
|
417
|
+
try {
|
|
418
|
+
u = new URL(val);
|
|
419
|
+
}
|
|
420
|
+
catch (e) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
if ('' !== u.username || '' !== u.password) {
|
|
424
|
+
secrets.push(path + ' is a URL carrying userinfo, which puts a ' +
|
|
425
|
+
'credential in the config file; use the proxy feature\'s ' +
|
|
426
|
+
'`fromEnv` option instead (§8.6)');
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
function kindof(v) {
|
|
430
|
+
if (null === v) {
|
|
431
|
+
return 'null';
|
|
432
|
+
}
|
|
433
|
+
if (Array.isArray(v)) {
|
|
434
|
+
return 'list';
|
|
435
|
+
}
|
|
436
|
+
if ('number' === typeof v) {
|
|
437
|
+
return Number.isInteger(v) ? 'integer' : 'decimal';
|
|
438
|
+
}
|
|
439
|
+
return typeof v;
|
|
440
|
+
}
|
|
441
|
+
function ismap(v) {
|
|
442
|
+
return null != v && 'object' === typeof v && !Array.isArray(v);
|
|
443
|
+
}
|
|
444
|
+
//# sourceMappingURL=shape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape.js","sourceRoot":"","sources":["../../src/shape.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
export type StationOptions = {
|
|
2
|
+
profile?: string;
|
|
3
|
+
proxy?: 'auto' | 'off' | 'require' | string;
|
|
4
|
+
folder?: string;
|
|
5
|
+
config?: StationConfig | null;
|
|
6
|
+
load?: boolean;
|
|
7
|
+
repoScoped?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type StationConfig = {
|
|
10
|
+
station: number;
|
|
11
|
+
profiles?: Record<string, Profile>;
|
|
12
|
+
};
|
|
13
|
+
export type Profile = {
|
|
14
|
+
secrets?: {
|
|
15
|
+
providers?: any[];
|
|
16
|
+
};
|
|
17
|
+
feature?: Record<string, any>;
|
|
18
|
+
api?: Record<string, SdkBlock>;
|
|
19
|
+
sdk?: Record<string, SdkBlock>;
|
|
20
|
+
};
|
|
21
|
+
export type PolicyBlock = {
|
|
22
|
+
hosts?: string[];
|
|
23
|
+
allow?: {
|
|
24
|
+
op?: string[];
|
|
25
|
+
method?: string[];
|
|
26
|
+
};
|
|
27
|
+
budget?: {
|
|
28
|
+
rps?: number;
|
|
29
|
+
concurrency?: number;
|
|
30
|
+
};
|
|
31
|
+
mode?: 'live' | 'record' | 'replay' | 'mock' | 'block';
|
|
32
|
+
};
|
|
33
|
+
export type SdkBlock = {
|
|
34
|
+
package?: string;
|
|
35
|
+
export?: string;
|
|
36
|
+
base?: string;
|
|
37
|
+
secret?: string;
|
|
38
|
+
resolve?: 'library' | 'proxy';
|
|
39
|
+
policy?: PolicyBlock;
|
|
40
|
+
capture?: 'meta' | 'headers' | 'full';
|
|
41
|
+
agent?: {
|
|
42
|
+
write: boolean;
|
|
43
|
+
};
|
|
44
|
+
feature?: Record<string, any>;
|
|
45
|
+
options?: Record<string, any>;
|
|
46
|
+
active?: boolean;
|
|
47
|
+
};
|
|
48
|
+
export type Descriptor = {
|
|
49
|
+
station: 1;
|
|
50
|
+
name: string;
|
|
51
|
+
slug: string;
|
|
52
|
+
envtoken: string;
|
|
53
|
+
version: string;
|
|
54
|
+
target: string;
|
|
55
|
+
base: string;
|
|
56
|
+
server: {
|
|
57
|
+
name: string;
|
|
58
|
+
value: string;
|
|
59
|
+
}[];
|
|
60
|
+
auth: {
|
|
61
|
+
active: boolean;
|
|
62
|
+
prefix: string;
|
|
63
|
+
secretname: string;
|
|
64
|
+
};
|
|
65
|
+
entities: Record<string, DescriptorEntity>;
|
|
66
|
+
features: {
|
|
67
|
+
name: string;
|
|
68
|
+
active: boolean;
|
|
69
|
+
options?: Record<string, any>;
|
|
70
|
+
transport?: string;
|
|
71
|
+
}[];
|
|
72
|
+
};
|
|
73
|
+
export type DescriptorEntity = {
|
|
74
|
+
fields: Record<string, {
|
|
75
|
+
kind: string;
|
|
76
|
+
}>;
|
|
77
|
+
ops: Record<string, {
|
|
78
|
+
points: DescriptorPoint[];
|
|
79
|
+
}>;
|
|
80
|
+
};
|
|
81
|
+
export type DescriptorPoint = {
|
|
82
|
+
method: string;
|
|
83
|
+
path: string;
|
|
84
|
+
params: string[];
|
|
85
|
+
select?: Record<string, any>;
|
|
86
|
+
};
|
|
87
|
+
export type StationEvent = {
|
|
88
|
+
t: number;
|
|
89
|
+
plugin?: string;
|
|
90
|
+
api?: string;
|
|
91
|
+
corr?: string;
|
|
92
|
+
kind: 'construct' | 'op' | 'http' | 'error' | 'feature' | 'station';
|
|
93
|
+
op?: {
|
|
94
|
+
entity: string;
|
|
95
|
+
op: string;
|
|
96
|
+
outcome: string;
|
|
97
|
+
durationMs: number;
|
|
98
|
+
};
|
|
99
|
+
http?: {
|
|
100
|
+
method: string;
|
|
101
|
+
host: string;
|
|
102
|
+
path: string;
|
|
103
|
+
status: number;
|
|
104
|
+
durationMs: number;
|
|
105
|
+
bytes: number;
|
|
106
|
+
};
|
|
107
|
+
err?: {
|
|
108
|
+
code?: string;
|
|
109
|
+
status?: number;
|
|
110
|
+
message: string;
|
|
111
|
+
};
|
|
112
|
+
meta?: Record<string, any>;
|
|
113
|
+
};
|
|
114
|
+
export type Binding = {
|
|
115
|
+
plugin: string;
|
|
116
|
+
instance?: string;
|
|
117
|
+
base?: string;
|
|
118
|
+
placeholder?: string;
|
|
119
|
+
secretname?: string;
|
|
120
|
+
rung: 'none' | 'R1';
|
|
121
|
+
};
|
|
122
|
+
export type PluginEntry = {
|
|
123
|
+
name: string;
|
|
124
|
+
api: string;
|
|
125
|
+
descriptor: Descriptor;
|
|
126
|
+
rung: 'none' | 'R1';
|
|
127
|
+
client: any;
|
|
128
|
+
warnings: string[];
|
|
129
|
+
secretname?: string;
|
|
130
|
+
};
|
|
131
|
+
export type ResolvedInstance = {
|
|
132
|
+
name: string;
|
|
133
|
+
api: string;
|
|
134
|
+
active: boolean;
|
|
135
|
+
live: boolean;
|
|
136
|
+
rung: 'none' | 'R1';
|
|
137
|
+
block: SdkBlock;
|
|
138
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":";AAAA,yEAAyE;AACzE,uEAAuE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@voxgig/station",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "station: one control surface for outbound integrations (TypeScript, canonical)",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"types": "dist/src/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/src/index.d.ts",
|
|
11
|
+
"browser": "./dist/src/index.browser.js",
|
|
12
|
+
"default": "./dist/src/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/voxgig/station",
|
|
17
|
+
"keywords": [
|
|
18
|
+
"voxgig",
|
|
19
|
+
"station",
|
|
20
|
+
"sdk",
|
|
21
|
+
"integration",
|
|
22
|
+
"api-client",
|
|
23
|
+
"plugins"
|
|
24
|
+
],
|
|
25
|
+
"author": "",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git://github.com/voxgig/station.git"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc",
|
|
32
|
+
"clean": "rm -rf dist coverage node_modules package-lock.json",
|
|
33
|
+
"test": "npm run build && node --test \"dist/test/*.test.js\"",
|
|
34
|
+
"test-some": "npm run build && node --test --test-name-pattern=$npm_config_pattern \"dist/test/*.test.js\"",
|
|
35
|
+
"watch": "tsc -w",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"reset": "npm run clean && npm i && npm run build && npm test",
|
|
38
|
+
"repo-tag": "REPO_VERSION=`node -e \"console.log(require('./package').version)\"` && echo TAG: typescript/v$REPO_VERSION && git commit -a -m typescript/v$REPO_VERSION && git push && git tag typescript/v$REPO_VERSION && git push --tags;",
|
|
39
|
+
"repo-publish": "npm run clean && npm i && npm run repo-publish-quick",
|
|
40
|
+
"repo-publish-quick": "npm run build && npm run test && npm run repo-tag && npm stage publish --access public --registry https://registry.npmjs.org",
|
|
41
|
+
"repo-publish-dry": "npm run clean && npm i && npm run repo-publish-dry-quick",
|
|
42
|
+
"repo-publish-dry-quick": "npm run build && npm run test && npm stage publish --dry-run --access public --registry https://registry.npmjs.org"
|
|
43
|
+
},
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"files": [
|
|
46
|
+
"LICENSE",
|
|
47
|
+
"src",
|
|
48
|
+
"!src/omnihome.*",
|
|
49
|
+
"dist/src",
|
|
50
|
+
"!dist/src/omnihome.*"
|
|
51
|
+
],
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@voxgig/sekreto": "^0.1.0",
|
|
54
|
+
"@voxgig/struct": "^0.2.2"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "^22.0.0",
|
|
58
|
+
"typescript": "^5.5.0"
|
|
59
|
+
}
|
|
60
|
+
}
|