@camstack/types 1.2.327 → 1.2.329
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/dist/addon/base-addon.d.ts +58 -0
- package/dist/addon/secret-aware-context.d.ts +27 -0
- package/dist/addon/secret-paths.d.ts +48 -0
- package/dist/addon/secret-settings-view.d.ts +80 -0
- package/dist/addon.d.ts +1 -0
- package/dist/addon.js +52 -4
- package/dist/addon.mjs +27 -5
- package/dist/capabilities/addons.cap.d.ts +4 -4
- package/dist/capabilities/alarm-panel.cap.d.ts +8 -8
- package/dist/capabilities/alerts.cap.d.ts +5 -5
- package/dist/capabilities/control.cap.d.ts +11 -11
- package/dist/capabilities/day-night.cap.d.ts +16 -16
- package/dist/capabilities/device-manager.cap.d.ts +2 -0
- package/dist/capabilities/enum-sensor.cap.d.ts +4 -4
- package/dist/capabilities/index.d.ts +6 -2
- package/dist/capabilities/integrations.cap.d.ts +6 -3
- package/dist/capabilities/keychain-admin.cap.d.ts +312 -0
- package/dist/capabilities/keychain.cap.d.ts +180 -0
- package/dist/capabilities/lawn-mower-control.cap.d.ts +4 -4
- package/dist/capabilities/log-channels.cap.d.ts +1 -1
- package/dist/capabilities/log-destination.cap.d.ts +5 -5
- package/dist/capabilities/notification-rules.cap.d.ts +118 -118
- package/dist/capabilities/osd-manager.cap.d.ts +11 -11
- package/dist/capabilities/osd.cap.d.ts +5 -5
- package/dist/capabilities/pipeline-analytics.cap.d.ts +1291 -118
- package/dist/capabilities/pipeline-orchestrator.cap.d.ts +384 -0
- package/dist/capabilities/pipeline-runner.cap.d.ts +145 -2
- package/dist/capabilities/storage.cap.d.ts +1 -1
- package/dist/capabilities/system.cap.d.ts +16 -16
- package/dist/capabilities/webrtc-session.cap.d.ts +3 -3
- package/dist/client.js +37 -1
- package/dist/client.mjs +37 -1
- package/dist/device/device-context.d.ts +11 -1
- package/dist/device/index.d.ts +1 -1
- package/dist/enums/event-category.d.ts +7 -0
- package/dist/enums.js +1 -1
- package/dist/enums.mjs +1 -1
- package/dist/{event-category-CtBAl88K.js → event-category-CHduMGvg.js} +7 -0
- package/dist/{event-category-B_0XEcLF.mjs → event-category-DaIZi00R.mjs} +7 -0
- package/dist/generated/addon-api.d.ts +289 -0
- package/dist/generated/cap-input-defaults.d.ts +1 -1
- package/dist/generated/capability-router-map.d.ts +8 -2
- package/dist/generated/method-access-map.d.ts +1 -1
- package/dist/generated/method-device-selectors.d.ts +1 -1
- package/dist/generated/system-proxy.d.ts +4 -2
- package/dist/health/wiring-health.d.ts +8 -8
- package/dist/index.d.ts +6 -2
- package/dist/index.js +1881 -101
- package/dist/index.mjs +1766 -98
- package/dist/interfaces/addon.d.ts +25 -0
- package/dist/interfaces/config-ui-secrets.d.ts +12 -0
- package/dist/interfaces/config-ui.d.ts +29 -1
- package/dist/interfaces/event-bus.d.ts +11 -0
- package/dist/interfaces/pipeline-runner-capability.d.ts +18 -0
- package/dist/keychain/index.d.ts +5 -0
- package/dist/keychain/known-secrets.d.ts +31 -0
- package/dist/keychain/resolve-secret-views.d.ts +2 -0
- package/dist/keychain/secret-marker.d.ts +52 -0
- package/dist/keychain/secret-ref.d.ts +62 -0
- package/dist/keychain/secrets-access.d.ts +77 -0
- package/dist/logging/log-channel.d.ts +2 -2
- package/dist/pipeline/auto-tune/plan-schemas.d.ts +123 -0
- package/dist/pipeline/auto-tune/recommend.d.ts +26 -1
- package/dist/pipeline/load-balancing/schemas.d.ts +8 -7
- package/dist/pipeline/node-benchmark/benchmark-mode.d.ts +57 -0
- package/dist/pipeline/node-benchmark/index.d.ts +9 -0
- package/dist/pipeline/node-benchmark/results.d.ts +52 -0
- package/dist/pipeline/node-benchmark/schemas.d.ts +848 -0
- package/dist/pipeline/node-benchmark/step-tree.d.ts +23 -0
- package/dist/{sleep-az1qiFzK.js → sleep-ClNXPpWR.js} +1580 -121
- package/dist/{sleep-B0JG7-_F.mjs → sleep-DujUvOcb.mjs} +1340 -121
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_event_category = require("./event-category-
|
|
1
|
+
const require_event_category = require("./event-category-CHduMGvg.js");
|
|
2
2
|
let zod = require("zod");
|
|
3
3
|
//#region src/audio/audio-chunk-format.ts
|
|
4
4
|
/**
|
|
@@ -116,6 +116,192 @@ function buildAlawTable() {
|
|
|
116
116
|
var ULAW_TABLE = buildUlawTable();
|
|
117
117
|
var ALAW_TABLE = buildAlawTable();
|
|
118
118
|
//#endregion
|
|
119
|
+
//#region src/interfaces/config-ui-secrets.ts
|
|
120
|
+
/**
|
|
121
|
+
* The value a redacted secret is replaced with on every read surface.
|
|
122
|
+
*
|
|
123
|
+
* It is also the WRITE-BACK token: an `upsertLocation` that sends this value
|
|
124
|
+
* back for a key means "keep what is stored", which is what lets an operator
|
|
125
|
+
* rename a location without retyping its password. A literal an operator could
|
|
126
|
+
* plausibly choose as a real password would turn that convenience into a way
|
|
127
|
+
* to lock yourself out, hence the sentinel shape.
|
|
128
|
+
*/
|
|
129
|
+
var REDACTED_SECRET = "__camstack_redacted__";
|
|
130
|
+
/** Is this field's stored value a credential? */
|
|
131
|
+
function isSecretConfigField(field) {
|
|
132
|
+
if (field.type === "password") return true;
|
|
133
|
+
return "secret" in field && field.secret === true;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Is this field's value stored in the KEYCHAIN (D787)? Every secret field is,
|
|
137
|
+
* by its TYPE, unless it opts out with `storage: 'inline'` — never by its name.
|
|
138
|
+
*/
|
|
139
|
+
function isKeychainStoredField(field) {
|
|
140
|
+
if (!isSecretConfigField(field)) return false;
|
|
141
|
+
if (field.type === "textarea" && field.isJson === true) return false;
|
|
142
|
+
return !("storage" in field && field.storage === "inline");
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Every config key in `schema` whose value is a secret, including keys nested
|
|
146
|
+
* inside `group` / `sub-tabs` containers.
|
|
147
|
+
*/
|
|
148
|
+
function collectSecretConfigKeys(schema) {
|
|
149
|
+
const keys = /* @__PURE__ */ new Set();
|
|
150
|
+
for (const section of sectionsOf(schema)) for (const field of fieldsOf(section)) walkField(field, keys);
|
|
151
|
+
return keys;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Every top-level config key whose value the KEYCHAIN stores (D787) —
|
|
155
|
+
* {@link collectSecretConfigKeys} minus `storage: 'inline'` and minus JSON
|
|
156
|
+
* blobs (see {@link isKeychainStoredField}). What a form sends as
|
|
157
|
+
* `secretKeys` so the owner's door knows which values to keep out of the blob.
|
|
158
|
+
*/
|
|
159
|
+
function collectKeychainStoredKeys(schema) {
|
|
160
|
+
const keys = /* @__PURE__ */ new Set();
|
|
161
|
+
for (const section of sectionsOf(schema)) for (const field of fieldsOf(section)) walkKeychainField(field, keys);
|
|
162
|
+
return keys;
|
|
163
|
+
}
|
|
164
|
+
function walkKeychainField(field, out) {
|
|
165
|
+
if (!isRecord$2(field)) return;
|
|
166
|
+
const type = field["type"];
|
|
167
|
+
const key = field["key"];
|
|
168
|
+
const isSecret = type === "password" || field["secret"] === true;
|
|
169
|
+
const isJsonBlob = type === "textarea" && field["isJson"] === true;
|
|
170
|
+
if (isSecret && !isJsonBlob && field["storage"] !== "inline" && typeof key === "string" && key.length > 0) out.add(key);
|
|
171
|
+
if (type === "group") {
|
|
172
|
+
for (const child of fieldsOf(field)) walkKeychainField(child, out);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (type === "sub-tabs") {
|
|
176
|
+
const tabs = field["tabs"];
|
|
177
|
+
if (!Array.isArray(tabs)) return;
|
|
178
|
+
for (const tab of tabs) for (const child of fieldsOf(tab)) walkKeychainField(child, out);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* True when the value is a readable schema declaring at least one field of any
|
|
183
|
+
* kind. Lets a caller tell "this provider has no secrets" from "this provider's
|
|
184
|
+
* schema could not be read", which are the same empty set otherwise.
|
|
185
|
+
*/
|
|
186
|
+
function schemaDeclaresAnyField(schema) {
|
|
187
|
+
for (const section of sectionsOf(schema)) if (fieldsOf(section).length > 0) return true;
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
function isRecord$2(value) {
|
|
191
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
192
|
+
}
|
|
193
|
+
function sectionsOf(schema) {
|
|
194
|
+
if (!isRecord$2(schema)) return [];
|
|
195
|
+
const sections = schema["sections"];
|
|
196
|
+
return Array.isArray(sections) ? sections : [];
|
|
197
|
+
}
|
|
198
|
+
function fieldsOf(node) {
|
|
199
|
+
if (!isRecord$2(node)) return [];
|
|
200
|
+
const fields = node["fields"];
|
|
201
|
+
return Array.isArray(fields) ? fields : [];
|
|
202
|
+
}
|
|
203
|
+
function walkField(field, out) {
|
|
204
|
+
if (!isRecord$2(field)) return;
|
|
205
|
+
const type = field["type"];
|
|
206
|
+
const key = field["key"];
|
|
207
|
+
if ((type === "password" || field["secret"] === true) && typeof key === "string" && key.length > 0) out.add(key);
|
|
208
|
+
if (type === "group") {
|
|
209
|
+
for (const child of fieldsOf(field)) walkField(child, out);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (type === "sub-tabs") {
|
|
213
|
+
const tabs = field["tabs"];
|
|
214
|
+
if (!Array.isArray(tabs)) return;
|
|
215
|
+
for (const tab of tabs) for (const child of fieldsOf(tab)) walkField(child, out);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
//#endregion
|
|
219
|
+
//#region src/keychain/secret-marker.ts
|
|
220
|
+
/**
|
|
221
|
+
* What a settings blob holds where a secret used to be.
|
|
222
|
+
*
|
|
223
|
+
* A keychain-stored field never holds its value. It holds a self-describing
|
|
224
|
+
* MARKER, `{"$secret":"secret://…","v":1}` — an object, never a string, so no
|
|
225
|
+
* code can mistake it for a credential and no form can send it to a camera as
|
|
226
|
+
* a password. The empty state is `null` / absent, never `''`.
|
|
227
|
+
*
|
|
228
|
+
* The same shapes travel to and from the admin UI:
|
|
229
|
+
*
|
|
230
|
+
* - read: `hydrateSchema` hands a secret field a {@link SecretFieldView}
|
|
231
|
+
* (`{ $secret, v, set: true }`) or `null` — the browser learns whether a
|
|
232
|
+
* value is set, never the value.
|
|
233
|
+
* - write: an untouched field submits the view back unchanged (KEEP); a typed
|
|
234
|
+
* value is a plain non-empty string (CHANGE); the Clear button submits
|
|
235
|
+
* {@link SECRET_CLEAR} (CLEAR). `''`, `null` and the legacy redaction tokens
|
|
236
|
+
* are KEEP — an empty string is what a defaults-fallback pushes back, and
|
|
237
|
+
* treating it as "clear" is how a save destroyed a stored credential before.
|
|
238
|
+
*
|
|
239
|
+
* Pure and browser-safe.
|
|
240
|
+
*/
|
|
241
|
+
var SECRET_CLEAR = { $secretClear: true };
|
|
242
|
+
/** The `'***'` token D634 redaction writes for non-admins. */
|
|
243
|
+
var LEGACY_DEVICE_REDACTION = "***";
|
|
244
|
+
function isRecord$1(value) {
|
|
245
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
246
|
+
}
|
|
247
|
+
function makeSecretMarker(ref) {
|
|
248
|
+
return {
|
|
249
|
+
$secret: ref,
|
|
250
|
+
v: 1
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/** A stored marker: an object whose `$secret` is a non-empty string. */
|
|
254
|
+
function isSecretMarker(value) {
|
|
255
|
+
if (!isRecord$1(value)) return false;
|
|
256
|
+
const ref = value["$secret"];
|
|
257
|
+
return typeof ref === "string" && ref.length > 0;
|
|
258
|
+
}
|
|
259
|
+
/** Anything carrying a `$secret` key — a stored marker or a UI view of one. */
|
|
260
|
+
function isSecretMarkerLike(value) {
|
|
261
|
+
return isRecord$1(value) && Object.prototype.hasOwnProperty.call(value, "$secret");
|
|
262
|
+
}
|
|
263
|
+
function isSecretClear(value) {
|
|
264
|
+
return isRecord$1(value) && value["$secretClear"] === true;
|
|
265
|
+
}
|
|
266
|
+
function isSecretFieldView(value) {
|
|
267
|
+
return isRecord$1(value) && value["set"] === true && isSecretMarkerLike(value);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* The UI view of a stored secret field value: a marker becomes "set", inline
|
|
271
|
+
* plaintext becomes "set (inline)", nothing becomes `null`. A value never
|
|
272
|
+
* survives this function.
|
|
273
|
+
*/
|
|
274
|
+
function toSecretFieldView(stored) {
|
|
275
|
+
if (isSecretMarker(stored)) return {
|
|
276
|
+
$secret: stored.$secret,
|
|
277
|
+
v: 1,
|
|
278
|
+
set: true
|
|
279
|
+
};
|
|
280
|
+
if (typeof stored === "string" && stored.length > 0) return {
|
|
281
|
+
$secret: null,
|
|
282
|
+
v: 1,
|
|
283
|
+
set: true,
|
|
284
|
+
inline: true
|
|
285
|
+
};
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Classify an incoming value for a secret path. Only a non-empty string that is
|
|
290
|
+
* not a redaction token sets a value; only {@link SECRET_CLEAR} clears.
|
|
291
|
+
* Everything else — a marker (ours or anyone's), a UI view, `''`, `null`,
|
|
292
|
+
* `undefined`, a redaction token, a non-string — keeps what is stored.
|
|
293
|
+
*/
|
|
294
|
+
function classifySecretWrite(incoming) {
|
|
295
|
+
if (isSecretClear(incoming)) return { kind: "clear" };
|
|
296
|
+
if (typeof incoming !== "string") return { kind: "keep" };
|
|
297
|
+
if (incoming.length === 0) return { kind: "keep" };
|
|
298
|
+
if (incoming === "__camstack_redacted__" || incoming === LEGACY_DEVICE_REDACTION) return { kind: "keep" };
|
|
299
|
+
return {
|
|
300
|
+
kind: "set",
|
|
301
|
+
value: incoming
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
//#endregion
|
|
119
305
|
//#region src/interfaces/config-ui.ts
|
|
120
306
|
/** Predefined tabs with standard label, icon, and sort order.
|
|
121
307
|
*
|
|
@@ -358,33 +544,19 @@ function resolveHydratedFieldValue(schema, key) {
|
|
|
358
544
|
const entry = collectHydratedFieldEntries(schema).get(key);
|
|
359
545
|
return entry === void 0 ? void 0 : entry.value ?? entry.default;
|
|
360
546
|
}
|
|
361
|
-
|
|
362
|
-
* Merge a `ConfigUISchema` with a raw `values` record into a
|
|
363
|
-
* `ConfigUISchemaWithValues`. Used by every `get*Settings` backend endpoint
|
|
364
|
-
* before returning to the admin UI. Groups are walked recursively.
|
|
365
|
-
*
|
|
366
|
-
* For each leaf field:
|
|
367
|
-
* - structural fields (`separator`, `info`, `button`) pass through unchanged
|
|
368
|
-
* - other fields get `value = values[key] ?? field.default ?? null`
|
|
369
|
-
*
|
|
370
|
-
* Unknown keys in `values` (keys not declared in the schema) are silently
|
|
371
|
-
* ignored — they don't contribute to the output. Missing keys fall back to
|
|
372
|
-
* the schema default or `null`. This mirrors the old backend behaviour where
|
|
373
|
-
* `FormBuilder` rendered `values[key] ?? field.default`.
|
|
374
|
-
*/
|
|
375
|
-
function hydrateSchema(schema, values) {
|
|
547
|
+
function hydrateSchema(schema, values, options = {}) {
|
|
376
548
|
return {
|
|
377
549
|
...schema.tabs ? { tabs: [...schema.tabs] } : {},
|
|
378
550
|
sections: schema.sections.map((section) => ({
|
|
379
551
|
...section,
|
|
380
|
-
fields: section.fields.map((field) => hydrateField(field, values))
|
|
552
|
+
fields: section.fields.map((field) => hydrateField(field, values, options))
|
|
381
553
|
}))
|
|
382
554
|
};
|
|
383
555
|
}
|
|
384
|
-
function hydrateField(field, values) {
|
|
556
|
+
function hydrateField(field, values, options) {
|
|
385
557
|
if (isValuelessField(field)) return field;
|
|
386
558
|
if (field.type === "group") {
|
|
387
|
-
const hydratedChildren = field.fields.map((child) => hydrateField(child, values));
|
|
559
|
+
const hydratedChildren = field.fields.map((child) => hydrateField(child, values, options));
|
|
388
560
|
return {
|
|
389
561
|
...field,
|
|
390
562
|
fields: hydratedChildren
|
|
@@ -393,7 +565,7 @@ function hydrateField(field, values) {
|
|
|
393
565
|
if (field.type === "sub-tabs") {
|
|
394
566
|
const hydratedTabs = field.tabs.map((tab) => ({
|
|
395
567
|
...tab,
|
|
396
|
-
fields: tab.fields.map((child) => hydrateField(child, values))
|
|
568
|
+
fields: tab.fields.map((child) => hydrateField(child, values, options))
|
|
397
569
|
}));
|
|
398
570
|
return {
|
|
399
571
|
...field,
|
|
@@ -403,6 +575,33 @@ function hydrateField(field, values) {
|
|
|
403
575
|
const key = field.key;
|
|
404
576
|
const storedValue = Object.prototype.hasOwnProperty.call(values, key) ? values[key] : void 0;
|
|
405
577
|
const defaultValue = field.default;
|
|
578
|
+
const asView = (v) => isSecretMarker(v) || options.inlineSecretsAsViews === true && v !== void 0;
|
|
579
|
+
if (isKeychainStoredField(field) && asView(storedValue)) return {
|
|
580
|
+
...field,
|
|
581
|
+
value: toSecretFieldView(storedValue)
|
|
582
|
+
};
|
|
583
|
+
if (isKeychainStoredField(field) && options.inlineSecretsAsViews === true) return {
|
|
584
|
+
...field,
|
|
585
|
+
value: null
|
|
586
|
+
};
|
|
587
|
+
if (field.type === "editable-array" && Array.isArray(storedValue)) {
|
|
588
|
+
const secretItemKeys = field.itemFields.filter((item) => "key" in item && isKeychainStoredField(item)).map((item) => "key" in item ? item.key : "");
|
|
589
|
+
if (secretItemKeys.length > 0) {
|
|
590
|
+
const rows = storedValue.map((row) => {
|
|
591
|
+
if (row === null || typeof row !== "object" || Array.isArray(row)) return row;
|
|
592
|
+
const out = { ...row };
|
|
593
|
+
for (const k of secretItemKeys) {
|
|
594
|
+
const v = Reflect.get(row, k);
|
|
595
|
+
if (asView(v)) out[k] = toSecretFieldView(v);
|
|
596
|
+
}
|
|
597
|
+
return out;
|
|
598
|
+
});
|
|
599
|
+
return {
|
|
600
|
+
...field,
|
|
601
|
+
value: rows
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
}
|
|
406
605
|
if (field.multiple) {
|
|
407
606
|
const stored = Array.isArray(storedValue) ? storedValue : storedValue !== void 0 && storedValue !== null ? [storedValue] : [];
|
|
408
607
|
const itemFallback = field.multiple.itemDefault !== void 0 ? field.multiple.itemDefault : defaultValue !== void 0 ? defaultValue : typeOf(field) === "string" ? "" : null;
|
|
@@ -668,7 +867,575 @@ function projectStore(store, perNodeKeys, nodeId) {
|
|
|
668
867
|
return out;
|
|
669
868
|
}
|
|
670
869
|
//#endregion
|
|
870
|
+
//#region src/keychain/secret-ref.ts
|
|
871
|
+
/**
|
|
872
|
+
* The address of one keychain value: `secret://<owner>/<scope>/<fieldPath>`.
|
|
873
|
+
*
|
|
874
|
+
* A ref is DETERMINISTIC from where the value lives — the owner addon, the
|
|
875
|
+
* scope (global, one node, one device, one integration, one row of a business
|
|
876
|
+
* collection) and the field path inside that scope — so saving the same field
|
|
877
|
+
* twice updates the same ref, and the ref itself says which settings blob
|
|
878
|
+
* points at it. It is also the AAD of the value's ciphertext: a ciphertext
|
|
879
|
+
* copied onto another ref does not decrypt.
|
|
880
|
+
*
|
|
881
|
+
* Array items are addressed by ITEM ID, never by index
|
|
882
|
+
* (`providers[authentik].clientSecret`), so reordering a list keeps every ref.
|
|
883
|
+
*
|
|
884
|
+
* Pure and browser-safe: no node builtins, no I/O.
|
|
885
|
+
*/
|
|
886
|
+
/** The URI scheme of every keychain ref. */
|
|
887
|
+
var SECRET_REF_SCHEME = "secret://";
|
|
888
|
+
var GLOBAL_SCOPE = { kind: "global" };
|
|
889
|
+
/** The scope every addon-wide (not per-node, not per-device) setting lives in. */
|
|
890
|
+
function globalSecretScope() {
|
|
891
|
+
return GLOBAL_SCOPE;
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* An addon id as a ref owner. A hub in-process builtin's context id is
|
|
895
|
+
* `addon:<id>`; the owner of its secrets is `<id>`, exactly as for the same
|
|
896
|
+
* addon running forked.
|
|
897
|
+
*/
|
|
898
|
+
function normalizeSecretOwner(addonId) {
|
|
899
|
+
return addonId.startsWith("addon:") ? addonId.slice(6) : addonId;
|
|
900
|
+
}
|
|
901
|
+
function encodeSegment(value) {
|
|
902
|
+
return encodeURIComponent(value);
|
|
903
|
+
}
|
|
904
|
+
function decodeSegment(value) {
|
|
905
|
+
try {
|
|
906
|
+
return decodeURIComponent(value);
|
|
907
|
+
} catch {
|
|
908
|
+
return null;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
/** The scope as it appears inside a ref (`global`, `node/<id>`, …). */
|
|
912
|
+
function formatSecretScope(scope) {
|
|
913
|
+
switch (scope.kind) {
|
|
914
|
+
case "global": return "global";
|
|
915
|
+
case "node": return `node/${encodeSegment(scope.nodeId)}`;
|
|
916
|
+
case "device": return `device/${scope.deviceId}`;
|
|
917
|
+
case "integration": return `integration/${encodeSegment(scope.integrationId)}`;
|
|
918
|
+
case "row": return `row/${encodeSegment(scope.collection)}/${encodeSegment(scope.rowId)}`;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* The field path of a value inside an array item, addressed by the item's id:
|
|
923
|
+
* `arrayItemFieldPath('providers', 'authentik', 'clientSecret')` →
|
|
924
|
+
* `providers[authentik].clientSecret`.
|
|
925
|
+
*/
|
|
926
|
+
function arrayItemFieldPath(arrayKey, itemId, fieldKey) {
|
|
927
|
+
return `${arrayKey}[${encodeSegment(itemId)}].${fieldKey}`;
|
|
928
|
+
}
|
|
929
|
+
/** Build a ref. Throws on an empty owner or field path — both are bugs. */
|
|
930
|
+
function buildSecretRef(parts) {
|
|
931
|
+
const owner = normalizeSecretOwner(parts.owner);
|
|
932
|
+
if (owner.length === 0) throw new Error("buildSecretRef: owner is empty");
|
|
933
|
+
if (parts.fieldPath.length === 0) throw new Error("buildSecretRef: fieldPath is empty");
|
|
934
|
+
return `${SECRET_REF_SCHEME}${encodeSegment(owner)}/${formatSecretScope(parts.scope)}/${parts.fieldPath}`;
|
|
935
|
+
}
|
|
936
|
+
/** True when `value` is shaped like a keychain ref (not that it exists). */
|
|
937
|
+
function isSecretRef(value) {
|
|
938
|
+
return typeof value === "string" && parseSecretRef(value) !== null;
|
|
939
|
+
}
|
|
940
|
+
function parseScope(segments) {
|
|
941
|
+
switch (segments[0]) {
|
|
942
|
+
case "global": return {
|
|
943
|
+
scope: GLOBAL_SCOPE,
|
|
944
|
+
used: 1
|
|
945
|
+
};
|
|
946
|
+
case "node": {
|
|
947
|
+
const nodeId = segments[1] !== void 0 ? decodeSegment(segments[1]) : null;
|
|
948
|
+
return nodeId === null || nodeId.length === 0 ? null : {
|
|
949
|
+
scope: {
|
|
950
|
+
kind: "node",
|
|
951
|
+
nodeId
|
|
952
|
+
},
|
|
953
|
+
used: 2
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
case "device": {
|
|
957
|
+
const raw = segments[1];
|
|
958
|
+
if (raw === void 0 || !/^\d+$/.test(raw)) return null;
|
|
959
|
+
return {
|
|
960
|
+
scope: {
|
|
961
|
+
kind: "device",
|
|
962
|
+
deviceId: Number(raw)
|
|
963
|
+
},
|
|
964
|
+
used: 2
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
case "integration": {
|
|
968
|
+
const id = segments[1] !== void 0 ? decodeSegment(segments[1]) : null;
|
|
969
|
+
return id === null || id.length === 0 ? null : {
|
|
970
|
+
scope: {
|
|
971
|
+
kind: "integration",
|
|
972
|
+
integrationId: id
|
|
973
|
+
},
|
|
974
|
+
used: 2
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
case "row": {
|
|
978
|
+
const collection = segments[1] !== void 0 ? decodeSegment(segments[1]) : null;
|
|
979
|
+
const rowId = segments[2] !== void 0 ? decodeSegment(segments[2]) : null;
|
|
980
|
+
if (collection === null || rowId === null || collection.length === 0 || rowId.length === 0) return null;
|
|
981
|
+
return {
|
|
982
|
+
scope: {
|
|
983
|
+
kind: "row",
|
|
984
|
+
collection,
|
|
985
|
+
rowId
|
|
986
|
+
},
|
|
987
|
+
used: 3
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
default: return null;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
/** Take a ref apart; `null` when it is not a well-formed ref. */
|
|
994
|
+
function parseSecretRef(ref) {
|
|
995
|
+
if (!ref.startsWith("secret://")) return null;
|
|
996
|
+
const segments = ref.slice(9).split("/");
|
|
997
|
+
const owner = segments[0] !== void 0 ? decodeSegment(segments[0]) : null;
|
|
998
|
+
if (owner === null || owner.length === 0) return null;
|
|
999
|
+
const parsed = parseScope(segments.slice(1));
|
|
1000
|
+
if (parsed === null) return null;
|
|
1001
|
+
const fieldPath = segments.slice(1 + parsed.used).join("/");
|
|
1002
|
+
if (fieldPath.length === 0) return null;
|
|
1003
|
+
return {
|
|
1004
|
+
owner,
|
|
1005
|
+
scope: parsed.scope,
|
|
1006
|
+
fieldPath
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
//#endregion
|
|
1010
|
+
//#region src/addon/secret-settings-view.ts
|
|
1011
|
+
/** A write to a secret path could not be stored in the keychain, so it was refused. */
|
|
1012
|
+
var KeychainWriteRefusedError = class extends Error {
|
|
1013
|
+
fieldPath;
|
|
1014
|
+
constructor(fieldPath, cause) {
|
|
1015
|
+
super(`secret "${fieldPath}" was not saved: the keychain could not store it (${cause instanceof Error ? cause.message : String(cause)}). Nothing was written.`);
|
|
1016
|
+
this.fieldPath = fieldPath;
|
|
1017
|
+
this.name = "KeychainWriteRefusedError";
|
|
1018
|
+
}
|
|
1019
|
+
};
|
|
1020
|
+
var KEEP = { kind: "keep" };
|
|
1021
|
+
function isRecord(value) {
|
|
1022
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1023
|
+
}
|
|
1024
|
+
function defaultMintId() {
|
|
1025
|
+
return globalThis.crypto.randomUUID();
|
|
1026
|
+
}
|
|
1027
|
+
/** Every marker ref anywhere under `value`. */
|
|
1028
|
+
function collectSecretMarkerRefs(value, out = []) {
|
|
1029
|
+
if (isSecretMarker(value)) {
|
|
1030
|
+
out.push(value.$secret);
|
|
1031
|
+
return out;
|
|
1032
|
+
}
|
|
1033
|
+
if (Array.isArray(value)) for (const item of value) collectSecretMarkerRefs(item, out);
|
|
1034
|
+
else if (isRecord(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
|
|
1035
|
+
return out;
|
|
1036
|
+
}
|
|
1037
|
+
async function storedValueEquals(secrets, marker, candidate) {
|
|
1038
|
+
const resolved = (await secrets.resolve([marker.$secret]))[marker.$secret];
|
|
1039
|
+
return resolved?.kind === "value" && resolved.value === candidate;
|
|
1040
|
+
}
|
|
1041
|
+
async function writeLeaf(ctx, incoming, stored, fieldPath) {
|
|
1042
|
+
const intent = classifySecretWrite(incoming);
|
|
1043
|
+
const ref = ctx.secrets.ref(ctx.scope, fieldPath);
|
|
1044
|
+
const storedIsOurs = isSecretMarker(stored) && stored.$secret === ref;
|
|
1045
|
+
if (intent.kind === "keep") return KEEP;
|
|
1046
|
+
if (intent.kind === "set" && !ctx.operatorWrite && typeof stored === "string" && stored === intent.value) return KEEP;
|
|
1047
|
+
if (intent.kind === "clear") {
|
|
1048
|
+
if (storedIsOurs) ctx.deletes.push(ref);
|
|
1049
|
+
return {
|
|
1050
|
+
kind: "value",
|
|
1051
|
+
value: null
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
if (storedIsOurs && await storedValueEquals(ctx.secrets, stored, intent.value)) return {
|
|
1055
|
+
kind: "value",
|
|
1056
|
+
value: stored
|
|
1057
|
+
};
|
|
1058
|
+
try {
|
|
1059
|
+
await ctx.secrets.put(ref, intent.value);
|
|
1060
|
+
} catch (err) {
|
|
1061
|
+
throw new KeychainWriteRefusedError(fieldPath, err);
|
|
1062
|
+
}
|
|
1063
|
+
return {
|
|
1064
|
+
kind: "value",
|
|
1065
|
+
value: makeSecretMarker(ref)
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
/** Apply a member result onto `out`: KEEP restores the stored member, or removes it. */
|
|
1069
|
+
function applyMember(out, key, result, storedParent) {
|
|
1070
|
+
if (result.kind === "value") {
|
|
1071
|
+
out[key] = result.value;
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
if (Object.prototype.hasOwnProperty.call(storedParent, key)) out[key] = storedParent[key];
|
|
1075
|
+
else delete out[key];
|
|
1076
|
+
}
|
|
1077
|
+
async function writeMembers(ctx, children, incoming, stored, pathOf) {
|
|
1078
|
+
const out = { ...incoming };
|
|
1079
|
+
for (const [member, node] of children) applyMember(out, member, await writeNode(ctx, node, incoming[member], stored[member], pathOf(member)), stored);
|
|
1080
|
+
return out;
|
|
1081
|
+
}
|
|
1082
|
+
function rowId(row, idKey) {
|
|
1083
|
+
const raw = row[idKey];
|
|
1084
|
+
if (typeof raw === "string" && raw.length > 0) return raw;
|
|
1085
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return String(raw);
|
|
1086
|
+
return null;
|
|
1087
|
+
}
|
|
1088
|
+
async function writeArray(ctx, node, incoming, stored, fieldPath) {
|
|
1089
|
+
const storedRows = Array.isArray(stored) ? stored.filter(isRecord) : [];
|
|
1090
|
+
if (!Array.isArray(incoming)) {
|
|
1091
|
+
ctx.deletes.push(...collectSecretMarkerRefs(storedRows));
|
|
1092
|
+
return {
|
|
1093
|
+
kind: "value",
|
|
1094
|
+
value: incoming
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
const storedById = /* @__PURE__ */ new Map();
|
|
1098
|
+
for (const row of storedRows) {
|
|
1099
|
+
const id = rowId(row, node.idKey);
|
|
1100
|
+
if (id !== null) storedById.set(id, row);
|
|
1101
|
+
}
|
|
1102
|
+
const kept = /* @__PURE__ */ new Set();
|
|
1103
|
+
const rows = [];
|
|
1104
|
+
for (const row of incoming) {
|
|
1105
|
+
if (!isRecord(row)) {
|
|
1106
|
+
rows.push(row);
|
|
1107
|
+
continue;
|
|
1108
|
+
}
|
|
1109
|
+
const id = rowId(row, node.idKey) ?? ctx.mintId();
|
|
1110
|
+
kept.add(id);
|
|
1111
|
+
const withId = {
|
|
1112
|
+
...row,
|
|
1113
|
+
[node.idKey]: row[node.idKey] ?? id
|
|
1114
|
+
};
|
|
1115
|
+
if (rowId(withId, node.idKey) === null) withId[node.idKey] = id;
|
|
1116
|
+
const storedRow = storedById.get(id) ?? {};
|
|
1117
|
+
rows.push(await writeMembers(ctx, node.item, withId, storedRow, (member) => arrayItemFieldPath(fieldPath, id, member)));
|
|
1118
|
+
}
|
|
1119
|
+
for (const [id, row] of storedById) if (!kept.has(id)) ctx.deletes.push(...collectSecretMarkerRefs(row));
|
|
1120
|
+
return {
|
|
1121
|
+
kind: "value",
|
|
1122
|
+
value: rows
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
async function writeNode(ctx, node, incoming, stored, fieldPath) {
|
|
1126
|
+
switch (node.kind) {
|
|
1127
|
+
case "leaf": return writeLeaf(ctx, incoming, stored, fieldPath);
|
|
1128
|
+
case "array": return writeArray(ctx, node, incoming, stored, fieldPath);
|
|
1129
|
+
case "object": {
|
|
1130
|
+
if (!isRecord(incoming)) {
|
|
1131
|
+
if (incoming === void 0) return KEEP;
|
|
1132
|
+
ctx.deletes.push(...collectSecretMarkerRefs(stored));
|
|
1133
|
+
return {
|
|
1134
|
+
kind: "value",
|
|
1135
|
+
value: incoming
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
const storedObj = isRecord(stored) ? stored : {};
|
|
1139
|
+
return {
|
|
1140
|
+
kind: "value",
|
|
1141
|
+
value: await writeMembers(ctx, node.children, incoming, storedObj, (m) => `${fieldPath}.${m}`)
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
/** Split a perNode-scoped key (`key@nodeId`). */
|
|
1147
|
+
function splitNodeKey(key) {
|
|
1148
|
+
const at = key.indexOf("@");
|
|
1149
|
+
return at <= 0 ? {
|
|
1150
|
+
base: key,
|
|
1151
|
+
nodeId: null
|
|
1152
|
+
} : {
|
|
1153
|
+
base: key.slice(0, at),
|
|
1154
|
+
nodeId: key.slice(at + 1)
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
/** Rewrite a settings patch so no secret value reaches the blob. See the file doc. */
|
|
1158
|
+
async function transformSecretWrite(input) {
|
|
1159
|
+
const deletes = [];
|
|
1160
|
+
const base = {
|
|
1161
|
+
secrets: input.secrets,
|
|
1162
|
+
mintId: input.mintId ?? defaultMintId,
|
|
1163
|
+
deletes,
|
|
1164
|
+
operatorWrite: input.operatorWrite === true
|
|
1165
|
+
};
|
|
1166
|
+
const out = {};
|
|
1167
|
+
for (const [key, incoming] of Object.entries(input.patch)) {
|
|
1168
|
+
const { base: baseKey, nodeId } = splitNodeKey(key);
|
|
1169
|
+
const node = input.tree.get(baseKey);
|
|
1170
|
+
if (node === void 0) {
|
|
1171
|
+
out[key] = incoming;
|
|
1172
|
+
continue;
|
|
1173
|
+
}
|
|
1174
|
+
const scope = nodeId !== null && nodeId.length > 0 ? {
|
|
1175
|
+
kind: "node",
|
|
1176
|
+
nodeId
|
|
1177
|
+
} : input.scope ?? globalSecretScope();
|
|
1178
|
+
const result = await writeNode({
|
|
1179
|
+
...base,
|
|
1180
|
+
scope
|
|
1181
|
+
}, node, incoming, input.stored[key], baseKey);
|
|
1182
|
+
if (result.kind === "value") out[key] = result.value;
|
|
1183
|
+
}
|
|
1184
|
+
return {
|
|
1185
|
+
patch: out,
|
|
1186
|
+
afterWrite: async () => {
|
|
1187
|
+
if (deletes.length === 0) return { orphaned: [] };
|
|
1188
|
+
try {
|
|
1189
|
+
await input.secrets.delete([...new Set(deletes)]);
|
|
1190
|
+
return { orphaned: [] };
|
|
1191
|
+
} catch {
|
|
1192
|
+
return { orphaned: [...new Set(deletes)] };
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
/**
|
|
1198
|
+
* Replace every marker in `value` with its keychain value. Returns the SAME
|
|
1199
|
+
* reference when there is no marker (no allocation, no keychain call).
|
|
1200
|
+
*/
|
|
1201
|
+
async function resolveSecretMarkers(value, secrets) {
|
|
1202
|
+
const refs = collectSecretMarkerRefs(value);
|
|
1203
|
+
if (refs.length === 0) return {
|
|
1204
|
+
value,
|
|
1205
|
+
unresolved: []
|
|
1206
|
+
};
|
|
1207
|
+
const answers = await secrets.resolve(refs);
|
|
1208
|
+
const unresolved = [];
|
|
1209
|
+
for (const ref of new Set(refs)) {
|
|
1210
|
+
const answer = answers[ref];
|
|
1211
|
+
if (answer === void 0) unresolved.push({
|
|
1212
|
+
ref,
|
|
1213
|
+
reason: "not-initialized"
|
|
1214
|
+
});
|
|
1215
|
+
else if (answer.kind === "unavailable") unresolved.push({
|
|
1216
|
+
ref,
|
|
1217
|
+
reason: answer.reason
|
|
1218
|
+
});
|
|
1219
|
+
else if (answer.kind === "missing" || answer.kind === "forbidden") unresolved.push({
|
|
1220
|
+
ref,
|
|
1221
|
+
reason: answer.kind
|
|
1222
|
+
});
|
|
1223
|
+
}
|
|
1224
|
+
const substitute = (v) => {
|
|
1225
|
+
if (isSecretMarker(v)) {
|
|
1226
|
+
const answer = answers[v.$secret];
|
|
1227
|
+
return answer?.kind === "value" ? {
|
|
1228
|
+
keep: true,
|
|
1229
|
+
value: answer.value
|
|
1230
|
+
} : {
|
|
1231
|
+
keep: false,
|
|
1232
|
+
value: void 0
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
if (Array.isArray(v)) {
|
|
1236
|
+
const items = [];
|
|
1237
|
+
for (const item of v) {
|
|
1238
|
+
const s = substitute(item);
|
|
1239
|
+
if (s.keep) items.push(s.value);
|
|
1240
|
+
}
|
|
1241
|
+
return {
|
|
1242
|
+
keep: true,
|
|
1243
|
+
value: items
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
if (isRecord(v)) {
|
|
1247
|
+
const out = {};
|
|
1248
|
+
for (const [k, child] of Object.entries(v)) {
|
|
1249
|
+
const s = substitute(child);
|
|
1250
|
+
if (s.keep) out[k] = s.value;
|
|
1251
|
+
}
|
|
1252
|
+
return {
|
|
1253
|
+
keep: true,
|
|
1254
|
+
value: out
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
return {
|
|
1258
|
+
keep: true,
|
|
1259
|
+
value: v
|
|
1260
|
+
};
|
|
1261
|
+
};
|
|
1262
|
+
return {
|
|
1263
|
+
value: substitute(value).value,
|
|
1264
|
+
unresolved
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
//#endregion
|
|
1268
|
+
//#region src/addon/secret-aware-context.ts
|
|
1269
|
+
function touchesTree(tree, patch) {
|
|
1270
|
+
if (tree.size === 0) return false;
|
|
1271
|
+
for (const key of Object.keys(patch)) {
|
|
1272
|
+
const at = key.indexOf("@");
|
|
1273
|
+
if (tree.has(at > 0 ? key.slice(0, at) : key)) return true;
|
|
1274
|
+
}
|
|
1275
|
+
return false;
|
|
1276
|
+
}
|
|
1277
|
+
/** The blob as stored now; a failed read REFUSES the write instead of guessing. */
|
|
1278
|
+
async function readStoredAddonBlob(raw) {
|
|
1279
|
+
const threeWay = raw.readAddonStoreResult;
|
|
1280
|
+
if (threeWay === void 0) return raw.readAddonStore();
|
|
1281
|
+
const result = await threeWay.call(raw);
|
|
1282
|
+
if (result.kind === "failed") throw new Error(`settings write refused: the stored blob could not be read (${result.error})`);
|
|
1283
|
+
return result.kind === "missing" ? {} : result.value;
|
|
1284
|
+
}
|
|
1285
|
+
function createSecretAwareSettings(raw, opts) {
|
|
1286
|
+
const chains = /* @__PURE__ */ new Map();
|
|
1287
|
+
const serialize = (key, fn) => {
|
|
1288
|
+
const next = (chains.get(key) ?? Promise.resolve()).then(fn, fn);
|
|
1289
|
+
const tail = next.catch(() => void 0);
|
|
1290
|
+
chains.set(key, tail);
|
|
1291
|
+
tail.then(() => {
|
|
1292
|
+
if (chains.get(key) === tail) chains.delete(key);
|
|
1293
|
+
});
|
|
1294
|
+
return next;
|
|
1295
|
+
};
|
|
1296
|
+
const write = async (chainKey, tree, scope, patch, readStored, rawWrite) => {
|
|
1297
|
+
if (!touchesTree(tree, patch)) return rawWrite(patch);
|
|
1298
|
+
return serialize(chainKey, async () => {
|
|
1299
|
+
const stored = await readStored();
|
|
1300
|
+
const result = await transformSecretWrite({
|
|
1301
|
+
tree,
|
|
1302
|
+
secrets: opts.secrets,
|
|
1303
|
+
patch,
|
|
1304
|
+
stored,
|
|
1305
|
+
...scope !== void 0 ? { scope } : {},
|
|
1306
|
+
operatorWrite: opts.isOperatorWrite?.() === true
|
|
1307
|
+
});
|
|
1308
|
+
if (Object.keys(result.patch).length > 0) await rawWrite(result.patch);
|
|
1309
|
+
const { orphaned } = await result.afterWrite();
|
|
1310
|
+
if (orphaned.length > 0) opts.onOrphaned(orphaned);
|
|
1311
|
+
});
|
|
1312
|
+
};
|
|
1313
|
+
const wrapped = {
|
|
1314
|
+
writeAddonStore: (patch) => write("addon", opts.addonTree(), void 0, patch, () => readStoredAddonBlob(raw), (p) => raw.writeAddonStore(p)),
|
|
1315
|
+
writeDeviceStore: (deviceId, patch) => write(`device:${deviceId}`, opts.deviceTree(), {
|
|
1316
|
+
kind: "device",
|
|
1317
|
+
deviceId
|
|
1318
|
+
}, patch, () => raw.readDeviceStore(deviceId), (p) => raw.writeDeviceStore(deviceId, p))
|
|
1319
|
+
};
|
|
1320
|
+
return new Proxy(raw, { get(target, prop, receiver) {
|
|
1321
|
+
if (prop === "writeAddonStore") return wrapped.writeAddonStore;
|
|
1322
|
+
if (prop === "writeDeviceStore") return wrapped.writeDeviceStore;
|
|
1323
|
+
const value = Reflect.get(target, prop, receiver);
|
|
1324
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
1325
|
+
} });
|
|
1326
|
+
}
|
|
1327
|
+
/** The context the addon sees: identical, except that its settings writes are secret-aware. */
|
|
1328
|
+
function withSecretAwareSettings(context, opts) {
|
|
1329
|
+
const raw = context.settings;
|
|
1330
|
+
if (raw === void 0) return context;
|
|
1331
|
+
const settings = createSecretAwareSettings(raw, opts);
|
|
1332
|
+
return new Proxy(context, { get(target, prop, receiver) {
|
|
1333
|
+
if (prop === "settings") return settings;
|
|
1334
|
+
return Reflect.get(target, prop, receiver);
|
|
1335
|
+
} });
|
|
1336
|
+
}
|
|
1337
|
+
//#endregion
|
|
1338
|
+
//#region src/addon/secret-paths.ts
|
|
1339
|
+
var LEAF = { kind: "leaf" };
|
|
1340
|
+
var DEFAULT_ITEM_ID_KEY = "id";
|
|
1341
|
+
function collectFields(fields, out) {
|
|
1342
|
+
for (const field of fields) {
|
|
1343
|
+
if (field.type === "group") {
|
|
1344
|
+
collectFields(field.fields, out);
|
|
1345
|
+
continue;
|
|
1346
|
+
}
|
|
1347
|
+
if (field.type === "sub-tabs") {
|
|
1348
|
+
for (const tab of field.tabs) collectFields(tab.fields, out);
|
|
1349
|
+
continue;
|
|
1350
|
+
}
|
|
1351
|
+
if (field.type === "editable-array") {
|
|
1352
|
+
const item = /* @__PURE__ */ new Map();
|
|
1353
|
+
for (const itemField of field.itemFields) if ("key" in itemField && isKeychainStoredField(itemField)) item.set(itemField.key, LEAF);
|
|
1354
|
+
if (item.size > 0) out.set(field.key, {
|
|
1355
|
+
kind: "array",
|
|
1356
|
+
idKey: field.itemIdKey ?? DEFAULT_ITEM_ID_KEY,
|
|
1357
|
+
item
|
|
1358
|
+
});
|
|
1359
|
+
continue;
|
|
1360
|
+
}
|
|
1361
|
+
if (!("key" in field) || typeof field.key !== "string") continue;
|
|
1362
|
+
if (isKeychainStoredField(field)) out.set(field.key, LEAF);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
/** The secret tree a settings schema declares. */
|
|
1366
|
+
function secretTreeFromSchema(schema) {
|
|
1367
|
+
const out = /* @__PURE__ */ new Map();
|
|
1368
|
+
if (schema === null) return out;
|
|
1369
|
+
for (const section of schema.sections) collectFields(section.fields, out);
|
|
1370
|
+
return out;
|
|
1371
|
+
}
|
|
1372
|
+
/** Parse `a.b[id].c` into segments; throws on a malformed path (a declaration bug). */
|
|
1373
|
+
function parseSecretStatePath(path) {
|
|
1374
|
+
const segments = [];
|
|
1375
|
+
for (const raw of path.split(".")) {
|
|
1376
|
+
const m = /^([^[\]]+)(?:\[([^[\]]+)\])?$/.exec(raw);
|
|
1377
|
+
if (m === null || m[1] === void 0) throw new Error(`secretStatePaths: malformed path "${path}"`);
|
|
1378
|
+
segments.push(m[2] !== void 0 ? {
|
|
1379
|
+
key: m[1],
|
|
1380
|
+
idKey: m[2]
|
|
1381
|
+
} : { key: m[1] });
|
|
1382
|
+
}
|
|
1383
|
+
if (segments.length === 0) throw new Error(`secretStatePaths: empty path`);
|
|
1384
|
+
if (segments[segments.length - 1]?.idKey !== void 0) throw new Error(`secretStatePaths: "${path}" ends on an array — name the member that is secret`);
|
|
1385
|
+
return segments;
|
|
1386
|
+
}
|
|
1387
|
+
function insert(into, segments) {
|
|
1388
|
+
const [head, ...rest] = segments;
|
|
1389
|
+
if (head === void 0) return;
|
|
1390
|
+
if (rest.length === 0) {
|
|
1391
|
+
into.set(head.key, LEAF);
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
const existing = into.get(head.key);
|
|
1395
|
+
if (head.idKey !== void 0) {
|
|
1396
|
+
const item = new Map(existing?.kind === "array" ? existing.item : []);
|
|
1397
|
+
insert(item, rest);
|
|
1398
|
+
into.set(head.key, {
|
|
1399
|
+
kind: "array",
|
|
1400
|
+
idKey: head.idKey,
|
|
1401
|
+
item
|
|
1402
|
+
});
|
|
1403
|
+
return;
|
|
1404
|
+
}
|
|
1405
|
+
const children = new Map(existing?.kind === "object" ? existing.children : []);
|
|
1406
|
+
insert(children, rest);
|
|
1407
|
+
into.set(head.key, {
|
|
1408
|
+
kind: "object",
|
|
1409
|
+
children
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1412
|
+
/** Add hidden-state paths (`secretStatePaths`) to a schema-derived tree. */
|
|
1413
|
+
function mergeSecretStatePaths(tree, paths) {
|
|
1414
|
+
if (paths.length === 0) return tree;
|
|
1415
|
+
const out = new Map(tree);
|
|
1416
|
+
for (const path of paths) insert(out, parseSecretStatePath(path));
|
|
1417
|
+
return out;
|
|
1418
|
+
}
|
|
1419
|
+
/**
|
|
1420
|
+
* The tree of a DECLARED path list — a device's `secretConfigPaths`
|
|
1421
|
+
* (`collectZodSecretPaths`: dotted, `[]` for array items). An array segment
|
|
1422
|
+
* without a named id field is addressed by the item's `id`.
|
|
1423
|
+
*/
|
|
1424
|
+
function secretTreeFromPaths(paths) {
|
|
1425
|
+
const out = /* @__PURE__ */ new Map();
|
|
1426
|
+
for (const path of paths) {
|
|
1427
|
+
if (path.length === 0) continue;
|
|
1428
|
+
insert(out, parseSecretStatePath(path.replace(/\[\]/g, `[${DEFAULT_ITEM_ID_KEY}]`)));
|
|
1429
|
+
}
|
|
1430
|
+
return out;
|
|
1431
|
+
}
|
|
1432
|
+
//#endregion
|
|
671
1433
|
//#region src/addon/base-addon.ts
|
|
1434
|
+
/** First retry of a secret the keychain could not serve; doubles to {@link SECRET_RETRY_MAX_MS}. */
|
|
1435
|
+
var SECRET_RETRY_FIRST_MS = 5e3;
|
|
1436
|
+
var SECRET_RETRY_MAX_MS = 5 * 6e4;
|
|
1437
|
+
/** Coalesces a burst of `keychain.changed` events into one re-resolve. */
|
|
1438
|
+
var SECRET_CHANGE_DEBOUNCE_MS = 250;
|
|
672
1439
|
/**
|
|
673
1440
|
* Base class for CamStack addons. Eliminates settings boilerplate:
|
|
674
1441
|
*
|
|
@@ -738,6 +1505,25 @@ var BaseAddon = class {
|
|
|
738
1505
|
settingsStoreReady = false;
|
|
739
1506
|
/** Default config values. Provided via constructor. */
|
|
740
1507
|
defaults;
|
|
1508
|
+
/**
|
|
1509
|
+
* Hidden-state paths that hold secrets (D787) — values the addon writes
|
|
1510
|
+
* itself (`updateGlobalSettings`, `state()`) that no form field declares.
|
|
1511
|
+
* Stored in the keychain exactly like a `password` field. Syntax:
|
|
1512
|
+
* `'snapshotSigningKey'`, `'identity.pincode'`, `'tokens[userId].accessToken'`
|
|
1513
|
+
* (`key[<id field>]` addresses array items by id, never by index).
|
|
1514
|
+
*/
|
|
1515
|
+
secretStatePaths = [];
|
|
1516
|
+
_addonSecretTree = null;
|
|
1517
|
+
_deviceSecretTree = null;
|
|
1518
|
+
/** Refs the last resolved config pointed at — a change to one re-resolves. */
|
|
1519
|
+
_configSecretRefs = /* @__PURE__ */ new Set();
|
|
1520
|
+
_unresolvedSecrets = [];
|
|
1521
|
+
_secretRetryTimer = null;
|
|
1522
|
+
_secretRetryDelayMs = SECRET_RETRY_FIRST_MS;
|
|
1523
|
+
_secretChangeTimer = null;
|
|
1524
|
+
_secretChangeUnsub = null;
|
|
1525
|
+
/** >0 while an OPERATOR's settings write is being applied. */
|
|
1526
|
+
_operatorWriteDepth = 0;
|
|
741
1527
|
constructor(defaults) {
|
|
742
1528
|
this.defaults = defaults;
|
|
743
1529
|
this._config = { ...defaults };
|
|
@@ -773,8 +1559,18 @@ var BaseAddon = class {
|
|
|
773
1559
|
return this._config;
|
|
774
1560
|
}
|
|
775
1561
|
async initialize(context) {
|
|
776
|
-
|
|
1562
|
+
const secrets = context.secrets;
|
|
1563
|
+
this._ctx = secrets !== void 0 ? withSecretAwareSettings(context, {
|
|
1564
|
+
secrets,
|
|
1565
|
+
addonTree: () => this.addonSecretTree(),
|
|
1566
|
+
deviceTree: () => this.deviceSecretTree(),
|
|
1567
|
+
onOrphaned: (refs) => context.logger.warn("keychain refs dropped by a settings write could not be deleted", { meta: { refs: [...refs] } }),
|
|
1568
|
+
isOperatorWrite: () => this._operatorWriteDepth > 0
|
|
1569
|
+
}) : context;
|
|
777
1570
|
await this.resolveConfig();
|
|
1571
|
+
if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
|
|
1572
|
+
if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
|
|
1573
|
+
});
|
|
778
1574
|
const result = await this.onInitialize();
|
|
779
1575
|
this.emitLifecycle(require_event_category.EventCategory.AddonStarted);
|
|
780
1576
|
const normalized = normalizeAddonInitResult(result);
|
|
@@ -807,6 +1603,12 @@ var BaseAddon = class {
|
|
|
807
1603
|
*/
|
|
808
1604
|
async onHubReachable() {}
|
|
809
1605
|
async shutdown() {
|
|
1606
|
+
this._secretChangeUnsub?.();
|
|
1607
|
+
this._secretChangeUnsub = null;
|
|
1608
|
+
if (this._secretRetryTimer !== null) clearTimeout(this._secretRetryTimer);
|
|
1609
|
+
if (this._secretChangeTimer !== null) clearTimeout(this._secretChangeTimer);
|
|
1610
|
+
this._secretRetryTimer = null;
|
|
1611
|
+
this._secretChangeTimer = null;
|
|
810
1612
|
this.emitLifecycle(require_event_category.EventCategory.AddonStopped);
|
|
811
1613
|
if (this.autoEmitReadiness) this.emitReadinessForProviders("down");
|
|
812
1614
|
await this.onShutdown();
|
|
@@ -885,7 +1687,7 @@ var BaseAddon = class {
|
|
|
885
1687
|
return hydrateSchema(schema, overlay ? {
|
|
886
1688
|
...projected,
|
|
887
1689
|
...overlay
|
|
888
|
-
} : projected);
|
|
1690
|
+
} : projected, { inlineSecretsAsViews: true });
|
|
889
1691
|
}
|
|
890
1692
|
/**
|
|
891
1693
|
* The integration-level view of this addon's settings: exactly the sections
|
|
@@ -934,7 +1736,7 @@ var BaseAddon = class {
|
|
|
934
1736
|
return hydrateSchema({
|
|
935
1737
|
...schema,
|
|
936
1738
|
sections
|
|
937
|
-
}, projected);
|
|
1739
|
+
}, projected, { inlineSecretsAsViews: true });
|
|
938
1740
|
}
|
|
939
1741
|
/**
|
|
940
1742
|
* The raw addon store PROJECTED onto the target node's bare per-node keys:
|
|
@@ -988,18 +1790,50 @@ var BaseAddon = class {
|
|
|
988
1790
|
value: result.value[key]
|
|
989
1791
|
};
|
|
990
1792
|
}
|
|
1793
|
+
/**
|
|
1794
|
+
* The OPERATOR's write — a settings form or an admin API call, dispatched by
|
|
1795
|
+
* the kernel. Identical to {@link updateGlobalSettings}, except that a secret
|
|
1796
|
+
* the operator sends is stored as sent even when it equals a pre-keychain
|
|
1797
|
+
* inline value: the operator re-entering a password is a change, an addon
|
|
1798
|
+
* writing back what it read is not (D787).
|
|
1799
|
+
*/
|
|
1800
|
+
async updateGlobalSettingsAsOperator(patch, nodeId) {
|
|
1801
|
+
this._operatorWriteDepth++;
|
|
1802
|
+
try {
|
|
1803
|
+
await this.applyGlobalPatch(patch, nodeId);
|
|
1804
|
+
} finally {
|
|
1805
|
+
this._operatorWriteDepth--;
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
991
1808
|
async updateGlobalSettings(patch, nodeId) {
|
|
1809
|
+
await this.applyGlobalPatch(patch, nodeId);
|
|
1810
|
+
}
|
|
1811
|
+
/**
|
|
1812
|
+
* REMOVE stored secrets — their keychain values and their markers (D787).
|
|
1813
|
+
*
|
|
1814
|
+
* Writing `''` to a secret field KEEPS what is stored (an empty string is
|
|
1815
|
+
* what a defaults fallback writes back, and it must never destroy a
|
|
1816
|
+
* credential), so code that means "forget this token" says so here. Without
|
|
1817
|
+
* a keychain (an older kernel) it writes `''`, as such code always did.
|
|
1818
|
+
*/
|
|
1819
|
+
async clearSecrets(keys, nodeId) {
|
|
1820
|
+
const tree = this.addonSecretTree();
|
|
1821
|
+
const keychain = this._ctx?.secrets !== void 0;
|
|
1822
|
+
const patch = {};
|
|
1823
|
+
for (const key of keys) patch[key] = keychain && tree.has(key) ? SECRET_CLEAR : "";
|
|
1824
|
+
await this.applyGlobalPatch(patch, nodeId);
|
|
1825
|
+
}
|
|
1826
|
+
async applyGlobalPatch(barePatch, nodeId) {
|
|
992
1827
|
const keys = this.perNodeKeys();
|
|
993
1828
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
994
1829
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
995
|
-
const barePatch = patch;
|
|
996
1830
|
const scoped = keys.size > 0 ? scopePatch(barePatch, keys, target) : barePatch;
|
|
997
1831
|
await this._ctx?.settings?.writeAddonStore(scoped);
|
|
998
1832
|
if (target !== localNode) return;
|
|
999
1833
|
await this.resolveConfig();
|
|
1000
1834
|
await this.onConfigChanged();
|
|
1001
1835
|
this.emitLifecycle(require_event_category.EventCategory.AddonUpdated, { level: "global" });
|
|
1002
|
-
this.maybeAutoRestart(
|
|
1836
|
+
this.maybeAutoRestart(Object.keys(barePatch), this.globalSettingsSchema());
|
|
1003
1837
|
}
|
|
1004
1838
|
/**
|
|
1005
1839
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -1024,7 +1858,7 @@ var BaseAddon = class {
|
|
|
1024
1858
|
* time to return its response before the addon is torn down and
|
|
1025
1859
|
* re-initialised by `AddonRegistryService.restartAddon`.
|
|
1026
1860
|
*/
|
|
1027
|
-
maybeAutoRestart(
|
|
1861
|
+
maybeAutoRestart(patchKeys, schema) {
|
|
1028
1862
|
if (!schema) return;
|
|
1029
1863
|
const restartKeys = /* @__PURE__ */ new Set();
|
|
1030
1864
|
for (const section of schema.sections) for (const field of section.fields) {
|
|
@@ -1032,13 +1866,13 @@ var BaseAddon = class {
|
|
|
1032
1866
|
if (field.requiresRestart) restartKeys.add(field.key);
|
|
1033
1867
|
}
|
|
1034
1868
|
if (restartKeys.size === 0) return;
|
|
1035
|
-
if (!
|
|
1869
|
+
if (!patchKeys.some((k) => restartKeys.has(k))) return;
|
|
1036
1870
|
const ctx = this._ctx;
|
|
1037
1871
|
if (!ctx) return;
|
|
1038
1872
|
const addonId = ctx.id;
|
|
1039
1873
|
setImmediate(() => {
|
|
1040
1874
|
ctx.api.addons?.restartAddon?.mutate({ addonId }).then(() => {
|
|
1041
|
-
ctx.logger.info("addon auto-restart triggered by restart-required setting change", { meta: { changedFields:
|
|
1875
|
+
ctx.logger.info("addon auto-restart triggered by restart-required setting change", { meta: { changedFields: patchKeys.filter((k) => restartKeys.has(k)) } });
|
|
1042
1876
|
}).catch((err) => {
|
|
1043
1877
|
ctx.logger.error("addon auto-restart failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
1044
1878
|
});
|
|
@@ -1047,7 +1881,7 @@ var BaseAddon = class {
|
|
|
1047
1881
|
async getDeviceSettings(deviceId) {
|
|
1048
1882
|
const schema = this.deviceSettingsSchema();
|
|
1049
1883
|
if (!schema) return { sections: [] };
|
|
1050
|
-
return hydrateSchema(schema, await this._ctx?.settings?.readDeviceStore(deviceId) ?? {});
|
|
1884
|
+
return hydrateSchema(schema, await this._ctx?.settings?.readDeviceStore(deviceId) ?? {}, { inlineSecretsAsViews: true });
|
|
1051
1885
|
}
|
|
1052
1886
|
async updateDeviceSettings(deviceId, patch) {
|
|
1053
1887
|
await this._ctx?.settings?.writeDeviceStore(deviceId, patch);
|
|
@@ -1177,7 +2011,7 @@ var BaseAddon = class {
|
|
|
1177
2011
|
* selection at boot (absent scoped key ⇒ the constructor default wins).
|
|
1178
2012
|
*/
|
|
1179
2013
|
async resolveConfig() {
|
|
1180
|
-
const stored = await this.readAddonStoreWithRetry();
|
|
2014
|
+
const stored = await this.resolveStoredSecrets(await this.readAddonStoreWithRetry());
|
|
1181
2015
|
const perNode = this.perNodeKeys();
|
|
1182
2016
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
1183
2017
|
const resolved = { ...this.defaults };
|
|
@@ -1202,7 +2036,7 @@ var BaseAddon = class {
|
|
|
1202
2036
|
key,
|
|
1203
2037
|
schema,
|
|
1204
2038
|
fallback,
|
|
1205
|
-
read: () => this.readAddonStoreWithRetry(),
|
|
2039
|
+
read: async () => this.resolveStoredSecrets(await this.readAddonStoreWithRetry()),
|
|
1206
2040
|
write: async (patch) => {
|
|
1207
2041
|
await this._ctx?.settings?.writeAddonStore(patch);
|
|
1208
2042
|
},
|
|
@@ -1219,7 +2053,7 @@ var BaseAddon = class {
|
|
|
1219
2053
|
key,
|
|
1220
2054
|
schema,
|
|
1221
2055
|
fallback,
|
|
1222
|
-
read: async () => await this._ctx?.settings?.readDeviceStore(deviceId) ?? {},
|
|
2056
|
+
read: async () => this.resolveStoredSecrets(await this._ctx?.settings?.readDeviceStore(deviceId) ?? {}, false),
|
|
1223
2057
|
write: async (patch) => {
|
|
1224
2058
|
await this._ctx?.settings?.writeDeviceStore(deviceId, patch);
|
|
1225
2059
|
},
|
|
@@ -1231,6 +2065,70 @@ var BaseAddon = class {
|
|
|
1231
2065
|
} })
|
|
1232
2066
|
});
|
|
1233
2067
|
}
|
|
2068
|
+
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
2069
|
+
addonSecretTree() {
|
|
2070
|
+
this._addonSecretTree ??= mergeSecretStatePaths(secretTreeFromSchema(this.globalSettingsSchema()), this.secretStatePaths);
|
|
2071
|
+
return this._addonSecretTree;
|
|
2072
|
+
}
|
|
2073
|
+
deviceSecretTree() {
|
|
2074
|
+
this._deviceSecretTree ??= secretTreeFromSchema(this.deviceSettingsSchema());
|
|
2075
|
+
return this._deviceSecretTree;
|
|
2076
|
+
}
|
|
2077
|
+
/**
|
|
2078
|
+
* Secrets of this addon's stored config that could not be resolved on the
|
|
2079
|
+
* last read, with why. Non-empty means the addon is running WITHOUT them —
|
|
2080
|
+
* an addon whose function needs one should report itself degraded rather
|
|
2081
|
+
* than start on an empty credential.
|
|
2082
|
+
*/
|
|
2083
|
+
get unresolvedSecrets() {
|
|
2084
|
+
return this._unresolvedSecrets;
|
|
2085
|
+
}
|
|
2086
|
+
/**
|
|
2087
|
+
* Substitute every keychain marker in a stored blob with its value. With no
|
|
2088
|
+
* keychain (no `ctx.secrets`) or no marker, the blob comes back unchanged.
|
|
2089
|
+
* An unresolvable value is LEFT OUT (never `''`), logged by ref — never by
|
|
2090
|
+
* value — and, when the cause can pass, retried with backoff. Nothing is
|
|
2091
|
+
* ever written back from here.
|
|
2092
|
+
*/
|
|
2093
|
+
async resolveStoredSecrets(blob, trackForConfig = true) {
|
|
2094
|
+
const secrets = this._ctx?.secrets;
|
|
2095
|
+
if (secrets === void 0) return blob;
|
|
2096
|
+
const refs = collectSecretMarkerRefs(blob);
|
|
2097
|
+
if (trackForConfig) this._configSecretRefs = new Set(refs);
|
|
2098
|
+
if (refs.length === 0) {
|
|
2099
|
+
if (trackForConfig) this._unresolvedSecrets = [];
|
|
2100
|
+
return blob;
|
|
2101
|
+
}
|
|
2102
|
+
const { value, unresolved } = await resolveSecretMarkers(blob, secrets);
|
|
2103
|
+
if (trackForConfig) {
|
|
2104
|
+
this._unresolvedSecrets = unresolved;
|
|
2105
|
+
if (unresolved.length > 0) {
|
|
2106
|
+
this._ctx?.logger?.error?.("secret unavailable — running without it, will retry", { meta: { secrets: unresolved.map((u) => ({
|
|
2107
|
+
ref: u.ref,
|
|
2108
|
+
reason: u.reason
|
|
2109
|
+
})) } });
|
|
2110
|
+
if (unresolved.some((u) => u.reason !== "missing" && u.reason !== "forbidden")) {
|
|
2111
|
+
this.scheduleSecretReresolve(this._secretRetryDelayMs);
|
|
2112
|
+
this._secretRetryDelayMs = Math.min(this._secretRetryDelayMs * 2, SECRET_RETRY_MAX_MS);
|
|
2113
|
+
}
|
|
2114
|
+
} else this._secretRetryDelayMs = SECRET_RETRY_FIRST_MS;
|
|
2115
|
+
}
|
|
2116
|
+
return value;
|
|
2117
|
+
}
|
|
2118
|
+
/** Re-resolve the config (a keychain-only change, or a retry) and apply it. */
|
|
2119
|
+
scheduleSecretReresolve(delayMs) {
|
|
2120
|
+
const timerOf = delayMs === SECRET_CHANGE_DEBOUNCE_MS ? "_secretChangeTimer" : "_secretRetryTimer";
|
|
2121
|
+
if (this[timerOf] !== null) return;
|
|
2122
|
+
const timer = setTimeout(() => {
|
|
2123
|
+
this[timerOf] = null;
|
|
2124
|
+
if (this._ctx === null) return;
|
|
2125
|
+
this.resolveConfig().then(() => this.onConfigChanged()).catch((err) => {
|
|
2126
|
+
this._ctx?.logger?.error?.("re-resolving secrets failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
2127
|
+
});
|
|
2128
|
+
}, delayMs);
|
|
2129
|
+
timer.unref?.();
|
|
2130
|
+
this[timerOf] = timer;
|
|
2131
|
+
}
|
|
1234
2132
|
/**
|
|
1235
2133
|
* Wrap `ctx.settings.readAddonStore()` with a short retry budget so a
|
|
1236
2134
|
* transient settings-store outage (mid-restart of sqlite-settings, tsx-watch
|
|
@@ -1470,6 +2368,312 @@ var PeerBytesTicketSchema = zod.z.object({
|
|
|
1470
2368
|
declaredBytes: zod.z.number().int().nonnegative().nullable()
|
|
1471
2369
|
});
|
|
1472
2370
|
//#endregion
|
|
2371
|
+
//#region src/keychain/known-secrets.ts
|
|
2372
|
+
/**
|
|
2373
|
+
* The per-process set of secret VALUES this process has resolved, so the log
|
|
2374
|
+
* pipeline can scrub them wherever they turn up — a library's error message,
|
|
2375
|
+
* an argv a spawn logged, a URL someone interpolated.
|
|
2376
|
+
*
|
|
2377
|
+
* Key-name redaction cannot catch `Command failed: cloudflared --token eyJ…`:
|
|
2378
|
+
* the value is in a message, not under a named key. The only thing that can is
|
|
2379
|
+
* knowing the value. `ctx.secrets` registers every value it resolves (and every
|
|
2380
|
+
* value it stores), and `scrubKnownSecrets` replaces each occurrence with
|
|
2381
|
+
* `‹secret:<hash of its ref>›` — enough to say WHICH secret leaked, nothing of
|
|
2382
|
+
* what it is.
|
|
2383
|
+
*
|
|
2384
|
+
* Process-global on purpose: a forked runner scrubs its own logs before they
|
|
2385
|
+
* leave over UDS, and hub-main scrubs what it resolved. Values shorter than
|
|
2386
|
+
* {@link MIN_SCRUBBED_LENGTH} are not registered — a 4-digit PIN would turn
|
|
2387
|
+
* every timestamp containing it into a redaction.
|
|
2388
|
+
*/
|
|
2389
|
+
/** Below this length a value is too likely to collide with ordinary text. */
|
|
2390
|
+
var MIN_SCRUBBED_LENGTH = 8;
|
|
2391
|
+
/**
|
|
2392
|
+
* One store per PROCESS, even when two copies of this module are loaded (an
|
|
2393
|
+
* addon bundle next to the framework's): keyed on a registered symbol.
|
|
2394
|
+
*/
|
|
2395
|
+
var STORE_KEY = Symbol.for("camstack.keychain.known-secrets");
|
|
2396
|
+
function store() {
|
|
2397
|
+
const existing = Reflect.get(globalThis, STORE_KEY);
|
|
2398
|
+
if (existing !== void 0 && existing !== null) return existing;
|
|
2399
|
+
const created = {
|
|
2400
|
+
values: /* @__PURE__ */ new Map(),
|
|
2401
|
+
compiled: null
|
|
2402
|
+
};
|
|
2403
|
+
Reflect.set(globalThis, STORE_KEY, created);
|
|
2404
|
+
return created;
|
|
2405
|
+
}
|
|
2406
|
+
/** FNV-1a, 32-bit, as 8 hex chars — a label, not a security property. */
|
|
2407
|
+
function shortSecretRefHash(ref) {
|
|
2408
|
+
let hash = 2166136261;
|
|
2409
|
+
for (let i = 0; i < ref.length; i++) {
|
|
2410
|
+
hash ^= ref.charCodeAt(i);
|
|
2411
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
2412
|
+
}
|
|
2413
|
+
return hash.toString(16).padStart(8, "0");
|
|
2414
|
+
}
|
|
2415
|
+
function escapeRegExp(value) {
|
|
2416
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2417
|
+
}
|
|
2418
|
+
function recompile() {
|
|
2419
|
+
const s = store();
|
|
2420
|
+
if (s.values.size === 0) {
|
|
2421
|
+
s.compiled = null;
|
|
2422
|
+
return;
|
|
2423
|
+
}
|
|
2424
|
+
const values = [...s.values.keys()].toSorted((a, b) => b.length - a.length);
|
|
2425
|
+
s.compiled = new RegExp(values.map(escapeRegExp).join("|"), "g");
|
|
2426
|
+
}
|
|
2427
|
+
/** Remember a value this process holds. A no-op for short or empty values. */
|
|
2428
|
+
function registerKnownSecret(value, ref) {
|
|
2429
|
+
if (value.length < 8) return;
|
|
2430
|
+
const label = `‹secret:${shortSecretRefHash(ref)}›`;
|
|
2431
|
+
const values = store().values;
|
|
2432
|
+
if (values.get(value) === label) return;
|
|
2433
|
+
values.set(value, label);
|
|
2434
|
+
recompile();
|
|
2435
|
+
}
|
|
2436
|
+
/** Forget a value (it was replaced or deleted). */
|
|
2437
|
+
function forgetKnownSecret(value) {
|
|
2438
|
+
if (!store().values.delete(value)) return;
|
|
2439
|
+
recompile();
|
|
2440
|
+
}
|
|
2441
|
+
/** How many values are registered — for tests and the status page. */
|
|
2442
|
+
function knownSecretCount() {
|
|
2443
|
+
return store().values.size;
|
|
2444
|
+
}
|
|
2445
|
+
/** Test seam: drop every registered value. */
|
|
2446
|
+
function clearKnownSecrets() {
|
|
2447
|
+
const s = store();
|
|
2448
|
+
s.values.clear();
|
|
2449
|
+
s.compiled = null;
|
|
2450
|
+
}
|
|
2451
|
+
/** Replace every registered value inside `text` with its label. */
|
|
2452
|
+
function scrubKnownSecrets(text) {
|
|
2453
|
+
const s = store();
|
|
2454
|
+
const re = s.compiled;
|
|
2455
|
+
if (re === null || text.length < 8) return text;
|
|
2456
|
+
re.lastIndex = 0;
|
|
2457
|
+
if (!re.test(text)) return text;
|
|
2458
|
+
re.lastIndex = 0;
|
|
2459
|
+
return text.replace(re, (match) => s.values.get(match) ?? "‹secret›");
|
|
2460
|
+
}
|
|
2461
|
+
//#endregion
|
|
2462
|
+
//#region src/keychain/secrets-access.ts
|
|
2463
|
+
/** Re-fetch a cached value after this long, even with no change event. */
|
|
2464
|
+
var REVALIDATE_AFTER_MS = 15 * 6e4;
|
|
2465
|
+
function unavailableAll(refs) {
|
|
2466
|
+
const out = {};
|
|
2467
|
+
for (const ref of refs) out[ref] = {
|
|
2468
|
+
kind: "unavailable",
|
|
2469
|
+
reason: "not-initialized"
|
|
2470
|
+
};
|
|
2471
|
+
return out;
|
|
2472
|
+
}
|
|
2473
|
+
function createSecretsAccess(opts) {
|
|
2474
|
+
const owner = normalizeSecretOwner(opts.owner);
|
|
2475
|
+
const now = opts.now ?? Date.now;
|
|
2476
|
+
const cache = /* @__PURE__ */ new Map();
|
|
2477
|
+
const handlers = /* @__PURE__ */ new Set();
|
|
2478
|
+
/** One delegate handle per owner — each holds a cache and a subscription. */
|
|
2479
|
+
const delegates = /* @__PURE__ */ new Map();
|
|
2480
|
+
const drop = (ref) => {
|
|
2481
|
+
const entry = cache.get(ref);
|
|
2482
|
+
if (entry === void 0) return;
|
|
2483
|
+
cache.delete(ref);
|
|
2484
|
+
forgetKnownSecret(entry.value);
|
|
2485
|
+
for (const handler of handlers) try {
|
|
2486
|
+
handler(ref);
|
|
2487
|
+
} catch {}
|
|
2488
|
+
};
|
|
2489
|
+
opts.eventBus?.subscribe({ category: require_event_category.EventCategory.KeychainChanged }, (event) => {
|
|
2490
|
+
const data = event.data;
|
|
2491
|
+
if (data === null || typeof data !== "object") return;
|
|
2492
|
+
const ref = Reflect.get(data, "ref");
|
|
2493
|
+
const version = Reflect.get(data, "version");
|
|
2494
|
+
if (typeof ref !== "string") return;
|
|
2495
|
+
const entry = cache.get(ref);
|
|
2496
|
+
if (entry === void 0) return;
|
|
2497
|
+
if (typeof version === "number" && version === entry.version) return;
|
|
2498
|
+
drop(ref);
|
|
2499
|
+
});
|
|
2500
|
+
const remember = (ref, value, version) => {
|
|
2501
|
+
const previous = cache.get(ref);
|
|
2502
|
+
if (previous !== void 0 && previous.value !== value) forgetKnownSecret(previous.value);
|
|
2503
|
+
cache.set(ref, {
|
|
2504
|
+
value,
|
|
2505
|
+
version,
|
|
2506
|
+
fetchedAt: now()
|
|
2507
|
+
});
|
|
2508
|
+
registerKnownSecret(value, ref);
|
|
2509
|
+
};
|
|
2510
|
+
return {
|
|
2511
|
+
owner,
|
|
2512
|
+
ref: (scope, fieldPath) => buildSecretRef({
|
|
2513
|
+
owner,
|
|
2514
|
+
scope,
|
|
2515
|
+
fieldPath
|
|
2516
|
+
}),
|
|
2517
|
+
async resolve(refs) {
|
|
2518
|
+
const out = {};
|
|
2519
|
+
const toFetch = [];
|
|
2520
|
+
const t = now();
|
|
2521
|
+
for (const ref of new Set(refs)) {
|
|
2522
|
+
const hit = cache.get(ref);
|
|
2523
|
+
if (hit !== void 0 && t - hit.fetchedAt < 9e5) out[ref] = {
|
|
2524
|
+
kind: "value",
|
|
2525
|
+
value: hit.value,
|
|
2526
|
+
version: hit.version
|
|
2527
|
+
};
|
|
2528
|
+
else toFetch.push(ref);
|
|
2529
|
+
}
|
|
2530
|
+
if (toFetch.length === 0) return out;
|
|
2531
|
+
let fetched;
|
|
2532
|
+
try {
|
|
2533
|
+
fetched = await opts.transport.resolve({ refs: toFetch });
|
|
2534
|
+
} catch {
|
|
2535
|
+
fetched = unavailableAll(toFetch);
|
|
2536
|
+
}
|
|
2537
|
+
for (const ref of toFetch) {
|
|
2538
|
+
const entry = fetched[ref] ?? {
|
|
2539
|
+
kind: "unavailable",
|
|
2540
|
+
reason: "not-initialized"
|
|
2541
|
+
};
|
|
2542
|
+
if (entry.kind === "value") {
|
|
2543
|
+
remember(ref, entry.value, entry.version);
|
|
2544
|
+
out[ref] = entry;
|
|
2545
|
+
continue;
|
|
2546
|
+
}
|
|
2547
|
+
if (entry.kind === "unavailable") {
|
|
2548
|
+
const stale = cache.get(ref);
|
|
2549
|
+
out[ref] = stale !== void 0 ? {
|
|
2550
|
+
kind: "value",
|
|
2551
|
+
value: stale.value,
|
|
2552
|
+
version: stale.version
|
|
2553
|
+
} : entry;
|
|
2554
|
+
continue;
|
|
2555
|
+
}
|
|
2556
|
+
if (cache.has(ref)) drop(ref);
|
|
2557
|
+
out[ref] = entry;
|
|
2558
|
+
}
|
|
2559
|
+
return out;
|
|
2560
|
+
},
|
|
2561
|
+
async put(ref, value) {
|
|
2562
|
+
const result = await opts.transport.put({
|
|
2563
|
+
ref,
|
|
2564
|
+
value
|
|
2565
|
+
});
|
|
2566
|
+
remember(ref, value, result.version);
|
|
2567
|
+
return result.version;
|
|
2568
|
+
},
|
|
2569
|
+
async delete(refs) {
|
|
2570
|
+
if (refs.length === 0) return 0;
|
|
2571
|
+
const result = await opts.transport.delete({ refs });
|
|
2572
|
+
for (const ref of refs) drop(ref);
|
|
2573
|
+
return result.deleted;
|
|
2574
|
+
},
|
|
2575
|
+
state: () => opts.transport.getState(),
|
|
2576
|
+
onChange(handler) {
|
|
2577
|
+
handlers.add(handler);
|
|
2578
|
+
return () => {
|
|
2579
|
+
handlers.delete(handler);
|
|
2580
|
+
};
|
|
2581
|
+
},
|
|
2582
|
+
forOwner: opts.transportForOwner === void 0 ? null : (other) => {
|
|
2583
|
+
const target = normalizeSecretOwner(other);
|
|
2584
|
+
const existing = delegates.get(target);
|
|
2585
|
+
if (existing !== void 0) return existing;
|
|
2586
|
+
const build = opts.transportForOwner;
|
|
2587
|
+
if (build === void 0) throw new Error("forOwner: no delegate transport");
|
|
2588
|
+
const delegate = createSecretsAccess({
|
|
2589
|
+
owner: target,
|
|
2590
|
+
transport: build(target),
|
|
2591
|
+
...opts.eventBus !== void 0 ? { eventBus: opts.eventBus } : {},
|
|
2592
|
+
now
|
|
2593
|
+
});
|
|
2594
|
+
delegates.set(target, delegate);
|
|
2595
|
+
return delegate;
|
|
2596
|
+
}
|
|
2597
|
+
};
|
|
2598
|
+
}
|
|
2599
|
+
//#endregion
|
|
2600
|
+
//#region src/keychain/resolve-secret-views.ts
|
|
2601
|
+
/**
|
|
2602
|
+
* Resolve the secret VIEWS a form sends as action input (D789).
|
|
2603
|
+
*
|
|
2604
|
+
* A settings form knows a stored secret only as `{ $secret: ref, set: true }`.
|
|
2605
|
+
* An action that reads one from the form (`paramsFromForm`, e.g. "list my
|
|
2606
|
+
* Cloudflare zones with the stored token") therefore receives the view. The
|
|
2607
|
+
* owner's process swaps each view for its value before the action's input
|
|
2608
|
+
* schema runs — through the owner's own `ctx.secrets`, so a view naming
|
|
2609
|
+
* another addon's ref resolves to nothing (owner-checked by the keychain).
|
|
2610
|
+
*
|
|
2611
|
+
* A view that cannot be resolved (pre-keychain inline value, missing,
|
|
2612
|
+
* unavailable, forbidden) and an explicit clear instruction are DROPPED: the
|
|
2613
|
+
* action sees the parameter as absent, never as an object it would mis-read.
|
|
2614
|
+
*/
|
|
2615
|
+
function viewRef(value) {
|
|
2616
|
+
if (!isSecretMarkerLike(value)) return null;
|
|
2617
|
+
const ref = Reflect.get(value, "$secret");
|
|
2618
|
+
return typeof ref === "string" && ref.length > 0 ? ref : null;
|
|
2619
|
+
}
|
|
2620
|
+
function collect(value, out) {
|
|
2621
|
+
if (isSecretClear(value) || isSecretMarkerLike(value)) {
|
|
2622
|
+
const ref = viewRef(value);
|
|
2623
|
+
if (ref !== null) out.add(ref);
|
|
2624
|
+
return true;
|
|
2625
|
+
}
|
|
2626
|
+
let found = false;
|
|
2627
|
+
if (Array.isArray(value)) for (const item of value) found = collect(item, out) || found;
|
|
2628
|
+
else if (value !== null && typeof value === "object") for (const child of Object.values(value)) found = collect(child, out) || found;
|
|
2629
|
+
return found;
|
|
2630
|
+
}
|
|
2631
|
+
async function resolveSecretViewsIn(args, secrets) {
|
|
2632
|
+
const refs = /* @__PURE__ */ new Set();
|
|
2633
|
+
if (!collect(args, refs)) return args;
|
|
2634
|
+
const answers = secrets !== void 0 && refs.size > 0 ? await secrets.resolve([...refs]) : {};
|
|
2635
|
+
const swap = (value) => {
|
|
2636
|
+
if (isSecretClear(value) || isSecretMarkerLike(value)) {
|
|
2637
|
+
const ref = viewRef(value);
|
|
2638
|
+
const answer = ref !== null ? answers[ref] : void 0;
|
|
2639
|
+
return answer?.kind === "value" ? {
|
|
2640
|
+
keep: true,
|
|
2641
|
+
value: answer.value
|
|
2642
|
+
} : {
|
|
2643
|
+
keep: false,
|
|
2644
|
+
value: void 0
|
|
2645
|
+
};
|
|
2646
|
+
}
|
|
2647
|
+
if (Array.isArray(value)) {
|
|
2648
|
+
const items = [];
|
|
2649
|
+
for (const item of value) {
|
|
2650
|
+
const s = swap(item);
|
|
2651
|
+
if (s.keep) items.push(s.value);
|
|
2652
|
+
}
|
|
2653
|
+
return {
|
|
2654
|
+
keep: true,
|
|
2655
|
+
value: items
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2658
|
+
if (value !== null && typeof value === "object") {
|
|
2659
|
+
const out = {};
|
|
2660
|
+
for (const [k, child] of Object.entries(value)) {
|
|
2661
|
+
const s = swap(child);
|
|
2662
|
+
if (s.keep) out[k] = s.value;
|
|
2663
|
+
}
|
|
2664
|
+
return {
|
|
2665
|
+
keep: true,
|
|
2666
|
+
value: out
|
|
2667
|
+
};
|
|
2668
|
+
}
|
|
2669
|
+
return {
|
|
2670
|
+
keep: true,
|
|
2671
|
+
value
|
|
2672
|
+
};
|
|
2673
|
+
};
|
|
2674
|
+
return swap(args).value;
|
|
2675
|
+
}
|
|
2676
|
+
//#endregion
|
|
1473
2677
|
//#region src/capabilities/schemas/streaming-shared.ts
|
|
1474
2678
|
/** Shared Zod schemas used across streaming capabilities. */
|
|
1475
2679
|
var CamProfileSchema = zod.z.enum([
|
|
@@ -2782,7 +3986,95 @@ var deviceOpsCapability = {
|
|
|
2782
3986
|
*/
|
|
2783
3987
|
getRawState: method(zod.z.object({ deviceId: zod.z.number() }), RawStateResultSchema.nullable(), { auth: "protected" })
|
|
2784
3988
|
}
|
|
2785
|
-
};
|
|
3989
|
+
};
|
|
3990
|
+
//#endregion
|
|
3991
|
+
//#region src/device/zod-secret.ts
|
|
3992
|
+
/**
|
|
3993
|
+
* Secret declaration on a Zod config schema (D783).
|
|
3994
|
+
*
|
|
3995
|
+
* A field is a secret because its schema SAYS so — `.meta({ secret: true })`,
|
|
3996
|
+
* spelled {@link SECRET_META} — never because its key looks like a credential.
|
|
3997
|
+
* It is the Zod twin of `secret: true` / `type: 'password'` on a config-UI
|
|
3998
|
+
* field (`isSecretConfigField`), and every consumer that treats secrets
|
|
3999
|
+
* specially reads it: the settings form (`zodEntriesToConfigUI`), the paths a
|
|
4000
|
+
* device advertises at `registerDevice` (`secretConfigPaths`, which the
|
|
4001
|
+
* non-admin redaction withholds at any depth), and the keychain later.
|
|
4002
|
+
*
|
|
4003
|
+
* Zod keeps `.meta()` in `z.globalRegistry`, which lives on `globalThis`, so a
|
|
4004
|
+
* declaration made by an addon's own copy of zod is visible here.
|
|
4005
|
+
*/
|
|
4006
|
+
/**
|
|
4007
|
+
* `{ secret: true }` — the one spelling of "this field holds a credential".
|
|
4008
|
+
*
|
|
4009
|
+
* Addons write the LITERAL `.meta({ secret: true })` rather than importing
|
|
4010
|
+
* this constant: an addon running against an older `@camstack/types` would
|
|
4011
|
+
* otherwise call `.meta(undefined)`, which in zod v4 is the metadata GETTER
|
|
4012
|
+
* and returns no schema at all.
|
|
4013
|
+
*/
|
|
4014
|
+
var SECRET_META = { secret: true };
|
|
4015
|
+
function defOf(schema) {
|
|
4016
|
+
return schema._zod?.def ?? {};
|
|
4017
|
+
}
|
|
4018
|
+
function declaredHere(schema) {
|
|
4019
|
+
return zod.z.globalRegistry.get(schema)?.secret === true;
|
|
4020
|
+
}
|
|
4021
|
+
/** Wrapper kinds whose `innerType` is the field itself. */
|
|
4022
|
+
var WRAPPERS = new Set([
|
|
4023
|
+
"optional",
|
|
4024
|
+
"nullable",
|
|
4025
|
+
"default",
|
|
4026
|
+
"prefault",
|
|
4027
|
+
"catch",
|
|
4028
|
+
"readonly",
|
|
4029
|
+
"nonoptional"
|
|
4030
|
+
]);
|
|
4031
|
+
/**
|
|
4032
|
+
* The schema layers that make up ONE field: the schema itself and every
|
|
4033
|
+
* wrapper below it (optional / default / nullable / readonly / catch), plus
|
|
4034
|
+
* both sides of a pipe (`z.preprocess` puts the real schema on `out`,
|
|
4035
|
+
* `.transform` on `in`).
|
|
4036
|
+
*/
|
|
4037
|
+
function layersOf(schema) {
|
|
4038
|
+
const out = [];
|
|
4039
|
+
const queue = [schema];
|
|
4040
|
+
while (queue.length > 0) {
|
|
4041
|
+
const s = queue.shift();
|
|
4042
|
+
if (!s || out.includes(s)) continue;
|
|
4043
|
+
out.push(s);
|
|
4044
|
+
const def = defOf(s);
|
|
4045
|
+
if (def.type !== void 0 && WRAPPERS.has(def.type) && def.innerType) queue.push(def.innerType);
|
|
4046
|
+
if (def.type === "pipe") {
|
|
4047
|
+
if (def.in) queue.push(def.in);
|
|
4048
|
+
if (def.out) queue.push(def.out);
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
return out;
|
|
4052
|
+
}
|
|
4053
|
+
/** True when any layer of this field declares `.meta({ secret: true })`. */
|
|
4054
|
+
function isZodSecret(schema) {
|
|
4055
|
+
return layersOf(schema).some(declaredHere);
|
|
4056
|
+
}
|
|
4057
|
+
/**
|
|
4058
|
+
* Every declared secret path in a config schema: dotted keys, `[]` for "each
|
|
4059
|
+
* element of this array" (`connection.password`, `rows[].token`). A declared
|
|
4060
|
+
* object or array is reported as one path and not descended into — everything
|
|
4061
|
+
* under it is secret.
|
|
4062
|
+
*/
|
|
4063
|
+
function collectZodSecretPaths(schema, prefix = "") {
|
|
4064
|
+
if (isZodSecret(schema)) return prefix.length > 0 ? [prefix] : [];
|
|
4065
|
+
const paths = [];
|
|
4066
|
+
for (const layer of layersOf(schema)) {
|
|
4067
|
+
const def = defOf(layer);
|
|
4068
|
+
if (def.type === "object" && def.shape) for (const [key, child] of Object.entries(def.shape)) paths.push(...collectZodSecretPaths(child, prefix ? `${prefix}.${key}` : key));
|
|
4069
|
+
else if (def.type === "array" && def.element) paths.push(...collectZodSecretPaths(def.element, `${prefix}[]`));
|
|
4070
|
+
else if (def.type === "union" && def.options) for (const option of def.options) paths.push(...collectZodSecretPaths(option, prefix));
|
|
4071
|
+
else if (def.type === "intersection") {
|
|
4072
|
+
if (def.left) paths.push(...collectZodSecretPaths(def.left, prefix));
|
|
4073
|
+
if (def.right) paths.push(...collectZodSecretPaths(def.right, prefix));
|
|
4074
|
+
}
|
|
4075
|
+
}
|
|
4076
|
+
return [...new Set(paths)];
|
|
4077
|
+
}
|
|
2786
4078
|
//#endregion
|
|
2787
4079
|
//#region src/device/device-state-handle.ts
|
|
2788
4080
|
var DEVICE_STATE_EVENT_CATEGORY = "device.state-changed";
|
|
@@ -3417,6 +4709,21 @@ function createDeviceProxy(api, binding, opts) {
|
|
|
3417
4709
|
getRetrainExportUrl: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getRetrainExportUrl", "query", input),
|
|
3418
4710
|
setRetrainFrameComplete: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "setRetrainFrameComplete", "mutation", input),
|
|
3419
4711
|
writeRetrainArchive: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "writeRetrainArchive", "mutation", input),
|
|
4712
|
+
previewRetrainDataset: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "previewRetrainDataset", "query", input),
|
|
4713
|
+
createRetrainDatasetVersion: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "createRetrainDatasetVersion", "mutation", input),
|
|
4714
|
+
listRetrainDatasetVersions: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listRetrainDatasetVersions", "query", input),
|
|
4715
|
+
deleteRetrainDatasetVersion: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "deleteRetrainDatasetVersion", "mutation", input),
|
|
4716
|
+
verifyRetrainDatasetVersion: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "verifyRetrainDatasetVersion", "query", input),
|
|
4717
|
+
listRetrainAssistProfiles: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listRetrainAssistProfiles", "query", input),
|
|
4718
|
+
proposeRetrainAnnotationsWithLlm: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "proposeRetrainAnnotationsWithLlm", "mutation", input),
|
|
4719
|
+
createRetrainCollection: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "createRetrainCollection", "mutation", input),
|
|
4720
|
+
listRetrainCollections: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listRetrainCollections", "query", input),
|
|
4721
|
+
renameRetrainCollection: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "renameRetrainCollection", "mutation", input),
|
|
4722
|
+
deleteRetrainCollection: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "deleteRetrainCollection", "mutation", input),
|
|
4723
|
+
addRetrainFrameFromRecording: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "addRetrainFrameFromRecording", "mutation", input),
|
|
4724
|
+
addRetrainFramesToCollection: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "addRetrainFramesToCollection", "mutation", input),
|
|
4725
|
+
removeRetrainFrameFromCollection: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "removeRetrainFrameFromCollection", "mutation", input),
|
|
4726
|
+
listRetrainCollectionFrames: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listRetrainCollectionFrames", "query", input),
|
|
3420
4727
|
getEventMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getEventMedia", "query", input),
|
|
3421
4728
|
getTrackMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getTrackMedia", "query", input),
|
|
3422
4729
|
listTrackMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listTrackMedia", "query", input),
|
|
@@ -3923,94 +5230,6 @@ function createDeviceProxy(api, binding, opts) {
|
|
|
3923
5230
|
};
|
|
3924
5231
|
}
|
|
3925
5232
|
//#endregion
|
|
3926
|
-
//#region src/device/zod-secret.ts
|
|
3927
|
-
/**
|
|
3928
|
-
* Secret declaration on a Zod config schema (D783).
|
|
3929
|
-
*
|
|
3930
|
-
* A field is a secret because its schema SAYS so — `.meta({ secret: true })`,
|
|
3931
|
-
* spelled {@link SECRET_META} — never because its key looks like a credential.
|
|
3932
|
-
* It is the Zod twin of `secret: true` / `type: 'password'` on a config-UI
|
|
3933
|
-
* field (`isSecretConfigField`), and every consumer that treats secrets
|
|
3934
|
-
* specially reads it: the settings form (`zodEntriesToConfigUI`), the paths a
|
|
3935
|
-
* device advertises at `registerDevice` (`secretConfigPaths`, which the
|
|
3936
|
-
* non-admin redaction withholds at any depth), and the keychain later.
|
|
3937
|
-
*
|
|
3938
|
-
* Zod keeps `.meta()` in `z.globalRegistry`, which lives on `globalThis`, so a
|
|
3939
|
-
* declaration made by an addon's own copy of zod is visible here.
|
|
3940
|
-
*/
|
|
3941
|
-
/**
|
|
3942
|
-
* `{ secret: true }` — the one spelling of "this field holds a credential".
|
|
3943
|
-
*
|
|
3944
|
-
* Addons write the LITERAL `.meta({ secret: true })` rather than importing
|
|
3945
|
-
* this constant: an addon running against an older `@camstack/types` would
|
|
3946
|
-
* otherwise call `.meta(undefined)`, which in zod v4 is the metadata GETTER
|
|
3947
|
-
* and returns no schema at all.
|
|
3948
|
-
*/
|
|
3949
|
-
var SECRET_META = { secret: true };
|
|
3950
|
-
function defOf(schema) {
|
|
3951
|
-
return schema._zod?.def ?? {};
|
|
3952
|
-
}
|
|
3953
|
-
function declaredHere(schema) {
|
|
3954
|
-
return zod.z.globalRegistry.get(schema)?.secret === true;
|
|
3955
|
-
}
|
|
3956
|
-
/** Wrapper kinds whose `innerType` is the field itself. */
|
|
3957
|
-
var WRAPPERS = new Set([
|
|
3958
|
-
"optional",
|
|
3959
|
-
"nullable",
|
|
3960
|
-
"default",
|
|
3961
|
-
"prefault",
|
|
3962
|
-
"catch",
|
|
3963
|
-
"readonly",
|
|
3964
|
-
"nonoptional"
|
|
3965
|
-
]);
|
|
3966
|
-
/**
|
|
3967
|
-
* The schema layers that make up ONE field: the schema itself and every
|
|
3968
|
-
* wrapper below it (optional / default / nullable / readonly / catch), plus
|
|
3969
|
-
* both sides of a pipe (`z.preprocess` puts the real schema on `out`,
|
|
3970
|
-
* `.transform` on `in`).
|
|
3971
|
-
*/
|
|
3972
|
-
function layersOf(schema) {
|
|
3973
|
-
const out = [];
|
|
3974
|
-
const queue = [schema];
|
|
3975
|
-
while (queue.length > 0) {
|
|
3976
|
-
const s = queue.shift();
|
|
3977
|
-
if (!s || out.includes(s)) continue;
|
|
3978
|
-
out.push(s);
|
|
3979
|
-
const def = defOf(s);
|
|
3980
|
-
if (def.type !== void 0 && WRAPPERS.has(def.type) && def.innerType) queue.push(def.innerType);
|
|
3981
|
-
if (def.type === "pipe") {
|
|
3982
|
-
if (def.in) queue.push(def.in);
|
|
3983
|
-
if (def.out) queue.push(def.out);
|
|
3984
|
-
}
|
|
3985
|
-
}
|
|
3986
|
-
return out;
|
|
3987
|
-
}
|
|
3988
|
-
/** True when any layer of this field declares `.meta({ secret: true })`. */
|
|
3989
|
-
function isZodSecret(schema) {
|
|
3990
|
-
return layersOf(schema).some(declaredHere);
|
|
3991
|
-
}
|
|
3992
|
-
/**
|
|
3993
|
-
* Every declared secret path in a config schema: dotted keys, `[]` for "each
|
|
3994
|
-
* element of this array" (`connection.password`, `rows[].token`). A declared
|
|
3995
|
-
* object or array is reported as one path and not descended into — everything
|
|
3996
|
-
* under it is secret.
|
|
3997
|
-
*/
|
|
3998
|
-
function collectZodSecretPaths(schema, prefix = "") {
|
|
3999
|
-
if (isZodSecret(schema)) return prefix.length > 0 ? [prefix] : [];
|
|
4000
|
-
const paths = [];
|
|
4001
|
-
for (const layer of layersOf(schema)) {
|
|
4002
|
-
const def = defOf(layer);
|
|
4003
|
-
if (def.type === "object" && def.shape) for (const [key, child] of Object.entries(def.shape)) paths.push(...collectZodSecretPaths(child, prefix ? `${prefix}.${key}` : key));
|
|
4004
|
-
else if (def.type === "array" && def.element) paths.push(...collectZodSecretPaths(def.element, `${prefix}[]`));
|
|
4005
|
-
else if (def.type === "union" && def.options) for (const option of def.options) paths.push(...collectZodSecretPaths(option, prefix));
|
|
4006
|
-
else if (def.type === "intersection") {
|
|
4007
|
-
if (def.left) paths.push(...collectZodSecretPaths(def.left, prefix));
|
|
4008
|
-
if (def.right) paths.push(...collectZodSecretPaths(def.right, prefix));
|
|
4009
|
-
}
|
|
4010
|
-
}
|
|
4011
|
-
return [...new Set(paths)];
|
|
4012
|
-
}
|
|
4013
|
-
//#endregion
|
|
4014
5233
|
//#region src/generated/device-scoped-caps.ts
|
|
4015
5234
|
/**
|
|
4016
5235
|
* AUTO-GENERATED by scripts/generate-device-scoped-caps.ts — DO NOT EDIT.
|
|
@@ -4405,6 +5624,18 @@ Object.defineProperty(exports, "FrameHandleSchema", {
|
|
|
4405
5624
|
return FrameHandleSchema;
|
|
4406
5625
|
}
|
|
4407
5626
|
});
|
|
5627
|
+
Object.defineProperty(exports, "KeychainWriteRefusedError", {
|
|
5628
|
+
enumerable: true,
|
|
5629
|
+
get: function() {
|
|
5630
|
+
return KeychainWriteRefusedError;
|
|
5631
|
+
}
|
|
5632
|
+
});
|
|
5633
|
+
Object.defineProperty(exports, "MIN_SCRUBBED_LENGTH", {
|
|
5634
|
+
enumerable: true,
|
|
5635
|
+
get: function() {
|
|
5636
|
+
return MIN_SCRUBBED_LENGTH;
|
|
5637
|
+
}
|
|
5638
|
+
});
|
|
4408
5639
|
Object.defineProperty(exports, "PEER_BYTES_CHUNK_BYTES", {
|
|
4409
5640
|
enumerable: true,
|
|
4410
5641
|
get: function() {
|
|
@@ -4459,6 +5690,18 @@ Object.defineProperty(exports, "ProfileSlotStatusSchema", {
|
|
|
4459
5690
|
return ProfileSlotStatusSchema;
|
|
4460
5691
|
}
|
|
4461
5692
|
});
|
|
5693
|
+
Object.defineProperty(exports, "REDACTED_SECRET", {
|
|
5694
|
+
enumerable: true,
|
|
5695
|
+
get: function() {
|
|
5696
|
+
return REDACTED_SECRET;
|
|
5697
|
+
}
|
|
5698
|
+
});
|
|
5699
|
+
Object.defineProperty(exports, "REVALIDATE_AFTER_MS", {
|
|
5700
|
+
enumerable: true,
|
|
5701
|
+
get: function() {
|
|
5702
|
+
return REVALIDATE_AFTER_MS;
|
|
5703
|
+
}
|
|
5704
|
+
});
|
|
4462
5705
|
Object.defineProperty(exports, "RawStateResultSchema", {
|
|
4463
5706
|
enumerable: true,
|
|
4464
5707
|
get: function() {
|
|
@@ -4477,12 +5720,24 @@ Object.defineProperty(exports, "ReadinessTimeoutError", {
|
|
|
4477
5720
|
return ReadinessTimeoutError;
|
|
4478
5721
|
}
|
|
4479
5722
|
});
|
|
5723
|
+
Object.defineProperty(exports, "SECRET_CLEAR", {
|
|
5724
|
+
enumerable: true,
|
|
5725
|
+
get: function() {
|
|
5726
|
+
return SECRET_CLEAR;
|
|
5727
|
+
}
|
|
5728
|
+
});
|
|
4480
5729
|
Object.defineProperty(exports, "SECRET_META", {
|
|
4481
5730
|
enumerable: true,
|
|
4482
5731
|
get: function() {
|
|
4483
5732
|
return SECRET_META;
|
|
4484
5733
|
}
|
|
4485
5734
|
});
|
|
5735
|
+
Object.defineProperty(exports, "SECRET_REF_SCHEME", {
|
|
5736
|
+
enumerable: true,
|
|
5737
|
+
get: function() {
|
|
5738
|
+
return SECRET_REF_SCHEME;
|
|
5739
|
+
}
|
|
5740
|
+
});
|
|
4486
5741
|
Object.defineProperty(exports, "SHARE_VIEW_KINDS", {
|
|
4487
5742
|
enumerable: true,
|
|
4488
5743
|
get: function() {
|
|
@@ -4531,6 +5786,12 @@ Object.defineProperty(exports, "adminUiCapability", {
|
|
|
4531
5786
|
return adminUiCapability;
|
|
4532
5787
|
}
|
|
4533
5788
|
});
|
|
5789
|
+
Object.defineProperty(exports, "arrayItemFieldPath", {
|
|
5790
|
+
enumerable: true,
|
|
5791
|
+
get: function() {
|
|
5792
|
+
return arrayItemFieldPath;
|
|
5793
|
+
}
|
|
5794
|
+
});
|
|
4534
5795
|
Object.defineProperty(exports, "asBoolean", {
|
|
4535
5796
|
enumerable: true,
|
|
4536
5797
|
get: function() {
|
|
@@ -4567,6 +5828,24 @@ Object.defineProperty(exports, "audioChunkBytesPerSample", {
|
|
|
4567
5828
|
return audioChunkBytesPerSample;
|
|
4568
5829
|
}
|
|
4569
5830
|
});
|
|
5831
|
+
Object.defineProperty(exports, "buildSecretRef", {
|
|
5832
|
+
enumerable: true,
|
|
5833
|
+
get: function() {
|
|
5834
|
+
return buildSecretRef;
|
|
5835
|
+
}
|
|
5836
|
+
});
|
|
5837
|
+
Object.defineProperty(exports, "classifySecretWrite", {
|
|
5838
|
+
enumerable: true,
|
|
5839
|
+
get: function() {
|
|
5840
|
+
return classifySecretWrite;
|
|
5841
|
+
}
|
|
5842
|
+
});
|
|
5843
|
+
Object.defineProperty(exports, "clearKnownSecrets", {
|
|
5844
|
+
enumerable: true,
|
|
5845
|
+
get: function() {
|
|
5846
|
+
return clearKnownSecrets;
|
|
5847
|
+
}
|
|
5848
|
+
});
|
|
4570
5849
|
Object.defineProperty(exports, "collectHydratedFieldEntries", {
|
|
4571
5850
|
enumerable: true,
|
|
4572
5851
|
get: function() {
|
|
@@ -4579,6 +5858,24 @@ Object.defineProperty(exports, "collectHydratedFieldValues", {
|
|
|
4579
5858
|
return collectHydratedFieldValues;
|
|
4580
5859
|
}
|
|
4581
5860
|
});
|
|
5861
|
+
Object.defineProperty(exports, "collectKeychainStoredKeys", {
|
|
5862
|
+
enumerable: true,
|
|
5863
|
+
get: function() {
|
|
5864
|
+
return collectKeychainStoredKeys;
|
|
5865
|
+
}
|
|
5866
|
+
});
|
|
5867
|
+
Object.defineProperty(exports, "collectSecretConfigKeys", {
|
|
5868
|
+
enumerable: true,
|
|
5869
|
+
get: function() {
|
|
5870
|
+
return collectSecretConfigKeys;
|
|
5871
|
+
}
|
|
5872
|
+
});
|
|
5873
|
+
Object.defineProperty(exports, "collectSecretMarkerRefs", {
|
|
5874
|
+
enumerable: true,
|
|
5875
|
+
get: function() {
|
|
5876
|
+
return collectSecretMarkerRefs;
|
|
5877
|
+
}
|
|
5878
|
+
});
|
|
4582
5879
|
Object.defineProperty(exports, "collectZodSecretPaths", {
|
|
4583
5880
|
enumerable: true,
|
|
4584
5881
|
get: function() {
|
|
@@ -4621,6 +5918,12 @@ Object.defineProperty(exports, "createMirrorSource", {
|
|
|
4621
5918
|
return createMirrorSource;
|
|
4622
5919
|
}
|
|
4623
5920
|
});
|
|
5921
|
+
Object.defineProperty(exports, "createSecretsAccess", {
|
|
5922
|
+
enumerable: true,
|
|
5923
|
+
get: function() {
|
|
5924
|
+
return createSecretsAccess;
|
|
5925
|
+
}
|
|
5926
|
+
});
|
|
4624
5927
|
Object.defineProperty(exports, "createSliceHandle", {
|
|
4625
5928
|
enumerable: true,
|
|
4626
5929
|
get: function() {
|
|
@@ -4687,6 +5990,24 @@ Object.defineProperty(exports, "expandCapMethods", {
|
|
|
4687
5990
|
return expandCapMethods;
|
|
4688
5991
|
}
|
|
4689
5992
|
});
|
|
5993
|
+
Object.defineProperty(exports, "forgetKnownSecret", {
|
|
5994
|
+
enumerable: true,
|
|
5995
|
+
get: function() {
|
|
5996
|
+
return forgetKnownSecret;
|
|
5997
|
+
}
|
|
5998
|
+
});
|
|
5999
|
+
Object.defineProperty(exports, "formatSecretScope", {
|
|
6000
|
+
enumerable: true,
|
|
6001
|
+
get: function() {
|
|
6002
|
+
return formatSecretScope;
|
|
6003
|
+
}
|
|
6004
|
+
});
|
|
6005
|
+
Object.defineProperty(exports, "globalSecretScope", {
|
|
6006
|
+
enumerable: true,
|
|
6007
|
+
get: function() {
|
|
6008
|
+
return globalSecretScope;
|
|
6009
|
+
}
|
|
6010
|
+
});
|
|
4690
6011
|
Object.defineProperty(exports, "hydrateSchema", {
|
|
4691
6012
|
enumerable: true,
|
|
4692
6013
|
get: function() {
|
|
@@ -4717,24 +6038,84 @@ Object.defineProperty(exports, "isEvent", {
|
|
|
4717
6038
|
return isEvent;
|
|
4718
6039
|
}
|
|
4719
6040
|
});
|
|
6041
|
+
Object.defineProperty(exports, "isKeychainStoredField", {
|
|
6042
|
+
enumerable: true,
|
|
6043
|
+
get: function() {
|
|
6044
|
+
return isKeychainStoredField;
|
|
6045
|
+
}
|
|
6046
|
+
});
|
|
6047
|
+
Object.defineProperty(exports, "isSecretClear", {
|
|
6048
|
+
enumerable: true,
|
|
6049
|
+
get: function() {
|
|
6050
|
+
return isSecretClear;
|
|
6051
|
+
}
|
|
6052
|
+
});
|
|
6053
|
+
Object.defineProperty(exports, "isSecretConfigField", {
|
|
6054
|
+
enumerable: true,
|
|
6055
|
+
get: function() {
|
|
6056
|
+
return isSecretConfigField;
|
|
6057
|
+
}
|
|
6058
|
+
});
|
|
6059
|
+
Object.defineProperty(exports, "isSecretFieldView", {
|
|
6060
|
+
enumerable: true,
|
|
6061
|
+
get: function() {
|
|
6062
|
+
return isSecretFieldView;
|
|
6063
|
+
}
|
|
6064
|
+
});
|
|
6065
|
+
Object.defineProperty(exports, "isSecretMarker", {
|
|
6066
|
+
enumerable: true,
|
|
6067
|
+
get: function() {
|
|
6068
|
+
return isSecretMarker;
|
|
6069
|
+
}
|
|
6070
|
+
});
|
|
6071
|
+
Object.defineProperty(exports, "isSecretMarkerLike", {
|
|
6072
|
+
enumerable: true,
|
|
6073
|
+
get: function() {
|
|
6074
|
+
return isSecretMarkerLike;
|
|
6075
|
+
}
|
|
6076
|
+
});
|
|
6077
|
+
Object.defineProperty(exports, "isSecretRef", {
|
|
6078
|
+
enumerable: true,
|
|
6079
|
+
get: function() {
|
|
6080
|
+
return isSecretRef;
|
|
6081
|
+
}
|
|
6082
|
+
});
|
|
4720
6083
|
Object.defineProperty(exports, "isZodSecret", {
|
|
4721
6084
|
enumerable: true,
|
|
4722
6085
|
get: function() {
|
|
4723
6086
|
return isZodSecret;
|
|
4724
6087
|
}
|
|
4725
6088
|
});
|
|
6089
|
+
Object.defineProperty(exports, "knownSecretCount", {
|
|
6090
|
+
enumerable: true,
|
|
6091
|
+
get: function() {
|
|
6092
|
+
return knownSecretCount;
|
|
6093
|
+
}
|
|
6094
|
+
});
|
|
4726
6095
|
Object.defineProperty(exports, "makeProfileBrokerId", {
|
|
4727
6096
|
enumerable: true,
|
|
4728
6097
|
get: function() {
|
|
4729
6098
|
return makeProfileBrokerId;
|
|
4730
6099
|
}
|
|
4731
6100
|
});
|
|
6101
|
+
Object.defineProperty(exports, "makeSecretMarker", {
|
|
6102
|
+
enumerable: true,
|
|
6103
|
+
get: function() {
|
|
6104
|
+
return makeSecretMarker;
|
|
6105
|
+
}
|
|
6106
|
+
});
|
|
4732
6107
|
Object.defineProperty(exports, "makeSourceBrokerId", {
|
|
4733
6108
|
enumerable: true,
|
|
4734
6109
|
get: function() {
|
|
4735
6110
|
return makeSourceBrokerId;
|
|
4736
6111
|
}
|
|
4737
6112
|
});
|
|
6113
|
+
Object.defineProperty(exports, "mergeSecretStatePaths", {
|
|
6114
|
+
enumerable: true,
|
|
6115
|
+
get: function() {
|
|
6116
|
+
return mergeSecretStatePaths;
|
|
6117
|
+
}
|
|
6118
|
+
});
|
|
4738
6119
|
Object.defineProperty(exports, "method", {
|
|
4739
6120
|
enumerable: true,
|
|
4740
6121
|
get: function() {
|
|
@@ -4759,6 +6140,12 @@ Object.defineProperty(exports, "normalizeNodeId", {
|
|
|
4759
6140
|
return normalizeNodeId;
|
|
4760
6141
|
}
|
|
4761
6142
|
});
|
|
6143
|
+
Object.defineProperty(exports, "normalizeSecretOwner", {
|
|
6144
|
+
enumerable: true,
|
|
6145
|
+
get: function() {
|
|
6146
|
+
return normalizeSecretOwner;
|
|
6147
|
+
}
|
|
6148
|
+
});
|
|
4762
6149
|
Object.defineProperty(exports, "optionalMethod", {
|
|
4763
6150
|
enumerable: true,
|
|
4764
6151
|
get: function() {
|
|
@@ -4789,6 +6176,18 @@ Object.defineProperty(exports, "parseProfileBrokerId", {
|
|
|
4789
6176
|
return parseProfileBrokerId;
|
|
4790
6177
|
}
|
|
4791
6178
|
});
|
|
6179
|
+
Object.defineProperty(exports, "parseSecretRef", {
|
|
6180
|
+
enumerable: true,
|
|
6181
|
+
get: function() {
|
|
6182
|
+
return parseSecretRef;
|
|
6183
|
+
}
|
|
6184
|
+
});
|
|
6185
|
+
Object.defineProperty(exports, "parseSecretStatePath", {
|
|
6186
|
+
enumerable: true,
|
|
6187
|
+
get: function() {
|
|
6188
|
+
return parseSecretStatePath;
|
|
6189
|
+
}
|
|
6190
|
+
});
|
|
4792
6191
|
Object.defineProperty(exports, "readNodePin", {
|
|
4793
6192
|
enumerable: true,
|
|
4794
6193
|
get: function() {
|
|
@@ -4801,6 +6200,12 @@ Object.defineProperty(exports, "readinessKey", {
|
|
|
4801
6200
|
return readinessKey;
|
|
4802
6201
|
}
|
|
4803
6202
|
});
|
|
6203
|
+
Object.defineProperty(exports, "registerKnownSecret", {
|
|
6204
|
+
enumerable: true,
|
|
6205
|
+
get: function() {
|
|
6206
|
+
return registerKnownSecret;
|
|
6207
|
+
}
|
|
6208
|
+
});
|
|
4804
6209
|
Object.defineProperty(exports, "resolveCapMount", {
|
|
4805
6210
|
enumerable: true,
|
|
4806
6211
|
get: function() {
|
|
@@ -4813,18 +6218,60 @@ Object.defineProperty(exports, "resolveHydratedFieldValue", {
|
|
|
4813
6218
|
return resolveHydratedFieldValue;
|
|
4814
6219
|
}
|
|
4815
6220
|
});
|
|
6221
|
+
Object.defineProperty(exports, "resolveSecretMarkers", {
|
|
6222
|
+
enumerable: true,
|
|
6223
|
+
get: function() {
|
|
6224
|
+
return resolveSecretMarkers;
|
|
6225
|
+
}
|
|
6226
|
+
});
|
|
6227
|
+
Object.defineProperty(exports, "resolveSecretViewsIn", {
|
|
6228
|
+
enumerable: true,
|
|
6229
|
+
get: function() {
|
|
6230
|
+
return resolveSecretViewsIn;
|
|
6231
|
+
}
|
|
6232
|
+
});
|
|
6233
|
+
Object.defineProperty(exports, "schemaDeclaresAnyField", {
|
|
6234
|
+
enumerable: true,
|
|
6235
|
+
get: function() {
|
|
6236
|
+
return schemaDeclaresAnyField;
|
|
6237
|
+
}
|
|
6238
|
+
});
|
|
4816
6239
|
Object.defineProperty(exports, "scopeKey", {
|
|
4817
6240
|
enumerable: true,
|
|
4818
6241
|
get: function() {
|
|
4819
6242
|
return scopeKey;
|
|
4820
6243
|
}
|
|
4821
6244
|
});
|
|
6245
|
+
Object.defineProperty(exports, "scrubKnownSecrets", {
|
|
6246
|
+
enumerable: true,
|
|
6247
|
+
get: function() {
|
|
6248
|
+
return scrubKnownSecrets;
|
|
6249
|
+
}
|
|
6250
|
+
});
|
|
6251
|
+
Object.defineProperty(exports, "secretTreeFromPaths", {
|
|
6252
|
+
enumerable: true,
|
|
6253
|
+
get: function() {
|
|
6254
|
+
return secretTreeFromPaths;
|
|
6255
|
+
}
|
|
6256
|
+
});
|
|
6257
|
+
Object.defineProperty(exports, "secretTreeFromSchema", {
|
|
6258
|
+
enumerable: true,
|
|
6259
|
+
get: function() {
|
|
6260
|
+
return secretTreeFromSchema;
|
|
6261
|
+
}
|
|
6262
|
+
});
|
|
4822
6263
|
Object.defineProperty(exports, "selectAssignedProfileSlots", {
|
|
4823
6264
|
enumerable: true,
|
|
4824
6265
|
get: function() {
|
|
4825
6266
|
return selectAssignedProfileSlots;
|
|
4826
6267
|
}
|
|
4827
6268
|
});
|
|
6269
|
+
Object.defineProperty(exports, "shortSecretRefHash", {
|
|
6270
|
+
enumerable: true,
|
|
6271
|
+
get: function() {
|
|
6272
|
+
return shortSecretRefHash;
|
|
6273
|
+
}
|
|
6274
|
+
});
|
|
4828
6275
|
Object.defineProperty(exports, "sleep", {
|
|
4829
6276
|
enumerable: true,
|
|
4830
6277
|
get: function() {
|
|
@@ -4849,6 +6296,18 @@ Object.defineProperty(exports, "toNodeId", {
|
|
|
4849
6296
|
return toNodeId;
|
|
4850
6297
|
}
|
|
4851
6298
|
});
|
|
6299
|
+
Object.defineProperty(exports, "toSecretFieldView", {
|
|
6300
|
+
enumerable: true,
|
|
6301
|
+
get: function() {
|
|
6302
|
+
return toSecretFieldView;
|
|
6303
|
+
}
|
|
6304
|
+
});
|
|
6305
|
+
Object.defineProperty(exports, "transformSecretWrite", {
|
|
6306
|
+
enumerable: true,
|
|
6307
|
+
get: function() {
|
|
6308
|
+
return transformSecretWrite;
|
|
6309
|
+
}
|
|
6310
|
+
});
|
|
4852
6311
|
Object.defineProperty(exports, "viewerUiCapability", {
|
|
4853
6312
|
enumerable: true,
|
|
4854
6313
|
get: function() {
|