@suss/extractor 0.14.1 → 0.16.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 CHANGED
@@ -38,6 +38,7 @@ __export(index_exports, {
38
38
  makeTransitionId: () => makeTransitionId,
39
39
  mapReading: () => mapReading,
40
40
  paramToInput: () => paramToInput,
41
+ registrationHelperDiscovery: () => registrationHelperDiscovery,
41
42
  sharedGatingConditions: () => sharedGatingConditions,
42
43
  terminalToOutput: () => terminalToOutput,
43
44
  unreadableReading: () => unreadableReading,
@@ -132,7 +133,7 @@ function nested(value, depth) {
132
133
  // src/packHelpers.ts
133
134
  function httpRouteDiscovery(opts) {
134
135
  const kind = opts.kind ?? "handler";
135
- return opts.importNames.map((importName) => ({
136
+ const calls = opts.importNames.map((importName) => ({
136
137
  kind,
137
138
  match: {
138
139
  type: "registrationCall",
@@ -153,6 +154,44 @@ function httpRouteDiscovery(opts) {
153
154
  ...opts.mount !== void 0 ? { mount: opts.mount } : {},
154
155
  requiresImport: [opts.importModule]
155
156
  }));
157
+ if (opts.importNames.length === 0) {
158
+ return calls;
159
+ }
160
+ return [
161
+ ...calls,
162
+ // Routes registered in a loop over an array of specs, a shape
163
+ // registration-call discovery cannot see. The receiver comes from
164
+ // the same import declaration as the calls above, so a loop that
165
+ // never touches this library's routable is left alone.
166
+ {
167
+ kind,
168
+ match: {
169
+ type: "registrationLoop",
170
+ elementShape: LOOP_ELEMENT_SHAPE,
171
+ receiver: {
172
+ importModule: opts.importModule,
173
+ importNames: [...opts.importNames]
174
+ }
175
+ },
176
+ requiresImport: [opts.importModule]
177
+ }
178
+ ];
179
+ }
180
+ var LOOP_ELEMENT_SHAPE = {
181
+ methodKey: "method",
182
+ pathKey: "path",
183
+ handlerKey: "handler"
184
+ };
185
+ function registrationHelperDiscovery(helpers, kind = "handler") {
186
+ return helpers.map((helper) => ({
187
+ kind,
188
+ match: {
189
+ type: "registrationTemplate",
190
+ helperName: helper.helperName,
191
+ ...helper.importModule !== void 0 ? { importModule: helper.importModule } : {},
192
+ registrations: helper.registrations.map((one) => ({ ...one }))
193
+ }
194
+ }));
156
195
  }
157
196
  function unwrapJsonStringify(body) {
158
197
  if (body === null || typeof body !== "object") {
@@ -1126,6 +1165,7 @@ function paramToInput(param) {
1126
1165
  makeTransitionId,
1127
1166
  mapReading,
1128
1167
  paramToInput,
1168
+ registrationHelperDiscovery,
1129
1169
  sharedGatingConditions,
1130
1170
  terminalToOutput,
1131
1171
  unreadableReading,
package/dist/index.d.cts CHANGED
@@ -187,8 +187,18 @@ type DiscoveryMatch = {
187
187
  * `development` conditions are not handled yet.
188
188
  */
189
189
  type: "packageExports";
190
- /** Absolute path to the package's `package.json`. */
191
- packageJsonPath: string;
190
+ /**
191
+ * Absolute path to the package's `package.json`. Left out when
192
+ * `workspaces` is set, since the workspace decides the list.
193
+ */
194
+ packageJsonPath?: string;
195
+ /**
196
+ * One pattern for every package the workspace declares. The pack
197
+ * cannot list the packages, because they belong to the project
198
+ * rather than to any library, so the adapter reads the workspace
199
+ * manifest and applies this pattern once per package it finds.
200
+ */
201
+ workspaces?: true;
192
202
  /**
193
203
  * Restrict to these `exports` keys (without the leading `./`). The
194
204
  * root export is keyed `"."`. Leave it unset for every sub-path that
@@ -316,6 +326,18 @@ type DiscoveryMatch = {
316
326
  pathKey: string;
317
327
  handlerKey: string;
318
328
  };
329
+ /**
330
+ * The routable the loop registers on. When set, the loop's body
331
+ * must call a method on a variable constructed from one of these
332
+ * imports, the same resolution a registration call's subject
333
+ * gets. Without it, any loop over objects with the three keys
334
+ * above matches, and a file that imports the library can contain
335
+ * an unrelated one.
336
+ */
337
+ receiver?: {
338
+ importModule: string;
339
+ importNames: string[];
340
+ };
319
341
  } | {
320
342
  /**
321
343
  * Helper-call expansion: one function call at the user's site is
@@ -438,7 +460,14 @@ type DiscoveryMatch = {
438
460
  * than the original, because full symbol resolution is not built yet.
439
461
  */
440
462
  type: "packageImport";
441
- packages: string[];
463
+ /** Left out when `workspaces` is set. */
464
+ packages?: string[];
465
+ /**
466
+ * Track imports of every package the workspace declares. A file
467
+ * inside one workspace package importing another is the consumer
468
+ * side of the package-export boundary, whichever two they are.
469
+ */
470
+ workspaces?: true;
442
471
  };
443
472
  type BindingExtraction = {
444
473
  method: {
@@ -519,11 +548,48 @@ type BindingExtraction = {
519
548
  type: "fromClientMethod";
520
549
  };
521
550
  };
551
+ /**
552
+ * Where a declared channel's spelling comes from.
553
+ *
554
+ * `decoratorArgument` reads the argument off the same decorator the
555
+ * match selected the handler by, so `@EventPattern("order.placed")`
556
+ * gives "order.placed". `literal` is for a wire whose channel the
557
+ * library fixes. `unstated` says the wire is known and the channel is
558
+ * not, which pairs the way a null channel always has.
559
+ */
560
+ type ChannelSource = {
561
+ from: "decoratorArgument";
562
+ position: number;
563
+ } | {
564
+ from: "literal";
565
+ value: string;
566
+ } | {
567
+ from: "unstated";
568
+ };
569
+ /**
570
+ * A binding the pattern states outright, for a boundary the match
571
+ * cannot read from the source.
572
+ *
573
+ * `bindingExtraction` speaks REST and nothing else, so a declarative
574
+ * pack whose boundary is a queue or a topic had nowhere to say so and
575
+ * was pushed into a callback, which pack health then reports as an
576
+ * ast-link. This is the same vocabulary `DiscoveredCustomUnit` already
577
+ * has, declared instead of returned. Message bus only for now; the
578
+ * design note in the proposals directory says what comes next and why
579
+ * the rest stays put.
580
+ */
581
+ type DeclaredBinding = {
582
+ semantics: "message-bus";
583
+ messageBus: MessageBusSemantics["messageBus"];
584
+ channel: ChannelSource;
585
+ };
522
586
  interface DiscoveryPattern {
523
587
  /** The kind of code unit this discovers: "handler", "loader", "action", "component", etc. */
524
588
  kind: string;
525
589
  match: DiscoveryMatch;
526
590
  bindingExtraction?: BindingExtraction;
591
+ /** A binding the pattern states outright. See `DeclaredBinding`. */
592
+ binding?: DeclaredBinding;
527
593
  /**
528
594
  * How the routable this pattern discovers (Express's `Router()`,
529
595
  * Hono's `new Hono()`, and similar) can itself be mounted onto
@@ -1589,6 +1655,35 @@ declare function httpRouteDiscovery(opts: {
1589
1655
  */
1590
1656
  mount?: DiscoveryPattern["mount"];
1591
1657
  }): DiscoveryPattern[];
1658
+ /**
1659
+ * One route a project helper registers when called, spelled with `{N}`
1660
+ * placeholders for the call's positional arguments.
1661
+ */
1662
+ interface RegistrationHelper {
1663
+ /** The helper's exported name, as the project's code imports it. */
1664
+ helperName: string;
1665
+ /** The module the helper is imported from, to tell two apart. */
1666
+ importModule?: string;
1667
+ registrations: Array<{
1668
+ method: string;
1669
+ pathTemplate: string;
1670
+ handlerArg: string;
1671
+ }>;
1672
+ }
1673
+ /**
1674
+ * Discovery patterns for a project's own registration helpers.
1675
+ *
1676
+ * A helper like `registerCrud(app, "users", handlers)` registers
1677
+ * routes the call-site never spells out, so registration-call
1678
+ * discovery cannot see them. The helper's name belongs to one project,
1679
+ * which is why this arrives through per-project pack config rather
1680
+ * than shipping inside any framework pack:
1681
+ *
1682
+ * suss extract -f express=config.json
1683
+ *
1684
+ * with `{ "registrationHelpers": [...] }` in the file.
1685
+ */
1686
+ declare function registrationHelperDiscovery(helpers: readonly RegistrationHelper[], kind?: string): DiscoveryPattern[];
1592
1687
  /**
1593
1688
  * The payload behind a `JSON.stringify(...)` call, or the argument
1594
1689
  * unchanged when it is anything else. A producer serializes its message
@@ -1775,11 +1870,7 @@ interface ConstructedFrom {
1775
1870
  */
1776
1871
  readonly named?: readonly string[];
1777
1872
  }
1778
- /**
1779
- * What a reader gives back for a name nothing in the source settles.
1780
- * `"nothing"` gives null, `"reference"` gives the value to go and ask
1781
- * about instead.
1782
- */
1873
+
1783
1874
  type UnsettledName = "nothing" | "reference";
1784
1875
  /**
1785
1876
  * One value a call states, as the questions a pack can ask about it.
@@ -1816,6 +1907,13 @@ interface ValueOps {
1816
1907
  items(): readonly ValueOps[];
1817
1908
  /** What one named property of this object states, or null for none. */
1818
1909
  property(name: string): ValueOps | null;
1910
+ /**
1911
+ * This value in the form an effect records an argument, or null when
1912
+ * the adapter cannot write one. A pack that wants a payload compared
1913
+ * across a boundary asks for this, since a body reduced to text
1914
+ * cannot be paired field by field.
1915
+ */
1916
+ asArg(): EffectArg | null;
1819
1917
  /**
1820
1918
  * The pieces of text the source wrote here, in order, with whatever
1821
1919
  * it interpolated between them left out. A string is one piece and a
@@ -2218,4 +2316,4 @@ declare function terminalToOutput(terminal: RawTerminal): Output;
2218
2316
  declare function effectToIR(effect: RawEffect): Effect;
2219
2317
  declare function paramToInput(param: RawParameter): Input;
2220
2318
 
2221
- export { type AccessRecognizer, type AstCapableOps, type BindingExtraction, type BodyContent, type CallOps, type CaseGroup, type ChosenReading, type ConditionHandle, type ConditionInfo, type ConditionSource, type ConstructedFrom, type ContractPattern, type DeclaredBy, type DeclaredMatch, type DefaultedReading, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type ExitKind, type ExtractorOptions, type FailureDelivery, IdMap, IdSet, type Identified, type InputMappingPattern, type InvocationRecognizer, type LanguageAdapter, type LoweredStatementParts, type OpsCarrier, type PackDeclarations, PathBudgetExceeded, type PatternPack, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type Reading, type ReceiverOrigin, type ResponsePropertyMapping, type ResponsePropertyMeaning, type SourceRange, type StatementBlock, type StructuredPathConditionsInput, type StructuredPathConditionsResult, type StructuredStatement, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type TransparentWrapper, UnmodeledFlow, type UnsettledName, type ValueEntry, type ValueOps, absentReading, ambiguousReading, andThenReading, assembleSummary, assessConfidence, detectGaps, effectToIR, enumerateOrDegrade, enumerateStructuredPaths, firstWrittenReading, httpRouteDiscovery, makeTransitionId, mapReading, paramToInput, sharedGatingConditions, terminalToOutput, unreadableReading, unwrapJsonStringify, valueToReadFurtherFrom, writtenReading };
2319
+ export { type AccessRecognizer, type AstCapableOps, type BindingExtraction, type BodyContent, type CallOps, type CaseGroup, type ChannelSource, type ChosenReading, type ConditionHandle, type ConditionInfo, type ConditionSource, type ConstructedFrom, type ContractPattern, type DeclaredBinding, type DeclaredBy, type DeclaredMatch, type DefaultedReading, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type ExitKind, type ExtractorOptions, type FailureDelivery, IdMap, IdSet, type Identified, type InputMappingPattern, type InvocationRecognizer, type LanguageAdapter, type LoweredStatementParts, type OpsCarrier, type PackDeclarations, PathBudgetExceeded, type PatternPack, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type Reading, type ReceiverOrigin, type RegistrationHelper, type ResponsePropertyMapping, type ResponsePropertyMeaning, type SourceRange, type StatementBlock, type StructuredPathConditionsInput, type StructuredPathConditionsResult, type StructuredStatement, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type TransparentWrapper, UnmodeledFlow, type UnsettledName, type ValueEntry, type ValueOps, absentReading, ambiguousReading, andThenReading, assembleSummary, assessConfidence, detectGaps, effectToIR, enumerateOrDegrade, enumerateStructuredPaths, firstWrittenReading, httpRouteDiscovery, makeTransitionId, mapReading, paramToInput, registrationHelperDiscovery, sharedGatingConditions, terminalToOutput, unreadableReading, unwrapJsonStringify, valueToReadFurtherFrom, writtenReading };
package/dist/index.d.ts CHANGED
@@ -187,8 +187,18 @@ type DiscoveryMatch = {
187
187
  * `development` conditions are not handled yet.
188
188
  */
189
189
  type: "packageExports";
190
- /** Absolute path to the package's `package.json`. */
191
- packageJsonPath: string;
190
+ /**
191
+ * Absolute path to the package's `package.json`. Left out when
192
+ * `workspaces` is set, since the workspace decides the list.
193
+ */
194
+ packageJsonPath?: string;
195
+ /**
196
+ * One pattern for every package the workspace declares. The pack
197
+ * cannot list the packages, because they belong to the project
198
+ * rather than to any library, so the adapter reads the workspace
199
+ * manifest and applies this pattern once per package it finds.
200
+ */
201
+ workspaces?: true;
192
202
  /**
193
203
  * Restrict to these `exports` keys (without the leading `./`). The
194
204
  * root export is keyed `"."`. Leave it unset for every sub-path that
@@ -316,6 +326,18 @@ type DiscoveryMatch = {
316
326
  pathKey: string;
317
327
  handlerKey: string;
318
328
  };
329
+ /**
330
+ * The routable the loop registers on. When set, the loop's body
331
+ * must call a method on a variable constructed from one of these
332
+ * imports, the same resolution a registration call's subject
333
+ * gets. Without it, any loop over objects with the three keys
334
+ * above matches, and a file that imports the library can contain
335
+ * an unrelated one.
336
+ */
337
+ receiver?: {
338
+ importModule: string;
339
+ importNames: string[];
340
+ };
319
341
  } | {
320
342
  /**
321
343
  * Helper-call expansion: one function call at the user's site is
@@ -438,7 +460,14 @@ type DiscoveryMatch = {
438
460
  * than the original, because full symbol resolution is not built yet.
439
461
  */
440
462
  type: "packageImport";
441
- packages: string[];
463
+ /** Left out when `workspaces` is set. */
464
+ packages?: string[];
465
+ /**
466
+ * Track imports of every package the workspace declares. A file
467
+ * inside one workspace package importing another is the consumer
468
+ * side of the package-export boundary, whichever two they are.
469
+ */
470
+ workspaces?: true;
442
471
  };
443
472
  type BindingExtraction = {
444
473
  method: {
@@ -519,11 +548,48 @@ type BindingExtraction = {
519
548
  type: "fromClientMethod";
520
549
  };
521
550
  };
551
+ /**
552
+ * Where a declared channel's spelling comes from.
553
+ *
554
+ * `decoratorArgument` reads the argument off the same decorator the
555
+ * match selected the handler by, so `@EventPattern("order.placed")`
556
+ * gives "order.placed". `literal` is for a wire whose channel the
557
+ * library fixes. `unstated` says the wire is known and the channel is
558
+ * not, which pairs the way a null channel always has.
559
+ */
560
+ type ChannelSource = {
561
+ from: "decoratorArgument";
562
+ position: number;
563
+ } | {
564
+ from: "literal";
565
+ value: string;
566
+ } | {
567
+ from: "unstated";
568
+ };
569
+ /**
570
+ * A binding the pattern states outright, for a boundary the match
571
+ * cannot read from the source.
572
+ *
573
+ * `bindingExtraction` speaks REST and nothing else, so a declarative
574
+ * pack whose boundary is a queue or a topic had nowhere to say so and
575
+ * was pushed into a callback, which pack health then reports as an
576
+ * ast-link. This is the same vocabulary `DiscoveredCustomUnit` already
577
+ * has, declared instead of returned. Message bus only for now; the
578
+ * design note in the proposals directory says what comes next and why
579
+ * the rest stays put.
580
+ */
581
+ type DeclaredBinding = {
582
+ semantics: "message-bus";
583
+ messageBus: MessageBusSemantics["messageBus"];
584
+ channel: ChannelSource;
585
+ };
522
586
  interface DiscoveryPattern {
523
587
  /** The kind of code unit this discovers: "handler", "loader", "action", "component", etc. */
524
588
  kind: string;
525
589
  match: DiscoveryMatch;
526
590
  bindingExtraction?: BindingExtraction;
591
+ /** A binding the pattern states outright. See `DeclaredBinding`. */
592
+ binding?: DeclaredBinding;
527
593
  /**
528
594
  * How the routable this pattern discovers (Express's `Router()`,
529
595
  * Hono's `new Hono()`, and similar) can itself be mounted onto
@@ -1589,6 +1655,35 @@ declare function httpRouteDiscovery(opts: {
1589
1655
  */
1590
1656
  mount?: DiscoveryPattern["mount"];
1591
1657
  }): DiscoveryPattern[];
1658
+ /**
1659
+ * One route a project helper registers when called, spelled with `{N}`
1660
+ * placeholders for the call's positional arguments.
1661
+ */
1662
+ interface RegistrationHelper {
1663
+ /** The helper's exported name, as the project's code imports it. */
1664
+ helperName: string;
1665
+ /** The module the helper is imported from, to tell two apart. */
1666
+ importModule?: string;
1667
+ registrations: Array<{
1668
+ method: string;
1669
+ pathTemplate: string;
1670
+ handlerArg: string;
1671
+ }>;
1672
+ }
1673
+ /**
1674
+ * Discovery patterns for a project's own registration helpers.
1675
+ *
1676
+ * A helper like `registerCrud(app, "users", handlers)` registers
1677
+ * routes the call-site never spells out, so registration-call
1678
+ * discovery cannot see them. The helper's name belongs to one project,
1679
+ * which is why this arrives through per-project pack config rather
1680
+ * than shipping inside any framework pack:
1681
+ *
1682
+ * suss extract -f express=config.json
1683
+ *
1684
+ * with `{ "registrationHelpers": [...] }` in the file.
1685
+ */
1686
+ declare function registrationHelperDiscovery(helpers: readonly RegistrationHelper[], kind?: string): DiscoveryPattern[];
1592
1687
  /**
1593
1688
  * The payload behind a `JSON.stringify(...)` call, or the argument
1594
1689
  * unchanged when it is anything else. A producer serializes its message
@@ -1775,11 +1870,7 @@ interface ConstructedFrom {
1775
1870
  */
1776
1871
  readonly named?: readonly string[];
1777
1872
  }
1778
- /**
1779
- * What a reader gives back for a name nothing in the source settles.
1780
- * `"nothing"` gives null, `"reference"` gives the value to go and ask
1781
- * about instead.
1782
- */
1873
+
1783
1874
  type UnsettledName = "nothing" | "reference";
1784
1875
  /**
1785
1876
  * One value a call states, as the questions a pack can ask about it.
@@ -1816,6 +1907,13 @@ interface ValueOps {
1816
1907
  items(): readonly ValueOps[];
1817
1908
  /** What one named property of this object states, or null for none. */
1818
1909
  property(name: string): ValueOps | null;
1910
+ /**
1911
+ * This value in the form an effect records an argument, or null when
1912
+ * the adapter cannot write one. A pack that wants a payload compared
1913
+ * across a boundary asks for this, since a body reduced to text
1914
+ * cannot be paired field by field.
1915
+ */
1916
+ asArg(): EffectArg | null;
1819
1917
  /**
1820
1918
  * The pieces of text the source wrote here, in order, with whatever
1821
1919
  * it interpolated between them left out. A string is one piece and a
@@ -2218,4 +2316,4 @@ declare function terminalToOutput(terminal: RawTerminal): Output;
2218
2316
  declare function effectToIR(effect: RawEffect): Effect;
2219
2317
  declare function paramToInput(param: RawParameter): Input;
2220
2318
 
2221
- export { type AccessRecognizer, type AstCapableOps, type BindingExtraction, type BodyContent, type CallOps, type CaseGroup, type ChosenReading, type ConditionHandle, type ConditionInfo, type ConditionSource, type ConstructedFrom, type ContractPattern, type DeclaredBy, type DeclaredMatch, type DefaultedReading, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type ExitKind, type ExtractorOptions, type FailureDelivery, IdMap, IdSet, type Identified, type InputMappingPattern, type InvocationRecognizer, type LanguageAdapter, type LoweredStatementParts, type OpsCarrier, type PackDeclarations, PathBudgetExceeded, type PatternPack, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type Reading, type ReceiverOrigin, type ResponsePropertyMapping, type ResponsePropertyMeaning, type SourceRange, type StatementBlock, type StructuredPathConditionsInput, type StructuredPathConditionsResult, type StructuredStatement, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type TransparentWrapper, UnmodeledFlow, type UnsettledName, type ValueEntry, type ValueOps, absentReading, ambiguousReading, andThenReading, assembleSummary, assessConfidence, detectGaps, effectToIR, enumerateOrDegrade, enumerateStructuredPaths, firstWrittenReading, httpRouteDiscovery, makeTransitionId, mapReading, paramToInput, sharedGatingConditions, terminalToOutput, unreadableReading, unwrapJsonStringify, valueToReadFurtherFrom, writtenReading };
2319
+ export { type AccessRecognizer, type AstCapableOps, type BindingExtraction, type BodyContent, type CallOps, type CaseGroup, type ChannelSource, type ChosenReading, type ConditionHandle, type ConditionInfo, type ConditionSource, type ConstructedFrom, type ContractPattern, type DeclaredBinding, type DeclaredBy, type DeclaredMatch, type DefaultedReading, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type ExitKind, type ExtractorOptions, type FailureDelivery, IdMap, IdSet, type Identified, type InputMappingPattern, type InvocationRecognizer, type LanguageAdapter, type LoweredStatementParts, type OpsCarrier, type PackDeclarations, PathBudgetExceeded, type PatternPack, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type Reading, type ReceiverOrigin, type RegistrationHelper, type ResponsePropertyMapping, type ResponsePropertyMeaning, type SourceRange, type StatementBlock, type StructuredPathConditionsInput, type StructuredPathConditionsResult, type StructuredStatement, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type TransparentWrapper, UnmodeledFlow, type UnsettledName, type ValueEntry, type ValueOps, absentReading, ambiguousReading, andThenReading, assembleSummary, assessConfidence, detectGaps, effectToIR, enumerateOrDegrade, enumerateStructuredPaths, firstWrittenReading, httpRouteDiscovery, makeTransitionId, mapReading, paramToInput, registrationHelperDiscovery, sharedGatingConditions, terminalToOutput, unreadableReading, unwrapJsonStringify, valueToReadFurtherFrom, writtenReading };
package/dist/index.js CHANGED
@@ -90,7 +90,7 @@ function nested(value, depth) {
90
90
  // src/packHelpers.ts
91
91
  function httpRouteDiscovery(opts) {
92
92
  const kind = opts.kind ?? "handler";
93
- return opts.importNames.map((importName) => ({
93
+ const calls = opts.importNames.map((importName) => ({
94
94
  kind,
95
95
  match: {
96
96
  type: "registrationCall",
@@ -111,6 +111,44 @@ function httpRouteDiscovery(opts) {
111
111
  ...opts.mount !== void 0 ? { mount: opts.mount } : {},
112
112
  requiresImport: [opts.importModule]
113
113
  }));
114
+ if (opts.importNames.length === 0) {
115
+ return calls;
116
+ }
117
+ return [
118
+ ...calls,
119
+ // Routes registered in a loop over an array of specs, a shape
120
+ // registration-call discovery cannot see. The receiver comes from
121
+ // the same import declaration as the calls above, so a loop that
122
+ // never touches this library's routable is left alone.
123
+ {
124
+ kind,
125
+ match: {
126
+ type: "registrationLoop",
127
+ elementShape: LOOP_ELEMENT_SHAPE,
128
+ receiver: {
129
+ importModule: opts.importModule,
130
+ importNames: [...opts.importNames]
131
+ }
132
+ },
133
+ requiresImport: [opts.importModule]
134
+ }
135
+ ];
136
+ }
137
+ var LOOP_ELEMENT_SHAPE = {
138
+ methodKey: "method",
139
+ pathKey: "path",
140
+ handlerKey: "handler"
141
+ };
142
+ function registrationHelperDiscovery(helpers, kind = "handler") {
143
+ return helpers.map((helper) => ({
144
+ kind,
145
+ match: {
146
+ type: "registrationTemplate",
147
+ helperName: helper.helperName,
148
+ ...helper.importModule !== void 0 ? { importModule: helper.importModule } : {},
149
+ registrations: helper.registrations.map((one) => ({ ...one }))
150
+ }
151
+ }));
114
152
  }
115
153
  function unwrapJsonStringify(body) {
116
154
  if (body === null || typeof body !== "object") {
@@ -1083,6 +1121,7 @@ export {
1083
1121
  makeTransitionId,
1084
1122
  mapReading,
1085
1123
  paramToInput,
1124
+ registrationHelperDiscovery,
1086
1125
  sharedGatingConditions,
1087
1126
  terminalToOutput,
1088
1127
  unreadableReading,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/extractor",
3
- "version": "0.14.1",
3
+ "version": "0.16.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.14.1"
48
+ "@suss/behavioral-ir": "0.16.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "tsup": "*",