@suss/extractor 0.0.1 → 0.1.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 CHANGED
@@ -4,7 +4,7 @@ Assembly engine that turns raw language-adapter output into a `BehavioralSummary
4
4
 
5
5
  ## What this package is
6
6
 
7
- `@suss/extractor` is the core assembly layer of the suss pipeline. Language adapters (such as `@suss/adapter-typescript`) parse source code and produce a `RawCodeStructure` — a normalized, adapter-specific intermediate form. The extractor's `assembleSummary` function converts that structure into the final `BehavioralSummary` IR, handling condition polarity, terminal mapping, gap detection, confidence assessment, and `expectedInput` pass-through for client field tracking. It also exports the `RawCodeStructure` type, the `PatternPack` interface, and all related raw types so adapters can share a common contract.
7
+ `@suss/extractor` is the core assembly layer of the suss pipeline. Language adapters (such as `@suss/adapter-typescript`) parse source code and produce a `RawCodeStructure`, a normalized, adapter-specific intermediate form. The extractor's `assembleSummary` function converts that structure into the final `BehavioralSummary` IR, handling condition polarity, terminal mapping, gap detection, confidence assessment, and `expectedInput` pass-through for client field tracking. It also exports the `RawCodeStructure` type, the `PatternPack` interface, and all related raw types so adapters can share a common contract.
8
8
 
9
9
  ## Where it sits in suss
10
10
 
package/dist/index.cjs CHANGED
@@ -150,6 +150,17 @@ function detectGaps(raw, transitions, options) {
150
150
  return [];
151
151
  }
152
152
  const gaps = [];
153
+ if (raw.unmatchedReturns !== void 0 && raw.unmatchedReturns > 0) {
154
+ const count = raw.unmatchedReturns;
155
+ gaps.push({
156
+ // Not a contract violation: the handler may be answering
157
+ // correctly, in a shape nobody taught the pack.
158
+ type: "unreadOutcome",
159
+ conditions: [],
160
+ consequence: "unknown",
161
+ 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
+ });
163
+ }
153
164
  if (raw.declaredContract) {
154
165
  const producedStatuses = new Set(
155
166
  transitions.flatMap((t) => {
@@ -190,6 +201,9 @@ function detectGaps(raw, transitions, options) {
190
201
  return gaps;
191
202
  }
192
203
  function assessConfidence(raw) {
204
+ if ((raw.unmatchedReturns ?? 0) > 0) {
205
+ return { source: "inferred_static", level: "low" };
206
+ }
193
207
  let total = 0;
194
208
  let opaque = 0;
195
209
  for (const branch of raw.branches) {
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Effect, Predicate, TypeShape, RenderNode, CodeUnitKind, BoundaryBinding, BehavioralSummary, ConfidenceInfo, Transition, Gap, Input, Output } from '@suss/behavioral-ir';
1
+ import { Effect, MessageBusSemantics, 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";
@@ -374,7 +374,49 @@ type BindingExtraction = {
374
374
  type: "fromRegistration";
375
375
  position: number;
376
376
  } | {
377
+ type: "fromArgumentProperty";
378
+ position: number;
379
+ property: string;
380
+ } | {
381
+ /**
382
+ * The route path comes from where the file sits, which is how
383
+ * Next.js and React Router describe their routes. The pack
384
+ * spells out its own convention here, since the adapter knows
385
+ * about files and the pack knows what the framework does with
386
+ * their names.
387
+ *
388
+ * `app/api/orders/[id]/route.ts` under `{ root: "app",
389
+ * dropBasenames: ["route"], dynamic: "brackets" }` comes out as
390
+ * `/api/orders/{id}`, which pairs with an Express provider
391
+ * writing `/api/orders/:id`.
392
+ */
377
393
  type: "fromFilename";
394
+ /**
395
+ * Where a route path starts. The directories below it become
396
+ * the path; everything above it belongs to the project layout
397
+ * and is dropped.
398
+ */
399
+ root: string;
400
+ /**
401
+ * Filenames that say what a file is rather than adding a
402
+ * segment: `route`, `page`, `index`, `_index`.
403
+ */
404
+ dropBasenames?: string[];
405
+ /**
406
+ * How the framework writes a parameter in a filename. Next.js
407
+ * uses `[id]`, React Router uses `$id`.
408
+ */
409
+ dynamic?: "brackets" | "dollarPrefix";
410
+ /**
411
+ * Whether a directory in parentheses organises files without
412
+ * appearing in the URL, as `app/(marketing)/about` does.
413
+ */
414
+ dropParenthesized?: boolean;
415
+ /**
416
+ * Whether one filename holds the whole path with dots between
417
+ * the segments, as `routes/orders.$id.tsx` does.
418
+ */
419
+ flat?: boolean;
378
420
  } | {
379
421
  type: "fromContract";
380
422
  } | {
@@ -487,6 +529,10 @@ interface TerminalExtraction {
487
529
  } | {
488
530
  from: "constructor";
489
531
  codes: Record<string, number>;
532
+ } | {
533
+ from: "argumentProperty";
534
+ position: number;
535
+ name: string;
490
536
  } | {
491
537
  from: "argumentConstructor";
492
538
  position: number;
@@ -619,6 +665,23 @@ interface ResponsePropertyMapping {
619
665
  /** What the value semantically represents */
620
666
  semantics: ResponsePropertyMeaning;
621
667
  }
668
+ /**
669
+ * A library wrapper whose result is the function it was handed. The
670
+ * adapter works this out on its own for factories declared in the
671
+ * project by reading their bodies. A library's body is not readable,
672
+ * so the pack states it.
673
+ */
674
+ interface TransparentWrapper {
675
+ /** Callee text as written, e.g. "Sentry.wrapHandler". */
676
+ callee: string;
677
+ /** Which argument holds the wrapped function. */
678
+ argument: number;
679
+ /**
680
+ * The module the callee has to have been imported from. Without it a
681
+ * local object spelled the same way would be taken for the library.
682
+ */
683
+ module: string;
684
+ }
622
685
  interface PatternPack {
623
686
  name: string;
624
687
  /**
@@ -792,6 +855,17 @@ interface PatternPack {
792
855
  * available without an import).
793
856
  */
794
857
  requiresImport?: string[];
858
+ /**
859
+ * Library wrappers whose result is the wrapped function. The adapter
860
+ * derives this automatically for project-local factories by reading
861
+ * their bodies; a library wrapper's body is not readable, so the
862
+ * pack states the judgment: a call to `callee` resolves to its
863
+ * `argument`-th argument.
864
+ *
865
+ * `callee` matches the call expression text as written, e.g.
866
+ * `"Sentry.wrapHandler"`.
867
+ */
868
+ transparentWrappers?: TransparentWrapper[];
795
869
  /**
796
870
  * Per-property-access recognizers — sister to
797
871
  * `invocationRecognizers` but firing on `PropertyAccessExpression`
@@ -884,6 +958,29 @@ interface DiscoveredCustomUnit {
884
958
  method: string;
885
959
  path: string;
886
960
  };
961
+ /**
962
+ * GraphQL field identity for units a callback discovers against an
963
+ * external manifest rather than an in-code resolver map. AppSync
964
+ * routes a field to a Lambda in the deploy template, so the field is
965
+ * the boundary that code serves. When set, the adapter builds a
966
+ * `graphql-resolver` binding from `(typeName, fieldName)`, which
967
+ * pairs with the operations a client sends.
968
+ */
969
+ resolverInfo?: {
970
+ typeName: string;
971
+ fieldName: string;
972
+ };
973
+ /**
974
+ * Message-bus channel identity for consumer units a callback
975
+ * discovers against a subject the code itself names (a handler
976
+ * factory whose config carries the expected subject). When set, the
977
+ * adapter builds a `message-bus` binding from `(messageBus, channel)`,
978
+ * which pairs with producers sending on the same channel.
979
+ */
980
+ channelInfo?: {
981
+ messageBus: MessageBusSemantics["messageBus"];
982
+ channel: string;
983
+ };
887
984
  /**
888
985
  * Metadata merged onto the resulting summary's `metadata` field.
889
986
  */
@@ -1188,6 +1285,13 @@ interface RawCodeStructure {
1188
1285
  boundaryBinding: BoundaryBinding | null;
1189
1286
  parameters: RawParameter[];
1190
1287
  branches: RawBranch[];
1288
+ /**
1289
+ * Return statements in the body that none of the pack's terminals
1290
+ * matched. A handler shaped in a way the pack does not describe still
1291
+ * returns something, and saying nothing about it reads the same as
1292
+ * saying it does nothing.
1293
+ */
1294
+ unmatchedReturns?: number;
1191
1295
  dependencyCalls: RawDependencyCall[];
1192
1296
  declaredContract: RawDeclaredContract | null;
1193
1297
  /**
@@ -1266,4 +1370,4 @@ declare function terminalToOutput(terminal: RawTerminal): Output;
1266
1370
  declare function effectToIR(effect: RawEffect): Effect;
1267
1371
  declare function paramToInput(param: RawParameter): Input;
1268
1372
 
1269
- 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, assembleSummary, assessConfidence, detectGaps, effectToIR, httpRouteDiscovery, makeTransitionId, paramToInput, terminalToOutput };
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 };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Effect, Predicate, TypeShape, RenderNode, CodeUnitKind, BoundaryBinding, BehavioralSummary, ConfidenceInfo, Transition, Gap, Input, Output } from '@suss/behavioral-ir';
1
+ import { Effect, MessageBusSemantics, 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";
@@ -374,7 +374,49 @@ type BindingExtraction = {
374
374
  type: "fromRegistration";
375
375
  position: number;
376
376
  } | {
377
+ type: "fromArgumentProperty";
378
+ position: number;
379
+ property: string;
380
+ } | {
381
+ /**
382
+ * The route path comes from where the file sits, which is how
383
+ * Next.js and React Router describe their routes. The pack
384
+ * spells out its own convention here, since the adapter knows
385
+ * about files and the pack knows what the framework does with
386
+ * their names.
387
+ *
388
+ * `app/api/orders/[id]/route.ts` under `{ root: "app",
389
+ * dropBasenames: ["route"], dynamic: "brackets" }` comes out as
390
+ * `/api/orders/{id}`, which pairs with an Express provider
391
+ * writing `/api/orders/:id`.
392
+ */
377
393
  type: "fromFilename";
394
+ /**
395
+ * Where a route path starts. The directories below it become
396
+ * the path; everything above it belongs to the project layout
397
+ * and is dropped.
398
+ */
399
+ root: string;
400
+ /**
401
+ * Filenames that say what a file is rather than adding a
402
+ * segment: `route`, `page`, `index`, `_index`.
403
+ */
404
+ dropBasenames?: string[];
405
+ /**
406
+ * How the framework writes a parameter in a filename. Next.js
407
+ * uses `[id]`, React Router uses `$id`.
408
+ */
409
+ dynamic?: "brackets" | "dollarPrefix";
410
+ /**
411
+ * Whether a directory in parentheses organises files without
412
+ * appearing in the URL, as `app/(marketing)/about` does.
413
+ */
414
+ dropParenthesized?: boolean;
415
+ /**
416
+ * Whether one filename holds the whole path with dots between
417
+ * the segments, as `routes/orders.$id.tsx` does.
418
+ */
419
+ flat?: boolean;
378
420
  } | {
379
421
  type: "fromContract";
380
422
  } | {
@@ -487,6 +529,10 @@ interface TerminalExtraction {
487
529
  } | {
488
530
  from: "constructor";
489
531
  codes: Record<string, number>;
532
+ } | {
533
+ from: "argumentProperty";
534
+ position: number;
535
+ name: string;
490
536
  } | {
491
537
  from: "argumentConstructor";
492
538
  position: number;
@@ -619,6 +665,23 @@ interface ResponsePropertyMapping {
619
665
  /** What the value semantically represents */
620
666
  semantics: ResponsePropertyMeaning;
621
667
  }
668
+ /**
669
+ * A library wrapper whose result is the function it was handed. The
670
+ * adapter works this out on its own for factories declared in the
671
+ * project by reading their bodies. A library's body is not readable,
672
+ * so the pack states it.
673
+ */
674
+ interface TransparentWrapper {
675
+ /** Callee text as written, e.g. "Sentry.wrapHandler". */
676
+ callee: string;
677
+ /** Which argument holds the wrapped function. */
678
+ argument: number;
679
+ /**
680
+ * The module the callee has to have been imported from. Without it a
681
+ * local object spelled the same way would be taken for the library.
682
+ */
683
+ module: string;
684
+ }
622
685
  interface PatternPack {
623
686
  name: string;
624
687
  /**
@@ -792,6 +855,17 @@ interface PatternPack {
792
855
  * available without an import).
793
856
  */
794
857
  requiresImport?: string[];
858
+ /**
859
+ * Library wrappers whose result is the wrapped function. The adapter
860
+ * derives this automatically for project-local factories by reading
861
+ * their bodies; a library wrapper's body is not readable, so the
862
+ * pack states the judgment: a call to `callee` resolves to its
863
+ * `argument`-th argument.
864
+ *
865
+ * `callee` matches the call expression text as written, e.g.
866
+ * `"Sentry.wrapHandler"`.
867
+ */
868
+ transparentWrappers?: TransparentWrapper[];
795
869
  /**
796
870
  * Per-property-access recognizers — sister to
797
871
  * `invocationRecognizers` but firing on `PropertyAccessExpression`
@@ -884,6 +958,29 @@ interface DiscoveredCustomUnit {
884
958
  method: string;
885
959
  path: string;
886
960
  };
961
+ /**
962
+ * GraphQL field identity for units a callback discovers against an
963
+ * external manifest rather than an in-code resolver map. AppSync
964
+ * routes a field to a Lambda in the deploy template, so the field is
965
+ * the boundary that code serves. When set, the adapter builds a
966
+ * `graphql-resolver` binding from `(typeName, fieldName)`, which
967
+ * pairs with the operations a client sends.
968
+ */
969
+ resolverInfo?: {
970
+ typeName: string;
971
+ fieldName: string;
972
+ };
973
+ /**
974
+ * Message-bus channel identity for consumer units a callback
975
+ * discovers against a subject the code itself names (a handler
976
+ * factory whose config carries the expected subject). When set, the
977
+ * adapter builds a `message-bus` binding from `(messageBus, channel)`,
978
+ * which pairs with producers sending on the same channel.
979
+ */
980
+ channelInfo?: {
981
+ messageBus: MessageBusSemantics["messageBus"];
982
+ channel: string;
983
+ };
887
984
  /**
888
985
  * Metadata merged onto the resulting summary's `metadata` field.
889
986
  */
@@ -1188,6 +1285,13 @@ interface RawCodeStructure {
1188
1285
  boundaryBinding: BoundaryBinding | null;
1189
1286
  parameters: RawParameter[];
1190
1287
  branches: RawBranch[];
1288
+ /**
1289
+ * Return statements in the body that none of the pack's terminals
1290
+ * matched. A handler shaped in a way the pack does not describe still
1291
+ * returns something, and saying nothing about it reads the same as
1292
+ * saying it does nothing.
1293
+ */
1294
+ unmatchedReturns?: number;
1191
1295
  dependencyCalls: RawDependencyCall[];
1192
1296
  declaredContract: RawDeclaredContract | null;
1193
1297
  /**
@@ -1266,4 +1370,4 @@ declare function terminalToOutput(terminal: RawTerminal): Output;
1266
1370
  declare function effectToIR(effect: RawEffect): Effect;
1267
1371
  declare function paramToInput(param: RawParameter): Input;
1268
1372
 
1269
- 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, assembleSummary, assessConfidence, detectGaps, effectToIR, httpRouteDiscovery, makeTransitionId, paramToInput, terminalToOutput };
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 };
package/dist/index.js CHANGED
@@ -119,6 +119,17 @@ function detectGaps(raw, transitions, options) {
119
119
  return [];
120
120
  }
121
121
  const gaps = [];
122
+ if (raw.unmatchedReturns !== void 0 && raw.unmatchedReturns > 0) {
123
+ const count = raw.unmatchedReturns;
124
+ gaps.push({
125
+ // Not a contract violation: the handler may be answering
126
+ // correctly, in a shape nobody taught the pack.
127
+ type: "unreadOutcome",
128
+ conditions: [],
129
+ consequence: "unknown",
130
+ 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
+ });
132
+ }
122
133
  if (raw.declaredContract) {
123
134
  const producedStatuses = new Set(
124
135
  transitions.flatMap((t) => {
@@ -159,6 +170,9 @@ function detectGaps(raw, transitions, options) {
159
170
  return gaps;
160
171
  }
161
172
  function assessConfidence(raw) {
173
+ if ((raw.unmatchedReturns ?? 0) > 0) {
174
+ return { source: "inferred_static", level: "low" };
175
+ }
162
176
  let total = 0;
163
177
  let opaque = 0;
164
178
  for (const branch of raw.branches) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/extractor",
3
- "version": "0.0.1",
3
+ "version": "0.1.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.0.1"
48
+ "@suss/behavioral-ir": "0.1.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "tsup": "*",