@suss/ir-core 0.9.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/README.md +28 -6
- package/dist/{chunk-XUXN6DIX.js → chunk-HBAMPBAO.js} +3 -1
- package/dist/index.cjs +121 -44
- package/dist/index.d.cts +110 -48
- package/dist/index.d.ts +110 -48
- package/dist/index.js +113 -44
- package/dist/schemas.cjs +3 -1
- package/dist/schemas.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,17 +31,39 @@ The boundary key uses only the subject, so both forms end up in the same bucket
|
|
|
31
31
|
|
|
32
32
|
Schemas are the single source of truth (`@suss/ir-core/schemas`); the types are derived from them. The recursive `TypeShape` is a hand-written named export so consuming packages reference it by name across the package boundary rather than inlining the recursion.
|
|
33
33
|
|
|
34
|
-
##
|
|
34
|
+
## Boundary names
|
|
35
35
|
|
|
36
|
-
A
|
|
36
|
+
A container or an access path in a summary is a string that means one of three things, and the braces alone say which:
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
- A **literal** (`orders-v1`) is the name itself.
|
|
39
|
+
- A **pattern** (`{stage}-orders-v1`) is fixed text with `{}` holes for the parts a deployment fills in. A template writes `!Sub "${StageName}-orders-v1"` and the code writes `` `${stage}-orders-v1` ``: neither states a string, both agree about the fixed text, and each spells the parameter its own way.
|
|
40
|
+
- A **reference** (`{location.bucket}`, `{ORDER_TABLE}`) is one hole and no fixed text, saying where to go and ask rather than what the name is.
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
`boundaryName.ts` owns the syntax in both directions. `parseBoundaryName` turns a string into the discriminated `BoundaryName` value, `boundaryNameString` turns one back into exactly the string it came from, and everything else, `namesAgree`, `namesNothing`, `fixedTextLength`, `referenceName`, `referenceFromName`, is a view over that pair. Nothing outside the module reads or writes the braces itself: a second parser can disagree about which of the three a string is, and a second printer can spell a value the parser cannot read back. Both have happened. The two halves of caller grounding once spelled a reference differently and never met (#456), which is why writing one and reading one are the same module rather than a format each side implements for itself. `check:name-syntax` in CI keeps it that way.
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
The wire format is the string. Summaries on disk carry `orders-v1`, `{stage}-orders-v1`, `{location.table}` unchanged, so nothing about this layout is a schema change, and an old summary reads the same as a new one.
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
### How two names pair
|
|
47
|
+
|
|
48
|
+
`namesAgree` says whether two names are the same name. Two patterns agree when their fixed parts line up, since a hole on one side meets a hole on the other. A pattern and a literal agree when the fixed text is where the pattern says it is, which is what happens when one side hardcoded what the other parameterized. A reference agrees with nothing until something settles it, since one bare hole would otherwise agree with every name there is.
|
|
49
|
+
|
|
50
|
+
A hole covers anything, because a name has no separator every project agrees on: a region is written `us-east-1`, and a hole that stopped at the first hyphen would miss it. The cost is that one name can be covered by two patterns, `orders-{suffix}` and `orders-blue-{suffix}` both cover `orders-blue-v1`, so choosing between them belongs to whichever pass picks a provider for an access. `fixedTextLength` is what such a pass ranks by: the pattern that states more fixed text is the more specific one, and two patterns that state the same amount settle nothing between them.
|
|
51
|
+
|
|
52
|
+
### A name that says where to go and ask
|
|
53
|
+
|
|
54
|
+
Some code cannot say what it reaches. A storage layer takes the bucket as an argument, and a service reads its table name out of the deployment. Neither states a name, and a name is what the rest of suss pairs on, so both write a reference instead.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
referenceName({ root: "location", fields: ["bucket"] }); // "{location.bucket}"
|
|
58
|
+
referenceName({ root: "ORDER_TABLE", fields: [] }); // "{ORDER_TABLE}"
|
|
59
|
+
referenceFromName("{location.bucket}"); // { root: "location", fields: ["bucket"] }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The root is the value the code starts from, and the fields are what it reads inside that value. A language adapter writes one while reading source, and the checker settles it much later by joining over a whole run: `{location.bucket}` against what each caller passed for `location`, `{ORDER_TABLE}` against what the deployment sets. Whether the root is a parameter of the unit or a variable the deployment sets is not in the string, on purpose: it depends on the unit's inputs and their roles, which the grounding pass already has in hand, and a bare `{X}` is deliberately tried both ways there. A root kind written into the name would either be ignored or change which of those runs.
|
|
63
|
+
|
|
64
|
+
### Two look-alike conventions that stay apart
|
|
65
|
+
|
|
66
|
+
A REST route path also spells its parameters with braces (`/users/{id}`), but a route hole stops at the `/` between segments, so route paths keep their own comparison beside the REST semantics. A message-bus channel never uses braces at all; its `bus#subject` form has its own module, described above.
|
|
45
67
|
|
|
46
68
|
## Words OpenTelemetry already has
|
|
47
69
|
|
|
@@ -503,7 +503,9 @@ var StorageSemanticsSchema = z9.object({
|
|
|
503
503
|
* The table, bucket, collection, or index, as the source declares
|
|
504
504
|
* it, or null when the source states one this reader could not
|
|
505
505
|
* settle. A null container pairs with nothing, rather than with
|
|
506
|
-
* whatever its source text happens to spell.
|
|
506
|
+
* whatever its source text happens to spell. The string is in the
|
|
507
|
+
* boundary-name syntax: a literal, a pattern with `{}` holes, or a
|
|
508
|
+
* reference, and `parseBoundaryName` is the one reader of it.
|
|
507
509
|
*/
|
|
508
510
|
container: z9.string().nullable(),
|
|
509
511
|
/**
|
package/dist/index.cjs
CHANGED
|
@@ -43,6 +43,7 @@ __export(index_exports, {
|
|
|
43
43
|
bodyShapesMatch: () => bodyShapesMatch,
|
|
44
44
|
boundaryKey: () => boundaryKey,
|
|
45
45
|
boundaryLabel: () => boundaryLabel,
|
|
46
|
+
boundaryNameString: () => boundaryNameString,
|
|
46
47
|
busIdentityKey: () => busIdentityKey,
|
|
47
48
|
busesAgree: () => busesAgree,
|
|
48
49
|
canPair: () => canPair,
|
|
@@ -75,7 +76,12 @@ __export(index_exports, {
|
|
|
75
76
|
normalizeRuleBoundary: () => normalizeRuleBoundary,
|
|
76
77
|
packageExportBinding: () => packageExportBinding,
|
|
77
78
|
pairingKey: () => pairingKey,
|
|
79
|
+
parseBoundaryName: () => parseBoundaryName,
|
|
78
80
|
parseChannel: () => parseChannel,
|
|
81
|
+
patternHole: () => patternHole,
|
|
82
|
+
referenceFromName: () => referenceFromName,
|
|
83
|
+
referenceName: () => referenceName,
|
|
84
|
+
referenceOf: () => referenceOf,
|
|
79
85
|
reportsUnpairedItself: () => reportsUnpairedItself,
|
|
80
86
|
restBinding: () => restBinding,
|
|
81
87
|
routePathAdmits: () => routePathAdmits,
|
|
@@ -596,7 +602,9 @@ var StorageSemanticsSchema = import_zod9.z.object({
|
|
|
596
602
|
* The table, bucket, collection, or index, as the source declares
|
|
597
603
|
* it, or null when the source states one this reader could not
|
|
598
604
|
* settle. A null container pairs with nothing, rather than with
|
|
599
|
-
* 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.
|
|
600
608
|
*/
|
|
601
609
|
container: import_zod9.z.string().nullable(),
|
|
602
610
|
/**
|
|
@@ -858,29 +866,47 @@ function servesRequest(binding, method, path) {
|
|
|
858
866
|
return serves(binding.semantics, method, path);
|
|
859
867
|
}
|
|
860
868
|
|
|
861
|
-
// src/codeScope.ts
|
|
862
|
-
function codeScopePath(raw) {
|
|
863
|
-
return raw.trim().replace(/^\.\//, "").replace(/\/+$/, "");
|
|
864
|
-
}
|
|
865
|
-
function fileInCodeScope(file, scope) {
|
|
866
|
-
const prefix = codeScopePath(scope);
|
|
867
|
-
if (prefix === "" || prefix === ".") {
|
|
868
|
-
return true;
|
|
869
|
-
}
|
|
870
|
-
const path = codeScopePath(file);
|
|
871
|
-
return path === prefix || path.startsWith(`${prefix}/`);
|
|
872
|
-
}
|
|
873
|
-
|
|
874
869
|
// src/dispatch.ts
|
|
875
870
|
function dispatchByType(table, value) {
|
|
876
871
|
const handler = table[value.type];
|
|
877
872
|
return handler(value);
|
|
878
873
|
}
|
|
879
874
|
|
|
880
|
-
// src/
|
|
881
|
-
var
|
|
875
|
+
// src/boundaryName.ts
|
|
876
|
+
var HOLE_SPLIT = /(\{[^}]*\})/;
|
|
882
877
|
var SUB_TOKEN = /\$\{([^}]*)\}/g;
|
|
883
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
|
+
}
|
|
884
910
|
function namePatternFromSub(value) {
|
|
885
911
|
if (Array.isArray(value)) {
|
|
886
912
|
return typeof value[0] === "string" ? namePatternFromSub(value[0]) : null;
|
|
@@ -891,56 +917,101 @@ function namePatternFromSub(value) {
|
|
|
891
917
|
if (SUB_ESCAPE.test(value)) {
|
|
892
918
|
return null;
|
|
893
919
|
}
|
|
894
|
-
return value.replace(
|
|
920
|
+
return value.replace(
|
|
921
|
+
SUB_TOKEN,
|
|
922
|
+
(_whole, inner) => patternHole(inner)
|
|
923
|
+
);
|
|
895
924
|
}
|
|
896
925
|
function namesNothing(name) {
|
|
897
|
-
return
|
|
926
|
+
return parseBoundaryName(name).type === "reference";
|
|
898
927
|
}
|
|
899
928
|
function hasNameHole(name) {
|
|
900
|
-
|
|
901
|
-
return HOLE.test(name);
|
|
929
|
+
return parseBoundaryName(name).type !== "literal";
|
|
902
930
|
}
|
|
903
931
|
function namePatternKey(name) {
|
|
904
|
-
return
|
|
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
|
+
);
|
|
905
945
|
}
|
|
906
946
|
function fixedTextLength(name) {
|
|
907
|
-
return
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
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
|
+
);
|
|
911
958
|
}
|
|
912
959
|
function quote(text) {
|
|
913
960
|
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
914
961
|
}
|
|
915
|
-
function holeSource(_following) {
|
|
916
|
-
return ".+";
|
|
917
|
-
}
|
|
918
962
|
function admits(pattern, name) {
|
|
919
|
-
const
|
|
920
|
-
const source = parts.map(
|
|
921
|
-
(part, index) => isHole(part) ? holeSource(parts[index + 1] ?? "") : quote(part)
|
|
922
|
-
).join("");
|
|
963
|
+
const source = pattern.parts.map((part) => part.type === "hole" ? ".+" : quote(part.text)).join("");
|
|
923
964
|
return new RegExp(`^${source}$`).test(name);
|
|
924
965
|
}
|
|
925
966
|
function namesAgree(a, b) {
|
|
926
|
-
|
|
967
|
+
const left = parseBoundaryName(a);
|
|
968
|
+
const right = parseBoundaryName(b);
|
|
969
|
+
if (left.type === "reference" || right.type === "reference") {
|
|
927
970
|
return false;
|
|
928
971
|
}
|
|
929
|
-
if (
|
|
930
|
-
return true;
|
|
931
|
-
}
|
|
932
|
-
const aHasHole = hasNameHole(a);
|
|
933
|
-
const bHasHole = hasNameHole(b);
|
|
934
|
-
if (aHasHole && bHasHole) {
|
|
972
|
+
if (left.type === "pattern" && right.type === "pattern") {
|
|
935
973
|
return namePatternKey(a) === namePatternKey(b);
|
|
936
974
|
}
|
|
937
|
-
if (
|
|
938
|
-
return admits(
|
|
975
|
+
if (left.type === "pattern") {
|
|
976
|
+
return admits(left, b);
|
|
939
977
|
}
|
|
940
|
-
if (
|
|
941
|
-
return admits(
|
|
978
|
+
if (right.type === "pattern") {
|
|
979
|
+
return admits(right, a);
|
|
942
980
|
}
|
|
943
|
-
return
|
|
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}/`);
|
|
944
1015
|
}
|
|
945
1016
|
|
|
946
1017
|
// src/suppressions.ts
|
|
@@ -1343,6 +1414,7 @@ function metricBinding(opts) {
|
|
|
1343
1414
|
bodyShapesMatch,
|
|
1344
1415
|
boundaryKey,
|
|
1345
1416
|
boundaryLabel,
|
|
1417
|
+
boundaryNameString,
|
|
1346
1418
|
busIdentityKey,
|
|
1347
1419
|
busesAgree,
|
|
1348
1420
|
canPair,
|
|
@@ -1375,7 +1447,12 @@ function metricBinding(opts) {
|
|
|
1375
1447
|
normalizeRuleBoundary,
|
|
1376
1448
|
packageExportBinding,
|
|
1377
1449
|
pairingKey,
|
|
1450
|
+
parseBoundaryName,
|
|
1378
1451
|
parseChannel,
|
|
1452
|
+
patternHole,
|
|
1453
|
+
referenceFromName,
|
|
1454
|
+
referenceName,
|
|
1455
|
+
referenceOf,
|
|
1379
1456
|
reportsUnpairedItself,
|
|
1380
1457
|
restBinding,
|
|
1381
1458
|
routePathAdmits,
|
package/dist/index.d.cts
CHANGED
|
@@ -243,6 +243,115 @@ declare function reportsUnpairedItself(binding: BoundaryBinding): boolean;
|
|
|
243
243
|
*/
|
|
244
244
|
declare function servesRequest(binding: BoundaryBinding, method: string, path: string): MatchResult | null;
|
|
245
245
|
|
|
246
|
+
/**
|
|
247
|
+
* The boundary-name syntax, parsed and printed in one place.
|
|
248
|
+
*
|
|
249
|
+
* A name in a summary means one of three things, told apart by the
|
|
250
|
+
* braces alone: a literal (`orders-v1`), a pattern with deploy-time
|
|
251
|
+
* holes (`{stage}-orders-v1`), or a reference saying where to go and
|
|
252
|
+
* ask (`{location.bucket}`, `{ORDER_TABLE}`). A second parser can
|
|
253
|
+
* disagree about which is which, and a second printer can spell a
|
|
254
|
+
* value this one cannot read back, so everything that touches the
|
|
255
|
+
* braces calls this module: `parseBoundaryName` and
|
|
256
|
+
* `boundaryNameString` are the two directions, and the helpers below
|
|
257
|
+
* are views over them. The package README tells the longer story,
|
|
258
|
+
* including why REST route paths and message-bus channels stay apart.
|
|
259
|
+
*/
|
|
260
|
+
/** One piece of a pattern: text the writer stated, or a hole. */
|
|
261
|
+
type NamePart = {
|
|
262
|
+
type: "text";
|
|
263
|
+
text: string;
|
|
264
|
+
} | {
|
|
265
|
+
type: "hole";
|
|
266
|
+
label: string;
|
|
267
|
+
};
|
|
268
|
+
/**
|
|
269
|
+
* What a name string means. A `reference` keeps its raw dot-separated
|
|
270
|
+
* path, and `referenceOf` is the view that checks the parts are all
|
|
271
|
+
* present, so a malformed spelling still classifies as a reference and
|
|
272
|
+
* still pairs with nothing.
|
|
273
|
+
*/
|
|
274
|
+
type BoundaryName = {
|
|
275
|
+
type: "literal";
|
|
276
|
+
value: string;
|
|
277
|
+
} | {
|
|
278
|
+
type: "pattern";
|
|
279
|
+
parts: NamePart[];
|
|
280
|
+
} | {
|
|
281
|
+
type: "reference";
|
|
282
|
+
path: string[];
|
|
283
|
+
};
|
|
284
|
+
/** What a name string means. The exact inverse of `boundaryNameString`. */
|
|
285
|
+
declare function parseBoundaryName(name: string): BoundaryName;
|
|
286
|
+
/** The one serializer. Everything a summary spells goes through here. */
|
|
287
|
+
declare function boundaryNameString(name: BoundaryName): string;
|
|
288
|
+
/**
|
|
289
|
+
* How a hole is spelled. A reader that assembles a pattern a part at a
|
|
290
|
+
* time, the way the adapter's name reader does, mints each hole here
|
|
291
|
+
* so the spelling cannot drift from the parse above.
|
|
292
|
+
*/
|
|
293
|
+
declare function patternHole(label: string): string;
|
|
294
|
+
/**
|
|
295
|
+
* The name a CloudFormation `Fn::Sub` value states. The array form
|
|
296
|
+
* takes its template from the first element, and the variable map only
|
|
297
|
+
* says where a substitution comes from, which does not change the name.
|
|
298
|
+
*/
|
|
299
|
+
declare function namePatternFromSub(value: unknown): string | null;
|
|
300
|
+
/**
|
|
301
|
+
* Whether a name says only that somebody else knows it. A wrapper that
|
|
302
|
+
* takes its bucket as an argument states `{location.bucket}` and
|
|
303
|
+
* nothing more, which says which parameter to ask rather than which
|
|
304
|
+
* bucket. A name like that agrees with nothing until something grounds
|
|
305
|
+
* it, since otherwise it would agree with every name there is.
|
|
306
|
+
*/
|
|
307
|
+
declare function namesNothing(name: string): boolean;
|
|
308
|
+
/** Whether a name has anything a source left for deploy time to fill. */
|
|
309
|
+
declare function hasNameHole(name: string): boolean;
|
|
310
|
+
/**
|
|
311
|
+
* The form two patterns are compared in: every hole reduced to the same
|
|
312
|
+
* token, since the two sides pick their own name for the parameter.
|
|
313
|
+
*/
|
|
314
|
+
declare function namePatternKey(name: string): string;
|
|
315
|
+
/** How much of a name the writer stated rather than left for deploy time. */
|
|
316
|
+
declare function fixedTextLength(name: string): number;
|
|
317
|
+
/**
|
|
318
|
+
* Whether two names are the same name. Two patterns agree when their
|
|
319
|
+
* fixed parts line up, since a hole on one side meets a hole on the
|
|
320
|
+
* other. A pattern and a concrete name agree when the fixed parts are
|
|
321
|
+
* where the pattern says they are, which is what happens when one side
|
|
322
|
+
* hardcoded what the other parameterized. A reference agrees with
|
|
323
|
+
* nothing on either side.
|
|
324
|
+
*/
|
|
325
|
+
declare function namesAgree(a: string, b: string): boolean;
|
|
326
|
+
/** Where a reference says to go and ask. */
|
|
327
|
+
interface Reference {
|
|
328
|
+
/**
|
|
329
|
+
* The value the code starts from: a parameter of the unit the
|
|
330
|
+
* reference was written in, or a variable the deployment sets. Which
|
|
331
|
+
* of those it is depends on the unit's inputs, so the reader that
|
|
332
|
+
* grounds a reference decides, not the string.
|
|
333
|
+
*/
|
|
334
|
+
root: string;
|
|
335
|
+
/** The fields to read inside it, outermost first. */
|
|
336
|
+
fields: string[];
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* How a reference is written. Null when a part of it is empty, since a
|
|
340
|
+
* reference has to say what to ask about.
|
|
341
|
+
*/
|
|
342
|
+
declare function referenceName(reference: Reference): string | null;
|
|
343
|
+
/**
|
|
344
|
+
* The place a parsed reference says to ask, or null for a name that is
|
|
345
|
+
* not a reference, or for a reference with a part missing, which says
|
|
346
|
+
* nothing anybody could answer.
|
|
347
|
+
*/
|
|
348
|
+
declare function referenceOf(name: BoundaryName): Reference | null;
|
|
349
|
+
/**
|
|
350
|
+
* The reference a name states, or null when the name states a name
|
|
351
|
+
* rather than where to go and ask.
|
|
352
|
+
*/
|
|
353
|
+
declare function referenceFromName(name: string): Reference | null;
|
|
354
|
+
|
|
246
355
|
/**
|
|
247
356
|
* Deciding when two message-bus channel strings mean the same thing.
|
|
248
357
|
*
|
|
@@ -325,53 +434,6 @@ declare function dispatchByType<T extends {
|
|
|
325
434
|
type: string;
|
|
326
435
|
}, R>(table: DispatchTable<T, R>, value: T): R;
|
|
327
436
|
|
|
328
|
-
/**
|
|
329
|
-
* A name two sides state with a hole in it.
|
|
330
|
-
*
|
|
331
|
-
* A deployed resource is often called something built at deploy time:
|
|
332
|
-
* a template writes `!Sub "${StageName}-orders-v1"` and the code writes
|
|
333
|
-
* `` `${stage}-orders-v1` ``. Neither side states a string, both agree
|
|
334
|
-
* about the fixed text, and each spells the parameter its own way. So a
|
|
335
|
-
* name is written here as fixed text with `{}` holes, and two names
|
|
336
|
-
* agree when their fixed parts line up.
|
|
337
|
-
*
|
|
338
|
-
* A hole covers a whole parameter rather than one piece of a name,
|
|
339
|
-
* because a name has no separator every project agrees on. Two
|
|
340
|
-
* patterns that both cover one name are told apart by how much fixed
|
|
341
|
-
* text each states, rather than by narrowing what a hole can cover.
|
|
342
|
-
*/
|
|
343
|
-
/**
|
|
344
|
-
* The name a CloudFormation `Fn::Sub` value states. The array form
|
|
345
|
-
* takes its template from the first element, and the variable map only
|
|
346
|
-
* says where a substitution comes from, which does not change the name.
|
|
347
|
-
*/
|
|
348
|
-
declare function namePatternFromSub(value: unknown): string | null;
|
|
349
|
-
/**
|
|
350
|
-
* Whether a name says only that somebody else knows it. A wrapper that
|
|
351
|
-
* takes its bucket as an argument states `{location.bucket}` and
|
|
352
|
-
* nothing more, which says which parameter to ask rather than which
|
|
353
|
-
* bucket. A name like that agrees with nothing until something grounds
|
|
354
|
-
* it, since otherwise it would agree with every name there is.
|
|
355
|
-
*/
|
|
356
|
-
declare function namesNothing(name: string): boolean;
|
|
357
|
-
/** Whether a name has anything a source left for deploy time to fill. */
|
|
358
|
-
declare function hasNameHole(name: string): boolean;
|
|
359
|
-
/**
|
|
360
|
-
* The form two patterns are compared in: every hole reduced to the same
|
|
361
|
-
* token, since the two sides pick their own name for the parameter.
|
|
362
|
-
*/
|
|
363
|
-
declare function namePatternKey(name: string): string;
|
|
364
|
-
/** How much of a name the writer stated rather than left for deploy time. */
|
|
365
|
-
declare function fixedTextLength(name: string): number;
|
|
366
|
-
/**
|
|
367
|
-
* Whether two names are the same name. Two patterns agree when their
|
|
368
|
-
* fixed parts line up, since a hole on one side meets a hole on the
|
|
369
|
-
* other. A pattern and a concrete name agree when the fixed parts are
|
|
370
|
-
* where the pattern says they are, which is what happens when one side
|
|
371
|
-
* hardcoded what the other parameterized.
|
|
372
|
-
*/
|
|
373
|
-
declare function namesAgree(a: string, b: string): boolean;
|
|
374
|
-
|
|
375
437
|
/**
|
|
376
438
|
* The suppression pipeline, over the smallest finding both checkers share.
|
|
377
439
|
*
|
|
@@ -622,4 +684,4 @@ declare function metricBinding(opts: {
|
|
|
622
684
|
metricType: string | null;
|
|
623
685
|
}): BoundaryBinding;
|
|
624
686
|
|
|
625
|
-
export { type BoundaryBehavior, type BoundaryBinding, BoundaryBindingSchema, type BoundarySemanticsDefinition, type BusIdentityKey, type Channel, type ConfidenceInfo, ConfidenceInfoSchema, type ConfidenceLevel, ConfidenceLevelSchema, type ConfidenceSource, ConfidenceSourceSchema, type Corroboration, CorroborationSchema, DeployableUnit, type DispatchTable, type FindingSuppression, type FnIdentityKey, type GqlIdentityKey, type MatchResult, MessageBusSemantics, MessageBusTechnology, type MetricIdentityKey, type MetricSemantics, MetricSemanticsSchema, type ParsedChannel, Semantics, type SemconvAttribute, type SemconvMapping, type SourceLocation, SourceLocationSchema, type SuppressableSeverity, type SuppressibleFinding, type SuppressionFile, SuppressionFileSchema, type SuppressionRule, SuppressionRuleSchema, TypeShape, applySuppressionsToFindings, bodyShapesMatch, boundaryKey, boundaryLabel, busIdentityKey, busesAgree, canPair, channelsPair, codeScopePath, countsForThreshold, dispatchByType, displayLabel, exchangesHttpResponses, fileInCodeScope, fixedTextLength, fnIdentityKey, formatChannel, functionCallBinding, gqlIdentityKey, graphqlOperationBinding, graphqlResolverBinding, hasNameHole, isGraphqlOperationBinding, messageBusBinding, metricBinding, metricIdentityKey, namePatternFromSub, namePatternKey, namesAgree, namesNothing, normalizeRuleBoundary, packageExportBinding, pairingKey, parseChannel, reportsUnpairedItself, restBinding, ruleBoundaryMatchesKey, runtimeConfigBinding, semanticsAgree, semconvAttributes, servesRequest, storageBinding, validateRule };
|
|
687
|
+
export { type BoundaryBehavior, type BoundaryBinding, BoundaryBindingSchema, type BoundaryName, type BoundarySemanticsDefinition, type BusIdentityKey, type Channel, type ConfidenceInfo, ConfidenceInfoSchema, type ConfidenceLevel, ConfidenceLevelSchema, type ConfidenceSource, ConfidenceSourceSchema, type Corroboration, CorroborationSchema, DeployableUnit, type DispatchTable, type FindingSuppression, type FnIdentityKey, type GqlIdentityKey, type MatchResult, MessageBusSemantics, MessageBusTechnology, type MetricIdentityKey, type MetricSemantics, MetricSemanticsSchema, type NamePart, type ParsedChannel, type Reference, Semantics, type SemconvAttribute, type SemconvMapping, type SourceLocation, SourceLocationSchema, type SuppressableSeverity, type SuppressibleFinding, type SuppressionFile, SuppressionFileSchema, type SuppressionRule, SuppressionRuleSchema, TypeShape, applySuppressionsToFindings, bodyShapesMatch, boundaryKey, boundaryLabel, boundaryNameString, busIdentityKey, busesAgree, canPair, channelsPair, codeScopePath, countsForThreshold, dispatchByType, displayLabel, exchangesHttpResponses, fileInCodeScope, fixedTextLength, fnIdentityKey, formatChannel, functionCallBinding, gqlIdentityKey, graphqlOperationBinding, graphqlResolverBinding, hasNameHole, isGraphqlOperationBinding, messageBusBinding, metricBinding, metricIdentityKey, namePatternFromSub, namePatternKey, namesAgree, namesNothing, normalizeRuleBoundary, packageExportBinding, pairingKey, parseBoundaryName, parseChannel, patternHole, referenceFromName, referenceName, referenceOf, reportsUnpairedItself, restBinding, ruleBoundaryMatchesKey, runtimeConfigBinding, semanticsAgree, semconvAttributes, servesRequest, storageBinding, validateRule };
|
package/dist/index.d.ts
CHANGED
|
@@ -243,6 +243,115 @@ declare function reportsUnpairedItself(binding: BoundaryBinding): boolean;
|
|
|
243
243
|
*/
|
|
244
244
|
declare function servesRequest(binding: BoundaryBinding, method: string, path: string): MatchResult | null;
|
|
245
245
|
|
|
246
|
+
/**
|
|
247
|
+
* The boundary-name syntax, parsed and printed in one place.
|
|
248
|
+
*
|
|
249
|
+
* A name in a summary means one of three things, told apart by the
|
|
250
|
+
* braces alone: a literal (`orders-v1`), a pattern with deploy-time
|
|
251
|
+
* holes (`{stage}-orders-v1`), or a reference saying where to go and
|
|
252
|
+
* ask (`{location.bucket}`, `{ORDER_TABLE}`). A second parser can
|
|
253
|
+
* disagree about which is which, and a second printer can spell a
|
|
254
|
+
* value this one cannot read back, so everything that touches the
|
|
255
|
+
* braces calls this module: `parseBoundaryName` and
|
|
256
|
+
* `boundaryNameString` are the two directions, and the helpers below
|
|
257
|
+
* are views over them. The package README tells the longer story,
|
|
258
|
+
* including why REST route paths and message-bus channels stay apart.
|
|
259
|
+
*/
|
|
260
|
+
/** One piece of a pattern: text the writer stated, or a hole. */
|
|
261
|
+
type NamePart = {
|
|
262
|
+
type: "text";
|
|
263
|
+
text: string;
|
|
264
|
+
} | {
|
|
265
|
+
type: "hole";
|
|
266
|
+
label: string;
|
|
267
|
+
};
|
|
268
|
+
/**
|
|
269
|
+
* What a name string means. A `reference` keeps its raw dot-separated
|
|
270
|
+
* path, and `referenceOf` is the view that checks the parts are all
|
|
271
|
+
* present, so a malformed spelling still classifies as a reference and
|
|
272
|
+
* still pairs with nothing.
|
|
273
|
+
*/
|
|
274
|
+
type BoundaryName = {
|
|
275
|
+
type: "literal";
|
|
276
|
+
value: string;
|
|
277
|
+
} | {
|
|
278
|
+
type: "pattern";
|
|
279
|
+
parts: NamePart[];
|
|
280
|
+
} | {
|
|
281
|
+
type: "reference";
|
|
282
|
+
path: string[];
|
|
283
|
+
};
|
|
284
|
+
/** What a name string means. The exact inverse of `boundaryNameString`. */
|
|
285
|
+
declare function parseBoundaryName(name: string): BoundaryName;
|
|
286
|
+
/** The one serializer. Everything a summary spells goes through here. */
|
|
287
|
+
declare function boundaryNameString(name: BoundaryName): string;
|
|
288
|
+
/**
|
|
289
|
+
* How a hole is spelled. A reader that assembles a pattern a part at a
|
|
290
|
+
* time, the way the adapter's name reader does, mints each hole here
|
|
291
|
+
* so the spelling cannot drift from the parse above.
|
|
292
|
+
*/
|
|
293
|
+
declare function patternHole(label: string): string;
|
|
294
|
+
/**
|
|
295
|
+
* The name a CloudFormation `Fn::Sub` value states. The array form
|
|
296
|
+
* takes its template from the first element, and the variable map only
|
|
297
|
+
* says where a substitution comes from, which does not change the name.
|
|
298
|
+
*/
|
|
299
|
+
declare function namePatternFromSub(value: unknown): string | null;
|
|
300
|
+
/**
|
|
301
|
+
* Whether a name says only that somebody else knows it. A wrapper that
|
|
302
|
+
* takes its bucket as an argument states `{location.bucket}` and
|
|
303
|
+
* nothing more, which says which parameter to ask rather than which
|
|
304
|
+
* bucket. A name like that agrees with nothing until something grounds
|
|
305
|
+
* it, since otherwise it would agree with every name there is.
|
|
306
|
+
*/
|
|
307
|
+
declare function namesNothing(name: string): boolean;
|
|
308
|
+
/** Whether a name has anything a source left for deploy time to fill. */
|
|
309
|
+
declare function hasNameHole(name: string): boolean;
|
|
310
|
+
/**
|
|
311
|
+
* The form two patterns are compared in: every hole reduced to the same
|
|
312
|
+
* token, since the two sides pick their own name for the parameter.
|
|
313
|
+
*/
|
|
314
|
+
declare function namePatternKey(name: string): string;
|
|
315
|
+
/** How much of a name the writer stated rather than left for deploy time. */
|
|
316
|
+
declare function fixedTextLength(name: string): number;
|
|
317
|
+
/**
|
|
318
|
+
* Whether two names are the same name. Two patterns agree when their
|
|
319
|
+
* fixed parts line up, since a hole on one side meets a hole on the
|
|
320
|
+
* other. A pattern and a concrete name agree when the fixed parts are
|
|
321
|
+
* where the pattern says they are, which is what happens when one side
|
|
322
|
+
* hardcoded what the other parameterized. A reference agrees with
|
|
323
|
+
* nothing on either side.
|
|
324
|
+
*/
|
|
325
|
+
declare function namesAgree(a: string, b: string): boolean;
|
|
326
|
+
/** Where a reference says to go and ask. */
|
|
327
|
+
interface Reference {
|
|
328
|
+
/**
|
|
329
|
+
* The value the code starts from: a parameter of the unit the
|
|
330
|
+
* reference was written in, or a variable the deployment sets. Which
|
|
331
|
+
* of those it is depends on the unit's inputs, so the reader that
|
|
332
|
+
* grounds a reference decides, not the string.
|
|
333
|
+
*/
|
|
334
|
+
root: string;
|
|
335
|
+
/** The fields to read inside it, outermost first. */
|
|
336
|
+
fields: string[];
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* How a reference is written. Null when a part of it is empty, since a
|
|
340
|
+
* reference has to say what to ask about.
|
|
341
|
+
*/
|
|
342
|
+
declare function referenceName(reference: Reference): string | null;
|
|
343
|
+
/**
|
|
344
|
+
* The place a parsed reference says to ask, or null for a name that is
|
|
345
|
+
* not a reference, or for a reference with a part missing, which says
|
|
346
|
+
* nothing anybody could answer.
|
|
347
|
+
*/
|
|
348
|
+
declare function referenceOf(name: BoundaryName): Reference | null;
|
|
349
|
+
/**
|
|
350
|
+
* The reference a name states, or null when the name states a name
|
|
351
|
+
* rather than where to go and ask.
|
|
352
|
+
*/
|
|
353
|
+
declare function referenceFromName(name: string): Reference | null;
|
|
354
|
+
|
|
246
355
|
/**
|
|
247
356
|
* Deciding when two message-bus channel strings mean the same thing.
|
|
248
357
|
*
|
|
@@ -325,53 +434,6 @@ declare function dispatchByType<T extends {
|
|
|
325
434
|
type: string;
|
|
326
435
|
}, R>(table: DispatchTable<T, R>, value: T): R;
|
|
327
436
|
|
|
328
|
-
/**
|
|
329
|
-
* A name two sides state with a hole in it.
|
|
330
|
-
*
|
|
331
|
-
* A deployed resource is often called something built at deploy time:
|
|
332
|
-
* a template writes `!Sub "${StageName}-orders-v1"` and the code writes
|
|
333
|
-
* `` `${stage}-orders-v1` ``. Neither side states a string, both agree
|
|
334
|
-
* about the fixed text, and each spells the parameter its own way. So a
|
|
335
|
-
* name is written here as fixed text with `{}` holes, and two names
|
|
336
|
-
* agree when their fixed parts line up.
|
|
337
|
-
*
|
|
338
|
-
* A hole covers a whole parameter rather than one piece of a name,
|
|
339
|
-
* because a name has no separator every project agrees on. Two
|
|
340
|
-
* patterns that both cover one name are told apart by how much fixed
|
|
341
|
-
* text each states, rather than by narrowing what a hole can cover.
|
|
342
|
-
*/
|
|
343
|
-
/**
|
|
344
|
-
* The name a CloudFormation `Fn::Sub` value states. The array form
|
|
345
|
-
* takes its template from the first element, and the variable map only
|
|
346
|
-
* says where a substitution comes from, which does not change the name.
|
|
347
|
-
*/
|
|
348
|
-
declare function namePatternFromSub(value: unknown): string | null;
|
|
349
|
-
/**
|
|
350
|
-
* Whether a name says only that somebody else knows it. A wrapper that
|
|
351
|
-
* takes its bucket as an argument states `{location.bucket}` and
|
|
352
|
-
* nothing more, which says which parameter to ask rather than which
|
|
353
|
-
* bucket. A name like that agrees with nothing until something grounds
|
|
354
|
-
* it, since otherwise it would agree with every name there is.
|
|
355
|
-
*/
|
|
356
|
-
declare function namesNothing(name: string): boolean;
|
|
357
|
-
/** Whether a name has anything a source left for deploy time to fill. */
|
|
358
|
-
declare function hasNameHole(name: string): boolean;
|
|
359
|
-
/**
|
|
360
|
-
* The form two patterns are compared in: every hole reduced to the same
|
|
361
|
-
* token, since the two sides pick their own name for the parameter.
|
|
362
|
-
*/
|
|
363
|
-
declare function namePatternKey(name: string): string;
|
|
364
|
-
/** How much of a name the writer stated rather than left for deploy time. */
|
|
365
|
-
declare function fixedTextLength(name: string): number;
|
|
366
|
-
/**
|
|
367
|
-
* Whether two names are the same name. Two patterns agree when their
|
|
368
|
-
* fixed parts line up, since a hole on one side meets a hole on the
|
|
369
|
-
* other. A pattern and a concrete name agree when the fixed parts are
|
|
370
|
-
* where the pattern says they are, which is what happens when one side
|
|
371
|
-
* hardcoded what the other parameterized.
|
|
372
|
-
*/
|
|
373
|
-
declare function namesAgree(a: string, b: string): boolean;
|
|
374
|
-
|
|
375
437
|
/**
|
|
376
438
|
* The suppression pipeline, over the smallest finding both checkers share.
|
|
377
439
|
*
|
|
@@ -622,4 +684,4 @@ declare function metricBinding(opts: {
|
|
|
622
684
|
metricType: string | null;
|
|
623
685
|
}): BoundaryBinding;
|
|
624
686
|
|
|
625
|
-
export { type BoundaryBehavior, type BoundaryBinding, BoundaryBindingSchema, type BoundarySemanticsDefinition, type BusIdentityKey, type Channel, type ConfidenceInfo, ConfidenceInfoSchema, type ConfidenceLevel, ConfidenceLevelSchema, type ConfidenceSource, ConfidenceSourceSchema, type Corroboration, CorroborationSchema, DeployableUnit, type DispatchTable, type FindingSuppression, type FnIdentityKey, type GqlIdentityKey, type MatchResult, MessageBusSemantics, MessageBusTechnology, type MetricIdentityKey, type MetricSemantics, MetricSemanticsSchema, type ParsedChannel, Semantics, type SemconvAttribute, type SemconvMapping, type SourceLocation, SourceLocationSchema, type SuppressableSeverity, type SuppressibleFinding, type SuppressionFile, SuppressionFileSchema, type SuppressionRule, SuppressionRuleSchema, TypeShape, applySuppressionsToFindings, bodyShapesMatch, boundaryKey, boundaryLabel, busIdentityKey, busesAgree, canPair, channelsPair, codeScopePath, countsForThreshold, dispatchByType, displayLabel, exchangesHttpResponses, fileInCodeScope, fixedTextLength, fnIdentityKey, formatChannel, functionCallBinding, gqlIdentityKey, graphqlOperationBinding, graphqlResolverBinding, hasNameHole, isGraphqlOperationBinding, messageBusBinding, metricBinding, metricIdentityKey, namePatternFromSub, namePatternKey, namesAgree, namesNothing, normalizeRuleBoundary, packageExportBinding, pairingKey, parseChannel, reportsUnpairedItself, restBinding, ruleBoundaryMatchesKey, runtimeConfigBinding, semanticsAgree, semconvAttributes, servesRequest, storageBinding, validateRule };
|
|
687
|
+
export { type BoundaryBehavior, type BoundaryBinding, BoundaryBindingSchema, type BoundaryName, type BoundarySemanticsDefinition, type BusIdentityKey, type Channel, type ConfidenceInfo, ConfidenceInfoSchema, type ConfidenceLevel, ConfidenceLevelSchema, type ConfidenceSource, ConfidenceSourceSchema, type Corroboration, CorroborationSchema, DeployableUnit, type DispatchTable, type FindingSuppression, type FnIdentityKey, type GqlIdentityKey, type MatchResult, MessageBusSemantics, MessageBusTechnology, type MetricIdentityKey, type MetricSemantics, MetricSemanticsSchema, type NamePart, type ParsedChannel, type Reference, Semantics, type SemconvAttribute, type SemconvMapping, type SourceLocation, SourceLocationSchema, type SuppressableSeverity, type SuppressibleFinding, type SuppressionFile, SuppressionFileSchema, type SuppressionRule, SuppressionRuleSchema, TypeShape, applySuppressionsToFindings, bodyShapesMatch, boundaryKey, boundaryLabel, boundaryNameString, busIdentityKey, busesAgree, canPair, channelsPair, codeScopePath, countsForThreshold, dispatchByType, displayLabel, exchangesHttpResponses, fileInCodeScope, fixedTextLength, fnIdentityKey, formatChannel, functionCallBinding, gqlIdentityKey, graphqlOperationBinding, graphqlResolverBinding, hasNameHole, isGraphqlOperationBinding, messageBusBinding, metricBinding, metricIdentityKey, namePatternFromSub, namePatternKey, namesAgree, namesNothing, normalizeRuleBoundary, packageExportBinding, pairingKey, parseBoundaryName, parseChannel, patternHole, referenceFromName, referenceName, referenceOf, reportsUnpairedItself, restBinding, ruleBoundaryMatchesKey, runtimeConfigBinding, semanticsAgree, semconvAttributes, servesRequest, storageBinding, validateRule };
|
package/dist/index.js
CHANGED
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
semconvMappingOf,
|
|
34
34
|
typeDefinitionKey,
|
|
35
35
|
withDefinitionsInlined
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-HBAMPBAO.js";
|
|
37
37
|
|
|
38
38
|
// src/semconv.ts
|
|
39
39
|
function semconvAttributes(binding) {
|
|
@@ -106,29 +106,47 @@ function servesRequest(binding, method, path) {
|
|
|
106
106
|
return serves(binding.semantics, method, path);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
// src/codeScope.ts
|
|
110
|
-
function codeScopePath(raw) {
|
|
111
|
-
return raw.trim().replace(/^\.\//, "").replace(/\/+$/, "");
|
|
112
|
-
}
|
|
113
|
-
function fileInCodeScope(file, scope) {
|
|
114
|
-
const prefix = codeScopePath(scope);
|
|
115
|
-
if (prefix === "" || prefix === ".") {
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
|
-
const path = codeScopePath(file);
|
|
119
|
-
return path === prefix || path.startsWith(`${prefix}/`);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
109
|
// src/dispatch.ts
|
|
123
110
|
function dispatchByType(table, value) {
|
|
124
111
|
const handler = table[value.type];
|
|
125
112
|
return handler(value);
|
|
126
113
|
}
|
|
127
114
|
|
|
128
|
-
// src/
|
|
129
|
-
var
|
|
115
|
+
// src/boundaryName.ts
|
|
116
|
+
var HOLE_SPLIT = /(\{[^}]*\})/;
|
|
130
117
|
var SUB_TOKEN = /\$\{([^}]*)\}/g;
|
|
131
118
|
var SUB_ESCAPE = /\$\{!/;
|
|
119
|
+
function isHole(piece) {
|
|
120
|
+
return piece.startsWith("{") && piece.endsWith("}");
|
|
121
|
+
}
|
|
122
|
+
function parseBoundaryName(name) {
|
|
123
|
+
const parts = name.split(HOLE_SPLIT).filter((piece) => piece !== "").map(
|
|
124
|
+
(piece) => isHole(piece) ? { type: "hole", label: piece.slice(1, -1) } : { type: "text", text: piece }
|
|
125
|
+
);
|
|
126
|
+
const only = parts.length === 1 ? parts[0] : void 0;
|
|
127
|
+
if (only !== void 0 && only.type === "hole") {
|
|
128
|
+
return { type: "reference", path: only.label.split(".") };
|
|
129
|
+
}
|
|
130
|
+
if (parts.some((part) => part.type === "hole")) {
|
|
131
|
+
return { type: "pattern", parts };
|
|
132
|
+
}
|
|
133
|
+
return { type: "literal", value: name };
|
|
134
|
+
}
|
|
135
|
+
var partString = {
|
|
136
|
+
text: (part) => part.text,
|
|
137
|
+
hole: (part) => patternHole(part.label)
|
|
138
|
+
};
|
|
139
|
+
var nameString = {
|
|
140
|
+
literal: (name) => name.value,
|
|
141
|
+
pattern: (name) => name.parts.map((part) => dispatchByType(partString, part)).join(""),
|
|
142
|
+
reference: (name) => patternHole(name.path.join("."))
|
|
143
|
+
};
|
|
144
|
+
function boundaryNameString(name) {
|
|
145
|
+
return dispatchByType(nameString, name);
|
|
146
|
+
}
|
|
147
|
+
function patternHole(label) {
|
|
148
|
+
return `{${label}}`;
|
|
149
|
+
}
|
|
132
150
|
function namePatternFromSub(value) {
|
|
133
151
|
if (Array.isArray(value)) {
|
|
134
152
|
return typeof value[0] === "string" ? namePatternFromSub(value[0]) : null;
|
|
@@ -139,56 +157,101 @@ function namePatternFromSub(value) {
|
|
|
139
157
|
if (SUB_ESCAPE.test(value)) {
|
|
140
158
|
return null;
|
|
141
159
|
}
|
|
142
|
-
return value.replace(
|
|
160
|
+
return value.replace(
|
|
161
|
+
SUB_TOKEN,
|
|
162
|
+
(_whole, inner) => patternHole(inner)
|
|
163
|
+
);
|
|
143
164
|
}
|
|
144
165
|
function namesNothing(name) {
|
|
145
|
-
return
|
|
166
|
+
return parseBoundaryName(name).type === "reference";
|
|
146
167
|
}
|
|
147
168
|
function hasNameHole(name) {
|
|
148
|
-
|
|
149
|
-
return HOLE.test(name);
|
|
169
|
+
return parseBoundaryName(name).type !== "literal";
|
|
150
170
|
}
|
|
151
171
|
function namePatternKey(name) {
|
|
152
|
-
return
|
|
172
|
+
return dispatchByType(
|
|
173
|
+
{
|
|
174
|
+
literal: (parsed) => parsed.value,
|
|
175
|
+
pattern: (parsed) => boundaryNameString({
|
|
176
|
+
type: "pattern",
|
|
177
|
+
parts: parsed.parts.map(
|
|
178
|
+
(part) => part.type === "hole" ? { type: "hole", label: "" } : part
|
|
179
|
+
)
|
|
180
|
+
}),
|
|
181
|
+
reference: () => patternHole("")
|
|
182
|
+
},
|
|
183
|
+
parseBoundaryName(name)
|
|
184
|
+
);
|
|
153
185
|
}
|
|
154
186
|
function fixedTextLength(name) {
|
|
155
|
-
return
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
187
|
+
return dispatchByType(
|
|
188
|
+
{
|
|
189
|
+
literal: (parsed) => parsed.value.length,
|
|
190
|
+
pattern: (parsed) => parsed.parts.reduce(
|
|
191
|
+
(total, part) => total + (part.type === "text" ? part.text.length : 0),
|
|
192
|
+
0
|
|
193
|
+
),
|
|
194
|
+
reference: () => 0
|
|
195
|
+
},
|
|
196
|
+
parseBoundaryName(name)
|
|
197
|
+
);
|
|
159
198
|
}
|
|
160
199
|
function quote(text) {
|
|
161
200
|
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
162
201
|
}
|
|
163
|
-
function holeSource(_following) {
|
|
164
|
-
return ".+";
|
|
165
|
-
}
|
|
166
202
|
function admits(pattern, name) {
|
|
167
|
-
const
|
|
168
|
-
const source = parts.map(
|
|
169
|
-
(part, index) => isHole(part) ? holeSource(parts[index + 1] ?? "") : quote(part)
|
|
170
|
-
).join("");
|
|
203
|
+
const source = pattern.parts.map((part) => part.type === "hole" ? ".+" : quote(part.text)).join("");
|
|
171
204
|
return new RegExp(`^${source}$`).test(name);
|
|
172
205
|
}
|
|
173
206
|
function namesAgree(a, b) {
|
|
174
|
-
|
|
207
|
+
const left = parseBoundaryName(a);
|
|
208
|
+
const right = parseBoundaryName(b);
|
|
209
|
+
if (left.type === "reference" || right.type === "reference") {
|
|
175
210
|
return false;
|
|
176
211
|
}
|
|
177
|
-
if (
|
|
178
|
-
return true;
|
|
179
|
-
}
|
|
180
|
-
const aHasHole = hasNameHole(a);
|
|
181
|
-
const bHasHole = hasNameHole(b);
|
|
182
|
-
if (aHasHole && bHasHole) {
|
|
212
|
+
if (left.type === "pattern" && right.type === "pattern") {
|
|
183
213
|
return namePatternKey(a) === namePatternKey(b);
|
|
184
214
|
}
|
|
185
|
-
if (
|
|
186
|
-
return admits(
|
|
215
|
+
if (left.type === "pattern") {
|
|
216
|
+
return admits(left, b);
|
|
187
217
|
}
|
|
188
|
-
if (
|
|
189
|
-
return admits(
|
|
218
|
+
if (right.type === "pattern") {
|
|
219
|
+
return admits(right, a);
|
|
190
220
|
}
|
|
191
|
-
return
|
|
221
|
+
return a === b;
|
|
222
|
+
}
|
|
223
|
+
function referenceName(reference) {
|
|
224
|
+
const path = [reference.root, ...reference.fields];
|
|
225
|
+
if (path.some((part) => part === "")) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
return boundaryNameString({ type: "reference", path });
|
|
229
|
+
}
|
|
230
|
+
function referenceOf(name) {
|
|
231
|
+
if (name.type !== "reference") {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
const root = name.path[0];
|
|
235
|
+
if (root === void 0 || name.path.some((part) => part === "")) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
return { root, fields: name.path.slice(1) };
|
|
239
|
+
}
|
|
240
|
+
function referenceFromName(name) {
|
|
241
|
+
return referenceOf(parseBoundaryName(name));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// src/codeScope.ts
|
|
245
|
+
function codeScopePath(raw) {
|
|
246
|
+
return raw.trim().replace(/^\.\//, "").replace(/\/+$/, "");
|
|
247
|
+
}
|
|
248
|
+
function fileInCodeScope(file, scope) {
|
|
249
|
+
const prefix = codeScopePath(scope);
|
|
250
|
+
if (prefix === "" || prefix === ".") {
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
const path = codeScopePath(file);
|
|
254
|
+
return path === prefix || path.startsWith(`${prefix}/`);
|
|
192
255
|
}
|
|
193
256
|
|
|
194
257
|
// src/suppressions.ts
|
|
@@ -590,6 +653,7 @@ export {
|
|
|
590
653
|
bodyShapesMatch,
|
|
591
654
|
boundaryKey,
|
|
592
655
|
boundaryLabel,
|
|
656
|
+
boundaryNameString,
|
|
593
657
|
busIdentityKey,
|
|
594
658
|
busesAgree,
|
|
595
659
|
canPair,
|
|
@@ -622,7 +686,12 @@ export {
|
|
|
622
686
|
normalizeRuleBoundary,
|
|
623
687
|
packageExportBinding,
|
|
624
688
|
pairingKey,
|
|
689
|
+
parseBoundaryName,
|
|
625
690
|
parseChannel,
|
|
691
|
+
patternHole,
|
|
692
|
+
referenceFromName,
|
|
693
|
+
referenceName,
|
|
694
|
+
referenceOf,
|
|
626
695
|
reportsUnpairedItself,
|
|
627
696
|
restBinding,
|
|
628
697
|
routePathAdmits,
|
package/dist/schemas.cjs
CHANGED
|
@@ -535,7 +535,9 @@ var StorageSemanticsSchema = import_zod9.z.object({
|
|
|
535
535
|
* The table, bucket, collection, or index, as the source declares
|
|
536
536
|
* it, or null when the source states one this reader could not
|
|
537
537
|
* settle. A null container pairs with nothing, rather than with
|
|
538
|
-
* whatever its source text happens to spell.
|
|
538
|
+
* whatever its source text happens to spell. The string is in the
|
|
539
|
+
* boundary-name syntax: a literal, a pattern with `{}` holes, or a
|
|
540
|
+
* reference, and `parseBoundaryName` is the one reader of it.
|
|
539
541
|
*/
|
|
540
542
|
container: import_zod9.z.string().nullable(),
|
|
541
543
|
/**
|
package/dist/schemas.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/ir-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Shared IR primitives for suss — type shapes, boundary bindings, source locations, and confidence. Imported by both @suss/behavioral-ir and @suss/intent-ir so neither depends on the other.",
|
|
6
6
|
"keywords": [
|