@camstack/system 1.2.132 → 1.2.134
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 +1 -1
- package/dist/addon-runner.mjs +1 -1
- package/dist/builtins/sqlite-storage/analyze-stats.d.ts +41 -0
- package/dist/builtins/sqlite-storage/query-profiler.d.ts +74 -1
- package/dist/builtins/sqlite-storage/sqlite-pragmas.d.ts +24 -0
- package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +42 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
- package/dist/builtins/sqlite-storage/statement-shape.d.ts +64 -0
- package/dist/index.js +30 -2
- package/dist/index.mjs +30 -2
- package/dist/kernel/moleculer/runner-native-allocator.d.ts +17 -0
- package/dist/kernel/moleculer/trpc-links.d.ts +31 -3
- package/dist/{manifest-python-deps-Dmcnb287.mjs → manifest-python-deps-08rqn6xN.mjs} +18 -14
- package/dist/{manifest-python-deps-BTkFwAk_.js → manifest-python-deps-C9x9vrqt.js} +18 -14
- package/package.json +1 -1
package/dist/addon-runner.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Cek0wNdY.js");
|
|
2
|
-
const require_manifest_python_deps = require("./manifest-python-deps-
|
|
2
|
+
const require_manifest_python_deps = require("./manifest-python-deps-C9x9vrqt.js");
|
|
3
3
|
const require_custom_action_registry = require("./custom-action-registry-jY0NOZK8.js");
|
|
4
4
|
let node_path = require("node:path");
|
|
5
5
|
node_path = require_chunk.__toESM(node_path);
|
package/dist/addon-runner.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { I as createUdsLoggerWithControl, L as LocalChildClient, Pt as startRunnerHeapWatch, at as setWorkerNativeCapsChangeListener, bt as installManifestNativeDeps, i as createUdsAddonContext, nt as getWorkerNativeCapProvider, rt as getWorkerNativeCapSnapshot, st as validateProviderRegistrations, t as installManifestPythonDeps, xt as resolveAddonClass } from "./manifest-python-deps-
|
|
1
|
+
import { I as createUdsLoggerWithControl, L as LocalChildClient, Pt as startRunnerHeapWatch, at as setWorkerNativeCapsChangeListener, bt as installManifestNativeDeps, i as createUdsAddonContext, nt as getWorkerNativeCapProvider, rt as getWorkerNativeCapSnapshot, st as validateProviderRegistrations, t as installManifestPythonDeps, xt as resolveAddonClass } from "./manifest-python-deps-08rqn6xN.mjs";
|
|
2
2
|
import { t as CustomActionRegistry } from "./custom-action-registry-F__gp_VX.mjs";
|
|
3
3
|
import { register } from "node:module";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Database } from 'better-sqlite3';
|
|
2
|
+
/**
|
|
3
|
+
* Delay before the one-shot `ANALYZE`, in ms.
|
|
4
|
+
*
|
|
5
|
+
* Boot is itself the busiest window this runner has (`ensureTable` ×9, the
|
|
6
|
+
* retired-key purge, the vector registry), and adding a 14 s statement to it
|
|
7
|
+
* would turn a slow boot into one an operator reads as a hang. Long enough that
|
|
8
|
+
* the cluster is serving before the engine goes quiet.
|
|
9
|
+
*/
|
|
10
|
+
export declare const ANALYZE_DELAY_MS = 120000;
|
|
11
|
+
/** Env var that disables the one-shot analysis. `off` is the only value read. */
|
|
12
|
+
export declare const ANALYZE_ENV_VAR = "CAMSTACK_SQLITE_ANALYZE";
|
|
13
|
+
/** What {@link runAnalyzeIfMissing} did, for the caller to log. */
|
|
14
|
+
export type AnalyzeOutcome = {
|
|
15
|
+
readonly kind: 'already-present';
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: 'analyzed';
|
|
18
|
+
readonly tookMs: number;
|
|
19
|
+
readonly indexesAnalyzed: number;
|
|
20
|
+
} | {
|
|
21
|
+
readonly kind: 'failed';
|
|
22
|
+
readonly tookMs: number;
|
|
23
|
+
readonly error: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* True when the database has no `sqlite_stat1` at all.
|
|
27
|
+
*
|
|
28
|
+
* Deliberately "none", not "old": a stale row still tells the planner the
|
|
29
|
+
* relative selectivity of two indexes, which is the entire question being
|
|
30
|
+
* asked. Re-analysis on a staleness heuristic would put a 14 s statement on a
|
|
31
|
+
* timer, and this runner cannot afford one.
|
|
32
|
+
*/
|
|
33
|
+
export declare function hasIndexStatistics(db: Database): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* `ANALYZE` the database, but only if it has never been analysed.
|
|
36
|
+
*
|
|
37
|
+
* Synchronous and long — see the module doc. Never throws: a database that
|
|
38
|
+
* refuses to be analysed is a missing optimisation, not a reason to take the
|
|
39
|
+
* settings engine down, and the outcome says so rather than going quiet.
|
|
40
|
+
*/
|
|
41
|
+
export declare function runAnalyzeIfMissing(db: Database, now?: () => number): AnalyzeOutcome;
|
|
@@ -35,6 +35,23 @@ import { IScopedLogger } from '@camstack/types';
|
|
|
35
35
|
* statement that really does take seconds. Both are needed; neither is
|
|
36
36
|
* sufficient.
|
|
37
37
|
*
|
|
38
|
+
* ## Why the SQL shape, and why the plan
|
|
39
|
+
*
|
|
40
|
+
* `(op, collection)` names a TABLE, not a call site. On 2026-08-25 the busiest
|
|
41
|
+
* line of a 90 s operator stall was `query pipeline-analytics:tracks — 1005
|
|
42
|
+
* calls, max 1615 ms` and eleven different reads in one addon are spelled
|
|
43
|
+
* exactly that way; the aggregate accused all of them and cleared none. So the
|
|
44
|
+
* shape of the prepared statement (`statement-shape.ts`) is part of the
|
|
45
|
+
* aggregate KEY, and the report prints it.
|
|
46
|
+
*
|
|
47
|
+
* A shape says WHICH query. It does not say why it is slow — "1.6 s for 501
|
|
48
|
+
* rows" is a full scan and "1.6 s for 501 rows" is also a cold disk, and those
|
|
49
|
+
* are the same line. `EXPLAIN QUERY PLAN` is the only thing that separates
|
|
50
|
+
* them, and it is attached to the slow-call WARN because that is where the
|
|
51
|
+
* question is asked. It is asked ONCE PER SHAPE PER WINDOW: a scan that fires
|
|
52
|
+
* the WARN fires it repeatedly, and a plan re-printed forty times is how the
|
|
53
|
+
* one line that mattered stops being read.
|
|
54
|
+
*
|
|
38
55
|
* ## Cost
|
|
39
56
|
*
|
|
40
57
|
* Two `Date.now()` calls and one `Map` lookup per statement. At the measured
|
|
@@ -64,17 +81,37 @@ export interface SqliteOpSample {
|
|
|
64
81
|
readonly deviceId?: number;
|
|
65
82
|
/** See {@link SqliteOpLabel.owner}. */
|
|
66
83
|
readonly owner?: string;
|
|
84
|
+
/** See {@link SqliteOpLabel.sql}. */
|
|
85
|
+
readonly sql?: string;
|
|
86
|
+
/** See {@link SqliteOpLabel.params}. */
|
|
87
|
+
readonly params?: readonly unknown[];
|
|
67
88
|
}
|
|
68
|
-
/** One `(op, collection, owner)`
|
|
89
|
+
/** One `(op, collection, owner, shape)` tuple's totals over a report window. */
|
|
69
90
|
export interface SqliteOpStat {
|
|
70
91
|
readonly op: string;
|
|
71
92
|
readonly collection: string;
|
|
72
93
|
readonly owner?: string;
|
|
94
|
+
/** `statement-shape.ts` fingerprint; absent for a fixed-shape statement the
|
|
95
|
+
* engine did not hand to the profiler. */
|
|
96
|
+
readonly shape?: string;
|
|
73
97
|
readonly calls: number;
|
|
74
98
|
readonly totalMs: number;
|
|
75
99
|
readonly maxMs: number;
|
|
76
100
|
readonly rows: number;
|
|
77
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Runs `EXPLAIN QUERY PLAN` for a statement on the connection that executed it.
|
|
104
|
+
*
|
|
105
|
+
* Injected rather than reached for: the profiler must not own a database
|
|
106
|
+
* handle, and the plan is only trustworthy from the SAME connection — a second
|
|
107
|
+
* handle can differ in `sqlite_stat1` visibility and in temp-schema state, and
|
|
108
|
+
* a plan taken from a connection that is not the one that stalled describes a
|
|
109
|
+
* query nobody ran.
|
|
110
|
+
*
|
|
111
|
+
* Returns one string per plan row (SQLite's `detail` column). Must not throw —
|
|
112
|
+
* an unexplainable statement yields `[]`.
|
|
113
|
+
*/
|
|
114
|
+
export type SqlExplainer = (sql: string, params: readonly unknown[]) => readonly string[];
|
|
78
115
|
/**
|
|
79
116
|
* Duration at which one statement is worth a WARN on its own, in ms.
|
|
80
117
|
*
|
|
@@ -104,6 +141,8 @@ export interface SqliteOpProfilerOptions {
|
|
|
104
141
|
readonly reportIntervalMs?: number;
|
|
105
142
|
readonly reportFloorMs?: number;
|
|
106
143
|
readonly topN?: number;
|
|
144
|
+
/** Absent ⇒ slow-call WARNs carry no plan. See {@link SqlExplainer}. */
|
|
145
|
+
readonly explain?: SqlExplainer;
|
|
107
146
|
}
|
|
108
147
|
/** Identity of a measured call, without the numbers `measure` supplies itself. */
|
|
109
148
|
export interface SqliteOpLabel {
|
|
@@ -117,6 +156,27 @@ export interface SqliteOpLabel {
|
|
|
117
156
|
* exactly where the 67 660-call storm of 2026-08-25 stopped.
|
|
118
157
|
*/
|
|
119
158
|
readonly owner?: string;
|
|
159
|
+
/**
|
|
160
|
+
* The statement the engine prepared, verbatim.
|
|
161
|
+
*
|
|
162
|
+
* Supplied only by the ops that BUILD their SQL from a filter — those are the
|
|
163
|
+
* ones whose cost varies by call site. A fixed-shape statement (`get`, `set`)
|
|
164
|
+
* is already fully described by `(op, collection)`.
|
|
165
|
+
*
|
|
166
|
+
* Used for two things and nothing else: the shape that becomes part of the
|
|
167
|
+
* aggregate key, and the `EXPLAIN QUERY PLAN` on a slow call. Never logged
|
|
168
|
+
* raw — see `statement-shape.ts` for what is dropped.
|
|
169
|
+
*/
|
|
170
|
+
readonly sql?: string;
|
|
171
|
+
/**
|
|
172
|
+
* The values bound to {@link sql}.
|
|
173
|
+
*
|
|
174
|
+
* Needed because `EXPLAIN QUERY PLAN` on a statement with unbound parameters
|
|
175
|
+
* is not the plan that ran: SQLite's planner reads bound values for `LIKE`
|
|
176
|
+
* prefixes and for index-selection on `IN`. Never logged, in whole or in
|
|
177
|
+
* part — they carry device ids, keys and user data.
|
|
178
|
+
*/
|
|
179
|
+
readonly params?: readonly unknown[];
|
|
120
180
|
}
|
|
121
181
|
export declare class SqliteOpProfiler {
|
|
122
182
|
private readonly logger;
|
|
@@ -125,7 +185,11 @@ export declare class SqliteOpProfiler {
|
|
|
125
185
|
private readonly reportIntervalMs;
|
|
126
186
|
private readonly reportFloorMs;
|
|
127
187
|
private readonly topN;
|
|
188
|
+
private readonly explain;
|
|
128
189
|
private readonly stats;
|
|
190
|
+
/** Shapes already explained in this window — see the class doc on why the
|
|
191
|
+
* plan is printed once and not on every WARN. Cleared by `drain`. */
|
|
192
|
+
private readonly explainedShapes;
|
|
129
193
|
private windowStartedAt;
|
|
130
194
|
private timer;
|
|
131
195
|
constructor(options: SqliteOpProfilerOptions);
|
|
@@ -139,6 +203,15 @@ export declare class SqliteOpProfiler {
|
|
|
139
203
|
*/
|
|
140
204
|
measure<T>(label: SqliteOpLabel, run: () => T): T;
|
|
141
205
|
record(sample: SqliteOpSample): void;
|
|
206
|
+
/**
|
|
207
|
+
* `EXPLAIN QUERY PLAN` for a statement that just held the loop, or nothing.
|
|
208
|
+
*
|
|
209
|
+
* Nothing when: no explainer was injected, the op carries no SQL, or this
|
|
210
|
+
* shape has already been explained in this window. The explainer is
|
|
211
|
+
* contracted not to throw and is guarded anyway — a profiler that can turn a
|
|
212
|
+
* slow query into a crash is worse than one that cannot explain it.
|
|
213
|
+
*/
|
|
214
|
+
private planFor;
|
|
142
215
|
/** Totals for the window, busiest first, and reset. */
|
|
143
216
|
drain(): readonly SqliteOpStat[];
|
|
144
217
|
/** Emit one aggregate line for the window, unless the engine was idle. */
|
|
@@ -142,3 +142,27 @@ export interface SqliteOccupancy {
|
|
|
142
142
|
* three header reads on a connection that is already open.
|
|
143
143
|
*/
|
|
144
144
|
export declare function sqliteOccupancy(counters: SqlitePageCounters): SqliteOccupancy;
|
|
145
|
+
/**
|
|
146
|
+
* How often the connection re-checks whether the file has outgrown its map.
|
|
147
|
+
*
|
|
148
|
+
* The database grows by roughly 40 MB a day on this cluster, so five minutes is
|
|
149
|
+
* far finer than it needs to be — but the check is three arithmetic operations
|
|
150
|
+
* on a `stat`, and the failure it prevents is a node silently returning to
|
|
151
|
+
* 418 synchronous page-ins per second. Cheap insurance against a slow leak.
|
|
152
|
+
*/
|
|
153
|
+
export declare const MAP_GROWTH_CHECK_INTERVAL_MS: number;
|
|
154
|
+
/**
|
|
155
|
+
* The new `mmap_size` when the file has outgrown the current one, or `null`.
|
|
156
|
+
*
|
|
157
|
+
* `sqliteMmapSizeFor` already autoscales to any user's database — but it is
|
|
158
|
+
* applied when the connection OPENS and never again, which is the same bug one
|
|
159
|
+
* level up: a file that grows during uptime keeps the map it had at boot. This
|
|
160
|
+
* node escaped it only because 130 MB → 858 MB spanned seventeen days of
|
|
161
|
+
* restarts; a node up for two months walks straight back into 70% of the file
|
|
162
|
+
* outside the map.
|
|
163
|
+
*
|
|
164
|
+
* GROWTH only. Shrinking after a delete buys nothing and costs a remap, and the
|
|
165
|
+
* file does not shrink without a `VACUUM` regardless — measured 2026-08-25,
|
|
166
|
+
* `freePct=1` on an 858 MB file that had been deleting rows for days.
|
|
167
|
+
*/
|
|
168
|
+
export declare function resolveMapGrowth(currentMmapBytes: number, fileSizeBytes: number): number | null;
|
|
@@ -221,6 +221,24 @@ export declare class SqliteSettingsBackend implements ISettingsBackend {
|
|
|
221
221
|
* Returns null if the backend has not been initialized yet.
|
|
222
222
|
*/
|
|
223
223
|
getDatabase(): Database.Database | null;
|
|
224
|
+
/**
|
|
225
|
+
* `EXPLAIN QUERY PLAN` for a statement, as one string per plan row.
|
|
226
|
+
*
|
|
227
|
+
* Answers the question the timing alone cannot: "1.6 s for 501 rows" is a
|
|
228
|
+
* full scan and is also a cold disk, and only the plan tells them apart. It
|
|
229
|
+
* runs on the SAME connection as the statement, because that is the only
|
|
230
|
+
* connection whose `sqlite_stat1` visibility and temp schema match.
|
|
231
|
+
*
|
|
232
|
+
* `EXPLAIN QUERY PLAN` prepares and plans; it does not execute the statement,
|
|
233
|
+
* so it costs no page reads of its own. It is still called only from the
|
|
234
|
+
* slow-call path, once per shape per window.
|
|
235
|
+
*
|
|
236
|
+
* Never throws. A plan that cannot be taken (a statement the engine will no
|
|
237
|
+
* longer prepare, a closed handle mid-shutdown) is a missing ANSWER, not a
|
|
238
|
+
* failure worth turning a slow query into a crash — the profiler falls back
|
|
239
|
+
* to the line it would have printed anyway.
|
|
240
|
+
*/
|
|
241
|
+
private explainQueryPlan;
|
|
224
242
|
/**
|
|
225
243
|
* Run one synchronous statement under the profiler.
|
|
226
244
|
*
|
|
@@ -251,6 +269,30 @@ export declare class SqliteSettingsBackend implements ISettingsBackend {
|
|
|
251
269
|
* enumerated. `unref` so it can never hold the process open, and
|
|
252
270
|
* `CAMSTACK_SQLITE_STORAGE_REPORT=off` turns it off without a rebuild.
|
|
253
271
|
*/
|
|
272
|
+
/**
|
|
273
|
+
* Follow the file as it grows.
|
|
274
|
+
*
|
|
275
|
+
* The map is derived from the file at OPEN and never again, which is the same
|
|
276
|
+
* expiry one level up: a database that grows during uptime keeps the map it
|
|
277
|
+
* had at boot, and once the file passes it every read outside the window
|
|
278
|
+
* costs a synchronous page-in — 418 per second, measured 2026-08-25. This
|
|
279
|
+
* node escaped it only because 130 MB → 858 MB spanned seventeen days of
|
|
280
|
+
* restarts.
|
|
281
|
+
*
|
|
282
|
+
* `unref` so it never holds the process open, and the same kill switch as the
|
|
283
|
+
* storage report turns it off without a rebuild.
|
|
284
|
+
*/
|
|
285
|
+
private scheduleMapGrowthCheck;
|
|
286
|
+
/**
|
|
287
|
+
* One-shot `ANALYZE`, long after boot, only if the database has none.
|
|
288
|
+
*
|
|
289
|
+
* `analyze-stats.ts` has the measurement that justifies it: without
|
|
290
|
+
* `sqlite_stat1` the planner walked 150 133 media rows to return six, and one
|
|
291
|
+
* `ANALYZE` took that same query from 727 ms to 3.2 ms. It is scheduled
|
|
292
|
+
* rather than run inline because it is a 14 s synchronous statement on the
|
|
293
|
+
* one thread the cluster reads configuration through.
|
|
294
|
+
*/
|
|
295
|
+
private scheduleIndexAnalysis;
|
|
254
296
|
private scheduleStorageReport;
|
|
255
297
|
private logStorageReport;
|
|
256
298
|
private readPragmaNumber;
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A stable, low-cardinality name for the SHAPE of a SQL statement.
|
|
3
|
+
*
|
|
4
|
+
* ## Why the profiler needs this
|
|
5
|
+
*
|
|
6
|
+
* `SqliteOpProfiler` aggregated on `(op, collection)`, and that pair is not an
|
|
7
|
+
* identity — it is a table. On 2026-08-25 the busiest line of a 90 s operator
|
|
8
|
+
* stall read `query pipeline-analytics:tracks — 1005 calls, 2694 ms,
|
|
9
|
+
* max 1615 ms, 17981 rows`, and there was no way to tell from it WHICH of the
|
|
10
|
+
* eleven distinct reads that addon issues against that one table had spent the
|
|
11
|
+
* second and a half. Every one of them was equally accused; the aggregate could
|
|
12
|
+
* not clear a single one.
|
|
13
|
+
*
|
|
14
|
+
* The shape is the missing half. It is derived from the statement the engine
|
|
15
|
+
* actually prepared, so it cannot drift from the code the way a hand-passed
|
|
16
|
+
* label would, and it collapses to exactly one string per call site — which is
|
|
17
|
+
* what makes it safe as part of an aggregate key.
|
|
18
|
+
*
|
|
19
|
+
* ## What is thrown away, and why
|
|
20
|
+
*
|
|
21
|
+
* - **The projection.** `queryDeclared` always selects every column, so the
|
|
22
|
+
* `SELECT` list is the table's schema restated — pure noise, and the single
|
|
23
|
+
* longest part of the statement.
|
|
24
|
+
* - **The table name.** It is already the `collection` field of the report.
|
|
25
|
+
* - **The width of an `IN` list.** `IN (?, ?, … ×500)` and `IN (?)` are the
|
|
26
|
+
* same call site; keeping the literal placeholders would make every page of
|
|
27
|
+
* a chunked read its own aggregate row. The COUNT is kept (`IN (?×500)`)
|
|
28
|
+
* because it is the difference between "reads one row" and "reads five
|
|
29
|
+
* hundred", which is the whole question being asked.
|
|
30
|
+
*
|
|
31
|
+
* Bound values are never included. They are not needed to name a call site, and
|
|
32
|
+
* a statement's parameters routinely carry device ids, keys and user data — a
|
|
33
|
+
* profiler line is not a place to put them.
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* Longest fingerprint kept, in characters.
|
|
37
|
+
*
|
|
38
|
+
* A shape is a log field printed five to a line; past this it stops being
|
|
39
|
+
* readable and starts pushing the numbers off the end. Every real statement in
|
|
40
|
+
* this engine fits well inside it — the cap exists for the pathological
|
|
41
|
+
* `whereIn` with forty distinct fields, not for the normal case.
|
|
42
|
+
*/
|
|
43
|
+
export declare const SQL_SHAPE_MAX_LENGTH = 220;
|
|
44
|
+
/** Appended when a shape is cut at {@link SQL_SHAPE_MAX_LENGTH}. */
|
|
45
|
+
export declare const SQL_SHAPE_ELLIPSIS = "\u2026";
|
|
46
|
+
/**
|
|
47
|
+
* How many distinct raw statements the memo holds before it is dropped.
|
|
48
|
+
*
|
|
49
|
+
* The engine prepares its SQL fresh on every call, so without a memo this runs
|
|
50
|
+
* a handful of regexes per statement at a few hundred statements a second.
|
|
51
|
+
* With one, it runs them once per call site. The bound exists because the raw
|
|
52
|
+
* key is unbounded in principle (an `IN` list of a new width mints a new
|
|
53
|
+
* entry): at the cap the memo is cleared rather than grown, which costs one
|
|
54
|
+
* re-derivation per shape and cannot leak.
|
|
55
|
+
*/
|
|
56
|
+
export declare const SQL_SHAPE_MEMO_MAX_ENTRIES = 512;
|
|
57
|
+
/**
|
|
58
|
+
* Name the shape of `sql`, whose table is `collection`.
|
|
59
|
+
*
|
|
60
|
+
* Memoised on the raw statement — see {@link SQL_SHAPE_MEMO_MAX_ENTRIES}.
|
|
61
|
+
*/
|
|
62
|
+
export declare function sqlShape(sql: string, collection: string): string;
|
|
63
|
+
/** Test seam: forget every memoised shape. */
|
|
64
|
+
export declare function resetSqlShapeMemo(): void;
|
package/dist/index.js
CHANGED
|
@@ -26,7 +26,7 @@ require("./builtins/system-config/index.js");
|
|
|
26
26
|
const require_builtins_winston_logging_index = require("./builtins/winston-logging/index.js");
|
|
27
27
|
const require_file_data_plane = require("./file-data-plane-DO8KbxCe.js");
|
|
28
28
|
const require_tls$1 = require("./tls-u8QCJCFE.js");
|
|
29
|
-
const require_manifest_python_deps = require("./manifest-python-deps-
|
|
29
|
+
const require_manifest_python_deps = require("./manifest-python-deps-C9x9vrqt.js");
|
|
30
30
|
const require_resource_monitor = require("./resource-monitor-CdnzxBLP.js");
|
|
31
31
|
const require_custom_action_registry = require("./custom-action-registry-jY0NOZK8.js");
|
|
32
32
|
let zod = require("zod");
|
|
@@ -94022,6 +94022,34 @@ function positiveInt(raw, fallback) {
|
|
|
94022
94022
|
const n = Number.parseInt(raw, 10);
|
|
94023
94023
|
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
94024
94024
|
}
|
|
94025
|
+
/**
|
|
94026
|
+
* The child's environment, with the allocator pair applied or REMOVED.
|
|
94027
|
+
*
|
|
94028
|
+
* `runnerNativeAllocatorEnv` returns what to add. That was sufficient while the
|
|
94029
|
+
* parent had nothing to inherit: omitting a key meant the child never saw it.
|
|
94030
|
+
* Once the image entrypoint welds the pair into the container environment — the
|
|
94031
|
+
* change that lets an operator drop them from the Unraid template — the child
|
|
94032
|
+
* INHERITS them, and a kill switch that only omits is a kill switch that does
|
|
94033
|
+
* nothing.
|
|
94034
|
+
*
|
|
94035
|
+
* So `CAMSTACK_RUNNER_NATIVE_ALLOCATOR=off` DELETES. And only that: the
|
|
94036
|
+
* non-linux path returns nothing to add for its own reasons (the pin is a pure
|
|
94037
|
+
* throughput cut there — measured `sharp.concurrency()` 6 on the darwin agent
|
|
94038
|
+
* against 1 on the linux hub) and must not be read as "remove what the operator
|
|
94039
|
+
* chose". Deletion is the switch's job, not absence's.
|
|
94040
|
+
*/
|
|
94041
|
+
function applyRunnerNativeAllocator(parentEnv, platform = process.platform) {
|
|
94042
|
+
const next = { ...parentEnv };
|
|
94043
|
+
if (parentEnv["CAMSTACK_RUNNER_NATIVE_ALLOCATOR"] === "off") {
|
|
94044
|
+
delete next["MALLOC_ARENA_MAX"];
|
|
94045
|
+
delete next["VIPS_CONCURRENCY"];
|
|
94046
|
+
return next;
|
|
94047
|
+
}
|
|
94048
|
+
return {
|
|
94049
|
+
...next,
|
|
94050
|
+
...runnerNativeAllocatorEnv(parentEnv, platform)
|
|
94051
|
+
};
|
|
94052
|
+
}
|
|
94025
94053
|
//#endregion
|
|
94026
94054
|
//#region src/kernel/moleculer/process-service.ts
|
|
94027
94055
|
/**
|
|
@@ -94319,7 +94347,7 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
|
|
|
94319
94347
|
CAMSTACK_LOG_LEVEL: process.env["CAMSTACK_LOG_LEVEL"] ?? "info",
|
|
94320
94348
|
...parentTcpPort !== void 0 ? { CAMSTACK_PARENT_TCP_PORT: String(parentTcpPort) } : {},
|
|
94321
94349
|
...parentUdsPath !== void 0 ? { CAMSTACK_PARENT_UDS_PATH: parentUdsPath } : {},
|
|
94322
|
-
...
|
|
94350
|
+
...applyRunnerNativeAllocator(process.env),
|
|
94323
94351
|
...env
|
|
94324
94352
|
};
|
|
94325
94353
|
const heapFlags = runnerHeapFlags(addons);
|
package/dist/index.mjs
CHANGED
|
@@ -25,7 +25,7 @@ import "./builtins/system-config/index.mjs";
|
|
|
25
25
|
import { WinstonDestination, WinstonLoggingAddon } from "./builtins/winston-logging/index.mjs";
|
|
26
26
|
import { a as parseTokenizedUrl, c as collectModelFiles, d as downloadModel, f as ensureModel, h as isModelDownloaded, i as parseRangeHeader, l as deleteModelFromDisk, m as getModelFilePath, n as contentTypeFor, o as resolveFilePath, p as fetchJson, r as createAuthenticatedFileServer, s as ModelDownloadService, t as createFileDataPlaneHandler, u as downloadFile } from "./file-data-plane-BhKdxJgf.mjs";
|
|
27
27
|
import { C as registerLanHttpHandler, S as readLanHttpState, _ as DEFAULT_LAN_HTTP_PORT, a as writeTlsMode, b as bindPendingLanHttp, c as loadTlsCert, d as collectCertIdentity, f as CA_VALIDITY_DAYS, g as evaluateExistingCert, h as SERVER_AUTH_OID, i as writeExtraSans, l as reissueTlsLeaf, m as MAX_LEAF_VALIDITY_DAYS, n as readTlsAccessStatus, o as validateUploadedTls, p as LEAF_RENEWAL_WINDOW_DAYS, r as readTlsMode, s as ensureTlsCert, t as readExtraSans, u as CA_COMMON_NAME, v as allFamiliesListenHost, x as closeLanHttp, y as applyLanHttp } from "./tls-CQhPGSJm.mjs";
|
|
28
|
-
import { $ as buildNativeCapProxy, A as createHubCapForwardService, At as buildHeapSample, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as runNpm, D as localProviderLink, Dt as HEAP_WATCH_INTERVAL_MS, E as ipcParentLink, Et as HEAP_RECLAIM_TRIGGER_MB, F as createUdsLogger, Ft as strandedMb, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, Mt as shouldReclaim, N as createUdsEventBus, Nt as startHeapWatch, O as HUB_CAP_FWD_ACTION, Ot as HEAP_WATCH_WARN_RATIO, P as udsChildLogToWorkerEntry, Pt as startRunnerHeapWatch, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, St as resolveNpmInvocation, T as ipcChildLink, Tt as HEAP_RECLAIM_MIN_INTERVAL_MS, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as createKernelHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as installManifestNativeDeps, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getBrokerEventBus, dt as capBareAction, et as buildUdsNativeCapProxy, f as getMoleculerEventStats, ft as capServiceName, g as AddonDepsManager, gt as DeviceRegistry, h as subscribePassthrough, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, jt as createV8Reclaimer, k as HUB_CAP_FWD_SERVICE, kt as RUNNER_HEAP_WATCH_INTERVAL_MS, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as setNodeEventInterest, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as registerEventBusService, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as clusterEventTopic, ut as capActionSuffix, v as resolveHwAccel, vt as CapabilityUnavailableError, w as buildLinkChain, wt as createAddonDataPlaneFacility, x as getCapUsageRegistry, xt as resolveAddonClass, y as CapUsageRegistry, yt as copyBundledNativeModules, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-
|
|
28
|
+
import { $ as buildNativeCapProxy, A as createHubCapForwardService, At as buildHeapSample, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as runNpm, D as localProviderLink, Dt as HEAP_WATCH_INTERVAL_MS, E as ipcParentLink, Et as HEAP_RECLAIM_TRIGGER_MB, F as createUdsLogger, Ft as strandedMb, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, Mt as shouldReclaim, N as createUdsEventBus, Nt as startHeapWatch, O as HUB_CAP_FWD_ACTION, Ot as HEAP_WATCH_WARN_RATIO, P as udsChildLogToWorkerEntry, Pt as startRunnerHeapWatch, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, St as resolveNpmInvocation, T as ipcChildLink, Tt as HEAP_RECLAIM_MIN_INTERVAL_MS, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as createKernelHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as installManifestNativeDeps, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getBrokerEventBus, dt as capBareAction, et as buildUdsNativeCapProxy, f as getMoleculerEventStats, ft as capServiceName, g as AddonDepsManager, gt as DeviceRegistry, h as subscribePassthrough, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, jt as createV8Reclaimer, k as HUB_CAP_FWD_SERVICE, kt as RUNNER_HEAP_WATCH_INTERVAL_MS, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as setNodeEventInterest, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as registerEventBusService, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as clusterEventTopic, ut as capActionSuffix, v as resolveHwAccel, vt as CapabilityUnavailableError, w as buildLinkChain, wt as createAddonDataPlaneFacility, x as getCapUsageRegistry, xt as resolveAddonClass, y as CapUsageRegistry, yt as copyBundledNativeModules, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-08rqn6xN.mjs";
|
|
29
29
|
import { n as getSinglePidStats, t as getPidStats } from "./resource-monitor-BWmQ5i-o.mjs";
|
|
30
30
|
import { t as CustomActionRegistry } from "./custom-action-registry-F__gp_VX.mjs";
|
|
31
31
|
import { z } from "zod";
|
|
@@ -94015,6 +94015,34 @@ function positiveInt(raw, fallback) {
|
|
|
94015
94015
|
const n = Number.parseInt(raw, 10);
|
|
94016
94016
|
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
94017
94017
|
}
|
|
94018
|
+
/**
|
|
94019
|
+
* The child's environment, with the allocator pair applied or REMOVED.
|
|
94020
|
+
*
|
|
94021
|
+
* `runnerNativeAllocatorEnv` returns what to add. That was sufficient while the
|
|
94022
|
+
* parent had nothing to inherit: omitting a key meant the child never saw it.
|
|
94023
|
+
* Once the image entrypoint welds the pair into the container environment — the
|
|
94024
|
+
* change that lets an operator drop them from the Unraid template — the child
|
|
94025
|
+
* INHERITS them, and a kill switch that only omits is a kill switch that does
|
|
94026
|
+
* nothing.
|
|
94027
|
+
*
|
|
94028
|
+
* So `CAMSTACK_RUNNER_NATIVE_ALLOCATOR=off` DELETES. And only that: the
|
|
94029
|
+
* non-linux path returns nothing to add for its own reasons (the pin is a pure
|
|
94030
|
+
* throughput cut there — measured `sharp.concurrency()` 6 on the darwin agent
|
|
94031
|
+
* against 1 on the linux hub) and must not be read as "remove what the operator
|
|
94032
|
+
* chose". Deletion is the switch's job, not absence's.
|
|
94033
|
+
*/
|
|
94034
|
+
function applyRunnerNativeAllocator(parentEnv, platform = process.platform) {
|
|
94035
|
+
const next = { ...parentEnv };
|
|
94036
|
+
if (parentEnv["CAMSTACK_RUNNER_NATIVE_ALLOCATOR"] === "off") {
|
|
94037
|
+
delete next["MALLOC_ARENA_MAX"];
|
|
94038
|
+
delete next["VIPS_CONCURRENCY"];
|
|
94039
|
+
return next;
|
|
94040
|
+
}
|
|
94041
|
+
return {
|
|
94042
|
+
...next,
|
|
94043
|
+
...runnerNativeAllocatorEnv(parentEnv, platform)
|
|
94044
|
+
};
|
|
94045
|
+
}
|
|
94018
94046
|
//#endregion
|
|
94019
94047
|
//#region src/kernel/moleculer/process-service.ts
|
|
94020
94048
|
/**
|
|
@@ -94312,7 +94340,7 @@ function createProcessService(parentNodeId, dataDir, deps, parentTcpPort, parent
|
|
|
94312
94340
|
CAMSTACK_LOG_LEVEL: process.env["CAMSTACK_LOG_LEVEL"] ?? "info",
|
|
94313
94341
|
...parentTcpPort !== void 0 ? { CAMSTACK_PARENT_TCP_PORT: String(parentTcpPort) } : {},
|
|
94314
94342
|
...parentUdsPath !== void 0 ? { CAMSTACK_PARENT_UDS_PATH: parentUdsPath } : {},
|
|
94315
|
-
...
|
|
94343
|
+
...applyRunnerNativeAllocator(process.env),
|
|
94316
94344
|
...env
|
|
94317
94345
|
};
|
|
94318
94346
|
const heapFlags = runnerHeapFlags(addons);
|
|
@@ -93,3 +93,20 @@ export interface RunnerNativeAllocatorEnv {
|
|
|
93
93
|
* exists to leave.
|
|
94
94
|
*/
|
|
95
95
|
export declare function runnerNativeAllocatorEnv(parentEnv: NodeJS.ProcessEnv, platform?: NodeJS.Platform): RunnerNativeAllocatorEnv;
|
|
96
|
+
/**
|
|
97
|
+
* The child's environment, with the allocator pair applied or REMOVED.
|
|
98
|
+
*
|
|
99
|
+
* `runnerNativeAllocatorEnv` returns what to add. That was sufficient while the
|
|
100
|
+
* parent had nothing to inherit: omitting a key meant the child never saw it.
|
|
101
|
+
* Once the image entrypoint welds the pair into the container environment — the
|
|
102
|
+
* change that lets an operator drop them from the Unraid template — the child
|
|
103
|
+
* INHERITS them, and a kill switch that only omits is a kill switch that does
|
|
104
|
+
* nothing.
|
|
105
|
+
*
|
|
106
|
+
* So `CAMSTACK_RUNNER_NATIVE_ALLOCATOR=off` DELETES. And only that: the
|
|
107
|
+
* non-linux path returns nothing to add for its own reasons (the pin is a pure
|
|
108
|
+
* throughput cut there — measured `sharp.concurrency()` 6 on the darwin agent
|
|
109
|
+
* against 1 on the linux hub) and must not be read as "remove what the operator
|
|
110
|
+
* chose". Deletion is the switch's job, not absence's.
|
|
111
|
+
*/
|
|
112
|
+
export declare function applyRunnerNativeAllocator(parentEnv: NodeJS.ProcessEnv, platform?: NodeJS.Platform): NodeJS.ProcessEnv;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ServiceBroker } from 'moleculer';
|
|
2
1
|
import { TRPCLink } from '@trpc/client';
|
|
3
2
|
import { AnyRouter } from '@trpc/server';
|
|
4
|
-
import {
|
|
3
|
+
import { ServiceBroker } from 'moleculer';
|
|
4
|
+
import { CapCallInput, LocalChildRegistry } from '../transport/index.js';
|
|
5
5
|
/**
|
|
6
6
|
* Resolver for in-process capability providers.
|
|
7
7
|
*
|
|
@@ -51,11 +51,39 @@ export type CapUsageObserver = (obs: CapUsageObservation) => void;
|
|
|
51
51
|
export interface CapUsageObserverOptions {
|
|
52
52
|
/** The addon whose `ctx.api` produced this link chain. */
|
|
53
53
|
readonly callerAddonId: string;
|
|
54
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* Resolve which addon hosts the provider for `capName`. `null` means "not in
|
|
56
|
+
* the LOCAL registry" — which is every cross-process provider, and no longer
|
|
57
|
+
* means "do not record": see {@link UNRESOLVED_PROVIDER_ADDON_ID}.
|
|
58
|
+
*/
|
|
55
59
|
readonly providerAddonIdForCap: (capName: string) => string | null;
|
|
56
60
|
/** Sink for observations. MUST NOT throw — wrapper catches anyway. */
|
|
57
61
|
readonly observer: CapUsageObserver;
|
|
58
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Stands in for a provider the LOCAL registry cannot name.
|
|
65
|
+
*
|
|
66
|
+
* `providerAddonIdForCap` only searches the local registry, so it returns
|
|
67
|
+
* `null` for every cross-process provider — which is every forked runner,
|
|
68
|
+
* `sqlite-settings` included. That `null` used to drop the whole observation,
|
|
69
|
+
* caller and all. The result: `nodes.getCapUsageGraph`, an admin API whose
|
|
70
|
+
* entire job is to show caller → provider → cap traffic, returned an EMPTY
|
|
71
|
+
* ARRAY on the live hub while the engine logged 39 717 calls a minute to a
|
|
72
|
+
* single collection. An empty graph reads as "nothing is calling anything".
|
|
73
|
+
*
|
|
74
|
+
* The caller is always known — it is whoever made the call — and "who is
|
|
75
|
+
* hammering this cap" is the question the graph exists to answer. So the
|
|
76
|
+
* observation is recorded and the unknown half stays VISIBLY unknown, which is
|
|
77
|
+
* a fact an operator can act on, rather than absent, which is a fact nobody can
|
|
78
|
+
* see. Naming the provider properly needs the cluster-wide cap list; until then
|
|
79
|
+
* this placeholder is the honest value.
|
|
80
|
+
*/
|
|
81
|
+
export declare const UNRESOLVED_PROVIDER_ADDON_ID = "(unresolved: cross-process)";
|
|
82
|
+
/**
|
|
83
|
+
* Record one cap call, if an observer is configured. Extracted so the rule —
|
|
84
|
+
* an unresolved provider never erases the caller — has ONE place and a test.
|
|
85
|
+
*/
|
|
86
|
+
export declare function recordCapUsage(observerOpts: CapUsageObserverOptions, capName: string, methodName: string): void;
|
|
59
87
|
/**
|
|
60
88
|
* Non-terminating tRPC link that intercepts calls when the target
|
|
61
89
|
* capability has a provider registered in the local process.
|
|
@@ -6089,8 +6089,21 @@ function createHubCapForwardService(onUnownedCall) {
|
|
|
6089
6089
|
} } }
|
|
6090
6090
|
};
|
|
6091
6091
|
}
|
|
6092
|
-
|
|
6093
|
-
|
|
6092
|
+
/**
|
|
6093
|
+
* Record one cap call, if an observer is configured. Extracted so the rule —
|
|
6094
|
+
* an unresolved provider never erases the caller — has ONE place and a test.
|
|
6095
|
+
*/
|
|
6096
|
+
function recordCapUsage(observerOpts, capName, methodName) {
|
|
6097
|
+
try {
|
|
6098
|
+
observerOpts.observer({
|
|
6099
|
+
callerAddonId: observerOpts.callerAddonId,
|
|
6100
|
+
providerAddonId: observerOpts.providerAddonIdForCap(capName) ?? "(unresolved: cross-process)",
|
|
6101
|
+
capName,
|
|
6102
|
+
methodName,
|
|
6103
|
+
atMs: Date.now()
|
|
6104
|
+
});
|
|
6105
|
+
} catch {}
|
|
6106
|
+
}
|
|
6094
6107
|
/** Convert camelCase → kebab-case. */
|
|
6095
6108
|
function toKebab(name) {
|
|
6096
6109
|
return name.replace(/[A-Z]/g, (m, i) => (i > 0 ? "-" : "") + m.toLowerCase());
|
|
@@ -6140,16 +6153,7 @@ function localProviderLink(resolver, observerOpts) {
|
|
|
6140
6153
|
if (!provider || typeof provider !== "object") return next(opWithDefaults);
|
|
6141
6154
|
const fn = Reflect.get(provider, parsed.method);
|
|
6142
6155
|
if (typeof fn !== "function") return next(opWithDefaults);
|
|
6143
|
-
if (observerOpts)
|
|
6144
|
-
const providerAddonId = observerOpts.providerAddonIdForCap(parsed.capName);
|
|
6145
|
-
if (providerAddonId) observerOpts.observer({
|
|
6146
|
-
callerAddonId: observerOpts.callerAddonId,
|
|
6147
|
-
providerAddonId,
|
|
6148
|
-
capName: parsed.capName,
|
|
6149
|
-
methodName: parsed.method,
|
|
6150
|
-
atMs: Date.now()
|
|
6151
|
-
});
|
|
6152
|
-
} catch {}
|
|
6156
|
+
if (observerOpts) recordCapUsage(observerOpts, parsed.capName, parsed.method);
|
|
6153
6157
|
return observable((observer) => {
|
|
6154
6158
|
Promise.resolve(fn.call(provider, opWithDefaults.input)).then((data) => {
|
|
6155
6159
|
observer.next({ result: {
|
|
@@ -6410,9 +6414,9 @@ function brokerTransportLink(broker, serviceMap, observerOpts, linkOptions) {
|
|
|
6410
6414
|
}
|
|
6411
6415
|
if (observerOpts) try {
|
|
6412
6416
|
const providerAddonId = serviceName !== parsed.capName ? serviceName : observerOpts.providerAddonIdForCap(parsed.capName);
|
|
6413
|
-
|
|
6417
|
+
observerOpts.observer({
|
|
6414
6418
|
callerAddonId: observerOpts.callerAddonId,
|
|
6415
|
-
providerAddonId,
|
|
6419
|
+
providerAddonId: providerAddonId ?? "(unresolved: cross-process)",
|
|
6416
6420
|
capName: parsed.capName,
|
|
6417
6421
|
methodName: parsed.method,
|
|
6418
6422
|
atMs: Date.now()
|
|
@@ -6093,8 +6093,21 @@ function createHubCapForwardService(onUnownedCall) {
|
|
|
6093
6093
|
} } }
|
|
6094
6094
|
};
|
|
6095
6095
|
}
|
|
6096
|
-
|
|
6097
|
-
|
|
6096
|
+
/**
|
|
6097
|
+
* Record one cap call, if an observer is configured. Extracted so the rule —
|
|
6098
|
+
* an unresolved provider never erases the caller — has ONE place and a test.
|
|
6099
|
+
*/
|
|
6100
|
+
function recordCapUsage(observerOpts, capName, methodName) {
|
|
6101
|
+
try {
|
|
6102
|
+
observerOpts.observer({
|
|
6103
|
+
callerAddonId: observerOpts.callerAddonId,
|
|
6104
|
+
providerAddonId: observerOpts.providerAddonIdForCap(capName) ?? "(unresolved: cross-process)",
|
|
6105
|
+
capName,
|
|
6106
|
+
methodName,
|
|
6107
|
+
atMs: Date.now()
|
|
6108
|
+
});
|
|
6109
|
+
} catch {}
|
|
6110
|
+
}
|
|
6098
6111
|
/** Convert camelCase → kebab-case. */
|
|
6099
6112
|
function toKebab(name) {
|
|
6100
6113
|
return name.replace(/[A-Z]/g, (m, i) => (i > 0 ? "-" : "") + m.toLowerCase());
|
|
@@ -6144,16 +6157,7 @@ function localProviderLink(resolver, observerOpts) {
|
|
|
6144
6157
|
if (!provider || typeof provider !== "object") return next(opWithDefaults);
|
|
6145
6158
|
const fn = Reflect.get(provider, parsed.method);
|
|
6146
6159
|
if (typeof fn !== "function") return next(opWithDefaults);
|
|
6147
|
-
if (observerOpts)
|
|
6148
|
-
const providerAddonId = observerOpts.providerAddonIdForCap(parsed.capName);
|
|
6149
|
-
if (providerAddonId) observerOpts.observer({
|
|
6150
|
-
callerAddonId: observerOpts.callerAddonId,
|
|
6151
|
-
providerAddonId,
|
|
6152
|
-
capName: parsed.capName,
|
|
6153
|
-
methodName: parsed.method,
|
|
6154
|
-
atMs: Date.now()
|
|
6155
|
-
});
|
|
6156
|
-
} catch {}
|
|
6160
|
+
if (observerOpts) recordCapUsage(observerOpts, parsed.capName, parsed.method);
|
|
6157
6161
|
return observable((observer) => {
|
|
6158
6162
|
Promise.resolve(fn.call(provider, opWithDefaults.input)).then((data) => {
|
|
6159
6163
|
observer.next({ result: {
|
|
@@ -6414,9 +6418,9 @@ function brokerTransportLink(broker, serviceMap, observerOpts, linkOptions) {
|
|
|
6414
6418
|
}
|
|
6415
6419
|
if (observerOpts) try {
|
|
6416
6420
|
const providerAddonId = serviceName !== parsed.capName ? serviceName : observerOpts.providerAddonIdForCap(parsed.capName);
|
|
6417
|
-
|
|
6421
|
+
observerOpts.observer({
|
|
6418
6422
|
callerAddonId: observerOpts.callerAddonId,
|
|
6419
|
-
providerAddonId,
|
|
6423
|
+
providerAddonId: providerAddonId ?? "(unresolved: cross-process)",
|
|
6420
6424
|
capName: parsed.capName,
|
|
6421
6425
|
methodName: parsed.method,
|
|
6422
6426
|
atMs: Date.now()
|