@suss/extractor 0.9.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/dist/index.js CHANGED
@@ -3,7 +3,8 @@ import { createHash } from "crypto";
3
3
  import {
4
4
  exchangesHttpResponses,
5
5
  withGraphqlMetadata,
6
- withHttpMetadata
6
+ withHttpMetadata,
7
+ withSourceDocumentMetadata
7
8
  } from "@suss/behavioral-ir";
8
9
 
9
10
  // src/inputReads.ts
@@ -124,6 +125,7 @@ function unwrapJsonStringify(body) {
124
125
  }
125
126
 
126
127
  // src/paths/enumeratePaths.ts
128
+ import { CATCH_ENTRY_TEXT } from "@suss/behavioral-ir";
127
129
  var MAX_PATHS = 256;
128
130
  var UnmodeledFlow = class extends Error {
129
131
  };
@@ -167,7 +169,7 @@ var loopCompletedCond = (headerText, exit) => fixedCond(
167
169
  exit === "throw" ? "earlyThrow" : "earlyReturn"
168
170
  );
169
171
  var catchEntryCond = () => fixedCond(
170
- { sourceText: "catch", expression: null },
172
+ { sourceText: CATCH_ENTRY_TEXT, expression: null },
171
173
  "positive",
172
174
  "catchBlock"
173
175
  );
@@ -177,7 +179,7 @@ function classify(path, home, isAncestorOrSelf) {
177
179
  return cond.info;
178
180
  }
179
181
  const encloses = home !== null && isAncestorOrSelf(cond.branchStmt, home);
180
- if (encloses || cond.info.polarity === "positive") {
182
+ if (encloses || cond.info.polarity === "positive" || cond.oppositeExit === null) {
181
183
  return { ...cond.info, source: "explicit" };
182
184
  }
183
185
  const source = cond.oppositeExit === "throw" ? "earlyThrow" : "earlyReturn";
@@ -185,6 +187,9 @@ function classify(path, home, isAncestorOrSelf) {
185
187
  });
186
188
  }
187
189
  function childrenOf(stmt) {
190
+ return [...ownBlocksOf(stmt), ...(stmt.callbacks ?? []).flat()];
191
+ }
192
+ function ownBlocksOf(stmt) {
188
193
  if (stmt.kind === "if") {
189
194
  return stmt.elseBody === null ? stmt.thenBody : [...stmt.thenBody, ...stmt.elseBody];
190
195
  }
@@ -211,7 +216,7 @@ function hasStrayBreak(nodes) {
211
216
  if (node.kind === "switch" || node.kind === "loop") {
212
217
  return false;
213
218
  }
214
- return hasStrayBreak(childrenOf(node));
219
+ return hasStrayBreak(ownBlocksOf(node));
215
220
  });
216
221
  }
217
222
  function exitKindOfList(stmts) {
@@ -248,11 +253,6 @@ function containsTerminal(ctx, stmt) {
248
253
  return childrenOf(stmt).some((child) => containsTerminal(ctx, child));
249
254
  }
250
255
  function stepIf(stmt, path, ctx) {
251
- const armsExit = stmt.exitKind !== null;
252
- const armsHaveTerminals = containsTerminal(ctx, stmt);
253
- if (!armsExit && !armsHaveTerminals) {
254
- return [path];
255
- }
256
256
  const thenExit = exitKindOfList(stmt.thenBody);
257
257
  const elseExit = stmt.elseBody === null ? null : exitKindOfList(stmt.elseBody);
258
258
  const thenPath = [
@@ -261,7 +261,12 @@ function stepIf(stmt, path, ctx) {
261
261
  ];
262
262
  const elsePath = [
263
263
  ...path,
264
- branchCond(stmt, stmt.condition, "negative", thenExit)
264
+ branchCond(
265
+ stmt,
266
+ stmt.condition,
267
+ "negative",
268
+ stmt.elseBody === null ? thenExit : null
269
+ )
265
270
  ];
266
271
  const out = [];
267
272
  out.push(...enumerate(ctx, stmt.thenBody, thenPath));
@@ -333,7 +338,11 @@ function stepTry(stmt, path, ctx) {
333
338
  }
334
339
  return out;
335
340
  }
336
- function stepExit(_stmt, _path, _ctx) {
341
+ function stepExit(stmt, path, ctx) {
342
+ const collector = ctx.state.callbackExits[ctx.state.callbackExits.length - 1];
343
+ if (collector !== void 0 && (stmt.exit === "return" || stmt.exit === "throw")) {
344
+ collector.push(path);
345
+ }
337
346
  return [];
338
347
  }
339
348
  function stepOpaque(_stmt, path, _ctx) {
@@ -347,16 +356,36 @@ var stepHandlers = {
347
356
  exit: stepExit,
348
357
  opaque: stepOpaque
349
358
  };
359
+ function stepCallback(ctx, body, path) {
360
+ const exits = [];
361
+ ctx.state.callbackExits.push(exits);
362
+ let fellThrough;
363
+ try {
364
+ fellThrough = enumerate(ctx, body, path);
365
+ } finally {
366
+ ctx.state.callbackExits.pop();
367
+ }
368
+ const out = [...fellThrough, ...exits];
369
+ return out.length === 0 ? [path] : out;
370
+ }
350
371
  function stepStatement(ctx, stmt, path) {
372
+ let paths = [path];
373
+ for (const body of stmt.callbacks ?? []) {
374
+ paths = paths.flatMap((each) => stepCallback(ctx, body, each));
375
+ }
351
376
  const ownTerminals = ctx.terminalsByStmt.get(stmt) ?? [];
352
- for (const terminal of ownTerminals) {
353
- recordTerminal(ctx, terminal, stmt, path);
377
+ for (const each of withRejoined(paths)) {
378
+ for (const terminal of ownTerminals) {
379
+ recordTerminal(ctx, terminal, stmt, each);
380
+ }
354
381
  }
355
382
  const handler = stepHandlers[stmt.kind];
356
- return handler(
357
- stmt,
358
- path,
359
- ctx
383
+ return paths.flatMap(
384
+ (each) => handler(
385
+ stmt,
386
+ each,
387
+ ctx
388
+ )
360
389
  );
361
390
  }
362
391
  function soleDisagreement(left, right) {
@@ -413,14 +442,18 @@ function mergeRejoined(paths) {
413
442
  }
414
443
  return current;
415
444
  }
445
+ function withRejoined(paths) {
446
+ const merged = mergeRejoined(paths);
447
+ return merged.length === paths.length ? paths : [...merged, ...paths];
448
+ }
416
449
  function enumerate(ctx, stmts, prefix) {
417
450
  let frontiers = [[...prefix]];
418
451
  for (const stmt of stmts) {
419
452
  const nextFrontiers = [];
420
- for (const path of frontiers) {
453
+ for (const path of mergeRejoined(frontiers)) {
421
454
  nextFrontiers.push(...stepStatement(ctx, stmt, path));
422
455
  }
423
- frontiers = mergeRejoined(nextFrontiers);
456
+ frontiers = nextFrontiers;
424
457
  if (frontiers.length > MAX_PATHS) {
425
458
  throw new PathBudgetExceeded(
426
459
  `path budget exceeded, more than ${MAX_PATHS} paths`
@@ -460,14 +493,15 @@ function enumerateStructuredPaths(input) {
460
493
  const state = {
461
494
  byTerminal: /* @__PURE__ */ new Map(),
462
495
  fallthrough: [],
463
- pathCount: 0
496
+ pathCount: 0,
497
+ callbackExits: []
464
498
  };
465
499
  const ctx = {
466
500
  terminalsByStmt: input.terminalsByStmt,
467
501
  state,
468
502
  isAncestorOrSelf
469
503
  };
470
- const continuations = enumerate(ctx, input.statements, []);
504
+ const continuations = withRejoined(enumerate(ctx, input.statements, []));
471
505
  for (const path of continuations) {
472
506
  chargeBudget(state);
473
507
  state.fallthrough.push(classify(path, null, isAncestorOrSelf));
@@ -766,6 +800,11 @@ function buildMetadata(raw) {
766
800
  if (graphql !== null) {
767
801
  metadata = withGraphqlMetadata(metadata, graphql);
768
802
  }
803
+ if (raw.sourceDocumentLabel !== void 0) {
804
+ metadata = withSourceDocumentMetadata(metadata, {
805
+ label: raw.sourceDocumentLabel
806
+ });
807
+ }
769
808
  return Object.keys(metadata).length > 0 ? metadata : null;
770
809
  }
771
810
  function buildHttpMetadataValue(raw) {
@@ -783,6 +822,12 @@ function buildHttpMetadataValue(raw) {
783
822
  if (raw.statusAccessors !== void 0 && raw.statusAccessors.length > 0) {
784
823
  http.statusAccessors = raw.statusAccessors;
785
824
  }
825
+ if (raw.successAccessors !== void 0 && raw.successAccessors.length > 0) {
826
+ http.successAccessors = raw.successAccessors;
827
+ }
828
+ if (raw.failureDelivery !== void 0) {
829
+ http.failureDelivery = raw.failureDelivery;
830
+ }
786
831
  return Object.keys(http).length > 0 ? http : null;
787
832
  }
788
833
  function buildGraphqlMetadataValue(raw) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/extractor",
3
- "version": "0.9.0",
3
+ "version": "0.10.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.9.0"
48
+ "@suss/behavioral-ir": "0.10.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "tsup": "*",