@camstack/system 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon/addon-api-factory.d.ts +35 -0
- package/dist/addon-routes/addon-route-registry.d.ts +37 -0
- package/dist/addon-runner.js +599 -0
- package/dist/addon-runner.mjs +597 -0
- package/dist/auth/api-key-manager.d.ts +26 -0
- package/dist/auth/auth-manager.d.ts +109 -0
- package/dist/auth/parse-record.d.ts +18 -0
- package/dist/auth/scope-matcher.d.ts +7 -0
- package/dist/auth/scoped-token-manager.d.ts +40 -0
- package/dist/auth/totp-manager.d.ts +51 -0
- package/dist/auth/user-manager.d.ts +34 -0
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.d.ts +53 -0
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +259 -0
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +251 -0
- package/dist/builtins/addon-pages-aggregator/dedupe-pages.d.ts +6 -0
- package/dist/builtins/addon-pages-aggregator/index.d.ts +1 -0
- package/dist/builtins/addon-pages-aggregator/index.js +8 -0
- package/dist/builtins/addon-pages-aggregator/index.mjs +2 -0
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.d.ts +47 -0
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +228 -0
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +220 -0
- package/dist/builtins/addon-widgets-aggregator/index.d.ts +1 -0
- package/dist/builtins/addon-widgets-aggregator/index.js +8 -0
- package/dist/builtins/addon-widgets-aggregator/index.mjs +2 -0
- package/dist/builtins/alerts/alerts.addon.d.ts +81 -0
- package/dist/builtins/alerts/alerts.addon.js +601 -0
- package/dist/builtins/alerts/alerts.addon.mjs +595 -0
- package/dist/builtins/alerts/index.d.ts +1 -0
- package/dist/builtins/alerts/index.js +4 -0
- package/dist/builtins/alerts/index.mjs +2 -0
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.d.ts +147 -0
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2229 -0
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2220 -0
- package/dist/builtins/backup-orchestrator/cron-helpers.d.ts +23 -0
- package/dist/builtins/backup-orchestrator/destination-policy.d.ts +72 -0
- package/dist/builtins/backup-orchestrator/download-helpers.d.ts +12 -0
- package/dist/builtins/backup-orchestrator/index.d.ts +2 -0
- package/dist/builtins/backup-orchestrator/index.js +8 -0
- package/dist/builtins/backup-orchestrator/index.mjs +2 -0
- package/dist/builtins/backup-orchestrator/manifest-store.d.ts +77 -0
- package/dist/builtins/console-logging/console-destination.d.ts +13 -0
- package/dist/builtins/console-logging/console-logging.addon.d.ts +25 -0
- package/dist/builtins/console-logging/index.d.ts +3 -0
- package/dist/builtins/console-logging/index.js +104 -0
- package/dist/builtins/console-logging/index.mjs +95 -0
- package/dist/builtins/device-manager/device-config-contribution.d.ts +32 -0
- package/dist/builtins/device-manager/device-event-propagator.d.ts +26 -0
- package/dist/builtins/device-manager/device-link-overlay.d.ts +23 -0
- package/dist/builtins/device-manager/device-link-resolver.d.ts +15 -0
- package/dist/builtins/device-manager/device-manager.addon.d.ts +452 -0
- package/dist/builtins/device-manager/device-manager.addon.js +3299 -0
- package/dist/builtins/device-manager/device-manager.addon.mjs +3292 -0
- package/dist/builtins/device-manager/index.d.ts +2 -0
- package/dist/builtins/device-manager/index.js +8 -0
- package/dist/builtins/device-manager/index.mjs +2 -0
- package/dist/builtins/hub-forwarder/hub-forwarder-destination.d.ts +44 -0
- package/dist/builtins/hub-forwarder/hub-forwarder.addon.d.ts +15 -0
- package/dist/builtins/hub-forwarder/index.d.ts +3 -0
- package/dist/builtins/hub-forwarder/index.js +154 -0
- package/dist/builtins/hub-forwarder/index.mjs +145 -0
- package/dist/builtins/local-auth/auth-schema.d.ts +26 -0
- package/dist/builtins/local-auth/index.d.ts +1 -0
- package/dist/builtins/local-auth/index.js +4 -0
- package/dist/builtins/local-auth/index.mjs +2 -0
- package/dist/builtins/local-auth/local-auth.addon.d.ts +18 -0
- package/dist/builtins/local-auth/local-auth.addon.js +8094 -0
- package/dist/builtins/local-auth/local-auth.addon.mjs +8063 -0
- package/dist/builtins/local-auth/oauth-grants.d.ts +45 -0
- package/dist/builtins/local-auth/oauth-session-manager.d.ts +50 -0
- package/dist/builtins/local-network/index.d.ts +2 -0
- package/dist/builtins/local-network/index.js +10 -0
- package/dist/builtins/local-network/index.mjs +2 -0
- package/dist/builtins/local-network/local-network.addon.d.ts +150 -0
- package/dist/builtins/local-network/local-network.addon.js +489 -0
- package/dist/builtins/local-network/local-network.addon.mjs +477 -0
- package/dist/builtins/native-metrics/index.d.ts +2 -0
- package/dist/builtins/native-metrics/native-metrics-provider.d.ts +48 -0
- package/dist/builtins/native-metrics/native-metrics.addon.d.ts +73 -0
- package/dist/builtins/native-metrics/native-metrics.addon.js +922 -0
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +914 -0
- package/dist/builtins/platform-probe/hardware-decode-accel-probe.d.ts +37 -0
- package/dist/builtins/platform-probe/hardware-encoder-probe.d.ts +13 -0
- package/dist/builtins/platform-probe/index.d.ts +22 -0
- package/dist/builtins/platform-probe/index.js +834 -0
- package/dist/builtins/platform-probe/index.mjs +822 -0
- package/dist/builtins/platform-probe/inference-config-resolver.d.ts +29 -0
- package/dist/builtins/platform-probe/intel-accelerators.d.ts +11 -0
- package/dist/builtins/platform-probe/platform-scorer.d.ts +30 -0
- package/dist/builtins/platform-probe/runtime-packages.d.ts +6 -0
- package/dist/builtins/remote-access-orchestrator/enabled-providers-reconcile.d.ts +96 -0
- package/dist/builtins/remote-access-orchestrator/index.d.ts +1 -0
- package/dist/builtins/remote-access-orchestrator/index.js +8 -0
- package/dist/builtins/remote-access-orchestrator/index.mjs +2 -0
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.d.ts +40 -0
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +214 -0
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +208 -0
- package/dist/builtins/shared/settle-sources.d.ts +22 -0
- package/dist/builtins/snapshot/index.d.ts +2 -0
- package/dist/builtins/snapshot/index.js +494 -0
- package/dist/builtins/snapshot/index.mjs +488 -0
- package/dist/builtins/snapshot/snapshot.addon.d.ts +120 -0
- package/dist/builtins/sqlite-storage/config-store.d.ts +8 -0
- package/dist/builtins/sqlite-storage/device-store.d.ts +23 -0
- package/dist/builtins/sqlite-storage/filesystem-browse-provider.d.ts +25 -0
- package/dist/builtins/sqlite-storage/filesystem-storage-provider.d.ts +83 -0
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.d.ts +32 -0
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +396 -0
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +388 -0
- package/dist/builtins/sqlite-storage/index.d.ts +8 -0
- package/dist/builtins/sqlite-storage/index.js +62 -0
- package/dist/builtins/sqlite-storage/index.mjs +49 -0
- package/dist/builtins/sqlite-storage/integration-registry.d.ts +27 -0
- package/dist/builtins/sqlite-storage/path-guard.d.ts +4 -0
- package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +102 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts +14 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +644 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +636 -0
- package/dist/builtins/storage-orchestrator/index.d.ts +6 -0
- package/dist/builtins/storage-orchestrator/index.js +10 -0
- package/dist/builtins/storage-orchestrator/index.mjs +2 -0
- package/dist/builtins/storage-orchestrator/location-store.d.ts +49 -0
- package/dist/builtins/storage-orchestrator/provider-discovery.d.ts +10 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +103 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1138 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1128 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts +236 -0
- package/dist/builtins/storage-orchestrator/storage-pressure-manager.d.ts +38 -0
- package/dist/builtins/system-backup/system-backup.service.d.ts +137 -0
- package/dist/builtins/system-config/index.d.ts +1 -0
- package/dist/builtins/system-config/index.js +8 -0
- package/dist/builtins/system-config/index.mjs +2 -0
- package/dist/builtins/system-config/system-config.addon.d.ts +10 -0
- package/dist/builtins/system-config/system-config.addon.js +232 -0
- package/dist/builtins/system-config/system-config.addon.mjs +226 -0
- package/dist/builtins/winston-logging/index.d.ts +3 -0
- package/dist/builtins/winston-logging/index.js +156 -0
- package/dist/builtins/winston-logging/index.mjs +144 -0
- package/dist/builtins/winston-logging/winston-destination.d.ts +21 -0
- package/dist/builtins/winston-logging/winston-logging.addon.d.ts +19 -0
- package/dist/chunk-CNf5ZN-e.mjs +37 -0
- package/dist/chunk-Cek0wNdY.js +64 -0
- package/dist/download/model-download-service.d.ts +41 -0
- package/dist/download/model-downloader.d.ts +31 -0
- package/dist/events/event-bus.d.ts +10 -0
- package/dist/events/system-event-bus.d.ts +14 -0
- package/dist/feature/feature-manager.d.ts +11 -0
- package/dist/formatter-B7qW8bPJ.mjs +162 -0
- package/dist/formatter-DqAKDlvN.js +167 -0
- package/dist/http/authenticated-file-server.d.ts +53 -0
- package/dist/http/data-plane-registry.d.ts +23 -0
- package/dist/http/file-data-plane.d.ts +10 -0
- package/dist/http/reverse-proxy.d.ts +15 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.js +93485 -0
- package/dist/index.mjs +93179 -0
- package/dist/intel-accelerators-Gg0P5mnl.js +20 -0
- package/dist/intel-accelerators-hGgpZ0pX.mjs +19 -0
- package/dist/kernel/addon-class-resolver.d.ts +4 -0
- package/dist/kernel/addon-engine-manager.d.ts +22 -0
- package/dist/kernel/addon-health-monitor.d.ts +154 -0
- package/dist/kernel/addon-installer.d.ts +208 -0
- package/dist/kernel/addon-loader.d.ts +106 -0
- package/dist/kernel/addon-manifest.d.ts +77 -0
- package/dist/kernel/capability-handle.d.ts +46 -0
- package/dist/kernel/capability-registry.d.ts +412 -0
- package/dist/kernel/config-manager.d.ts +212 -0
- package/dist/kernel/config-schema.d.ts +93 -0
- package/dist/kernel/custom-action-registry.d.ts +23 -0
- package/dist/kernel/deps/addon-deps-manager.d.ts +19 -0
- package/dist/kernel/deps/manifest-native-deps.d.ts +25 -0
- package/dist/kernel/deps/manifest-python-deps.d.ts +20 -0
- package/dist/kernel/device-registry.d.ts +29 -0
- package/dist/kernel/fs-utils.d.ts +41 -0
- package/dist/kernel/hwaccel/hwaccel-resolver.d.ts +19 -0
- package/dist/kernel/hwaccel/hwaccel-service.d.ts +4 -0
- package/dist/kernel/index.d.ts +74 -0
- package/dist/kernel/infra-capabilities.d.ts +13 -0
- package/dist/kernel/moleculer/addon-context-factory.d.ts +91 -0
- package/dist/kernel/moleculer/addon-data-plane-facility.d.ts +19 -0
- package/dist/kernel/moleculer/addon-runner.d.ts +1 -0
- package/dist/kernel/moleculer/addon-service-factory.d.ts +50 -0
- package/dist/kernel/moleculer/broker-factory.d.ts +50 -0
- package/dist/kernel/moleculer/cap-usage-registry.d.ts +46 -0
- package/dist/kernel/moleculer/capabilities-access.d.ts +21 -0
- package/dist/kernel/moleculer/child-addon-call-dispatch.d.ts +46 -0
- package/dist/kernel/moleculer/child-cap-dispatch.d.ts +20 -0
- package/dist/kernel/moleculer/cluster-secret.d.ts +15 -0
- package/dist/kernel/moleculer/core-cap-service.d.ts +50 -0
- package/dist/kernel/moleculer/crash-supervisor.d.ts +50 -0
- package/dist/kernel/moleculer/device-cap-proxy.d.ts +79 -0
- package/dist/kernel/moleculer/event-bus-core.d.ts +53 -0
- package/dist/kernel/moleculer/event-bus.d.ts +53 -0
- package/dist/kernel/moleculer/hub-log-forwarder.d.ts +36 -0
- package/dist/kernel/moleculer/hub-service.d.ts +35 -0
- package/dist/kernel/moleculer/node-registry.d.ts +126 -0
- package/dist/kernel/moleculer/process-context.d.ts +4 -0
- package/dist/kernel/moleculer/process-service.d.ts +72 -0
- package/dist/kernel/moleculer/provider-registry.d.ts +28 -0
- package/dist/kernel/moleculer/readiness-context.d.ts +62 -0
- package/dist/kernel/moleculer/readiness-service.d.ts +7 -0
- package/dist/kernel/moleculer/register-node-client.d.ts +35 -0
- package/dist/kernel/moleculer/remote-logger.d.ts +43 -0
- package/dist/kernel/moleculer/resilient-cap-call.d.ts +28 -0
- package/dist/kernel/moleculer/stream-probe-service.d.ts +9 -0
- package/dist/kernel/moleculer/trpc-links.d.ts +189 -0
- package/dist/kernel/moleculer/typed-array-serde.d.ts +25 -0
- package/dist/kernel/moleculer/worker-device-restore.d.ts +10 -0
- package/dist/kernel/provider-kind-drift.d.ts +12 -0
- package/dist/kernel/restart-coordinator.d.ts +90 -0
- package/dist/kernel/storage-location-registry.d.ts +40 -0
- package/dist/kernel/transport/cap-action-name.d.ts +100 -0
- package/dist/kernel/transport/cap-route-resolver.d.ts +148 -0
- package/dist/kernel/transport/cap-route.d.ts +148 -0
- package/dist/kernel/transport/child-cap-protocol.d.ts +136 -0
- package/dist/kernel/transport/create-local-transport.d.ts +7 -0
- package/dist/kernel/transport/frame-codec.d.ts +7 -0
- package/dist/kernel/transport/index.d.ts +27 -0
- package/dist/kernel/transport/local-child-client.d.ts +136 -0
- package/dist/kernel/transport/local-child-registry.d.ts +179 -0
- package/dist/kernel/transport/local-endpoint-path.d.ts +6 -0
- package/dist/kernel/transport/local-transport.d.ts +46 -0
- package/dist/kernel/transport/parent-unowned-call.d.ts +75 -0
- package/dist/kernel/transport/socket-channel.d.ts +27 -0
- package/dist/kernel/transport/uds-event-bridge.d.ts +36 -0
- package/dist/kernel/transport/uds-event-bus.d.ts +22 -0
- package/dist/kernel/transport/uds-local-transport.d.ts +18 -0
- package/dist/kernel/transport/uds-log-ingest.d.ts +28 -0
- package/dist/kernel/transport/uds-logger.d.ts +44 -0
- package/dist/kernel/utils/ring-buffer.d.ts +15 -0
- package/dist/kernel/workspace-detect.d.ts +9 -0
- package/dist/lifecycle/lifecycle-state-machine.d.ts +28 -0
- package/dist/logging/formatter.d.ts +30 -0
- package/dist/logging/log-manager.d.ts +54 -0
- package/dist/logging/log-ring-buffer.d.ts +47 -0
- package/dist/logging/partitioned-log-buffer.d.ts +35 -0
- package/dist/logging/scoped-logger.d.ts +17 -0
- package/dist/main-DNnMW7Z2.js +9983 -0
- package/dist/main-rtjOwPBR.mjs +9976 -0
- package/dist/manifest-python-deps-D1DbAQEv.js +6724 -0
- package/dist/manifest-python-deps-DZsKTbs1.mjs +6315 -0
- package/dist/network/network-quality.d.ts +11 -0
- package/dist/notification/notification-service.d.ts +37 -0
- package/dist/notification/toast-service.d.ts +22 -0
- package/dist/pipeline/engine-manager-resolver.d.ts +15 -0
- package/dist/pipeline/pipeline-runner.d.ts +8 -0
- package/dist/pipeline/pipeline-validator.d.ts +13 -0
- package/dist/process/resource-monitor.d.ts +11 -0
- package/dist/python/python-env-manager.d.ts +12 -0
- package/dist/repl/interfaces.d.ts +31 -0
- package/dist/repl/repl-engine.d.ts +8 -0
- package/dist/resource-monitor-ClDGFyf6.mjs +57 -0
- package/dist/resource-monitor-IIEanuJt.js +74 -0
- package/dist/settle-sources-Bhsy57y-.js +38 -0
- package/dist/settle-sources-CDtNC8ub.mjs +33 -0
- package/dist/storage/fs-storage-backend.d.ts +40 -0
- package/dist/storage/storage-location-manager.d.ts +23 -0
- package/dist/storage/storage-manager.d.ts +83 -0
- package/dist/tar-BgAEMRBR.js +5434 -0
- package/dist/tar-ByMOPNM0.mjs +5429 -0
- package/dist/tls/cert-manager.d.ts +26 -0
- package/dist/tls/index.d.ts +1 -0
- package/package.json +343 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path$1 from "node:path";
|
|
4
|
+
import { BaseAddon, EventCategory, addonPagesCapability, errMsg } from "@camstack/types";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
//#region src/builtins/addon-pages-aggregator/dedupe-pages.ts
|
|
7
|
+
/** Stable identity for an addon page, independent of which node reported it. */
|
|
8
|
+
function pageKey(info) {
|
|
9
|
+
return `${info.addonId}::${info.page.id}`;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Return a new list with duplicate `(addonId, page.id)` entries removed,
|
|
13
|
+
* preserving first-seen order. Pure — never mutates the input.
|
|
14
|
+
*/
|
|
15
|
+
function dedupePages(pages) {
|
|
16
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17
|
+
const out = [];
|
|
18
|
+
for (const info of pages) {
|
|
19
|
+
const key = pageKey(info);
|
|
20
|
+
if (seen.has(key)) continue;
|
|
21
|
+
seen.add(key);
|
|
22
|
+
out.push(info);
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.ts
|
|
28
|
+
/**
|
|
29
|
+
* Addon Pages Aggregator — hub-local builtin that owns the singleton
|
|
30
|
+
* `addon-pages` cap.
|
|
31
|
+
*
|
|
32
|
+
* Walks every registered `addon-pages-source` (collection) provider and
|
|
33
|
+
* emits an enriched `AddonPageInfo[]` list with versioned `bundleUrl`s
|
|
34
|
+
* pointing at `/api/addon-pages/<addonId>/<bundle>?v=<mtime>`. The
|
|
35
|
+
* filesystem `mtime` cache-buster lets the browser pick up addon
|
|
36
|
+
* rebuilds without manual reload — same scheme the original
|
|
37
|
+
* hand-written `AddonPagesService.listPages` used before this split.
|
|
38
|
+
*
|
|
39
|
+
* The static file endpoint (`/api/addon-pages/:addonId/*`) is still
|
|
40
|
+
* served by `AddonPagesService.resolveBundle()` on the server side; this
|
|
41
|
+
* addon only owns the listing surface.
|
|
42
|
+
*
|
|
43
|
+
* Why a builtin rather than a cap-providers helper:
|
|
44
|
+
* - The aggregator is conceptually the "addon-pages provider" — addons
|
|
45
|
+
* own caps, not the server. Living in `@camstack/system/builtins` keeps
|
|
46
|
+
* the surface symmetrical with `system-config`, `local-auth`, etc.
|
|
47
|
+
* - Lets the aggregator subscribe to source readiness without piping a
|
|
48
|
+
* CapabilityRegistry handle through `RouterServices`.
|
|
49
|
+
*/
|
|
50
|
+
/**
|
|
51
|
+
* Backoff schedule (ms) used to retry sources that failed during a
|
|
52
|
+
* `listPages()` round-trip — typically because the cap was just
|
|
53
|
+
* registered (provider connected via Moleculer) but the worker-side
|
|
54
|
+
* action registration hadn't propagated yet, so `Service '...listPages'
|
|
55
|
+
* is not found on '<node>'` raced ahead of the call.
|
|
56
|
+
*
|
|
57
|
+
* We schedule one retry per entry; if every retry fails we stop
|
|
58
|
+
* (the next aggregate() run will pick the source up again). On
|
|
59
|
+
* success we re-emit `AddonPageReady` so admin-ui invalidates its
|
|
60
|
+
* `addonPages.listPages` query and the sidebar populates without a
|
|
61
|
+
* page reload.
|
|
62
|
+
*/
|
|
63
|
+
var RETRY_BACKOFF_MS = [
|
|
64
|
+
500,
|
|
65
|
+
1500,
|
|
66
|
+
4e3
|
|
67
|
+
];
|
|
68
|
+
/**
|
|
69
|
+
* Per-source ceiling for a single `listPages()` round-trip. A healthy
|
|
70
|
+
* source answers in well under this (hub-local UDS <100ms, a reachable
|
|
71
|
+
* remote agent <1s). A source that's unreachable — e.g. an agent-hosted
|
|
72
|
+
* addon whose cap routes through a Moleculer `waitForServices` that waits
|
|
73
|
+
* its full 30s discovery timeout for a node that will never appear — must
|
|
74
|
+
* NOT block the whole listing: we time it out fast, serve its cached
|
|
75
|
+
* snapshot, and let the background retry recover it. Combined with the
|
|
76
|
+
* parallel fan-out below, one slow/dead source can no longer stall the
|
|
77
|
+
* sidebar query (which otherwise hangs long enough to trip the WS
|
|
78
|
+
* keepalive → "server unreachable").
|
|
79
|
+
*/
|
|
80
|
+
var PER_SOURCE_TIMEOUT_MS = 2500;
|
|
81
|
+
var AddonPagesAggregatorAddon = class extends BaseAddon {
|
|
82
|
+
id = "addon-pages-aggregator";
|
|
83
|
+
resolvedPaths = null;
|
|
84
|
+
/**
|
|
85
|
+
* Last successful `listPages()` snapshot per source. Used as the
|
|
86
|
+
* "stale-but-valid" fallback when a source transiently fails — drops
|
|
87
|
+
* happen often enough during boot (Moleculer service-discovery
|
|
88
|
+
* window) that swallowing the error and returning empty leaves the
|
|
89
|
+
* sidebar with nothing for several seconds. Keeping the previous
|
|
90
|
+
* good entry means a flake is invisible to the operator.
|
|
91
|
+
*/
|
|
92
|
+
lastGood = /* @__PURE__ */ new Map();
|
|
93
|
+
/** In-flight retry guards keyed by sourceId. Avoids double-scheduling. */
|
|
94
|
+
retryTimers = /* @__PURE__ */ new Map();
|
|
95
|
+
constructor() {
|
|
96
|
+
super({});
|
|
97
|
+
}
|
|
98
|
+
async onInitialize() {
|
|
99
|
+
this.resolvedPaths = await this.resolvePaths();
|
|
100
|
+
const provider = { listPages: async () => this.aggregate() };
|
|
101
|
+
this.ctx.logger.info("Initialized — aggregating addon-pages-source providers");
|
|
102
|
+
return [{
|
|
103
|
+
capability: addonPagesCapability,
|
|
104
|
+
provider
|
|
105
|
+
}];
|
|
106
|
+
}
|
|
107
|
+
async onShutdown() {
|
|
108
|
+
for (const t of this.retryTimers.values()) clearTimeout(t);
|
|
109
|
+
this.retryTimers.clear();
|
|
110
|
+
this.lastGood.clear();
|
|
111
|
+
}
|
|
112
|
+
async aggregate() {
|
|
113
|
+
const sources = this.capabilities?.getCollection("addon-pages-source") ?? [];
|
|
114
|
+
const out = [];
|
|
115
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
116
|
+
const settled = await settleSourcesWithTimeout(sources.map((source) => [source.id, () => Promise.resolve(source.listPages())]), PER_SOURCE_TIMEOUT_MS);
|
|
117
|
+
for (const [sourceId, result] of settled) {
|
|
118
|
+
seenIds.add(sourceId);
|
|
119
|
+
if (result.ok) {
|
|
120
|
+
const enriched = result.value.map((page) => ({
|
|
121
|
+
addonId: sourceId,
|
|
122
|
+
page,
|
|
123
|
+
bundleUrl: this.makeBundleUrl(sourceId, page.bundle)
|
|
124
|
+
}));
|
|
125
|
+
for (const item of enriched) out.push(item);
|
|
126
|
+
this.lastGood.set(sourceId, enriched);
|
|
127
|
+
} else {
|
|
128
|
+
this.ctx.logger.warn("addon-pages-source provider failed", { meta: {
|
|
129
|
+
sourceId,
|
|
130
|
+
error: result.error.message
|
|
131
|
+
} });
|
|
132
|
+
const cached = this.lastGood.get(sourceId);
|
|
133
|
+
if (cached !== void 0) {
|
|
134
|
+
for (const item of cached) out.push(item);
|
|
135
|
+
this.ctx.logger.info("addon-pages-source falling back to cached snapshot", { meta: {
|
|
136
|
+
sourceId,
|
|
137
|
+
cachedPages: cached.length
|
|
138
|
+
} });
|
|
139
|
+
}
|
|
140
|
+
this.scheduleRetry(sourceId);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
for (const cachedId of this.lastGood.keys()) if (!seenIds.has(cachedId)) this.lastGood.delete(cachedId);
|
|
144
|
+
return dedupePages(out);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Schedule background re-call of `listPages()` on a source that just
|
|
148
|
+
* failed. Walks `RETRY_BACKOFF_MS` from index 0 — each successful
|
|
149
|
+
* call updates the cache and emits `AddonPageReady` so the admin UI
|
|
150
|
+
* invalidates its query. Stops on first success or after the schedule
|
|
151
|
+
* is exhausted.
|
|
152
|
+
*/
|
|
153
|
+
scheduleRetry(sourceId, attempt = 0) {
|
|
154
|
+
if (attempt >= RETRY_BACKOFF_MS.length) return;
|
|
155
|
+
if (this.retryTimers.has(sourceId)) return;
|
|
156
|
+
const delayMs = RETRY_BACKOFF_MS[attempt] ?? RETRY_BACKOFF_MS[RETRY_BACKOFF_MS.length - 1];
|
|
157
|
+
const timer = setTimeout(() => {
|
|
158
|
+
this.retryTimers.delete(sourceId);
|
|
159
|
+
this.retrySource(sourceId, attempt);
|
|
160
|
+
}, delayMs);
|
|
161
|
+
this.retryTimers.set(sourceId, timer);
|
|
162
|
+
}
|
|
163
|
+
async retrySource(sourceId, attempt) {
|
|
164
|
+
const source = (this.capabilities?.getCollection("addon-pages-source") ?? []).find((s) => s.id === sourceId);
|
|
165
|
+
if (!source) return;
|
|
166
|
+
try {
|
|
167
|
+
const enriched = (await Promise.resolve(source.listPages())).map((page) => ({
|
|
168
|
+
addonId: source.id,
|
|
169
|
+
page,
|
|
170
|
+
bundleUrl: this.makeBundleUrl(source.id, page.bundle)
|
|
171
|
+
}));
|
|
172
|
+
this.lastGood.set(source.id, enriched);
|
|
173
|
+
this.ctx.logger.info("addon-pages-source recovered after retry", { meta: {
|
|
174
|
+
sourceId,
|
|
175
|
+
attempt: attempt + 1,
|
|
176
|
+
pages: enriched.length
|
|
177
|
+
} });
|
|
178
|
+
this.ctx.eventBus.emit({
|
|
179
|
+
id: randomUUID(),
|
|
180
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
181
|
+
source: {
|
|
182
|
+
type: "addon",
|
|
183
|
+
id: this.id
|
|
184
|
+
},
|
|
185
|
+
category: EventCategory.AddonPageReady,
|
|
186
|
+
data: {
|
|
187
|
+
addonId: sourceId,
|
|
188
|
+
recovered: true
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
} catch (err) {
|
|
192
|
+
this.ctx.logger.debug("addon-pages-source retry failed", { meta: {
|
|
193
|
+
sourceId,
|
|
194
|
+
attempt: attempt + 1,
|
|
195
|
+
error: errMsg(err)
|
|
196
|
+
} });
|
|
197
|
+
this.scheduleRetry(sourceId, attempt + 1);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Build `/api/addon-pages/<addonId>/<bundle>?v=<mtime>`. Falls back
|
|
202
|
+
* to `Date.now()` when the bundle path can't be stat'd (remote addon
|
|
203
|
+
* with no local file, addon not yet on disk, etc.) — the browser
|
|
204
|
+
* just gets a fresh URL on each call instead of cache-friendly mtime.
|
|
205
|
+
*/
|
|
206
|
+
makeBundleUrl(addonId, bundle) {
|
|
207
|
+
const bundlePath = this.resolveBundlePath(addonId, bundle);
|
|
208
|
+
let mtime = Date.now();
|
|
209
|
+
if (bundlePath !== null) try {
|
|
210
|
+
mtime = fs.statSync(bundlePath).mtimeMs;
|
|
211
|
+
} catch {}
|
|
212
|
+
return `/api/addon-pages/${addonId}/${bundle}?v=${Math.floor(mtime)}`;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Resolve the local filesystem path for an addon's bundle. Mirrors
|
|
216
|
+
* `AddonPagesService.resolveBundle` (server-side), but without the
|
|
217
|
+
* existence / traversal checks — those still live on the server's
|
|
218
|
+
* static file route. We only need a stat-able path here for the
|
|
219
|
+
* `mtime` cache-buster.
|
|
220
|
+
*/
|
|
221
|
+
resolveBundlePath(addonId, bundle) {
|
|
222
|
+
const paths = this.resolvedPaths;
|
|
223
|
+
if (!paths) return null;
|
|
224
|
+
const addonDistPath = path$1.join(paths.addonsDir, "@camstack", `addon-${addonId}`, "dist");
|
|
225
|
+
const resolvedBase = path$1.resolve(addonDistPath);
|
|
226
|
+
const resolvedFile = path$1.resolve(addonDistPath, bundle);
|
|
227
|
+
if (!resolvedFile.startsWith(resolvedBase + path$1.sep) && resolvedFile !== resolvedBase) return null;
|
|
228
|
+
return resolvedFile;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Read `server.dataPath` from the cluster yml-backed sections (same
|
|
232
|
+
* source the server uses at boot), then derive the addons directory
|
|
233
|
+
* from it. Falls back to `camstack-data/addons` when no settings API
|
|
234
|
+
* is available — agents and isolated tests don't see this addon, so
|
|
235
|
+
* the fallback is purely defensive.
|
|
236
|
+
*/
|
|
237
|
+
async resolvePaths() {
|
|
238
|
+
const fallback = { addonsDir: path$1.resolve("camstack-data", "addons") };
|
|
239
|
+
if (!this.ctx.settings) return fallback;
|
|
240
|
+
try {
|
|
241
|
+
const server = await this.ctx.settings.getSection("server");
|
|
242
|
+
const dataPath = typeof server["dataPath"] === "string" && server["dataPath"] ? server["dataPath"] : "camstack-data";
|
|
243
|
+
return { addonsDir: path$1.resolve(dataPath, "addons") };
|
|
244
|
+
} catch (err) {
|
|
245
|
+
this.ctx.logger.debug("Failed to read server.dataPath — falling back", { meta: { error: errMsg(err) } });
|
|
246
|
+
return fallback;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
//#endregion
|
|
251
|
+
export { AddonPagesAggregatorAddon, AddonPagesAggregatorAddon as default };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { AddonPageInfo } from '@camstack/types';
|
|
2
|
+
/**
|
|
3
|
+
* Return a new list with duplicate `(addonId, page.id)` entries removed,
|
|
4
|
+
* preserving first-seen order. Pure — never mutates the input.
|
|
5
|
+
*/
|
|
6
|
+
export declare function dedupePages(pages: readonly AddonPageInfo[]): readonly AddonPageInfo[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AddonPagesAggregatorAddon, default } from './addon-pages-aggregator.addon.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
require("../../chunk-Cek0wNdY.js");
|
|
6
|
+
const require_builtins_addon_pages_aggregator_addon_pages_aggregator_addon = require("./addon-pages-aggregator.addon.js");
|
|
7
|
+
exports.AddonPagesAggregatorAddon = require_builtins_addon_pages_aggregator_addon_pages_aggregator_addon.AddonPagesAggregatorAddon;
|
|
8
|
+
exports.default = require_builtins_addon_pages_aggregator_addon_pages_aggregator_addon.AddonPagesAggregatorAddon;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { BaseAddon, ProviderRegistration } from '@camstack/types';
|
|
2
|
+
export declare class AddonWidgetsAggregatorAddon extends BaseAddon {
|
|
3
|
+
readonly id = "addon-widgets-aggregator";
|
|
4
|
+
private resolvedPaths;
|
|
5
|
+
/**
|
|
6
|
+
* Last successful `listWidgets()` snapshot per source. Used as the
|
|
7
|
+
* "stale-but-valid" fallback when a source transiently fails — drops
|
|
8
|
+
* happen often enough during boot (Moleculer service-discovery
|
|
9
|
+
* window) that swallowing the error and returning empty would leave
|
|
10
|
+
* the dashboard with nothing for several seconds. Keeping the
|
|
11
|
+
* previous good entry means a flake is invisible to the operator.
|
|
12
|
+
*/
|
|
13
|
+
private readonly lastGood;
|
|
14
|
+
/** In-flight retry guards keyed by sourceAddonId. Avoids double-scheduling. */
|
|
15
|
+
private readonly retryTimers;
|
|
16
|
+
constructor();
|
|
17
|
+
protected onInitialize(): Promise<ProviderRegistration[]>;
|
|
18
|
+
protected onShutdown(): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Strip the `@<nodeId>` suffix that the CapabilityBridge appends to
|
|
21
|
+
* collection-provider registry keys for cross-node addons (see
|
|
22
|
+
* `moleculer.service.ts` — `registryKey = ${addonId}@${nodeId}`).
|
|
23
|
+
*
|
|
24
|
+
* The widget bundle is hub-resident (the same npm package ships to
|
|
25
|
+
* every node and the hub keeps a copy on disk keyed by the bare
|
|
26
|
+
* manifest id), so both the static-file URL and the admin-ui widget
|
|
27
|
+
* namespace must use the bare addon id. Without this, a widget
|
|
28
|
+
* source running on a remote agent yields a `bundleUrl` like
|
|
29
|
+
* `/api/addon-widgets/pipeline-analytics@agent-0/pipeline-analytics/remoteEntry.js`
|
|
30
|
+
* — the embedded `@<node>/<group>` makes the static-file route's
|
|
31
|
+
* `:addonId` param mismatch the registered provider and 404.
|
|
32
|
+
*/
|
|
33
|
+
private bareAddonId;
|
|
34
|
+
private aggregate;
|
|
35
|
+
private scheduleRetry;
|
|
36
|
+
private retrySource;
|
|
37
|
+
/**
|
|
38
|
+
* Build `/api/addon-widgets/<addonId>/<bundle>?v=<mtime>`. Falls back
|
|
39
|
+
* to `Date.now()` when the bundle path can't be stat'd (remote addon
|
|
40
|
+
* with no local file, addon not yet on disk, etc.) — the browser
|
|
41
|
+
* just gets a fresh URL on each call instead of cache-friendly mtime.
|
|
42
|
+
*/
|
|
43
|
+
private makeBundleUrl;
|
|
44
|
+
private resolveBundlePath;
|
|
45
|
+
private resolvePaths;
|
|
46
|
+
}
|
|
47
|
+
export default AddonWidgetsAggregatorAddon;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
+
const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
|
|
7
|
+
let node_fs = require("node:fs");
|
|
8
|
+
node_fs = require_chunk.__toESM(node_fs);
|
|
9
|
+
let node_path = require("node:path");
|
|
10
|
+
node_path = require_chunk.__toESM(node_path);
|
|
11
|
+
let _camstack_types = require("@camstack/types");
|
|
12
|
+
let node_crypto = require("node:crypto");
|
|
13
|
+
//#region src/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.ts
|
|
14
|
+
/**
|
|
15
|
+
* Addon Widgets Aggregator — hub-local builtin that owns the singleton
|
|
16
|
+
* `addon-widgets` cap.
|
|
17
|
+
*
|
|
18
|
+
* Mirrors `addon-pages-aggregator` exactly: walks every registered
|
|
19
|
+
* `addon-widgets-source` (collection) provider and emits an enriched
|
|
20
|
+
* widget metadata list with versioned `bundleUrl`s pointing at
|
|
21
|
+
* `/api/addon-widgets/<addonId>/<bundle>?v=<mtime>`. The filesystem
|
|
22
|
+
* `mtime` cache-buster lets the browser pick up addon rebuilds without
|
|
23
|
+
* manual reload.
|
|
24
|
+
*
|
|
25
|
+
* The static file endpoint (`/api/addon-widgets/:addonId/*`) is served
|
|
26
|
+
* by `AddonWidgetsService.resolveBundle()` on the server side; this
|
|
27
|
+
* addon only owns the listing surface.
|
|
28
|
+
*
|
|
29
|
+
* Why a builtin: same reasoning as `addon-pages-aggregator`. The
|
|
30
|
+
* aggregator is the de-facto "addon-widgets provider" — addons own caps,
|
|
31
|
+
* not the server. Living in `@camstack/system/builtins` keeps the surface
|
|
32
|
+
* symmetrical with `system-config`, `local-auth`, etc.
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Backoff schedule (ms) used to retry sources that failed during a
|
|
36
|
+
* `listWidgets()` round-trip — typically because the cap was just
|
|
37
|
+
* registered (provider connected via Moleculer) but the worker-side
|
|
38
|
+
* action registration hadn't propagated yet, so `Service '...listWidgets'
|
|
39
|
+
* is not found on '<node>'` raced ahead of the call.
|
|
40
|
+
*
|
|
41
|
+
* On success we re-emit `AddonWidgetReady` so admin-ui invalidates its
|
|
42
|
+
* `addonWidgets.listWidgets` query and the registry populates without a
|
|
43
|
+
* page reload.
|
|
44
|
+
*/
|
|
45
|
+
var RETRY_BACKOFF_MS = [
|
|
46
|
+
500,
|
|
47
|
+
1500,
|
|
48
|
+
4e3
|
|
49
|
+
];
|
|
50
|
+
/**
|
|
51
|
+
* Per-source ceiling for a single `listWidgets()` round-trip. Mirrors the
|
|
52
|
+
* addon-pages aggregator: a slow/unreachable source (e.g. an agent-hosted
|
|
53
|
+
* widget source whose cap call sits in a Moleculer `waitForServices`) times
|
|
54
|
+
* out fast and serves its cached snapshot instead of blocking the dashboard.
|
|
55
|
+
*/
|
|
56
|
+
var PER_SOURCE_TIMEOUT_MS = 2500;
|
|
57
|
+
var AddonWidgetsAggregatorAddon = class extends _camstack_types.BaseAddon {
|
|
58
|
+
id = "addon-widgets-aggregator";
|
|
59
|
+
resolvedPaths = null;
|
|
60
|
+
/**
|
|
61
|
+
* Last successful `listWidgets()` snapshot per source. Used as the
|
|
62
|
+
* "stale-but-valid" fallback when a source transiently fails — drops
|
|
63
|
+
* happen often enough during boot (Moleculer service-discovery
|
|
64
|
+
* window) that swallowing the error and returning empty would leave
|
|
65
|
+
* the dashboard with nothing for several seconds. Keeping the
|
|
66
|
+
* previous good entry means a flake is invisible to the operator.
|
|
67
|
+
*/
|
|
68
|
+
lastGood = /* @__PURE__ */ new Map();
|
|
69
|
+
/** In-flight retry guards keyed by sourceAddonId. Avoids double-scheduling. */
|
|
70
|
+
retryTimers = /* @__PURE__ */ new Map();
|
|
71
|
+
constructor() {
|
|
72
|
+
super({});
|
|
73
|
+
}
|
|
74
|
+
async onInitialize() {
|
|
75
|
+
this.resolvedPaths = await this.resolvePaths();
|
|
76
|
+
const provider = { listWidgets: async () => this.aggregate() };
|
|
77
|
+
this.ctx.logger.info("Initialized — aggregating addon-widgets-source providers");
|
|
78
|
+
return [{
|
|
79
|
+
capability: _camstack_types.addonWidgetsCapability,
|
|
80
|
+
provider
|
|
81
|
+
}];
|
|
82
|
+
}
|
|
83
|
+
async onShutdown() {
|
|
84
|
+
for (const t of this.retryTimers.values()) clearTimeout(t);
|
|
85
|
+
this.retryTimers.clear();
|
|
86
|
+
this.lastGood.clear();
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Strip the `@<nodeId>` suffix that the CapabilityBridge appends to
|
|
90
|
+
* collection-provider registry keys for cross-node addons (see
|
|
91
|
+
* `moleculer.service.ts` — `registryKey = ${addonId}@${nodeId}`).
|
|
92
|
+
*
|
|
93
|
+
* The widget bundle is hub-resident (the same npm package ships to
|
|
94
|
+
* every node and the hub keeps a copy on disk keyed by the bare
|
|
95
|
+
* manifest id), so both the static-file URL and the admin-ui widget
|
|
96
|
+
* namespace must use the bare addon id. Without this, a widget
|
|
97
|
+
* source running on a remote agent yields a `bundleUrl` like
|
|
98
|
+
* `/api/addon-widgets/pipeline-analytics@agent-0/pipeline-analytics/remoteEntry.js`
|
|
99
|
+
* — the embedded `@<node>/<group>` makes the static-file route's
|
|
100
|
+
* `:addonId` param mismatch the registered provider and 404.
|
|
101
|
+
*/
|
|
102
|
+
bareAddonId(registryKey) {
|
|
103
|
+
const at = registryKey.indexOf("@");
|
|
104
|
+
return at === -1 ? registryKey : registryKey.slice(0, at);
|
|
105
|
+
}
|
|
106
|
+
async aggregate() {
|
|
107
|
+
const entries = this.capabilities?.getCollectionEntries("addon-widgets-source") ?? [];
|
|
108
|
+
const out = [];
|
|
109
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
110
|
+
const settled = await require_settle_sources.settleSourcesWithTimeout(entries.map(([registryKey, source]) => [registryKey, () => Promise.resolve(source.listWidgets())]), PER_SOURCE_TIMEOUT_MS);
|
|
111
|
+
for (const [addonId, result] of settled) {
|
|
112
|
+
const publicAddonId = this.bareAddonId(addonId);
|
|
113
|
+
seenIds.add(addonId);
|
|
114
|
+
if (result.ok) {
|
|
115
|
+
const enriched = result.value.map((w) => ({
|
|
116
|
+
...w,
|
|
117
|
+
addonId: publicAddonId,
|
|
118
|
+
bundleUrl: this.makeBundleUrl(publicAddonId, w.bundle)
|
|
119
|
+
}));
|
|
120
|
+
for (const item of enriched) out.push(item);
|
|
121
|
+
this.lastGood.set(addonId, enriched);
|
|
122
|
+
} else {
|
|
123
|
+
this.ctx.logger.warn("addon-widgets-source provider failed", { meta: {
|
|
124
|
+
sourceId: addonId,
|
|
125
|
+
error: result.error.message
|
|
126
|
+
} });
|
|
127
|
+
const cached = this.lastGood.get(addonId);
|
|
128
|
+
if (cached !== void 0) {
|
|
129
|
+
for (const item of cached) out.push(item);
|
|
130
|
+
this.ctx.logger.info("addon-widgets-source falling back to cached snapshot", { meta: {
|
|
131
|
+
sourceId: addonId,
|
|
132
|
+
cachedWidgets: cached.length
|
|
133
|
+
} });
|
|
134
|
+
}
|
|
135
|
+
this.scheduleRetry(addonId);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
for (const cachedId of this.lastGood.keys()) if (!seenIds.has(cachedId)) this.lastGood.delete(cachedId);
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
scheduleRetry(sourceId, attempt = 0) {
|
|
142
|
+
if (attempt >= RETRY_BACKOFF_MS.length) return;
|
|
143
|
+
if (this.retryTimers.has(sourceId)) return;
|
|
144
|
+
const delayMs = RETRY_BACKOFF_MS[attempt] ?? RETRY_BACKOFF_MS[RETRY_BACKOFF_MS.length - 1];
|
|
145
|
+
const timer = setTimeout(() => {
|
|
146
|
+
this.retryTimers.delete(sourceId);
|
|
147
|
+
this.retrySource(sourceId, attempt);
|
|
148
|
+
}, delayMs);
|
|
149
|
+
this.retryTimers.set(sourceId, timer);
|
|
150
|
+
}
|
|
151
|
+
async retrySource(sourceId, attempt) {
|
|
152
|
+
const found = (this.capabilities?.getCollectionEntries("addon-widgets-source") ?? []).find(([id]) => id === sourceId);
|
|
153
|
+
if (!found) return;
|
|
154
|
+
const [addonId, source] = found;
|
|
155
|
+
const publicAddonId = this.bareAddonId(addonId);
|
|
156
|
+
try {
|
|
157
|
+
const enriched = (await Promise.resolve(source.listWidgets())).map((w) => ({
|
|
158
|
+
...w,
|
|
159
|
+
addonId: publicAddonId,
|
|
160
|
+
bundleUrl: this.makeBundleUrl(publicAddonId, w.bundle)
|
|
161
|
+
}));
|
|
162
|
+
this.lastGood.set(addonId, enriched);
|
|
163
|
+
this.ctx.logger.info("addon-widgets-source recovered after retry", { meta: {
|
|
164
|
+
sourceId: addonId,
|
|
165
|
+
attempt: attempt + 1,
|
|
166
|
+
widgets: enriched.length
|
|
167
|
+
} });
|
|
168
|
+
this.ctx.eventBus.emit({
|
|
169
|
+
id: (0, node_crypto.randomUUID)(),
|
|
170
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
171
|
+
source: {
|
|
172
|
+
type: "addon",
|
|
173
|
+
id: this.id
|
|
174
|
+
},
|
|
175
|
+
category: _camstack_types.EventCategory.AddonWidgetReady,
|
|
176
|
+
data: {
|
|
177
|
+
addonId: publicAddonId,
|
|
178
|
+
recovered: true
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
} catch (err) {
|
|
182
|
+
this.ctx.logger.debug("addon-widgets-source retry failed", { meta: {
|
|
183
|
+
sourceId,
|
|
184
|
+
attempt: attempt + 1,
|
|
185
|
+
error: (0, _camstack_types.errMsg)(err)
|
|
186
|
+
} });
|
|
187
|
+
this.scheduleRetry(sourceId, attempt + 1);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Build `/api/addon-widgets/<addonId>/<bundle>?v=<mtime>`. Falls back
|
|
192
|
+
* to `Date.now()` when the bundle path can't be stat'd (remote addon
|
|
193
|
+
* with no local file, addon not yet on disk, etc.) — the browser
|
|
194
|
+
* just gets a fresh URL on each call instead of cache-friendly mtime.
|
|
195
|
+
*/
|
|
196
|
+
makeBundleUrl(addonId, bundle) {
|
|
197
|
+
const bundlePath = this.resolveBundlePath(addonId, bundle);
|
|
198
|
+
let mtime = Date.now();
|
|
199
|
+
if (bundlePath !== null) try {
|
|
200
|
+
mtime = node_fs.statSync(bundlePath).mtimeMs;
|
|
201
|
+
} catch {}
|
|
202
|
+
return `/api/addon-widgets/${addonId}/${bundle}?v=${Math.floor(mtime)}`;
|
|
203
|
+
}
|
|
204
|
+
resolveBundlePath(addonId, bundle) {
|
|
205
|
+
const paths = this.resolvedPaths;
|
|
206
|
+
if (!paths) return null;
|
|
207
|
+
const addonDistPath = node_path.join(paths.addonsDir, "@camstack", `addon-${addonId}`, "dist");
|
|
208
|
+
const resolvedBase = node_path.resolve(addonDistPath);
|
|
209
|
+
const resolvedFile = node_path.resolve(addonDistPath, bundle);
|
|
210
|
+
if (!resolvedFile.startsWith(resolvedBase + node_path.sep) && resolvedFile !== resolvedBase) return null;
|
|
211
|
+
return resolvedFile;
|
|
212
|
+
}
|
|
213
|
+
async resolvePaths() {
|
|
214
|
+
const fallback = { addonsDir: node_path.resolve("camstack-data", "addons") };
|
|
215
|
+
if (!this.ctx.settings) return fallback;
|
|
216
|
+
try {
|
|
217
|
+
const server = await this.ctx.settings.getSection("server");
|
|
218
|
+
const dataPath = typeof server["dataPath"] === "string" && server["dataPath"] ? server["dataPath"] : "camstack-data";
|
|
219
|
+
return { addonsDir: node_path.resolve(dataPath, "addons") };
|
|
220
|
+
} catch (err) {
|
|
221
|
+
this.ctx.logger.debug("Failed to read server.dataPath — falling back", { meta: { error: (0, _camstack_types.errMsg)(err) } });
|
|
222
|
+
return fallback;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
//#endregion
|
|
227
|
+
exports.AddonWidgetsAggregatorAddon = AddonWidgetsAggregatorAddon;
|
|
228
|
+
exports.default = AddonWidgetsAggregatorAddon;
|