@camstack/addon-waste-collections 0.1.35 → 0.1.37
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/CHANGELOG.md +14 -0
- package/dist/waste-collections.addon.js +181 -733
- package/dist/waste-collections.addon.mjs +181 -733
- package/package.json +2 -2
|
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
//#region ../types/dist/event-category-
|
|
5
|
+
//#region ../types/dist/event-category-78tcBemo.mjs
|
|
6
6
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
7
7
|
EventCategory["SystemBoot"] = "system.boot";
|
|
8
8
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -123,6 +123,23 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
123
123
|
*/
|
|
124
124
|
EventCategory["AddonLoadRecovered"] = "addon.load-recovered";
|
|
125
125
|
/**
|
|
126
|
+
* An addon is RUNNING although its installed package is below what the
|
|
127
|
+
* running server requires (`camstack.requires.addons` of the server, or the
|
|
128
|
+
* addon's own `requires.server`). Raised once per streak by the unsupported-
|
|
129
|
+
* addon tracker, on the hub and on agents; AlertCenter turns it into one
|
|
130
|
+
* operator alert per node and package. The addon is never held back for it.
|
|
131
|
+
*
|
|
132
|
+
* Payload: `{ packageName, addonId, nodeId, installedVersion, requiredMinimum, serverVersion, unmet }`.
|
|
133
|
+
*/
|
|
134
|
+
EventCategory["AddonUnsupportedVersion"] = "addon.unsupported-version";
|
|
135
|
+
/**
|
|
136
|
+
* The package of an {@link AddonUnsupportedVersion} alert reached a supported
|
|
137
|
+
* version, or is no longer installed on the node. AlertCenter dismisses the matching alert.
|
|
138
|
+
*
|
|
139
|
+
* Payload: `{ packageName, addonId, nodeId }`.
|
|
140
|
+
*/
|
|
141
|
+
EventCategory["AddonUnsupportedResolved"] = "addon.unsupported-resolved";
|
|
142
|
+
/**
|
|
126
143
|
* Monitor scheduled the next retry for a failed addon. Transient —
|
|
127
144
|
* surfaced to the UI for live-updating the "next retry in Ns"
|
|
128
145
|
* countdown on the Addons page row, NOT persisted as an alert.
|
|
@@ -233,7 +250,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
233
250
|
EventCategory["ProcessRestarted"] = "process.restarted";
|
|
234
251
|
/**
|
|
235
252
|
* The SET of storage locations changed — one was created, edited, enabled,
|
|
236
|
-
* disabled or deleted through
|
|
253
|
+
* disabled or deleted through the location instances or the forced-delete action.
|
|
237
254
|
*
|
|
238
255
|
* Telemetry, not a transaction (D8/D11): every consumer that re-resolves on
|
|
239
256
|
* it must also converge on its own periodic path, because a dropped event
|
|
@@ -5984,7 +6001,7 @@ var ZodIssueCode = {
|
|
|
5984
6001
|
var ZodFirstPartyTypeKind;
|
|
5985
6002
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5986
6003
|
//#endregion
|
|
5987
|
-
//#region ../types/dist/sleep-
|
|
6004
|
+
//#region ../types/dist/sleep-AEnTN3b4.mjs
|
|
5988
6005
|
/**
|
|
5989
6006
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5990
6007
|
* window to float samples (D455).
|
|
@@ -6079,16 +6096,6 @@ var AddonMigrationsBlockedError = class extends Error {
|
|
|
6079
6096
|
this.migrationIds = migrationIds;
|
|
6080
6097
|
}
|
|
6081
6098
|
};
|
|
6082
|
-
/**
|
|
6083
|
-
* The value a redacted secret is replaced with on every read surface.
|
|
6084
|
-
*
|
|
6085
|
-
* It is also the WRITE-BACK token: an `upsertLocation` that sends this value
|
|
6086
|
-
* back for a key means "keep what is stored", which is what lets an operator
|
|
6087
|
-
* rename a location without retyping its password. A literal an operator could
|
|
6088
|
-
* plausibly choose as a real password would turn that convenience into a way
|
|
6089
|
-
* to lock yourself out, hence the sentinel shape.
|
|
6090
|
-
*/
|
|
6091
|
-
var REDACTED_SECRET = "__camstack_redacted__";
|
|
6092
6099
|
/** Is this field's stored value a credential? */
|
|
6093
6100
|
function isSecretConfigField(field) {
|
|
6094
6101
|
if (field.type === "password") return true;
|
|
@@ -6730,10 +6737,33 @@ var RECORDS_ERROR_CODES = [
|
|
|
6730
6737
|
"transact-refused"
|
|
6731
6738
|
];
|
|
6732
6739
|
var PREFIX = "records/";
|
|
6740
|
+
/**
|
|
6741
|
+
* Cross-realm brand. An addon bundles its own copy of this module (D15) while
|
|
6742
|
+
* `ctx.records` is the HOST's door, so an error thrown by the host is an
|
|
6743
|
+
* instance of the host's classes, not of the addon's. The brand plus the code
|
|
6744
|
+
* make `instanceof` answer by identity of the failure, not of the copy.
|
|
6745
|
+
*/
|
|
6746
|
+
var RECORDS_ERROR_BRAND = Symbol.for("camstack.records-error");
|
|
6747
|
+
function carriesBrand(value) {
|
|
6748
|
+
return typeof value === "object" && value !== null && Reflect.get(value, RECORDS_ERROR_BRAND) === true;
|
|
6749
|
+
}
|
|
6733
6750
|
/** The base of every records failure. `detail` is the human part of the message. */
|
|
6734
6751
|
var RecordsError = class extends Error {
|
|
6752
|
+
/** The code a subclass stands for; the base stands for every code. */
|
|
6753
|
+
static code = void 0;
|
|
6754
|
+
/**
|
|
6755
|
+
* `instanceof` across copies of this module: any branded error is a
|
|
6756
|
+
* `RecordsError`, and is an instance of a subclass only when its code is the
|
|
6757
|
+
* subclass's code. Same-copy answers are unchanged.
|
|
6758
|
+
*/
|
|
6759
|
+
static [Symbol.hasInstance](value) {
|
|
6760
|
+
if (Function.prototype[Symbol.hasInstance].call(this, value)) return true;
|
|
6761
|
+
if (!carriesBrand(value)) return false;
|
|
6762
|
+
return this.code === void 0 || value.code === this.code;
|
|
6763
|
+
}
|
|
6735
6764
|
code;
|
|
6736
6765
|
detail;
|
|
6766
|
+
[RECORDS_ERROR_BRAND] = true;
|
|
6737
6767
|
constructor(code, detail) {
|
|
6738
6768
|
super(`${PREFIX}${code}: ${detail}`);
|
|
6739
6769
|
this.name = "RecordsError";
|
|
@@ -6742,96 +6772,110 @@ var RecordsError = class extends Error {
|
|
|
6742
6772
|
}
|
|
6743
6773
|
};
|
|
6744
6774
|
/** A write guarded by `ifRev` found another revision (or an existing row for `ifRev: 0`). */
|
|
6745
|
-
var RevConflictError = class extends RecordsError {
|
|
6775
|
+
var RevConflictError = class RevConflictError extends RecordsError {
|
|
6776
|
+
static code = "rev-conflict";
|
|
6746
6777
|
constructor(detail) {
|
|
6747
|
-
super(
|
|
6778
|
+
super(RevConflictError.code, detail);
|
|
6748
6779
|
this.name = "RevConflictError";
|
|
6749
6780
|
}
|
|
6750
6781
|
};
|
|
6751
6782
|
/** `data` exceeds the kind's `maxDataBytes`. Bytes are never record fields (D613). */
|
|
6752
|
-
var RecordTooLargeError = class extends RecordsError {
|
|
6783
|
+
var RecordTooLargeError = class RecordTooLargeError extends RecordsError {
|
|
6784
|
+
static code = "too-large";
|
|
6753
6785
|
constructor(detail) {
|
|
6754
|
-
super(
|
|
6786
|
+
super(RecordTooLargeError.code, detail);
|
|
6755
6787
|
this.name = "RecordTooLargeError";
|
|
6756
6788
|
}
|
|
6757
6789
|
};
|
|
6758
6790
|
/** A filter, order or aggregate names a field no declared index serves. */
|
|
6759
|
-
var UnindexedFilterError = class extends RecordsError {
|
|
6791
|
+
var UnindexedFilterError = class UnindexedFilterError extends RecordsError {
|
|
6792
|
+
static code = "unindexed-filter";
|
|
6760
6793
|
constructor(detail) {
|
|
6761
|
-
super(
|
|
6794
|
+
super(UnindexedFilterError.code, detail);
|
|
6762
6795
|
this.name = "UnindexedFilterError";
|
|
6763
6796
|
}
|
|
6764
6797
|
};
|
|
6765
6798
|
/** A bulk delete with no predicate at all. */
|
|
6766
|
-
var FilterRequiredError = class extends RecordsError {
|
|
6799
|
+
var FilterRequiredError = class FilterRequiredError extends RecordsError {
|
|
6800
|
+
static code = "filter-required";
|
|
6767
6801
|
constructor(detail) {
|
|
6768
|
-
super(
|
|
6802
|
+
super(FilterRequiredError.code, detail);
|
|
6769
6803
|
this.name = "FilterRequiredError";
|
|
6770
6804
|
}
|
|
6771
6805
|
};
|
|
6772
6806
|
/** The caller declared no such kind (another addon's kind is indistinguishable from none). */
|
|
6773
|
-
var UnknownRecordKindError = class extends RecordsError {
|
|
6807
|
+
var UnknownRecordKindError = class UnknownRecordKindError extends RecordsError {
|
|
6808
|
+
static code = "unknown-kind";
|
|
6774
6809
|
constructor(detail) {
|
|
6775
|
-
super(
|
|
6810
|
+
super(UnknownRecordKindError.code, detail);
|
|
6776
6811
|
this.name = "UnknownRecordKindError";
|
|
6777
6812
|
}
|
|
6778
6813
|
};
|
|
6779
|
-
var RecordNotFoundError = class extends RecordsError {
|
|
6814
|
+
var RecordNotFoundError = class RecordNotFoundError extends RecordsError {
|
|
6815
|
+
static code = "not-found";
|
|
6780
6816
|
constructor(detail) {
|
|
6781
|
-
super(
|
|
6817
|
+
super(RecordNotFoundError.code, detail);
|
|
6782
6818
|
this.name = "RecordNotFoundError";
|
|
6783
6819
|
}
|
|
6784
6820
|
};
|
|
6785
6821
|
/** A unique index refused the write. */
|
|
6786
|
-
var RecordUniqueViolationError = class extends RecordsError {
|
|
6822
|
+
var RecordUniqueViolationError = class RecordUniqueViolationError extends RecordsError {
|
|
6823
|
+
static code = "unique-violation";
|
|
6787
6824
|
constructor(detail) {
|
|
6788
|
-
super(
|
|
6825
|
+
super(RecordUniqueViolationError.code, detail);
|
|
6789
6826
|
this.name = "RecordUniqueViolationError";
|
|
6790
6827
|
}
|
|
6791
6828
|
};
|
|
6792
6829
|
/** A secret field held a plain string where only a keychain marker may be stored. */
|
|
6793
|
-
var PlaintextSecretError = class extends RecordsError {
|
|
6830
|
+
var PlaintextSecretError = class PlaintextSecretError extends RecordsError {
|
|
6831
|
+
static code = "plaintext-secret";
|
|
6794
6832
|
constructor(detail) {
|
|
6795
|
-
super(
|
|
6833
|
+
super(PlaintextSecretError.code, detail);
|
|
6796
6834
|
this.name = "PlaintextSecretError";
|
|
6797
6835
|
}
|
|
6798
6836
|
};
|
|
6799
6837
|
/** A marker whose ref is not the row's own (the D878 rule). */
|
|
6800
|
-
var ForeignSecretMarkerError = class extends RecordsError {
|
|
6838
|
+
var ForeignSecretMarkerError = class ForeignSecretMarkerError extends RecordsError {
|
|
6839
|
+
static code = "foreign-marker";
|
|
6801
6840
|
constructor(detail) {
|
|
6802
|
-
super(
|
|
6841
|
+
super(ForeignSecretMarkerError.code, detail);
|
|
6803
6842
|
this.name = "ForeignSecretMarkerError";
|
|
6804
6843
|
}
|
|
6805
6844
|
};
|
|
6806
6845
|
/** The keychain could not store or serve a value: explicit, never an empty string. */
|
|
6807
|
-
var RecordSecretUnavailableError = class extends RecordsError {
|
|
6846
|
+
var RecordSecretUnavailableError = class RecordSecretUnavailableError extends RecordsError {
|
|
6847
|
+
static code = "secret-unavailable";
|
|
6808
6848
|
constructor(detail) {
|
|
6809
|
-
super(
|
|
6849
|
+
super(RecordSecretUnavailableError.code, detail);
|
|
6810
6850
|
this.name = "RecordSecretUnavailableError";
|
|
6811
6851
|
}
|
|
6812
6852
|
};
|
|
6813
|
-
var InvalidRecordError = class extends RecordsError {
|
|
6853
|
+
var InvalidRecordError = class InvalidRecordError extends RecordsError {
|
|
6854
|
+
static code = "invalid-record";
|
|
6814
6855
|
constructor(detail) {
|
|
6815
|
-
super(
|
|
6856
|
+
super(InvalidRecordError.code, detail);
|
|
6816
6857
|
this.name = "InvalidRecordError";
|
|
6817
6858
|
}
|
|
6818
6859
|
};
|
|
6819
6860
|
/** A kind declaration the engine refuses (non-additive change, bad shape, wrong file). */
|
|
6820
|
-
var DeclarationRefusedError = class extends RecordsError {
|
|
6861
|
+
var DeclarationRefusedError = class DeclarationRefusedError extends RecordsError {
|
|
6862
|
+
static code = "declaration-refused";
|
|
6821
6863
|
constructor(detail) {
|
|
6822
|
-
super(
|
|
6864
|
+
super(DeclarationRefusedError.code, detail);
|
|
6823
6865
|
this.name = "DeclarationRefusedError";
|
|
6824
6866
|
}
|
|
6825
6867
|
};
|
|
6826
|
-
var NoCallerError = class extends RecordsError {
|
|
6868
|
+
var NoCallerError = class NoCallerError extends RecordsError {
|
|
6869
|
+
static code = "no-caller";
|
|
6827
6870
|
constructor(detail) {
|
|
6828
|
-
super(
|
|
6871
|
+
super(NoCallerError.code, detail);
|
|
6829
6872
|
this.name = "NoCallerError";
|
|
6830
6873
|
}
|
|
6831
6874
|
};
|
|
6832
|
-
var BatchTooLargeError = class extends RecordsError {
|
|
6875
|
+
var BatchTooLargeError = class BatchTooLargeError extends RecordsError {
|
|
6876
|
+
static code = "batch-too-large";
|
|
6833
6877
|
constructor(detail) {
|
|
6834
|
-
super(
|
|
6878
|
+
super(BatchTooLargeError.code, detail);
|
|
6835
6879
|
this.name = "BatchTooLargeError";
|
|
6836
6880
|
}
|
|
6837
6881
|
};
|
|
@@ -6840,9 +6884,10 @@ var BatchTooLargeError = class extends RecordsError {
|
|
|
6840
6884
|
* caller did not declare in ONE file and ONE `transactFamily`. The detail names
|
|
6841
6885
|
* which rule (`cross-file`, `cross-family`, `no-family`); it is never a silent split.
|
|
6842
6886
|
*/
|
|
6843
|
-
var TransactRefusedError = class extends RecordsError {
|
|
6887
|
+
var TransactRefusedError = class TransactRefusedError extends RecordsError {
|
|
6888
|
+
static code = "transact-refused";
|
|
6844
6889
|
constructor(detail) {
|
|
6845
|
-
super(
|
|
6890
|
+
super(TransactRefusedError.code, detail);
|
|
6846
6891
|
this.name = "TransactRefusedError";
|
|
6847
6892
|
}
|
|
6848
6893
|
};
|
|
@@ -7361,17 +7406,35 @@ var RecordIntegrationInstances = class {
|
|
|
7361
7406
|
...kind !== void 0 ? { kind } : {}
|
|
7362
7407
|
});
|
|
7363
7408
|
if (minted === void 0) throw new Error("createInstance needs an id: this provider does not mint one");
|
|
7409
|
+
const initial = this.hooks.initialBody(name, kind);
|
|
7410
|
+
const patch = this.patchOf(values ?? {});
|
|
7411
|
+
if (Object.keys(patch).length > 0) Object.assign(patch, await this.hooks.validateWrite?.({
|
|
7412
|
+
kind,
|
|
7413
|
+
body: {
|
|
7414
|
+
...initial,
|
|
7415
|
+
...patch
|
|
7416
|
+
}
|
|
7417
|
+
}));
|
|
7364
7418
|
await this.handle.put({
|
|
7365
7419
|
id: minted,
|
|
7366
|
-
data:
|
|
7420
|
+
data: initial
|
|
7367
7421
|
}, { ifRev: 0 });
|
|
7368
|
-
const patch = this.patchOf(values ?? {});
|
|
7369
7422
|
if (Object.keys(patch).length > 0) await this.handle.patch(minted, patch);
|
|
7370
7423
|
await this.hooks.onChanged?.(minted, "created");
|
|
7371
7424
|
return { id: minted };
|
|
7372
7425
|
};
|
|
7373
7426
|
updateInstance = async ({ id, name, values }) => {
|
|
7374
7427
|
const patch = this.patchOf(values);
|
|
7428
|
+
if (this.hooks.validateWrite !== void 0 && Object.keys(patch).length > 0) {
|
|
7429
|
+
const stored = await this.handle.get(id);
|
|
7430
|
+
if (stored !== null) Object.assign(patch, await this.hooks.validateWrite({
|
|
7431
|
+
kind: void 0,
|
|
7432
|
+
body: {
|
|
7433
|
+
...stored.data,
|
|
7434
|
+
...patch
|
|
7435
|
+
}
|
|
7436
|
+
}));
|
|
7437
|
+
}
|
|
7375
7438
|
if (name !== void 0) patch[this.nameField] = name;
|
|
7376
7439
|
await this.handle.patch(id, patch);
|
|
7377
7440
|
await this.hooks.onChanged?.(id, "updated");
|
|
@@ -10735,7 +10798,6 @@ var CAP_NAMES = Object.freeze([
|
|
|
10735
10798
|
"notifier",
|
|
10736
10799
|
"numeric-sensor",
|
|
10737
10800
|
"oauth-integration",
|
|
10738
|
-
"oidc-connections",
|
|
10739
10801
|
"osd",
|
|
10740
10802
|
"osd-manager",
|
|
10741
10803
|
"pet-feeder",
|
|
@@ -12627,8 +12689,7 @@ Object.freeze({
|
|
|
12627
12689
|
"device-provider": { "reloadDevice": 18e4 },
|
|
12628
12690
|
"llm": {
|
|
12629
12691
|
"generate": 6e5,
|
|
12630
|
-
"generateVision": 6e5
|
|
12631
|
-
"testProfile": 6e5
|
|
12692
|
+
"generateVision": 6e5
|
|
12632
12693
|
},
|
|
12633
12694
|
"llm-runtime": {
|
|
12634
12695
|
"complete": 6e5,
|
|
@@ -14031,17 +14092,18 @@ var IntegrationInstanceRefSchema = object({
|
|
|
14031
14092
|
* message to what the kind supports — callers never special-case a service.
|
|
14032
14093
|
*
|
|
14033
14094
|
* DESIGN DECISIONS (locked):
|
|
14034
|
-
* -
|
|
14035
|
-
*
|
|
14036
|
-
*
|
|
14037
|
-
*
|
|
14038
|
-
*
|
|
14039
|
-
*
|
|
14040
|
-
*
|
|
14041
|
-
*
|
|
14042
|
-
*
|
|
14043
|
-
* routable. `send` / `testTarget` /
|
|
14044
|
-
* `
|
|
14095
|
+
* - A target is an INSTANCE of this function cap (epic #440, step 6): the
|
|
14096
|
+
* providers serve their targets as `integration-instances` records, one
|
|
14097
|
+
* record kind per target kind, and the admin lists, adds, edits, switches
|
|
14098
|
+
* and deletes them through that cap. This cap keeps what an instance cannot
|
|
14099
|
+
* say: the kind catalogue (`listTargetKinds`: capabilities, delivery
|
|
14100
|
+
* parameters, brand icon), dispatch (`send`, `testTarget`) and the form
|
|
14101
|
+
* helpers (`discoverTargets`, `probeTargetConfig`).
|
|
14102
|
+
* - `listTargetKinds` / `discoverTargets` return arrays → the generated
|
|
14103
|
+
* cap-mount auto-`concatCollection`-fans them across every registered
|
|
14104
|
+
* provider so one catalog is routable. `send` / `testTarget` /
|
|
14105
|
+
* `probeTargetConfig` route to ONE provider by the `addonId` the generated
|
|
14106
|
+
* collection router extracts from the call input.
|
|
14045
14107
|
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
14046
14108
|
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
14047
14109
|
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
@@ -14286,7 +14348,7 @@ var TargetKindCapsSchema = object({
|
|
|
14286
14348
|
* the union is large and not meant for runtime validation here; the exported
|
|
14287
14349
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
14288
14350
|
*/
|
|
14289
|
-
var ConfigSchemaPassthrough$
|
|
14351
|
+
var ConfigSchemaPassthrough$3 = unknown();
|
|
14290
14352
|
var TargetKindSchema = object({
|
|
14291
14353
|
kind: string(),
|
|
14292
14354
|
label: string(),
|
|
@@ -14323,7 +14385,7 @@ var TargetKindSchema = object({
|
|
|
14323
14385
|
* its raster path, which is the safe default for an unknown image.
|
|
14324
14386
|
*/
|
|
14325
14387
|
iconMediaType: string().optional(),
|
|
14326
|
-
configSchema: ConfigSchemaPassthrough$
|
|
14388
|
+
configSchema: ConfigSchemaPassthrough$3,
|
|
14327
14389
|
/**
|
|
14328
14390
|
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
14329
14391
|
* base URL, instance, chat, recipient). A save that changes any of them
|
|
@@ -14345,9 +14407,11 @@ var TargetKindSchema = object({
|
|
|
14345
14407
|
caps: TargetKindCapsSchema
|
|
14346
14408
|
});
|
|
14347
14409
|
/**
|
|
14348
|
-
* A
|
|
14349
|
-
*
|
|
14350
|
-
*
|
|
14410
|
+
* A target as a provider's own code sees it (the notification-center bridge, the
|
|
14411
|
+
* send path): the flat record body split into the name, the switch and the
|
|
14412
|
+
* kind's fields. `config` holds secrets; a reader that serves it to a client
|
|
14413
|
+
* REDACTS secret fields (a presence marker only) — never round-trip a stored
|
|
14414
|
+
* secret to the UI.
|
|
14351
14415
|
*/
|
|
14352
14416
|
var TargetSchema = object({
|
|
14353
14417
|
id: string(),
|
|
@@ -14390,13 +14454,13 @@ var ProbeTargetConfigResultSchema = object({
|
|
|
14390
14454
|
stateInstance: string().optional(),
|
|
14391
14455
|
error: string().optional()
|
|
14392
14456
|
});
|
|
14393
|
-
method(object({}), array(TargetKindSchema)), method(object({
|
|
14457
|
+
method(object({}), array(TargetKindSchema)), method(object({
|
|
14394
14458
|
kind: string(),
|
|
14395
14459
|
config: record(string(), unknown()).optional(),
|
|
14396
14460
|
/**
|
|
14397
14461
|
* When editing / duplicating, the blank password field never round-trips
|
|
14398
14462
|
* the stored token. Pass the source target id so the provider can restore
|
|
14399
|
-
* secrets server-side before dialling
|
|
14463
|
+
* secrets server-side before dialling.
|
|
14400
14464
|
*/
|
|
14401
14465
|
targetId: string().optional()
|
|
14402
14466
|
}), array(DiscoveredTargetSchema), { auth: "admin" }), method(object({
|
|
@@ -14419,27 +14483,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
14419
14483
|
}), TestResultSchema, {
|
|
14420
14484
|
kind: "mutation",
|
|
14421
14485
|
auth: "admin"
|
|
14422
|
-
}), method(object({
|
|
14423
|
-
target: TargetSchema,
|
|
14424
|
-
/**
|
|
14425
|
-
* Duplicate flow: create a NEW target id while copying secrets from an
|
|
14426
|
-
* existing row. The UI never holds the stored token; blank / redacted
|
|
14427
|
-
* password fields are restored from this source (D634 still applies if
|
|
14428
|
-
* destinations diverge without a re-typed secret).
|
|
14429
|
-
*/
|
|
14430
|
-
cloneSecretsFrom: string().optional()
|
|
14431
|
-
}), TargetSchema, {
|
|
14432
|
-
kind: "mutation",
|
|
14433
|
-
auth: "admin"
|
|
14434
|
-
}), method(object({ targetId: string() }), _void(), {
|
|
14435
|
-
kind: "mutation",
|
|
14436
|
-
auth: "admin"
|
|
14437
|
-
}), method(object({
|
|
14438
|
-
targetId: string(),
|
|
14439
|
-
enabled: boolean()
|
|
14440
|
-
}), _void(), {
|
|
14441
|
-
kind: "mutation",
|
|
14442
|
-
auth: "admin"
|
|
14443
14486
|
});
|
|
14444
14487
|
looseObject({
|
|
14445
14488
|
name: string().min(1),
|
|
@@ -22697,7 +22740,6 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
22697
22740
|
"notification-output": system("notification delivery — reached through notifier devices and rules"),
|
|
22698
22741
|
"notification-rules": system("notification rules — authored on their own page"),
|
|
22699
22742
|
"oauth-integration": system(IDENTITY),
|
|
22700
|
-
"oidc-connections": system(IDENTITY),
|
|
22701
22743
|
"osd-manager": system(PIPELINE),
|
|
22702
22744
|
"pipeline-executor": system(PIPELINE),
|
|
22703
22745
|
"pipeline-orchestrator": system(PIPELINE),
|
|
@@ -54385,7 +54427,7 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
54385
54427
|
* or the redaction token KEEPS the stored secret; `{ $secretClear: true }`
|
|
54386
54428
|
* clears it.
|
|
54387
54429
|
*/
|
|
54388
|
-
var ConfigSchemaPassthrough$
|
|
54430
|
+
var ConfigSchemaPassthrough$2 = custom();
|
|
54389
54431
|
var IntegrationInstanceIdSchema = string().min(1);
|
|
54390
54432
|
var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
|
|
54391
54433
|
/**
|
|
@@ -54431,7 +54473,16 @@ var IntegrationInstanceKindSchema = object({
|
|
|
54431
54473
|
/** Relative hub path to start an interactive account link of an instance of this kind. */
|
|
54432
54474
|
connectPath: string().min(1).optional(),
|
|
54433
54475
|
/** Relative hub path to disconnect that account. */
|
|
54434
|
-
disconnectPath: string().min(1).optional()
|
|
54476
|
+
disconnectPath: string().min(1).optional(),
|
|
54477
|
+
/**
|
|
54478
|
+
* Parts of the edit dialog the flat form cannot draw (a node picker, a model source), named by a
|
|
54479
|
+
* GENERIC widget id the client maps to a component; a client that knows none of them shows none.
|
|
54480
|
+
* `connection` widgets write connection values, `model` widgets own the model choice.
|
|
54481
|
+
*/
|
|
54482
|
+
widgets: array(object({
|
|
54483
|
+
id: string().min(1),
|
|
54484
|
+
placement: _enum(["connection", "model"])
|
|
54485
|
+
})).optional()
|
|
54435
54486
|
});
|
|
54436
54487
|
/**
|
|
54437
54488
|
* How devices of an instance are found. `none`: the operator adds them by hand; `lan`: the addon
|
|
@@ -54536,7 +54587,7 @@ var integrationInstancesCapability = {
|
|
|
54536
54587
|
id: IntegrationInstanceIdSchema.nullable(),
|
|
54537
54588
|
/** The kind of the create form (`id: null`) of a provider with several. */
|
|
54538
54589
|
kind: string().min(1).optional()
|
|
54539
|
-
}), ConfigSchemaPassthrough$
|
|
54590
|
+
}), ConfigSchemaPassthrough$2, { auth: "admin" }),
|
|
54540
54591
|
listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
|
|
54541
54592
|
getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
|
|
54542
54593
|
/**
|
|
@@ -55487,12 +55538,12 @@ var LlmModelInfoSchema = object({
|
|
|
55487
55538
|
});
|
|
55488
55539
|
/**
|
|
55489
55540
|
* The provider-specific part of a profile, validated by the provider that owns
|
|
55490
|
-
* the kind (
|
|
55541
|
+
* the kind (saving an instance refuses what it does not accept). A field its
|
|
55491
55542
|
* `connectionSchema` declares `type: 'password'` is a secret: it is held in the
|
|
55492
55543
|
* keychain, never in the row, and reads back as the redaction marker.
|
|
55493
55544
|
*/
|
|
55494
55545
|
var LlmProviderConfigSchema = record(string(), unknown());
|
|
55495
|
-
|
|
55546
|
+
object({
|
|
55496
55547
|
id: string(),
|
|
55497
55548
|
name: string(),
|
|
55498
55549
|
/** The provider-declared kind (`LlmProfileKindDescriptor.kind`). */
|
|
@@ -55586,8 +55637,8 @@ var LlmProviderWidgetSchema = object({
|
|
|
55586
55637
|
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
55587
55638
|
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
55588
55639
|
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
55589
|
-
var ConfigSchemaPassthrough$
|
|
55590
|
-
|
|
55640
|
+
var ConfigSchemaPassthrough$1 = unknown();
|
|
55641
|
+
object({
|
|
55591
55642
|
kind: LlmProfileKindSchema,
|
|
55592
55643
|
label: string(),
|
|
55593
55644
|
/** A generic icon name (`cloud`, `plug`, `cpu`) — never a glyph or a logo. */
|
|
@@ -55595,9 +55646,9 @@ var LlmProfileKindDescriptorSchema = object({
|
|
|
55595
55646
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
55596
55647
|
addonId: string(),
|
|
55597
55648
|
/** The provider's own fields (`providerConfig`); `password` fields are keychain-held. */
|
|
55598
|
-
connectionSchema: ConfigSchemaPassthrough$
|
|
55649
|
+
connectionSchema: ConfigSchemaPassthrough$1,
|
|
55599
55650
|
/** Provider fields that belong under "Advanced" (extra headers, runtime tuning). */
|
|
55600
|
-
advancedSchema: ConfigSchemaPassthrough$
|
|
55651
|
+
advancedSchema: ConfigSchemaPassthrough$1.optional(),
|
|
55601
55652
|
/** The kind's defaults; a model from `listModels` may override any of them. */
|
|
55602
55653
|
features: LlmModelFeaturesSchema,
|
|
55603
55654
|
/** `false` = the provider stamps `supportsVision` itself (from what a node reports). */
|
|
@@ -55726,20 +55777,7 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, {
|
|
|
55726
55777
|
}), method(object({
|
|
55727
55778
|
addonId: string().optional(),
|
|
55728
55779
|
requestId: string()
|
|
55729
|
-
}), _void(), { kind: "mutation" }), method(
|
|
55730
|
-
kind: "mutation",
|
|
55731
|
-
auth: "admin"
|
|
55732
|
-
}), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
|
|
55733
|
-
kind: "mutation",
|
|
55734
|
-
auth: "admin"
|
|
55735
|
-
}), method(ProfileRefInputSchema, _void(), {
|
|
55736
|
-
kind: "mutation",
|
|
55737
|
-
auth: "admin"
|
|
55738
|
-
}), method(ProfileRefInputSchema, LlmGenerateResultSchema, {
|
|
55739
|
-
kind: "mutation",
|
|
55740
|
-
auth: "admin",
|
|
55741
|
-
timeoutMs: AI_CAP_TRANSPORT_TIMEOUT_MS
|
|
55742
|
-
}), method(LlmListModelsInputSchema, LlmListModelsResultSchema, {
|
|
55780
|
+
}), _void(), { kind: "mutation" }), method(LlmListModelsInputSchema, LlmListModelsResultSchema, {
|
|
55743
55781
|
kind: "mutation",
|
|
55744
55782
|
auth: "admin"
|
|
55745
55783
|
}), method(object({}), array(LlmDefaultSchema)), method(object({
|
|
@@ -56734,20 +56772,7 @@ method(_void(), MeshStatusSchema), method(object({
|
|
|
56734
56772
|
/** Optional hostname override the host should advertise once joined. */
|
|
56735
56773
|
hostname: string().optional() }), object({
|
|
56736
56774
|
/** Authentication URL the operator should open in a browser. */
|
|
56737
|
-
loginUrl: string() }), { kind: "mutation" }), method(_void(), object({ left: literal(true) }), { kind: "mutation" }), method(_void(), object({ loggedOut: literal(true) }), { kind: "mutation" }), method(_void(), object({ peers: array(MeshPeerSchema).readonly() }))
|
|
56738
|
-
/** Optional auth key — when provided, probes the key validity
|
|
56739
|
-
* against the provider's API. Omit when already joined to
|
|
56740
|
-
* just ping the daemon. */
|
|
56741
|
-
authKey: string().optional() }), object({
|
|
56742
|
-
ok: boolean(),
|
|
56743
|
-
/** Provider-side identifier resolved by the probe (tailnet
|
|
56744
|
-
* name for Tailscale, network id for ZeroTier, etc.). */
|
|
56745
|
-
tenant: string().optional(),
|
|
56746
|
-
/** Daemon binary version, when reachable. */
|
|
56747
|
-
daemonVersion: string().optional(),
|
|
56748
|
-
/** Human-readable error when `ok: false`. */
|
|
56749
|
-
error: string().optional()
|
|
56750
|
-
}), { kind: "mutation" });
|
|
56775
|
+
loginUrl: string() }), { kind: "mutation" }), method(_void(), object({ left: literal(true) }), { kind: "mutation" }), method(_void(), object({ loggedOut: literal(true) }), { kind: "mutation" }), method(_void(), object({ peers: array(MeshPeerSchema).readonly() }));
|
|
56751
56776
|
object({
|
|
56752
56777
|
value: number().nullable().meta(readingMeta("device", { label: "Total" })),
|
|
56753
56778
|
lastFetchedAt: number()
|
|
@@ -66405,61 +66430,6 @@ var OauthIntegrationDescriptorSchema = object({
|
|
|
66405
66430
|
});
|
|
66406
66431
|
method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
|
|
66407
66432
|
/**
|
|
66408
|
-
* `oidc-connections` — CRUD for IdP rows under `auth-oidc` (D828 sibling).
|
|
66409
|
-
*
|
|
66410
|
-
* One hub-only singleton (`auth-oidc`). The Settings UI is a DataTable +
|
|
66411
|
-
* ItemFormDialog; there is no `editable-array` in `globalSettingsSchema`.
|
|
66412
|
-
* Operator-chosen `id` is the URL slug (`/addon/auth-oidc/<id>/…`) and is
|
|
66413
|
-
* immutable after create.
|
|
66414
|
-
*/
|
|
66415
|
-
var ConfigSchemaPassthrough$1 = unknown();
|
|
66416
|
-
var OidcConnectionSchema = object({
|
|
66417
|
-
id: string().min(1),
|
|
66418
|
-
displayName: string(),
|
|
66419
|
-
icon: string(),
|
|
66420
|
-
issuerUrl: string(),
|
|
66421
|
-
clientId: string(),
|
|
66422
|
-
/** Client id + secret present — never the secret value. */
|
|
66423
|
-
configured: boolean(),
|
|
66424
|
-
/**
|
|
66425
|
-
* Values for the connection form schema. `clientSecret` is a keychain
|
|
66426
|
-
* {@link SecretFieldView} or `null` / `''`, never plaintext (D789).
|
|
66427
|
-
*/
|
|
66428
|
-
formValues: record(string(), unknown()).optional()
|
|
66429
|
-
});
|
|
66430
|
-
/**
|
|
66431
|
-
* Patch one connection. Omitted or empty `clientSecret` keeps the stored
|
|
66432
|
-
* secret (D787). `{ $secretClear: true }` clears it. `id` is never renamed.
|
|
66433
|
-
*/
|
|
66434
|
-
var OidcConnectionUpdateInputSchema = object({
|
|
66435
|
-
id: string().min(1),
|
|
66436
|
-
displayName: string().min(1),
|
|
66437
|
-
icon: string().optional(),
|
|
66438
|
-
issuerUrl: string().optional(),
|
|
66439
|
-
clientId: string().optional(),
|
|
66440
|
-
clientSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
|
|
66441
|
-
redirectUri: string().optional(),
|
|
66442
|
-
scopes: string().optional(),
|
|
66443
|
-
usernameClaim: _enum([
|
|
66444
|
-
"preferred_username",
|
|
66445
|
-
"email",
|
|
66446
|
-
"sub"
|
|
66447
|
-
]).optional()
|
|
66448
|
-
});
|
|
66449
|
-
method(object({}), ConfigSchemaPassthrough$1, { auth: "admin" }), method(object({}), array(OidcConnectionSchema).readonly(), { auth: "admin" }), method(object({
|
|
66450
|
-
id: string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug"),
|
|
66451
|
-
displayName: string().min(1)
|
|
66452
|
-
}), object({ id: string() }), {
|
|
66453
|
-
kind: "mutation",
|
|
66454
|
-
auth: "admin"
|
|
66455
|
-
}), method(OidcConnectionUpdateInputSchema, object({ ok: literal(true) }), {
|
|
66456
|
-
kind: "mutation",
|
|
66457
|
-
auth: "admin"
|
|
66458
|
-
}), method(object({ id: string().min(1) }), object({ ok: literal(true) }), {
|
|
66459
|
-
kind: "mutation",
|
|
66460
|
-
auth: "admin"
|
|
66461
|
-
});
|
|
66462
|
-
/**
|
|
66463
66433
|
* Generic on-screen-display (video overlay) cap. Each camera exposes
|
|
66464
66434
|
* a firmware-defined list of overlays (channel name, timestamp,
|
|
66465
66435
|
* watermark, logo, …); the cap models them as a homogeneous array of
|
|
@@ -72632,21 +72602,6 @@ method(object({
|
|
|
72632
72602
|
*/
|
|
72633
72603
|
ttlSec: union([number().int().positive(), literal("never")]).optional()
|
|
72634
72604
|
}), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
|
|
72635
|
-
var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
|
|
72636
|
-
providerId: string().min(1),
|
|
72637
|
-
displayName: string().min(1),
|
|
72638
|
-
configSchema: unknown(),
|
|
72639
|
-
nodeLocal: boolean(),
|
|
72640
|
-
shouldSaveDiskSpace: literal(true),
|
|
72641
|
-
minFreePercent: number().min(0).max(100)
|
|
72642
|
-
}), object({
|
|
72643
|
-
providerId: string().min(1),
|
|
72644
|
-
displayName: string().min(1),
|
|
72645
|
-
configSchema: unknown(),
|
|
72646
|
-
nodeLocal: boolean(),
|
|
72647
|
-
shouldSaveDiskSpace: literal(false),
|
|
72648
|
-
minFreePercent: literal(null)
|
|
72649
|
-
})]);
|
|
72650
72605
|
method(object({
|
|
72651
72606
|
location: StorageLocationRefSchema,
|
|
72652
72607
|
relativePath: string()
|
|
@@ -72708,42 +72663,7 @@ method(object({
|
|
|
72708
72663
|
}), _instanceof(Uint8Array), { auth: "admin" }), method(object({ downloadId: string() }), _void(), {
|
|
72709
72664
|
kind: "mutation",
|
|
72710
72665
|
auth: "admin"
|
|
72711
|
-
}), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(
|
|
72712
|
-
createdAt: true,
|
|
72713
|
-
updatedAt: true
|
|
72714
|
-
}), StorageLocationSchema, {
|
|
72715
|
-
kind: "mutation",
|
|
72716
|
-
auth: "admin"
|
|
72717
|
-
}), method(object({
|
|
72718
|
-
id: string(),
|
|
72719
|
-
force: boolean().optional()
|
|
72720
|
-
}), _void(), {
|
|
72721
|
-
kind: "mutation",
|
|
72722
|
-
auth: "admin"
|
|
72723
|
-
}), method(_void(), array(StorageDrainProgressSchema).readonly()), method(object({ id: string() }), object({
|
|
72724
|
-
ok: boolean(),
|
|
72725
|
-
error: string().optional()
|
|
72726
|
-
}), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
|
|
72727
|
-
providerId: string(),
|
|
72728
|
-
/**
|
|
72729
|
-
* The location this config is an UNSAVED edit of, when there is one.
|
|
72730
|
-
*
|
|
72731
|
-
* `listLocations` replaces every declared secret with the redaction
|
|
72732
|
-
* sentinel, so the edit modal's form state holds the sentinel for any
|
|
72733
|
-
* credential the operator did not retype — and posting that here
|
|
72734
|
-
* without a way to resolve it makes the provider try to authenticate
|
|
72735
|
-
* as `__camstack_redacted__` and report the operator's own working
|
|
72736
|
-
* password as wrong. Given this id, the orchestrator restores each
|
|
72737
|
-
* sentinel from the stored config (same rule as `upsertLocation`)
|
|
72738
|
-
* before dispatching. Omitted by the "Add location" wizard, where
|
|
72739
|
-
* every value was typed just now and nothing is stored yet.
|
|
72740
|
-
*/
|
|
72741
|
-
locationId: string().optional(),
|
|
72742
|
-
config: record(string(), unknown())
|
|
72743
|
-
}), object({
|
|
72744
|
-
ok: boolean(),
|
|
72745
|
-
error: string().optional()
|
|
72746
|
-
}), { auth: "admin" });
|
|
72666
|
+
}), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(_void(), array(StorageDrainProgressSchema).readonly());
|
|
72747
72667
|
/** One table of the settings database. */
|
|
72748
72668
|
var StorageTableSchema = object({
|
|
72749
72669
|
/** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
|
|
@@ -80336,13 +80256,6 @@ Object.freeze({
|
|
|
80336
80256
|
access: "delete",
|
|
80337
80257
|
role: "admin"
|
|
80338
80258
|
},
|
|
80339
|
-
"llm.deleteProfile": {
|
|
80340
|
-
capName: "llm",
|
|
80341
|
-
capScope: "system",
|
|
80342
|
-
addonId: null,
|
|
80343
|
-
access: "delete",
|
|
80344
|
-
role: "admin"
|
|
80345
|
-
},
|
|
80346
80259
|
"llm.generate": {
|
|
80347
80260
|
capName: "llm",
|
|
80348
80261
|
capScope: "system",
|
|
@@ -80406,20 +80319,6 @@ Object.freeze({
|
|
|
80406
80319
|
access: "view",
|
|
80407
80320
|
role: "admin"
|
|
80408
80321
|
},
|
|
80409
|
-
"llm.listProfileKinds": {
|
|
80410
|
-
capName: "llm",
|
|
80411
|
-
capScope: "system",
|
|
80412
|
-
addonId: null,
|
|
80413
|
-
access: "view",
|
|
80414
|
-
role: "admin"
|
|
80415
|
-
},
|
|
80416
|
-
"llm.listProfiles": {
|
|
80417
|
-
capName: "llm",
|
|
80418
|
-
capScope: "system",
|
|
80419
|
-
addonId: null,
|
|
80420
|
-
access: "view",
|
|
80421
|
-
role: "admin"
|
|
80422
|
-
},
|
|
80423
80322
|
"llm.listRuntimeNodes": {
|
|
80424
80323
|
capName: "llm",
|
|
80425
80324
|
capScope: "system",
|
|
@@ -80427,13 +80326,6 @@ Object.freeze({
|
|
|
80427
80326
|
access: "view",
|
|
80428
80327
|
role: "admin"
|
|
80429
80328
|
},
|
|
80430
|
-
"llm.migrateRawSecrets": {
|
|
80431
|
-
capName: "llm",
|
|
80432
|
-
capScope: "system",
|
|
80433
|
-
addonId: null,
|
|
80434
|
-
access: "create",
|
|
80435
|
-
role: "admin"
|
|
80436
|
-
},
|
|
80437
80329
|
"llm.resolveModelRef": {
|
|
80438
80330
|
capName: "llm",
|
|
80439
80331
|
capScope: "system",
|
|
@@ -80462,20 +80354,6 @@ Object.freeze({
|
|
|
80462
80354
|
access: "create",
|
|
80463
80355
|
role: "admin"
|
|
80464
80356
|
},
|
|
80465
|
-
"llm.testProfile": {
|
|
80466
|
-
capName: "llm",
|
|
80467
|
-
capScope: "system",
|
|
80468
|
-
addonId: null,
|
|
80469
|
-
access: "create",
|
|
80470
|
-
role: "admin"
|
|
80471
|
-
},
|
|
80472
|
-
"llm.upsertProfile": {
|
|
80473
|
-
capName: "llm",
|
|
80474
|
-
capScope: "system",
|
|
80475
|
-
addonId: null,
|
|
80476
|
-
access: "create",
|
|
80477
|
-
role: "admin"
|
|
80478
|
-
},
|
|
80479
80357
|
"llmRuntime.appleAvailability": {
|
|
80480
80358
|
capName: "llm-runtime",
|
|
80481
80359
|
capScope: "system",
|
|
@@ -80980,13 +80858,6 @@ Object.freeze({
|
|
|
80980
80858
|
access: "create",
|
|
80981
80859
|
role: "admin"
|
|
80982
80860
|
},
|
|
80983
|
-
"meshNetwork.testConnection": {
|
|
80984
|
-
capName: "mesh-network",
|
|
80985
|
-
capScope: "system",
|
|
80986
|
-
addonId: null,
|
|
80987
|
-
access: "create",
|
|
80988
|
-
role: "admin"
|
|
80989
|
-
},
|
|
80990
80861
|
"meter.getStatus": {
|
|
80991
80862
|
capName: "meter",
|
|
80992
80863
|
capScope: "device",
|
|
@@ -81645,13 +81516,6 @@ Object.freeze({
|
|
|
81645
81516
|
access: "create",
|
|
81646
81517
|
role: "admin"
|
|
81647
81518
|
},
|
|
81648
|
-
"notificationOutput.deleteTarget": {
|
|
81649
|
-
capName: "notification-output",
|
|
81650
|
-
capScope: "system",
|
|
81651
|
-
addonId: null,
|
|
81652
|
-
access: "delete",
|
|
81653
|
-
role: "admin"
|
|
81654
|
-
},
|
|
81655
81519
|
"notificationOutput.discoverTargets": {
|
|
81656
81520
|
capName: "notification-output",
|
|
81657
81521
|
capScope: "system",
|
|
@@ -81659,13 +81523,6 @@ Object.freeze({
|
|
|
81659
81523
|
access: "view",
|
|
81660
81524
|
role: "admin"
|
|
81661
81525
|
},
|
|
81662
|
-
"notificationOutput.getTarget": {
|
|
81663
|
-
capName: "notification-output",
|
|
81664
|
-
capScope: "system",
|
|
81665
|
-
addonId: null,
|
|
81666
|
-
access: "view",
|
|
81667
|
-
role: "admin"
|
|
81668
|
-
},
|
|
81669
81526
|
"notificationOutput.listTargetKinds": {
|
|
81670
81527
|
capName: "notification-output",
|
|
81671
81528
|
capScope: "system",
|
|
@@ -81673,13 +81530,6 @@ Object.freeze({
|
|
|
81673
81530
|
access: "view",
|
|
81674
81531
|
role: "admin"
|
|
81675
81532
|
},
|
|
81676
|
-
"notificationOutput.listTargets": {
|
|
81677
|
-
capName: "notification-output",
|
|
81678
|
-
capScope: "system",
|
|
81679
|
-
addonId: null,
|
|
81680
|
-
access: "view",
|
|
81681
|
-
role: "admin"
|
|
81682
|
-
},
|
|
81683
81533
|
"notificationOutput.probeTargetConfig": {
|
|
81684
81534
|
capName: "notification-output",
|
|
81685
81535
|
capScope: "system",
|
|
@@ -81694,13 +81544,6 @@ Object.freeze({
|
|
|
81694
81544
|
access: "create",
|
|
81695
81545
|
role: "admin"
|
|
81696
81546
|
},
|
|
81697
|
-
"notificationOutput.setTargetEnabled": {
|
|
81698
|
-
capName: "notification-output",
|
|
81699
|
-
capScope: "system",
|
|
81700
|
-
addonId: null,
|
|
81701
|
-
access: "create",
|
|
81702
|
-
role: "admin"
|
|
81703
|
-
},
|
|
81704
81547
|
"notificationOutput.testTarget": {
|
|
81705
81548
|
capName: "notification-output",
|
|
81706
81549
|
capScope: "system",
|
|
@@ -81708,13 +81551,6 @@ Object.freeze({
|
|
|
81708
81551
|
access: "create",
|
|
81709
81552
|
role: "admin"
|
|
81710
81553
|
},
|
|
81711
|
-
"notificationOutput.upsertTarget": {
|
|
81712
|
-
capName: "notification-output",
|
|
81713
|
-
capScope: "system",
|
|
81714
|
-
addonId: null,
|
|
81715
|
-
access: "create",
|
|
81716
|
-
role: "admin"
|
|
81717
|
-
},
|
|
81718
81554
|
"notificationRules.activateAlarmProfile": {
|
|
81719
81555
|
capName: "notification-rules",
|
|
81720
81556
|
capScope: "system",
|
|
@@ -81974,41 +81810,6 @@ Object.freeze({
|
|
|
81974
81810
|
access: "view",
|
|
81975
81811
|
role: "admin"
|
|
81976
81812
|
},
|
|
81977
|
-
"oidcConnections.connectionFormSchema": {
|
|
81978
|
-
capName: "oidc-connections",
|
|
81979
|
-
capScope: "system",
|
|
81980
|
-
addonId: null,
|
|
81981
|
-
access: "view",
|
|
81982
|
-
role: "admin"
|
|
81983
|
-
},
|
|
81984
|
-
"oidcConnections.createConnection": {
|
|
81985
|
-
capName: "oidc-connections",
|
|
81986
|
-
capScope: "system",
|
|
81987
|
-
addonId: null,
|
|
81988
|
-
access: "create",
|
|
81989
|
-
role: "admin"
|
|
81990
|
-
},
|
|
81991
|
-
"oidcConnections.deleteConnection": {
|
|
81992
|
-
capName: "oidc-connections",
|
|
81993
|
-
capScope: "system",
|
|
81994
|
-
addonId: null,
|
|
81995
|
-
access: "delete",
|
|
81996
|
-
role: "admin"
|
|
81997
|
-
},
|
|
81998
|
-
"oidcConnections.listConnections": {
|
|
81999
|
-
capName: "oidc-connections",
|
|
82000
|
-
capScope: "system",
|
|
82001
|
-
addonId: null,
|
|
82002
|
-
access: "view",
|
|
82003
|
-
role: "admin"
|
|
82004
|
-
},
|
|
82005
|
-
"oidcConnections.updateConnection": {
|
|
82006
|
-
capName: "oidc-connections",
|
|
82007
|
-
capScope: "system",
|
|
82008
|
-
addonId: null,
|
|
82009
|
-
access: "create",
|
|
82010
|
-
role: "admin"
|
|
82011
|
-
},
|
|
82012
81813
|
"osd.getStatus": {
|
|
82013
81814
|
capName: "osd",
|
|
82014
81815
|
capScope: "device",
|
|
@@ -85040,13 +84841,6 @@ Object.freeze({
|
|
|
85040
84841
|
access: "delete",
|
|
85041
84842
|
role: "admin"
|
|
85042
84843
|
},
|
|
85043
|
-
"storage.deleteLocation": {
|
|
85044
|
-
capName: "storage",
|
|
85045
|
-
capScope: "system",
|
|
85046
|
-
addonId: null,
|
|
85047
|
-
access: "delete",
|
|
85048
|
-
role: "admin"
|
|
85049
|
-
},
|
|
85050
84844
|
"storage.endDownload": {
|
|
85051
84845
|
capName: "storage",
|
|
85052
84846
|
capScope: "system",
|
|
@@ -85103,13 +84897,6 @@ Object.freeze({
|
|
|
85103
84897
|
access: "view",
|
|
85104
84898
|
role: "admin"
|
|
85105
84899
|
},
|
|
85106
|
-
"storage.listProviders": {
|
|
85107
|
-
capName: "storage",
|
|
85108
|
-
capScope: "system",
|
|
85109
|
-
addonId: null,
|
|
85110
|
-
access: "view",
|
|
85111
|
-
role: "admin"
|
|
85112
|
-
},
|
|
85113
84900
|
"storage.read": {
|
|
85114
84901
|
capName: "storage",
|
|
85115
84902
|
capScope: "system",
|
|
@@ -85131,27 +84918,6 @@ Object.freeze({
|
|
|
85131
84918
|
access: "view",
|
|
85132
84919
|
role: "admin"
|
|
85133
84920
|
},
|
|
85134
|
-
"storage.testConfig": {
|
|
85135
|
-
capName: "storage",
|
|
85136
|
-
capScope: "system",
|
|
85137
|
-
addonId: null,
|
|
85138
|
-
access: "view",
|
|
85139
|
-
role: "admin"
|
|
85140
|
-
},
|
|
85141
|
-
"storage.testLocation": {
|
|
85142
|
-
capName: "storage",
|
|
85143
|
-
capScope: "system",
|
|
85144
|
-
addonId: null,
|
|
85145
|
-
access: "view",
|
|
85146
|
-
role: "admin"
|
|
85147
|
-
},
|
|
85148
|
-
"storage.upsertLocation": {
|
|
85149
|
-
capName: "storage",
|
|
85150
|
-
capScope: "system",
|
|
85151
|
-
addonId: null,
|
|
85152
|
-
access: "create",
|
|
85153
|
-
role: "admin"
|
|
85154
|
-
},
|
|
85155
84921
|
"storage.write": {
|
|
85156
84922
|
capName: "storage",
|
|
85157
84923
|
capScope: "system",
|
|
@@ -91304,20 +91070,6 @@ object({
|
|
|
91304
91070
|
});
|
|
91305
91071
|
//#endregion
|
|
91306
91072
|
//#region src/custom-actions.ts
|
|
91307
|
-
var SourceIdSchema = string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug");
|
|
91308
|
-
/**
|
|
91309
|
-
* A source as the admin list shows it. No `args`: a credential never leaves the
|
|
91310
|
-
* addon - `formValues` carries the redaction token where one is stored (D880).
|
|
91311
|
-
*/
|
|
91312
|
-
var WasteSourceRowSchema = object({
|
|
91313
|
-
id: string(),
|
|
91314
|
-
label: string(),
|
|
91315
|
-
module: string(),
|
|
91316
|
-
enabled: boolean(),
|
|
91317
|
-
formValues: record(string(), unknown()).optional(),
|
|
91318
|
-
serviceId: string().optional(),
|
|
91319
|
-
countryId: string().optional()
|
|
91320
|
-
});
|
|
91321
91073
|
var wasteCollectionsActions = defineCustomActions({
|
|
91322
91074
|
fetchNow: customAction(object({
|
|
91323
91075
|
/** When set, refresh only this configured source id; otherwise all enabled. */
|
|
@@ -91332,10 +91084,6 @@ sourceId: string().min(1).optional() }).optional(), object({
|
|
|
91332
91084
|
kind: "mutation",
|
|
91333
91085
|
auth: "admin"
|
|
91334
91086
|
}),
|
|
91335
|
-
listSources: customAction(object({}), array(WasteSourceRowSchema).readonly(), {
|
|
91336
|
-
kind: "mutation",
|
|
91337
|
-
auth: "admin"
|
|
91338
|
-
}),
|
|
91339
91087
|
listCountries: customAction(object({}), array(object({
|
|
91340
91088
|
id: string(),
|
|
91341
91089
|
label: string()
|
|
@@ -91353,43 +91101,6 @@ sourceId: string().min(1).optional() }).optional(), object({
|
|
|
91353
91101
|
})).readonly(), {
|
|
91354
91102
|
kind: "mutation",
|
|
91355
91103
|
auth: "admin"
|
|
91356
|
-
}),
|
|
91357
|
-
serviceFormSchema: customAction(object({ serviceId: string().min(1) }), unknown(), {
|
|
91358
|
-
kind: "mutation",
|
|
91359
|
-
auth: "admin"
|
|
91360
|
-
}),
|
|
91361
|
-
createSource: customAction(object({
|
|
91362
|
-
/** Optional override; when omitted the id is derived from `label`. */
|
|
91363
|
-
id: SourceIdSchema.optional(),
|
|
91364
|
-
label: string().min(1),
|
|
91365
|
-
serviceId: string().min(1),
|
|
91366
|
-
args: record(string(), unknown()).default({}),
|
|
91367
|
-
enabled: boolean().optional()
|
|
91368
|
-
}), object({ id: string() }), {
|
|
91369
|
-
kind: "mutation",
|
|
91370
|
-
auth: "admin"
|
|
91371
|
-
}),
|
|
91372
|
-
updateSource: customAction(object({
|
|
91373
|
-
id: string().min(1),
|
|
91374
|
-
label: string().min(1),
|
|
91375
|
-
serviceId: string().min(1).optional(),
|
|
91376
|
-
args: record(string(), unknown()).optional(),
|
|
91377
|
-
enabled: boolean().optional()
|
|
91378
|
-
}), object({ ok: literal(true) }), {
|
|
91379
|
-
kind: "mutation",
|
|
91380
|
-
auth: "admin"
|
|
91381
|
-
}),
|
|
91382
|
-
/**
|
|
91383
|
-
* D880: rewrite every legacy source (free `argsJson`) as typed `args`, moving its
|
|
91384
|
-
* credentials into the keychain. Dry run unless `apply`; idempotent; counts only.
|
|
91385
|
-
*/
|
|
91386
|
-
migrateSources: customAction(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
|
|
91387
|
-
kind: "mutation",
|
|
91388
|
-
auth: "admin"
|
|
91389
|
-
}),
|
|
91390
|
-
deleteSource: customAction(object({ id: string().min(1) }), object({ ok: literal(true) }), {
|
|
91391
|
-
kind: "mutation",
|
|
91392
|
-
auth: "admin"
|
|
91393
91104
|
})
|
|
91394
91105
|
});
|
|
91395
91106
|
//#endregion
|
|
@@ -91423,33 +91134,6 @@ var WASTE_SECRET_STATE_PATHS = SECRET_ARG_KEYS.map((key) => `sources[id].args.${
|
|
|
91423
91134
|
function isSecretArg(key) {
|
|
91424
91135
|
return SECRET_ARG_KEYS.includes(key.toLowerCase());
|
|
91425
91136
|
}
|
|
91426
|
-
/** Is a credential stored for this argument (a value, or a marker the keychain could not serve)? */
|
|
91427
|
-
function isSet(value) {
|
|
91428
|
-
return isSecretMarker(value) || typeof value === "string" && value.length > 0;
|
|
91429
|
-
}
|
|
91430
|
-
/** The arguments as a read surface may show them: a stored credential is the redaction token. */
|
|
91431
|
-
function redactArgs(args) {
|
|
91432
|
-
const out = {};
|
|
91433
|
-
for (const [key, value] of Object.entries(args)) out[key] = isSecretArg(key) && isSet(value) ? REDACTED_SECRET : value;
|
|
91434
|
-
return out;
|
|
91435
|
-
}
|
|
91436
|
-
/**
|
|
91437
|
-
* The write-back of {@link redactArgs}: a credential sent back as the redaction
|
|
91438
|
-
* token means "keep what is stored", so it takes the stored (resolved) value; one
|
|
91439
|
-
* with nothing stored is dropped, never saved as the token.
|
|
91440
|
-
*/
|
|
91441
|
-
function restoreRedactedArgs(incoming, stored) {
|
|
91442
|
-
const out = {};
|
|
91443
|
-
for (const [key, value] of Object.entries(incoming)) {
|
|
91444
|
-
if (isSecretArg(key) && value === "__camstack_redacted__") {
|
|
91445
|
-
const previous = stored[key];
|
|
91446
|
-
if (previous !== void 0) out[key] = previous;
|
|
91447
|
-
continue;
|
|
91448
|
-
}
|
|
91449
|
-
out[key] = value;
|
|
91450
|
-
}
|
|
91451
|
-
return out;
|
|
91452
|
-
}
|
|
91453
91137
|
/**
|
|
91454
91138
|
* Does this argument bag hold a credential that is NOT a usable value: a MARKER the keychain did
|
|
91455
91139
|
* not resolve, or the redaction token (a row read without its credential)? Either would reach a
|
|
@@ -91458,10 +91142,6 @@ function restoreRedactedArgs(incoming, stored) {
|
|
|
91458
91142
|
function hasUnresolvedCredential(args) {
|
|
91459
91143
|
return Object.entries(args).some(([key, value]) => isSecretArg(key) && (isSecretMarker(value) || value === "__camstack_redacted__"));
|
|
91460
91144
|
}
|
|
91461
|
-
/** Does it hold a non-empty credential (any form)? */
|
|
91462
|
-
function hasCredential(args) {
|
|
91463
|
-
return Object.entries(args).some(([key, value]) => isSecretArg(key) && isSet(value));
|
|
91464
|
-
}
|
|
91465
91145
|
//#endregion
|
|
91466
91146
|
//#region src/waste-types.ts
|
|
91467
91147
|
/**
|
|
@@ -91926,35 +91606,6 @@ function reportFailed(log, failures) {
|
|
|
91926
91606
|
};
|
|
91927
91607
|
}
|
|
91928
91608
|
//#endregion
|
|
91929
|
-
//#region src/source-id.ts
|
|
91930
|
-
/**
|
|
91931
|
-
* Stable calendar source ids derived from the operator label.
|
|
91932
|
-
* Pattern: `^[a-z0-9][a-z0-9-]{0,63}$` (max 64 chars).
|
|
91933
|
-
*/
|
|
91934
|
-
var SOURCE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
91935
|
-
/** Slugify a display label into a candidate source id (may be empty). */
|
|
91936
|
-
function slugifySourceLabel(label) {
|
|
91937
|
-
const s = label.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
91938
|
-
if (s.length === 0) return "";
|
|
91939
|
-
return s.slice(0, 64);
|
|
91940
|
-
}
|
|
91941
|
-
/**
|
|
91942
|
-
* Pick an unused id from `label`. Collisions append `-2`, `-3`, …
|
|
91943
|
-
* (truncated so the result stays within the 64-char pattern).
|
|
91944
|
-
*/
|
|
91945
|
-
function allocateSourceId(label, existingIds) {
|
|
91946
|
-
const taken = existingIds instanceof Set ? existingIds : new Set(existingIds);
|
|
91947
|
-
const baseRaw = slugifySourceLabel(label);
|
|
91948
|
-
const base = baseRaw.length > 0 ? baseRaw : "source";
|
|
91949
|
-
if (!taken.has(base) && SOURCE_ID_PATTERN.test(base)) return base;
|
|
91950
|
-
for (let n = 2; n < 1e4; n++) {
|
|
91951
|
-
const suffix = `-${n}`;
|
|
91952
|
-
const candidate = `${base.slice(0, Math.max(1, 64 - suffix.length)).replace(/-+$/g, "")}${suffix}`;
|
|
91953
|
-
if (SOURCE_ID_PATTERN.test(candidate) && !taken.has(candidate)) return candidate;
|
|
91954
|
-
}
|
|
91955
|
-
throw new Error("could not allocate a unique source id");
|
|
91956
|
-
}
|
|
91957
|
-
//#endregion
|
|
91958
91609
|
//#region src/waste-calendar-source.ts
|
|
91959
91610
|
function startOfUtcDayMs(isoDate) {
|
|
91960
91611
|
const [y, m, d] = isoDate.split("-").map((p) => Number(p));
|
|
@@ -177999,6 +177650,35 @@ function mergeServiceArgs(service, formValues) {
|
|
|
177999
177650
|
return out;
|
|
178000
177651
|
}
|
|
178001
177652
|
//#endregion
|
|
177653
|
+
//#region src/source-id.ts
|
|
177654
|
+
/**
|
|
177655
|
+
* Stable calendar source ids derived from the operator label.
|
|
177656
|
+
* Pattern: `^[a-z0-9][a-z0-9-]{0,63}$` (max 64 chars).
|
|
177657
|
+
*/
|
|
177658
|
+
var SOURCE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
177659
|
+
/** Slugify a display label into a candidate source id (may be empty). */
|
|
177660
|
+
function slugifySourceLabel(label) {
|
|
177661
|
+
const s = label.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
177662
|
+
if (s.length === 0) return "";
|
|
177663
|
+
return s.slice(0, 64);
|
|
177664
|
+
}
|
|
177665
|
+
/**
|
|
177666
|
+
* Pick an unused id from `label`. Collisions append `-2`, `-3`, …
|
|
177667
|
+
* (truncated so the result stays within the 64-char pattern).
|
|
177668
|
+
*/
|
|
177669
|
+
function allocateSourceId(label, existingIds) {
|
|
177670
|
+
const taken = existingIds instanceof Set ? existingIds : new Set(existingIds);
|
|
177671
|
+
const baseRaw = slugifySourceLabel(label);
|
|
177672
|
+
const base = baseRaw.length > 0 ? baseRaw : "source";
|
|
177673
|
+
if (!taken.has(base) && SOURCE_ID_PATTERN.test(base)) return base;
|
|
177674
|
+
for (let n = 2; n < 1e4; n++) {
|
|
177675
|
+
const suffix = `-${n}`;
|
|
177676
|
+
const candidate = `${base.slice(0, Math.max(1, 64 - suffix.length)).replace(/-+$/g, "")}${suffix}`;
|
|
177677
|
+
if (SOURCE_ID_PATTERN.test(candidate) && !taken.has(candidate)) return candidate;
|
|
177678
|
+
}
|
|
177679
|
+
throw new Error("could not allocate a unique source id");
|
|
177680
|
+
}
|
|
177681
|
+
//#endregion
|
|
178002
177682
|
//#region src/waste-source-store.ts
|
|
178003
177683
|
/**
|
|
178004
177684
|
* A stored body as a source. A row read while the keychain is locked still carries its markers
|
|
@@ -178502,23 +178182,13 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
178502
178182
|
customActions: wasteCollectionsActions,
|
|
178503
178183
|
actionHandlers: {
|
|
178504
178184
|
fetchNow: async (input) => this.fetchNow(input),
|
|
178505
|
-
listSources: async () => this.listSources(),
|
|
178506
178185
|
listCountries: async () => listCatalogCountries(),
|
|
178507
178186
|
listServices: async (input) => listCatalogServices(input.countryId).map((s) => ({
|
|
178508
178187
|
id: s.id,
|
|
178509
178188
|
label: serviceDisplayLabel(s),
|
|
178510
178189
|
title: s.label,
|
|
178511
178190
|
module: s.module
|
|
178512
|
-
}))
|
|
178513
|
-
serviceFormSchema: async (input) => {
|
|
178514
|
-
const service = findCatalogService(input.serviceId);
|
|
178515
|
-
if (!service) throw new Error(`unknown service '${input.serviceId}'`);
|
|
178516
|
-
return serviceFormSchema(service);
|
|
178517
|
-
},
|
|
178518
|
-
createSource: async (input) => this.createSource(input),
|
|
178519
|
-
updateSource: async (input) => this.updateSource(input),
|
|
178520
|
-
deleteSource: async (input) => this.deleteSource(input),
|
|
178521
|
-
migrateSources: async (input) => this.migrateSources(input.apply)
|
|
178191
|
+
}))
|
|
178522
178192
|
}
|
|
178523
178193
|
};
|
|
178524
178194
|
}
|
|
@@ -178585,17 +178255,6 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
178585
178255
|
if (!this.legacyServed) return records;
|
|
178586
178256
|
return withUnmigratedLegacyRows(records, this.parseConfigSources(this.config.sources));
|
|
178587
178257
|
}
|
|
178588
|
-
async persistSources(sources) {
|
|
178589
|
-
this.activeSources = [...sources];
|
|
178590
|
-
const store = this.sourceStore;
|
|
178591
|
-
if (store !== null) {
|
|
178592
|
-
await store.replaceAll(sources);
|
|
178593
|
-
const kept = new Set(sources.map((s) => s.id));
|
|
178594
|
-
await this.pruneLegacyRows((legacyId) => kept.has(legacyId));
|
|
178595
|
-
return;
|
|
178596
|
-
}
|
|
178597
|
-
await this.updateGlobalSettings({ sources: [...sources] });
|
|
178598
|
-
}
|
|
178599
178258
|
/**
|
|
178600
178259
|
* A source deleted at the records leaves the legacy setting too, or it would reappear beside the
|
|
178601
178260
|
* records (while the migration is pending) or be copied back by it.
|
|
@@ -178610,195 +178269,6 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
178610
178269
|
if (this.activeSources.length > 0 || this.sourceStore !== null) return this.activeSources;
|
|
178611
178270
|
return this.parseConfigSources(this.config.sources);
|
|
178612
178271
|
}
|
|
178613
|
-
listSources() {
|
|
178614
|
-
return this.configuredSources().map((s) => {
|
|
178615
|
-
let args = {};
|
|
178616
|
-
try {
|
|
178617
|
-
args = sourceArgs(s);
|
|
178618
|
-
} catch {
|
|
178619
|
-
args = { ...s.args };
|
|
178620
|
-
}
|
|
178621
|
-
const matched = matchCatalogService(s.module, args);
|
|
178622
|
-
const formValues = {
|
|
178623
|
-
label: s.label,
|
|
178624
|
-
enabled: s.enabled,
|
|
178625
|
-
...redactArgs(args)
|
|
178626
|
-
};
|
|
178627
|
-
if (matched) for (const key of Object.keys(matched.defaultArgs)) delete formValues[key];
|
|
178628
|
-
return {
|
|
178629
|
-
id: s.id,
|
|
178630
|
-
label: s.label,
|
|
178631
|
-
module: s.module,
|
|
178632
|
-
enabled: s.enabled,
|
|
178633
|
-
formValues,
|
|
178634
|
-
serviceId: matched?.id,
|
|
178635
|
-
countryId: matched?.countryId
|
|
178636
|
-
};
|
|
178637
|
-
});
|
|
178638
|
-
}
|
|
178639
|
-
async createSource(input) {
|
|
178640
|
-
const existing = this.configuredSources().map((s) => s.id);
|
|
178641
|
-
let id;
|
|
178642
|
-
if (input.id !== void 0) {
|
|
178643
|
-
if (!SOURCE_ID_PATTERN.test(input.id)) throw new Error("id must be a lowercase slug (e.g. ics-home, junker)");
|
|
178644
|
-
if (existing.includes(input.id)) throw new Error(`source '${input.id}' already exists`);
|
|
178645
|
-
id = input.id;
|
|
178646
|
-
} else id = allocateSourceId(input.label, existing);
|
|
178647
|
-
const service = findCatalogService(input.serviceId);
|
|
178648
|
-
if (!service) throw new Error(`unknown service '${input.serviceId}'`);
|
|
178649
|
-
const row = WasteSourceConfigSchema.parse({
|
|
178650
|
-
id,
|
|
178651
|
-
label: input.label,
|
|
178652
|
-
module: service.module,
|
|
178653
|
-
args: this.typedArgs(mergeServiceArgs(service, restoreRedactedArgs(input.args, {}))),
|
|
178654
|
-
enabled: input.enabled ?? true
|
|
178655
|
-
});
|
|
178656
|
-
await this.persistSources([...this.configuredSources(), row]);
|
|
178657
|
-
return { id };
|
|
178658
|
-
}
|
|
178659
|
-
async updateSource(input) {
|
|
178660
|
-
const sources = this.configuredSources();
|
|
178661
|
-
const idx = sources.findIndex((s) => s.id === input.id);
|
|
178662
|
-
if (idx < 0) throw new Error(`source '${input.id}' not found`);
|
|
178663
|
-
const prev = sources[idx];
|
|
178664
|
-
let prevArgs = {};
|
|
178665
|
-
try {
|
|
178666
|
-
prevArgs = sourceArgs(prev);
|
|
178667
|
-
} catch {
|
|
178668
|
-
prevArgs = { ...prev.args };
|
|
178669
|
-
}
|
|
178670
|
-
let module = prev.module;
|
|
178671
|
-
let args = prevArgs;
|
|
178672
|
-
if (input.serviceId !== void 0) {
|
|
178673
|
-
const service = findCatalogService(input.serviceId);
|
|
178674
|
-
if (!service) throw new Error(`unknown service '${input.serviceId}'`);
|
|
178675
|
-
module = service.module;
|
|
178676
|
-
args = mergeServiceArgs(service, restoreRedactedArgs(input.args ?? {}, prevArgs));
|
|
178677
|
-
} else if (input.args !== void 0) {
|
|
178678
|
-
const sent = restoreRedactedArgs(input.args, prevArgs);
|
|
178679
|
-
const matched = matchCatalogService(prev.module, prevArgs);
|
|
178680
|
-
args = matched ? mergeServiceArgs(matched, sent) : {
|
|
178681
|
-
...prevArgs,
|
|
178682
|
-
...sent
|
|
178683
|
-
};
|
|
178684
|
-
}
|
|
178685
|
-
const next = WasteSourceConfigSchema.parse({
|
|
178686
|
-
id: prev.id,
|
|
178687
|
-
label: input.label,
|
|
178688
|
-
module,
|
|
178689
|
-
args: this.typedArgs(args),
|
|
178690
|
-
enabled: input.enabled ?? prev.enabled
|
|
178691
|
-
});
|
|
178692
|
-
await this.persistSources(sources.map((s, i) => i === idx ? next : s));
|
|
178693
|
-
return { ok: true };
|
|
178694
|
-
}
|
|
178695
|
-
/** Validate an argument bag against the typed value schema (a nested object is refused). */
|
|
178696
|
-
typedArgs(args) {
|
|
178697
|
-
const out = {};
|
|
178698
|
-
for (const [key, value] of Object.entries(args)) out[key] = WasteArgValueSchema.parse(value);
|
|
178699
|
-
return out;
|
|
178700
|
-
}
|
|
178701
|
-
/**
|
|
178702
|
-
* D880: rewrite every legacy source (free `argsJson`) as typed `args`; its
|
|
178703
|
-
* credentials go to the keychain through the normal save path. Then every
|
|
178704
|
-
* credential is read back and compared with what the legacy row held; on any
|
|
178705
|
-
* difference the legacy rows are written back, so nothing is lost. Dry run
|
|
178706
|
-
* unless `apply`. Idempotent. Counts only - never an argument, a ref or an id.
|
|
178707
|
-
*/
|
|
178708
|
-
async migrateSources(apply) {
|
|
178709
|
-
const sources = this.configuredSources();
|
|
178710
|
-
const legacy = sources.filter((s) => s.argsJson !== void 0);
|
|
178711
|
-
const rawRows = legacy.filter((s) => {
|
|
178712
|
-
try {
|
|
178713
|
-
return hasCredential(sourceArgs(s));
|
|
178714
|
-
} catch {
|
|
178715
|
-
return false;
|
|
178716
|
-
}
|
|
178717
|
-
});
|
|
178718
|
-
const markerRows = sources.filter((s) => s.argsJson === void 0 && hasCredential(s.args)).length;
|
|
178719
|
-
const report = (over) => ({
|
|
178720
|
-
apply,
|
|
178721
|
-
rows: sources.length,
|
|
178722
|
-
raw: rawRows.length,
|
|
178723
|
-
markers: markerRows,
|
|
178724
|
-
migrated: 0,
|
|
178725
|
-
failed: 0,
|
|
178726
|
-
remainingRaw: rawRows.length,
|
|
178727
|
-
unresolvedMarkers: this.unresolvedSecrets.length,
|
|
178728
|
-
redundantCopies: legacy.length,
|
|
178729
|
-
verified: rawRows.length === 0 && this.unresolvedSecrets.length === 0,
|
|
178730
|
-
...over
|
|
178731
|
-
});
|
|
178732
|
-
if (!apply || legacy.length === 0) return report({});
|
|
178733
|
-
const expected = /* @__PURE__ */ new Map();
|
|
178734
|
-
const unconverted = /* @__PURE__ */ new Set();
|
|
178735
|
-
const migrated = sources.map((s) => {
|
|
178736
|
-
if (s.argsJson === void 0) return s;
|
|
178737
|
-
try {
|
|
178738
|
-
const args = sourceArgs(s);
|
|
178739
|
-
const row = WasteSourceConfigSchema.parse({
|
|
178740
|
-
id: s.id,
|
|
178741
|
-
label: s.label,
|
|
178742
|
-
module: s.module,
|
|
178743
|
-
args: this.typedArgs(args),
|
|
178744
|
-
enabled: s.enabled
|
|
178745
|
-
});
|
|
178746
|
-
expected.set(s.id, args);
|
|
178747
|
-
return row;
|
|
178748
|
-
} catch {
|
|
178749
|
-
unconverted.add(s.id);
|
|
178750
|
-
return s;
|
|
178751
|
-
}
|
|
178752
|
-
});
|
|
178753
|
-
const failedRows = unconverted.size;
|
|
178754
|
-
if (expected.size === 0) return report({
|
|
178755
|
-
failed: failedRows,
|
|
178756
|
-
remainingRaw: legacy.length
|
|
178757
|
-
});
|
|
178758
|
-
try {
|
|
178759
|
-
await this.persistSources(migrated);
|
|
178760
|
-
} catch {
|
|
178761
|
-
return report({ failed: legacy.length });
|
|
178762
|
-
}
|
|
178763
|
-
const back = new Map(this.configuredSources().map((s) => [s.id, s]));
|
|
178764
|
-
if (![...expected].every(([id, args]) => {
|
|
178765
|
-
const row = back.get(id);
|
|
178766
|
-
return row !== void 0 && row.argsJson === void 0 && JSON.stringify(sortedEntries(row.args)) === JSON.stringify(sortedEntries(args));
|
|
178767
|
-
}) || this.unresolvedSecrets.length > 0) {
|
|
178768
|
-
await this.rollbackMigration(sources, expected);
|
|
178769
|
-
return report({ failed: legacy.length });
|
|
178770
|
-
}
|
|
178771
|
-
return report({
|
|
178772
|
-
migrated: expected.size,
|
|
178773
|
-
failed: failedRows,
|
|
178774
|
-
remainingRaw: failedRows,
|
|
178775
|
-
unresolvedMarkers: 0,
|
|
178776
|
-
redundantCopies: legacy.length,
|
|
178777
|
-
verified: failedRows === 0
|
|
178778
|
-
});
|
|
178779
|
-
}
|
|
178780
|
-
/**
|
|
178781
|
-
* Put the legacy rows back. A converted row's `args` is written as `null`, which
|
|
178782
|
-
* the save path reads as "this object is gone": it deletes the keychain refs the
|
|
178783
|
-
* migration created (a plain `{}` would keep them). Never throws - the report
|
|
178784
|
-
* says failed either way, and the legacy JSON stays authoritative.
|
|
178785
|
-
*/
|
|
178786
|
-
async rollbackMigration(sources, converted) {
|
|
178787
|
-
try {
|
|
178788
|
-
await this.updateGlobalSettingsAsOperator({ sources: sources.map((s) => converted.has(s.id) ? {
|
|
178789
|
-
...s,
|
|
178790
|
-
args: null
|
|
178791
|
-
} : s) });
|
|
178792
|
-
} catch {
|
|
178793
|
-
this.ctx.logger.error("waste-collections: the migration rollback failed - keychain refs may be orphaned (System -> Secrets)");
|
|
178794
|
-
}
|
|
178795
|
-
}
|
|
178796
|
-
async deleteSource(input) {
|
|
178797
|
-
const sources = this.configuredSources();
|
|
178798
|
-
if (!sources.some((s) => s.id === input.id)) throw new Error(`source '${input.id}' not found`);
|
|
178799
|
-
await this.persistSources(sources.filter((s) => s.id !== input.id));
|
|
178800
|
-
return { ok: true };
|
|
178801
|
-
}
|
|
178802
178272
|
async fetchNow(input) {
|
|
178803
178273
|
const targets = this.configuredSources().filter((s) => {
|
|
178804
178274
|
if (!s.enabled) return false;
|
|
@@ -178899,32 +178369,10 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
178899
178369
|
this.ctx.logger.warn("waste-collections: cache persist failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
178900
178370
|
}
|
|
178901
178371
|
}
|
|
178902
|
-
globalSettingsSchema() {
|
|
178903
|
-
return this.schema({ sections: [{
|
|
178904
|
-
id: "refresh",
|
|
178905
|
-
title: "Refresh",
|
|
178906
|
-
description: "Sources are managed in the table above (Add / Edit): pick a country and a service, then fill only that service’s fields. Each source becomes one calendar under System → Calendars.",
|
|
178907
|
-
columns: 2,
|
|
178908
|
-
fields: [{
|
|
178909
|
-
type: "addon-action-button",
|
|
178910
|
-
key: "_fetchNow",
|
|
178911
|
-
label: "Refresh",
|
|
178912
|
-
buttonLabel: "Fetch now",
|
|
178913
|
-
description: "Sources refresh once at boot and then every 24 hours; this refreshes every enabled source immediately.",
|
|
178914
|
-
addonId: "waste-collections",
|
|
178915
|
-
action: "fetchNow",
|
|
178916
|
-
variant: "primary",
|
|
178917
|
-
paramsStatic: {}
|
|
178918
|
-
}]
|
|
178919
|
-
}] });
|
|
178920
|
-
}
|
|
178921
178372
|
};
|
|
178922
178373
|
/** Re-parse helper for tests. */
|
|
178923
178374
|
function parseWasteConfig(raw) {
|
|
178924
178375
|
return WasteConfigSchema.parse(raw);
|
|
178925
178376
|
}
|
|
178926
|
-
function sortedEntries(args) {
|
|
178927
|
-
return Object.entries(args).toSorted(([a], [b]) => a < b ? -1 : 1);
|
|
178928
|
-
}
|
|
178929
178377
|
//#endregion
|
|
178930
178378
|
export { WasteCollectionsAddon, WasteCollectionsAddon as default, sourceArgs as a, wasteCollectionsActions as customActions, WasteConfigSchema as i, createWasteCalendarSource as n, parseWasteConfig, DEFAULT_WASTE_CONFIG as r, collectionsToEvents as t };
|