@suss/ir-core 0.33.1 → 0.34.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/README.md +30 -30
- package/dist/{chunk-N564FH5U.js → chunk-A46EUIKC.js} +546 -237
- package/dist/index.cjs +659 -284
- package/dist/index.d.cts +452 -360
- package/dist/index.d.ts +452 -360
- package/dist/index.js +94 -31
- package/dist/{schemas-CIns0fqF.d.cts → schemas-lfg8kvak.d.cts} +94 -81
- package/dist/{schemas-CIns0fqF.d.ts → schemas-lfg8kvak.d.ts} +94 -81
- package/dist/schemas.cjs +541 -239
- package/dist/schemas.d.cts +1 -1
- package/dist/schemas.d.ts +1 -1
- package/dist/schemas.js +1 -1
- package/package.json +2 -2
package/dist/schemas.cjs
CHANGED
|
@@ -40,10 +40,10 @@ __export(schemas_exports, {
|
|
|
40
40
|
withDefinitionsInlined: () => withDefinitionsInlined
|
|
41
41
|
});
|
|
42
42
|
module.exports = __toCommonJS(schemas_exports);
|
|
43
|
-
var
|
|
43
|
+
var import_zod13 = require("zod");
|
|
44
44
|
|
|
45
45
|
// src/semantics/registry.ts
|
|
46
|
-
var
|
|
46
|
+
var import_zod12 = require("zod");
|
|
47
47
|
|
|
48
48
|
// src/semantics/functionCall.ts
|
|
49
49
|
var import_zod = require("zod");
|
|
@@ -64,6 +64,9 @@ function unitIdentityKey(deploymentTarget, instanceName) {
|
|
|
64
64
|
function metricIdentityKey(metricSystem, metricType) {
|
|
65
65
|
return `metric:${metricSystem} ${metricType}`;
|
|
66
66
|
}
|
|
67
|
+
function ioIdentityKey(target) {
|
|
68
|
+
return `io:${target}`;
|
|
69
|
+
}
|
|
67
70
|
|
|
68
71
|
// src/semantics/definition.ts
|
|
69
72
|
function defineBoundarySemantics(definition) {
|
|
@@ -74,10 +77,11 @@ function defineBoundarySemantics(definition) {
|
|
|
74
77
|
var FunctionCallSemanticsSchema = import_zod.z.object({
|
|
75
78
|
name: import_zod.z.literal("function-call"),
|
|
76
79
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* module path
|
|
80
|
-
*
|
|
80
|
+
* Where the function lives inside this repository. It is either the
|
|
81
|
+
* name of a module the project lists in `suss.json`, such as
|
|
82
|
+
* `"billing"`, or a module path, such as the file a server action is
|
|
83
|
+
* written in. A name has no separator in it and a path always does.
|
|
84
|
+
* Packs that don't do cross-module pairing can leave it unset.
|
|
81
85
|
*/
|
|
82
86
|
module: import_zod.z.string().optional(),
|
|
83
87
|
/** Named export within the module, when applicable. */
|
|
@@ -102,35 +106,38 @@ var FunctionCallSemanticsSchema = import_zod.z.object({
|
|
|
102
106
|
*/
|
|
103
107
|
exportPath: import_zod.z.array(import_zod.z.string()).optional()
|
|
104
108
|
});
|
|
109
|
+
function isModuleName(module2) {
|
|
110
|
+
return !module2.includes("/") && !module2.includes("\\");
|
|
111
|
+
}
|
|
105
112
|
var functionCallSemantics = defineBoundarySemantics({
|
|
106
113
|
name: "function-call",
|
|
107
114
|
schema: FunctionCallSemanticsSchema,
|
|
108
|
-
// A call
|
|
109
|
-
//
|
|
115
|
+
// A call inside the process does not produce a span, and the
|
|
116
|
+
// conventions have no attributes for it.
|
|
110
117
|
semconv: {},
|
|
111
118
|
behavior: {
|
|
112
|
-
/** A call returns a value,
|
|
119
|
+
/** A call returns a value, with no status. */
|
|
113
120
|
exchangesHttpResponses: false,
|
|
114
121
|
leavesTheProcess: false,
|
|
115
122
|
reportsUnpairedItself: false,
|
|
116
123
|
/**
|
|
117
|
-
* In-repo units
|
|
118
|
-
*
|
|
119
|
-
*
|
|
124
|
+
* In-repo units such as components have no key on purpose. They
|
|
125
|
+
* pair through call edges instead of the keyed pass, so a missing
|
|
126
|
+
* key does not make them unreachable.
|
|
120
127
|
*/
|
|
121
128
|
canPair: () => true,
|
|
122
|
-
/**
|
|
123
|
-
* `"fn:<package>::<exportPath>"` when both are set, and
|
|
124
|
-
* `"fn:<module>::<exportName>"` for an in-repo unit that states its
|
|
125
|
-
* module, the way a server action does. Components and bare
|
|
126
|
-
* handlers state neither and have no key at all.
|
|
127
|
-
*/
|
|
128
129
|
rewritePaths(semantics, rewrite) {
|
|
129
|
-
if (semantics.module === void 0) {
|
|
130
|
+
if (semantics.module === void 0 || isModuleName(semantics.module)) {
|
|
130
131
|
return semantics;
|
|
131
132
|
}
|
|
132
133
|
return { ...semantics, module: rewrite(semantics.module) };
|
|
133
134
|
},
|
|
135
|
+
/**
|
|
136
|
+
* `"fn:<package>::<exportPath>"` when both are set, and
|
|
137
|
+
* `"fn:<module>::<exportName>"` for an in-repo unit that gives its
|
|
138
|
+
* module, as a server action does. Components and plain handlers
|
|
139
|
+
* give neither and have no key.
|
|
140
|
+
*/
|
|
134
141
|
identityKey(semantics) {
|
|
135
142
|
if (semantics.package !== void 0 && semantics.exportPath !== void 0 && semantics.exportPath.length > 0) {
|
|
136
143
|
return fnIdentityKey(semantics.package, semantics.exportPath);
|
|
@@ -161,15 +168,15 @@ var graphqlOperationSemantics = defineBoundarySemantics({
|
|
|
161
168
|
behavior: {
|
|
162
169
|
/**
|
|
163
170
|
* An operation gets a data-and-errors document back, which the
|
|
164
|
-
* GraphQL contract checker reads. Status codes
|
|
171
|
+
* GraphQL contract checker reads. Status codes do not apply.
|
|
165
172
|
*/
|
|
166
173
|
exchangesHttpResponses: false,
|
|
167
174
|
leavesTheProcess: true,
|
|
168
175
|
reportsUnpairedItself: false,
|
|
169
176
|
identityKey: () => null,
|
|
170
|
-
/**
|
|
177
|
+
/** No key on purpose. The dedicated pass pairs an operation by its document. */
|
|
171
178
|
canPair: () => true,
|
|
172
|
-
/**
|
|
179
|
+
/** `query GetUser`. Only for display, since the contract checker does the pairing. */
|
|
173
180
|
displayLabel(semantics) {
|
|
174
181
|
return `${semantics.operationType} ${semantics.operationName ?? "<anonymous>"}`;
|
|
175
182
|
}
|
|
@@ -192,14 +199,14 @@ var GraphqlResolverSemanticsSchema = import_zod3.z.object({
|
|
|
192
199
|
var graphqlResolverSemantics = defineBoundarySemantics({
|
|
193
200
|
name: "graphql-resolver",
|
|
194
201
|
schema: GraphqlResolverSemanticsSchema,
|
|
195
|
-
// The conventions
|
|
196
|
-
// resolver the server ran for one field of it.
|
|
202
|
+
// The conventions describe the operation a client sent. They have no
|
|
203
|
+
// attribute for the resolver the server ran for one field of it.
|
|
197
204
|
semconv: {},
|
|
198
205
|
behavior: {
|
|
199
|
-
/** A resolver returns a field value,
|
|
206
|
+
/** A resolver returns a field value, with no status. */
|
|
200
207
|
exchangesHttpResponses: false,
|
|
201
|
-
// A
|
|
202
|
-
// server calls
|
|
208
|
+
// A resolver runs because a client outside the process sent a query,
|
|
209
|
+
// even though the server calls it in its own process.
|
|
203
210
|
leavesTheProcess: true,
|
|
204
211
|
reportsUnpairedItself: false,
|
|
205
212
|
/**
|
|
@@ -216,8 +223,32 @@ var graphqlResolverSemantics = defineBoundarySemantics({
|
|
|
216
223
|
}
|
|
217
224
|
});
|
|
218
225
|
|
|
219
|
-
// src/semantics/
|
|
226
|
+
// src/semantics/io.ts
|
|
220
227
|
var import_zod4 = require("zod");
|
|
228
|
+
var IoSemanticsSchema = import_zod4.z.object({
|
|
229
|
+
name: import_zod4.z.literal("io"),
|
|
230
|
+
/** The stream, such as `stdout` or `stderr`. Null when the source does not say which. */
|
|
231
|
+
target: import_zod4.z.string().nullable()
|
|
232
|
+
});
|
|
233
|
+
var ioSemantics = defineBoundarySemantics({
|
|
234
|
+
name: "io",
|
|
235
|
+
schema: IoSemanticsSchema,
|
|
236
|
+
// The semantic conventions have no attribute for a process's own streams.
|
|
237
|
+
semconv: {},
|
|
238
|
+
behavior: {
|
|
239
|
+
exchangesHttpResponses: false,
|
|
240
|
+
leavesTheProcess: true,
|
|
241
|
+
reportsUnpairedItself: false,
|
|
242
|
+
identityKey(semantics) {
|
|
243
|
+
return semantics.target === null ? null : ioIdentityKey(semantics.target);
|
|
244
|
+
},
|
|
245
|
+
// Nothing declares the reading side, so a write has nothing to meet.
|
|
246
|
+
canPair: () => false
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// src/semantics/messageBus.ts
|
|
251
|
+
var import_zod5 = require("zod");
|
|
221
252
|
|
|
222
253
|
// src/channel.ts
|
|
223
254
|
function parseChannel(channel) {
|
|
@@ -237,12 +268,11 @@ function channelsPair(a, b) {
|
|
|
237
268
|
}
|
|
238
269
|
|
|
239
270
|
// src/semantics/messageBus.ts
|
|
240
|
-
var MessageBusSemanticsSchema =
|
|
241
|
-
name:
|
|
242
|
-
messageBus:
|
|
243
|
-
// The conventions
|
|
244
|
-
// underscore and one with a dot
|
|
245
|
-
// not a typo.
|
|
271
|
+
var MessageBusSemanticsSchema = import_zod5.z.object({
|
|
272
|
+
name: import_zod5.z.literal("message-bus"),
|
|
273
|
+
messageBus: import_zod5.z.enum([
|
|
274
|
+
// The conventions write these two AWS buses differently, one with an
|
|
275
|
+
// underscore and one with a dot. Both are as the conventions give them.
|
|
246
276
|
"aws_sqs",
|
|
247
277
|
"aws.sns",
|
|
248
278
|
"s3",
|
|
@@ -261,12 +291,13 @@ var MessageBusSemanticsSchema = import_zod4.z.object({
|
|
|
261
291
|
"cloudflare-tail"
|
|
262
292
|
]),
|
|
263
293
|
/**
|
|
264
|
-
*
|
|
265
|
-
* a subject pattern, or `bus#detailType`. Null when this
|
|
266
|
-
* not say which channel, as with a send whose queue the
|
|
267
|
-
*
|
|
294
|
+
* A stable channel identifier: a CloudFormation logical id, a queue or
|
|
295
|
+
* topic name, a subject pattern, or `bus#detailType`. Null when this
|
|
296
|
+
* source does not say which channel, as with a send whose queue the
|
|
297
|
+
* code picks at run time, or a receive whose queue is set in the
|
|
298
|
+
* event-source mapping.
|
|
268
299
|
*/
|
|
269
|
-
channel:
|
|
300
|
+
channel: import_zod5.z.string().min(1).nullable()
|
|
270
301
|
});
|
|
271
302
|
var messageBusSemantics = defineBoundarySemantics({
|
|
272
303
|
name: "message-bus",
|
|
@@ -280,25 +311,26 @@ var messageBusSemantics = defineBoundarySemantics({
|
|
|
280
311
|
exchangesHttpResponses: false,
|
|
281
312
|
leavesTheProcess: true,
|
|
282
313
|
/**
|
|
283
|
-
* `checkMessageBus`
|
|
284
|
-
*
|
|
314
|
+
* `checkMessageBus` reports every channel, unused ones included, so
|
|
315
|
+
* the generic unmatched list leaves them out.
|
|
285
316
|
*/
|
|
286
317
|
reportsUnpairedItself: true,
|
|
287
318
|
/**
|
|
288
|
-
* A channel
|
|
289
|
-
*
|
|
290
|
-
* channel
|
|
319
|
+
* A channel that is null here can still pair. The dedicated pass
|
|
320
|
+
* follows environment variable chains before comparing, so a
|
|
321
|
+
* consumer whose channel is set at run time meets the template that
|
|
322
|
+
* declares it.
|
|
291
323
|
*/
|
|
292
324
|
canPair: () => true,
|
|
293
325
|
/**
|
|
294
326
|
* `"bus:<messageBus> <subject>"`, or null when the channel is null
|
|
295
327
|
* or its subject is empty.
|
|
296
328
|
*
|
|
297
|
-
* The key has the subject
|
|
298
|
-
*
|
|
329
|
+
* The key has the subject and leaves out the bus, so a template
|
|
330
|
+
* writing `default#order.placed` and a handler writing
|
|
299
331
|
* `order.placed` land in one bucket, where `sidesAgree` compares the
|
|
300
|
-
* buses.
|
|
301
|
-
*
|
|
332
|
+
* buses. Keying on the bus would separate a side that cannot know
|
|
333
|
+
* its bus from the side that can.
|
|
302
334
|
*
|
|
303
335
|
* The bus technology stays in, because a queue and an event router
|
|
304
336
|
* are different destinations even with the same subject. Case stays
|
|
@@ -321,9 +353,9 @@ var messageBusSemantics = defineBoundarySemantics({
|
|
|
321
353
|
return channelsPair(a.channel, b.channel);
|
|
322
354
|
},
|
|
323
355
|
/**
|
|
324
|
-
* The whole channel, bus included,
|
|
325
|
-
*
|
|
326
|
-
*
|
|
356
|
+
* The whole channel, bus included, where the identity key has only
|
|
357
|
+
* the subject. A reader looking at unmatched channels needs to see
|
|
358
|
+
* which bus each one was on.
|
|
327
359
|
*/
|
|
328
360
|
displayLabel(semantics) {
|
|
329
361
|
if (semantics.channel === null) {
|
|
@@ -335,25 +367,25 @@ var messageBusSemantics = defineBoundarySemantics({
|
|
|
335
367
|
});
|
|
336
368
|
|
|
337
369
|
// src/semantics/metric.ts
|
|
338
|
-
var
|
|
339
|
-
var MetricSemanticsSchema =
|
|
340
|
-
name:
|
|
341
|
-
/**
|
|
342
|
-
metricSystem:
|
|
370
|
+
var import_zod6 = require("zod");
|
|
371
|
+
var MetricSemanticsSchema = import_zod6.z.object({
|
|
372
|
+
name: import_zod6.z.literal("metric"),
|
|
373
|
+
/** The system the series is stored in, such as `cloud-monitoring` or `cloudwatch`. */
|
|
374
|
+
metricSystem: import_zod6.z.string(),
|
|
343
375
|
/**
|
|
344
|
-
* The type string the system
|
|
345
|
-
* both sides
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
376
|
+
* The type string the system uses for the metric, written the same
|
|
377
|
+
* way on both sides. Null when the reader could not work the string
|
|
378
|
+
* out, as with a query it could not parse. A null type pairs with
|
|
379
|
+
* nothing, so it cannot pair with an unrelated metric that happens to
|
|
380
|
+
* share its source text.
|
|
349
381
|
*/
|
|
350
|
-
metricType:
|
|
382
|
+
metricType: import_zod6.z.string().nullable()
|
|
351
383
|
});
|
|
352
384
|
var metricSemantics = defineBoundarySemantics({
|
|
353
385
|
name: "metric",
|
|
354
386
|
schema: MetricSemanticsSchema,
|
|
355
|
-
// OpenTelemetry
|
|
356
|
-
// attribute for the system
|
|
387
|
+
// OpenTelemetry identifies a metric by its instrument name. It has no
|
|
388
|
+
// attribute for the metric's system or its type string.
|
|
357
389
|
semconv: {},
|
|
358
390
|
behavior: {
|
|
359
391
|
exchangesHttpResponses: false,
|
|
@@ -369,7 +401,7 @@ var metricSemantics = defineBoundarySemantics({
|
|
|
369
401
|
});
|
|
370
402
|
|
|
371
403
|
// src/semantics/rest.ts
|
|
372
|
-
var
|
|
404
|
+
var import_zod7 = require("zod");
|
|
373
405
|
|
|
374
406
|
// src/boundaryName.ts
|
|
375
407
|
var HOLE_SPLIT = /(\{[^}]*\})/;
|
|
@@ -417,6 +449,38 @@ function pathAfterOrigin(text) {
|
|
|
417
449
|
const cut = withoutOrigin.search(/[?#]/);
|
|
418
450
|
return cut === -1 ? withoutOrigin : withoutOrigin.slice(0, cut);
|
|
419
451
|
}
|
|
452
|
+
var PRIVATE_SUFFIXES = [
|
|
453
|
+
"local",
|
|
454
|
+
"internal",
|
|
455
|
+
"localhost",
|
|
456
|
+
"lan",
|
|
457
|
+
"home.arpa",
|
|
458
|
+
"svc",
|
|
459
|
+
"test",
|
|
460
|
+
"example",
|
|
461
|
+
"invalid",
|
|
462
|
+
"example.com",
|
|
463
|
+
"example.net",
|
|
464
|
+
"example.org"
|
|
465
|
+
];
|
|
466
|
+
var PRIVATE_IPV4 = /^(?:127\.|10\.|192\.168\.|169\.254\.|172\.(?:1[6-9]|2\d|3[01])\.|0\.0\.0\.0$)/;
|
|
467
|
+
function hostnameOf(host) {
|
|
468
|
+
const lower = host.toLowerCase();
|
|
469
|
+
const name = lower.startsWith("[") ? lower.slice(0, lower.indexOf("]") + 1) : lower.split(":")[0] ?? lower;
|
|
470
|
+
return name.replace(/\.$/, "");
|
|
471
|
+
}
|
|
472
|
+
function hostCanBeOwn(host) {
|
|
473
|
+
const name = hostnameOf(host);
|
|
474
|
+
if (name === "[::1]" || /^\[f[cd]/.test(name) || PRIVATE_IPV4.test(name)) {
|
|
475
|
+
return true;
|
|
476
|
+
}
|
|
477
|
+
if (!name.includes(".")) {
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
480
|
+
return PRIVATE_SUFFIXES.some(
|
|
481
|
+
(suffix) => name === suffix || name.endsWith(`.${suffix}`)
|
|
482
|
+
);
|
|
483
|
+
}
|
|
420
484
|
|
|
421
485
|
// src/semantics/pathPattern.ts
|
|
422
486
|
var ONE = { kind: "one" };
|
|
@@ -467,14 +531,18 @@ function shapedItem(segment) {
|
|
|
467
531
|
return ".*";
|
|
468
532
|
}
|
|
469
533
|
if (part.startsWith("{")) {
|
|
470
|
-
return "[^/]+";
|
|
534
|
+
return /[?*]\}$/.test(part) ? "[^/]*" : "[^/]+";
|
|
471
535
|
}
|
|
472
536
|
return escapedForRegex(part);
|
|
473
537
|
}).join("");
|
|
538
|
+
const holes = segment.match(/\{[^}]*\}|\*/g) ?? [];
|
|
539
|
+
const text = segment.replace(/\{[^}]*\}|\*/g, "");
|
|
474
540
|
return {
|
|
475
541
|
kind: "shaped",
|
|
476
542
|
shape: segment.replace(/\{[^}]*\}/g, "{}"),
|
|
477
|
-
test: new RegExp(`^${source}$`)
|
|
543
|
+
test: new RegExp(`^${source}$`),
|
|
544
|
+
bare: holes.every((hole) => hole === "*" || /[?*]\}$/.test(hole)) ? text : null,
|
|
545
|
+
textLength: text.length
|
|
478
546
|
};
|
|
479
547
|
}
|
|
480
548
|
function itemsOf(segment) {
|
|
@@ -494,10 +562,35 @@ function segmentsOf(alternative) {
|
|
|
494
562
|
const trimmed = alternative.startsWith("/") ? alternative.slice(1) : alternative;
|
|
495
563
|
return trimmed === "" ? [] : trimmed.split("/");
|
|
496
564
|
}
|
|
565
|
+
function patternSegmentsOf(alternative) {
|
|
566
|
+
const segments = segmentsOf(alternative);
|
|
567
|
+
const first = segments[0];
|
|
568
|
+
const opensWithBaseUrl = !alternative.startsWith("/") && first !== void 0 && HOLE_SEGMENT.test(first);
|
|
569
|
+
return opensWithBaseUrl ? segments.slice(1) : segments;
|
|
570
|
+
}
|
|
571
|
+
function pathAfterBaseUrl(path) {
|
|
572
|
+
const segments = segmentsOf(path);
|
|
573
|
+
const first = segments[0];
|
|
574
|
+
if (path.startsWith("/") || first === void 0 || segments.length < 2 || !HOLE_SEGMENT.test(first)) {
|
|
575
|
+
return path;
|
|
576
|
+
}
|
|
577
|
+
return `/${segments.slice(1).join("/")}`;
|
|
578
|
+
}
|
|
579
|
+
var PATTERNS = /* @__PURE__ */ new Map();
|
|
580
|
+
var PATTERN_CACHE_LIMIT = 5e4;
|
|
497
581
|
function patternOf(path) {
|
|
498
|
-
|
|
499
|
-
|
|
582
|
+
const known = PATTERNS.get(path);
|
|
583
|
+
if (known !== void 0) {
|
|
584
|
+
return known;
|
|
585
|
+
}
|
|
586
|
+
const pattern = alternativesOf(path).map(
|
|
587
|
+
(alternative) => patternSegmentsOf(alternative).flatMap(itemsOf)
|
|
500
588
|
);
|
|
589
|
+
if (PATTERNS.size >= PATTERN_CACHE_LIMIT) {
|
|
590
|
+
PATTERNS.clear();
|
|
591
|
+
}
|
|
592
|
+
PATTERNS.set(path, pattern);
|
|
593
|
+
return pattern;
|
|
501
594
|
}
|
|
502
595
|
function literalItemsOf(path) {
|
|
503
596
|
return segmentsOf(path).map((text) => ({ kind: "text", text }));
|
|
@@ -576,17 +669,104 @@ function pathSpansShapes(path) {
|
|
|
576
669
|
function countOf(items, kinds) {
|
|
577
670
|
return items.filter((item) => kinds.includes(item.kind)).length;
|
|
578
671
|
}
|
|
579
|
-
function
|
|
580
|
-
|
|
581
|
-
const ranks = alternatives.map((items) => [
|
|
672
|
+
function readingSpecificity(items) {
|
|
673
|
+
return [
|
|
582
674
|
countOf(items, ["text"]),
|
|
583
675
|
countOf(items, ["shaped"]),
|
|
584
676
|
-countOf(items, ["optional", "star"])
|
|
585
|
-
]
|
|
586
|
-
|
|
587
|
-
|
|
677
|
+
];
|
|
678
|
+
}
|
|
679
|
+
function segmentFit(route, request) {
|
|
680
|
+
if (!meetOnOneSegment(route, request)) {
|
|
681
|
+
return "apart";
|
|
682
|
+
}
|
|
683
|
+
if (request.kind === "text") {
|
|
684
|
+
if (route.kind === "text") {
|
|
685
|
+
return "stated";
|
|
686
|
+
}
|
|
687
|
+
return route.kind === "shaped" ? "shaped" : "free";
|
|
688
|
+
}
|
|
689
|
+
if (absorbsAnything(route)) {
|
|
690
|
+
return "free";
|
|
691
|
+
}
|
|
692
|
+
if (request.kind !== "shaped") {
|
|
693
|
+
return "guess";
|
|
694
|
+
}
|
|
695
|
+
if (route.kind === "shaped") {
|
|
696
|
+
return "free";
|
|
697
|
+
}
|
|
698
|
+
return route.kind === "text" && request.bare === route.text ? "stated" : "guess";
|
|
699
|
+
}
|
|
700
|
+
function shapedText(item) {
|
|
701
|
+
return item.kind === "shaped" ? item.textLength : 0;
|
|
702
|
+
}
|
|
703
|
+
function betterFit(a, b) {
|
|
704
|
+
return a === void 0 || compareRanks(b, a) > 0;
|
|
705
|
+
}
|
|
706
|
+
function bestFit(route, request) {
|
|
707
|
+
const width = request.length + 1;
|
|
708
|
+
const best = new Array((route.length + 1) * width);
|
|
709
|
+
best[0] = [0, 0];
|
|
710
|
+
const offer = (i, j, fit) => {
|
|
711
|
+
const at = i * width + j;
|
|
712
|
+
if (betterFit(best[at], fit)) {
|
|
713
|
+
best[at] = fit;
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
for (let reached = 0; reached <= route.length + request.length; reached += 1) {
|
|
717
|
+
for (let i = Math.max(0, reached - request.length); i <= Math.min(route.length, reached); i += 1) {
|
|
718
|
+
const j = reached - i;
|
|
719
|
+
const fit = best[i * width + j];
|
|
720
|
+
if (fit === void 0) {
|
|
721
|
+
continue;
|
|
722
|
+
}
|
|
723
|
+
const left = route[i];
|
|
724
|
+
const right = request[j];
|
|
725
|
+
if (left !== void 0 && skippable(left)) {
|
|
726
|
+
offer(i + 1, j, fit);
|
|
727
|
+
}
|
|
728
|
+
if (right !== void 0 && skippable(right)) {
|
|
729
|
+
offer(i, j + 1, fit);
|
|
730
|
+
}
|
|
731
|
+
if (left === void 0 || right === void 0) {
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
if (left.kind === "star" && right.kind === "star") {
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
const kind = segmentFit(left, right);
|
|
738
|
+
if (kind === "apart" || kind === "guess") {
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
offer(
|
|
742
|
+
left.kind === "star" ? i : i + 1,
|
|
743
|
+
right.kind === "star" ? j : j + 1,
|
|
744
|
+
[
|
|
745
|
+
fit[0] + (kind === "stated" ? 1 : 0),
|
|
746
|
+
fit[1] + (kind === "shaped" ? shapedText(left) : 0)
|
|
747
|
+
]
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
return best[route.length * width + request.length] ?? null;
|
|
752
|
+
}
|
|
753
|
+
function requestRank(route, request) {
|
|
754
|
+
let found = null;
|
|
755
|
+
for (const routeItems of patternOf(route)) {
|
|
756
|
+
for (const requestItems of patternOf(request)) {
|
|
757
|
+
const fit = bestFit(routeItems, requestItems);
|
|
758
|
+
const rank = fit === null ? null : [...fit, ...readingSpecificity(routeItems)];
|
|
759
|
+
if (rank !== null && (found === null || compareRanks(rank, found) > 0)) {
|
|
760
|
+
found = rank;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
return found;
|
|
765
|
+
}
|
|
766
|
+
function isCatchAll(route) {
|
|
767
|
+
return patternOf(route).some(
|
|
768
|
+
(items) => items.length > 0 && items.every(absorbsAnything) && items.some((item) => item.kind === "star")
|
|
588
769
|
);
|
|
589
|
-
return [...loosest, 1 - alternatives.length];
|
|
590
770
|
}
|
|
591
771
|
function compareRanks(a, b) {
|
|
592
772
|
const length = Math.max(a.length, b.length);
|
|
@@ -600,23 +780,34 @@ function compareRanks(a, b) {
|
|
|
600
780
|
}
|
|
601
781
|
|
|
602
782
|
// src/semantics/rest.ts
|
|
603
|
-
var RestSemanticsSchema =
|
|
604
|
-
name:
|
|
783
|
+
var RestSemanticsSchema = import_zod7.z.object({
|
|
784
|
+
name: import_zod7.z.literal("rest"),
|
|
605
785
|
/**
|
|
606
786
|
* Uppercase HTTP method ("GET", "POST", …), `"*"` for a handler that
|
|
607
787
|
* responds to every method, or null when this source does not say
|
|
608
788
|
* which method.
|
|
609
789
|
*/
|
|
610
|
-
method:
|
|
790
|
+
method: import_zod7.z.string().min(1).nullable(),
|
|
611
791
|
/** Normalized route path ("/users/{id}"), or null when this source does not give one. */
|
|
612
|
-
path:
|
|
792
|
+
path: import_zod7.z.string().min(1).nullable(),
|
|
613
793
|
/**
|
|
614
|
-
* Status codes the producing source explicitly
|
|
615
|
-
* responses
|
|
616
|
-
*
|
|
617
|
-
* metadata. Absent
|
|
794
|
+
* Status codes the producing source declared explicitly, such as
|
|
795
|
+
* OpenAPI responses or CloudFormation MethodResponses. They are kept
|
|
796
|
+
* here so the pairing layer can see them without unwrapping any
|
|
797
|
+
* metadata. Absent or empty for an inferred source.
|
|
618
798
|
*/
|
|
619
|
-
declaredResponses:
|
|
799
|
+
declaredResponses: import_zod7.z.array(import_zod7.z.number()).optional(),
|
|
800
|
+
/**
|
|
801
|
+
* The host a client wrote in an absolute URL, with its port
|
|
802
|
+
* (`api.example.com:8443`). A piece suss could not read is written
|
|
803
|
+
* `{name}`. Absent for a relative URL and for a route.
|
|
804
|
+
*/
|
|
805
|
+
host: import_zod7.z.string().min(1).optional(),
|
|
806
|
+
/**
|
|
807
|
+
* True for a route the framework also serves with `.{format}` after
|
|
808
|
+
* its path, the way Rails serves `/search.json` from `get "search"`.
|
|
809
|
+
*/
|
|
810
|
+
optionalFormat: import_zod7.z.boolean().optional()
|
|
620
811
|
});
|
|
621
812
|
var OPENING_HOLE = /^\{([^{}]+)\}(.*)$/;
|
|
622
813
|
function baseUrlReference(semantics) {
|
|
@@ -624,7 +815,10 @@ function baseUrlReference(semantics) {
|
|
|
624
815
|
return label === void 0 ? null : referenceFromName(patternHole(label));
|
|
625
816
|
}
|
|
626
817
|
function normalizePath(path) {
|
|
627
|
-
let normalized = path.replace(
|
|
818
|
+
let normalized = withoutParameterConstraints(path).replace(
|
|
819
|
+
/:([a-zA-Z_]\w*)([?+*]?)/g,
|
|
820
|
+
"{$1$2}"
|
|
821
|
+
);
|
|
628
822
|
if (normalized.length > 1 && normalized.endsWith("/")) {
|
|
629
823
|
normalized = normalized.slice(0, -1);
|
|
630
824
|
}
|
|
@@ -634,6 +828,46 @@ function normalizePath(path) {
|
|
|
634
828
|
);
|
|
635
829
|
return normalized;
|
|
636
830
|
}
|
|
831
|
+
function withoutParameterConstraints(path) {
|
|
832
|
+
let out = "";
|
|
833
|
+
let i = 0;
|
|
834
|
+
while (i < path.length) {
|
|
835
|
+
const name = /^:[a-zA-Z_]\w*\(/.exec(path.slice(i));
|
|
836
|
+
if (name === null) {
|
|
837
|
+
out += path[i];
|
|
838
|
+
i += 1;
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
const open = i + name[0].length - 1;
|
|
842
|
+
const close = closingParenthesis(path, open);
|
|
843
|
+
if (close === -1) {
|
|
844
|
+
return out + path.slice(i);
|
|
845
|
+
}
|
|
846
|
+
out += path.slice(i, open);
|
|
847
|
+
i = close + 1;
|
|
848
|
+
}
|
|
849
|
+
return out;
|
|
850
|
+
}
|
|
851
|
+
function closingParenthesis(text, open) {
|
|
852
|
+
let depth = 0;
|
|
853
|
+
for (let i = open; i < text.length; i += 1) {
|
|
854
|
+
const char = text[i];
|
|
855
|
+
if (char === "\\") {
|
|
856
|
+
i += 1;
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
if (char === "(") {
|
|
860
|
+
depth += 1;
|
|
861
|
+
}
|
|
862
|
+
if (char === ")") {
|
|
863
|
+
depth -= 1;
|
|
864
|
+
if (depth === 0) {
|
|
865
|
+
return i;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
return -1;
|
|
870
|
+
}
|
|
637
871
|
function pathShape(path) {
|
|
638
872
|
return normalizePath(path).replace(/\{[^{}]*?([?+*]?)\}/g, "{$1}");
|
|
639
873
|
}
|
|
@@ -646,8 +880,39 @@ function methodsAgree(a, b) {
|
|
|
646
880
|
function routePathAdmits(declaredPath, requestPath) {
|
|
647
881
|
return patternAdmits(normalizePath(declaredPath), normalizePath(requestPath));
|
|
648
882
|
}
|
|
649
|
-
function
|
|
650
|
-
|
|
883
|
+
function readHost(semantics) {
|
|
884
|
+
const host = semantics.host;
|
|
885
|
+
return host === void 0 || /[{(]/.test(host) ? void 0 : host;
|
|
886
|
+
}
|
|
887
|
+
function hostsAgree(a, b) {
|
|
888
|
+
return readHost(a) === readHost(b);
|
|
889
|
+
}
|
|
890
|
+
function hostUnread(semantics) {
|
|
891
|
+
if (semantics.host !== void 0) {
|
|
892
|
+
return readHost(semantics) === void 0;
|
|
893
|
+
}
|
|
894
|
+
return baseUrlReference(semantics) !== null;
|
|
895
|
+
}
|
|
896
|
+
function servedPath(semantics, path) {
|
|
897
|
+
const normalized = normalizePath(path);
|
|
898
|
+
return semantics.optionalFormat === true ? `${normalized}(|.{format})` : normalized;
|
|
899
|
+
}
|
|
900
|
+
function withoutHost(semantics) {
|
|
901
|
+
const { host: _host, ...rest } = semantics;
|
|
902
|
+
return rest;
|
|
903
|
+
}
|
|
904
|
+
function groundedBaseUrl(semantics, deployment) {
|
|
905
|
+
const reference = baseUrlReference(semantics);
|
|
906
|
+
if (reference === null) {
|
|
907
|
+
return null;
|
|
908
|
+
}
|
|
909
|
+
const base = deployment.setTo(reference);
|
|
910
|
+
if (base === null) {
|
|
911
|
+
return null;
|
|
912
|
+
}
|
|
913
|
+
const rest = OPENING_HOLE.exec(semantics.path ?? "")?.[2] ?? "";
|
|
914
|
+
const grounded = pathAfterOrigin(`${base}${rest}`);
|
|
915
|
+
return { ...semantics, path: grounded === "" ? "/" : grounded };
|
|
651
916
|
}
|
|
652
917
|
var restSemantics = defineBoundarySemantics({
|
|
653
918
|
name: "rest",
|
|
@@ -673,39 +938,65 @@ var restSemantics = defineBoundarySemantics({
|
|
|
673
938
|
return `${semantics.method.toUpperCase()} ${normalizePath(semantics.path)}`;
|
|
674
939
|
},
|
|
675
940
|
/**
|
|
676
|
-
* The bucket has only the path
|
|
677
|
-
*
|
|
678
|
-
*
|
|
679
|
-
* sees and a suppression targets ("GET /users", "* /users")
|
|
680
|
-
* this key groups what a reader still tells apart.
|
|
941
|
+
* The bucket has only the path, so a `"*"` route lands with the
|
|
942
|
+
* consumers that each give a method, and `methodsAgree` compares the
|
|
943
|
+
* methods inside the bucket. The identity key is still what a reader
|
|
944
|
+
* sees and what a suppression targets ("GET /users", "* /users").
|
|
681
945
|
*
|
|
682
|
-
*
|
|
683
|
-
* `{userId}` where the Express route that serves it
|
|
684
|
-
* both match the same requests
|
|
685
|
-
* boundaries that never
|
|
946
|
+
* Parameter names are dropped too. A hand-written spec writes
|
|
947
|
+
* `{userId}` where the Express route that serves it writes `:id`,
|
|
948
|
+
* and both match the same requests. Pairing on the name would leave
|
|
949
|
+
* two boundaries that never meet.
|
|
686
950
|
*/
|
|
687
951
|
pairingKey(semantics) {
|
|
688
952
|
if (semantics.method === null || semantics.path === null) {
|
|
689
953
|
return null;
|
|
690
954
|
}
|
|
691
|
-
|
|
955
|
+
const shape = pathShape(pathAfterBaseUrl(normalizePath(semantics.path)));
|
|
956
|
+
const host = readHost(semantics);
|
|
957
|
+
return host === void 0 ? `rest ${shape}` : `rest //${host}${shape}`;
|
|
958
|
+
},
|
|
959
|
+
/**
|
|
960
|
+
* A spec writes `/teams/{team_id}` where the route that serves it
|
|
961
|
+
* writes `/teams/:id`. The method stays in, so a `"*"` handler is
|
|
962
|
+
* never compared with the document for one method.
|
|
963
|
+
*/
|
|
964
|
+
operationKey(semantics) {
|
|
965
|
+
if (semantics.method === null || semantics.path === null) {
|
|
966
|
+
return null;
|
|
967
|
+
}
|
|
968
|
+
return `${semantics.method.toUpperCase()} ${pathShape(semantics.path)}`;
|
|
692
969
|
},
|
|
693
970
|
sidesAgree(a, b) {
|
|
694
|
-
return methodsAgree(a.method, b.method);
|
|
971
|
+
return methodsAgree(a.method, b.method) && hostsAgree(a, b);
|
|
695
972
|
},
|
|
696
973
|
spansBuckets(semantics) {
|
|
697
|
-
return semantics.path !== null && pathSpansShapes(normalizePath(semantics.path));
|
|
974
|
+
return semantics.path !== null && (semantics.optionalFormat === true || pathSpansShapes(normalizePath(semantics.path)));
|
|
698
975
|
},
|
|
699
976
|
bucketsMeet(a, b) {
|
|
700
|
-
return a.path !== null && b.path !== null &&
|
|
977
|
+
return a.path !== null && b.path !== null && hostsAgree(a, b) && pathsMeet(servedPath(a, a.path), servedPath(b, b.path));
|
|
701
978
|
},
|
|
702
|
-
|
|
703
|
-
|
|
979
|
+
/**
|
|
980
|
+
* A catch-all route never takes a call whose host suss could not
|
|
981
|
+
* read, since nothing else ties that call to this app.
|
|
982
|
+
*/
|
|
983
|
+
pairRank(provider, consumer) {
|
|
984
|
+
if (provider.path === null || consumer.path === null) {
|
|
985
|
+
return [];
|
|
986
|
+
}
|
|
987
|
+
const route = normalizePath(provider.path);
|
|
988
|
+
if (hostUnread(consumer) && isCatchAll(route)) {
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
return requestRank(
|
|
992
|
+
servedPath(provider, provider.path),
|
|
993
|
+
normalizePath(consumer.path)
|
|
994
|
+
);
|
|
704
995
|
},
|
|
705
996
|
/**
|
|
706
|
-
*
|
|
707
|
-
*
|
|
708
|
-
*
|
|
997
|
+
* The identity key, with a missing half still readable: `ANY` when
|
|
998
|
+
* the source gave no method, and `?` when it gave no path. Null when
|
|
999
|
+
* both are missing, since then there is nothing to show.
|
|
709
1000
|
*/
|
|
710
1001
|
displayLabel(semantics) {
|
|
711
1002
|
if (semantics.method === null && semantics.path === null) {
|
|
@@ -716,11 +1007,11 @@ var restSemantics = defineBoundarySemantics({
|
|
|
716
1007
|
return `${method} ${path}`;
|
|
717
1008
|
},
|
|
718
1009
|
/**
|
|
719
|
-
*
|
|
720
|
-
*
|
|
721
|
-
*
|
|
722
|
-
*
|
|
723
|
-
*
|
|
1010
|
+
* With both halves present, the method goes through `methodsAgree`
|
|
1011
|
+
* (so a `"*"` route matches every method) and the path through
|
|
1012
|
+
* `routePathAdmits`. A route missing either half might still handle
|
|
1013
|
+
* the request, and this declaration cannot settle that, so the
|
|
1014
|
+
* result is `"unknown"`.
|
|
724
1015
|
*/
|
|
725
1016
|
servesRequest(semantics, method, path) {
|
|
726
1017
|
if (semantics.method === null || semantics.path === null) {
|
|
@@ -733,43 +1024,41 @@ var restSemantics = defineBoundarySemantics({
|
|
|
733
1024
|
},
|
|
734
1025
|
/**
|
|
735
1026
|
* A route the registration pattern admits is inside it. A route
|
|
736
|
-
*
|
|
737
|
-
*
|
|
1027
|
+
* without a path counts as outside, because nothing shows that the
|
|
1028
|
+
* pattern covers it.
|
|
738
1029
|
*/
|
|
739
1030
|
withinScope(semantics, scope) {
|
|
740
1031
|
return semantics.path !== null && routePathAdmits(scope, semantics.path);
|
|
741
1032
|
},
|
|
742
1033
|
/**
|
|
743
1034
|
* A call whose base URL the deployment fills in, resolved to the
|
|
744
|
-
* path it reaches
|
|
1035
|
+
* path it reaches, and a call to a host that can be the project's
|
|
1036
|
+
* own, with the host left out so it meets the route.
|
|
745
1037
|
*
|
|
746
|
-
* The source cannot settle
|
|
747
|
-
*
|
|
748
|
-
*
|
|
749
|
-
*
|
|
750
|
-
*
|
|
751
|
-
*
|
|
752
|
-
*
|
|
753
|
-
* Only a hole at the front is a base URL. A hole further along is a
|
|
754
|
-
* route parameter, and `/orders/{id}` means every id rather than a
|
|
755
|
-
* variable somebody set.
|
|
1038
|
+
* The source cannot settle a base URL on its own, so the adapter
|
|
1039
|
+
* leaves the hole in. `API_BASE` could be `http://backend.internal`,
|
|
1040
|
+
* and then the path is `/orders`. It could equally be `/api/v2`, and
|
|
1041
|
+
* then the path is `/api/v2/orders`. Filling in the deployed value
|
|
1042
|
+
* and reading the path back out gets the right one either way, and
|
|
1043
|
+
* a base the deployment sets counts as the app's own.
|
|
756
1044
|
*/
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
}
|
|
762
|
-
const base = deployment.setTo(reference);
|
|
763
|
-
if (base === null) {
|
|
1045
|
+
/** A listed host matches with or without the port the call gives. */
|
|
1046
|
+
withoutOwnHost(semantics, ownHosts) {
|
|
1047
|
+
const host = semantics.host?.toLowerCase();
|
|
1048
|
+
if (host === void 0) {
|
|
764
1049
|
return null;
|
|
765
1050
|
}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
1051
|
+
return ownHosts.has(host) || ownHosts.has(host.replace(/:\d+$/, "")) ? withoutHost(semantics) : null;
|
|
1052
|
+
},
|
|
1053
|
+
groundName(semantics, deployment) {
|
|
1054
|
+
const based = groundedBaseUrl(semantics, deployment);
|
|
1055
|
+
const grounded = based ?? semantics;
|
|
1056
|
+
const host = readHost(grounded);
|
|
1057
|
+
return host !== void 0 && hostCanBeOwn(host) ? withoutHost(grounded) : based;
|
|
769
1058
|
},
|
|
770
1059
|
nameReference: baseUrlReference,
|
|
771
1060
|
ruleBoundary: {
|
|
772
|
-
// "METHOD /path": one
|
|
1061
|
+
// "METHOD /path": one token, a space, then an absolute path.
|
|
773
1062
|
claims(raw) {
|
|
774
1063
|
return /^\S+ +\//.test(raw.trim());
|
|
775
1064
|
},
|
|
@@ -784,12 +1073,12 @@ var restSemantics = defineBoundarySemantics({
|
|
|
784
1073
|
});
|
|
785
1074
|
|
|
786
1075
|
// src/semantics/runtimeConfig.ts
|
|
787
|
-
var
|
|
1076
|
+
var import_zod9 = require("zod");
|
|
788
1077
|
|
|
789
1078
|
// src/deployableUnit.ts
|
|
790
|
-
var
|
|
791
|
-
var DeployableUnitSchema =
|
|
792
|
-
deploymentTarget:
|
|
1079
|
+
var import_zod8 = require("zod");
|
|
1080
|
+
var DeployableUnitSchema = import_zod8.z.object({
|
|
1081
|
+
deploymentTarget: import_zod8.z.enum([
|
|
793
1082
|
"lambda",
|
|
794
1083
|
"ecs-task",
|
|
795
1084
|
"container",
|
|
@@ -798,7 +1087,7 @@ var DeployableUnitSchema = import_zod7.z.object({
|
|
|
798
1087
|
]),
|
|
799
1088
|
// An empty name would agree with every other empty name, so a unit
|
|
800
1089
|
// with no name has to leave the field off instead.
|
|
801
|
-
instanceName:
|
|
1090
|
+
instanceName: import_zod8.z.string().min(1)
|
|
802
1091
|
});
|
|
803
1092
|
|
|
804
1093
|
// src/semantics/runtimeConfig.ts
|
|
@@ -806,14 +1095,14 @@ var RuntimeConfigSemanticsSchema = DeployableUnitSchema.partial({
|
|
|
806
1095
|
deploymentTarget: true,
|
|
807
1096
|
instanceName: true
|
|
808
1097
|
}).extend({
|
|
809
|
-
name:
|
|
1098
|
+
name: import_zod9.z.literal("runtime-config")
|
|
810
1099
|
});
|
|
811
1100
|
var runtimeConfigSemantics = defineBoundarySemantics({
|
|
812
1101
|
name: "runtime-config",
|
|
813
1102
|
schema: RuntimeConfigSemanticsSchema,
|
|
814
|
-
// service.name and cloud.resource_id
|
|
815
|
-
// instanceName is the
|
|
816
|
-
//
|
|
1103
|
+
// service.name and cloud.resource_id identify the same deployable, but
|
|
1104
|
+
// instanceName is the template's logical id, a different string from
|
|
1105
|
+
// both.
|
|
817
1106
|
semconv: {},
|
|
818
1107
|
behavior: {
|
|
819
1108
|
/** A process reads its config at startup, and nothing replies. */
|
|
@@ -822,46 +1111,53 @@ var runtimeConfigSemantics = defineBoundarySemantics({
|
|
|
822
1111
|
// comes from outside the code.
|
|
823
1112
|
leavesTheProcess: true,
|
|
824
1113
|
reportsUnpairedItself: false,
|
|
825
|
-
identityKey: () => null
|
|
1114
|
+
identityKey: () => null,
|
|
1115
|
+
// A reader sees which deployable's environment this is. Code that
|
|
1116
|
+
// reads a variable does not say which deployable runs it, so a read
|
|
1117
|
+
// has no name.
|
|
1118
|
+
displayLabel(semantics) {
|
|
1119
|
+
const instance = semantics.instanceName;
|
|
1120
|
+
return instance === void 0 ? null : `runtime-config:${instance}`;
|
|
1121
|
+
}
|
|
826
1122
|
}
|
|
827
1123
|
});
|
|
828
1124
|
|
|
829
1125
|
// src/semantics/storage.ts
|
|
830
|
-
var
|
|
831
|
-
var StorageSemanticsSchema =
|
|
832
|
-
name:
|
|
1126
|
+
var import_zod10 = require("zod");
|
|
1127
|
+
var StorageSemanticsSchema = import_zod10.z.object({
|
|
1128
|
+
name: import_zod10.z.literal("storage"),
|
|
833
1129
|
/**
|
|
834
|
-
*
|
|
835
|
-
*
|
|
836
|
-
*
|
|
837
|
-
* containers can share a name,
|
|
838
|
-
*
|
|
839
|
-
*
|
|
1130
|
+
* The storage system: an OpenTelemetry value such as `postgresql` or
|
|
1131
|
+
* `aws.dynamodb`, or suss's own word for one the conventions do not
|
|
1132
|
+
* cover, such as `s3`. Null when the reader could not work out which
|
|
1133
|
+
* system the source uses. Two products' containers can share a name,
|
|
1134
|
+
* and this field keeps them apart. The pairing pass decides how to
|
|
1135
|
+
* treat a null.
|
|
840
1136
|
*/
|
|
841
|
-
storageSystem:
|
|
1137
|
+
storageSystem: import_zod10.z.string().nullable(),
|
|
842
1138
|
/**
|
|
843
1139
|
* The ORM, schema, or deployment scope. A single-database setup uses
|
|
844
1140
|
* `"default"`, and a monorepo with several schemas gives each one its
|
|
845
1141
|
* own value.
|
|
846
1142
|
*/
|
|
847
|
-
scope:
|
|
1143
|
+
scope: import_zod10.z.string(),
|
|
848
1144
|
/**
|
|
849
|
-
* The table, bucket, collection
|
|
850
|
-
*
|
|
851
|
-
*
|
|
852
|
-
*
|
|
853
|
-
*
|
|
854
|
-
*
|
|
1145
|
+
* The table, bucket, collection or index, as the source declares it.
|
|
1146
|
+
* Null when the reader could not work out the container, and a null
|
|
1147
|
+
* container pairs with nothing, so it cannot match by accident of
|
|
1148
|
+
* shared source text. The string uses the boundary-name syntax (a
|
|
1149
|
+
* literal, a pattern with `{}` holes, or a reference), and only
|
|
1150
|
+
* `parseBoundaryName` should read it.
|
|
855
1151
|
*/
|
|
856
|
-
container:
|
|
1152
|
+
container: import_zod10.z.string().nullable(),
|
|
857
1153
|
/**
|
|
858
|
-
* A secondary way into the container, a DynamoDB global
|
|
859
|
-
* index or an Elasticsearch alias, each with its own key
|
|
860
|
-
* Null means the container's
|
|
1154
|
+
* A secondary way into the container, such as a DynamoDB global
|
|
1155
|
+
* secondary index or an Elasticsearch alias, each with its own key
|
|
1156
|
+
* fields. Null means the container's primary key. A query through an
|
|
861
1157
|
* index and a query through the table are different accesses, so
|
|
862
1158
|
* they pair separately.
|
|
863
1159
|
*/
|
|
864
|
-
accessPath:
|
|
1160
|
+
accessPath: import_zod10.z.string().nullable()
|
|
865
1161
|
});
|
|
866
1162
|
function containerReference(semantics) {
|
|
867
1163
|
return semantics.container === null ? null : referenceFromName(semantics.container);
|
|
@@ -871,21 +1167,21 @@ var storageSemantics = defineBoundarySemantics({
|
|
|
871
1167
|
schema: StorageSemanticsSchema,
|
|
872
1168
|
semconv: {
|
|
873
1169
|
storageSystem: { name: "db.system.name" },
|
|
874
|
-
// "default"
|
|
1170
|
+
// suss writes "default" when the source did not say which database.
|
|
875
1171
|
scope: { name: "db.namespace", placeholderValues: ["default"] },
|
|
876
1172
|
container: { name: "db.collection.name" }
|
|
877
|
-
//
|
|
878
|
-
//
|
|
1173
|
+
// The conventions have no attribute for a secondary index, so
|
|
1174
|
+
// accessPath is left out.
|
|
879
1175
|
},
|
|
880
1176
|
behavior: {
|
|
881
|
-
/** A query returns rows or items,
|
|
1177
|
+
/** A query returns rows or items, with no status. */
|
|
882
1178
|
exchangesHttpResponses: false,
|
|
883
1179
|
leavesTheProcess: true,
|
|
884
1180
|
reportsUnpairedItself: false,
|
|
885
1181
|
identityKey: () => null,
|
|
886
1182
|
displayLabel: storageLabel,
|
|
887
1183
|
/**
|
|
888
|
-
* A container the source
|
|
1184
|
+
* A container the source gives as a variable is whatever the
|
|
889
1185
|
* deployment sets that variable to. `{SUBSCRIBERS_TABLE}` in the
|
|
890
1186
|
* code and `prod-subscribers-v1` in the manifest are one table.
|
|
891
1187
|
*/
|
|
@@ -914,9 +1210,9 @@ function storageContainerLabel(semantics) {
|
|
|
914
1210
|
}
|
|
915
1211
|
|
|
916
1212
|
// src/semantics/unitInvocation.ts
|
|
917
|
-
var
|
|
1213
|
+
var import_zod11 = require("zod");
|
|
918
1214
|
var UnitInvocationSemanticsSchema = DeployableUnitSchema.extend({
|
|
919
|
-
name:
|
|
1215
|
+
name: import_zod11.z.literal("unit-invocation"),
|
|
920
1216
|
instanceName: DeployableUnitSchema.shape.instanceName.nullable()
|
|
921
1217
|
});
|
|
922
1218
|
function calleeReference(semantics) {
|
|
@@ -946,9 +1242,9 @@ var unitInvocationSemantics = defineBoundarySemantics({
|
|
|
946
1242
|
*/
|
|
947
1243
|
reportsUnpairedItself: false,
|
|
948
1244
|
/**
|
|
949
|
-
* A callee the source
|
|
950
|
-
*
|
|
951
|
-
*
|
|
1245
|
+
* A callee the source gives only as a variable has no name until the
|
|
1246
|
+
* deployment fills it in. It gets no key, since a key built from the
|
|
1247
|
+
* variable would pair only with itself.
|
|
952
1248
|
*/
|
|
953
1249
|
identityKey(semantics) {
|
|
954
1250
|
if (semantics.instanceName === null || namesNothing(semantics.instanceName)) {
|
|
@@ -969,11 +1265,10 @@ var unitInvocationSemantics = defineBoundarySemantics({
|
|
|
969
1265
|
);
|
|
970
1266
|
},
|
|
971
1267
|
/**
|
|
972
|
-
* A call that reads its callee's name
|
|
973
|
-
*
|
|
974
|
-
*
|
|
975
|
-
*
|
|
976
|
-
* id and neither side ever writes the other's string.
|
|
1268
|
+
* A call that reads its callee's name from the environment reaches
|
|
1269
|
+
* whichever resource the template wires that variable to. The name
|
|
1270
|
+
* is grounded to the logical id and not the deployed name, because
|
|
1271
|
+
* the invoked unit's own summary is keyed by the logical id.
|
|
977
1272
|
*/
|
|
978
1273
|
groundName(semantics, deployment) {
|
|
979
1274
|
const reference = calleeReference(semantics);
|
|
@@ -988,7 +1283,7 @@ var unitInvocationSemantics = defineBoundarySemantics({
|
|
|
988
1283
|
});
|
|
989
1284
|
|
|
990
1285
|
// src/semantics/registry.ts
|
|
991
|
-
var SemanticsSchema =
|
|
1286
|
+
var SemanticsSchema = import_zod12.z.discriminatedUnion("name", [
|
|
992
1287
|
restSemantics.schema,
|
|
993
1288
|
functionCallSemantics.schema,
|
|
994
1289
|
graphqlResolverSemantics.schema,
|
|
@@ -997,7 +1292,8 @@ var SemanticsSchema = import_zod11.z.discriminatedUnion("name", [
|
|
|
997
1292
|
storageSemantics.schema,
|
|
998
1293
|
messageBusSemantics.schema,
|
|
999
1294
|
metricSemantics.schema,
|
|
1000
|
-
unitInvocationSemantics.schema
|
|
1295
|
+
unitInvocationSemantics.schema,
|
|
1296
|
+
ioSemantics.schema
|
|
1001
1297
|
]);
|
|
1002
1298
|
var DEFINITIONS = [
|
|
1003
1299
|
restSemantics,
|
|
@@ -1008,97 +1304,103 @@ var DEFINITIONS = [
|
|
|
1008
1304
|
storageSemantics,
|
|
1009
1305
|
messageBusSemantics,
|
|
1010
1306
|
metricSemantics,
|
|
1011
|
-
unitInvocationSemantics
|
|
1307
|
+
unitInvocationSemantics,
|
|
1308
|
+
ioSemantics
|
|
1012
1309
|
];
|
|
1013
1310
|
var BY_NAME = new Map(
|
|
1014
1311
|
DEFINITIONS.map((d) => [d.name, d])
|
|
1015
1312
|
);
|
|
1016
1313
|
|
|
1017
1314
|
// src/schemas.ts
|
|
1018
|
-
var ConfidenceSourceSchema =
|
|
1315
|
+
var ConfidenceSourceSchema = import_zod13.z.enum([
|
|
1019
1316
|
"inferred_static",
|
|
1020
1317
|
"inferred_ai",
|
|
1021
1318
|
"declared",
|
|
1022
1319
|
"derived"
|
|
1023
1320
|
]);
|
|
1024
|
-
var ConfidenceLevelSchema =
|
|
1025
|
-
var CorroborationSchema =
|
|
1026
|
-
outcome:
|
|
1321
|
+
var ConfidenceLevelSchema = import_zod13.z.enum(["high", "medium", "low"]);
|
|
1322
|
+
var CorroborationSchema = import_zod13.z.object({
|
|
1323
|
+
outcome: import_zod13.z.enum(["observed", "refuted", "untested"]),
|
|
1027
1324
|
/** Executions that produced a verdict for this claim. */
|
|
1028
|
-
runs:
|
|
1325
|
+
runs: import_zod13.z.number(),
|
|
1029
1326
|
/** Present when refuted: the input and observation that disagreed. */
|
|
1030
|
-
counterexample:
|
|
1327
|
+
counterexample: import_zod13.z.unknown().optional(),
|
|
1031
1328
|
/** Present when untested: why no verdict was reachable. */
|
|
1032
|
-
reason:
|
|
1329
|
+
reason: import_zod13.z.string().optional()
|
|
1033
1330
|
});
|
|
1034
|
-
var ConfidenceInfoSchema =
|
|
1331
|
+
var ConfidenceInfoSchema = import_zod13.z.object({
|
|
1035
1332
|
source: ConfidenceSourceSchema,
|
|
1036
1333
|
level: ConfidenceLevelSchema,
|
|
1037
1334
|
corroboration: CorroborationSchema.optional()
|
|
1038
1335
|
});
|
|
1039
|
-
var SourceLocationSchema =
|
|
1040
|
-
file:
|
|
1336
|
+
var SourceLocationSchema = import_zod13.z.object({
|
|
1337
|
+
file: import_zod13.z.string(),
|
|
1041
1338
|
/** Line numbers, for a person reading the summary or an editor link. */
|
|
1042
|
-
range:
|
|
1339
|
+
range: import_zod13.z.object({ start: import_zod13.z.number(), end: import_zod13.z.number() }),
|
|
1043
1340
|
/**
|
|
1044
|
-
* Character offsets of the unit in its file. Identity and joins
|
|
1045
|
-
*
|
|
1046
|
-
*
|
|
1047
|
-
*
|
|
1048
|
-
* contract artifact's say.
|
|
1341
|
+
* Character offsets of the unit in its file. Identity and joins use
|
|
1342
|
+
* these, because two functions can share a line but never an offset
|
|
1343
|
+
* range. `range` gives the same place in lines. Absent on a summary
|
|
1344
|
+
* with no source position, such as one read from a contract.
|
|
1049
1345
|
*/
|
|
1050
|
-
span:
|
|
1051
|
-
exportName:
|
|
1346
|
+
span: import_zod13.z.object({ start: import_zod13.z.number(), end: import_zod13.z.number() }).optional(),
|
|
1347
|
+
exportName: import_zod13.z.string().nullable(),
|
|
1052
1348
|
/**
|
|
1053
|
-
* The project the extract
|
|
1054
|
-
*
|
|
1349
|
+
* The project the extract ran on, by the name the project gives
|
|
1350
|
+
* itself.
|
|
1055
1351
|
*
|
|
1056
1352
|
* Paths are relative to wherever the extract ran, so two services in
|
|
1057
|
-
* one repository both report `src/handlers.ts`, and merging their
|
|
1058
|
-
* summaries
|
|
1059
|
-
*
|
|
1353
|
+
* one repository can both report `src/handlers.ts`, and merging their
|
|
1354
|
+
* summaries would mix them up. This field tells them apart, and a
|
|
1355
|
+
* reader groups by it.
|
|
1356
|
+
*/
|
|
1357
|
+
workspace: import_zod13.z.string().optional(),
|
|
1358
|
+
/**
|
|
1359
|
+
* The module of the application the unit belongs to, by the name the
|
|
1360
|
+
* project's `suss.json` gives it. Set only when the project lists its
|
|
1361
|
+
* modules, and then on every unit whose file is under a module's root.
|
|
1060
1362
|
*/
|
|
1061
|
-
|
|
1363
|
+
module: import_zod13.z.string().optional()
|
|
1062
1364
|
});
|
|
1063
|
-
var BoundaryBindingSchema =
|
|
1064
|
-
transport:
|
|
1365
|
+
var BoundaryBindingSchema = import_zod13.z.object({
|
|
1366
|
+
transport: import_zod13.z.string(),
|
|
1065
1367
|
semantics: SemanticsSchema,
|
|
1066
|
-
recognition:
|
|
1368
|
+
recognition: import_zod13.z.string()
|
|
1067
1369
|
});
|
|
1068
1370
|
function typeDefinitionKey(ref) {
|
|
1069
1371
|
return ref.def ?? null;
|
|
1070
1372
|
}
|
|
1071
|
-
var TypeShapeSchema =
|
|
1072
|
-
() =>
|
|
1073
|
-
|
|
1074
|
-
type:
|
|
1075
|
-
properties:
|
|
1076
|
-
spreads:
|
|
1373
|
+
var TypeShapeSchema = import_zod13.z.lazy(
|
|
1374
|
+
() => import_zod13.z.discriminatedUnion("type", [
|
|
1375
|
+
import_zod13.z.object({
|
|
1376
|
+
type: import_zod13.z.literal("record"),
|
|
1377
|
+
properties: import_zod13.z.record(import_zod13.z.string(), TypeShapeSchema),
|
|
1378
|
+
spreads: import_zod13.z.array(import_zod13.z.object({ sourceText: import_zod13.z.string() })).optional()
|
|
1077
1379
|
}),
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
type:
|
|
1082
|
-
value:
|
|
1083
|
-
raw:
|
|
1380
|
+
import_zod13.z.object({ type: import_zod13.z.literal("dictionary"), values: TypeShapeSchema }),
|
|
1381
|
+
import_zod13.z.object({ type: import_zod13.z.literal("array"), items: TypeShapeSchema }),
|
|
1382
|
+
import_zod13.z.object({
|
|
1383
|
+
type: import_zod13.z.literal("literal"),
|
|
1384
|
+
value: import_zod13.z.union([import_zod13.z.string(), import_zod13.z.number(), import_zod13.z.boolean()]),
|
|
1385
|
+
raw: import_zod13.z.string().optional()
|
|
1084
1386
|
}),
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
type:
|
|
1093
|
-
variants:
|
|
1387
|
+
import_zod13.z.object({ type: import_zod13.z.literal("text") }),
|
|
1388
|
+
import_zod13.z.object({ type: import_zod13.z.literal("integer") }),
|
|
1389
|
+
import_zod13.z.object({ type: import_zod13.z.literal("number") }),
|
|
1390
|
+
import_zod13.z.object({ type: import_zod13.z.literal("boolean") }),
|
|
1391
|
+
import_zod13.z.object({ type: import_zod13.z.literal("null") }),
|
|
1392
|
+
import_zod13.z.object({ type: import_zod13.z.literal("undefined") }),
|
|
1393
|
+
import_zod13.z.object({
|
|
1394
|
+
type: import_zod13.z.literal("union"),
|
|
1395
|
+
variants: import_zod13.z.array(TypeShapeSchema)
|
|
1094
1396
|
}),
|
|
1095
|
-
|
|
1096
|
-
type:
|
|
1097
|
-
name:
|
|
1098
|
-
from:
|
|
1099
|
-
def:
|
|
1397
|
+
import_zod13.z.object({
|
|
1398
|
+
type: import_zod13.z.literal("ref"),
|
|
1399
|
+
name: import_zod13.z.string(),
|
|
1400
|
+
from: import_zod13.z.string().optional(),
|
|
1401
|
+
def: import_zod13.z.string().optional()
|
|
1100
1402
|
}),
|
|
1101
|
-
|
|
1403
|
+
import_zod13.z.object({ type: import_zod13.z.literal("unknown") })
|
|
1102
1404
|
])
|
|
1103
1405
|
);
|
|
1104
1406
|
var MAX_DEFINITION_DEPTH = 6;
|