@attraccess/plugin-wago 0.1.0-nightly.954.1 → 0.1.0-nightly.955.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -52997,10 +52997,10 @@ function canonicalEnvelope(data, category) {
52997
52997
  }
52998
52998
  function validEnvelope(data, now) {
52999
52999
  const time = sourceTime(data.timestamp);
53000
- return time !== null && time <= now && typeof data.streamId === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(data.streamId) && Number.isSafeInteger(data.sequence) && data.sequence >= 1;
53000
+ return time !== null && time <= now && typeof data.streamId === "string" && data.streamId.trim().length > 0 && data.streamId.length <= 128 && Number.isSafeInteger(data.sequence) && data.sequence >= 1;
53001
53001
  }
53002
53002
  function admitEnvelope(state, data, category, now) {
53003
- const stream = data.streamId.toLowerCase();
53003
+ const stream = data.streamId;
53004
53004
  const timestamp = sourceTime(data.timestamp);
53005
53005
  const sequence = data.sequence;
53006
53006
  if (state.retiredStreams.includes(stream) || state.trackingExhausted) return "rejected";
@@ -53166,7 +53166,7 @@ var WagoDiagnosticsStore = class {
53166
53166
  const canonical = canonicalEnvelope(data, kind);
53167
53167
  if (canonical && !validEnvelope(data, this.now())) return false;
53168
53168
  const isObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
53169
- if (canonical && kind === "state" && data.readiness !== void 0 && (!isObject(data.readiness) || data.readiness.hardwareAvailable !== void 0 && typeof data.readiness.hardwareAvailable !== "boolean"))
53169
+ if (canonical && kind === "state" && data.readiness !== void 0 && (!isObject(data.readiness) || typeof data.readiness.hardwareAvailable !== "boolean"))
53170
53170
  return false;
53171
53171
  if (canonical && kind === "state" && !(data.contentHash === null || typeof data.contentHash === "string" && /^[0-9a-f]{64}$/i.test(data.contentHash)))
53172
53172
  return false;
@@ -59411,7 +59411,7 @@ function diagnosticReferences(nodes, channelIds, revision, capabilities) {
59411
59411
  resourceId: node.resourceId,
59412
59412
  channelId,
59413
59413
  control,
59414
- href: `/resources/${node.resourceId}/flows`,
59414
+ href: `/resources/${node.resourceId}/flows?node=${encodeURIComponent(node.id)}`,
59415
59415
  invalid: !channelIds.includes(channelId) || control && (node.data.expectedConfigurationRevision !== revision || capabilities && (!own(capabilities, channelId)?.includes("output") || node.data.action === "pulse" && !own(capabilities, channelId)?.includes("pulse"))),
59416
59416
  conflict: control && nodes.some(
59417
59417
  (other) => other.type === "plugin.wago.command" && other.resourceId !== node.resourceId && other.data.channelId === channelId
@@ -59424,6 +59424,90 @@ var WagoDiagnosticsService = class {
59424
59424
  __publicField(this, "context", context);
59425
59425
  __publicField(this, "wago", wago);
59426
59426
  }
59427
+ async getResource(resourceId) {
59428
+ const nodes = await this.context.dataSource.getRepository(import_plugins_backend_sdk15.ResourceFlowNode).createQueryBuilder("node").where("node.resourceId = :resourceId", { resourceId }).andWhere("node.type LIKE :type", { type: "plugin.wago.%" }).orderBy("node.id", "ASC").take(1001).getMany();
59429
+ const controllerIds = /* @__PURE__ */ new Set();
59430
+ let invalidControllerReferences = 0;
59431
+ for (const node of nodes.slice(0, 1e3)) {
59432
+ const id = node.data.controllerId;
59433
+ if (typeof id !== "number" || !Number.isSafeInteger(id) || id <= 0) {
59434
+ invalidControllerReferences++;
59435
+ } else {
59436
+ controllerIds.add(id);
59437
+ }
59438
+ }
59439
+ const selectedControllerIds = [...controllerIds].slice(0, 20);
59440
+ if (!selectedControllerIds.length)
59441
+ return { resourceId, controllers: [], invalidControllerReferences, truncated: nodes.length > 1e3 };
59442
+ const localNodes = nodes.slice(0, 1e3);
59443
+ const localChannelIds = [
59444
+ ...new Set(
59445
+ localNodes.map((node) => node.data.channelId).filter((channelId) => typeof channelId === "string")
59446
+ )
59447
+ ];
59448
+ const controllerIdsForQuery = selectedControllerIds.map(String);
59449
+ const [controllers, appliedRevisions, conflictNodes] = await Promise.all([
59450
+ this.context.getRepository(WagoController).createQueryBuilder("controller").select(["controller.id", "controller.name", "controller.hardwareId"]).where("controller.id IN (:...controllerIds)", { controllerIds: selectedControllerIds }).getMany(),
59451
+ this.context.getRepository(WagoConfigurationRevision).createQueryBuilder("revision").select(["revision.controllerId", "revision.revision", "revision.snapshot"]).distinctOn(["revision.controllerId"]).where("revision.controllerId IN (:...controllerIds)", { controllerIds: selectedControllerIds }).andWhere("revision.state = :state", { state: "applied" }).orderBy("revision.controllerId", "ASC").addOrderBy("revision.revision", "DESC").getMany(),
59452
+ localChannelIds.length ? this.context.dataSource.getRepository(import_plugins_backend_sdk15.ResourceFlowNode).createQueryBuilder("node").where("node.data ->> 'controllerId' IN (:...controllerIds)", { controllerIds: controllerIdsForQuery }).andWhere("node.data ->> 'channelId' IN (:...channelIds)", { channelIds: localChannelIds }).andWhere("node.type = :type", { type: "plugin.wago.command" }).take(1001).getMany() : Promise.resolve([])
59453
+ ]);
59454
+ const controllersById = new Map(controllers.map((controller) => [controller.id, controller]));
59455
+ const appliedByControllerId = new Map(appliedRevisions.map((revision) => [revision.controllerId, revision]));
59456
+ const referencesTruncated = conflictNodes.length > 1e3;
59457
+ const conflictNodesByControllerId = /* @__PURE__ */ new Map();
59458
+ for (const node of conflictNodes.slice(0, 1e3)) {
59459
+ const controllerId = node.data.controllerId;
59460
+ if (typeof controllerId !== "number") continue;
59461
+ const matchingNodes = conflictNodesByControllerId.get(controllerId) ?? [];
59462
+ matchingNodes.push(node);
59463
+ conflictNodesByControllerId.set(controllerId, matchingNodes);
59464
+ }
59465
+ const controllersResult = selectedControllerIds.map((controllerId) => {
59466
+ const controller = controllersById.get(controllerId);
59467
+ if (!controller)
59468
+ return {
59469
+ controllerId,
59470
+ name: `Controller ${controllerId}`,
59471
+ unavailable: true,
59472
+ references: [],
59473
+ referencesTruncated: false
59474
+ };
59475
+ try {
59476
+ const applied = appliedByControllerId.get(controllerId);
59477
+ const appliedSnapshot = applied ? JSON.parse(applied.snapshot) : null;
59478
+ const controllerNodes = localNodes.filter((node) => node.data.controllerId === controllerId);
59479
+ const references = diagnosticReferences(
59480
+ [...controllerNodes, ...conflictNodesByControllerId.get(controllerId) ?? []],
59481
+ appliedSnapshot?.logicalChannels.map((channel) => channel.id) ?? [],
59482
+ applied?.revision ?? null,
59483
+ Object.fromEntries(
59484
+ appliedSnapshot?.logicalChannels.map((channel) => [channel.id, channel.capabilities]) ?? []
59485
+ )
59486
+ ).filter((reference) => reference.resourceId === resourceId);
59487
+ return {
59488
+ controllerId,
59489
+ name: controller.name ?? controller.hardwareId,
59490
+ unavailable: false,
59491
+ references,
59492
+ referencesTruncated
59493
+ };
59494
+ } catch {
59495
+ return {
59496
+ controllerId,
59497
+ name: controller.name ?? controller.hardwareId,
59498
+ unavailable: true,
59499
+ references: [],
59500
+ referencesTruncated: false
59501
+ };
59502
+ }
59503
+ });
59504
+ return {
59505
+ resourceId,
59506
+ controllers: controllersResult,
59507
+ invalidControllerReferences,
59508
+ truncated: nodes.length > 1e3 || controllerIds.size > 20
59509
+ };
59510
+ }
59427
59511
  async get(controllerId) {
59428
59512
  const controller = await this.context.getRepository(WagoController).findOneBy({ id: controllerId });
59429
59513
  if (!controller) throw new import_common11.NotFoundException("WAGO controller not found");
@@ -59464,7 +59548,7 @@ var WagoDiagnosticsService = class {
59464
59548
  stateConnected: runtime.connected ?? null,
59465
59549
  stateHardwareAvailable: runtime.hardwareAvailable ?? null,
59466
59550
  stateSourceAt: runtime.stateSourceAt ?? null,
59467
- sequenceExplanation: runtime.activeStream ? "Gaps are scoped to boot UUID and message category; duplicates and retired streams are ignored." : "Legacy payloads have no source envelope; sequence gaps and source freshness are unavailable.",
59551
+ sequenceExplanation: runtime.activeStream ? "Gaps are scoped to boot stream ID and message category; duplicates and retired streams are ignored." : "Legacy payloads have no source envelope; sequence gaps and source freshness are unavailable.",
59468
59552
  configuration: {
59469
59553
  draftUpdatedAt: draft?.updatedAt ?? null,
59470
59554
  draftChanged: !!draft && (!latest || configurationHash(JSON.parse(draft.snapshot)) !== latest.contentHash || configurationHash(metadataFromProvenance(draft.presetProvenance)) !== configurationHash(metadataFromProvenance(latest.presetProvenance))),
@@ -59513,9 +59597,9 @@ var WagoDiagnosticsService = class {
59513
59597
  limitations: [
59514
59598
  "Hardware readiness remains unknown. Legacy samples have no source time and are never current.",
59515
59599
  "Diagnostics retain at most 256 channels, 50 recent events and 15 minutes in this process.",
59516
- "Resource-page slots are not integrated. Flow links open the affected resource flow; node IDs identify the exact nodes.",
59600
+ "Flow links select the affected node. Diagnostics warnings do not block resource usage; required flow nodes determine gating.",
59517
59601
  "The MQTT SDK exposes neither retained-message flags nor broker connection events. Source age is bounded to 90 seconds; immediate broker-disconnect detection is unavailable.",
59518
- "Stream tracking keeps at most 16 retired boot UUIDs per controller and six category counters, and fails closed when exhausted. Replayed source times never become receipt times."
59602
+ "Stream tracking keeps at most 16 retired boot stream IDs per controller and six category counters, and fails closed when exhausted. Replayed source times never become receipt times."
59519
59603
  ]
59520
59604
  };
59521
59605
  }
@@ -59531,10 +59615,17 @@ var WagoDiagnosticsController = class {
59531
59615
  constructor(diagnostics) {
59532
59616
  __publicField(this, "diagnostics", diagnostics);
59533
59617
  }
59618
+ getResource(id) {
59619
+ return this.diagnostics.getResource(id);
59620
+ }
59534
59621
  get(id) {
59535
59622
  return this.diagnostics.get(id);
59536
59623
  }
59537
59624
  };
59625
+ __decorateClass([
59626
+ (0, import_common12.Get)("resources/:id/diagnostics"),
59627
+ __decorateParam(0, (0, import_common12.Param)("id", import_common12.ParseIntPipe))
59628
+ ], WagoDiagnosticsController.prototype, "getResource", 1);
59538
59629
  __decorateClass([
59539
59630
  (0, import_common12.Get)("controllers/:id/diagnostics"),
59540
59631
  __decorateParam(0, (0, import_common12.Param)("id", import_common12.ParseIntPipe))