@camstack/server 1.2.84 → 1.2.85

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.
@@ -22,6 +22,7 @@
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
23
23
  exports.COLLECTION_ARRAY_METHODS = void 0;
24
24
  exports.aggregateCollectionCall = aggregateCollectionCall;
25
+ exports.addonPinOf = addonPinOf;
25
26
  exports.pinnedCollectionCall = pinnedCollectionCall;
26
27
  exports.createCapRouterPrimitives = createCapRouterPrimitives;
27
28
  exports.createCapRouterServices = createCapRouterServices;
@@ -120,6 +121,35 @@ async function aggregateCollectionCall(reg, capName, method, args) {
120
121
  const out = await fn(args);
121
122
  return Array.isArray(out) ? out : null;
122
123
  }
124
+ /**
125
+ * The `{ addonId }` a call PINS to, or `null` when the call is not an
126
+ * addon-pinned collection call and must route normally.
127
+ *
128
+ * ONE definition, because there are two places that must agree about it and
129
+ * they are in different packages. `LocalChildRegistry` decides whether to
130
+ * short-circuit a child's egress to a local sibling — and that decision happens
131
+ * BEFORE `onUnownedCall` runs, so a pin honoured only there is honoured never.
132
+ * That is precisely how the first attempt at this fix failed on the live hub:
133
+ * the handler was correct and unreachable, and the probe that proved it was a
134
+ * DELIBERATELY BOGUS addonId — refused if the selector routes, answered `null`
135
+ * if it is discarded. It came back `null`.
136
+ *
137
+ * Not an array method (that contract is the union) and not a non-collection cap
138
+ * (there `addonId` is provider DATA: `addons.getLogs({addonId})` names the addon
139
+ * whose logs you want, it does not name a provider).
140
+ */
141
+ function addonPinOf(reg, capName, method, args) {
142
+ if (!reg)
143
+ return null;
144
+ if (reg.getDefinition(capName)?.mode !== 'collection')
145
+ return null;
146
+ if (exports.COLLECTION_ARRAY_METHODS.get(capName)?.includes(method) === true)
147
+ return null;
148
+ if (args === null || typeof args !== 'object' || Array.isArray(args))
149
+ return null;
150
+ const addonId = args['addonId'];
151
+ return typeof addonId === 'string' && addonId.length > 0 ? addonId : null;
152
+ }
123
153
  /**
124
154
  * The `{ addonId }` PIN for a cap call arriving OUTSIDE the tRPC router — a
125
155
  * forked addon's `ctx.api.<cap>.<method>` reaching the parent's
@@ -160,20 +190,12 @@ async function aggregateCollectionCall(reg, capName, method, args) {
160
190
  * `null` legitimately, so a bare `null` could not be told from "not handled".
161
191
  */
162
192
  async function pinnedCollectionCall(reg, capName, method, args) {
163
- if (!reg)
164
- return null;
165
- if (reg.getDefinition(capName)?.mode !== 'collection')
166
- return null;
167
- if (exports.COLLECTION_ARRAY_METHODS.get(capName)?.includes(method) === true)
168
- return null;
169
- if (args === null || typeof args !== 'object' || Array.isArray(args))
170
- return null;
171
- const addonId = args['addonId'];
172
- if (typeof addonId !== 'string' || addonId.length === 0)
193
+ const addonId = addonPinOf(reg, capName, method, args);
194
+ if (addonId === null)
173
195
  return null;
174
- const provider = reg.getProviderByAddonId(capName, addonId);
196
+ const provider = reg?.getProviderByAddonId(capName, addonId) ?? null;
175
197
  if (provider === null) {
176
- const valid = reg.getProviderAddonIds(capName);
198
+ const valid = reg?.getProviderAddonIds(capName) ?? [];
177
199
  throw new Error(`Capability "${capName}" has no provider with addonId "${addonId}". ` +
178
200
  `Registered: ${valid.length > 0 ? valid.join(', ') : '(none)'}`);
179
201
  }
@@ -349,6 +349,14 @@ class MoleculerService {
349
349
  return false;
350
350
  return cap_router_runtime_js_1.COLLECTION_ARRAY_METHODS.get(capName)?.includes(method) === true;
351
351
  },
352
+ // Same story for an explicit `{ addonId }` selector: the sibling
353
+ // short-circuit picks the FIRST-registered child, which is precisely
354
+ // what the selector exists to override. `onUnownedCall` then resolves
355
+ // the named provider via `callPinnedCollection`. Shares `addonPinOf`
356
+ // with it — the bypass and the routing must never disagree about what
357
+ // counts as a pin, or the call is bounced out of the sibling path and
358
+ // then routed by first-provider anyway.
359
+ isAddonPinnedCall: (capName, method, args) => (0, cap_router_runtime_js_1.addonPinOf)(this.capabilityService.getRegistry(), capName, method, args) !== null,
352
360
  // Bound every UDS cap call by the cap's own declared timeout, falling
353
361
  // back to the channel default. Same resolver the REMOTE plane uses
354
362
  // below, so a method that survives 16 minutes across nodes survives it
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/server",
3
- "version": "1.2.84",
3
+ "version": "1.2.85",
4
4
  "private": false,
5
5
  "files": [
6
6
  "dist",
@@ -43,7 +43,7 @@
43
43
  "@camstack/addon-post-analysis": "1.2.56",
44
44
  "@camstack/sdk": "1.2.11",
45
45
  "@camstack/shm-ring": "1.1.9",
46
- "@camstack/system": "1.2.71",
46
+ "@camstack/system": "1.2.72",
47
47
  "@camstack/types": "1.2.55",
48
48
  "@camstack/ui-library": "1.2.38",
49
49
  "@fastify/compress": "^9.0.0",