@camstack/types 1.2.441 → 1.2.443

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_sleep = require("./sleep-BpwNJ1Su.js");
2
+ const require_sleep = require("./sleep-E7qAQwTE.js");
3
3
  const require_event_category = require("./event-category-E17zaHjN.js");
4
- const require_composition = require("./composition-DCmtFE-X.js");
4
+ const require_composition = require("./composition-C0dlyG9U.js");
5
5
  const require_node_color = require("./node-color.js");
6
6
  const require_err_msg = require("./err-msg-COHgq7Mb.js");
7
7
  const require_enums = require("./enums.js");
@@ -43463,6 +43463,64 @@ var embeddingEncoderCapability = {
43463
43463
  }
43464
43464
  };
43465
43465
  //#endregion
43466
+ //#region src/capabilities/node-removal-hook.cap.ts
43467
+ /**
43468
+ * `node-removal-hook` — the reliable way an addon learns a cluster node was
43469
+ * FORGOTTEN, so it can remove or re-point what it keeps for that node. The
43470
+ * node-scoped twin of `device-removal-hook` (D865); same shape, same rules.
43471
+ *
43472
+ * ## Why not "the node is absent"
43473
+ *
43474
+ * A node missing from topology is offline, restarting or not yet re-registered —
43475
+ * it is not forgotten. Deleting node-scoped data from absence destroys a live
43476
+ * node's configuration on any failed or partial read (D49). So the hook is
43477
+ * driven by a TOMBSTONE the hub records when the operator forgets a node
43478
+ * (`AgentRegistryService.forgetClusterNode`); nothing else produces one.
43479
+ *
43480
+ * ## The pieces
43481
+ *
43482
+ * 1. The hub records a tombstone `{ nodeId, removedAt, pending }` before it calls
43483
+ * anyone. `pending` is the set of implementers that have not yet acknowledged.
43484
+ * 2. The hub calls `onNodeRemoved` on EVERY provider of this collection, one by
43485
+ * one, with bounded retries, logged; an acknowledgement removes the
43486
+ * implementer from `pending`.
43487
+ * 3. The hub re-drives still-pending tombstones at boot, so an implementer that
43488
+ * was down at forget time is told when it is back.
43489
+ *
43490
+ * ## Contract for an implementer
43491
+ *
43492
+ * - **Idempotent.** Called again by a retry and by the boot re-drive. A node with
43493
+ * nothing left answers zero counts.
43494
+ * - **One key per store** in `counts`, rows changed per store, so the log line
43495
+ * can say where the data was.
43496
+ * - **Re-point or remove, never guess.** A row that merely MENTIONS the node as
43497
+ * a preference is re-pointed to "unset"; a row that only means something on
43498
+ * that node (a runtime bound to it) is removed.
43499
+ * - A provider that cannot tell is allowed to throw; the caller retries it.
43500
+ */
43501
+ /** What a hook is told about the removal. */
43502
+ var NodeRemovedInputSchema = zod.z.object({
43503
+ nodeId: zod.z.string().min(1),
43504
+ /** Epoch ms the node was forgotten (the tombstone's `removedAt`). */
43505
+ removedAt: zod.z.number()
43506
+ });
43507
+ /** Rows changed (removed or re-pointed), per store this implementer owns. */
43508
+ var NodeRemovedResultSchema = zod.z.object({ counts: zod.z.record(zod.z.string(), zod.z.number().int().nonnegative()) });
43509
+ var nodeRemovalHookCapability = {
43510
+ name: "node-removal-hook",
43511
+ scope: "system",
43512
+ mode: "collection",
43513
+ internal: true,
43514
+ methods: {
43515
+ /** Remove or re-point everything this addon keeps for the forgotten node. Idempotent. */
43516
+ onNodeRemoved: require_sleep.method(NodeRemovedInputSchema, NodeRemovedResultSchema, {
43517
+ kind: "mutation",
43518
+ auth: "admin"
43519
+ }) },
43520
+ /** In-process only — enumerated through `addons.listCapabilityProviders`. */
43521
+ mount: { kind: "skip" }
43522
+ };
43523
+ //#endregion
43466
43524
  //#region src/capabilities/device-removal-hook.cap.ts
43467
43525
  /**
43468
43526
  * `device-removal-hook` — the reliable way an addon learns a device was DELETED,
@@ -59120,8 +59178,7 @@ var pipelineOrchestratorCapability = {
59120
59178
  * replaced the four flat orchestrator lists (`enabledNodes`,
59121
59179
  * `enabledDecoderNodes`, `enabledAudioNodes`, `remoteSourcingNodes`).
59122
59180
  * Each flag is optional in the patch: omit a flag to leave it unchanged,
59123
- * pass `null` to reset it to the node default (`hub` → capable, every
59124
- * other node → not). Detection/audio eligibility is derived from these
59181
+ * pass `null` to reset it to the default (ON for every node). Detection/audio eligibility is derived from these
59125
59182
  * flags across the cluster; changing them re-derives the enabled sets
59126
59183
  * and reconciles dispatch immediately. There is NO separate decode flag:
59127
59184
  * decode is always co-located with its frame consumer, so decode
@@ -92880,6 +92937,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
92880
92937
  access: "create",
92881
92938
  role: "admin"
92882
92939
  },
92940
+ "nodeRemovalHook.onNodeRemoved": {
92941
+ capName: "node-removal-hook",
92942
+ capScope: "system",
92943
+ addonId: null,
92944
+ access: "create",
92945
+ role: "admin"
92946
+ },
92883
92947
  "nodes.clusterAddonStatus": {
92884
92948
  capName: "nodes",
92885
92949
  capScope: "system",
@@ -98115,6 +98179,7 @@ var KNOWN_CAP_NAMES = [
98115
98179
  "network-link",
98116
98180
  "network-quality",
98117
98181
  "node-certificates",
98182
+ "node-removal-hook",
98118
98183
  "nodes",
98119
98184
  "notification-output",
98120
98185
  "notification-rules",
@@ -98348,6 +98413,7 @@ var SYSTEM_CAP_NAMES = [
98348
98413
  "network-access",
98349
98414
  "network-quality",
98350
98415
  "node-certificates",
98416
+ "node-removal-hook",
98351
98417
  "nodes",
98352
98418
  "notification-output",
98353
98419
  "notification-rules",
@@ -102224,6 +102290,7 @@ var SYSTEM_CAP_VOCABULARY = {
102224
102290
  "device-manager": system("the device registry itself — rename, move and remove are administration"),
102225
102291
  "device-provider": system(INTERNAL),
102226
102292
  "device-removal-hook": system(INTERNAL),
102293
+ "node-removal-hook": system(INTERNAL),
102227
102294
  "device-state": system("the runtime-state mirror every trigger already reads through"),
102228
102295
  "display-palette": system("the cluster display palette — one colour table for every client, not a device action"),
102229
102296
  "embedding-encoder": system(ML),
@@ -104216,6 +104283,7 @@ var CAPABILITY_NAMES = {
104216
104283
  networkLink: "network-link",
104217
104284
  networkQuality: "network-quality",
104218
104285
  nodeCertificates: "node-certificates",
104286
+ nodeRemovalHook: "node-removal-hook",
104219
104287
  nodes: "nodes",
104220
104288
  notificationOutput: "notification-output",
104221
104289
  notificationRules: "notification-rules",
@@ -104754,6 +104822,10 @@ var CAPABILITY_ROUTER_KEYS = [
104754
104822
  key: "nodeCertificates",
104755
104823
  name: "node-certificates"
104756
104824
  },
104825
+ {
104826
+ key: "nodeRemovalHook",
104827
+ name: "node-removal-hook"
104828
+ },
104757
104829
  {
104758
104830
  key: "nodes",
104759
104831
  name: "nodes"
@@ -105180,6 +105252,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
105180
105252
  networkLinkCapability,
105181
105253
  networkQualityCapability,
105182
105254
  nodeCertificatesCapability,
105255
+ nodeRemovalHookCapability,
105183
105256
  nodesCapability,
105184
105257
  notificationOutputCapability,
105185
105258
  notificationRulesCapability,
@@ -119078,6 +119151,7 @@ var OAUTH_LANDING_REASONS = [
119078
119151
  "unknown_broker",
119079
119152
  "not_configured",
119080
119153
  "exchange_failed",
119154
+ "no_authorization_code",
119081
119155
  "no_refresh_token",
119082
119156
  "unavailable",
119083
119157
  "unknown"
@@ -119305,6 +119379,83 @@ function filterForwardedCookies(cookieHeader) {
119305
119379
  return cookieHeader.split(";").map((part) => part.trim()).filter((part) => part.length > 0 && !part.toLowerCase().startsWith("camstack_")).join("; ");
119306
119380
  }
119307
119381
  //#endregion
119382
+ //#region src/vendor-login-proxy/flow-trace.ts
119383
+ /** A short stable label for the attempt: enough to group lines, useless as a capability. */
119384
+ function attemptLabel(attemptId) {
119385
+ let hash = 2166136261;
119386
+ for (let i = 0; i < attemptId.length; i += 1) hash = Math.imul(hash ^ attemptId.charCodeAt(i), 16777619) >>> 0;
119387
+ return hash.toString(16).padStart(8, "0");
119388
+ }
119389
+ /** Distinct parameter names of a `?a=1&b=2` string (or of a urlencoded body), in order. */
119390
+ function paramNames(query) {
119391
+ const names = [];
119392
+ const raw = query.startsWith("?") ? query.slice(1) : query;
119393
+ for (const pair of raw.split("&")) {
119394
+ if (pair.length === 0) continue;
119395
+ const eq = pair.indexOf("=");
119396
+ const rawName = eq === -1 ? pair : pair.slice(0, eq);
119397
+ let name;
119398
+ try {
119399
+ name = decodeURIComponent(rawName.replace(/\+/g, " "));
119400
+ } catch {
119401
+ name = rawName;
119402
+ }
119403
+ if (!names.includes(name)) names.push(name);
119404
+ }
119405
+ return names;
119406
+ }
119407
+ function hasOa2(names) {
119408
+ return names.some((name) => name.startsWith("openid.oa2.") || name === "openid.ns.oa2");
119409
+ }
119410
+ function cookieNamesOf(setCookies) {
119411
+ return setCookies.map((c) => c.split(";")[0]?.split("=")[0]?.trim() ?? "");
119412
+ }
119413
+ function mediaType(contentType) {
119414
+ return (contentType ?? "none").split(";")[0]?.trim().toLowerCase() || "none";
119415
+ }
119416
+ /** Host and path of a Location (no query), or 'unparsable'. */
119417
+ function locationShape(location, base) {
119418
+ try {
119419
+ const url = new URL(location, base);
119420
+ return {
119421
+ target: `${url.host}${url.pathname}`,
119422
+ names: paramNames(url.search)
119423
+ };
119424
+ } catch {
119425
+ return {
119426
+ target: "unparsable",
119427
+ names: []
119428
+ };
119429
+ }
119430
+ }
119431
+ /** The `meta` of one trace line. Values: never. */
119432
+ function flowTraceMeta(input) {
119433
+ const queryNames = paramNames(input.requestSearch);
119434
+ const formNames = input.formBody === null ? null : paramNames(input.formBody);
119435
+ const meta = {
119436
+ session: attemptLabel(input.attemptId),
119437
+ method: input.method,
119438
+ upstream: `${input.upstreamHost}${input.upstreamPath}`,
119439
+ queryNames: queryNames.join(",") || "none",
119440
+ oa2InQuery: hasOa2(queryNames),
119441
+ status: input.status,
119442
+ setCookieNames: cookieNamesOf(input.setCookies).join(",") || "none",
119443
+ cookiesSent: (input.cookieHeader ?? "").split(";").filter((p) => p.trim().length > 0).length
119444
+ };
119445
+ if (formNames !== null) {
119446
+ meta["contentType"] = mediaType(input.contentType);
119447
+ meta["formNames"] = formNames.join(",") || "none";
119448
+ meta["oa2InForm"] = hasOa2(formNames);
119449
+ }
119450
+ if (input.location !== null) {
119451
+ const loc = locationShape(input.location, `http://${input.upstreamHost}/`);
119452
+ meta["location"] = loc.target;
119453
+ meta["locationQueryNames"] = loc.names.join(",") || "none";
119454
+ meta["oa2InLocation"] = hasOa2(loc.names);
119455
+ }
119456
+ return meta;
119457
+ }
119458
+ //#endregion
119308
119459
  //#region src/vendor-login-proxy/login-proxy.ts
119309
119460
  var MAX_REQUEST_BODY_BYTES = 2 * 1024 * 1024;
119310
119461
  var MAX_TEXT_BODY_BYTES = 4 * 1024 * 1024;
@@ -119410,6 +119561,8 @@ var VendorLoginProxy = class {
119410
119561
  #now;
119411
119562
  #fetch;
119412
119563
  #attempts = /* @__PURE__ */ new Map();
119564
+ /** Trace lines written per attempt (bounded by FLOW_TRACE_MAX_LINES). */
119565
+ #traced = /* @__PURE__ */ new Map();
119413
119566
  constructor(options) {
119414
119567
  this.#addonId = options.addonId;
119415
119568
  this.#logger = options.logger;
@@ -119436,6 +119589,7 @@ var VendorLoginProxy = class {
119436
119589
  }
119437
119590
  close(attemptId) {
119438
119591
  this.#attempts.delete(attemptId);
119592
+ this.#traced.delete(attemptId);
119439
119593
  }
119440
119594
  isOpen(attemptId) {
119441
119595
  const attempt = this.#attempts.get(attemptId);
@@ -119482,7 +119636,7 @@ var VendorLoginProxy = class {
119482
119636
  const forwardedPrefix = (firstHeader(req.headers["x-forwarded-prefix"]) ?? "").replace(/\/+$/, "");
119483
119637
  const attempt = this.#attempts.get(parsed.attemptId);
119484
119638
  if (attempt === void 0 || this.#now() > attempt.expiresAtMs) {
119485
- if (attempt !== void 0) this.#attempts.delete(parsed.attemptId);
119639
+ if (attempt !== void 0) this.close(parsed.attemptId);
119486
119640
  this.#logger.info("vendor login proxy: attempt refused", { meta: {
119487
119641
  addonId: this.#addonId,
119488
119642
  reason: attempt === void 0 ? "missing" : "expired"
@@ -119507,13 +119661,17 @@ var VendorLoginProxy = class {
119507
119661
  }
119508
119662
  const method = (req.method ?? "GET").toUpperCase();
119509
119663
  let body;
119664
+ let formBodyRaw = null;
119510
119665
  if (method !== "GET" && method !== "HEAD") {
119511
119666
  const read = await readRequestBody(req);
119512
119667
  if (read === "too-large") {
119513
119668
  sendPlain(res, 413, "Request too large");
119514
119669
  return;
119515
119670
  }
119516
- body = isFormEncoded(req.headers["content-type"]) ? Buffer.from(rewriteFormBody(read.toString("utf8"), route), "utf8") : read;
119671
+ if (isFormEncoded(req.headers["content-type"])) {
119672
+ formBodyRaw = read.toString("utf8");
119673
+ body = Buffer.from(rewriteFormBody(formBodyRaw, route), "utf8");
119674
+ } else body = read;
119517
119675
  }
119518
119676
  const headers = this.#requestHeaders(req, route, upstream);
119519
119677
  const upstreamRes = await this.#fetch(target, {
@@ -119538,6 +119696,19 @@ var VendorLoginProxy = class {
119538
119696
  sentNames: cookieNames(headers["cookie"]),
119539
119697
  vendorSets: setCookies.map(setCookieShape).join(" | ")
119540
119698
  } });
119699
+ this.#trace(parsed.attemptId, {
119700
+ attemptId: parsed.attemptId,
119701
+ method,
119702
+ upstreamHost: upstream.host,
119703
+ upstreamPath: target.pathname,
119704
+ requestSearch: target.search,
119705
+ formBody: formBodyRaw,
119706
+ contentType: firstHeader(req.headers["content-type"]),
119707
+ status: upstreamRes.status,
119708
+ location,
119709
+ setCookies,
119710
+ cookieHeader: headers["cookie"]
119711
+ });
119541
119712
  const settled = await attempt.evaluate({
119542
119713
  status: upstreamRes.status,
119543
119714
  url: target.toString(),
@@ -119546,7 +119717,7 @@ var VendorLoginProxy = class {
119546
119717
  bodyText: text
119547
119718
  });
119548
119719
  if (settled.kind === "done") {
119549
- this.#attempts.delete(parsed.attemptId);
119720
+ this.close(parsed.attemptId);
119550
119721
  this.#logger.info("vendor login proxy: sign-in finished", { meta: {
119551
119722
  addonId: this.#addonId,
119552
119723
  status: settled.landing.status
@@ -119570,6 +119741,16 @@ var VendorLoginProxy = class {
119570
119741
  res.writeHead(upstreamRes.status, out);
119571
119742
  await this.#pipeBody(upstreamRes, res);
119572
119743
  }
119744
+ /** One names-only line per proxied request, at most FLOW_TRACE_MAX_LINES per attempt. */
119745
+ #trace(attemptId, input) {
119746
+ const written = this.#traced.get(attemptId) ?? 0;
119747
+ if (written >= 200) return;
119748
+ this.#traced.set(attemptId, written + 1);
119749
+ this.#logger.info("vendor login proxy: flow", { meta: {
119750
+ addonId: this.#addonId,
119751
+ ...flowTraceMeta(input)
119752
+ } });
119753
+ }
119573
119754
  #requestHeaders(req, route, upstream) {
119574
119755
  const headers = {};
119575
119756
  for (const [name, value] of Object.entries(req.headers)) {
@@ -122125,6 +122306,8 @@ exports.NodeDirectoryEntrySchema = NodeDirectoryEntrySchema;
122125
122306
  exports.NodeDirectorySchema = NodeDirectorySchema;
122126
122307
  exports.NodeHttpsStateSchema = NodeHttpsStateSchema;
122127
122308
  exports.NodeRecommendationSchema = NodeRecommendationSchema;
122309
+ exports.NodeRemovedInputSchema = NodeRemovedInputSchema;
122310
+ exports.NodeRemovedResultSchema = NodeRemovedResultSchema;
122128
122311
  exports.NotificationActionIconSchema = NotificationActionIconSchema;
122129
122312
  exports.NotificationActionSchema = NotificationActionSchema;
122130
122313
  exports.NotificationFormatSchema = NotificationFormatSchema;
@@ -122346,6 +122529,7 @@ exports.RESERVED_BINDING_NAMES = require_composition.RESERVED_BINDING_NAMES;
122346
122529
  exports.RESERVED_COLLECTION_SOURCES = RESERVED_COLLECTION_SOURCES;
122347
122530
  exports.RESET_PERIODS = require_composition.RESET_PERIODS;
122348
122531
  exports.RESTORED_CAP_NAMES = RESTORED_CAP_NAMES;
122532
+ exports.RESTREAM_INPUT_PROBE = require_composition.RESTREAM_INPUT_PROBE;
122349
122533
  exports.RETRAIN_REVIEW_ADJUSTED_IOU = RETRAIN_REVIEW_ADJUSTED_IOU;
122350
122534
  exports.RETRAIN_REVIEW_MATCH_IOU = RETRAIN_REVIEW_MATCH_IOU;
122351
122535
  exports.RETRAIN_REVIEW_STATUSES = RETRAIN_REVIEW_STATUSES;
@@ -123558,6 +123742,7 @@ exports.nodeBenchmarkRefusals = nodeBenchmarkRefusals;
123558
123742
  exports.nodeCertificatesCapability = nodeCertificatesCapability;
123559
123743
  exports.nodePickerItems = nodePickerItems;
123560
123744
  exports.nodePin = require_sleep.nodePin;
123745
+ exports.nodeRemovalHookCapability = nodeRemovalHookCapability;
123561
123746
  exports.nodesCapability = nodesCapability;
123562
123747
  exports.normalizeAddonInitResult = require_sleep.normalizeAddonInitResult;
123563
123748
  exports.normalizeAudioLabel = normalizeAudioLabel;