@suss/ir-core 0.8.0 → 0.10.0

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.cjs CHANGED
@@ -30,6 +30,7 @@ __export(index_exports, {
30
30
  GraphqlOperationSemanticsSchema: () => GraphqlOperationSemanticsSchema,
31
31
  GraphqlResolverSemanticsSchema: () => GraphqlResolverSemanticsSchema,
32
32
  MessageBusSemanticsSchema: () => MessageBusSemanticsSchema,
33
+ MetricSemanticsSchema: () => MetricSemanticsSchema,
33
34
  RestSemanticsSchema: () => RestSemanticsSchema,
34
35
  RuntimeConfigSemanticsSchema: () => RuntimeConfigSemanticsSchema,
35
36
  SemanticsSchema: () => SemanticsSchema,
@@ -42,6 +43,7 @@ __export(index_exports, {
42
43
  bodyShapesMatch: () => bodyShapesMatch,
43
44
  boundaryKey: () => boundaryKey,
44
45
  boundaryLabel: () => boundaryLabel,
46
+ boundaryNameString: () => boundaryNameString,
45
47
  busIdentityKey: () => busIdentityKey,
46
48
  busesAgree: () => busesAgree,
47
49
  canPair: () => canPair,
@@ -53,6 +55,7 @@ __export(index_exports, {
53
55
  ecsContainerInstanceName: () => ecsContainerInstanceName,
54
56
  exchangesHttpResponses: () => exchangesHttpResponses,
55
57
  fileInCodeScope: () => fileInCodeScope,
58
+ fixedTextLength: () => fixedTextLength,
56
59
  fnIdentityKey: () => fnIdentityKey,
57
60
  formatChannel: () => formatChannel,
58
61
  functionCallBinding: () => functionCallBinding,
@@ -63,6 +66,8 @@ __export(index_exports, {
63
66
  isGraphqlOperationBinding: () => isGraphqlOperationBinding,
64
67
  messageBusBinding: () => messageBusBinding,
65
68
  methodsAgree: () => methodsAgree,
69
+ metricBinding: () => metricBinding,
70
+ metricIdentityKey: () => metricIdentityKey,
66
71
  namePatternFromSub: () => namePatternFromSub,
67
72
  namePatternKey: () => namePatternKey,
68
73
  namesAgree: () => namesAgree,
@@ -71,13 +76,19 @@ __export(index_exports, {
71
76
  normalizeRuleBoundary: () => normalizeRuleBoundary,
72
77
  packageExportBinding: () => packageExportBinding,
73
78
  pairingKey: () => pairingKey,
79
+ parseBoundaryName: () => parseBoundaryName,
74
80
  parseChannel: () => parseChannel,
81
+ patternHole: () => patternHole,
82
+ referenceFromName: () => referenceFromName,
83
+ referenceName: () => referenceName,
84
+ referenceOf: () => referenceOf,
75
85
  reportsUnpairedItself: () => reportsUnpairedItself,
76
86
  restBinding: () => restBinding,
77
87
  routePathAdmits: () => routePathAdmits,
78
88
  ruleBoundaryMatchesKey: () => ruleBoundaryMatchesKey,
79
89
  runtimeConfigBinding: () => runtimeConfigBinding,
80
90
  semanticsAgree: () => semanticsAgree,
91
+ semconvAttributes: () => semconvAttributes,
81
92
  servesRequest: () => servesRequest,
82
93
  storageBinding: () => storageBinding,
83
94
  typeDefinitionKey: () => typeDefinitionKey,
@@ -104,6 +115,10 @@ var GraphqlOperationSemanticsSchema = import_zod.z.object({
104
115
  var graphqlOperationSemantics = defineBoundarySemantics({
105
116
  name: "graphql-operation",
106
117
  schema: GraphqlOperationSemanticsSchema,
118
+ semconv: {
119
+ operationName: { name: "graphql.operation.name" },
120
+ operationType: { name: "graphql.operation.type" }
121
+ },
107
122
  behavior: {
108
123
  /**
109
124
  * An operation gets a data-and-errors document back, which the
@@ -131,12 +146,15 @@ function fnIdentityKey(packageName, exportPath) {
131
146
  function busIdentityKey(messageBus, subject) {
132
147
  return `bus:${messageBus} ${subject}`;
133
148
  }
149
+ function metricIdentityKey(metricSystem, metricType) {
150
+ return `metric:${metricSystem} ${metricType}`;
151
+ }
134
152
 
135
153
  // src/schemas.ts
136
- var import_zod10 = require("zod");
154
+ var import_zod11 = require("zod");
137
155
 
138
156
  // src/semantics/registry.ts
139
- var import_zod9 = require("zod");
157
+ var import_zod10 = require("zod");
140
158
 
141
159
  // src/semantics/functionCall.ts
142
160
  var import_zod2 = require("zod");
@@ -174,6 +192,9 @@ var FunctionCallSemanticsSchema = import_zod2.z.object({
174
192
  var functionCallSemantics = defineBoundarySemantics({
175
193
  name: "function-call",
176
194
  schema: FunctionCallSemanticsSchema,
195
+ // A call that never leaves the process does not produce a span, and
196
+ // the conventions do not name one. All of it is ours.
197
+ semconv: {},
177
198
  behavior: {
178
199
  /** A call returns a value, which is not a status and a body. */
179
200
  exchangesHttpResponses: false,
@@ -214,6 +235,9 @@ var GraphqlResolverSemanticsSchema = import_zod3.z.object({
214
235
  var graphqlResolverSemantics = defineBoundarySemantics({
215
236
  name: "graphql-resolver",
216
237
  schema: GraphqlResolverSemanticsSchema,
238
+ // The conventions name the operation a client sent, not the
239
+ // resolver the server ran for one field of it.
240
+ semconv: {},
217
241
  behavior: {
218
242
  /** A resolver returns a field value, not a status and a body. */
219
243
  exchangesHttpResponses: false,
@@ -262,8 +286,11 @@ function channelsPair(a, b) {
262
286
  var MessageBusSemanticsSchema = import_zod4.z.object({
263
287
  name: import_zod4.z.literal("message-bus"),
264
288
  messageBus: import_zod4.z.enum([
265
- "sqs",
266
- "sns",
289
+ // The conventions spell the two AWS buses this way, one with an
290
+ // underscore and one with a dot, so these are their strings and
291
+ // not a typo.
292
+ "aws_sqs",
293
+ "aws.sns",
267
294
  "s3",
268
295
  "eventbridge",
269
296
  "bullmq",
@@ -284,6 +311,10 @@ var MessageBusSemanticsSchema = import_zod4.z.object({
284
311
  var messageBusSemantics = defineBoundarySemantics({
285
312
  name: "message-bus",
286
313
  schema: MessageBusSemanticsSchema,
314
+ semconv: {
315
+ messageBus: { name: "messaging.system" },
316
+ channel: { name: "messaging.destination.name" }
317
+ },
287
318
  behavior: {
288
319
  /** A message goes onto the channel and nothing comes back. */
289
320
  exchangesHttpResponses: false,
@@ -342,25 +373,58 @@ var messageBusSemantics = defineBoundarySemantics({
342
373
  }
343
374
  });
344
375
 
345
- // src/semantics/rest.ts
376
+ // src/semantics/metric.ts
346
377
  var import_zod5 = require("zod");
347
- var RestSemanticsSchema = import_zod5.z.object({
348
- name: import_zod5.z.literal("rest"),
378
+ var MetricSemanticsSchema = import_zod5.z.object({
379
+ name: import_zod5.z.literal("metric"),
380
+ /** Which system the series lives in: cloud-monitoring, cloudwatch. */
381
+ metricSystem: import_zod5.z.string(),
382
+ /**
383
+ * The type string the system knows the metric by, spelled the way
384
+ * both sides spell it. Null when the source states one this reader
385
+ * could not settle, as with a query it could not read: a metric
386
+ * nobody could name pairs with nothing rather than with whatever
387
+ * happens to share its source text.
388
+ */
389
+ metricType: import_zod5.z.string().nullable()
390
+ });
391
+ var metricSemantics = defineBoundarySemantics({
392
+ name: "metric",
393
+ schema: MetricSemanticsSchema,
394
+ // OpenTelemetry knows a metric by its instrument name, and has no
395
+ // attribute for the system it lives in or the type string it goes by.
396
+ semconv: {},
397
+ behavior: {
398
+ exchangesHttpResponses: false,
399
+ reportsUnpairedItself: false,
400
+ identityKey(semantics) {
401
+ if (semantics.metricType === null) {
402
+ return null;
403
+ }
404
+ return metricIdentityKey(semantics.metricSystem, semantics.metricType);
405
+ }
406
+ }
407
+ });
408
+
409
+ // src/semantics/rest.ts
410
+ var import_zod6 = require("zod");
411
+ var RestSemanticsSchema = import_zod6.z.object({
412
+ name: import_zod6.z.literal("rest"),
349
413
  /**
350
414
  * Uppercase HTTP method ("GET", "POST", …), `"*"` for a handler that
351
415
  * responds to every method, or null when this source does not say
352
416
  * which method.
353
417
  */
354
- method: import_zod5.z.string().min(1).nullable(),
418
+ method: import_zod6.z.string().min(1).nullable(),
355
419
  /** Normalized route path ("/users/{id}"), or null when this source does not give one. */
356
- path: import_zod5.z.string().min(1).nullable(),
420
+ path: import_zod6.z.string().min(1).nullable(),
357
421
  /**
358
422
  * Status codes the producing source explicitly declared (OpenAPI
359
423
  * responses, CFN MethodResponses, ts-rest router statuses). They are
360
424
  * kept here so the pairing layer can see them without unwrapping any
361
425
  * metadata. Absent, or empty, for a source that was inferred.
362
426
  */
363
- declaredResponses: import_zod5.z.array(import_zod5.z.number()).optional()
427
+ declaredResponses: import_zod6.z.array(import_zod6.z.number()).optional()
364
428
  });
365
429
  function normalizePath(path) {
366
430
  let normalized = path.replace(/:([a-zA-Z_]\w*)/g, "{$1}");
@@ -399,6 +463,14 @@ function routePathAdmits(declaredPath, requestPath) {
399
463
  var restSemantics = defineBoundarySemantics({
400
464
  name: "rest",
401
465
  schema: RestSemanticsSchema,
466
+ semconv: {
467
+ // A span states the one method a request used, so a route that
468
+ // responds to every method has nothing to compare.
469
+ method: { name: "http.request.method", placeholderValues: ["*"] },
470
+ path: { name: "http.route" }
471
+ // declaredResponses is a list, and http.response.status_code is
472
+ // the one status a request got back.
473
+ },
402
474
  behavior: {
403
475
  /** A request goes out, a status and a body come back. */
404
476
  exchangesHttpResponses: true,
@@ -471,12 +543,12 @@ var restSemantics = defineBoundarySemantics({
471
543
  });
472
544
 
473
545
  // src/semantics/runtimeConfig.ts
474
- var import_zod7 = require("zod");
546
+ var import_zod8 = require("zod");
475
547
 
476
548
  // src/deployableUnit.ts
477
- var import_zod6 = require("zod");
478
- var DeployableUnitSchema = import_zod6.z.object({
479
- deploymentTarget: import_zod6.z.enum([
549
+ var import_zod7 = require("zod");
550
+ var DeployableUnitSchema = import_zod7.z.object({
551
+ deploymentTarget: import_zod7.z.enum([
480
552
  "lambda",
481
553
  "ecs-task",
482
554
  "container",
@@ -485,7 +557,7 @@ var DeployableUnitSchema = import_zod6.z.object({
485
557
  ]),
486
558
  // An empty name would agree with every other empty name, so a unit
487
559
  // with no name has to leave the field off instead.
488
- instanceName: import_zod6.z.string().min(1)
560
+ instanceName: import_zod7.z.string().min(1)
489
561
  });
490
562
  function ecsContainerInstanceName(taskDefinitionLogicalId, containerName) {
491
563
  return `${taskDefinitionLogicalId}/${containerName}`;
@@ -493,11 +565,15 @@ function ecsContainerInstanceName(taskDefinitionLogicalId, containerName) {
493
565
 
494
566
  // src/semantics/runtimeConfig.ts
495
567
  var RuntimeConfigSemanticsSchema = DeployableUnitSchema.extend({
496
- name: import_zod7.z.literal("runtime-config")
568
+ name: import_zod8.z.literal("runtime-config")
497
569
  });
498
570
  var runtimeConfigSemantics = defineBoundarySemantics({
499
571
  name: "runtime-config",
500
572
  schema: RuntimeConfigSemanticsSchema,
573
+ // service.name and cloud.resource_id name the same deployable, but
574
+ // instanceName is the deployment template's logical id, which is
575
+ // neither of those strings.
576
+ semconv: {},
501
577
  behavior: {
502
578
  /** A process reads its config at startup, and nothing replies. */
503
579
  exchangesHttpResponses: false,
@@ -507,28 +583,30 @@ var runtimeConfigSemantics = defineBoundarySemantics({
507
583
  });
508
584
 
509
585
  // src/semantics/storage.ts
510
- var import_zod8 = require("zod");
511
- var StorageSemanticsSchema = import_zod8.z.object({
512
- name: import_zod8.z.literal("storage"),
586
+ var import_zod9 = require("zod");
587
+ var StorageSemanticsSchema = import_zod9.z.object({
588
+ name: import_zod9.z.literal("storage"),
513
589
  /**
514
- * Which store this is: postgres, mysql, sqlite, dynamodb. Two
515
- * products' containers can share a name, so this keeps them apart,
516
- * and it is what a finding message calls the store.
590
+ * Which store this is: postgresql, mysql, aws.dynamodb, or our own
591
+ * word for one OpenTelemetry never named, such as s3. Two products'
592
+ * containers can share a name, so this keeps them apart.
517
593
  */
518
- storageSystem: import_zod8.z.string(),
594
+ storageSystem: import_zod9.z.string(),
519
595
  /**
520
596
  * The ORM, schema, or deployment scope. A single-database setup uses
521
597
  * `"default"`, and a monorepo with several schemas gives each one its
522
598
  * own value.
523
599
  */
524
- scope: import_zod8.z.string(),
600
+ scope: import_zod9.z.string(),
525
601
  /**
526
602
  * The table, bucket, collection, or index, as the source declares
527
603
  * it, or null when the source states one this reader could not
528
604
  * settle. A null container pairs with nothing, rather than with
529
- * whatever its source text happens to spell.
605
+ * whatever its source text happens to spell. The string is in the
606
+ * boundary-name syntax: a literal, a pattern with `{}` holes, or a
607
+ * reference, and `parseBoundaryName` is the one reader of it.
530
608
  */
531
- container: import_zod8.z.string().nullable(),
609
+ container: import_zod9.z.string().nullable(),
532
610
  /**
533
611
  * A secondary way into the container, a DynamoDB global secondary
534
612
  * index or an Elasticsearch alias, each with its own key fields.
@@ -536,11 +614,19 @@ var StorageSemanticsSchema = import_zod8.z.object({
536
614
  * index and a query through the table are different accesses, so
537
615
  * they pair separately.
538
616
  */
539
- accessPath: import_zod8.z.string().nullable()
617
+ accessPath: import_zod9.z.string().nullable()
540
618
  });
541
619
  var storageSemantics = defineBoundarySemantics({
542
620
  name: "storage",
543
621
  schema: StorageSemanticsSchema,
622
+ semconv: {
623
+ storageSystem: { name: "db.system.name" },
624
+ // "default" is our word for a source that named no database.
625
+ scope: { name: "db.namespace", placeholderValues: ["default"] },
626
+ container: { name: "db.collection.name" }
627
+ // A secondary index has no attribute of its own, so accessPath
628
+ // stays our word.
629
+ },
544
630
  behavior: {
545
631
  /** A query returns rows or items, not a status and a body. */
546
632
  exchangesHttpResponses: false,
@@ -550,14 +636,15 @@ var storageSemantics = defineBoundarySemantics({
550
636
  });
551
637
 
552
638
  // src/semantics/registry.ts
553
- var SemanticsSchema = import_zod9.z.discriminatedUnion("name", [
639
+ var SemanticsSchema = import_zod10.z.discriminatedUnion("name", [
554
640
  restSemantics.schema,
555
641
  functionCallSemantics.schema,
556
642
  graphqlResolverSemantics.schema,
557
643
  graphqlOperationSemantics.schema,
558
644
  runtimeConfigSemantics.schema,
559
645
  storageSemantics.schema,
560
- messageBusSemantics.schema
646
+ messageBusSemantics.schema,
647
+ metricSemantics.schema
561
648
  ]);
562
649
  var DEFINITIONS = [
563
650
  restSemantics,
@@ -566,48 +653,55 @@ var DEFINITIONS = [
566
653
  graphqlOperationSemantics,
567
654
  runtimeConfigSemantics,
568
655
  storageSemantics,
569
- messageBusSemantics
656
+ messageBusSemantics,
657
+ metricSemantics
570
658
  ];
571
659
  var BY_NAME = new Map(
572
660
  DEFINITIONS.map((d) => [d.name, d])
573
661
  );
574
662
  function behaviorOf(semantics) {
575
- const definition = BY_NAME.get(semantics.name);
663
+ return definitionFor(semantics.name).behavior;
664
+ }
665
+ function definitionFor(name) {
666
+ const definition = BY_NAME.get(name);
576
667
  if (definition === void 0) {
577
- throw new Error(`no boundary behavior for semantics "${semantics.name}"`);
668
+ throw new Error(`no boundary definition for semantics "${name}"`);
578
669
  }
579
- return definition.behavior;
670
+ return definition;
671
+ }
672
+ function semconvMappingOf(semantics) {
673
+ return definitionFor(semantics.name).semconv;
580
674
  }
581
675
  function allBehaviors() {
582
676
  return DEFINITIONS.map((d) => d.behavior);
583
677
  }
584
678
 
585
679
  // src/schemas.ts
586
- var ConfidenceSourceSchema = import_zod10.z.enum([
680
+ var ConfidenceSourceSchema = import_zod11.z.enum([
587
681
  "inferred_static",
588
682
  "inferred_ai",
589
683
  "declared",
590
684
  "derived"
591
685
  ]);
592
- var ConfidenceLevelSchema = import_zod10.z.enum(["high", "medium", "low"]);
593
- var CorroborationSchema = import_zod10.z.object({
594
- outcome: import_zod10.z.enum(["observed", "refuted", "untested"]),
686
+ var ConfidenceLevelSchema = import_zod11.z.enum(["high", "medium", "low"]);
687
+ var CorroborationSchema = import_zod11.z.object({
688
+ outcome: import_zod11.z.enum(["observed", "refuted", "untested"]),
595
689
  /** Executions that produced a verdict for this claim. */
596
- runs: import_zod10.z.number(),
690
+ runs: import_zod11.z.number(),
597
691
  /** Present when refuted: the input and observation that disagreed. */
598
- counterexample: import_zod10.z.unknown().optional(),
692
+ counterexample: import_zod11.z.unknown().optional(),
599
693
  /** Present when untested: why no verdict was reachable. */
600
- reason: import_zod10.z.string().optional()
694
+ reason: import_zod11.z.string().optional()
601
695
  });
602
- var ConfidenceInfoSchema = import_zod10.z.object({
696
+ var ConfidenceInfoSchema = import_zod11.z.object({
603
697
  source: ConfidenceSourceSchema,
604
698
  level: ConfidenceLevelSchema,
605
699
  corroboration: CorroborationSchema.optional()
606
700
  });
607
- var SourceLocationSchema = import_zod10.z.object({
608
- file: import_zod10.z.string(),
609
- range: import_zod10.z.object({ start: import_zod10.z.number(), end: import_zod10.z.number() }),
610
- exportName: import_zod10.z.string().nullable(),
701
+ var SourceLocationSchema = import_zod11.z.object({
702
+ file: import_zod11.z.string(),
703
+ range: import_zod11.z.object({ start: import_zod11.z.number(), end: import_zod11.z.number() }),
704
+ exportName: import_zod11.z.string().nullable(),
611
705
  /**
612
706
  * The project the extract was pointed at, by the name that project
613
707
  * calls itself.
@@ -617,47 +711,47 @@ var SourceLocationSchema = import_zod10.z.object({
617
711
  * summaries puts them on top of each other. This field is what tells
618
712
  * them apart, and what a reader groups by.
619
713
  */
620
- workspace: import_zod10.z.string().optional()
714
+ workspace: import_zod11.z.string().optional()
621
715
  });
622
- var BoundaryBindingSchema = import_zod10.z.object({
623
- transport: import_zod10.z.string(),
716
+ var BoundaryBindingSchema = import_zod11.z.object({
717
+ transport: import_zod11.z.string(),
624
718
  semantics: SemanticsSchema,
625
- recognition: import_zod10.z.string()
719
+ recognition: import_zod11.z.string()
626
720
  });
627
721
  function typeDefinitionKey(ref) {
628
722
  return ref.def ?? null;
629
723
  }
630
- var TypeShapeSchema = import_zod10.z.lazy(
631
- () => import_zod10.z.discriminatedUnion("type", [
632
- import_zod10.z.object({
633
- type: import_zod10.z.literal("record"),
634
- properties: import_zod10.z.record(import_zod10.z.string(), TypeShapeSchema),
635
- spreads: import_zod10.z.array(import_zod10.z.object({ sourceText: import_zod10.z.string() })).optional()
724
+ var TypeShapeSchema = import_zod11.z.lazy(
725
+ () => import_zod11.z.discriminatedUnion("type", [
726
+ import_zod11.z.object({
727
+ type: import_zod11.z.literal("record"),
728
+ properties: import_zod11.z.record(import_zod11.z.string(), TypeShapeSchema),
729
+ spreads: import_zod11.z.array(import_zod11.z.object({ sourceText: import_zod11.z.string() })).optional()
636
730
  }),
637
- import_zod10.z.object({ type: import_zod10.z.literal("dictionary"), values: TypeShapeSchema }),
638
- import_zod10.z.object({ type: import_zod10.z.literal("array"), items: TypeShapeSchema }),
639
- import_zod10.z.object({
640
- type: import_zod10.z.literal("literal"),
641
- value: import_zod10.z.union([import_zod10.z.string(), import_zod10.z.number(), import_zod10.z.boolean()]),
642
- raw: import_zod10.z.string().optional()
731
+ import_zod11.z.object({ type: import_zod11.z.literal("dictionary"), values: TypeShapeSchema }),
732
+ import_zod11.z.object({ type: import_zod11.z.literal("array"), items: TypeShapeSchema }),
733
+ import_zod11.z.object({
734
+ type: import_zod11.z.literal("literal"),
735
+ value: import_zod11.z.union([import_zod11.z.string(), import_zod11.z.number(), import_zod11.z.boolean()]),
736
+ raw: import_zod11.z.string().optional()
643
737
  }),
644
- import_zod10.z.object({ type: import_zod10.z.literal("text") }),
645
- import_zod10.z.object({ type: import_zod10.z.literal("integer") }),
646
- import_zod10.z.object({ type: import_zod10.z.literal("number") }),
647
- import_zod10.z.object({ type: import_zod10.z.literal("boolean") }),
648
- import_zod10.z.object({ type: import_zod10.z.literal("null") }),
649
- import_zod10.z.object({ type: import_zod10.z.literal("undefined") }),
650
- import_zod10.z.object({
651
- type: import_zod10.z.literal("union"),
652
- variants: import_zod10.z.array(TypeShapeSchema)
738
+ import_zod11.z.object({ type: import_zod11.z.literal("text") }),
739
+ import_zod11.z.object({ type: import_zod11.z.literal("integer") }),
740
+ import_zod11.z.object({ type: import_zod11.z.literal("number") }),
741
+ import_zod11.z.object({ type: import_zod11.z.literal("boolean") }),
742
+ import_zod11.z.object({ type: import_zod11.z.literal("null") }),
743
+ import_zod11.z.object({ type: import_zod11.z.literal("undefined") }),
744
+ import_zod11.z.object({
745
+ type: import_zod11.z.literal("union"),
746
+ variants: import_zod11.z.array(TypeShapeSchema)
653
747
  }),
654
- import_zod10.z.object({
655
- type: import_zod10.z.literal("ref"),
656
- name: import_zod10.z.string(),
657
- from: import_zod10.z.string().optional(),
658
- def: import_zod10.z.string().optional()
748
+ import_zod11.z.object({
749
+ type: import_zod11.z.literal("ref"),
750
+ name: import_zod11.z.string(),
751
+ from: import_zod11.z.string().optional(),
752
+ def: import_zod11.z.string().optional()
659
753
  }),
660
- import_zod10.z.object({ type: import_zod10.z.literal("unknown") })
754
+ import_zod11.z.object({ type: import_zod11.z.literal("unknown") })
661
755
  ])
662
756
  );
663
757
  var MAX_DEFINITION_DEPTH = 6;
@@ -701,6 +795,25 @@ function withDefinitionsInlined(shape, definitions, depth = 0, inProgress = /* @
701
795
  return shape;
702
796
  }
703
797
 
798
+ // src/semconv.ts
799
+ function semconvAttributes(binding) {
800
+ const semantics = binding.semantics;
801
+ const attributes = {};
802
+ for (const [field, attribute] of Object.entries(
803
+ semconvMappingOf(binding.semantics)
804
+ )) {
805
+ const value = semantics[field];
806
+ if (typeof value !== "string") {
807
+ continue;
808
+ }
809
+ if (attribute.placeholderValues?.includes(value) === true) {
810
+ continue;
811
+ }
812
+ attributes[attribute.name] = value;
813
+ }
814
+ return attributes;
815
+ }
816
+
704
817
  // src/boundaryKey.ts
705
818
  function boundaryKey(binding) {
706
819
  return behaviorOf(binding.semantics).identityKey(binding.semantics);
@@ -753,29 +866,47 @@ function servesRequest(binding, method, path) {
753
866
  return serves(binding.semantics, method, path);
754
867
  }
755
868
 
756
- // src/codeScope.ts
757
- function codeScopePath(raw) {
758
- return raw.trim().replace(/^\.\//, "").replace(/\/+$/, "");
759
- }
760
- function fileInCodeScope(file, scope) {
761
- const prefix = codeScopePath(scope);
762
- if (prefix === "" || prefix === ".") {
763
- return true;
764
- }
765
- const path = codeScopePath(file);
766
- return path === prefix || path.startsWith(`${prefix}/`);
767
- }
768
-
769
869
  // src/dispatch.ts
770
870
  function dispatchByType(table, value) {
771
871
  const handler = table[value.type];
772
872
  return handler(value);
773
873
  }
774
874
 
775
- // src/namePattern.ts
776
- var HOLE = /\{[^}]*\}/g;
875
+ // src/boundaryName.ts
876
+ var HOLE_SPLIT = /(\{[^}]*\})/;
777
877
  var SUB_TOKEN = /\$\{([^}]*)\}/g;
778
878
  var SUB_ESCAPE = /\$\{!/;
879
+ function isHole(piece) {
880
+ return piece.startsWith("{") && piece.endsWith("}");
881
+ }
882
+ function parseBoundaryName(name) {
883
+ const parts = name.split(HOLE_SPLIT).filter((piece) => piece !== "").map(
884
+ (piece) => isHole(piece) ? { type: "hole", label: piece.slice(1, -1) } : { type: "text", text: piece }
885
+ );
886
+ const only = parts.length === 1 ? parts[0] : void 0;
887
+ if (only !== void 0 && only.type === "hole") {
888
+ return { type: "reference", path: only.label.split(".") };
889
+ }
890
+ if (parts.some((part) => part.type === "hole")) {
891
+ return { type: "pattern", parts };
892
+ }
893
+ return { type: "literal", value: name };
894
+ }
895
+ var partString = {
896
+ text: (part) => part.text,
897
+ hole: (part) => patternHole(part.label)
898
+ };
899
+ var nameString = {
900
+ literal: (name) => name.value,
901
+ pattern: (name) => name.parts.map((part) => dispatchByType(partString, part)).join(""),
902
+ reference: (name) => patternHole(name.path.join("."))
903
+ };
904
+ function boundaryNameString(name) {
905
+ return dispatchByType(nameString, name);
906
+ }
907
+ function patternHole(label) {
908
+ return `{${label}}`;
909
+ }
779
910
  function namePatternFromSub(value) {
780
911
  if (Array.isArray(value)) {
781
912
  return typeof value[0] === "string" ? namePatternFromSub(value[0]) : null;
@@ -786,65 +917,123 @@ function namePatternFromSub(value) {
786
917
  if (SUB_ESCAPE.test(value)) {
787
918
  return null;
788
919
  }
789
- return value.replace(SUB_TOKEN, (_whole, inner) => `{${inner}}`);
920
+ return value.replace(
921
+ SUB_TOKEN,
922
+ (_whole, inner) => patternHole(inner)
923
+ );
790
924
  }
791
925
  function namesNothing(name) {
792
- return /^\{[^}]*\}$/.test(name);
926
+ return parseBoundaryName(name).type === "reference";
793
927
  }
794
928
  function hasNameHole(name) {
795
- HOLE.lastIndex = 0;
796
- return HOLE.test(name);
929
+ return parseBoundaryName(name).type !== "literal";
797
930
  }
798
931
  function namePatternKey(name) {
799
- return name.replace(HOLE, "{}");
932
+ return dispatchByType(
933
+ {
934
+ literal: (parsed) => parsed.value,
935
+ pattern: (parsed) => boundaryNameString({
936
+ type: "pattern",
937
+ parts: parsed.parts.map(
938
+ (part) => part.type === "hole" ? { type: "hole", label: "" } : part
939
+ )
940
+ }),
941
+ reference: () => patternHole("")
942
+ },
943
+ parseBoundaryName(name)
944
+ );
945
+ }
946
+ function fixedTextLength(name) {
947
+ return dispatchByType(
948
+ {
949
+ literal: (parsed) => parsed.value.length,
950
+ pattern: (parsed) => parsed.parts.reduce(
951
+ (total, part) => total + (part.type === "text" ? part.text.length : 0),
952
+ 0
953
+ ),
954
+ reference: () => 0
955
+ },
956
+ parseBoundaryName(name)
957
+ );
958
+ }
959
+ function quote(text) {
960
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
800
961
  }
801
962
  function admits(pattern, name) {
802
- const source = pattern.split(/(\{[^}]*\})/).map(
803
- (part) => part.startsWith("{") && part.endsWith("}") ? ".+" : part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
804
- ).join("");
963
+ const source = pattern.parts.map((part) => part.type === "hole" ? ".+" : quote(part.text)).join("");
805
964
  return new RegExp(`^${source}$`).test(name);
806
965
  }
807
966
  function namesAgree(a, b) {
808
- if (namesNothing(a) || namesNothing(b)) {
967
+ const left = parseBoundaryName(a);
968
+ const right = parseBoundaryName(b);
969
+ if (left.type === "reference" || right.type === "reference") {
809
970
  return false;
810
971
  }
811
- if (a === b) {
812
- return true;
813
- }
814
- const aHasHole = hasNameHole(a);
815
- const bHasHole = hasNameHole(b);
816
- if (aHasHole && bHasHole) {
972
+ if (left.type === "pattern" && right.type === "pattern") {
817
973
  return namePatternKey(a) === namePatternKey(b);
818
974
  }
819
- if (aHasHole) {
820
- return admits(a, b);
975
+ if (left.type === "pattern") {
976
+ return admits(left, b);
821
977
  }
822
- if (bHasHole) {
823
- return admits(b, a);
978
+ if (right.type === "pattern") {
979
+ return admits(right, a);
824
980
  }
825
- return false;
981
+ return a === b;
982
+ }
983
+ function referenceName(reference) {
984
+ const path = [reference.root, ...reference.fields];
985
+ if (path.some((part) => part === "")) {
986
+ return null;
987
+ }
988
+ return boundaryNameString({ type: "reference", path });
989
+ }
990
+ function referenceOf(name) {
991
+ if (name.type !== "reference") {
992
+ return null;
993
+ }
994
+ const root = name.path[0];
995
+ if (root === void 0 || name.path.some((part) => part === "")) {
996
+ return null;
997
+ }
998
+ return { root, fields: name.path.slice(1) };
999
+ }
1000
+ function referenceFromName(name) {
1001
+ return referenceOf(parseBoundaryName(name));
1002
+ }
1003
+
1004
+ // src/codeScope.ts
1005
+ function codeScopePath(raw) {
1006
+ return raw.trim().replace(/^\.\//, "").replace(/\/+$/, "");
1007
+ }
1008
+ function fileInCodeScope(file, scope) {
1009
+ const prefix = codeScopePath(scope);
1010
+ if (prefix === "" || prefix === ".") {
1011
+ return true;
1012
+ }
1013
+ const path = codeScopePath(file);
1014
+ return path === prefix || path.startsWith(`${prefix}/`);
826
1015
  }
827
1016
 
828
1017
  // src/suppressions.ts
829
- var import_zod11 = require("zod");
830
- var SuppressionSideSchema = import_zod11.z.object({
831
- summary: import_zod11.z.string().optional(),
832
- transitionId: import_zod11.z.string().optional()
1018
+ var import_zod12 = require("zod");
1019
+ var SuppressionSideSchema = import_zod12.z.object({
1020
+ summary: import_zod12.z.string().optional(),
1021
+ transitionId: import_zod12.z.string().optional()
833
1022
  }).optional();
834
- var SuppressionRuleSchema = import_zod11.z.object({
1023
+ var SuppressionRuleSchema = import_zod12.z.object({
835
1024
  /**
836
1025
  * The finding kind to match, behavioural or intent. It is an open
837
1026
  * string here because this package is below both IRs, and the loader
838
1027
  * is what validates it against the published kind enums.
839
1028
  */
840
- kind: import_zod11.z.string().optional(),
1029
+ kind: import_zod12.z.string().optional(),
841
1030
  /**
842
1031
  * Boundary as a human-readable key, e.g. "GET /pet/{petId}" or
843
1032
  * "fn:@acme/api::getUser". REST keys are normalized via the same
844
1033
  * path normalizer the checkers use, so `:id` and `{id}` compare
845
1034
  * equal.
846
1035
  */
847
- boundary: import_zod11.z.string().optional(),
1036
+ boundary: import_zod12.z.string().optional(),
848
1037
  /**
849
1038
  * Consumer-side discriminators, the narrowest useful match. These
850
1039
  * mean something only for behavioural findings: a rule that
@@ -867,15 +1056,15 @@ var SuppressionRuleSchema = import_zod11.z.object({
867
1056
  * kind-only or boundary-only matches, which also silence future
868
1057
  * regressions in that category.
869
1058
  */
870
- scope: import_zod11.z.enum(["narrow", "broad"]).default("narrow"),
1059
+ scope: import_zod12.z.enum(["narrow", "broad"]).default("narrow"),
871
1060
  /** Required human-written justification. */
872
- reason: import_zod11.z.string().min(1),
1061
+ reason: import_zod12.z.string().min(1),
873
1062
  /** What to do when a finding matches. */
874
- effect: import_zod11.z.enum(["mark", "downgrade", "hide"]).default("mark")
1063
+ effect: import_zod12.z.enum(["mark", "downgrade", "hide"]).default("mark")
875
1064
  }).strict();
876
- var SuppressionFileSchema = import_zod11.z.object({
877
- version: import_zod11.z.literal(1),
878
- rules: import_zod11.z.array(SuppressionRuleSchema)
1065
+ var SuppressionFileSchema = import_zod12.z.object({
1066
+ version: import_zod12.z.literal(1),
1067
+ rules: import_zod12.z.array(SuppressionRuleSchema)
879
1068
  }).strict();
880
1069
  function validateRule(rule) {
881
1070
  if (rule.scope === "broad") {
@@ -1189,6 +1378,17 @@ function messageBusBinding(opts) {
1189
1378
  recognition: opts.recognition
1190
1379
  };
1191
1380
  }
1381
+ function metricBinding(opts) {
1382
+ return {
1383
+ transport: opts.metricSystem,
1384
+ semantics: {
1385
+ name: "metric",
1386
+ metricSystem: opts.metricSystem,
1387
+ metricType: namedOrNull(opts.metricType, "metric type")
1388
+ },
1389
+ recognition: opts.recognition
1390
+ };
1391
+ }
1192
1392
  // Annotate the CommonJS export names for ESM import in node:
1193
1393
  0 && (module.exports = {
1194
1394
  BoundaryBindingSchema,
@@ -1201,6 +1401,7 @@ function messageBusBinding(opts) {
1201
1401
  GraphqlOperationSemanticsSchema,
1202
1402
  GraphqlResolverSemanticsSchema,
1203
1403
  MessageBusSemanticsSchema,
1404
+ MetricSemanticsSchema,
1204
1405
  RestSemanticsSchema,
1205
1406
  RuntimeConfigSemanticsSchema,
1206
1407
  SemanticsSchema,
@@ -1213,6 +1414,7 @@ function messageBusBinding(opts) {
1213
1414
  bodyShapesMatch,
1214
1415
  boundaryKey,
1215
1416
  boundaryLabel,
1417
+ boundaryNameString,
1216
1418
  busIdentityKey,
1217
1419
  busesAgree,
1218
1420
  canPair,
@@ -1224,6 +1426,7 @@ function messageBusBinding(opts) {
1224
1426
  ecsContainerInstanceName,
1225
1427
  exchangesHttpResponses,
1226
1428
  fileInCodeScope,
1429
+ fixedTextLength,
1227
1430
  fnIdentityKey,
1228
1431
  formatChannel,
1229
1432
  functionCallBinding,
@@ -1234,6 +1437,8 @@ function messageBusBinding(opts) {
1234
1437
  isGraphqlOperationBinding,
1235
1438
  messageBusBinding,
1236
1439
  methodsAgree,
1440
+ metricBinding,
1441
+ metricIdentityKey,
1237
1442
  namePatternFromSub,
1238
1443
  namePatternKey,
1239
1444
  namesAgree,
@@ -1242,13 +1447,19 @@ function messageBusBinding(opts) {
1242
1447
  normalizeRuleBoundary,
1243
1448
  packageExportBinding,
1244
1449
  pairingKey,
1450
+ parseBoundaryName,
1245
1451
  parseChannel,
1452
+ patternHole,
1453
+ referenceFromName,
1454
+ referenceName,
1455
+ referenceOf,
1246
1456
  reportsUnpairedItself,
1247
1457
  restBinding,
1248
1458
  routePathAdmits,
1249
1459
  ruleBoundaryMatchesKey,
1250
1460
  runtimeConfigBinding,
1251
1461
  semanticsAgree,
1462
+ semconvAttributes,
1252
1463
  servesRequest,
1253
1464
  storageBinding,
1254
1465
  typeDefinitionKey,