@camstack/system 1.2.456 → 1.2.458
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-runner.js +259 -37
- package/dist/addon-runner.mjs +289 -67
- package/dist/{agent-https-fm4AR8XL.mjs → agent-https-CQA3W-3e.mjs} +2 -2
- package/dist/{agent-https-yjxdfJDA.js → agent-https-DOELEug8.js} +2 -2
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +2 -2
- package/dist/builtins/alerts/alerts.addon.mjs +2 -2
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
- package/dist/builtins/calendars/calendars.addon.js +1 -1
- package/dist/builtins/calendars/calendars.addon.mjs +1 -1
- package/dist/builtins/camera-grid/index.js +1 -1
- package/dist/builtins/camera-grid/index.mjs +1 -1
- package/dist/builtins/composer/composer.addon.js +2 -2
- package/dist/builtins/composer/composer.addon.mjs +2 -2
- package/dist/builtins/console-logging/console-destination.d.ts +1 -2
- package/dist/builtins/console-logging/index.js +1 -4
- package/dist/builtins/console-logging/index.mjs +1 -4
- package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
- package/dist/builtins/device-history/capture/capture-engine.d.ts +6 -0
- package/dist/builtins/device-history/device-history.addon.d.ts +13 -0
- package/dist/builtins/device-history/device-history.addon.js +736 -315
- package/dist/builtins/device-history/device-history.addon.mjs +736 -315
- package/dist/builtins/device-history/ingest/producer-ingest.d.ts +11 -1
- package/dist/builtins/device-history/query/history-query.d.ts +35 -9
- package/dist/builtins/device-history/query/point-shape.d.ts +33 -0
- package/dist/builtins/device-history/retention/retention.d.ts +1 -1
- package/dist/builtins/device-history/rollup/rollup-engine.d.ts +2 -0
- package/dist/builtins/device-history/rollup/rollup-math.d.ts +6 -0
- package/dist/builtins/device-history/store/history-store.d.ts +7 -0
- package/dist/builtins/device-history/store/series-registry.d.ts +26 -1
- package/dist/builtins/device-manager/device-manager.addon.js +1 -1
- package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
- package/dist/builtins/hub-forwarder/hub-forwarder-destination.d.ts +1 -2
- package/dist/builtins/hub-forwarder/index.js +1 -4
- package/dist/builtins/hub-forwarder/index.mjs +1 -4
- package/dist/builtins/keychain/index.js +4 -4
- package/dist/builtins/keychain/index.mjs +4 -4
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +3 -3
- package/dist/builtins/local-auth/local-auth.addon.mjs +3 -3
- package/dist/builtins/local-network/local-network.addon.js +2 -2
- package/dist/builtins/local-network/local-network.addon.mjs +2 -2
- package/dist/builtins/loki-logging/index.js +323 -7
- package/dist/builtins/loki-logging/index.mjs +323 -7
- package/dist/builtins/loki-logging/loki-destination.d.ts +20 -3
- package/dist/builtins/loki-logging/loki-read.d.ts +51 -0
- package/dist/builtins/native-metrics/native-metrics.addon.js +290 -112
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +290 -112
- package/dist/builtins/native-metrics/node-metrics-history-recorder.d.ts +106 -35
- package/dist/builtins/platform-probe/coral-accelerators.d.ts +26 -9
- package/dist/builtins/platform-probe/index.js +11 -68
- package/dist/builtins/platform-probe/index.mjs +11 -68
- package/dist/builtins/record-entries/index.js +1 -1
- package/dist/builtins/record-entries/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/index.js +1 -1
- package/dist/builtins/sqlite-storage/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +2 -2
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +2 -2
- package/dist/builtins/storage-orchestrator/location-history-recorder.d.ts +76 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +9 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +144 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +144 -1
- package/dist/builtins/system-config/system-config.addon.js +3 -3
- package/dist/builtins/system-config/system-config.addon.mjs +3 -3
- package/dist/builtins/winston-logging/index.js +165 -3
- package/dist/builtins/winston-logging/index.mjs +165 -3
- package/dist/builtins/winston-logging/log-file-cursor.d.ts +12 -0
- package/dist/builtins/winston-logging/log-file-index-store.d.ts +11 -0
- package/dist/builtins/winston-logging/log-file-index.d.ts +77 -0
- package/dist/builtins/winston-logging/log-file-read-client.d.ts +44 -0
- package/dist/builtins/winston-logging/log-file-read-protocol.d.ts +41 -0
- package/dist/builtins/winston-logging/log-file-read-worker.d.ts +1 -0
- package/dist/builtins/winston-logging/log-file-reader.d.ts +51 -0
- package/dist/builtins/winston-logging/winston-destination.d.ts +20 -2
- package/dist/{composition-sources-Ds0Vr6eD.mjs → composition-sources-BVguN7VV.mjs} +1 -1
- package/dist/{composition-sources-gB4coDJR.js → composition-sources-DURQNDJ4.js} +1 -1
- package/dist/{constants-i5oD_gLB.mjs → constants-FGjnsE2x.mjs} +1 -1
- package/dist/{constants-CavyKfHZ.js → constants-Njka8Cv6.js} +1 -1
- package/dist/coral-accelerators-Bi_c3Rft.js +175 -0
- package/dist/coral-accelerators-DcnZvctM.mjs +162 -0
- package/dist/{device-ca-bpoEr0zG.js → device-ca-BrVK_5s3.js} +1 -1
- package/dist/{device-ca-qz1Pmeu1.mjs → device-ca-lEaAQ9Xi.mjs} +1 -1
- package/dist/{dist-BpqA2CT0.js → dist-BZQjLPiz.js} +526 -57
- package/dist/{dist-BSF_C8W2.mjs → dist-B_GRPhnY.mjs} +509 -58
- package/dist/file-host-worker.js +1 -1
- package/dist/file-host-worker.mjs +1 -1
- package/dist/{hub-mdns-ciao-CHaGDzyz.mjs → hub-mdns-ciao-Bg2HfTwP.mjs} +2 -2
- package/dist/{hub-mdns-ciao-CZlZRxxA.js → hub-mdns-ciao-nce3TXs_.js} +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +140 -48
- package/dist/index.mjs +134 -49
- package/dist/kernel/config-schema.d.ts +5 -0
- package/dist/kernel/hwaccel/hwaccel-resolver.d.ts +7 -3
- package/dist/kernel/moleculer/accessory-prune-gate.d.ts +55 -0
- package/dist/log-file-read-protocol-B7M3X3bT.mjs +785 -0
- package/dist/log-file-read-protocol-D0WcLbIV.js +817 -0
- package/dist/log-file-read-worker.js +64 -0
- package/dist/log-file-read-worker.mjs +64 -0
- package/dist/log-read-filter-Bi066jwU.js +134 -0
- package/dist/log-read-filter-D7Ev3zm-.mjs +93 -0
- package/dist/logging/log-manager.d.ts +10 -6
- package/dist/logging/log-read-filter.d.ts +43 -0
- package/dist/{phase6-declare-gate-n-IZRklX.js → phase6-declare-gate-Dag1_WR2.js} +1 -1
- package/dist/{phase6-declare-gate-B4yfsf2n.mjs → phase6-declare-gate-Lb-mrVvz.mjs} +1 -1
- package/dist/{restore-archive-BGUM4FKf.mjs → restore-archive-C7kLDI_0.mjs} +1 -1
- package/dist/{restore-archive-B4QG7XmX.js → restore-archive-FyfZ8YMC.js} +1 -1
- package/dist/{wal-maintenance-CNEs_I94.js → wal-maintenance-DfSIOj24.js} +0 -0
- package/dist/{wal-maintenance-AerF4XE6.mjs → wal-maintenance-Duejh7Lx.mjs} +0 -0
- package/package.json +1 -1
|
@@ -3,7 +3,9 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-BZQjLPiz.js");
|
|
7
|
+
const require_log_read_filter = require("../../log-read-filter-Bi066jwU.js");
|
|
8
|
+
let zod = require("zod");
|
|
7
9
|
//#region src/builtins/loki-logging/loki-payload.ts
|
|
8
10
|
/**
|
|
9
11
|
* Loki rejects a label name that is not a valid Prometheus label
|
|
@@ -41,6 +43,7 @@ function toUnixNano(timestamp) {
|
|
|
41
43
|
*/
|
|
42
44
|
function formatLokiLine(entry) {
|
|
43
45
|
const line = { message: entry.message };
|
|
46
|
+
if (entry.id !== void 0) line["id"] = entry.id;
|
|
44
47
|
if (entry.scope !== void 0 && entry.scope.length > 0) line["scope"] = entry.scope;
|
|
45
48
|
if (entry.tags !== void 0 && Object.keys(entry.tags).length > 0) line["tags"] = entry.tags;
|
|
46
49
|
if (entry.meta !== void 0 && Object.keys(entry.meta).length > 0) line["meta"] = entry.meta;
|
|
@@ -91,6 +94,231 @@ function buildPushBody(entries, labels) {
|
|
|
91
94
|
return { streams };
|
|
92
95
|
}
|
|
93
96
|
//#endregion
|
|
97
|
+
//#region src/builtins/loki-logging/loki-read.ts
|
|
98
|
+
/**
|
|
99
|
+
* Reading history back from Loki (D898): `GET /loki/api/v1/query_range`,
|
|
100
|
+
* newest first.
|
|
101
|
+
*
|
|
102
|
+
* The label selector narrows by what IS a label (`job`, `node`, `level` —
|
|
103
|
+
* see `loki-payload.ts` for why nothing else is); line filters (`|=`, `|~`)
|
|
104
|
+
* narrow by device, addon and text on the JSON body; the shared
|
|
105
|
+
* `lineMatches` then decides exactly, so a line filter only has to be a
|
|
106
|
+
* superset.
|
|
107
|
+
*
|
|
108
|
+
* The cursor is the nanosecond timestamp of the oldest line returned plus a
|
|
109
|
+
* short hash of each line returned AT that nanosecond: entries share
|
|
110
|
+
* timestamps (they are millisecond-precise), so "older than t" would drop the
|
|
111
|
+
* siblings a page cut through, and "t or older" would repeat them. The next
|
|
112
|
+
* page asks for `end = t + 1` (Loki's end is exclusive) and skips the hashes.
|
|
113
|
+
*/
|
|
114
|
+
var LEVELS = [
|
|
115
|
+
"debug",
|
|
116
|
+
"info",
|
|
117
|
+
"warn",
|
|
118
|
+
"error"
|
|
119
|
+
];
|
|
120
|
+
/** One query_range window. A busy stream fills `limit` long before this. */
|
|
121
|
+
var LOKI_WINDOW_MS = 360 * 60 * 1e3;
|
|
122
|
+
var MS_NS = 1000000n;
|
|
123
|
+
var CURSOR_PREFIX = "l1.";
|
|
124
|
+
var LokiCursorSchema = zod.z.object({
|
|
125
|
+
/** Nanosecond epoch, decimal string. */
|
|
126
|
+
n: zod.z.string().regex(/^\d{1,22}$/),
|
|
127
|
+
/** Hashes of the lines already returned at exactly `n`. */
|
|
128
|
+
h: zod.z.array(zod.z.string().max(16)).max(1e3)
|
|
129
|
+
});
|
|
130
|
+
function encodeLokiCursor(cursor) {
|
|
131
|
+
return `${CURSOR_PREFIX}${Buffer.from(JSON.stringify(cursor)).toString("base64url")}`;
|
|
132
|
+
}
|
|
133
|
+
function decodeLokiCursor(raw) {
|
|
134
|
+
if (!raw.startsWith(CURSOR_PREFIX)) return null;
|
|
135
|
+
try {
|
|
136
|
+
const parsed = LokiCursorSchema.safeParse(JSON.parse(Buffer.from(raw.slice(3), "base64url").toString("utf8")));
|
|
137
|
+
return parsed.success ? parsed.data : null;
|
|
138
|
+
} catch {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/** FNV-1a, 32 bit, hex — a dedupe key, not a security property. */
|
|
143
|
+
function lineHash(line) {
|
|
144
|
+
let h = 2166136261;
|
|
145
|
+
for (let i = 0; i < line.length; i += 1) {
|
|
146
|
+
h ^= line.charCodeAt(i);
|
|
147
|
+
h = Math.imul(h, 16777619);
|
|
148
|
+
}
|
|
149
|
+
return (h >>> 0).toString(16);
|
|
150
|
+
}
|
|
151
|
+
/** A LogQL double-quoted string literal. */
|
|
152
|
+
function quote(value) {
|
|
153
|
+
return `"${value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"")}"`;
|
|
154
|
+
}
|
|
155
|
+
function escapeRegex(value) {
|
|
156
|
+
return value.replaceAll(/[\\.+*?()|[\]{}^$]/g, (c) => `\\${c}`);
|
|
157
|
+
}
|
|
158
|
+
/** The LogQL for a read. Pure. */
|
|
159
|
+
function buildLogQl(query, labels) {
|
|
160
|
+
const selectors = [`job=${quote(labels.job)}`];
|
|
161
|
+
if (query.fromNodeId !== void 0) selectors.push(`node=${quote(query.fromNodeId)}`);
|
|
162
|
+
if (query.level !== void 0 && query.level !== "debug") {
|
|
163
|
+
const floor = LEVELS.indexOf(query.level);
|
|
164
|
+
selectors.push(`level=~${quote(LEVELS.slice(floor).join("|"))}`);
|
|
165
|
+
}
|
|
166
|
+
let logql = `{${selectors.join(",")}}`;
|
|
167
|
+
if (query.deviceId !== void 0) logql += ` |= ${quote(`"deviceId":${String(query.deviceId)}`)}`;
|
|
168
|
+
if (query.fromAddonId !== void 0) {
|
|
169
|
+
const bare = query.fromAddonId.split("@")[0] ?? query.fromAddonId;
|
|
170
|
+
logql += ` |= ${quote(`"addonId":"${bare}`)}`;
|
|
171
|
+
}
|
|
172
|
+
const text = query.text?.trim();
|
|
173
|
+
if (text !== void 0 && text.length > 0) logql += ` |~ ${quote(`(?i)${escapeRegex(text)}`)}`;
|
|
174
|
+
return logql;
|
|
175
|
+
}
|
|
176
|
+
var LokiResponseSchema = zod.z.object({ data: zod.z.object({ result: zod.z.array(zod.z.object({
|
|
177
|
+
stream: zod.z.record(zod.z.string(), zod.z.string()),
|
|
178
|
+
values: zod.z.array(zod.z.tuple([zod.z.string(), zod.z.string()]))
|
|
179
|
+
})) }) });
|
|
180
|
+
var LokiLineSchema = zod.z.object({
|
|
181
|
+
id: zod.z.string().optional(),
|
|
182
|
+
message: zod.z.string(),
|
|
183
|
+
scope: zod.z.string().optional(),
|
|
184
|
+
tags: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
|
|
185
|
+
meta: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
|
|
186
|
+
});
|
|
187
|
+
function levelOf(label) {
|
|
188
|
+
return label === "debug" || label === "warn" || label === "error" ? label : "info";
|
|
189
|
+
}
|
|
190
|
+
/** A query_range body → rows, newest first. Lines that are not ours are dropped. */
|
|
191
|
+
function parseQueryRange(body) {
|
|
192
|
+
const parsed = LokiResponseSchema.safeParse(body);
|
|
193
|
+
if (!parsed.success) throw new Error("Loki answered with an unexpected query_range shape");
|
|
194
|
+
const rows = [];
|
|
195
|
+
for (const stream of parsed.data.data.result) {
|
|
196
|
+
const level = levelOf(stream.stream["level"]);
|
|
197
|
+
for (const [nsText, text] of stream.values) {
|
|
198
|
+
let json;
|
|
199
|
+
try {
|
|
200
|
+
json = JSON.parse(text);
|
|
201
|
+
} catch {
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const line = LokiLineSchema.safeParse(json);
|
|
205
|
+
if (!line.success) continue;
|
|
206
|
+
const ns = BigInt(nsText);
|
|
207
|
+
const timestamp = Number(ns / MS_NS);
|
|
208
|
+
const tags = {};
|
|
209
|
+
for (const [k, v] of Object.entries(line.data.tags ?? {})) if (typeof v === "string" || typeof v === "number") tags[k] = v;
|
|
210
|
+
const hasTags = Object.keys(tags).length > 0;
|
|
211
|
+
const entry = {
|
|
212
|
+
...line.data.id !== void 0 ? { id: line.data.id } : {},
|
|
213
|
+
timestamp,
|
|
214
|
+
level,
|
|
215
|
+
...line.data.scope !== void 0 ? { scope: line.data.scope } : {},
|
|
216
|
+
message: line.data.message,
|
|
217
|
+
...hasTags ? { tags } : {},
|
|
218
|
+
...line.data.meta !== void 0 ? { meta: line.data.meta } : {}
|
|
219
|
+
};
|
|
220
|
+
rows.push({
|
|
221
|
+
ns,
|
|
222
|
+
hash: lineHash(text),
|
|
223
|
+
entry,
|
|
224
|
+
line: {
|
|
225
|
+
timestamp,
|
|
226
|
+
level,
|
|
227
|
+
message: line.data.message,
|
|
228
|
+
scope: line.data.scope,
|
|
229
|
+
tags: hasTags ? tags : void 0,
|
|
230
|
+
meta: line.data.meta
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return rows.toSorted((a, b) => a.ns < b.ns ? 1 : a.ns > b.ns ? -1 : 0);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* One backward slice over Loki: windows of {@link LOKI_WINDOW_MS}, newest
|
|
239
|
+
* first, until `limit` matches, the horizon, the budget or a cancel.
|
|
240
|
+
*/
|
|
241
|
+
async function readLokiBackward(args) {
|
|
242
|
+
const { query } = args;
|
|
243
|
+
const now = args.now ?? Date.now;
|
|
244
|
+
const filter = require_log_read_filter.compileLogFilter(query);
|
|
245
|
+
const limit = query.limit ?? 200;
|
|
246
|
+
const deadline = now() + Math.min(query.budgetMs ?? 2e3, 1e4);
|
|
247
|
+
const logql = buildLogQl(query, args.labels);
|
|
248
|
+
const horizonMs = query.since ?? now() - 30 * 864e5;
|
|
249
|
+
const horizonNs = BigInt(Math.trunc(horizonMs)) * MS_NS;
|
|
250
|
+
const cursor = query.before !== void 0 ? decodeLokiCursor(query.before) : null;
|
|
251
|
+
const untilNs = query.until !== void 0 ? BigInt(Math.trunc(query.until) + 1) * MS_NS : null;
|
|
252
|
+
let endNs = cursor !== null ? BigInt(cursor.n) + 1n : untilNs ?? BigInt(now() + 1) * MS_NS;
|
|
253
|
+
let seenAtBoundary = new Set(cursor?.h ?? []);
|
|
254
|
+
let boundaryNs = cursor !== null ? BigInt(cursor.n) : null;
|
|
255
|
+
const entries = [];
|
|
256
|
+
let bytes = 0;
|
|
257
|
+
while (endNs > horizonNs) {
|
|
258
|
+
if (args.isCancelled()) return {
|
|
259
|
+
entries,
|
|
260
|
+
nextBefore: encodeLokiCursor({
|
|
261
|
+
n: String(endNs - 1n),
|
|
262
|
+
h: [...seenAtBoundary]
|
|
263
|
+
}),
|
|
264
|
+
nextAfter: null,
|
|
265
|
+
scannedBytes: bytes,
|
|
266
|
+
done: false,
|
|
267
|
+
cancelled: true
|
|
268
|
+
};
|
|
269
|
+
const windowStart = endNs - BigInt(LOKI_WINDOW_MS) * MS_NS;
|
|
270
|
+
const startNs = windowStart > horizonNs ? windowStart : horizonNs;
|
|
271
|
+
const want = limit - entries.length + seenAtBoundary.size;
|
|
272
|
+
const { rows, bytes: got } = await args.range({
|
|
273
|
+
logql,
|
|
274
|
+
startNs,
|
|
275
|
+
endNs,
|
|
276
|
+
limit: want
|
|
277
|
+
});
|
|
278
|
+
bytes += got;
|
|
279
|
+
let oldest = null;
|
|
280
|
+
for (const row of rows) {
|
|
281
|
+
if (boundaryNs !== null && row.ns === boundaryNs && seenAtBoundary.has(row.hash)) continue;
|
|
282
|
+
if (oldest === null || row.ns < oldest) {
|
|
283
|
+
if (oldest !== null && row.ns < oldest) seenAtBoundary = /* @__PURE__ */ new Set();
|
|
284
|
+
oldest = row.ns;
|
|
285
|
+
}
|
|
286
|
+
if (row.ns === oldest) seenAtBoundary.add(row.hash);
|
|
287
|
+
boundaryNs = oldest;
|
|
288
|
+
if (!require_log_read_filter.lineMatches(row.line, filter)) continue;
|
|
289
|
+
entries.push(row.entry);
|
|
290
|
+
if (entries.length >= limit) return {
|
|
291
|
+
entries,
|
|
292
|
+
nextBefore: encodeLokiCursor({
|
|
293
|
+
n: String(row.ns),
|
|
294
|
+
h: [...seenAtBoundary]
|
|
295
|
+
}),
|
|
296
|
+
nextAfter: null,
|
|
297
|
+
scannedBytes: bytes,
|
|
298
|
+
done: false
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
if (rows.length >= want && oldest !== null) endNs = oldest + 1n;
|
|
302
|
+
else {
|
|
303
|
+
endNs = startNs;
|
|
304
|
+
seenAtBoundary = /* @__PURE__ */ new Set();
|
|
305
|
+
boundaryNs = null;
|
|
306
|
+
}
|
|
307
|
+
if (now() >= deadline) break;
|
|
308
|
+
}
|
|
309
|
+
const done = endNs <= horizonNs;
|
|
310
|
+
return {
|
|
311
|
+
entries,
|
|
312
|
+
nextBefore: done ? null : encodeLokiCursor({
|
|
313
|
+
n: String(endNs - 1n),
|
|
314
|
+
h: [...seenAtBoundary]
|
|
315
|
+
}),
|
|
316
|
+
nextAfter: null,
|
|
317
|
+
scannedBytes: bytes,
|
|
318
|
+
done
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
//#endregion
|
|
94
322
|
//#region src/builtins/loki-logging/loki-destination.ts
|
|
95
323
|
var LEVEL_ORDER = {
|
|
96
324
|
debug: 0,
|
|
@@ -114,6 +342,8 @@ var LokiDestination = class {
|
|
|
114
342
|
failures = 0;
|
|
115
343
|
lastError = null;
|
|
116
344
|
lastPushAtMs = null;
|
|
345
|
+
/** In-flight history reads, by requestId, so `cancelRead` can abort the fetch. */
|
|
346
|
+
reads = /* @__PURE__ */ new Map();
|
|
117
347
|
fetchFn;
|
|
118
348
|
setIntervalFn;
|
|
119
349
|
clearIntervalFn;
|
|
@@ -121,7 +351,7 @@ var LokiDestination = class {
|
|
|
121
351
|
this.fetchFn = deps?.fetchFn ?? ((url, init) => globalThis.fetch(url, {
|
|
122
352
|
method: init.method,
|
|
123
353
|
headers: { ...init.headers },
|
|
124
|
-
body: init.body,
|
|
354
|
+
...init.body !== void 0 ? { body: init.body } : {},
|
|
125
355
|
...init.signal ? { signal: init.signal } : {}
|
|
126
356
|
}));
|
|
127
357
|
this.setIntervalFn = deps?.setIntervalFn ?? setInterval;
|
|
@@ -213,9 +443,10 @@ var LokiDestination = class {
|
|
|
213
443
|
}
|
|
214
444
|
async push(body, config) {
|
|
215
445
|
const endpoint = `${config.url.replace(/\/+$/, "")}/loki/api/v1/push`;
|
|
216
|
-
const headers = {
|
|
217
|
-
|
|
218
|
-
|
|
446
|
+
const headers = {
|
|
447
|
+
"content-type": "application/json",
|
|
448
|
+
...this.authHeaders(config)
|
|
449
|
+
};
|
|
219
450
|
const controller = new AbortController();
|
|
220
451
|
const timeout = setTimeout(() => controller.abort(), Math.max(1e3, config.timeoutMs));
|
|
221
452
|
try {
|
|
@@ -283,8 +514,93 @@ var LokiDestination = class {
|
|
|
283
514
|
};
|
|
284
515
|
}
|
|
285
516
|
}
|
|
286
|
-
|
|
287
|
-
|
|
517
|
+
/** Headers every call to this Loki carries: tenant + basic auth. */
|
|
518
|
+
authHeaders(config) {
|
|
519
|
+
const headers = {};
|
|
520
|
+
if (config.tenantId.trim().length > 0) headers["X-Scope-OrgID"] = config.tenantId.trim();
|
|
521
|
+
if (config.username.length > 0) headers["authorization"] = `Basic ${Buffer.from(`${config.username}:${config.password}`).toString("base64")}`;
|
|
522
|
+
return headers;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Readable when it is configured (D898): the same URL and credentials the
|
|
526
|
+
* push uses answer `query_range`. Labels are `job`/`node`/`level`, so those
|
|
527
|
+
* filter at the index; device, addon and text are line filters.
|
|
528
|
+
*/
|
|
529
|
+
async describeRead() {
|
|
530
|
+
const configured = this.isActive();
|
|
531
|
+
return {
|
|
532
|
+
readable: configured,
|
|
533
|
+
reason: configured ? null : "Loki is not enabled or has no URL",
|
|
534
|
+
filters: {
|
|
535
|
+
deviceId: true,
|
|
536
|
+
addonId: true,
|
|
537
|
+
nodeId: true,
|
|
538
|
+
tags: true,
|
|
539
|
+
level: true,
|
|
540
|
+
text: true,
|
|
541
|
+
timeRange: true
|
|
542
|
+
},
|
|
543
|
+
retentionDays: null
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
/** One backward slice from `query_range`, newest first. Never logs. */
|
|
547
|
+
async read(query) {
|
|
548
|
+
const config = this.config;
|
|
549
|
+
if (config === null || !this.isActive()) throw new Error("Loki is not enabled or has no URL");
|
|
550
|
+
const controller = new AbortController();
|
|
551
|
+
const requestId = query.requestId;
|
|
552
|
+
if (requestId !== void 0) this.reads.set(requestId, controller);
|
|
553
|
+
const base = config.url.replace(/\/+$/, "");
|
|
554
|
+
const range = async ({ logql, startNs, endNs, limit }) => {
|
|
555
|
+
const params = new URLSearchParams({
|
|
556
|
+
query: logql,
|
|
557
|
+
start: String(startNs),
|
|
558
|
+
end: String(endNs),
|
|
559
|
+
limit: String(limit),
|
|
560
|
+
direction: "backward"
|
|
561
|
+
});
|
|
562
|
+
const timeout = setTimeout(() => controller.abort(), Math.max(1e3, config.timeoutMs));
|
|
563
|
+
try {
|
|
564
|
+
const response = await this.fetchFn(`${base}/loki/api/v1/query_range?${params.toString()}`, {
|
|
565
|
+
method: "GET",
|
|
566
|
+
headers: this.authHeaders(config),
|
|
567
|
+
signal: controller.signal
|
|
568
|
+
});
|
|
569
|
+
const text = await response.text();
|
|
570
|
+
if (!response.ok) throw new Error(`Loki query failed: ${String(response.status)}${text.length > 0 ? ` — ${text.slice(0, 200)}` : ""}`);
|
|
571
|
+
return {
|
|
572
|
+
rows: parseQueryRange(JSON.parse(text)),
|
|
573
|
+
bytes: Buffer.byteLength(text)
|
|
574
|
+
};
|
|
575
|
+
} finally {
|
|
576
|
+
clearTimeout(timeout);
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
try {
|
|
580
|
+
return await readLokiBackward({
|
|
581
|
+
query,
|
|
582
|
+
labels: { job: config.job },
|
|
583
|
+
range,
|
|
584
|
+
isCancelled: () => controller.signal.aborted
|
|
585
|
+
});
|
|
586
|
+
} catch (error) {
|
|
587
|
+
if (controller.signal.aborted) return {
|
|
588
|
+
entries: [],
|
|
589
|
+
nextBefore: query.before ?? null,
|
|
590
|
+
nextAfter: null,
|
|
591
|
+
scannedBytes: 0,
|
|
592
|
+
done: false,
|
|
593
|
+
cancelled: true
|
|
594
|
+
};
|
|
595
|
+
throw error;
|
|
596
|
+
} finally {
|
|
597
|
+
if (requestId !== void 0) this.reads.delete(requestId);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
async cancelRead(input) {
|
|
601
|
+
const controller = this.reads.get(input.requestId);
|
|
602
|
+
controller?.abort();
|
|
603
|
+
return { cancelled: controller !== void 0 };
|
|
288
604
|
}
|
|
289
605
|
async shutdown() {
|
|
290
606
|
this.stopTimer();
|