@suss/ir-core 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -0
- package/dist/{chunk-WRRENYBR.js → chunk-HBAMPBAO.js} +162 -76
- package/dist/index.cjs +335 -124
- package/dist/index.d.cts +185 -47
- package/dist/index.d.ts +185 -47
- package/dist/index.js +156 -36
- package/dist/{schemas-BLS4PXwc.d.cts → schemas-CGR6qjFf.d.cts} +14 -6
- package/dist/{schemas-BLS4PXwc.d.ts → schemas-CGR6qjFf.d.ts} +14 -6
- package/dist/schemas.cjs +150 -73
- package/dist/schemas.d.cts +1 -1
- package/dist/schemas.d.ts +1 -1
- package/dist/schemas.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,6 +31,71 @@ 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
|
+
## Boundary names
|
|
35
|
+
|
|
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
|
+
|
|
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.
|
|
41
|
+
|
|
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.
|
|
43
|
+
|
|
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.
|
|
45
|
+
|
|
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.
|
|
67
|
+
|
|
68
|
+
## Words OpenTelemetry already has
|
|
69
|
+
|
|
70
|
+
A summary says what a unit can reach. A trace says what it did reach. Comparing the two is the point, and it only works if both sides spell a boundary the same way, so wherever OpenTelemetry's semantic conventions have a word for something in a binding, that is the word suss writes.
|
|
71
|
+
|
|
72
|
+
The values are theirs. A Postgres table is `postgresql`, not `postgres`; a DynamoDB table is `aws.dynamodb`; an SQS queue is `aws_sqs` and an SNS topic is `aws.sns`, spelled the two different ways the conventions spell them.
|
|
73
|
+
|
|
74
|
+
The field names are ours. Each protocol module says which attribute each of its fields goes under, and `semconvAttributes(binding)` reads a binding as the attributes a span would state:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
semconvAttributes(
|
|
78
|
+
storageBinding({
|
|
79
|
+
recognition: "prisma",
|
|
80
|
+
storageSystem: "postgresql",
|
|
81
|
+
scope: "orders",
|
|
82
|
+
container: "users",
|
|
83
|
+
}),
|
|
84
|
+
);
|
|
85
|
+
// { "db.system.name": "postgresql", "db.namespace": "orders",
|
|
86
|
+
// "db.collection.name": "users" }
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
A field appears in that projection only when the value suss writes is the value a span gets, so the comparison is byte for byte with nothing in between. Three kinds of field stay out of it:
|
|
90
|
+
|
|
91
|
+
- **A field the conventions have no attribute for.** A secondary index (`accessPath`) is one, and a GraphQL resolver's type and field are another, since the conventions describe the operation a client sent and not the resolver the server ran for one field of it.
|
|
92
|
+
- **A value suss writes where the source stated none.** `scope: "default"` means no source said which database, and a REST method of `"*"` means the route responds to every method. A span says neither.
|
|
93
|
+
- **The same thing under a different string.** `service.name` and `cloud.resource_id` both point at the deployable a `runtime-config` boundary belongs to, but `instanceName` is the deployment template's logical id, which is neither of those strings.
|
|
94
|
+
|
|
95
|
+
Where the conventions have no word at all, suss keeps its own: a store they never covered (`s3`, `gcs`, `r2`, `d1`, `cloudflare-kv`), a bus they never covered (`eventbridge`, `bullmq`, `nats`, the Cloudflare triggers), a metric's system and type, and every boundary nobody crosses at run time, which is a function call across a package boundary, a contract a template declares, and an intent. Those are the boundaries suss exists for, and no observability convention has a word for them.
|
|
96
|
+
|
|
97
|
+
Adding a protocol means filling in `semconv` on its definition, empty included, so the question gets answered rather than skipped.
|
|
98
|
+
|
|
34
99
|
## Where it fits in suss
|
|
35
100
|
|
|
36
101
|
Both `@suss/behavioral-ir` (what code does) and `@suss/intent-ir` (what the team meant) build on this package, so neither IR depends on the other. They describe boundaries in the same vocabulary, and suss compares them rather than merging them. `@suss/behavioral-ir` re-exports these primitives, so existing consumers keep importing them from there unchanged.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// src/schemas.ts
|
|
2
|
-
import { z as
|
|
2
|
+
import { z as z11 } from "zod";
|
|
3
3
|
|
|
4
4
|
// src/semantics/registry.ts
|
|
5
|
-
import { z as
|
|
5
|
+
import { z as z10 } from "zod";
|
|
6
6
|
|
|
7
7
|
// src/semantics/functionCall.ts
|
|
8
8
|
import { z } from "zod";
|
|
@@ -17,6 +17,9 @@ function fnIdentityKey(packageName, exportPath) {
|
|
|
17
17
|
function busIdentityKey(messageBus, subject) {
|
|
18
18
|
return `bus:${messageBus} ${subject}`;
|
|
19
19
|
}
|
|
20
|
+
function metricIdentityKey(metricSystem, metricType) {
|
|
21
|
+
return `metric:${metricSystem} ${metricType}`;
|
|
22
|
+
}
|
|
20
23
|
|
|
21
24
|
// src/semantics/definition.ts
|
|
22
25
|
function defineBoundarySemantics(definition) {
|
|
@@ -58,6 +61,9 @@ var FunctionCallSemanticsSchema = z.object({
|
|
|
58
61
|
var functionCallSemantics = defineBoundarySemantics({
|
|
59
62
|
name: "function-call",
|
|
60
63
|
schema: FunctionCallSemanticsSchema,
|
|
64
|
+
// A call that never leaves the process does not produce a span, and
|
|
65
|
+
// the conventions do not name one. All of it is ours.
|
|
66
|
+
semconv: {},
|
|
61
67
|
behavior: {
|
|
62
68
|
/** A call returns a value, which is not a status and a body. */
|
|
63
69
|
exchangesHttpResponses: false,
|
|
@@ -93,6 +99,10 @@ var GraphqlOperationSemanticsSchema = z2.object({
|
|
|
93
99
|
var graphqlOperationSemantics = defineBoundarySemantics({
|
|
94
100
|
name: "graphql-operation",
|
|
95
101
|
schema: GraphqlOperationSemanticsSchema,
|
|
102
|
+
semconv: {
|
|
103
|
+
operationName: { name: "graphql.operation.name" },
|
|
104
|
+
operationType: { name: "graphql.operation.type" }
|
|
105
|
+
},
|
|
96
106
|
behavior: {
|
|
97
107
|
/**
|
|
98
108
|
* An operation gets a data-and-errors document back, which the
|
|
@@ -126,6 +136,9 @@ var GraphqlResolverSemanticsSchema = z3.object({
|
|
|
126
136
|
var graphqlResolverSemantics = defineBoundarySemantics({
|
|
127
137
|
name: "graphql-resolver",
|
|
128
138
|
schema: GraphqlResolverSemanticsSchema,
|
|
139
|
+
// The conventions name the operation a client sent, not the
|
|
140
|
+
// resolver the server ran for one field of it.
|
|
141
|
+
semconv: {},
|
|
129
142
|
behavior: {
|
|
130
143
|
/** A resolver returns a field value, not a status and a body. */
|
|
131
144
|
exchangesHttpResponses: false,
|
|
@@ -174,8 +187,11 @@ function channelsPair(a, b) {
|
|
|
174
187
|
var MessageBusSemanticsSchema = z4.object({
|
|
175
188
|
name: z4.literal("message-bus"),
|
|
176
189
|
messageBus: z4.enum([
|
|
177
|
-
|
|
178
|
-
|
|
190
|
+
// The conventions spell the two AWS buses this way, one with an
|
|
191
|
+
// underscore and one with a dot, so these are their strings and
|
|
192
|
+
// not a typo.
|
|
193
|
+
"aws_sqs",
|
|
194
|
+
"aws.sns",
|
|
179
195
|
"s3",
|
|
180
196
|
"eventbridge",
|
|
181
197
|
"bullmq",
|
|
@@ -196,6 +212,10 @@ var MessageBusSemanticsSchema = z4.object({
|
|
|
196
212
|
var messageBusSemantics = defineBoundarySemantics({
|
|
197
213
|
name: "message-bus",
|
|
198
214
|
schema: MessageBusSemanticsSchema,
|
|
215
|
+
semconv: {
|
|
216
|
+
messageBus: { name: "messaging.system" },
|
|
217
|
+
channel: { name: "messaging.destination.name" }
|
|
218
|
+
},
|
|
199
219
|
behavior: {
|
|
200
220
|
/** A message goes onto the channel and nothing comes back. */
|
|
201
221
|
exchangesHttpResponses: false,
|
|
@@ -254,25 +274,58 @@ var messageBusSemantics = defineBoundarySemantics({
|
|
|
254
274
|
}
|
|
255
275
|
});
|
|
256
276
|
|
|
257
|
-
// src/semantics/
|
|
277
|
+
// src/semantics/metric.ts
|
|
258
278
|
import { z as z5 } from "zod";
|
|
259
|
-
var
|
|
260
|
-
name: z5.literal("
|
|
279
|
+
var MetricSemanticsSchema = z5.object({
|
|
280
|
+
name: z5.literal("metric"),
|
|
281
|
+
/** Which system the series lives in: cloud-monitoring, cloudwatch. */
|
|
282
|
+
metricSystem: z5.string(),
|
|
283
|
+
/**
|
|
284
|
+
* The type string the system knows the metric by, spelled the way
|
|
285
|
+
* both sides spell it. Null when the source states one this reader
|
|
286
|
+
* could not settle, as with a query it could not read: a metric
|
|
287
|
+
* nobody could name pairs with nothing rather than with whatever
|
|
288
|
+
* happens to share its source text.
|
|
289
|
+
*/
|
|
290
|
+
metricType: z5.string().nullable()
|
|
291
|
+
});
|
|
292
|
+
var metricSemantics = defineBoundarySemantics({
|
|
293
|
+
name: "metric",
|
|
294
|
+
schema: MetricSemanticsSchema,
|
|
295
|
+
// OpenTelemetry knows a metric by its instrument name, and has no
|
|
296
|
+
// attribute for the system it lives in or the type string it goes by.
|
|
297
|
+
semconv: {},
|
|
298
|
+
behavior: {
|
|
299
|
+
exchangesHttpResponses: false,
|
|
300
|
+
reportsUnpairedItself: false,
|
|
301
|
+
identityKey(semantics) {
|
|
302
|
+
if (semantics.metricType === null) {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
return metricIdentityKey(semantics.metricSystem, semantics.metricType);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// src/semantics/rest.ts
|
|
311
|
+
import { z as z6 } from "zod";
|
|
312
|
+
var RestSemanticsSchema = z6.object({
|
|
313
|
+
name: z6.literal("rest"),
|
|
261
314
|
/**
|
|
262
315
|
* Uppercase HTTP method ("GET", "POST", …), `"*"` for a handler that
|
|
263
316
|
* responds to every method, or null when this source does not say
|
|
264
317
|
* which method.
|
|
265
318
|
*/
|
|
266
|
-
method:
|
|
319
|
+
method: z6.string().min(1).nullable(),
|
|
267
320
|
/** Normalized route path ("/users/{id}"), or null when this source does not give one. */
|
|
268
|
-
path:
|
|
321
|
+
path: z6.string().min(1).nullable(),
|
|
269
322
|
/**
|
|
270
323
|
* Status codes the producing source explicitly declared (OpenAPI
|
|
271
324
|
* responses, CFN MethodResponses, ts-rest router statuses). They are
|
|
272
325
|
* kept here so the pairing layer can see them without unwrapping any
|
|
273
326
|
* metadata. Absent, or empty, for a source that was inferred.
|
|
274
327
|
*/
|
|
275
|
-
declaredResponses:
|
|
328
|
+
declaredResponses: z6.array(z6.number()).optional()
|
|
276
329
|
});
|
|
277
330
|
function normalizePath(path) {
|
|
278
331
|
let normalized = path.replace(/:([a-zA-Z_]\w*)/g, "{$1}");
|
|
@@ -311,6 +364,14 @@ function routePathAdmits(declaredPath, requestPath) {
|
|
|
311
364
|
var restSemantics = defineBoundarySemantics({
|
|
312
365
|
name: "rest",
|
|
313
366
|
schema: RestSemanticsSchema,
|
|
367
|
+
semconv: {
|
|
368
|
+
// A span states the one method a request used, so a route that
|
|
369
|
+
// responds to every method has nothing to compare.
|
|
370
|
+
method: { name: "http.request.method", placeholderValues: ["*"] },
|
|
371
|
+
path: { name: "http.route" }
|
|
372
|
+
// declaredResponses is a list, and http.response.status_code is
|
|
373
|
+
// the one status a request got back.
|
|
374
|
+
},
|
|
314
375
|
behavior: {
|
|
315
376
|
/** A request goes out, a status and a body come back. */
|
|
316
377
|
exchangesHttpResponses: true,
|
|
@@ -383,12 +444,12 @@ var restSemantics = defineBoundarySemantics({
|
|
|
383
444
|
});
|
|
384
445
|
|
|
385
446
|
// src/semantics/runtimeConfig.ts
|
|
386
|
-
import { z as
|
|
447
|
+
import { z as z8 } from "zod";
|
|
387
448
|
|
|
388
449
|
// src/deployableUnit.ts
|
|
389
|
-
import { z as
|
|
390
|
-
var DeployableUnitSchema =
|
|
391
|
-
deploymentTarget:
|
|
450
|
+
import { z as z7 } from "zod";
|
|
451
|
+
var DeployableUnitSchema = z7.object({
|
|
452
|
+
deploymentTarget: z7.enum([
|
|
392
453
|
"lambda",
|
|
393
454
|
"ecs-task",
|
|
394
455
|
"container",
|
|
@@ -397,7 +458,7 @@ var DeployableUnitSchema = z6.object({
|
|
|
397
458
|
]),
|
|
398
459
|
// An empty name would agree with every other empty name, so a unit
|
|
399
460
|
// with no name has to leave the field off instead.
|
|
400
|
-
instanceName:
|
|
461
|
+
instanceName: z7.string().min(1)
|
|
401
462
|
});
|
|
402
463
|
function ecsContainerInstanceName(taskDefinitionLogicalId, containerName) {
|
|
403
464
|
return `${taskDefinitionLogicalId}/${containerName}`;
|
|
@@ -405,11 +466,15 @@ function ecsContainerInstanceName(taskDefinitionLogicalId, containerName) {
|
|
|
405
466
|
|
|
406
467
|
// src/semantics/runtimeConfig.ts
|
|
407
468
|
var RuntimeConfigSemanticsSchema = DeployableUnitSchema.extend({
|
|
408
|
-
name:
|
|
469
|
+
name: z8.literal("runtime-config")
|
|
409
470
|
});
|
|
410
471
|
var runtimeConfigSemantics = defineBoundarySemantics({
|
|
411
472
|
name: "runtime-config",
|
|
412
473
|
schema: RuntimeConfigSemanticsSchema,
|
|
474
|
+
// service.name and cloud.resource_id name the same deployable, but
|
|
475
|
+
// instanceName is the deployment template's logical id, which is
|
|
476
|
+
// neither of those strings.
|
|
477
|
+
semconv: {},
|
|
413
478
|
behavior: {
|
|
414
479
|
/** A process reads its config at startup, and nothing replies. */
|
|
415
480
|
exchangesHttpResponses: false,
|
|
@@ -419,28 +484,30 @@ var runtimeConfigSemantics = defineBoundarySemantics({
|
|
|
419
484
|
});
|
|
420
485
|
|
|
421
486
|
// src/semantics/storage.ts
|
|
422
|
-
import { z as
|
|
423
|
-
var StorageSemanticsSchema =
|
|
424
|
-
name:
|
|
487
|
+
import { z as z9 } from "zod";
|
|
488
|
+
var StorageSemanticsSchema = z9.object({
|
|
489
|
+
name: z9.literal("storage"),
|
|
425
490
|
/**
|
|
426
|
-
* Which store this is:
|
|
427
|
-
*
|
|
428
|
-
*
|
|
491
|
+
* Which store this is: postgresql, mysql, aws.dynamodb, or our own
|
|
492
|
+
* word for one OpenTelemetry never named, such as s3. Two products'
|
|
493
|
+
* containers can share a name, so this keeps them apart.
|
|
429
494
|
*/
|
|
430
|
-
storageSystem:
|
|
495
|
+
storageSystem: z9.string(),
|
|
431
496
|
/**
|
|
432
497
|
* The ORM, schema, or deployment scope. A single-database setup uses
|
|
433
498
|
* `"default"`, and a monorepo with several schemas gives each one its
|
|
434
499
|
* own value.
|
|
435
500
|
*/
|
|
436
|
-
scope:
|
|
501
|
+
scope: z9.string(),
|
|
437
502
|
/**
|
|
438
503
|
* The table, bucket, collection, or index, as the source declares
|
|
439
504
|
* it, or null when the source states one this reader could not
|
|
440
505
|
* settle. A null container pairs with nothing, rather than with
|
|
441
|
-
* 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.
|
|
442
509
|
*/
|
|
443
|
-
container:
|
|
510
|
+
container: z9.string().nullable(),
|
|
444
511
|
/**
|
|
445
512
|
* A secondary way into the container, a DynamoDB global secondary
|
|
446
513
|
* index or an Elasticsearch alias, each with its own key fields.
|
|
@@ -448,11 +515,19 @@ var StorageSemanticsSchema = z8.object({
|
|
|
448
515
|
* index and a query through the table are different accesses, so
|
|
449
516
|
* they pair separately.
|
|
450
517
|
*/
|
|
451
|
-
accessPath:
|
|
518
|
+
accessPath: z9.string().nullable()
|
|
452
519
|
});
|
|
453
520
|
var storageSemantics = defineBoundarySemantics({
|
|
454
521
|
name: "storage",
|
|
455
522
|
schema: StorageSemanticsSchema,
|
|
523
|
+
semconv: {
|
|
524
|
+
storageSystem: { name: "db.system.name" },
|
|
525
|
+
// "default" is our word for a source that named no database.
|
|
526
|
+
scope: { name: "db.namespace", placeholderValues: ["default"] },
|
|
527
|
+
container: { name: "db.collection.name" }
|
|
528
|
+
// A secondary index has no attribute of its own, so accessPath
|
|
529
|
+
// stays our word.
|
|
530
|
+
},
|
|
456
531
|
behavior: {
|
|
457
532
|
/** A query returns rows or items, not a status and a body. */
|
|
458
533
|
exchangesHttpResponses: false,
|
|
@@ -462,14 +537,15 @@ var storageSemantics = defineBoundarySemantics({
|
|
|
462
537
|
});
|
|
463
538
|
|
|
464
539
|
// src/semantics/registry.ts
|
|
465
|
-
var SemanticsSchema =
|
|
540
|
+
var SemanticsSchema = z10.discriminatedUnion("name", [
|
|
466
541
|
restSemantics.schema,
|
|
467
542
|
functionCallSemantics.schema,
|
|
468
543
|
graphqlResolverSemantics.schema,
|
|
469
544
|
graphqlOperationSemantics.schema,
|
|
470
545
|
runtimeConfigSemantics.schema,
|
|
471
546
|
storageSemantics.schema,
|
|
472
|
-
messageBusSemantics.schema
|
|
547
|
+
messageBusSemantics.schema,
|
|
548
|
+
metricSemantics.schema
|
|
473
549
|
]);
|
|
474
550
|
var DEFINITIONS = [
|
|
475
551
|
restSemantics,
|
|
@@ -478,48 +554,55 @@ var DEFINITIONS = [
|
|
|
478
554
|
graphqlOperationSemantics,
|
|
479
555
|
runtimeConfigSemantics,
|
|
480
556
|
storageSemantics,
|
|
481
|
-
messageBusSemantics
|
|
557
|
+
messageBusSemantics,
|
|
558
|
+
metricSemantics
|
|
482
559
|
];
|
|
483
560
|
var BY_NAME = new Map(
|
|
484
561
|
DEFINITIONS.map((d) => [d.name, d])
|
|
485
562
|
);
|
|
486
563
|
function behaviorOf(semantics) {
|
|
487
|
-
|
|
564
|
+
return definitionFor(semantics.name).behavior;
|
|
565
|
+
}
|
|
566
|
+
function definitionFor(name) {
|
|
567
|
+
const definition = BY_NAME.get(name);
|
|
488
568
|
if (definition === void 0) {
|
|
489
|
-
throw new Error(`no boundary
|
|
569
|
+
throw new Error(`no boundary definition for semantics "${name}"`);
|
|
490
570
|
}
|
|
491
|
-
return definition
|
|
571
|
+
return definition;
|
|
572
|
+
}
|
|
573
|
+
function semconvMappingOf(semantics) {
|
|
574
|
+
return definitionFor(semantics.name).semconv;
|
|
492
575
|
}
|
|
493
576
|
function allBehaviors() {
|
|
494
577
|
return DEFINITIONS.map((d) => d.behavior);
|
|
495
578
|
}
|
|
496
579
|
|
|
497
580
|
// src/schemas.ts
|
|
498
|
-
var ConfidenceSourceSchema =
|
|
581
|
+
var ConfidenceSourceSchema = z11.enum([
|
|
499
582
|
"inferred_static",
|
|
500
583
|
"inferred_ai",
|
|
501
584
|
"declared",
|
|
502
585
|
"derived"
|
|
503
586
|
]);
|
|
504
|
-
var ConfidenceLevelSchema =
|
|
505
|
-
var CorroborationSchema =
|
|
506
|
-
outcome:
|
|
587
|
+
var ConfidenceLevelSchema = z11.enum(["high", "medium", "low"]);
|
|
588
|
+
var CorroborationSchema = z11.object({
|
|
589
|
+
outcome: z11.enum(["observed", "refuted", "untested"]),
|
|
507
590
|
/** Executions that produced a verdict for this claim. */
|
|
508
|
-
runs:
|
|
591
|
+
runs: z11.number(),
|
|
509
592
|
/** Present when refuted: the input and observation that disagreed. */
|
|
510
|
-
counterexample:
|
|
593
|
+
counterexample: z11.unknown().optional(),
|
|
511
594
|
/** Present when untested: why no verdict was reachable. */
|
|
512
|
-
reason:
|
|
595
|
+
reason: z11.string().optional()
|
|
513
596
|
});
|
|
514
|
-
var ConfidenceInfoSchema =
|
|
597
|
+
var ConfidenceInfoSchema = z11.object({
|
|
515
598
|
source: ConfidenceSourceSchema,
|
|
516
599
|
level: ConfidenceLevelSchema,
|
|
517
600
|
corroboration: CorroborationSchema.optional()
|
|
518
601
|
});
|
|
519
|
-
var SourceLocationSchema =
|
|
520
|
-
file:
|
|
521
|
-
range:
|
|
522
|
-
exportName:
|
|
602
|
+
var SourceLocationSchema = z11.object({
|
|
603
|
+
file: z11.string(),
|
|
604
|
+
range: z11.object({ start: z11.number(), end: z11.number() }),
|
|
605
|
+
exportName: z11.string().nullable(),
|
|
523
606
|
/**
|
|
524
607
|
* The project the extract was pointed at, by the name that project
|
|
525
608
|
* calls itself.
|
|
@@ -529,47 +612,47 @@ var SourceLocationSchema = z10.object({
|
|
|
529
612
|
* summaries puts them on top of each other. This field is what tells
|
|
530
613
|
* them apart, and what a reader groups by.
|
|
531
614
|
*/
|
|
532
|
-
workspace:
|
|
615
|
+
workspace: z11.string().optional()
|
|
533
616
|
});
|
|
534
|
-
var BoundaryBindingSchema =
|
|
535
|
-
transport:
|
|
617
|
+
var BoundaryBindingSchema = z11.object({
|
|
618
|
+
transport: z11.string(),
|
|
536
619
|
semantics: SemanticsSchema,
|
|
537
|
-
recognition:
|
|
620
|
+
recognition: z11.string()
|
|
538
621
|
});
|
|
539
622
|
function typeDefinitionKey(ref) {
|
|
540
623
|
return ref.def ?? null;
|
|
541
624
|
}
|
|
542
|
-
var TypeShapeSchema =
|
|
543
|
-
() =>
|
|
544
|
-
|
|
545
|
-
type:
|
|
546
|
-
properties:
|
|
547
|
-
spreads:
|
|
625
|
+
var TypeShapeSchema = z11.lazy(
|
|
626
|
+
() => z11.discriminatedUnion("type", [
|
|
627
|
+
z11.object({
|
|
628
|
+
type: z11.literal("record"),
|
|
629
|
+
properties: z11.record(z11.string(), TypeShapeSchema),
|
|
630
|
+
spreads: z11.array(z11.object({ sourceText: z11.string() })).optional()
|
|
548
631
|
}),
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
type:
|
|
553
|
-
value:
|
|
554
|
-
raw:
|
|
632
|
+
z11.object({ type: z11.literal("dictionary"), values: TypeShapeSchema }),
|
|
633
|
+
z11.object({ type: z11.literal("array"), items: TypeShapeSchema }),
|
|
634
|
+
z11.object({
|
|
635
|
+
type: z11.literal("literal"),
|
|
636
|
+
value: z11.union([z11.string(), z11.number(), z11.boolean()]),
|
|
637
|
+
raw: z11.string().optional()
|
|
555
638
|
}),
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
type:
|
|
564
|
-
variants:
|
|
639
|
+
z11.object({ type: z11.literal("text") }),
|
|
640
|
+
z11.object({ type: z11.literal("integer") }),
|
|
641
|
+
z11.object({ type: z11.literal("number") }),
|
|
642
|
+
z11.object({ type: z11.literal("boolean") }),
|
|
643
|
+
z11.object({ type: z11.literal("null") }),
|
|
644
|
+
z11.object({ type: z11.literal("undefined") }),
|
|
645
|
+
z11.object({
|
|
646
|
+
type: z11.literal("union"),
|
|
647
|
+
variants: z11.array(TypeShapeSchema)
|
|
565
648
|
}),
|
|
566
|
-
|
|
567
|
-
type:
|
|
568
|
-
name:
|
|
569
|
-
from:
|
|
570
|
-
def:
|
|
649
|
+
z11.object({
|
|
650
|
+
type: z11.literal("ref"),
|
|
651
|
+
name: z11.string(),
|
|
652
|
+
from: z11.string().optional(),
|
|
653
|
+
def: z11.string().optional()
|
|
571
654
|
}),
|
|
572
|
-
|
|
655
|
+
z11.object({ type: z11.literal("unknown") })
|
|
573
656
|
])
|
|
574
657
|
);
|
|
575
658
|
var MAX_DEFINITION_DEPTH = 6;
|
|
@@ -618,6 +701,7 @@ export {
|
|
|
618
701
|
gqlIdentityKey,
|
|
619
702
|
fnIdentityKey,
|
|
620
703
|
busIdentityKey,
|
|
704
|
+
metricIdentityKey,
|
|
621
705
|
FunctionCallSemanticsSchema,
|
|
622
706
|
GraphqlResolverSemanticsSchema,
|
|
623
707
|
parseChannel,
|
|
@@ -625,6 +709,7 @@ export {
|
|
|
625
709
|
busesAgree,
|
|
626
710
|
channelsPair,
|
|
627
711
|
MessageBusSemanticsSchema,
|
|
712
|
+
MetricSemanticsSchema,
|
|
628
713
|
RestSemanticsSchema,
|
|
629
714
|
normalizePath,
|
|
630
715
|
methodsAgree,
|
|
@@ -635,6 +720,7 @@ export {
|
|
|
635
720
|
StorageSemanticsSchema,
|
|
636
721
|
SemanticsSchema,
|
|
637
722
|
behaviorOf,
|
|
723
|
+
semconvMappingOf,
|
|
638
724
|
allBehaviors,
|
|
639
725
|
ConfidenceSourceSchema,
|
|
640
726
|
ConfidenceLevelSchema,
|