@suss/ir-core 0.13.3 → 0.14.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/{chunk-AQYS5AC6.js → chunk-HSJYPBJU.js} +52 -0
- package/dist/index.cjs +63 -0
- package/dist/index.d.cts +53 -1
- package/dist/index.d.ts +53 -1
- package/dist/index.js +13 -1
- package/dist/schemas.cjs +47 -0
- package/dist/schemas.js +1 -1
- package/package.json +1 -1
|
@@ -309,6 +309,20 @@ var metricSemantics = defineBoundarySemantics({
|
|
|
309
309
|
|
|
310
310
|
// src/semantics/rest.ts
|
|
311
311
|
import { z as z6 } from "zod";
|
|
312
|
+
|
|
313
|
+
// src/urlPath.ts
|
|
314
|
+
var SCHEME_ORIGIN = /^[a-zA-Z][a-zA-Z\d+.-]*:\/\/[^/]*/;
|
|
315
|
+
var PROTOCOL_RELATIVE_ORIGIN = /^\/\/[^/]*/;
|
|
316
|
+
function pathAfterOrigin(text) {
|
|
317
|
+
const withoutOrigin = text.replace(SCHEME_ORIGIN, "").replace(PROTOCOL_RELATIVE_ORIGIN, "");
|
|
318
|
+
const cut = withoutOrigin.search(/[?#]/);
|
|
319
|
+
return cut === -1 ? withoutOrigin : withoutOrigin.slice(0, cut);
|
|
320
|
+
}
|
|
321
|
+
function statesAnOrigin(text) {
|
|
322
|
+
return SCHEME_ORIGIN.test(text) || PROTOCOL_RELATIVE_ORIGIN.test(text);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// src/semantics/rest.ts
|
|
312
326
|
var RestSemanticsSchema = z6.object({
|
|
313
327
|
name: z6.literal("rest"),
|
|
314
328
|
/**
|
|
@@ -327,6 +341,11 @@ var RestSemanticsSchema = z6.object({
|
|
|
327
341
|
*/
|
|
328
342
|
declaredResponses: z6.array(z6.number()).optional()
|
|
329
343
|
});
|
|
344
|
+
var OPENING_HOLE = /^\{([^{}]+)\}(.*)$/;
|
|
345
|
+
function variableOf(label) {
|
|
346
|
+
const parts = label.split(".");
|
|
347
|
+
return parts[parts.length - 1] ?? label;
|
|
348
|
+
}
|
|
330
349
|
function normalizePath(path) {
|
|
331
350
|
let normalized = path.replace(/:([a-zA-Z_]\w*)/g, "{$1}");
|
|
332
351
|
if (normalized.length > 1 && normalized.endsWith("/")) {
|
|
@@ -436,6 +455,37 @@ var restSemantics = defineBoundarySemantics({
|
|
|
436
455
|
}
|
|
437
456
|
return routePathAdmits(semantics.path, path) ? "match" : "nomatch";
|
|
438
457
|
},
|
|
458
|
+
/**
|
|
459
|
+
* A call whose base URL the deployment fills in, resolved to the
|
|
460
|
+
* path it reaches.
|
|
461
|
+
*
|
|
462
|
+
* The source cannot settle this on its own, which is why the
|
|
463
|
+
* adapter leaves the hole in. `API_BASE` could be
|
|
464
|
+
* `http://backend.internal`, and then the path is `/orders`. It
|
|
465
|
+
* could equally be `/api/v2`, and then the path is
|
|
466
|
+
* `/api/v2/orders`. Putting the deployed value in and reading the
|
|
467
|
+
* path back out gets the right one either way.
|
|
468
|
+
*
|
|
469
|
+
* Only a hole at the front is a base URL. A hole further along is a
|
|
470
|
+
* route parameter, and `/orders/{id}` means every id rather than a
|
|
471
|
+
* variable somebody set.
|
|
472
|
+
*/
|
|
473
|
+
groundName(semantics, deployedAs) {
|
|
474
|
+
if (semantics.path === null) {
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
const opening = OPENING_HOLE.exec(semantics.path);
|
|
478
|
+
if (opening === null) {
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
const [, label, rest] = opening;
|
|
482
|
+
const base = deployedAs(variableOf(label));
|
|
483
|
+
if (base === null) {
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
const grounded = pathAfterOrigin(`${base}${rest}`);
|
|
487
|
+
return { ...semantics, path: grounded === "" ? "/" : grounded };
|
|
488
|
+
},
|
|
439
489
|
ruleBoundary: {
|
|
440
490
|
// "METHOD /path": one leading token, then an absolute path.
|
|
441
491
|
claims(raw) {
|
|
@@ -718,6 +768,8 @@ export {
|
|
|
718
768
|
channelsPair,
|
|
719
769
|
MessageBusSemanticsSchema,
|
|
720
770
|
MetricSemanticsSchema,
|
|
771
|
+
pathAfterOrigin,
|
|
772
|
+
statesAnOrigin,
|
|
721
773
|
RestSemanticsSchema,
|
|
722
774
|
normalizePath,
|
|
723
775
|
pathShape,
|
package/dist/index.cjs
CHANGED
|
@@ -62,6 +62,7 @@ __export(index_exports, {
|
|
|
62
62
|
gqlIdentityKey: () => gqlIdentityKey,
|
|
63
63
|
graphqlOperationBinding: () => graphqlOperationBinding,
|
|
64
64
|
graphqlResolverBinding: () => graphqlResolverBinding,
|
|
65
|
+
groundedPairingKey: () => groundedPairingKey,
|
|
65
66
|
hasNameHole: () => hasNameHole,
|
|
66
67
|
isGraphqlOperationBinding: () => isGraphqlOperationBinding,
|
|
67
68
|
messageBusBinding: () => messageBusBinding,
|
|
@@ -78,6 +79,7 @@ __export(index_exports, {
|
|
|
78
79
|
pairingKey: () => pairingKey,
|
|
79
80
|
parseBoundaryName: () => parseBoundaryName,
|
|
80
81
|
parseChannel: () => parseChannel,
|
|
82
|
+
pathAfterOrigin: () => pathAfterOrigin,
|
|
81
83
|
pathShape: () => pathShape,
|
|
82
84
|
patternHole: () => patternHole,
|
|
83
85
|
referenceFromName: () => referenceFromName,
|
|
@@ -91,6 +93,7 @@ __export(index_exports, {
|
|
|
91
93
|
semanticsAgree: () => semanticsAgree,
|
|
92
94
|
semconvAttributes: () => semconvAttributes,
|
|
93
95
|
servesRequest: () => servesRequest,
|
|
96
|
+
statesAnOrigin: () => statesAnOrigin,
|
|
94
97
|
storageBinding: () => storageBinding,
|
|
95
98
|
typeDefinitionKey: () => typeDefinitionKey,
|
|
96
99
|
validateRule: () => validateRule,
|
|
@@ -409,6 +412,20 @@ var metricSemantics = defineBoundarySemantics({
|
|
|
409
412
|
|
|
410
413
|
// src/semantics/rest.ts
|
|
411
414
|
var import_zod6 = require("zod");
|
|
415
|
+
|
|
416
|
+
// src/urlPath.ts
|
|
417
|
+
var SCHEME_ORIGIN = /^[a-zA-Z][a-zA-Z\d+.-]*:\/\/[^/]*/;
|
|
418
|
+
var PROTOCOL_RELATIVE_ORIGIN = /^\/\/[^/]*/;
|
|
419
|
+
function pathAfterOrigin(text) {
|
|
420
|
+
const withoutOrigin = text.replace(SCHEME_ORIGIN, "").replace(PROTOCOL_RELATIVE_ORIGIN, "");
|
|
421
|
+
const cut = withoutOrigin.search(/[?#]/);
|
|
422
|
+
return cut === -1 ? withoutOrigin : withoutOrigin.slice(0, cut);
|
|
423
|
+
}
|
|
424
|
+
function statesAnOrigin(text) {
|
|
425
|
+
return SCHEME_ORIGIN.test(text) || PROTOCOL_RELATIVE_ORIGIN.test(text);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// src/semantics/rest.ts
|
|
412
429
|
var RestSemanticsSchema = import_zod6.z.object({
|
|
413
430
|
name: import_zod6.z.literal("rest"),
|
|
414
431
|
/**
|
|
@@ -427,6 +444,11 @@ var RestSemanticsSchema = import_zod6.z.object({
|
|
|
427
444
|
*/
|
|
428
445
|
declaredResponses: import_zod6.z.array(import_zod6.z.number()).optional()
|
|
429
446
|
});
|
|
447
|
+
var OPENING_HOLE = /^\{([^{}]+)\}(.*)$/;
|
|
448
|
+
function variableOf(label) {
|
|
449
|
+
const parts = label.split(".");
|
|
450
|
+
return parts[parts.length - 1] ?? label;
|
|
451
|
+
}
|
|
430
452
|
function normalizePath(path) {
|
|
431
453
|
let normalized = path.replace(/:([a-zA-Z_]\w*)/g, "{$1}");
|
|
432
454
|
if (normalized.length > 1 && normalized.endsWith("/")) {
|
|
@@ -536,6 +558,37 @@ var restSemantics = defineBoundarySemantics({
|
|
|
536
558
|
}
|
|
537
559
|
return routePathAdmits(semantics.path, path) ? "match" : "nomatch";
|
|
538
560
|
},
|
|
561
|
+
/**
|
|
562
|
+
* A call whose base URL the deployment fills in, resolved to the
|
|
563
|
+
* path it reaches.
|
|
564
|
+
*
|
|
565
|
+
* The source cannot settle this on its own, which is why the
|
|
566
|
+
* adapter leaves the hole in. `API_BASE` could be
|
|
567
|
+
* `http://backend.internal`, and then the path is `/orders`. It
|
|
568
|
+
* could equally be `/api/v2`, and then the path is
|
|
569
|
+
* `/api/v2/orders`. Putting the deployed value in and reading the
|
|
570
|
+
* path back out gets the right one either way.
|
|
571
|
+
*
|
|
572
|
+
* Only a hole at the front is a base URL. A hole further along is a
|
|
573
|
+
* route parameter, and `/orders/{id}` means every id rather than a
|
|
574
|
+
* variable somebody set.
|
|
575
|
+
*/
|
|
576
|
+
groundName(semantics, deployedAs) {
|
|
577
|
+
if (semantics.path === null) {
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
const opening = OPENING_HOLE.exec(semantics.path);
|
|
581
|
+
if (opening === null) {
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
const [, label, rest] = opening;
|
|
585
|
+
const base = deployedAs(variableOf(label));
|
|
586
|
+
if (base === null) {
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
589
|
+
const grounded = pathAfterOrigin(`${base}${rest}`);
|
|
590
|
+
return { ...semantics, path: grounded === "" ? "/" : grounded };
|
|
591
|
+
},
|
|
539
592
|
ruleBoundary: {
|
|
540
593
|
// "METHOD /path": one leading token, then an absolute path.
|
|
541
594
|
claims(raw) {
|
|
@@ -827,6 +880,13 @@ function semconvAttributes(binding) {
|
|
|
827
880
|
function boundaryKey(binding) {
|
|
828
881
|
return behaviorOf(binding.semantics).identityKey(binding.semantics);
|
|
829
882
|
}
|
|
883
|
+
function groundedPairingKey(binding, deployedAs) {
|
|
884
|
+
const behavior = behaviorOf(binding.semantics);
|
|
885
|
+
const grounded = behavior.groundName?.(binding.semantics, deployedAs) ?? null;
|
|
886
|
+
return pairingKey(
|
|
887
|
+
grounded === null ? binding : { ...binding, semantics: grounded }
|
|
888
|
+
);
|
|
889
|
+
}
|
|
830
890
|
function pairingKey(binding) {
|
|
831
891
|
const behavior = behaviorOf(binding.semantics);
|
|
832
892
|
return (behavior.pairingKey ?? behavior.identityKey)(binding.semantics);
|
|
@@ -1442,6 +1502,7 @@ function metricBinding(opts) {
|
|
|
1442
1502
|
gqlIdentityKey,
|
|
1443
1503
|
graphqlOperationBinding,
|
|
1444
1504
|
graphqlResolverBinding,
|
|
1505
|
+
groundedPairingKey,
|
|
1445
1506
|
hasNameHole,
|
|
1446
1507
|
isGraphqlOperationBinding,
|
|
1447
1508
|
messageBusBinding,
|
|
@@ -1458,6 +1519,7 @@ function metricBinding(opts) {
|
|
|
1458
1519
|
pairingKey,
|
|
1459
1520
|
parseBoundaryName,
|
|
1460
1521
|
parseChannel,
|
|
1522
|
+
pathAfterOrigin,
|
|
1461
1523
|
pathShape,
|
|
1462
1524
|
patternHole,
|
|
1463
1525
|
referenceFromName,
|
|
@@ -1471,6 +1533,7 @@ function metricBinding(opts) {
|
|
|
1471
1533
|
semanticsAgree,
|
|
1472
1534
|
semconvAttributes,
|
|
1473
1535
|
servesRequest,
|
|
1536
|
+
statesAnOrigin,
|
|
1474
1537
|
storageBinding,
|
|
1475
1538
|
typeDefinitionKey,
|
|
1476
1539
|
validateRule,
|
package/dist/index.d.cts
CHANGED
|
@@ -117,6 +117,24 @@ interface BoundaryBehavior<S extends {
|
|
|
117
117
|
* does not apply, and unknown means this declaration cannot settle it.
|
|
118
118
|
*/
|
|
119
119
|
servesRequest?(semantics: S, method: string, path: string): MatchResult;
|
|
120
|
+
/**
|
|
121
|
+
* The same boundary with a name the deployment fills in put in.
|
|
122
|
+
*
|
|
123
|
+
* A call written as `fetch(`${process.env.API_BASE}/orders`)` gets
|
|
124
|
+
* part of its boundary from the source and part from whatever runs
|
|
125
|
+
* the code. Both parts have to be in before anybody can see that the
|
|
126
|
+
* two sides describe one boundary.
|
|
127
|
+
*
|
|
128
|
+
* `deployedAs` is already scoped to the unit this boundary belongs to.
|
|
129
|
+
* A protocol asks for a variable and gets back what this deployment
|
|
130
|
+
* sets it to, or null. Return null to leave the boundary as it is,
|
|
131
|
+
* which is right whenever nothing needs filling in or nothing can
|
|
132
|
+
* fill it.
|
|
133
|
+
*
|
|
134
|
+
* A protocol whose names are settled in the source leaves this
|
|
135
|
+
* undefined.
|
|
136
|
+
*/
|
|
137
|
+
groundName?(semantics: S, deployedAs: (variable: string) => string | null): S | null;
|
|
120
138
|
/**
|
|
121
139
|
* Whether a provider produces a status and a body that a consumer
|
|
122
140
|
* reads back. Every protocol has to state this, so that one added
|
|
@@ -219,6 +237,16 @@ declare function semconvAttributes(binding: BoundaryBinding): Record<string, str
|
|
|
219
237
|
*/
|
|
220
238
|
|
|
221
239
|
declare function boundaryKey(binding: BoundaryBinding): string | null;
|
|
240
|
+
/**
|
|
241
|
+
* The bucket a boundary pairs in, with any name the deployment fills in
|
|
242
|
+
* put in first.
|
|
243
|
+
*
|
|
244
|
+
* A consumer that gets its base URL from the runtime states a different
|
|
245
|
+
* string from the provider it reaches, and the two are one boundary.
|
|
246
|
+
* Every protocol that has such a name says how to fill it in, and one
|
|
247
|
+
* whose names are settled in the source keys exactly as before.
|
|
248
|
+
*/
|
|
249
|
+
declare function groundedPairingKey(binding: BoundaryBinding, deployedAs: (variable: string) => string | null): string | null;
|
|
222
250
|
declare function pairingKey(binding: BoundaryBinding): string | null;
|
|
223
251
|
declare function semanticsAgree(a: Semantics, b: Semantics): boolean;
|
|
224
252
|
declare function boundaryLabel(binding: BoundaryBinding): string | null;
|
|
@@ -554,6 +582,30 @@ declare function applySuppressionsToFindings<T extends SuppressibleFinding>(find
|
|
|
554
582
|
*/
|
|
555
583
|
declare function countsForThreshold(finding: SuppressibleFinding): boolean;
|
|
556
584
|
|
|
585
|
+
/**
|
|
586
|
+
* urlPath.ts: the path part of a URL, however the URL was written.
|
|
587
|
+
*
|
|
588
|
+
* Two sides of one boundary spell it differently. A provider says
|
|
589
|
+
* `/orders`, because a route is mounted at a path. A consumer says
|
|
590
|
+
* `http://backend.internal/orders`, because a call has to say which
|
|
591
|
+
* host. They are the same boundary, and pairing them means reading the
|
|
592
|
+
* path out of both.
|
|
593
|
+
*
|
|
594
|
+
* The query and the fragment come off with the origin. Neither picks a
|
|
595
|
+
* route: `/orders?page=2` reaches the same handler as `/orders`.
|
|
596
|
+
*/
|
|
597
|
+
/**
|
|
598
|
+
* The path a URL states, with any origin, query, and fragment removed.
|
|
599
|
+
*
|
|
600
|
+
* Text that states no origin comes back with only the query and the
|
|
601
|
+
* fragment removed, since a relative URL is already a path. A URL that
|
|
602
|
+
* is nothing but an origin comes back as an empty string, which is what
|
|
603
|
+
* a caller wanting a path should treat as "the root".
|
|
604
|
+
*/
|
|
605
|
+
declare function pathAfterOrigin(text: string): string;
|
|
606
|
+
/** Whether the text opens with an origin rather than with a path. */
|
|
607
|
+
declare function statesAnOrigin(text: string): boolean;
|
|
608
|
+
|
|
557
609
|
/**
|
|
558
610
|
* @suss/ir-core: primitives shared across suss IRs.
|
|
559
611
|
*
|
|
@@ -684,4 +736,4 @@ declare function metricBinding(opts: {
|
|
|
684
736
|
metricType: string | null;
|
|
685
737
|
}): BoundaryBinding;
|
|
686
738
|
|
|
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 };
|
|
739
|
+
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, groundedPairingKey, hasNameHole, isGraphqlOperationBinding, messageBusBinding, metricBinding, metricIdentityKey, namePatternFromSub, namePatternKey, namesAgree, namesNothing, normalizeRuleBoundary, packageExportBinding, pairingKey, parseBoundaryName, parseChannel, pathAfterOrigin, patternHole, referenceFromName, referenceName, referenceOf, reportsUnpairedItself, restBinding, ruleBoundaryMatchesKey, runtimeConfigBinding, semanticsAgree, semconvAttributes, servesRequest, statesAnOrigin, storageBinding, validateRule };
|
package/dist/index.d.ts
CHANGED
|
@@ -117,6 +117,24 @@ interface BoundaryBehavior<S extends {
|
|
|
117
117
|
* does not apply, and unknown means this declaration cannot settle it.
|
|
118
118
|
*/
|
|
119
119
|
servesRequest?(semantics: S, method: string, path: string): MatchResult;
|
|
120
|
+
/**
|
|
121
|
+
* The same boundary with a name the deployment fills in put in.
|
|
122
|
+
*
|
|
123
|
+
* A call written as `fetch(`${process.env.API_BASE}/orders`)` gets
|
|
124
|
+
* part of its boundary from the source and part from whatever runs
|
|
125
|
+
* the code. Both parts have to be in before anybody can see that the
|
|
126
|
+
* two sides describe one boundary.
|
|
127
|
+
*
|
|
128
|
+
* `deployedAs` is already scoped to the unit this boundary belongs to.
|
|
129
|
+
* A protocol asks for a variable and gets back what this deployment
|
|
130
|
+
* sets it to, or null. Return null to leave the boundary as it is,
|
|
131
|
+
* which is right whenever nothing needs filling in or nothing can
|
|
132
|
+
* fill it.
|
|
133
|
+
*
|
|
134
|
+
* A protocol whose names are settled in the source leaves this
|
|
135
|
+
* undefined.
|
|
136
|
+
*/
|
|
137
|
+
groundName?(semantics: S, deployedAs: (variable: string) => string | null): S | null;
|
|
120
138
|
/**
|
|
121
139
|
* Whether a provider produces a status and a body that a consumer
|
|
122
140
|
* reads back. Every protocol has to state this, so that one added
|
|
@@ -219,6 +237,16 @@ declare function semconvAttributes(binding: BoundaryBinding): Record<string, str
|
|
|
219
237
|
*/
|
|
220
238
|
|
|
221
239
|
declare function boundaryKey(binding: BoundaryBinding): string | null;
|
|
240
|
+
/**
|
|
241
|
+
* The bucket a boundary pairs in, with any name the deployment fills in
|
|
242
|
+
* put in first.
|
|
243
|
+
*
|
|
244
|
+
* A consumer that gets its base URL from the runtime states a different
|
|
245
|
+
* string from the provider it reaches, and the two are one boundary.
|
|
246
|
+
* Every protocol that has such a name says how to fill it in, and one
|
|
247
|
+
* whose names are settled in the source keys exactly as before.
|
|
248
|
+
*/
|
|
249
|
+
declare function groundedPairingKey(binding: BoundaryBinding, deployedAs: (variable: string) => string | null): string | null;
|
|
222
250
|
declare function pairingKey(binding: BoundaryBinding): string | null;
|
|
223
251
|
declare function semanticsAgree(a: Semantics, b: Semantics): boolean;
|
|
224
252
|
declare function boundaryLabel(binding: BoundaryBinding): string | null;
|
|
@@ -554,6 +582,30 @@ declare function applySuppressionsToFindings<T extends SuppressibleFinding>(find
|
|
|
554
582
|
*/
|
|
555
583
|
declare function countsForThreshold(finding: SuppressibleFinding): boolean;
|
|
556
584
|
|
|
585
|
+
/**
|
|
586
|
+
* urlPath.ts: the path part of a URL, however the URL was written.
|
|
587
|
+
*
|
|
588
|
+
* Two sides of one boundary spell it differently. A provider says
|
|
589
|
+
* `/orders`, because a route is mounted at a path. A consumer says
|
|
590
|
+
* `http://backend.internal/orders`, because a call has to say which
|
|
591
|
+
* host. They are the same boundary, and pairing them means reading the
|
|
592
|
+
* path out of both.
|
|
593
|
+
*
|
|
594
|
+
* The query and the fragment come off with the origin. Neither picks a
|
|
595
|
+
* route: `/orders?page=2` reaches the same handler as `/orders`.
|
|
596
|
+
*/
|
|
597
|
+
/**
|
|
598
|
+
* The path a URL states, with any origin, query, and fragment removed.
|
|
599
|
+
*
|
|
600
|
+
* Text that states no origin comes back with only the query and the
|
|
601
|
+
* fragment removed, since a relative URL is already a path. A URL that
|
|
602
|
+
* is nothing but an origin comes back as an empty string, which is what
|
|
603
|
+
* a caller wanting a path should treat as "the root".
|
|
604
|
+
*/
|
|
605
|
+
declare function pathAfterOrigin(text: string): string;
|
|
606
|
+
/** Whether the text opens with an origin rather than with a path. */
|
|
607
|
+
declare function statesAnOrigin(text: string): boolean;
|
|
608
|
+
|
|
557
609
|
/**
|
|
558
610
|
* @suss/ir-core: primitives shared across suss IRs.
|
|
559
611
|
*
|
|
@@ -684,4 +736,4 @@ declare function metricBinding(opts: {
|
|
|
684
736
|
metricType: string | null;
|
|
685
737
|
}): BoundaryBinding;
|
|
686
738
|
|
|
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 };
|
|
739
|
+
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, groundedPairingKey, hasNameHole, isGraphqlOperationBinding, messageBusBinding, metricBinding, metricIdentityKey, namePatternFromSub, namePatternKey, namesAgree, namesNothing, normalizeRuleBoundary, packageExportBinding, pairingKey, parseBoundaryName, parseChannel, pathAfterOrigin, patternHole, referenceFromName, referenceName, referenceOf, reportsUnpairedItself, restBinding, ruleBoundaryMatchesKey, runtimeConfigBinding, semanticsAgree, semconvAttributes, servesRequest, statesAnOrigin, storageBinding, validateRule };
|
package/dist/index.js
CHANGED
|
@@ -29,12 +29,14 @@ import {
|
|
|
29
29
|
metricIdentityKey,
|
|
30
30
|
normalizePath,
|
|
31
31
|
parseChannel,
|
|
32
|
+
pathAfterOrigin,
|
|
32
33
|
pathShape,
|
|
33
34
|
routePathAdmits,
|
|
34
35
|
semconvMappingOf,
|
|
36
|
+
statesAnOrigin,
|
|
35
37
|
typeDefinitionKey,
|
|
36
38
|
withDefinitionsInlined
|
|
37
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-HSJYPBJU.js";
|
|
38
40
|
|
|
39
41
|
// src/semconv.ts
|
|
40
42
|
function semconvAttributes(binding) {
|
|
@@ -59,6 +61,13 @@ function semconvAttributes(binding) {
|
|
|
59
61
|
function boundaryKey(binding) {
|
|
60
62
|
return behaviorOf(binding.semantics).identityKey(binding.semantics);
|
|
61
63
|
}
|
|
64
|
+
function groundedPairingKey(binding, deployedAs) {
|
|
65
|
+
const behavior = behaviorOf(binding.semantics);
|
|
66
|
+
const grounded = behavior.groundName?.(binding.semantics, deployedAs) ?? null;
|
|
67
|
+
return pairingKey(
|
|
68
|
+
grounded === null ? binding : { ...binding, semantics: grounded }
|
|
69
|
+
);
|
|
70
|
+
}
|
|
62
71
|
function pairingKey(binding) {
|
|
63
72
|
const behavior = behaviorOf(binding.semantics);
|
|
64
73
|
return (behavior.pairingKey ?? behavior.identityKey)(binding.semantics);
|
|
@@ -673,6 +682,7 @@ export {
|
|
|
673
682
|
gqlIdentityKey,
|
|
674
683
|
graphqlOperationBinding,
|
|
675
684
|
graphqlResolverBinding,
|
|
685
|
+
groundedPairingKey,
|
|
676
686
|
hasNameHole,
|
|
677
687
|
isGraphqlOperationBinding,
|
|
678
688
|
messageBusBinding,
|
|
@@ -689,6 +699,7 @@ export {
|
|
|
689
699
|
pairingKey,
|
|
690
700
|
parseBoundaryName,
|
|
691
701
|
parseChannel,
|
|
702
|
+
pathAfterOrigin,
|
|
692
703
|
pathShape,
|
|
693
704
|
patternHole,
|
|
694
705
|
referenceFromName,
|
|
@@ -702,6 +713,7 @@ export {
|
|
|
702
713
|
semanticsAgree,
|
|
703
714
|
semconvAttributes,
|
|
704
715
|
servesRequest,
|
|
716
|
+
statesAnOrigin,
|
|
705
717
|
storageBinding,
|
|
706
718
|
typeDefinitionKey,
|
|
707
719
|
validateRule,
|
package/dist/schemas.cjs
CHANGED
|
@@ -344,6 +344,17 @@ var metricSemantics = defineBoundarySemantics({
|
|
|
344
344
|
|
|
345
345
|
// src/semantics/rest.ts
|
|
346
346
|
var import_zod6 = require("zod");
|
|
347
|
+
|
|
348
|
+
// src/urlPath.ts
|
|
349
|
+
var SCHEME_ORIGIN = /^[a-zA-Z][a-zA-Z\d+.-]*:\/\/[^/]*/;
|
|
350
|
+
var PROTOCOL_RELATIVE_ORIGIN = /^\/\/[^/]*/;
|
|
351
|
+
function pathAfterOrigin(text) {
|
|
352
|
+
const withoutOrigin = text.replace(SCHEME_ORIGIN, "").replace(PROTOCOL_RELATIVE_ORIGIN, "");
|
|
353
|
+
const cut = withoutOrigin.search(/[?#]/);
|
|
354
|
+
return cut === -1 ? withoutOrigin : withoutOrigin.slice(0, cut);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// src/semantics/rest.ts
|
|
347
358
|
var RestSemanticsSchema = import_zod6.z.object({
|
|
348
359
|
name: import_zod6.z.literal("rest"),
|
|
349
360
|
/**
|
|
@@ -362,6 +373,11 @@ var RestSemanticsSchema = import_zod6.z.object({
|
|
|
362
373
|
*/
|
|
363
374
|
declaredResponses: import_zod6.z.array(import_zod6.z.number()).optional()
|
|
364
375
|
});
|
|
376
|
+
var OPENING_HOLE = /^\{([^{}]+)\}(.*)$/;
|
|
377
|
+
function variableOf(label) {
|
|
378
|
+
const parts = label.split(".");
|
|
379
|
+
return parts[parts.length - 1] ?? label;
|
|
380
|
+
}
|
|
365
381
|
function normalizePath(path) {
|
|
366
382
|
let normalized = path.replace(/:([a-zA-Z_]\w*)/g, "{$1}");
|
|
367
383
|
if (normalized.length > 1 && normalized.endsWith("/")) {
|
|
@@ -471,6 +487,37 @@ var restSemantics = defineBoundarySemantics({
|
|
|
471
487
|
}
|
|
472
488
|
return routePathAdmits(semantics.path, path) ? "match" : "nomatch";
|
|
473
489
|
},
|
|
490
|
+
/**
|
|
491
|
+
* A call whose base URL the deployment fills in, resolved to the
|
|
492
|
+
* path it reaches.
|
|
493
|
+
*
|
|
494
|
+
* The source cannot settle this on its own, which is why the
|
|
495
|
+
* adapter leaves the hole in. `API_BASE` could be
|
|
496
|
+
* `http://backend.internal`, and then the path is `/orders`. It
|
|
497
|
+
* could equally be `/api/v2`, and then the path is
|
|
498
|
+
* `/api/v2/orders`. Putting the deployed value in and reading the
|
|
499
|
+
* path back out gets the right one either way.
|
|
500
|
+
*
|
|
501
|
+
* Only a hole at the front is a base URL. A hole further along is a
|
|
502
|
+
* route parameter, and `/orders/{id}` means every id rather than a
|
|
503
|
+
* variable somebody set.
|
|
504
|
+
*/
|
|
505
|
+
groundName(semantics, deployedAs) {
|
|
506
|
+
if (semantics.path === null) {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
const opening = OPENING_HOLE.exec(semantics.path);
|
|
510
|
+
if (opening === null) {
|
|
511
|
+
return null;
|
|
512
|
+
}
|
|
513
|
+
const [, label, rest] = opening;
|
|
514
|
+
const base = deployedAs(variableOf(label));
|
|
515
|
+
if (base === null) {
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
const grounded = pathAfterOrigin(`${base}${rest}`);
|
|
519
|
+
return { ...semantics, path: grounded === "" ? "/" : grounded };
|
|
520
|
+
},
|
|
474
521
|
ruleBoundary: {
|
|
475
522
|
// "METHOD /path": one leading token, then an absolute path.
|
|
476
523
|
claims(raw) {
|
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.14.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": [
|