@timeax/digital-service-engine 0.2.1 → 0.2.3

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.
@@ -4663,6 +4663,9 @@ function toFiniteNumber(v) {
4663
4663
  const n = Number(v);
4664
4664
  return Number.isFinite(n) ? n : NaN;
4665
4665
  }
4666
+ function isValidServiceIdRef(value) {
4667
+ return typeof value === "number" && Number.isFinite(value) || typeof value === "string" && value.trim().length > 0;
4668
+ }
4666
4669
  function constraintFitOk(svcMap, candidate, constraints) {
4667
4670
  const cap = getServiceCapability(svcMap, candidate);
4668
4671
  if (!cap) return false;
@@ -4671,18 +4674,27 @@ function constraintFitOk(svcMap, candidate, constraints) {
4671
4674
  return !(constraints.cancel === true && !cap.cancel);
4672
4675
  }
4673
4676
  function getServiceCapability(svcMap, candidate) {
4674
- if (candidate === void 0 || candidate === null) return void 0;
4675
- const direct = svcMap[candidate];
4676
- if (direct) return direct;
4677
- const byString = svcMap[String(candidate)];
4678
- if (byString) return byString;
4679
- if (typeof candidate === "string") {
4680
- const maybeNumber = Number(candidate);
4681
- if (Number.isFinite(maybeNumber)) {
4682
- return svcMap[maybeNumber];
4683
- }
4677
+ var _a;
4678
+ return (_a = getServiceCapabilityEntry(svcMap, candidate)) == null ? void 0 : _a.capability;
4679
+ }
4680
+ function getServiceCapabilityAliases(svcMap, candidate) {
4681
+ const entry = getServiceCapabilityEntry(svcMap, candidate);
4682
+ if (!entry) return [];
4683
+ return collectServiceRefAliases(entry.key, entry.capability);
4684
+ }
4685
+ function isSameServiceCapabilityRef(svcMap, left, right) {
4686
+ if (!isValidServiceIdRef(left) || !isValidServiceIdRef(right)) return false;
4687
+ const leftAliases = new Set(
4688
+ getServiceCapabilityAliases(svcMap, left).map((value) => String(value))
4689
+ );
4690
+ if (!leftAliases.size) {
4691
+ leftAliases.add(String(left));
4684
4692
  }
4685
- return void 0;
4693
+ const rightAliases = getServiceCapabilityAliases(svcMap, right);
4694
+ if (!rightAliases.length) {
4695
+ return leftAliases.has(String(right));
4696
+ }
4697
+ return rightAliases.some((value) => leftAliases.has(String(value)));
4686
4698
  }
4687
4699
  function normalizeRatePolicy(policy) {
4688
4700
  var _a;
@@ -4718,6 +4730,74 @@ function rateOk(svcMap, candidate, primary, policy) {
4718
4730
  if (!Number.isFinite(cRate) || !Number.isFinite(pRate)) return false;
4719
4731
  return passesRatePolicy(policy.ratePolicy, pRate, cRate);
4720
4732
  }
4733
+ function getServiceCapabilityEntry(svcMap, candidate) {
4734
+ if (candidate === void 0 || candidate === null) return void 0;
4735
+ const direct = svcMap[candidate];
4736
+ if (direct) {
4737
+ return { key: String(candidate), capability: direct };
4738
+ }
4739
+ const byString = svcMap[String(candidate)];
4740
+ if (byString) {
4741
+ return { key: String(candidate), capability: byString };
4742
+ }
4743
+ if (typeof candidate === "string") {
4744
+ const maybeNumber = Number(candidate);
4745
+ if (Number.isFinite(maybeNumber)) {
4746
+ const byNumber = svcMap[maybeNumber];
4747
+ if (byNumber) {
4748
+ return { key: String(maybeNumber), capability: byNumber };
4749
+ }
4750
+ }
4751
+ }
4752
+ const target = String(candidate);
4753
+ for (const [key, capability] of Object.entries(svcMap != null ? svcMap : {})) {
4754
+ if (collectServiceRefAliases(key, capability).some(
4755
+ (alias) => String(alias) === target
4756
+ )) {
4757
+ return { key, capability };
4758
+ }
4759
+ }
4760
+ return void 0;
4761
+ }
4762
+ function collectServiceRefAliases(key, capability) {
4763
+ const out = [];
4764
+ const seen = /* @__PURE__ */ new Set();
4765
+ const push = (value) => {
4766
+ if (!isValidServiceIdRef(value)) return;
4767
+ const normalized = normalizeServiceRef(value);
4768
+ if (!normalized) return;
4769
+ const aliasKey = String(normalized);
4770
+ if (seen.has(aliasKey)) return;
4771
+ seen.add(aliasKey);
4772
+ out.push(normalized);
4773
+ };
4774
+ push(getCanonicalServiceRef(key, capability));
4775
+ push(capability.service);
4776
+ push(capability.key);
4777
+ push(capability.id);
4778
+ return out;
4779
+ }
4780
+ function getCanonicalServiceRef(key, capability) {
4781
+ const explicitRefs = [capability.service, capability.key, capability.id];
4782
+ for (const ref of explicitRefs) {
4783
+ if (!isValidServiceIdRef(ref)) continue;
4784
+ if (String(ref) === key) {
4785
+ return ref;
4786
+ }
4787
+ }
4788
+ return normalizeServiceRef(key);
4789
+ }
4790
+ function normalizeServiceRef(value) {
4791
+ if (!isValidServiceIdRef(value)) return void 0;
4792
+ if (typeof value === "number") return value;
4793
+ const trimmed = value.trim();
4794
+ if (!trimmed) return void 0;
4795
+ const asNumber = Number(trimmed);
4796
+ if (Number.isFinite(asNumber) && String(asNumber) === trimmed) {
4797
+ return asNumber;
4798
+ }
4799
+ return trimmed;
4800
+ }
4721
4801
 
4722
4802
  // src/core/validate/steps/rates.ts
4723
4803
  function validateRates(v) {
@@ -6163,7 +6243,7 @@ function collectFailedFallbacks(props, services, settings) {
6163
6243
  const s = { ...DEFAULT_SETTINGS, ...settings != null ? settings : {} };
6164
6244
  const out = [];
6165
6245
  const fb = (_a = props.fallbacks) != null ? _a : {};
6166
- const primaryRate = (p) => rateOf(services, p);
6246
+ const primaryRate = (primary) => rateOf(services, primary);
6167
6247
  for (const [nodeId, list] of Object.entries((_b = fb.nodes) != null ? _b : {})) {
6168
6248
  const { primary, tagContexts } = primaryForNode(props, nodeId);
6169
6249
  if (!primary) {
@@ -6176,34 +6256,34 @@ function collectFailedFallbacks(props, services, settings) {
6176
6256
  });
6177
6257
  continue;
6178
6258
  }
6179
- for (const cand of list) {
6180
- const cap = getCap(services, cand);
6181
- if (!cap) {
6259
+ for (const candidate of list) {
6260
+ const capability = getCap(services, candidate);
6261
+ if (!capability) {
6182
6262
  out.push({
6183
6263
  scope: "node",
6184
6264
  nodeId,
6185
6265
  primary,
6186
- candidate: cand,
6266
+ candidate,
6187
6267
  reason: "unknown_service"
6188
6268
  });
6189
6269
  continue;
6190
6270
  }
6191
- if (String(cand) === String(primary)) {
6271
+ if (isSameServiceCapabilityRef(services, candidate, primary)) {
6192
6272
  out.push({
6193
6273
  scope: "node",
6194
6274
  nodeId,
6195
6275
  primary,
6196
- candidate: cand,
6276
+ candidate,
6197
6277
  reason: "cycle"
6198
6278
  });
6199
6279
  continue;
6200
6280
  }
6201
- if (!passesRate(s.ratePolicy, primaryRate(primary), cap.rate)) {
6281
+ if (!passesRate(s.ratePolicy, primaryRate(primary), capability.rate)) {
6202
6282
  out.push({
6203
6283
  scope: "node",
6204
6284
  nodeId,
6205
6285
  primary,
6206
- candidate: cand,
6286
+ candidate,
6207
6287
  reason: "rate_violation"
6208
6288
  });
6209
6289
  continue;
@@ -6213,58 +6293,55 @@ function collectFailedFallbacks(props, services, settings) {
6213
6293
  scope: "node",
6214
6294
  nodeId,
6215
6295
  primary,
6216
- candidate: cand,
6296
+ candidate,
6217
6297
  reason: "no_tag_context"
6218
6298
  });
6219
6299
  continue;
6220
6300
  }
6221
- let anyPass = false;
6222
- let anyFail = false;
6223
6301
  for (const tagId of tagContexts) {
6224
- const ok2 = s.requireConstraintFit ? satisfiesTagConstraints(tagId, { services, props }, cap) : true;
6225
- if (ok2) anyPass = true;
6226
- else {
6227
- anyFail = true;
6228
- out.push({
6229
- scope: "node",
6230
- nodeId,
6231
- primary,
6232
- candidate: cand,
6233
- tagContext: tagId,
6234
- reason: "constraint_mismatch"
6235
- });
6236
- }
6302
+ const fitsConstraints = s.requireConstraintFit ? satisfiesTagConstraints(
6303
+ tagId,
6304
+ { services, props },
6305
+ capability
6306
+ ) : true;
6307
+ if (fitsConstraints) continue;
6308
+ out.push({
6309
+ scope: "node",
6310
+ nodeId,
6311
+ primary,
6312
+ candidate,
6313
+ tagContext: tagId,
6314
+ reason: "constraint_mismatch"
6315
+ });
6237
6316
  }
6238
- void anyPass;
6239
- void anyFail;
6240
6317
  }
6241
6318
  }
6242
6319
  for (const [primary, list] of Object.entries((_c = fb.global) != null ? _c : {})) {
6243
- for (const cand of list) {
6244
- const cap = getCap(services, cand);
6245
- if (!cap) {
6320
+ for (const candidate of list) {
6321
+ const capability = getCap(services, candidate);
6322
+ if (!capability) {
6246
6323
  out.push({
6247
6324
  scope: "global",
6248
6325
  primary,
6249
- candidate: cand,
6326
+ candidate,
6250
6327
  reason: "unknown_service"
6251
6328
  });
6252
6329
  continue;
6253
6330
  }
6254
- if (String(cand) === String(primary)) {
6331
+ if (isSameServiceCapabilityRef(services, candidate, primary)) {
6255
6332
  out.push({
6256
6333
  scope: "global",
6257
6334
  primary,
6258
- candidate: cand,
6335
+ candidate,
6259
6336
  reason: "cycle"
6260
6337
  });
6261
6338
  continue;
6262
6339
  }
6263
- if (!passesRate(s.ratePolicy, primaryRate(primary), cap.rate)) {
6340
+ if (!passesRate(s.ratePolicy, primaryRate(primary), capability.rate)) {
6264
6341
  out.push({
6265
6342
  scope: "global",
6266
6343
  primary,
6267
- candidate: cand,
6344
+ candidate,
6268
6345
  reason: "rate_violation"
6269
6346
  });
6270
6347
  }
@@ -6273,52 +6350,61 @@ function collectFailedFallbacks(props, services, settings) {
6273
6350
  return out;
6274
6351
  }
6275
6352
  function rateOf(map, id) {
6276
- var _a;
6353
+ var _a, _b;
6277
6354
  if (id === void 0 || id === null) return void 0;
6278
- const c = getCap(map, id);
6279
- return (_a = c == null ? void 0 : c.rate) != null ? _a : void 0;
6355
+ return (_b = (_a = getCap(map, id)) == null ? void 0 : _a.rate) != null ? _b : void 0;
6280
6356
  }
6281
- function passesRate(policy, primaryRate, candRate) {
6282
- if (typeof candRate !== "number" || !Number.isFinite(candRate))
6357
+ function passesRate(policy, primaryRate, candidateRate) {
6358
+ if (typeof candidateRate !== "number" || !Number.isFinite(candidateRate)) {
6283
6359
  return false;
6284
- if (typeof primaryRate !== "number" || !Number.isFinite(primaryRate))
6360
+ }
6361
+ if (typeof primaryRate !== "number" || !Number.isFinite(primaryRate)) {
6285
6362
  return false;
6286
- return passesRatePolicy(normalizeRatePolicy(policy), primaryRate, candRate);
6363
+ }
6364
+ return passesRatePolicy(
6365
+ normalizeRatePolicy(policy),
6366
+ primaryRate,
6367
+ candidateRate
6368
+ );
6287
6369
  }
6288
6370
  function getCap(map, id) {
6289
6371
  return getServiceCapability(map, id);
6290
6372
  }
6291
- function isCapFlagEnabled(cap, flagId) {
6373
+ function isCapFlagEnabled(capability, flagId) {
6292
6374
  var _a, _b;
6293
- const fromFlags = (_b = (_a = cap.flags) == null ? void 0 : _a[flagId]) == null ? void 0 : _b.enabled;
6375
+ const fromFlags = (_b = (_a = capability.flags) == null ? void 0 : _a[flagId]) == null ? void 0 : _b.enabled;
6294
6376
  if (fromFlags === true) return true;
6295
6377
  if (fromFlags === false) return false;
6296
- const legacy = cap[flagId];
6378
+ const legacy = capability[flagId];
6297
6379
  return legacy === true;
6298
6380
  }
6299
- function satisfiesTagConstraints(tagId, ctx, cap) {
6300
- const tag = ctx.props.filters.find((t) => t.id === tagId);
6301
- const eff = tag == null ? void 0 : tag.constraints;
6302
- if (!eff) return true;
6303
- for (const [key, value] of Object.entries(eff)) {
6304
- if (value === true && !isCapFlagEnabled(cap, key)) {
6381
+ function satisfiesTagConstraints(tagId, ctx, capability) {
6382
+ const tag = ctx.props.filters.find((item) => item.id === tagId);
6383
+ const effectiveConstraints2 = tag == null ? void 0 : tag.constraints;
6384
+ if (!effectiveConstraints2) return true;
6385
+ for (const [key, value] of Object.entries(effectiveConstraints2)) {
6386
+ if (value === true && !isCapFlagEnabled(capability, key)) {
6305
6387
  return false;
6306
6388
  }
6307
6389
  }
6308
6390
  return true;
6309
6391
  }
6310
6392
  function primaryForNode(props, nodeId) {
6311
- const tag = props.filters.find((t) => t.id === nodeId);
6393
+ const tag = props.filters.find((item) => item.id === nodeId);
6312
6394
  if (tag) {
6313
6395
  return { primary: tag.service_id, tagContexts: [tag.id] };
6314
6396
  }
6315
6397
  const field = props.fields.find(
6316
- (f) => Array.isArray(f.options) && f.options.some((o) => o.id === nodeId)
6398
+ (item) => Array.isArray(item.options) && item.options.some((option2) => option2.id === nodeId)
6317
6399
  );
6318
- if (!field) return { tagContexts: [], reasonNoPrimary: "no_parent_field" };
6319
- const opt = field.options.find((o) => o.id === nodeId);
6320
- const contexts = bindIdsToArray(field.bind_id);
6321
- return { primary: opt.service_id, tagContexts: contexts };
6400
+ if (!field) {
6401
+ return { tagContexts: [], reasonNoPrimary: "no_parent_field" };
6402
+ }
6403
+ const option = field.options.find((item) => item.id === nodeId);
6404
+ return {
6405
+ primary: option.service_id,
6406
+ tagContexts: bindIdsToArray(field.bind_id)
6407
+ };
6322
6408
  }
6323
6409
  function bindIdsToArray(bind) {
6324
6410
  if (!bind) return [];