@suss/ir-core 0.21.0 → 0.21.1

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
@@ -33,6 +33,7 @@ __export(index_exports, {
33
33
  GraphqlResolverSemanticsSchema: () => GraphqlResolverSemanticsSchema,
34
34
  MessageBusSemanticsSchema: () => MessageBusSemanticsSchema,
35
35
  MetricSemanticsSchema: () => MetricSemanticsSchema,
36
+ NOTHING_DEPLOYED: () => NOTHING_DEPLOYED,
36
37
  RelationSchema: () => RelationSchema,
37
38
  RestSemanticsSchema: () => RestSemanticsSchema,
38
39
  RuntimeConfigSemanticsSchema: () => RuntimeConfigSemanticsSchema,
@@ -68,6 +69,7 @@ __export(index_exports, {
68
69
  gqlIdentityKey: () => gqlIdentityKey,
69
70
  graphqlOperationBinding: () => graphqlOperationBinding,
70
71
  graphqlResolverBinding: () => graphqlResolverBinding,
72
+ groundBinding: () => groundBinding,
71
73
  groundedPairingKey: () => groundedPairingKey,
72
74
  hasNameHole: () => hasNameHole,
73
75
  isGraphqlOperationBinding: () => isGraphqlOperationBinding,
@@ -77,6 +79,7 @@ __export(index_exports, {
77
79
  metricIdentityKey: () => metricIdentityKey,
78
80
  namePatternFromSub: () => namePatternFromSub,
79
81
  namePatternKey: () => namePatternKey,
82
+ nameReference: () => nameReference,
80
83
  namesAgree: () => namesAgree,
81
84
  namesBoundary: () => namesBoundary,
82
85
  namesBoundaryExactly: () => namesBoundaryExactly,
@@ -442,6 +445,141 @@ var metricSemantics = defineBoundarySemantics({
442
445
  // src/semantics/rest.ts
443
446
  var import_zod6 = require("zod");
444
447
 
448
+ // src/dispatch.ts
449
+ function dispatchByType(table, value) {
450
+ const handler = table[value.type];
451
+ return handler(value);
452
+ }
453
+
454
+ // src/boundaryName.ts
455
+ var HOLE_SPLIT = /(\{[^}]*\})/;
456
+ var SUB_TOKEN = /\$\{([^}]*)\}/g;
457
+ var SUB_ESCAPE = /\$\{!/;
458
+ function isHole(piece) {
459
+ return piece.startsWith("{") && piece.endsWith("}");
460
+ }
461
+ function parseBoundaryName(name) {
462
+ const parts = name.split(HOLE_SPLIT).filter((piece) => piece !== "").map(
463
+ (piece) => isHole(piece) ? { type: "hole", label: piece.slice(1, -1) } : { type: "text", text: piece }
464
+ );
465
+ const only = parts.length === 1 ? parts[0] : void 0;
466
+ if (only !== void 0 && only.type === "hole") {
467
+ return { type: "reference", path: only.label.split(".") };
468
+ }
469
+ if (parts.some((part) => part.type === "hole")) {
470
+ return { type: "pattern", parts };
471
+ }
472
+ return { type: "literal", value: name };
473
+ }
474
+ var partString = {
475
+ text: (part) => part.text,
476
+ hole: (part) => patternHole(part.label)
477
+ };
478
+ var nameString = {
479
+ literal: (name) => name.value,
480
+ pattern: (name) => name.parts.map((part) => dispatchByType(partString, part)).join(""),
481
+ reference: (name) => patternHole(name.path.join("."))
482
+ };
483
+ function boundaryNameString(name) {
484
+ return dispatchByType(nameString, name);
485
+ }
486
+ function patternHole(label) {
487
+ return `{${label}}`;
488
+ }
489
+ function namePatternFromSub(value) {
490
+ if (Array.isArray(value)) {
491
+ return typeof value[0] === "string" ? namePatternFromSub(value[0]) : null;
492
+ }
493
+ if (typeof value !== "string") {
494
+ return null;
495
+ }
496
+ if (SUB_ESCAPE.test(value)) {
497
+ return null;
498
+ }
499
+ return value.replace(
500
+ SUB_TOKEN,
501
+ (_whole, inner) => patternHole(inner)
502
+ );
503
+ }
504
+ function namesNothing(name) {
505
+ return parseBoundaryName(name).type === "reference";
506
+ }
507
+ function hasNameHole(name) {
508
+ return parseBoundaryName(name).type !== "literal";
509
+ }
510
+ function namePatternKey(name) {
511
+ return dispatchByType(
512
+ {
513
+ literal: (parsed) => parsed.value,
514
+ pattern: (parsed) => boundaryNameString({
515
+ type: "pattern",
516
+ parts: parsed.parts.map(
517
+ (part) => part.type === "hole" ? { type: "hole", label: "" } : part
518
+ )
519
+ }),
520
+ reference: () => patternHole("")
521
+ },
522
+ parseBoundaryName(name)
523
+ );
524
+ }
525
+ function fixedTextLength(name) {
526
+ return dispatchByType(
527
+ {
528
+ literal: (parsed) => parsed.value.length,
529
+ pattern: (parsed) => parsed.parts.reduce(
530
+ (total, part) => total + (part.type === "text" ? part.text.length : 0),
531
+ 0
532
+ ),
533
+ reference: () => 0
534
+ },
535
+ parseBoundaryName(name)
536
+ );
537
+ }
538
+ function quote(text) {
539
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
540
+ }
541
+ function admits(pattern, name) {
542
+ const source = pattern.parts.map((part) => part.type === "hole" ? ".+" : quote(part.text)).join("");
543
+ return new RegExp(`^${source}$`).test(name);
544
+ }
545
+ function namesAgree(a, b) {
546
+ const left = parseBoundaryName(a);
547
+ const right = parseBoundaryName(b);
548
+ if (left.type === "reference" || right.type === "reference") {
549
+ return false;
550
+ }
551
+ if (left.type === "pattern" && right.type === "pattern") {
552
+ return namePatternKey(a) === namePatternKey(b);
553
+ }
554
+ if (left.type === "pattern") {
555
+ return admits(left, b);
556
+ }
557
+ if (right.type === "pattern") {
558
+ return admits(right, a);
559
+ }
560
+ return a === b;
561
+ }
562
+ function referenceName(reference) {
563
+ const path = [reference.root, ...reference.fields];
564
+ if (path.some((part) => part === "")) {
565
+ return null;
566
+ }
567
+ return boundaryNameString({ type: "reference", path });
568
+ }
569
+ function referenceOf(name) {
570
+ if (name.type !== "reference") {
571
+ return null;
572
+ }
573
+ const root = name.path[0];
574
+ if (root === void 0 || name.path.some((part) => part === "")) {
575
+ return null;
576
+ }
577
+ return { root, fields: name.path.slice(1) };
578
+ }
579
+ function referenceFromName(name) {
580
+ return referenceOf(parseBoundaryName(name));
581
+ }
582
+
445
583
  // src/urlPath.ts
446
584
  var SCHEME_ORIGIN = /^[a-zA-Z][a-zA-Z\d+.-]*:\/\/[^/]*/;
447
585
  var PROTOCOL_RELATIVE_ORIGIN = /^\/\/[^/]*/;
@@ -474,9 +612,9 @@ var RestSemanticsSchema = import_zod6.z.object({
474
612
  declaredResponses: import_zod6.z.array(import_zod6.z.number()).optional()
475
613
  });
476
614
  var OPENING_HOLE = /^\{([^{}]+)\}(.*)$/;
477
- function variableOf(label) {
478
- const parts = label.split(".");
479
- return parts[parts.length - 1] ?? label;
615
+ function baseUrlReference(semantics) {
616
+ const label = OPENING_HOLE.exec(semantics.path ?? "")?.[1];
617
+ return label === void 0 ? null : referenceFromName(patternHole(label));
480
618
  }
481
619
  function normalizePath(path) {
482
620
  let normalized = path.replace(/:([a-zA-Z_]\w*)/g, "{$1}");
@@ -610,22 +748,20 @@ var restSemantics = defineBoundarySemantics({
610
748
  * route parameter, and `/orders/{id}` means every id rather than a
611
749
  * variable somebody set.
612
750
  */
613
- groundName(semantics, deployedAs) {
614
- if (semantics.path === null) {
615
- return null;
616
- }
617
- const opening = OPENING_HOLE.exec(semantics.path);
618
- if (opening === null) {
751
+ groundName(semantics, deployment) {
752
+ const reference = baseUrlReference(semantics);
753
+ if (reference === null) {
619
754
  return null;
620
755
  }
621
- const [, label, rest] = opening;
622
- const base = deployedAs(variableOf(label));
756
+ const base = deployment.setTo(reference);
623
757
  if (base === null) {
624
758
  return null;
625
759
  }
760
+ const rest = OPENING_HOLE.exec(semantics.path ?? "")?.[2] ?? "";
626
761
  const grounded = pathAfterOrigin(`${base}${rest}`);
627
762
  return { ...semantics, path: grounded === "" ? "/" : grounded };
628
763
  },
764
+ nameReference: baseUrlReference,
629
765
  ruleBoundary: {
630
766
  // "METHOD /path": one leading token, then an absolute path.
631
767
  claims(raw) {
@@ -715,6 +851,9 @@ var StorageSemanticsSchema = import_zod9.z.object({
715
851
  */
716
852
  accessPath: import_zod9.z.string().nullable()
717
853
  });
854
+ function containerReference(semantics) {
855
+ return semantics.container === null ? null : referenceFromName(semantics.container);
856
+ }
718
857
  var storageSemantics = defineBoundarySemantics({
719
858
  name: "storage",
720
859
  schema: StorageSemanticsSchema,
@@ -731,7 +870,21 @@ var storageSemantics = defineBoundarySemantics({
731
870
  exchangesHttpResponses: false,
732
871
  reportsUnpairedItself: false,
733
872
  identityKey: () => null,
734
- displayLabel: storageLabel
873
+ displayLabel: storageLabel,
874
+ /**
875
+ * A container the source states as a variable is whatever the
876
+ * deployment sets that variable to. `{SUBSCRIBERS_TABLE}` in the
877
+ * code and `prod-subscribers-v1` in the manifest are one table.
878
+ */
879
+ groundName(semantics, deployment) {
880
+ const reference = containerReference(semantics);
881
+ if (reference === null) {
882
+ return null;
883
+ }
884
+ const name = deployment.setTo(reference);
885
+ return name === null ? null : { ...semantics, container: name };
886
+ },
887
+ nameReference: containerReference
735
888
  }
736
889
  });
737
890
  function storageLabel(semantics) {
@@ -750,6 +903,9 @@ var UnitInvocationSemanticsSchema = DeployableUnitSchema.extend({
750
903
  name: import_zod10.z.literal("unit-invocation"),
751
904
  instanceName: DeployableUnitSchema.shape.instanceName.nullable()
752
905
  });
906
+ function calleeReference(semantics) {
907
+ return semantics.instanceName === null ? null : referenceFromName(semantics.instanceName);
908
+ }
753
909
  var unitInvocationSemantics = defineBoundarySemantics({
754
910
  name: "unit-invocation",
755
911
  schema: UnitInvocationSemanticsSchema,
@@ -772,8 +928,13 @@ var unitInvocationSemantics = defineBoundarySemantics({
772
928
  * being reported as a problem here.
773
929
  */
774
930
  reportsUnpairedItself: false,
931
+ /**
932
+ * A callee the source states only as a variable is not a name for
933
+ * anything until the deployment fills it in, so it keys as nothing
934
+ * rather than as a bucket that would agree with itself alone.
935
+ */
775
936
  identityKey(semantics) {
776
- if (semantics.instanceName === null) {
937
+ if (semantics.instanceName === null || namesNothing(semantics.instanceName)) {
777
938
  return null;
778
939
  }
779
940
  return unitIdentityKey(
@@ -789,7 +950,23 @@ var unitInvocationSemantics = defineBoundarySemantics({
789
950
  semantics.deploymentTarget,
790
951
  semantics.instanceName
791
952
  );
792
- }
953
+ },
954
+ /**
955
+ * A call that reads its callee's name out of the environment
956
+ * reaches whichever resource the template wires that variable to.
957
+ * The logical id is the answer rather than the deployed name,
958
+ * because the invoked unit's own summary is keyed by the logical
959
+ * id and neither side ever writes the other's string.
960
+ */
961
+ groundName(semantics, deployment) {
962
+ const reference = calleeReference(semantics);
963
+ if (reference === null) {
964
+ return null;
965
+ }
966
+ const logicalId = deployment.pointsAt(reference);
967
+ return logicalId === null ? null : { ...semantics, instanceName: logicalId };
968
+ },
969
+ nameReference: calleeReference
793
970
  }
794
971
  });
795
972
 
@@ -1000,12 +1177,16 @@ function withRewrittenPaths(binding, rewrite) {
1000
1177
  semantics: behavior.rewritePaths(binding.semantics, rewrite)
1001
1178
  };
1002
1179
  }
1003
- function groundedPairingKey(binding, deployedAs) {
1180
+ function groundedPairingKey(binding, deployment) {
1181
+ return pairingKey(groundBinding(binding, deployment));
1182
+ }
1183
+ function groundBinding(binding, deployment) {
1004
1184
  const behavior = behaviorOf(binding.semantics);
1005
- const grounded = behavior.groundName?.(binding.semantics, deployedAs) ?? null;
1006
- return pairingKey(
1007
- grounded === null ? binding : { ...binding, semantics: grounded }
1008
- );
1185
+ const grounded = behavior.groundName?.(binding.semantics, deployment) ?? null;
1186
+ return grounded === null ? binding : { ...binding, semantics: grounded };
1187
+ }
1188
+ function nameReference(binding) {
1189
+ return behaviorOf(binding.semantics).nameReference?.(binding.semantics) ?? null;
1009
1190
  }
1010
1191
  function pairingKey(binding) {
1011
1192
  const behavior = behaviorOf(binding.semantics);
@@ -1059,141 +1240,6 @@ function withinScope(binding, scope) {
1059
1240
  return within === void 0 ? false : within(binding.semantics, scope);
1060
1241
  }
1061
1242
 
1062
- // src/dispatch.ts
1063
- function dispatchByType(table, value) {
1064
- const handler = table[value.type];
1065
- return handler(value);
1066
- }
1067
-
1068
- // src/boundaryName.ts
1069
- var HOLE_SPLIT = /(\{[^}]*\})/;
1070
- var SUB_TOKEN = /\$\{([^}]*)\}/g;
1071
- var SUB_ESCAPE = /\$\{!/;
1072
- function isHole(piece) {
1073
- return piece.startsWith("{") && piece.endsWith("}");
1074
- }
1075
- function parseBoundaryName(name) {
1076
- const parts = name.split(HOLE_SPLIT).filter((piece) => piece !== "").map(
1077
- (piece) => isHole(piece) ? { type: "hole", label: piece.slice(1, -1) } : { type: "text", text: piece }
1078
- );
1079
- const only = parts.length === 1 ? parts[0] : void 0;
1080
- if (only !== void 0 && only.type === "hole") {
1081
- return { type: "reference", path: only.label.split(".") };
1082
- }
1083
- if (parts.some((part) => part.type === "hole")) {
1084
- return { type: "pattern", parts };
1085
- }
1086
- return { type: "literal", value: name };
1087
- }
1088
- var partString = {
1089
- text: (part) => part.text,
1090
- hole: (part) => patternHole(part.label)
1091
- };
1092
- var nameString = {
1093
- literal: (name) => name.value,
1094
- pattern: (name) => name.parts.map((part) => dispatchByType(partString, part)).join(""),
1095
- reference: (name) => patternHole(name.path.join("."))
1096
- };
1097
- function boundaryNameString(name) {
1098
- return dispatchByType(nameString, name);
1099
- }
1100
- function patternHole(label) {
1101
- return `{${label}}`;
1102
- }
1103
- function namePatternFromSub(value) {
1104
- if (Array.isArray(value)) {
1105
- return typeof value[0] === "string" ? namePatternFromSub(value[0]) : null;
1106
- }
1107
- if (typeof value !== "string") {
1108
- return null;
1109
- }
1110
- if (SUB_ESCAPE.test(value)) {
1111
- return null;
1112
- }
1113
- return value.replace(
1114
- SUB_TOKEN,
1115
- (_whole, inner) => patternHole(inner)
1116
- );
1117
- }
1118
- function namesNothing(name) {
1119
- return parseBoundaryName(name).type === "reference";
1120
- }
1121
- function hasNameHole(name) {
1122
- return parseBoundaryName(name).type !== "literal";
1123
- }
1124
- function namePatternKey(name) {
1125
- return dispatchByType(
1126
- {
1127
- literal: (parsed) => parsed.value,
1128
- pattern: (parsed) => boundaryNameString({
1129
- type: "pattern",
1130
- parts: parsed.parts.map(
1131
- (part) => part.type === "hole" ? { type: "hole", label: "" } : part
1132
- )
1133
- }),
1134
- reference: () => patternHole("")
1135
- },
1136
- parseBoundaryName(name)
1137
- );
1138
- }
1139
- function fixedTextLength(name) {
1140
- return dispatchByType(
1141
- {
1142
- literal: (parsed) => parsed.value.length,
1143
- pattern: (parsed) => parsed.parts.reduce(
1144
- (total, part) => total + (part.type === "text" ? part.text.length : 0),
1145
- 0
1146
- ),
1147
- reference: () => 0
1148
- },
1149
- parseBoundaryName(name)
1150
- );
1151
- }
1152
- function quote(text) {
1153
- return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1154
- }
1155
- function admits(pattern, name) {
1156
- const source = pattern.parts.map((part) => part.type === "hole" ? ".+" : quote(part.text)).join("");
1157
- return new RegExp(`^${source}$`).test(name);
1158
- }
1159
- function namesAgree(a, b) {
1160
- const left = parseBoundaryName(a);
1161
- const right = parseBoundaryName(b);
1162
- if (left.type === "reference" || right.type === "reference") {
1163
- return false;
1164
- }
1165
- if (left.type === "pattern" && right.type === "pattern") {
1166
- return namePatternKey(a) === namePatternKey(b);
1167
- }
1168
- if (left.type === "pattern") {
1169
- return admits(left, b);
1170
- }
1171
- if (right.type === "pattern") {
1172
- return admits(right, a);
1173
- }
1174
- return a === b;
1175
- }
1176
- function referenceName(reference) {
1177
- const path = [reference.root, ...reference.fields];
1178
- if (path.some((part) => part === "")) {
1179
- return null;
1180
- }
1181
- return boundaryNameString({ type: "reference", path });
1182
- }
1183
- function referenceOf(name) {
1184
- if (name.type !== "reference") {
1185
- return null;
1186
- }
1187
- const root = name.path[0];
1188
- if (root === void 0 || name.path.some((part) => part === "")) {
1189
- return null;
1190
- }
1191
- return { root, fields: name.path.slice(1) };
1192
- }
1193
- function referenceFromName(name) {
1194
- return referenceOf(parseBoundaryName(name));
1195
- }
1196
-
1197
1243
  // src/boundarySpelling.ts
1198
1244
  function resourceNameIn(spelling) {
1199
1245
  if (spelling.startsWith("arn:")) {
@@ -1258,6 +1304,13 @@ function fileInCodeScope(file, scope) {
1258
1304
  return path === prefix || path.startsWith(`${prefix}/`);
1259
1305
  }
1260
1306
 
1307
+ // src/deployment.ts
1308
+ var NOTHING_DEPLOYED = {
1309
+ variableFor: () => null,
1310
+ setTo: () => null,
1311
+ pointsAt: () => null
1312
+ };
1313
+
1261
1314
  // src/relation.ts
1262
1315
  var import_zod13 = require("zod");
1263
1316
  var RelationSchema = import_zod13.z.enum([
@@ -1669,6 +1722,7 @@ function metricBinding(opts) {
1669
1722
  GraphqlResolverSemanticsSchema,
1670
1723
  MessageBusSemanticsSchema,
1671
1724
  MetricSemanticsSchema,
1725
+ NOTHING_DEPLOYED,
1672
1726
  RelationSchema,
1673
1727
  RestSemanticsSchema,
1674
1728
  RuntimeConfigSemanticsSchema,
@@ -1704,6 +1758,7 @@ function metricBinding(opts) {
1704
1758
  gqlIdentityKey,
1705
1759
  graphqlOperationBinding,
1706
1760
  graphqlResolverBinding,
1761
+ groundBinding,
1707
1762
  groundedPairingKey,
1708
1763
  hasNameHole,
1709
1764
  isGraphqlOperationBinding,
@@ -1713,6 +1768,7 @@ function metricBinding(opts) {
1713
1768
  metricIdentityKey,
1714
1769
  namePatternFromSub,
1715
1770
  namePatternKey,
1771
+ nameReference,
1716
1772
  namesAgree,
1717
1773
  namesBoundary,
1718
1774
  namesBoundaryExactly,