@suss/extractor 0.1.0 → 0.2.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/index.cjs +44 -1
- package/dist/index.d.cts +85 -24
- package/dist/index.d.ts +85 -24
- package/dist/index.js +44 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -95,12 +95,14 @@ function assembleSummary(raw, options = DEFAULT_OPTIONS) {
|
|
|
95
95
|
identity: {
|
|
96
96
|
name: raw.identity.name,
|
|
97
97
|
exportPath: raw.identity.exportPath,
|
|
98
|
-
boundaryBinding: raw.boundaryBinding ?? null
|
|
98
|
+
boundaryBinding: raw.boundaryBinding ?? null,
|
|
99
|
+
...raw.deployableUnit !== void 0 ? { deployableUnit: raw.deployableUnit } : {}
|
|
99
100
|
},
|
|
100
101
|
inputs,
|
|
101
102
|
transitions,
|
|
102
103
|
gaps,
|
|
103
104
|
confidence,
|
|
105
|
+
...raw.definitions !== void 0 && raw.definitions !== null ? { definitions: raw.definitions } : {},
|
|
104
106
|
...metadata !== null ? { metadata } : {}
|
|
105
107
|
};
|
|
106
108
|
}
|
|
@@ -161,6 +163,23 @@ function detectGaps(raw, transitions, options) {
|
|
|
161
163
|
description: count === 1 ? "One return in this function matches none of the terminal shapes this pack looks for, so what it produces is not described here" : `${count} returns in this function match none of the terminal shapes this pack looks for, so what they produce is not described here`
|
|
162
164
|
});
|
|
163
165
|
}
|
|
166
|
+
if (raw.unreadBinding !== void 0) {
|
|
167
|
+
gaps.push({
|
|
168
|
+
type: "unreadOutcome",
|
|
169
|
+
conditions: [],
|
|
170
|
+
consequence: "unknown",
|
|
171
|
+
description: raw.unreadBinding
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
const unreadBody = describeUnreadBody(raw);
|
|
175
|
+
if (unreadBody !== null) {
|
|
176
|
+
gaps.push({
|
|
177
|
+
type: "unreadOutcome",
|
|
178
|
+
conditions: [],
|
|
179
|
+
consequence: "unknown",
|
|
180
|
+
description: unreadBody
|
|
181
|
+
});
|
|
182
|
+
}
|
|
164
183
|
if (raw.declaredContract) {
|
|
165
184
|
const producedStatuses = new Set(
|
|
166
185
|
transitions.flatMap((t) => {
|
|
@@ -200,10 +219,34 @@ function detectGaps(raw, transitions, options) {
|
|
|
200
219
|
}
|
|
201
220
|
return gaps;
|
|
202
221
|
}
|
|
222
|
+
function bodyWentUnread(raw) {
|
|
223
|
+
if (raw.bodyContent === "absent" || raw.bodyContent === "elsewhere") {
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
return raw.bodyContent === "statements" && raw.branches.length === 0;
|
|
227
|
+
}
|
|
228
|
+
function describeUnreadBody(raw) {
|
|
229
|
+
if (!bodyWentUnread(raw)) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
if (raw.bodyContent === "absent") {
|
|
233
|
+
return "This unit is a declaration with no body behind it, so nothing about what it does was read here";
|
|
234
|
+
}
|
|
235
|
+
if (raw.bodyContent === "elsewhere") {
|
|
236
|
+
return "The handler on this boundary comes from outside the code that registers it, so nothing about what it does was read here";
|
|
237
|
+
}
|
|
238
|
+
if ((raw.unmatchedReturns ?? 0) > 0) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
return "Nothing this unit's body does matches a shape this pack looks for, so what it does is not described here";
|
|
242
|
+
}
|
|
203
243
|
function assessConfidence(raw) {
|
|
204
244
|
if ((raw.unmatchedReturns ?? 0) > 0) {
|
|
205
245
|
return { source: "inferred_static", level: "low" };
|
|
206
246
|
}
|
|
247
|
+
if (bodyWentUnread(raw)) {
|
|
248
|
+
return { source: "inferred_static", level: "low" };
|
|
249
|
+
}
|
|
207
250
|
let total = 0;
|
|
208
251
|
let opaque = 0;
|
|
209
252
|
for (const branch of raw.branches) {
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect, MessageBusSemantics, Predicate, TypeShape, RenderNode, CodeUnitKind, BoundaryBinding, BehavioralSummary, ConfidenceInfo, Transition, Gap, Input, Output } from '@suss/behavioral-ir';
|
|
1
|
+
import { Effect, MessageBusSemantics, DeployableUnit, Predicate, TypeShape, RenderNode, CodeUnitKind, BoundaryBinding, BehavioralSummary, ConfidenceInfo, Transition, Gap, Input, Output } from '@suss/behavioral-ir';
|
|
2
2
|
|
|
3
3
|
type DiscoveryMatch = {
|
|
4
4
|
type: "namedExport";
|
|
@@ -211,30 +211,42 @@ type DiscoveryMatch = {
|
|
|
211
211
|
* The adapter populates `DiscoveredUnit.resolverInfo` so the
|
|
212
212
|
* produced binding carries `graphql-resolver(typeName, fieldName)`.
|
|
213
213
|
* `typeName` resolves from the class decorator's first argument
|
|
214
|
-
* (`@Resolver(() => User)` → `"User"`)
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
* the method name.
|
|
214
|
+
* (`@Resolver(() => User)` → `"User"`), and from
|
|
215
|
+
* `methodDecoratorTypeMap` when the class decorator has no
|
|
216
|
+
* argument. `fieldName` reads the method decorator's `{ name }`
|
|
217
|
+
* option override when present, otherwise the method name.
|
|
219
218
|
*/
|
|
220
219
|
type: "decoratedMethod";
|
|
221
220
|
/**
|
|
222
221
|
* The module a pack-recognised decorator must be imported from
|
|
223
222
|
* for discovery to fire. Codebases sometimes wrap framework
|
|
224
|
-
* decorators in their own re-exports
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
* accepted modules and any one match suffices.
|
|
223
|
+
* decorators in their own re-exports, composing the framework's
|
|
224
|
+
* decorator with extra metadata. When that happens, the
|
|
225
|
+
* import-module gate would miss them; pass an array of accepted
|
|
226
|
+
* modules and any one match suffices.
|
|
229
227
|
*/
|
|
230
228
|
importModule: string | string[];
|
|
231
229
|
/**
|
|
232
230
|
* Class decorator names to recognise. The first entry that
|
|
233
231
|
* appears on a class wins for typeName extraction (the rest are
|
|
234
|
-
* fallbacks for codebases with multiple wrapper styles).
|
|
232
|
+
* fallbacks for codebases with multiple wrapper styles). A pack
|
|
233
|
+
* ships only what its own framework declares here and takes a
|
|
234
|
+
* project's wrappers through its options.
|
|
235
235
|
*/
|
|
236
236
|
classDecorators: string[];
|
|
237
237
|
methodDecorators: string[];
|
|
238
|
+
/**
|
|
239
|
+
* Method decorator name → the type its field hangs off when the
|
|
240
|
+
* class decorator names none. NestJS puts `@Query` on the root
|
|
241
|
+
* `Query` type and `@Mutation` on `Mutation` whatever the class
|
|
242
|
+
* says, so those two answer for themselves.
|
|
243
|
+
*
|
|
244
|
+
* A decorator the map leaves out has no answer of its own, and a
|
|
245
|
+
* class that names no type leaves the field's owner unread. The
|
|
246
|
+
* binding then names no type and pairs with nothing, rather than
|
|
247
|
+
* claiming a field the schema does not have.
|
|
248
|
+
*/
|
|
249
|
+
methodDecoratorTypeMap: Record<string, string>;
|
|
238
250
|
} | {
|
|
239
251
|
/**
|
|
240
252
|
* NestJS-style REST controller discovery. Class decorated with
|
|
@@ -248,10 +260,9 @@ type DiscoveryMatch = {
|
|
|
248
260
|
* Same wrapper-decorator tolerance as `decoratedMethod`: the
|
|
249
261
|
* import-module gate fires on at least one method-route
|
|
250
262
|
* decorator from the framework module, but class decorators
|
|
251
|
-
* match by name only so project-internal
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
* config.
|
|
263
|
+
* match by name only, so a project-internal wrapper composing
|
|
264
|
+
* the framework's decorator matches once the project names it
|
|
265
|
+
* in the pack's options.
|
|
255
266
|
*
|
|
256
267
|
* The adapter populates `DiscoveredUnit.routeInfo` so the
|
|
257
268
|
* produced binding carries `rest(method, path)`.
|
|
@@ -685,14 +696,18 @@ interface TransparentWrapper {
|
|
|
685
696
|
interface PatternPack {
|
|
686
697
|
name: string;
|
|
687
698
|
/**
|
|
688
|
-
* Pack version stamp
|
|
699
|
+
* Pack version stamp, which feeds the cache invalidation key. Bump on
|
|
689
700
|
* any change that affects discovered units / extracted summaries.
|
|
690
|
-
* Format is opaque to the adapter
|
|
691
|
-
*
|
|
692
|
-
*
|
|
693
|
-
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
701
|
+
* Format is opaque to the adapter, so semver or a content hash both
|
|
702
|
+
* work.
|
|
703
|
+
*
|
|
704
|
+
* Optional, because whoever loads the pack knows more about it than
|
|
705
|
+
* the pack does. The CLI folds a hash of the file it loaded and of
|
|
706
|
+
* the config it passed into this stamp, so a pack run through the CLI
|
|
707
|
+
* invalidates on an edit whether or not it declares a version. A host
|
|
708
|
+
* that builds packs some other way carries that responsibility
|
|
709
|
+
* itself. A pack with nothing to stamp reads as `"unset"`, and warm
|
|
710
|
+
* caches will answer for code that has since changed.
|
|
696
711
|
*/
|
|
697
712
|
version?: string;
|
|
698
713
|
languages: string[];
|
|
@@ -981,6 +996,8 @@ interface DiscoveredCustomUnit {
|
|
|
981
996
|
messageBus: MessageBusSemantics["messageBus"];
|
|
982
997
|
channel: string;
|
|
983
998
|
};
|
|
999
|
+
/** The thing that gets deployed and runs this unit, when known. */
|
|
1000
|
+
deployableUnit?: DeployableUnit;
|
|
984
1001
|
/**
|
|
985
1002
|
* Metadata merged onto the resulting summary's `metadata` field.
|
|
986
1003
|
*/
|
|
@@ -1264,7 +1281,30 @@ interface RawDeclaredContract {
|
|
|
1264
1281
|
*/
|
|
1265
1282
|
provenance?: "derived" | "independent";
|
|
1266
1283
|
}
|
|
1284
|
+
/**
|
|
1285
|
+
* What sits where a unit's body should be.
|
|
1286
|
+
*
|
|
1287
|
+
* - "absent": there is no body behind this declaration (an overload
|
|
1288
|
+
* signature, an ambient declaration, an abstract method), so the
|
|
1289
|
+
* pack read nothing about what the unit does.
|
|
1290
|
+
* - "empty": a body with nothing in it. A summary that says nothing
|
|
1291
|
+
* about it has described it completely.
|
|
1292
|
+
* - "statements": a body with work in it, including a concise
|
|
1293
|
+
* arrow's expression. A summary that says nothing about it has
|
|
1294
|
+
* described none of it.
|
|
1295
|
+
* - "elsewhere": the unit's body is not in what this run read. A
|
|
1296
|
+
* route registered with a handler the caller supplies announces a
|
|
1297
|
+
* boundary and names no function to look in, and the boundary is
|
|
1298
|
+
* worth reporting with nothing behind it.
|
|
1299
|
+
*/
|
|
1300
|
+
type BodyContent = "absent" | "empty" | "statements" | "elsewhere";
|
|
1267
1301
|
interface RawCodeStructure {
|
|
1302
|
+
/**
|
|
1303
|
+
* The types this unit's shapes name rather than spell out. Carried
|
|
1304
|
+
* onto the summary as `definitions`, so a reader following a name has
|
|
1305
|
+
* somewhere to look.
|
|
1306
|
+
*/
|
|
1307
|
+
definitions?: Record<string, TypeShape> | null;
|
|
1268
1308
|
identity: {
|
|
1269
1309
|
name: string;
|
|
1270
1310
|
kind: CodeUnitKind;
|
|
@@ -1283,6 +1323,8 @@ interface RawCodeStructure {
|
|
|
1283
1323
|
* helpers, pure utilities, and anything the adapter can't place.
|
|
1284
1324
|
*/
|
|
1285
1325
|
boundaryBinding: BoundaryBinding | null;
|
|
1326
|
+
/** The thing that gets deployed and runs this code, when known. */
|
|
1327
|
+
deployableUnit?: DeployableUnit;
|
|
1286
1328
|
parameters: RawParameter[];
|
|
1287
1329
|
branches: RawBranch[];
|
|
1288
1330
|
/**
|
|
@@ -1292,6 +1334,14 @@ interface RawCodeStructure {
|
|
|
1292
1334
|
* saying it does nothing.
|
|
1293
1335
|
*/
|
|
1294
1336
|
unmatchedReturns?: number;
|
|
1337
|
+
/**
|
|
1338
|
+
* What the adapter found where the unit's body should be. A summary
|
|
1339
|
+
* with nothing in it means one of two different things, and only the
|
|
1340
|
+
* adapter can say which: a body nobody could read, or a body with
|
|
1341
|
+
* nothing in it to read. Adapters that do not say leave this unset,
|
|
1342
|
+
* and everything downstream treats the summary as it did before.
|
|
1343
|
+
*/
|
|
1344
|
+
bodyContent?: BodyContent;
|
|
1295
1345
|
dependencyCalls: RawDependencyCall[];
|
|
1296
1346
|
declaredContract: RawDeclaredContract | null;
|
|
1297
1347
|
/**
|
|
@@ -1358,6 +1408,17 @@ interface RawCodeStructure {
|
|
|
1358
1408
|
reference: string;
|
|
1359
1409
|
reason: string;
|
|
1360
1410
|
};
|
|
1411
|
+
/**
|
|
1412
|
+
* Set when the source does not state part of the boundary this unit
|
|
1413
|
+
* sits on. The binding still goes out with that part left empty, so
|
|
1414
|
+
* the unit pairs with nothing rather than with whatever a guess named,
|
|
1415
|
+
* and this sentence tells a reader which part is missing and why.
|
|
1416
|
+
*
|
|
1417
|
+
* A statement about the reading rather than about the code, so it
|
|
1418
|
+
* lands as an `unreadOutcome` gap and no checker holds it against the
|
|
1419
|
+
* unit.
|
|
1420
|
+
*/
|
|
1421
|
+
unreadBinding?: string;
|
|
1361
1422
|
}
|
|
1362
1423
|
interface ExtractorOptions {
|
|
1363
1424
|
gapHandling: "strict" | "permissive" | "silent";
|
|
@@ -1370,4 +1431,4 @@ declare function terminalToOutput(terminal: RawTerminal): Output;
|
|
|
1370
1431
|
declare function effectToIR(effect: RawEffect): Effect;
|
|
1371
1432
|
declare function paramToInput(param: RawParameter): Input;
|
|
1372
1433
|
|
|
1373
|
-
export { type AccessRecognizer, type BindingExtraction, type ContractPattern, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type ExtractorOptions, type InputMappingPattern, type InvocationRecognizer, type PatternPack, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type ResponsePropertyMapping, type ResponsePropertyMeaning, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type TransparentWrapper, assembleSummary, assessConfidence, detectGaps, effectToIR, httpRouteDiscovery, makeTransitionId, paramToInput, terminalToOutput };
|
|
1434
|
+
export { type AccessRecognizer, type BindingExtraction, type BodyContent, type ContractPattern, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type ExtractorOptions, type InputMappingPattern, type InvocationRecognizer, type PatternPack, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type ResponsePropertyMapping, type ResponsePropertyMeaning, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type TransparentWrapper, assembleSummary, assessConfidence, detectGaps, effectToIR, httpRouteDiscovery, makeTransitionId, paramToInput, terminalToOutput };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect, MessageBusSemantics, Predicate, TypeShape, RenderNode, CodeUnitKind, BoundaryBinding, BehavioralSummary, ConfidenceInfo, Transition, Gap, Input, Output } from '@suss/behavioral-ir';
|
|
1
|
+
import { Effect, MessageBusSemantics, DeployableUnit, Predicate, TypeShape, RenderNode, CodeUnitKind, BoundaryBinding, BehavioralSummary, ConfidenceInfo, Transition, Gap, Input, Output } from '@suss/behavioral-ir';
|
|
2
2
|
|
|
3
3
|
type DiscoveryMatch = {
|
|
4
4
|
type: "namedExport";
|
|
@@ -211,30 +211,42 @@ type DiscoveryMatch = {
|
|
|
211
211
|
* The adapter populates `DiscoveredUnit.resolverInfo` so the
|
|
212
212
|
* produced binding carries `graphql-resolver(typeName, fieldName)`.
|
|
213
213
|
* `typeName` resolves from the class decorator's first argument
|
|
214
|
-
* (`@Resolver(() => User)` → `"User"`)
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
* the method name.
|
|
214
|
+
* (`@Resolver(() => User)` → `"User"`), and from
|
|
215
|
+
* `methodDecoratorTypeMap` when the class decorator has no
|
|
216
|
+
* argument. `fieldName` reads the method decorator's `{ name }`
|
|
217
|
+
* option override when present, otherwise the method name.
|
|
219
218
|
*/
|
|
220
219
|
type: "decoratedMethod";
|
|
221
220
|
/**
|
|
222
221
|
* The module a pack-recognised decorator must be imported from
|
|
223
222
|
* for discovery to fire. Codebases sometimes wrap framework
|
|
224
|
-
* decorators in their own re-exports
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
* accepted modules and any one match suffices.
|
|
223
|
+
* decorators in their own re-exports, composing the framework's
|
|
224
|
+
* decorator with extra metadata. When that happens, the
|
|
225
|
+
* import-module gate would miss them; pass an array of accepted
|
|
226
|
+
* modules and any one match suffices.
|
|
229
227
|
*/
|
|
230
228
|
importModule: string | string[];
|
|
231
229
|
/**
|
|
232
230
|
* Class decorator names to recognise. The first entry that
|
|
233
231
|
* appears on a class wins for typeName extraction (the rest are
|
|
234
|
-
* fallbacks for codebases with multiple wrapper styles).
|
|
232
|
+
* fallbacks for codebases with multiple wrapper styles). A pack
|
|
233
|
+
* ships only what its own framework declares here and takes a
|
|
234
|
+
* project's wrappers through its options.
|
|
235
235
|
*/
|
|
236
236
|
classDecorators: string[];
|
|
237
237
|
methodDecorators: string[];
|
|
238
|
+
/**
|
|
239
|
+
* Method decorator name → the type its field hangs off when the
|
|
240
|
+
* class decorator names none. NestJS puts `@Query` on the root
|
|
241
|
+
* `Query` type and `@Mutation` on `Mutation` whatever the class
|
|
242
|
+
* says, so those two answer for themselves.
|
|
243
|
+
*
|
|
244
|
+
* A decorator the map leaves out has no answer of its own, and a
|
|
245
|
+
* class that names no type leaves the field's owner unread. The
|
|
246
|
+
* binding then names no type and pairs with nothing, rather than
|
|
247
|
+
* claiming a field the schema does not have.
|
|
248
|
+
*/
|
|
249
|
+
methodDecoratorTypeMap: Record<string, string>;
|
|
238
250
|
} | {
|
|
239
251
|
/**
|
|
240
252
|
* NestJS-style REST controller discovery. Class decorated with
|
|
@@ -248,10 +260,9 @@ type DiscoveryMatch = {
|
|
|
248
260
|
* Same wrapper-decorator tolerance as `decoratedMethod`: the
|
|
249
261
|
* import-module gate fires on at least one method-route
|
|
250
262
|
* decorator from the framework module, but class decorators
|
|
251
|
-
* match by name only so project-internal
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
* config.
|
|
263
|
+
* match by name only, so a project-internal wrapper composing
|
|
264
|
+
* the framework's decorator matches once the project names it
|
|
265
|
+
* in the pack's options.
|
|
255
266
|
*
|
|
256
267
|
* The adapter populates `DiscoveredUnit.routeInfo` so the
|
|
257
268
|
* produced binding carries `rest(method, path)`.
|
|
@@ -685,14 +696,18 @@ interface TransparentWrapper {
|
|
|
685
696
|
interface PatternPack {
|
|
686
697
|
name: string;
|
|
687
698
|
/**
|
|
688
|
-
* Pack version stamp
|
|
699
|
+
* Pack version stamp, which feeds the cache invalidation key. Bump on
|
|
689
700
|
* any change that affects discovered units / extracted summaries.
|
|
690
|
-
* Format is opaque to the adapter
|
|
691
|
-
*
|
|
692
|
-
*
|
|
693
|
-
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
701
|
+
* Format is opaque to the adapter, so semver or a content hash both
|
|
702
|
+
* work.
|
|
703
|
+
*
|
|
704
|
+
* Optional, because whoever loads the pack knows more about it than
|
|
705
|
+
* the pack does. The CLI folds a hash of the file it loaded and of
|
|
706
|
+
* the config it passed into this stamp, so a pack run through the CLI
|
|
707
|
+
* invalidates on an edit whether or not it declares a version. A host
|
|
708
|
+
* that builds packs some other way carries that responsibility
|
|
709
|
+
* itself. A pack with nothing to stamp reads as `"unset"`, and warm
|
|
710
|
+
* caches will answer for code that has since changed.
|
|
696
711
|
*/
|
|
697
712
|
version?: string;
|
|
698
713
|
languages: string[];
|
|
@@ -981,6 +996,8 @@ interface DiscoveredCustomUnit {
|
|
|
981
996
|
messageBus: MessageBusSemantics["messageBus"];
|
|
982
997
|
channel: string;
|
|
983
998
|
};
|
|
999
|
+
/** The thing that gets deployed and runs this unit, when known. */
|
|
1000
|
+
deployableUnit?: DeployableUnit;
|
|
984
1001
|
/**
|
|
985
1002
|
* Metadata merged onto the resulting summary's `metadata` field.
|
|
986
1003
|
*/
|
|
@@ -1264,7 +1281,30 @@ interface RawDeclaredContract {
|
|
|
1264
1281
|
*/
|
|
1265
1282
|
provenance?: "derived" | "independent";
|
|
1266
1283
|
}
|
|
1284
|
+
/**
|
|
1285
|
+
* What sits where a unit's body should be.
|
|
1286
|
+
*
|
|
1287
|
+
* - "absent": there is no body behind this declaration (an overload
|
|
1288
|
+
* signature, an ambient declaration, an abstract method), so the
|
|
1289
|
+
* pack read nothing about what the unit does.
|
|
1290
|
+
* - "empty": a body with nothing in it. A summary that says nothing
|
|
1291
|
+
* about it has described it completely.
|
|
1292
|
+
* - "statements": a body with work in it, including a concise
|
|
1293
|
+
* arrow's expression. A summary that says nothing about it has
|
|
1294
|
+
* described none of it.
|
|
1295
|
+
* - "elsewhere": the unit's body is not in what this run read. A
|
|
1296
|
+
* route registered with a handler the caller supplies announces a
|
|
1297
|
+
* boundary and names no function to look in, and the boundary is
|
|
1298
|
+
* worth reporting with nothing behind it.
|
|
1299
|
+
*/
|
|
1300
|
+
type BodyContent = "absent" | "empty" | "statements" | "elsewhere";
|
|
1267
1301
|
interface RawCodeStructure {
|
|
1302
|
+
/**
|
|
1303
|
+
* The types this unit's shapes name rather than spell out. Carried
|
|
1304
|
+
* onto the summary as `definitions`, so a reader following a name has
|
|
1305
|
+
* somewhere to look.
|
|
1306
|
+
*/
|
|
1307
|
+
definitions?: Record<string, TypeShape> | null;
|
|
1268
1308
|
identity: {
|
|
1269
1309
|
name: string;
|
|
1270
1310
|
kind: CodeUnitKind;
|
|
@@ -1283,6 +1323,8 @@ interface RawCodeStructure {
|
|
|
1283
1323
|
* helpers, pure utilities, and anything the adapter can't place.
|
|
1284
1324
|
*/
|
|
1285
1325
|
boundaryBinding: BoundaryBinding | null;
|
|
1326
|
+
/** The thing that gets deployed and runs this code, when known. */
|
|
1327
|
+
deployableUnit?: DeployableUnit;
|
|
1286
1328
|
parameters: RawParameter[];
|
|
1287
1329
|
branches: RawBranch[];
|
|
1288
1330
|
/**
|
|
@@ -1292,6 +1334,14 @@ interface RawCodeStructure {
|
|
|
1292
1334
|
* saying it does nothing.
|
|
1293
1335
|
*/
|
|
1294
1336
|
unmatchedReturns?: number;
|
|
1337
|
+
/**
|
|
1338
|
+
* What the adapter found where the unit's body should be. A summary
|
|
1339
|
+
* with nothing in it means one of two different things, and only the
|
|
1340
|
+
* adapter can say which: a body nobody could read, or a body with
|
|
1341
|
+
* nothing in it to read. Adapters that do not say leave this unset,
|
|
1342
|
+
* and everything downstream treats the summary as it did before.
|
|
1343
|
+
*/
|
|
1344
|
+
bodyContent?: BodyContent;
|
|
1295
1345
|
dependencyCalls: RawDependencyCall[];
|
|
1296
1346
|
declaredContract: RawDeclaredContract | null;
|
|
1297
1347
|
/**
|
|
@@ -1358,6 +1408,17 @@ interface RawCodeStructure {
|
|
|
1358
1408
|
reference: string;
|
|
1359
1409
|
reason: string;
|
|
1360
1410
|
};
|
|
1411
|
+
/**
|
|
1412
|
+
* Set when the source does not state part of the boundary this unit
|
|
1413
|
+
* sits on. The binding still goes out with that part left empty, so
|
|
1414
|
+
* the unit pairs with nothing rather than with whatever a guess named,
|
|
1415
|
+
* and this sentence tells a reader which part is missing and why.
|
|
1416
|
+
*
|
|
1417
|
+
* A statement about the reading rather than about the code, so it
|
|
1418
|
+
* lands as an `unreadOutcome` gap and no checker holds it against the
|
|
1419
|
+
* unit.
|
|
1420
|
+
*/
|
|
1421
|
+
unreadBinding?: string;
|
|
1361
1422
|
}
|
|
1362
1423
|
interface ExtractorOptions {
|
|
1363
1424
|
gapHandling: "strict" | "permissive" | "silent";
|
|
@@ -1370,4 +1431,4 @@ declare function terminalToOutput(terminal: RawTerminal): Output;
|
|
|
1370
1431
|
declare function effectToIR(effect: RawEffect): Effect;
|
|
1371
1432
|
declare function paramToInput(param: RawParameter): Input;
|
|
1372
1433
|
|
|
1373
|
-
export { type AccessRecognizer, type BindingExtraction, type ContractPattern, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type ExtractorOptions, type InputMappingPattern, type InvocationRecognizer, type PatternPack, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type ResponsePropertyMapping, type ResponsePropertyMeaning, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type TransparentWrapper, assembleSummary, assessConfidence, detectGaps, effectToIR, httpRouteDiscovery, makeTransitionId, paramToInput, terminalToOutput };
|
|
1434
|
+
export { type AccessRecognizer, type BindingExtraction, type BodyContent, type ContractPattern, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type ExtractorOptions, type InputMappingPattern, type InvocationRecognizer, type PatternPack, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type ResponsePropertyMapping, type ResponsePropertyMeaning, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type TransparentWrapper, assembleSummary, assessConfidence, detectGaps, effectToIR, httpRouteDiscovery, makeTransitionId, paramToInput, terminalToOutput };
|
package/dist/index.js
CHANGED
|
@@ -64,12 +64,14 @@ function assembleSummary(raw, options = DEFAULT_OPTIONS) {
|
|
|
64
64
|
identity: {
|
|
65
65
|
name: raw.identity.name,
|
|
66
66
|
exportPath: raw.identity.exportPath,
|
|
67
|
-
boundaryBinding: raw.boundaryBinding ?? null
|
|
67
|
+
boundaryBinding: raw.boundaryBinding ?? null,
|
|
68
|
+
...raw.deployableUnit !== void 0 ? { deployableUnit: raw.deployableUnit } : {}
|
|
68
69
|
},
|
|
69
70
|
inputs,
|
|
70
71
|
transitions,
|
|
71
72
|
gaps,
|
|
72
73
|
confidence,
|
|
74
|
+
...raw.definitions !== void 0 && raw.definitions !== null ? { definitions: raw.definitions } : {},
|
|
73
75
|
...metadata !== null ? { metadata } : {}
|
|
74
76
|
};
|
|
75
77
|
}
|
|
@@ -130,6 +132,23 @@ function detectGaps(raw, transitions, options) {
|
|
|
130
132
|
description: count === 1 ? "One return in this function matches none of the terminal shapes this pack looks for, so what it produces is not described here" : `${count} returns in this function match none of the terminal shapes this pack looks for, so what they produce is not described here`
|
|
131
133
|
});
|
|
132
134
|
}
|
|
135
|
+
if (raw.unreadBinding !== void 0) {
|
|
136
|
+
gaps.push({
|
|
137
|
+
type: "unreadOutcome",
|
|
138
|
+
conditions: [],
|
|
139
|
+
consequence: "unknown",
|
|
140
|
+
description: raw.unreadBinding
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
const unreadBody = describeUnreadBody(raw);
|
|
144
|
+
if (unreadBody !== null) {
|
|
145
|
+
gaps.push({
|
|
146
|
+
type: "unreadOutcome",
|
|
147
|
+
conditions: [],
|
|
148
|
+
consequence: "unknown",
|
|
149
|
+
description: unreadBody
|
|
150
|
+
});
|
|
151
|
+
}
|
|
133
152
|
if (raw.declaredContract) {
|
|
134
153
|
const producedStatuses = new Set(
|
|
135
154
|
transitions.flatMap((t) => {
|
|
@@ -169,10 +188,34 @@ function detectGaps(raw, transitions, options) {
|
|
|
169
188
|
}
|
|
170
189
|
return gaps;
|
|
171
190
|
}
|
|
191
|
+
function bodyWentUnread(raw) {
|
|
192
|
+
if (raw.bodyContent === "absent" || raw.bodyContent === "elsewhere") {
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
return raw.bodyContent === "statements" && raw.branches.length === 0;
|
|
196
|
+
}
|
|
197
|
+
function describeUnreadBody(raw) {
|
|
198
|
+
if (!bodyWentUnread(raw)) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
if (raw.bodyContent === "absent") {
|
|
202
|
+
return "This unit is a declaration with no body behind it, so nothing about what it does was read here";
|
|
203
|
+
}
|
|
204
|
+
if (raw.bodyContent === "elsewhere") {
|
|
205
|
+
return "The handler on this boundary comes from outside the code that registers it, so nothing about what it does was read here";
|
|
206
|
+
}
|
|
207
|
+
if ((raw.unmatchedReturns ?? 0) > 0) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
return "Nothing this unit's body does matches a shape this pack looks for, so what it does is not described here";
|
|
211
|
+
}
|
|
172
212
|
function assessConfidence(raw) {
|
|
173
213
|
if ((raw.unmatchedReturns ?? 0) > 0) {
|
|
174
214
|
return { source: "inferred_static", level: "low" };
|
|
175
215
|
}
|
|
216
|
+
if (bodyWentUnread(raw)) {
|
|
217
|
+
return { source: "inferred_static", level: "low" };
|
|
218
|
+
}
|
|
176
219
|
let total = 0;
|
|
177
220
|
let opaque = 0;
|
|
178
221
|
for (const branch of raw.branches) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/extractor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Framework-agnostic extraction pipeline that turns raw extractions into suss behavioral summaries.",
|
|
6
6
|
"keywords": [
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"typecheck": "tsc --noEmit"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@suss/behavioral-ir": "0.
|
|
48
|
+
"@suss/behavioral-ir": "0.2.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"tsup": "*",
|