@camstack/addon-waste-collections 0.1.36 → 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 +8 -0
- package/dist/waste-collections.addon.js +113 -703
- package/dist/waste-collections.addon.mjs +113 -703
- package/package.json +2 -2
|
@@ -6,7 +6,7 @@ let node_fs = require("node:fs");
|
|
|
6
6
|
let node_path = require("node:path");
|
|
7
7
|
let node_child_process = require("node:child_process");
|
|
8
8
|
let node_url = require("node:url");
|
|
9
|
-
//#region ../types/dist/event-category-
|
|
9
|
+
//#region ../types/dist/event-category-78tcBemo.mjs
|
|
10
10
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
11
11
|
EventCategory["SystemBoot"] = "system.boot";
|
|
12
12
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -127,6 +127,23 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
127
127
|
*/
|
|
128
128
|
EventCategory["AddonLoadRecovered"] = "addon.load-recovered";
|
|
129
129
|
/**
|
|
130
|
+
* An addon is RUNNING although its installed package is below what the
|
|
131
|
+
* running server requires (`camstack.requires.addons` of the server, or the
|
|
132
|
+
* addon's own `requires.server`). Raised once per streak by the unsupported-
|
|
133
|
+
* addon tracker, on the hub and on agents; AlertCenter turns it into one
|
|
134
|
+
* operator alert per node and package. The addon is never held back for it.
|
|
135
|
+
*
|
|
136
|
+
* Payload: `{ packageName, addonId, nodeId, installedVersion, requiredMinimum, serverVersion, unmet }`.
|
|
137
|
+
*/
|
|
138
|
+
EventCategory["AddonUnsupportedVersion"] = "addon.unsupported-version";
|
|
139
|
+
/**
|
|
140
|
+
* The package of an {@link AddonUnsupportedVersion} alert reached a supported
|
|
141
|
+
* version, or is no longer installed on the node. AlertCenter dismisses the matching alert.
|
|
142
|
+
*
|
|
143
|
+
* Payload: `{ packageName, addonId, nodeId }`.
|
|
144
|
+
*/
|
|
145
|
+
EventCategory["AddonUnsupportedResolved"] = "addon.unsupported-resolved";
|
|
146
|
+
/**
|
|
130
147
|
* Monitor scheduled the next retry for a failed addon. Transient —
|
|
131
148
|
* surfaced to the UI for live-updating the "next retry in Ns"
|
|
132
149
|
* countdown on the Addons page row, NOT persisted as an alert.
|
|
@@ -237,7 +254,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
237
254
|
EventCategory["ProcessRestarted"] = "process.restarted";
|
|
238
255
|
/**
|
|
239
256
|
* The SET of storage locations changed — one was created, edited, enabled,
|
|
240
|
-
* disabled or deleted through
|
|
257
|
+
* disabled or deleted through the location instances or the forced-delete action.
|
|
241
258
|
*
|
|
242
259
|
* Telemetry, not a transaction (D8/D11): every consumer that re-resolves on
|
|
243
260
|
* it must also converge on its own periodic path, because a dropped event
|
|
@@ -5988,7 +6005,7 @@ var ZodIssueCode = {
|
|
|
5988
6005
|
var ZodFirstPartyTypeKind;
|
|
5989
6006
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5990
6007
|
//#endregion
|
|
5991
|
-
//#region ../types/dist/sleep-
|
|
6008
|
+
//#region ../types/dist/sleep-AEnTN3b4.mjs
|
|
5992
6009
|
/**
|
|
5993
6010
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5994
6011
|
* window to float samples (D455).
|
|
@@ -6083,16 +6100,6 @@ var AddonMigrationsBlockedError = class extends Error {
|
|
|
6083
6100
|
this.migrationIds = migrationIds;
|
|
6084
6101
|
}
|
|
6085
6102
|
};
|
|
6086
|
-
/**
|
|
6087
|
-
* The value a redacted secret is replaced with on every read surface.
|
|
6088
|
-
*
|
|
6089
|
-
* It is also the WRITE-BACK token: an `upsertLocation` that sends this value
|
|
6090
|
-
* back for a key means "keep what is stored", which is what lets an operator
|
|
6091
|
-
* rename a location without retyping its password. A literal an operator could
|
|
6092
|
-
* plausibly choose as a real password would turn that convenience into a way
|
|
6093
|
-
* to lock yourself out, hence the sentinel shape.
|
|
6094
|
-
*/
|
|
6095
|
-
var REDACTED_SECRET = "__camstack_redacted__";
|
|
6096
6103
|
/** Is this field's stored value a credential? */
|
|
6097
6104
|
function isSecretConfigField(field) {
|
|
6098
6105
|
if (field.type === "password") return true;
|
|
@@ -7403,17 +7410,35 @@ var RecordIntegrationInstances = class {
|
|
|
7403
7410
|
...kind !== void 0 ? { kind } : {}
|
|
7404
7411
|
});
|
|
7405
7412
|
if (minted === void 0) throw new Error("createInstance needs an id: this provider does not mint one");
|
|
7413
|
+
const initial = this.hooks.initialBody(name, kind);
|
|
7414
|
+
const patch = this.patchOf(values ?? {});
|
|
7415
|
+
if (Object.keys(patch).length > 0) Object.assign(patch, await this.hooks.validateWrite?.({
|
|
7416
|
+
kind,
|
|
7417
|
+
body: {
|
|
7418
|
+
...initial,
|
|
7419
|
+
...patch
|
|
7420
|
+
}
|
|
7421
|
+
}));
|
|
7406
7422
|
await this.handle.put({
|
|
7407
7423
|
id: minted,
|
|
7408
|
-
data:
|
|
7424
|
+
data: initial
|
|
7409
7425
|
}, { ifRev: 0 });
|
|
7410
|
-
const patch = this.patchOf(values ?? {});
|
|
7411
7426
|
if (Object.keys(patch).length > 0) await this.handle.patch(minted, patch);
|
|
7412
7427
|
await this.hooks.onChanged?.(minted, "created");
|
|
7413
7428
|
return { id: minted };
|
|
7414
7429
|
};
|
|
7415
7430
|
updateInstance = async ({ id, name, values }) => {
|
|
7416
7431
|
const patch = this.patchOf(values);
|
|
7432
|
+
if (this.hooks.validateWrite !== void 0 && Object.keys(patch).length > 0) {
|
|
7433
|
+
const stored = await this.handle.get(id);
|
|
7434
|
+
if (stored !== null) Object.assign(patch, await this.hooks.validateWrite({
|
|
7435
|
+
kind: void 0,
|
|
7436
|
+
body: {
|
|
7437
|
+
...stored.data,
|
|
7438
|
+
...patch
|
|
7439
|
+
}
|
|
7440
|
+
}));
|
|
7441
|
+
}
|
|
7417
7442
|
if (name !== void 0) patch[this.nameField] = name;
|
|
7418
7443
|
await this.handle.patch(id, patch);
|
|
7419
7444
|
await this.hooks.onChanged?.(id, "updated");
|
|
@@ -10777,7 +10802,6 @@ var CAP_NAMES = Object.freeze([
|
|
|
10777
10802
|
"notifier",
|
|
10778
10803
|
"numeric-sensor",
|
|
10779
10804
|
"oauth-integration",
|
|
10780
|
-
"oidc-connections",
|
|
10781
10805
|
"osd",
|
|
10782
10806
|
"osd-manager",
|
|
10783
10807
|
"pet-feeder",
|
|
@@ -12669,8 +12693,7 @@ Object.freeze({
|
|
|
12669
12693
|
"device-provider": { "reloadDevice": 18e4 },
|
|
12670
12694
|
"llm": {
|
|
12671
12695
|
"generate": 6e5,
|
|
12672
|
-
"generateVision": 6e5
|
|
12673
|
-
"testProfile": 6e5
|
|
12696
|
+
"generateVision": 6e5
|
|
12674
12697
|
},
|
|
12675
12698
|
"llm-runtime": {
|
|
12676
12699
|
"complete": 6e5,
|
|
@@ -14073,17 +14096,18 @@ var IntegrationInstanceRefSchema = object({
|
|
|
14073
14096
|
* message to what the kind supports — callers never special-case a service.
|
|
14074
14097
|
*
|
|
14075
14098
|
* DESIGN DECISIONS (locked):
|
|
14076
|
-
* -
|
|
14077
|
-
*
|
|
14078
|
-
*
|
|
14079
|
-
*
|
|
14080
|
-
*
|
|
14081
|
-
*
|
|
14082
|
-
*
|
|
14083
|
-
*
|
|
14084
|
-
*
|
|
14085
|
-
* routable. `send` / `testTarget` /
|
|
14086
|
-
* `
|
|
14099
|
+
* - A target is an INSTANCE of this function cap (epic #440, step 6): the
|
|
14100
|
+
* providers serve their targets as `integration-instances` records, one
|
|
14101
|
+
* record kind per target kind, and the admin lists, adds, edits, switches
|
|
14102
|
+
* and deletes them through that cap. This cap keeps what an instance cannot
|
|
14103
|
+
* say: the kind catalogue (`listTargetKinds`: capabilities, delivery
|
|
14104
|
+
* parameters, brand icon), dispatch (`send`, `testTarget`) and the form
|
|
14105
|
+
* helpers (`discoverTargets`, `probeTargetConfig`).
|
|
14106
|
+
* - `listTargetKinds` / `discoverTargets` return arrays → the generated
|
|
14107
|
+
* cap-mount auto-`concatCollection`-fans them across every registered
|
|
14108
|
+
* provider so one catalog is routable. `send` / `testTarget` /
|
|
14109
|
+
* `probeTargetConfig` route to ONE provider by the `addonId` the generated
|
|
14110
|
+
* collection router extracts from the call input.
|
|
14087
14111
|
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
14088
14112
|
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
14089
14113
|
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
@@ -14328,7 +14352,7 @@ var TargetKindCapsSchema = object({
|
|
|
14328
14352
|
* the union is large and not meant for runtime validation here; the exported
|
|
14329
14353
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
14330
14354
|
*/
|
|
14331
|
-
var ConfigSchemaPassthrough$
|
|
14355
|
+
var ConfigSchemaPassthrough$3 = unknown();
|
|
14332
14356
|
var TargetKindSchema = object({
|
|
14333
14357
|
kind: string(),
|
|
14334
14358
|
label: string(),
|
|
@@ -14365,7 +14389,7 @@ var TargetKindSchema = object({
|
|
|
14365
14389
|
* its raster path, which is the safe default for an unknown image.
|
|
14366
14390
|
*/
|
|
14367
14391
|
iconMediaType: string().optional(),
|
|
14368
|
-
configSchema: ConfigSchemaPassthrough$
|
|
14392
|
+
configSchema: ConfigSchemaPassthrough$3,
|
|
14369
14393
|
/**
|
|
14370
14394
|
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
14371
14395
|
* base URL, instance, chat, recipient). A save that changes any of them
|
|
@@ -14387,9 +14411,11 @@ var TargetKindSchema = object({
|
|
|
14387
14411
|
caps: TargetKindCapsSchema
|
|
14388
14412
|
});
|
|
14389
14413
|
/**
|
|
14390
|
-
* A
|
|
14391
|
-
*
|
|
14392
|
-
*
|
|
14414
|
+
* A target as a provider's own code sees it (the notification-center bridge, the
|
|
14415
|
+
* send path): the flat record body split into the name, the switch and the
|
|
14416
|
+
* kind's fields. `config` holds secrets; a reader that serves it to a client
|
|
14417
|
+
* REDACTS secret fields (a presence marker only) — never round-trip a stored
|
|
14418
|
+
* secret to the UI.
|
|
14393
14419
|
*/
|
|
14394
14420
|
var TargetSchema = object({
|
|
14395
14421
|
id: string(),
|
|
@@ -14432,13 +14458,13 @@ var ProbeTargetConfigResultSchema = object({
|
|
|
14432
14458
|
stateInstance: string().optional(),
|
|
14433
14459
|
error: string().optional()
|
|
14434
14460
|
});
|
|
14435
|
-
method(object({}), array(TargetKindSchema)), method(object({
|
|
14461
|
+
method(object({}), array(TargetKindSchema)), method(object({
|
|
14436
14462
|
kind: string(),
|
|
14437
14463
|
config: record(string(), unknown()).optional(),
|
|
14438
14464
|
/**
|
|
14439
14465
|
* When editing / duplicating, the blank password field never round-trips
|
|
14440
14466
|
* the stored token. Pass the source target id so the provider can restore
|
|
14441
|
-
* secrets server-side before dialling
|
|
14467
|
+
* secrets server-side before dialling.
|
|
14442
14468
|
*/
|
|
14443
14469
|
targetId: string().optional()
|
|
14444
14470
|
}), array(DiscoveredTargetSchema), { auth: "admin" }), method(object({
|
|
@@ -14461,27 +14487,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
14461
14487
|
}), TestResultSchema, {
|
|
14462
14488
|
kind: "mutation",
|
|
14463
14489
|
auth: "admin"
|
|
14464
|
-
}), method(object({
|
|
14465
|
-
target: TargetSchema,
|
|
14466
|
-
/**
|
|
14467
|
-
* Duplicate flow: create a NEW target id while copying secrets from an
|
|
14468
|
-
* existing row. The UI never holds the stored token; blank / redacted
|
|
14469
|
-
* password fields are restored from this source (D634 still applies if
|
|
14470
|
-
* destinations diverge without a re-typed secret).
|
|
14471
|
-
*/
|
|
14472
|
-
cloneSecretsFrom: string().optional()
|
|
14473
|
-
}), TargetSchema, {
|
|
14474
|
-
kind: "mutation",
|
|
14475
|
-
auth: "admin"
|
|
14476
|
-
}), method(object({ targetId: string() }), _void(), {
|
|
14477
|
-
kind: "mutation",
|
|
14478
|
-
auth: "admin"
|
|
14479
|
-
}), method(object({
|
|
14480
|
-
targetId: string(),
|
|
14481
|
-
enabled: boolean()
|
|
14482
|
-
}), _void(), {
|
|
14483
|
-
kind: "mutation",
|
|
14484
|
-
auth: "admin"
|
|
14485
14490
|
});
|
|
14486
14491
|
looseObject({
|
|
14487
14492
|
name: string().min(1),
|
|
@@ -22739,7 +22744,6 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
22739
22744
|
"notification-output": system("notification delivery — reached through notifier devices and rules"),
|
|
22740
22745
|
"notification-rules": system("notification rules — authored on their own page"),
|
|
22741
22746
|
"oauth-integration": system(IDENTITY),
|
|
22742
|
-
"oidc-connections": system(IDENTITY),
|
|
22743
22747
|
"osd-manager": system(PIPELINE),
|
|
22744
22748
|
"pipeline-executor": system(PIPELINE),
|
|
22745
22749
|
"pipeline-orchestrator": system(PIPELINE),
|
|
@@ -54427,7 +54431,7 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
54427
54431
|
* or the redaction token KEEPS the stored secret; `{ $secretClear: true }`
|
|
54428
54432
|
* clears it.
|
|
54429
54433
|
*/
|
|
54430
|
-
var ConfigSchemaPassthrough$
|
|
54434
|
+
var ConfigSchemaPassthrough$2 = custom();
|
|
54431
54435
|
var IntegrationInstanceIdSchema = string().min(1);
|
|
54432
54436
|
var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
|
|
54433
54437
|
/**
|
|
@@ -54473,7 +54477,16 @@ var IntegrationInstanceKindSchema = object({
|
|
|
54473
54477
|
/** Relative hub path to start an interactive account link of an instance of this kind. */
|
|
54474
54478
|
connectPath: string().min(1).optional(),
|
|
54475
54479
|
/** Relative hub path to disconnect that account. */
|
|
54476
|
-
disconnectPath: string().min(1).optional()
|
|
54480
|
+
disconnectPath: string().min(1).optional(),
|
|
54481
|
+
/**
|
|
54482
|
+
* Parts of the edit dialog the flat form cannot draw (a node picker, a model source), named by a
|
|
54483
|
+
* GENERIC widget id the client maps to a component; a client that knows none of them shows none.
|
|
54484
|
+
* `connection` widgets write connection values, `model` widgets own the model choice.
|
|
54485
|
+
*/
|
|
54486
|
+
widgets: array(object({
|
|
54487
|
+
id: string().min(1),
|
|
54488
|
+
placement: _enum(["connection", "model"])
|
|
54489
|
+
})).optional()
|
|
54477
54490
|
});
|
|
54478
54491
|
/**
|
|
54479
54492
|
* How devices of an instance are found. `none`: the operator adds them by hand; `lan`: the addon
|
|
@@ -54578,7 +54591,7 @@ var integrationInstancesCapability = {
|
|
|
54578
54591
|
id: IntegrationInstanceIdSchema.nullable(),
|
|
54579
54592
|
/** The kind of the create form (`id: null`) of a provider with several. */
|
|
54580
54593
|
kind: string().min(1).optional()
|
|
54581
|
-
}), ConfigSchemaPassthrough$
|
|
54594
|
+
}), ConfigSchemaPassthrough$2, { auth: "admin" }),
|
|
54582
54595
|
listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
|
|
54583
54596
|
getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
|
|
54584
54597
|
/**
|
|
@@ -55529,12 +55542,12 @@ var LlmModelInfoSchema = object({
|
|
|
55529
55542
|
});
|
|
55530
55543
|
/**
|
|
55531
55544
|
* The provider-specific part of a profile, validated by the provider that owns
|
|
55532
|
-
* the kind (
|
|
55545
|
+
* the kind (saving an instance refuses what it does not accept). A field its
|
|
55533
55546
|
* `connectionSchema` declares `type: 'password'` is a secret: it is held in the
|
|
55534
55547
|
* keychain, never in the row, and reads back as the redaction marker.
|
|
55535
55548
|
*/
|
|
55536
55549
|
var LlmProviderConfigSchema = record(string(), unknown());
|
|
55537
|
-
|
|
55550
|
+
object({
|
|
55538
55551
|
id: string(),
|
|
55539
55552
|
name: string(),
|
|
55540
55553
|
/** The provider-declared kind (`LlmProfileKindDescriptor.kind`). */
|
|
@@ -55628,8 +55641,8 @@ var LlmProviderWidgetSchema = object({
|
|
|
55628
55641
|
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
55629
55642
|
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
55630
55643
|
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
55631
|
-
var ConfigSchemaPassthrough$
|
|
55632
|
-
|
|
55644
|
+
var ConfigSchemaPassthrough$1 = unknown();
|
|
55645
|
+
object({
|
|
55633
55646
|
kind: LlmProfileKindSchema,
|
|
55634
55647
|
label: string(),
|
|
55635
55648
|
/** A generic icon name (`cloud`, `plug`, `cpu`) — never a glyph or a logo. */
|
|
@@ -55637,9 +55650,9 @@ var LlmProfileKindDescriptorSchema = object({
|
|
|
55637
55650
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
55638
55651
|
addonId: string(),
|
|
55639
55652
|
/** The provider's own fields (`providerConfig`); `password` fields are keychain-held. */
|
|
55640
|
-
connectionSchema: ConfigSchemaPassthrough$
|
|
55653
|
+
connectionSchema: ConfigSchemaPassthrough$1,
|
|
55641
55654
|
/** Provider fields that belong under "Advanced" (extra headers, runtime tuning). */
|
|
55642
|
-
advancedSchema: ConfigSchemaPassthrough$
|
|
55655
|
+
advancedSchema: ConfigSchemaPassthrough$1.optional(),
|
|
55643
55656
|
/** The kind's defaults; a model from `listModels` may override any of them. */
|
|
55644
55657
|
features: LlmModelFeaturesSchema,
|
|
55645
55658
|
/** `false` = the provider stamps `supportsVision` itself (from what a node reports). */
|
|
@@ -55768,20 +55781,7 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, {
|
|
|
55768
55781
|
}), method(object({
|
|
55769
55782
|
addonId: string().optional(),
|
|
55770
55783
|
requestId: string()
|
|
55771
|
-
}), _void(), { kind: "mutation" }), method(
|
|
55772
|
-
kind: "mutation",
|
|
55773
|
-
auth: "admin"
|
|
55774
|
-
}), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
|
|
55775
|
-
kind: "mutation",
|
|
55776
|
-
auth: "admin"
|
|
55777
|
-
}), method(ProfileRefInputSchema, _void(), {
|
|
55778
|
-
kind: "mutation",
|
|
55779
|
-
auth: "admin"
|
|
55780
|
-
}), method(ProfileRefInputSchema, LlmGenerateResultSchema, {
|
|
55781
|
-
kind: "mutation",
|
|
55782
|
-
auth: "admin",
|
|
55783
|
-
timeoutMs: AI_CAP_TRANSPORT_TIMEOUT_MS
|
|
55784
|
-
}), method(LlmListModelsInputSchema, LlmListModelsResultSchema, {
|
|
55784
|
+
}), _void(), { kind: "mutation" }), method(LlmListModelsInputSchema, LlmListModelsResultSchema, {
|
|
55785
55785
|
kind: "mutation",
|
|
55786
55786
|
auth: "admin"
|
|
55787
55787
|
}), method(object({}), array(LlmDefaultSchema)), method(object({
|
|
@@ -56776,20 +56776,7 @@ method(_void(), MeshStatusSchema), method(object({
|
|
|
56776
56776
|
/** Optional hostname override the host should advertise once joined. */
|
|
56777
56777
|
hostname: string().optional() }), object({
|
|
56778
56778
|
/** Authentication URL the operator should open in a browser. */
|
|
56779
|
-
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() }))
|
|
56780
|
-
/** Optional auth key — when provided, probes the key validity
|
|
56781
|
-
* against the provider's API. Omit when already joined to
|
|
56782
|
-
* just ping the daemon. */
|
|
56783
|
-
authKey: string().optional() }), object({
|
|
56784
|
-
ok: boolean(),
|
|
56785
|
-
/** Provider-side identifier resolved by the probe (tailnet
|
|
56786
|
-
* name for Tailscale, network id for ZeroTier, etc.). */
|
|
56787
|
-
tenant: string().optional(),
|
|
56788
|
-
/** Daemon binary version, when reachable. */
|
|
56789
|
-
daemonVersion: string().optional(),
|
|
56790
|
-
/** Human-readable error when `ok: false`. */
|
|
56791
|
-
error: string().optional()
|
|
56792
|
-
}), { kind: "mutation" });
|
|
56779
|
+
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() }));
|
|
56793
56780
|
object({
|
|
56794
56781
|
value: number().nullable().meta(readingMeta("device", { label: "Total" })),
|
|
56795
56782
|
lastFetchedAt: number()
|
|
@@ -66447,61 +66434,6 @@ var OauthIntegrationDescriptorSchema = object({
|
|
|
66447
66434
|
});
|
|
66448
66435
|
method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
|
|
66449
66436
|
/**
|
|
66450
|
-
* `oidc-connections` — CRUD for IdP rows under `auth-oidc` (D828 sibling).
|
|
66451
|
-
*
|
|
66452
|
-
* One hub-only singleton (`auth-oidc`). The Settings UI is a DataTable +
|
|
66453
|
-
* ItemFormDialog; there is no `editable-array` in `globalSettingsSchema`.
|
|
66454
|
-
* Operator-chosen `id` is the URL slug (`/addon/auth-oidc/<id>/…`) and is
|
|
66455
|
-
* immutable after create.
|
|
66456
|
-
*/
|
|
66457
|
-
var ConfigSchemaPassthrough$1 = unknown();
|
|
66458
|
-
var OidcConnectionSchema = object({
|
|
66459
|
-
id: string().min(1),
|
|
66460
|
-
displayName: string(),
|
|
66461
|
-
icon: string(),
|
|
66462
|
-
issuerUrl: string(),
|
|
66463
|
-
clientId: string(),
|
|
66464
|
-
/** Client id + secret present — never the secret value. */
|
|
66465
|
-
configured: boolean(),
|
|
66466
|
-
/**
|
|
66467
|
-
* Values for the connection form schema. `clientSecret` is a keychain
|
|
66468
|
-
* {@link SecretFieldView} or `null` / `''`, never plaintext (D789).
|
|
66469
|
-
*/
|
|
66470
|
-
formValues: record(string(), unknown()).optional()
|
|
66471
|
-
});
|
|
66472
|
-
/**
|
|
66473
|
-
* Patch one connection. Omitted or empty `clientSecret` keeps the stored
|
|
66474
|
-
* secret (D787). `{ $secretClear: true }` clears it. `id` is never renamed.
|
|
66475
|
-
*/
|
|
66476
|
-
var OidcConnectionUpdateInputSchema = object({
|
|
66477
|
-
id: string().min(1),
|
|
66478
|
-
displayName: string().min(1),
|
|
66479
|
-
icon: string().optional(),
|
|
66480
|
-
issuerUrl: string().optional(),
|
|
66481
|
-
clientId: string().optional(),
|
|
66482
|
-
clientSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
|
|
66483
|
-
redirectUri: string().optional(),
|
|
66484
|
-
scopes: string().optional(),
|
|
66485
|
-
usernameClaim: _enum([
|
|
66486
|
-
"preferred_username",
|
|
66487
|
-
"email",
|
|
66488
|
-
"sub"
|
|
66489
|
-
]).optional()
|
|
66490
|
-
});
|
|
66491
|
-
method(object({}), ConfigSchemaPassthrough$1, { auth: "admin" }), method(object({}), array(OidcConnectionSchema).readonly(), { auth: "admin" }), method(object({
|
|
66492
|
-
id: string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug"),
|
|
66493
|
-
displayName: string().min(1)
|
|
66494
|
-
}), object({ id: string() }), {
|
|
66495
|
-
kind: "mutation",
|
|
66496
|
-
auth: "admin"
|
|
66497
|
-
}), method(OidcConnectionUpdateInputSchema, object({ ok: literal(true) }), {
|
|
66498
|
-
kind: "mutation",
|
|
66499
|
-
auth: "admin"
|
|
66500
|
-
}), method(object({ id: string().min(1) }), object({ ok: literal(true) }), {
|
|
66501
|
-
kind: "mutation",
|
|
66502
|
-
auth: "admin"
|
|
66503
|
-
});
|
|
66504
|
-
/**
|
|
66505
66437
|
* Generic on-screen-display (video overlay) cap. Each camera exposes
|
|
66506
66438
|
* a firmware-defined list of overlays (channel name, timestamp,
|
|
66507
66439
|
* watermark, logo, …); the cap models them as a homogeneous array of
|
|
@@ -72674,21 +72606,6 @@ method(object({
|
|
|
72674
72606
|
*/
|
|
72675
72607
|
ttlSec: union([number().int().positive(), literal("never")]).optional()
|
|
72676
72608
|
}), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
|
|
72677
|
-
var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
|
|
72678
|
-
providerId: string().min(1),
|
|
72679
|
-
displayName: string().min(1),
|
|
72680
|
-
configSchema: unknown(),
|
|
72681
|
-
nodeLocal: boolean(),
|
|
72682
|
-
shouldSaveDiskSpace: literal(true),
|
|
72683
|
-
minFreePercent: number().min(0).max(100)
|
|
72684
|
-
}), object({
|
|
72685
|
-
providerId: string().min(1),
|
|
72686
|
-
displayName: string().min(1),
|
|
72687
|
-
configSchema: unknown(),
|
|
72688
|
-
nodeLocal: boolean(),
|
|
72689
|
-
shouldSaveDiskSpace: literal(false),
|
|
72690
|
-
minFreePercent: literal(null)
|
|
72691
|
-
})]);
|
|
72692
72609
|
method(object({
|
|
72693
72610
|
location: StorageLocationRefSchema,
|
|
72694
72611
|
relativePath: string()
|
|
@@ -72750,42 +72667,7 @@ method(object({
|
|
|
72750
72667
|
}), _instanceof(Uint8Array), { auth: "admin" }), method(object({ downloadId: string() }), _void(), {
|
|
72751
72668
|
kind: "mutation",
|
|
72752
72669
|
auth: "admin"
|
|
72753
|
-
}), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(
|
|
72754
|
-
createdAt: true,
|
|
72755
|
-
updatedAt: true
|
|
72756
|
-
}), StorageLocationSchema, {
|
|
72757
|
-
kind: "mutation",
|
|
72758
|
-
auth: "admin"
|
|
72759
|
-
}), method(object({
|
|
72760
|
-
id: string(),
|
|
72761
|
-
force: boolean().optional()
|
|
72762
|
-
}), _void(), {
|
|
72763
|
-
kind: "mutation",
|
|
72764
|
-
auth: "admin"
|
|
72765
|
-
}), method(_void(), array(StorageDrainProgressSchema).readonly()), method(object({ id: string() }), object({
|
|
72766
|
-
ok: boolean(),
|
|
72767
|
-
error: string().optional()
|
|
72768
|
-
}), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
|
|
72769
|
-
providerId: string(),
|
|
72770
|
-
/**
|
|
72771
|
-
* The location this config is an UNSAVED edit of, when there is one.
|
|
72772
|
-
*
|
|
72773
|
-
* `listLocations` replaces every declared secret with the redaction
|
|
72774
|
-
* sentinel, so the edit modal's form state holds the sentinel for any
|
|
72775
|
-
* credential the operator did not retype — and posting that here
|
|
72776
|
-
* without a way to resolve it makes the provider try to authenticate
|
|
72777
|
-
* as `__camstack_redacted__` and report the operator's own working
|
|
72778
|
-
* password as wrong. Given this id, the orchestrator restores each
|
|
72779
|
-
* sentinel from the stored config (same rule as `upsertLocation`)
|
|
72780
|
-
* before dispatching. Omitted by the "Add location" wizard, where
|
|
72781
|
-
* every value was typed just now and nothing is stored yet.
|
|
72782
|
-
*/
|
|
72783
|
-
locationId: string().optional(),
|
|
72784
|
-
config: record(string(), unknown())
|
|
72785
|
-
}), object({
|
|
72786
|
-
ok: boolean(),
|
|
72787
|
-
error: string().optional()
|
|
72788
|
-
}), { auth: "admin" });
|
|
72670
|
+
}), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(_void(), array(StorageDrainProgressSchema).readonly());
|
|
72789
72671
|
/** One table of the settings database. */
|
|
72790
72672
|
var StorageTableSchema = object({
|
|
72791
72673
|
/** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
|
|
@@ -80378,13 +80260,6 @@ Object.freeze({
|
|
|
80378
80260
|
access: "delete",
|
|
80379
80261
|
role: "admin"
|
|
80380
80262
|
},
|
|
80381
|
-
"llm.deleteProfile": {
|
|
80382
|
-
capName: "llm",
|
|
80383
|
-
capScope: "system",
|
|
80384
|
-
addonId: null,
|
|
80385
|
-
access: "delete",
|
|
80386
|
-
role: "admin"
|
|
80387
|
-
},
|
|
80388
80263
|
"llm.generate": {
|
|
80389
80264
|
capName: "llm",
|
|
80390
80265
|
capScope: "system",
|
|
@@ -80448,20 +80323,6 @@ Object.freeze({
|
|
|
80448
80323
|
access: "view",
|
|
80449
80324
|
role: "admin"
|
|
80450
80325
|
},
|
|
80451
|
-
"llm.listProfileKinds": {
|
|
80452
|
-
capName: "llm",
|
|
80453
|
-
capScope: "system",
|
|
80454
|
-
addonId: null,
|
|
80455
|
-
access: "view",
|
|
80456
|
-
role: "admin"
|
|
80457
|
-
},
|
|
80458
|
-
"llm.listProfiles": {
|
|
80459
|
-
capName: "llm",
|
|
80460
|
-
capScope: "system",
|
|
80461
|
-
addonId: null,
|
|
80462
|
-
access: "view",
|
|
80463
|
-
role: "admin"
|
|
80464
|
-
},
|
|
80465
80326
|
"llm.listRuntimeNodes": {
|
|
80466
80327
|
capName: "llm",
|
|
80467
80328
|
capScope: "system",
|
|
@@ -80469,13 +80330,6 @@ Object.freeze({
|
|
|
80469
80330
|
access: "view",
|
|
80470
80331
|
role: "admin"
|
|
80471
80332
|
},
|
|
80472
|
-
"llm.migrateRawSecrets": {
|
|
80473
|
-
capName: "llm",
|
|
80474
|
-
capScope: "system",
|
|
80475
|
-
addonId: null,
|
|
80476
|
-
access: "create",
|
|
80477
|
-
role: "admin"
|
|
80478
|
-
},
|
|
80479
80333
|
"llm.resolveModelRef": {
|
|
80480
80334
|
capName: "llm",
|
|
80481
80335
|
capScope: "system",
|
|
@@ -80504,20 +80358,6 @@ Object.freeze({
|
|
|
80504
80358
|
access: "create",
|
|
80505
80359
|
role: "admin"
|
|
80506
80360
|
},
|
|
80507
|
-
"llm.testProfile": {
|
|
80508
|
-
capName: "llm",
|
|
80509
|
-
capScope: "system",
|
|
80510
|
-
addonId: null,
|
|
80511
|
-
access: "create",
|
|
80512
|
-
role: "admin"
|
|
80513
|
-
},
|
|
80514
|
-
"llm.upsertProfile": {
|
|
80515
|
-
capName: "llm",
|
|
80516
|
-
capScope: "system",
|
|
80517
|
-
addonId: null,
|
|
80518
|
-
access: "create",
|
|
80519
|
-
role: "admin"
|
|
80520
|
-
},
|
|
80521
80361
|
"llmRuntime.appleAvailability": {
|
|
80522
80362
|
capName: "llm-runtime",
|
|
80523
80363
|
capScope: "system",
|
|
@@ -81022,13 +80862,6 @@ Object.freeze({
|
|
|
81022
80862
|
access: "create",
|
|
81023
80863
|
role: "admin"
|
|
81024
80864
|
},
|
|
81025
|
-
"meshNetwork.testConnection": {
|
|
81026
|
-
capName: "mesh-network",
|
|
81027
|
-
capScope: "system",
|
|
81028
|
-
addonId: null,
|
|
81029
|
-
access: "create",
|
|
81030
|
-
role: "admin"
|
|
81031
|
-
},
|
|
81032
80865
|
"meter.getStatus": {
|
|
81033
80866
|
capName: "meter",
|
|
81034
80867
|
capScope: "device",
|
|
@@ -81687,13 +81520,6 @@ Object.freeze({
|
|
|
81687
81520
|
access: "create",
|
|
81688
81521
|
role: "admin"
|
|
81689
81522
|
},
|
|
81690
|
-
"notificationOutput.deleteTarget": {
|
|
81691
|
-
capName: "notification-output",
|
|
81692
|
-
capScope: "system",
|
|
81693
|
-
addonId: null,
|
|
81694
|
-
access: "delete",
|
|
81695
|
-
role: "admin"
|
|
81696
|
-
},
|
|
81697
81523
|
"notificationOutput.discoverTargets": {
|
|
81698
81524
|
capName: "notification-output",
|
|
81699
81525
|
capScope: "system",
|
|
@@ -81701,13 +81527,6 @@ Object.freeze({
|
|
|
81701
81527
|
access: "view",
|
|
81702
81528
|
role: "admin"
|
|
81703
81529
|
},
|
|
81704
|
-
"notificationOutput.getTarget": {
|
|
81705
|
-
capName: "notification-output",
|
|
81706
|
-
capScope: "system",
|
|
81707
|
-
addonId: null,
|
|
81708
|
-
access: "view",
|
|
81709
|
-
role: "admin"
|
|
81710
|
-
},
|
|
81711
81530
|
"notificationOutput.listTargetKinds": {
|
|
81712
81531
|
capName: "notification-output",
|
|
81713
81532
|
capScope: "system",
|
|
@@ -81715,13 +81534,6 @@ Object.freeze({
|
|
|
81715
81534
|
access: "view",
|
|
81716
81535
|
role: "admin"
|
|
81717
81536
|
},
|
|
81718
|
-
"notificationOutput.listTargets": {
|
|
81719
|
-
capName: "notification-output",
|
|
81720
|
-
capScope: "system",
|
|
81721
|
-
addonId: null,
|
|
81722
|
-
access: "view",
|
|
81723
|
-
role: "admin"
|
|
81724
|
-
},
|
|
81725
81537
|
"notificationOutput.probeTargetConfig": {
|
|
81726
81538
|
capName: "notification-output",
|
|
81727
81539
|
capScope: "system",
|
|
@@ -81736,13 +81548,6 @@ Object.freeze({
|
|
|
81736
81548
|
access: "create",
|
|
81737
81549
|
role: "admin"
|
|
81738
81550
|
},
|
|
81739
|
-
"notificationOutput.setTargetEnabled": {
|
|
81740
|
-
capName: "notification-output",
|
|
81741
|
-
capScope: "system",
|
|
81742
|
-
addonId: null,
|
|
81743
|
-
access: "create",
|
|
81744
|
-
role: "admin"
|
|
81745
|
-
},
|
|
81746
81551
|
"notificationOutput.testTarget": {
|
|
81747
81552
|
capName: "notification-output",
|
|
81748
81553
|
capScope: "system",
|
|
@@ -81750,13 +81555,6 @@ Object.freeze({
|
|
|
81750
81555
|
access: "create",
|
|
81751
81556
|
role: "admin"
|
|
81752
81557
|
},
|
|
81753
|
-
"notificationOutput.upsertTarget": {
|
|
81754
|
-
capName: "notification-output",
|
|
81755
|
-
capScope: "system",
|
|
81756
|
-
addonId: null,
|
|
81757
|
-
access: "create",
|
|
81758
|
-
role: "admin"
|
|
81759
|
-
},
|
|
81760
81558
|
"notificationRules.activateAlarmProfile": {
|
|
81761
81559
|
capName: "notification-rules",
|
|
81762
81560
|
capScope: "system",
|
|
@@ -82016,41 +81814,6 @@ Object.freeze({
|
|
|
82016
81814
|
access: "view",
|
|
82017
81815
|
role: "admin"
|
|
82018
81816
|
},
|
|
82019
|
-
"oidcConnections.connectionFormSchema": {
|
|
82020
|
-
capName: "oidc-connections",
|
|
82021
|
-
capScope: "system",
|
|
82022
|
-
addonId: null,
|
|
82023
|
-
access: "view",
|
|
82024
|
-
role: "admin"
|
|
82025
|
-
},
|
|
82026
|
-
"oidcConnections.createConnection": {
|
|
82027
|
-
capName: "oidc-connections",
|
|
82028
|
-
capScope: "system",
|
|
82029
|
-
addonId: null,
|
|
82030
|
-
access: "create",
|
|
82031
|
-
role: "admin"
|
|
82032
|
-
},
|
|
82033
|
-
"oidcConnections.deleteConnection": {
|
|
82034
|
-
capName: "oidc-connections",
|
|
82035
|
-
capScope: "system",
|
|
82036
|
-
addonId: null,
|
|
82037
|
-
access: "delete",
|
|
82038
|
-
role: "admin"
|
|
82039
|
-
},
|
|
82040
|
-
"oidcConnections.listConnections": {
|
|
82041
|
-
capName: "oidc-connections",
|
|
82042
|
-
capScope: "system",
|
|
82043
|
-
addonId: null,
|
|
82044
|
-
access: "view",
|
|
82045
|
-
role: "admin"
|
|
82046
|
-
},
|
|
82047
|
-
"oidcConnections.updateConnection": {
|
|
82048
|
-
capName: "oidc-connections",
|
|
82049
|
-
capScope: "system",
|
|
82050
|
-
addonId: null,
|
|
82051
|
-
access: "create",
|
|
82052
|
-
role: "admin"
|
|
82053
|
-
},
|
|
82054
81817
|
"osd.getStatus": {
|
|
82055
81818
|
capName: "osd",
|
|
82056
81819
|
capScope: "device",
|
|
@@ -85082,13 +84845,6 @@ Object.freeze({
|
|
|
85082
84845
|
access: "delete",
|
|
85083
84846
|
role: "admin"
|
|
85084
84847
|
},
|
|
85085
|
-
"storage.deleteLocation": {
|
|
85086
|
-
capName: "storage",
|
|
85087
|
-
capScope: "system",
|
|
85088
|
-
addonId: null,
|
|
85089
|
-
access: "delete",
|
|
85090
|
-
role: "admin"
|
|
85091
|
-
},
|
|
85092
84848
|
"storage.endDownload": {
|
|
85093
84849
|
capName: "storage",
|
|
85094
84850
|
capScope: "system",
|
|
@@ -85145,13 +84901,6 @@ Object.freeze({
|
|
|
85145
84901
|
access: "view",
|
|
85146
84902
|
role: "admin"
|
|
85147
84903
|
},
|
|
85148
|
-
"storage.listProviders": {
|
|
85149
|
-
capName: "storage",
|
|
85150
|
-
capScope: "system",
|
|
85151
|
-
addonId: null,
|
|
85152
|
-
access: "view",
|
|
85153
|
-
role: "admin"
|
|
85154
|
-
},
|
|
85155
84904
|
"storage.read": {
|
|
85156
84905
|
capName: "storage",
|
|
85157
84906
|
capScope: "system",
|
|
@@ -85173,27 +84922,6 @@ Object.freeze({
|
|
|
85173
84922
|
access: "view",
|
|
85174
84923
|
role: "admin"
|
|
85175
84924
|
},
|
|
85176
|
-
"storage.testConfig": {
|
|
85177
|
-
capName: "storage",
|
|
85178
|
-
capScope: "system",
|
|
85179
|
-
addonId: null,
|
|
85180
|
-
access: "view",
|
|
85181
|
-
role: "admin"
|
|
85182
|
-
},
|
|
85183
|
-
"storage.testLocation": {
|
|
85184
|
-
capName: "storage",
|
|
85185
|
-
capScope: "system",
|
|
85186
|
-
addonId: null,
|
|
85187
|
-
access: "view",
|
|
85188
|
-
role: "admin"
|
|
85189
|
-
},
|
|
85190
|
-
"storage.upsertLocation": {
|
|
85191
|
-
capName: "storage",
|
|
85192
|
-
capScope: "system",
|
|
85193
|
-
addonId: null,
|
|
85194
|
-
access: "create",
|
|
85195
|
-
role: "admin"
|
|
85196
|
-
},
|
|
85197
84925
|
"storage.write": {
|
|
85198
84926
|
capName: "storage",
|
|
85199
84927
|
capScope: "system",
|
|
@@ -91346,20 +91074,6 @@ object({
|
|
|
91346
91074
|
});
|
|
91347
91075
|
//#endregion
|
|
91348
91076
|
//#region src/custom-actions.ts
|
|
91349
|
-
var SourceIdSchema = string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug");
|
|
91350
|
-
/**
|
|
91351
|
-
* A source as the admin list shows it. No `args`: a credential never leaves the
|
|
91352
|
-
* addon - `formValues` carries the redaction token where one is stored (D880).
|
|
91353
|
-
*/
|
|
91354
|
-
var WasteSourceRowSchema = object({
|
|
91355
|
-
id: string(),
|
|
91356
|
-
label: string(),
|
|
91357
|
-
module: string(),
|
|
91358
|
-
enabled: boolean(),
|
|
91359
|
-
formValues: record(string(), unknown()).optional(),
|
|
91360
|
-
serviceId: string().optional(),
|
|
91361
|
-
countryId: string().optional()
|
|
91362
|
-
});
|
|
91363
91077
|
var wasteCollectionsActions = defineCustomActions({
|
|
91364
91078
|
fetchNow: customAction(object({
|
|
91365
91079
|
/** When set, refresh only this configured source id; otherwise all enabled. */
|
|
@@ -91374,10 +91088,6 @@ sourceId: string().min(1).optional() }).optional(), object({
|
|
|
91374
91088
|
kind: "mutation",
|
|
91375
91089
|
auth: "admin"
|
|
91376
91090
|
}),
|
|
91377
|
-
listSources: customAction(object({}), array(WasteSourceRowSchema).readonly(), {
|
|
91378
|
-
kind: "mutation",
|
|
91379
|
-
auth: "admin"
|
|
91380
|
-
}),
|
|
91381
91091
|
listCountries: customAction(object({}), array(object({
|
|
91382
91092
|
id: string(),
|
|
91383
91093
|
label: string()
|
|
@@ -91395,43 +91105,6 @@ sourceId: string().min(1).optional() }).optional(), object({
|
|
|
91395
91105
|
})).readonly(), {
|
|
91396
91106
|
kind: "mutation",
|
|
91397
91107
|
auth: "admin"
|
|
91398
|
-
}),
|
|
91399
|
-
serviceFormSchema: customAction(object({ serviceId: string().min(1) }), unknown(), {
|
|
91400
|
-
kind: "mutation",
|
|
91401
|
-
auth: "admin"
|
|
91402
|
-
}),
|
|
91403
|
-
createSource: customAction(object({
|
|
91404
|
-
/** Optional override; when omitted the id is derived from `label`. */
|
|
91405
|
-
id: SourceIdSchema.optional(),
|
|
91406
|
-
label: string().min(1),
|
|
91407
|
-
serviceId: string().min(1),
|
|
91408
|
-
args: record(string(), unknown()).default({}),
|
|
91409
|
-
enabled: boolean().optional()
|
|
91410
|
-
}), object({ id: string() }), {
|
|
91411
|
-
kind: "mutation",
|
|
91412
|
-
auth: "admin"
|
|
91413
|
-
}),
|
|
91414
|
-
updateSource: customAction(object({
|
|
91415
|
-
id: string().min(1),
|
|
91416
|
-
label: string().min(1),
|
|
91417
|
-
serviceId: string().min(1).optional(),
|
|
91418
|
-
args: record(string(), unknown()).optional(),
|
|
91419
|
-
enabled: boolean().optional()
|
|
91420
|
-
}), object({ ok: literal(true) }), {
|
|
91421
|
-
kind: "mutation",
|
|
91422
|
-
auth: "admin"
|
|
91423
|
-
}),
|
|
91424
|
-
/**
|
|
91425
|
-
* D880: rewrite every legacy source (free `argsJson`) as typed `args`, moving its
|
|
91426
|
-
* credentials into the keychain. Dry run unless `apply`; idempotent; counts only.
|
|
91427
|
-
*/
|
|
91428
|
-
migrateSources: customAction(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
|
|
91429
|
-
kind: "mutation",
|
|
91430
|
-
auth: "admin"
|
|
91431
|
-
}),
|
|
91432
|
-
deleteSource: customAction(object({ id: string().min(1) }), object({ ok: literal(true) }), {
|
|
91433
|
-
kind: "mutation",
|
|
91434
|
-
auth: "admin"
|
|
91435
91108
|
})
|
|
91436
91109
|
});
|
|
91437
91110
|
//#endregion
|
|
@@ -91465,33 +91138,6 @@ var WASTE_SECRET_STATE_PATHS = SECRET_ARG_KEYS.map((key) => `sources[id].args.${
|
|
|
91465
91138
|
function isSecretArg(key) {
|
|
91466
91139
|
return SECRET_ARG_KEYS.includes(key.toLowerCase());
|
|
91467
91140
|
}
|
|
91468
|
-
/** Is a credential stored for this argument (a value, or a marker the keychain could not serve)? */
|
|
91469
|
-
function isSet(value) {
|
|
91470
|
-
return isSecretMarker(value) || typeof value === "string" && value.length > 0;
|
|
91471
|
-
}
|
|
91472
|
-
/** The arguments as a read surface may show them: a stored credential is the redaction token. */
|
|
91473
|
-
function redactArgs(args) {
|
|
91474
|
-
const out = {};
|
|
91475
|
-
for (const [key, value] of Object.entries(args)) out[key] = isSecretArg(key) && isSet(value) ? REDACTED_SECRET : value;
|
|
91476
|
-
return out;
|
|
91477
|
-
}
|
|
91478
|
-
/**
|
|
91479
|
-
* The write-back of {@link redactArgs}: a credential sent back as the redaction
|
|
91480
|
-
* token means "keep what is stored", so it takes the stored (resolved) value; one
|
|
91481
|
-
* with nothing stored is dropped, never saved as the token.
|
|
91482
|
-
*/
|
|
91483
|
-
function restoreRedactedArgs(incoming, stored) {
|
|
91484
|
-
const out = {};
|
|
91485
|
-
for (const [key, value] of Object.entries(incoming)) {
|
|
91486
|
-
if (isSecretArg(key) && value === "__camstack_redacted__") {
|
|
91487
|
-
const previous = stored[key];
|
|
91488
|
-
if (previous !== void 0) out[key] = previous;
|
|
91489
|
-
continue;
|
|
91490
|
-
}
|
|
91491
|
-
out[key] = value;
|
|
91492
|
-
}
|
|
91493
|
-
return out;
|
|
91494
|
-
}
|
|
91495
91141
|
/**
|
|
91496
91142
|
* Does this argument bag hold a credential that is NOT a usable value: a MARKER the keychain did
|
|
91497
91143
|
* not resolve, or the redaction token (a row read without its credential)? Either would reach a
|
|
@@ -91500,10 +91146,6 @@ function restoreRedactedArgs(incoming, stored) {
|
|
|
91500
91146
|
function hasUnresolvedCredential(args) {
|
|
91501
91147
|
return Object.entries(args).some(([key, value]) => isSecretArg(key) && (isSecretMarker(value) || value === "__camstack_redacted__"));
|
|
91502
91148
|
}
|
|
91503
|
-
/** Does it hold a non-empty credential (any form)? */
|
|
91504
|
-
function hasCredential(args) {
|
|
91505
|
-
return Object.entries(args).some(([key, value]) => isSecretArg(key) && isSet(value));
|
|
91506
|
-
}
|
|
91507
91149
|
//#endregion
|
|
91508
91150
|
//#region src/waste-types.ts
|
|
91509
91151
|
/**
|
|
@@ -91968,35 +91610,6 @@ function reportFailed(log, failures) {
|
|
|
91968
91610
|
};
|
|
91969
91611
|
}
|
|
91970
91612
|
//#endregion
|
|
91971
|
-
//#region src/source-id.ts
|
|
91972
|
-
/**
|
|
91973
|
-
* Stable calendar source ids derived from the operator label.
|
|
91974
|
-
* Pattern: `^[a-z0-9][a-z0-9-]{0,63}$` (max 64 chars).
|
|
91975
|
-
*/
|
|
91976
|
-
var SOURCE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
91977
|
-
/** Slugify a display label into a candidate source id (may be empty). */
|
|
91978
|
-
function slugifySourceLabel(label) {
|
|
91979
|
-
const s = label.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
91980
|
-
if (s.length === 0) return "";
|
|
91981
|
-
return s.slice(0, 64);
|
|
91982
|
-
}
|
|
91983
|
-
/**
|
|
91984
|
-
* Pick an unused id from `label`. Collisions append `-2`, `-3`, …
|
|
91985
|
-
* (truncated so the result stays within the 64-char pattern).
|
|
91986
|
-
*/
|
|
91987
|
-
function allocateSourceId(label, existingIds) {
|
|
91988
|
-
const taken = existingIds instanceof Set ? existingIds : new Set(existingIds);
|
|
91989
|
-
const baseRaw = slugifySourceLabel(label);
|
|
91990
|
-
const base = baseRaw.length > 0 ? baseRaw : "source";
|
|
91991
|
-
if (!taken.has(base) && SOURCE_ID_PATTERN.test(base)) return base;
|
|
91992
|
-
for (let n = 2; n < 1e4; n++) {
|
|
91993
|
-
const suffix = `-${n}`;
|
|
91994
|
-
const candidate = `${base.slice(0, Math.max(1, 64 - suffix.length)).replace(/-+$/g, "")}${suffix}`;
|
|
91995
|
-
if (SOURCE_ID_PATTERN.test(candidate) && !taken.has(candidate)) return candidate;
|
|
91996
|
-
}
|
|
91997
|
-
throw new Error("could not allocate a unique source id");
|
|
91998
|
-
}
|
|
91999
|
-
//#endregion
|
|
92000
91613
|
//#region src/waste-calendar-source.ts
|
|
92001
91614
|
function startOfUtcDayMs(isoDate) {
|
|
92002
91615
|
const [y, m, d] = isoDate.split("-").map((p) => Number(p));
|
|
@@ -178041,6 +177654,35 @@ function mergeServiceArgs(service, formValues) {
|
|
|
178041
177654
|
return out;
|
|
178042
177655
|
}
|
|
178043
177656
|
//#endregion
|
|
177657
|
+
//#region src/source-id.ts
|
|
177658
|
+
/**
|
|
177659
|
+
* Stable calendar source ids derived from the operator label.
|
|
177660
|
+
* Pattern: `^[a-z0-9][a-z0-9-]{0,63}$` (max 64 chars).
|
|
177661
|
+
*/
|
|
177662
|
+
var SOURCE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
177663
|
+
/** Slugify a display label into a candidate source id (may be empty). */
|
|
177664
|
+
function slugifySourceLabel(label) {
|
|
177665
|
+
const s = label.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
177666
|
+
if (s.length === 0) return "";
|
|
177667
|
+
return s.slice(0, 64);
|
|
177668
|
+
}
|
|
177669
|
+
/**
|
|
177670
|
+
* Pick an unused id from `label`. Collisions append `-2`, `-3`, …
|
|
177671
|
+
* (truncated so the result stays within the 64-char pattern).
|
|
177672
|
+
*/
|
|
177673
|
+
function allocateSourceId(label, existingIds) {
|
|
177674
|
+
const taken = existingIds instanceof Set ? existingIds : new Set(existingIds);
|
|
177675
|
+
const baseRaw = slugifySourceLabel(label);
|
|
177676
|
+
const base = baseRaw.length > 0 ? baseRaw : "source";
|
|
177677
|
+
if (!taken.has(base) && SOURCE_ID_PATTERN.test(base)) return base;
|
|
177678
|
+
for (let n = 2; n < 1e4; n++) {
|
|
177679
|
+
const suffix = `-${n}`;
|
|
177680
|
+
const candidate = `${base.slice(0, Math.max(1, 64 - suffix.length)).replace(/-+$/g, "")}${suffix}`;
|
|
177681
|
+
if (SOURCE_ID_PATTERN.test(candidate) && !taken.has(candidate)) return candidate;
|
|
177682
|
+
}
|
|
177683
|
+
throw new Error("could not allocate a unique source id");
|
|
177684
|
+
}
|
|
177685
|
+
//#endregion
|
|
178044
177686
|
//#region src/waste-source-store.ts
|
|
178045
177687
|
/**
|
|
178046
177688
|
* A stored body as a source. A row read while the keychain is locked still carries its markers
|
|
@@ -178544,23 +178186,13 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
178544
178186
|
customActions: wasteCollectionsActions,
|
|
178545
178187
|
actionHandlers: {
|
|
178546
178188
|
fetchNow: async (input) => this.fetchNow(input),
|
|
178547
|
-
listSources: async () => this.listSources(),
|
|
178548
178189
|
listCountries: async () => listCatalogCountries(),
|
|
178549
178190
|
listServices: async (input) => listCatalogServices(input.countryId).map((s) => ({
|
|
178550
178191
|
id: s.id,
|
|
178551
178192
|
label: serviceDisplayLabel(s),
|
|
178552
178193
|
title: s.label,
|
|
178553
178194
|
module: s.module
|
|
178554
|
-
}))
|
|
178555
|
-
serviceFormSchema: async (input) => {
|
|
178556
|
-
const service = findCatalogService(input.serviceId);
|
|
178557
|
-
if (!service) throw new Error(`unknown service '${input.serviceId}'`);
|
|
178558
|
-
return serviceFormSchema(service);
|
|
178559
|
-
},
|
|
178560
|
-
createSource: async (input) => this.createSource(input),
|
|
178561
|
-
updateSource: async (input) => this.updateSource(input),
|
|
178562
|
-
deleteSource: async (input) => this.deleteSource(input),
|
|
178563
|
-
migrateSources: async (input) => this.migrateSources(input.apply)
|
|
178195
|
+
}))
|
|
178564
178196
|
}
|
|
178565
178197
|
};
|
|
178566
178198
|
}
|
|
@@ -178627,17 +178259,6 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
178627
178259
|
if (!this.legacyServed) return records;
|
|
178628
178260
|
return withUnmigratedLegacyRows(records, this.parseConfigSources(this.config.sources));
|
|
178629
178261
|
}
|
|
178630
|
-
async persistSources(sources) {
|
|
178631
|
-
this.activeSources = [...sources];
|
|
178632
|
-
const store = this.sourceStore;
|
|
178633
|
-
if (store !== null) {
|
|
178634
|
-
await store.replaceAll(sources);
|
|
178635
|
-
const kept = new Set(sources.map((s) => s.id));
|
|
178636
|
-
await this.pruneLegacyRows((legacyId) => kept.has(legacyId));
|
|
178637
|
-
return;
|
|
178638
|
-
}
|
|
178639
|
-
await this.updateGlobalSettings({ sources: [...sources] });
|
|
178640
|
-
}
|
|
178641
178262
|
/**
|
|
178642
178263
|
* A source deleted at the records leaves the legacy setting too, or it would reappear beside the
|
|
178643
178264
|
* records (while the migration is pending) or be copied back by it.
|
|
@@ -178652,195 +178273,6 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
178652
178273
|
if (this.activeSources.length > 0 || this.sourceStore !== null) return this.activeSources;
|
|
178653
178274
|
return this.parseConfigSources(this.config.sources);
|
|
178654
178275
|
}
|
|
178655
|
-
listSources() {
|
|
178656
|
-
return this.configuredSources().map((s) => {
|
|
178657
|
-
let args = {};
|
|
178658
|
-
try {
|
|
178659
|
-
args = sourceArgs(s);
|
|
178660
|
-
} catch {
|
|
178661
|
-
args = { ...s.args };
|
|
178662
|
-
}
|
|
178663
|
-
const matched = matchCatalogService(s.module, args);
|
|
178664
|
-
const formValues = {
|
|
178665
|
-
label: s.label,
|
|
178666
|
-
enabled: s.enabled,
|
|
178667
|
-
...redactArgs(args)
|
|
178668
|
-
};
|
|
178669
|
-
if (matched) for (const key of Object.keys(matched.defaultArgs)) delete formValues[key];
|
|
178670
|
-
return {
|
|
178671
|
-
id: s.id,
|
|
178672
|
-
label: s.label,
|
|
178673
|
-
module: s.module,
|
|
178674
|
-
enabled: s.enabled,
|
|
178675
|
-
formValues,
|
|
178676
|
-
serviceId: matched?.id,
|
|
178677
|
-
countryId: matched?.countryId
|
|
178678
|
-
};
|
|
178679
|
-
});
|
|
178680
|
-
}
|
|
178681
|
-
async createSource(input) {
|
|
178682
|
-
const existing = this.configuredSources().map((s) => s.id);
|
|
178683
|
-
let id;
|
|
178684
|
-
if (input.id !== void 0) {
|
|
178685
|
-
if (!SOURCE_ID_PATTERN.test(input.id)) throw new Error("id must be a lowercase slug (e.g. ics-home, junker)");
|
|
178686
|
-
if (existing.includes(input.id)) throw new Error(`source '${input.id}' already exists`);
|
|
178687
|
-
id = input.id;
|
|
178688
|
-
} else id = allocateSourceId(input.label, existing);
|
|
178689
|
-
const service = findCatalogService(input.serviceId);
|
|
178690
|
-
if (!service) throw new Error(`unknown service '${input.serviceId}'`);
|
|
178691
|
-
const row = WasteSourceConfigSchema.parse({
|
|
178692
|
-
id,
|
|
178693
|
-
label: input.label,
|
|
178694
|
-
module: service.module,
|
|
178695
|
-
args: this.typedArgs(mergeServiceArgs(service, restoreRedactedArgs(input.args, {}))),
|
|
178696
|
-
enabled: input.enabled ?? true
|
|
178697
|
-
});
|
|
178698
|
-
await this.persistSources([...this.configuredSources(), row]);
|
|
178699
|
-
return { id };
|
|
178700
|
-
}
|
|
178701
|
-
async updateSource(input) {
|
|
178702
|
-
const sources = this.configuredSources();
|
|
178703
|
-
const idx = sources.findIndex((s) => s.id === input.id);
|
|
178704
|
-
if (idx < 0) throw new Error(`source '${input.id}' not found`);
|
|
178705
|
-
const prev = sources[idx];
|
|
178706
|
-
let prevArgs = {};
|
|
178707
|
-
try {
|
|
178708
|
-
prevArgs = sourceArgs(prev);
|
|
178709
|
-
} catch {
|
|
178710
|
-
prevArgs = { ...prev.args };
|
|
178711
|
-
}
|
|
178712
|
-
let module = prev.module;
|
|
178713
|
-
let args = prevArgs;
|
|
178714
|
-
if (input.serviceId !== void 0) {
|
|
178715
|
-
const service = findCatalogService(input.serviceId);
|
|
178716
|
-
if (!service) throw new Error(`unknown service '${input.serviceId}'`);
|
|
178717
|
-
module = service.module;
|
|
178718
|
-
args = mergeServiceArgs(service, restoreRedactedArgs(input.args ?? {}, prevArgs));
|
|
178719
|
-
} else if (input.args !== void 0) {
|
|
178720
|
-
const sent = restoreRedactedArgs(input.args, prevArgs);
|
|
178721
|
-
const matched = matchCatalogService(prev.module, prevArgs);
|
|
178722
|
-
args = matched ? mergeServiceArgs(matched, sent) : {
|
|
178723
|
-
...prevArgs,
|
|
178724
|
-
...sent
|
|
178725
|
-
};
|
|
178726
|
-
}
|
|
178727
|
-
const next = WasteSourceConfigSchema.parse({
|
|
178728
|
-
id: prev.id,
|
|
178729
|
-
label: input.label,
|
|
178730
|
-
module,
|
|
178731
|
-
args: this.typedArgs(args),
|
|
178732
|
-
enabled: input.enabled ?? prev.enabled
|
|
178733
|
-
});
|
|
178734
|
-
await this.persistSources(sources.map((s, i) => i === idx ? next : s));
|
|
178735
|
-
return { ok: true };
|
|
178736
|
-
}
|
|
178737
|
-
/** Validate an argument bag against the typed value schema (a nested object is refused). */
|
|
178738
|
-
typedArgs(args) {
|
|
178739
|
-
const out = {};
|
|
178740
|
-
for (const [key, value] of Object.entries(args)) out[key] = WasteArgValueSchema.parse(value);
|
|
178741
|
-
return out;
|
|
178742
|
-
}
|
|
178743
|
-
/**
|
|
178744
|
-
* D880: rewrite every legacy source (free `argsJson`) as typed `args`; its
|
|
178745
|
-
* credentials go to the keychain through the normal save path. Then every
|
|
178746
|
-
* credential is read back and compared with what the legacy row held; on any
|
|
178747
|
-
* difference the legacy rows are written back, so nothing is lost. Dry run
|
|
178748
|
-
* unless `apply`. Idempotent. Counts only - never an argument, a ref or an id.
|
|
178749
|
-
*/
|
|
178750
|
-
async migrateSources(apply) {
|
|
178751
|
-
const sources = this.configuredSources();
|
|
178752
|
-
const legacy = sources.filter((s) => s.argsJson !== void 0);
|
|
178753
|
-
const rawRows = legacy.filter((s) => {
|
|
178754
|
-
try {
|
|
178755
|
-
return hasCredential(sourceArgs(s));
|
|
178756
|
-
} catch {
|
|
178757
|
-
return false;
|
|
178758
|
-
}
|
|
178759
|
-
});
|
|
178760
|
-
const markerRows = sources.filter((s) => s.argsJson === void 0 && hasCredential(s.args)).length;
|
|
178761
|
-
const report = (over) => ({
|
|
178762
|
-
apply,
|
|
178763
|
-
rows: sources.length,
|
|
178764
|
-
raw: rawRows.length,
|
|
178765
|
-
markers: markerRows,
|
|
178766
|
-
migrated: 0,
|
|
178767
|
-
failed: 0,
|
|
178768
|
-
remainingRaw: rawRows.length,
|
|
178769
|
-
unresolvedMarkers: this.unresolvedSecrets.length,
|
|
178770
|
-
redundantCopies: legacy.length,
|
|
178771
|
-
verified: rawRows.length === 0 && this.unresolvedSecrets.length === 0,
|
|
178772
|
-
...over
|
|
178773
|
-
});
|
|
178774
|
-
if (!apply || legacy.length === 0) return report({});
|
|
178775
|
-
const expected = /* @__PURE__ */ new Map();
|
|
178776
|
-
const unconverted = /* @__PURE__ */ new Set();
|
|
178777
|
-
const migrated = sources.map((s) => {
|
|
178778
|
-
if (s.argsJson === void 0) return s;
|
|
178779
|
-
try {
|
|
178780
|
-
const args = sourceArgs(s);
|
|
178781
|
-
const row = WasteSourceConfigSchema.parse({
|
|
178782
|
-
id: s.id,
|
|
178783
|
-
label: s.label,
|
|
178784
|
-
module: s.module,
|
|
178785
|
-
args: this.typedArgs(args),
|
|
178786
|
-
enabled: s.enabled
|
|
178787
|
-
});
|
|
178788
|
-
expected.set(s.id, args);
|
|
178789
|
-
return row;
|
|
178790
|
-
} catch {
|
|
178791
|
-
unconverted.add(s.id);
|
|
178792
|
-
return s;
|
|
178793
|
-
}
|
|
178794
|
-
});
|
|
178795
|
-
const failedRows = unconverted.size;
|
|
178796
|
-
if (expected.size === 0) return report({
|
|
178797
|
-
failed: failedRows,
|
|
178798
|
-
remainingRaw: legacy.length
|
|
178799
|
-
});
|
|
178800
|
-
try {
|
|
178801
|
-
await this.persistSources(migrated);
|
|
178802
|
-
} catch {
|
|
178803
|
-
return report({ failed: legacy.length });
|
|
178804
|
-
}
|
|
178805
|
-
const back = new Map(this.configuredSources().map((s) => [s.id, s]));
|
|
178806
|
-
if (![...expected].every(([id, args]) => {
|
|
178807
|
-
const row = back.get(id);
|
|
178808
|
-
return row !== void 0 && row.argsJson === void 0 && JSON.stringify(sortedEntries(row.args)) === JSON.stringify(sortedEntries(args));
|
|
178809
|
-
}) || this.unresolvedSecrets.length > 0) {
|
|
178810
|
-
await this.rollbackMigration(sources, expected);
|
|
178811
|
-
return report({ failed: legacy.length });
|
|
178812
|
-
}
|
|
178813
|
-
return report({
|
|
178814
|
-
migrated: expected.size,
|
|
178815
|
-
failed: failedRows,
|
|
178816
|
-
remainingRaw: failedRows,
|
|
178817
|
-
unresolvedMarkers: 0,
|
|
178818
|
-
redundantCopies: legacy.length,
|
|
178819
|
-
verified: failedRows === 0
|
|
178820
|
-
});
|
|
178821
|
-
}
|
|
178822
|
-
/**
|
|
178823
|
-
* Put the legacy rows back. A converted row's `args` is written as `null`, which
|
|
178824
|
-
* the save path reads as "this object is gone": it deletes the keychain refs the
|
|
178825
|
-
* migration created (a plain `{}` would keep them). Never throws - the report
|
|
178826
|
-
* says failed either way, and the legacy JSON stays authoritative.
|
|
178827
|
-
*/
|
|
178828
|
-
async rollbackMigration(sources, converted) {
|
|
178829
|
-
try {
|
|
178830
|
-
await this.updateGlobalSettingsAsOperator({ sources: sources.map((s) => converted.has(s.id) ? {
|
|
178831
|
-
...s,
|
|
178832
|
-
args: null
|
|
178833
|
-
} : s) });
|
|
178834
|
-
} catch {
|
|
178835
|
-
this.ctx.logger.error("waste-collections: the migration rollback failed - keychain refs may be orphaned (System -> Secrets)");
|
|
178836
|
-
}
|
|
178837
|
-
}
|
|
178838
|
-
async deleteSource(input) {
|
|
178839
|
-
const sources = this.configuredSources();
|
|
178840
|
-
if (!sources.some((s) => s.id === input.id)) throw new Error(`source '${input.id}' not found`);
|
|
178841
|
-
await this.persistSources(sources.filter((s) => s.id !== input.id));
|
|
178842
|
-
return { ok: true };
|
|
178843
|
-
}
|
|
178844
178276
|
async fetchNow(input) {
|
|
178845
178277
|
const targets = this.configuredSources().filter((s) => {
|
|
178846
178278
|
if (!s.enabled) return false;
|
|
@@ -178941,33 +178373,11 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
178941
178373
|
this.ctx.logger.warn("waste-collections: cache persist failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
178942
178374
|
}
|
|
178943
178375
|
}
|
|
178944
|
-
globalSettingsSchema() {
|
|
178945
|
-
return this.schema({ sections: [{
|
|
178946
|
-
id: "refresh",
|
|
178947
|
-
title: "Refresh",
|
|
178948
|
-
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.",
|
|
178949
|
-
columns: 2,
|
|
178950
|
-
fields: [{
|
|
178951
|
-
type: "addon-action-button",
|
|
178952
|
-
key: "_fetchNow",
|
|
178953
|
-
label: "Refresh",
|
|
178954
|
-
buttonLabel: "Fetch now",
|
|
178955
|
-
description: "Sources refresh once at boot and then every 24 hours; this refreshes every enabled source immediately.",
|
|
178956
|
-
addonId: "waste-collections",
|
|
178957
|
-
action: "fetchNow",
|
|
178958
|
-
variant: "primary",
|
|
178959
|
-
paramsStatic: {}
|
|
178960
|
-
}]
|
|
178961
|
-
}] });
|
|
178962
|
-
}
|
|
178963
178376
|
};
|
|
178964
178377
|
/** Re-parse helper for tests. */
|
|
178965
178378
|
function parseWasteConfig(raw) {
|
|
178966
178379
|
return WasteConfigSchema.parse(raw);
|
|
178967
178380
|
}
|
|
178968
|
-
function sortedEntries(args) {
|
|
178969
|
-
return Object.entries(args).toSorted(([a], [b]) => a < b ? -1 : 1);
|
|
178970
|
-
}
|
|
178971
178381
|
//#endregion
|
|
178972
178382
|
exports.DEFAULT_WASTE_CONFIG = DEFAULT_WASTE_CONFIG;
|
|
178973
178383
|
exports.WasteCollectionsAddon = WasteCollectionsAddon;
|