@suss/packs 0.33.0 → 0.34.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.
Files changed (63) hide show
  1. package/README.md +10 -11
  2. package/dist/activerecord.d.ts +7 -0
  3. package/dist/activerecord.js +34 -4
  4. package/dist/apollo-client.js +26 -39
  5. package/dist/apollo.js +12 -20
  6. package/dist/aws-dynamodb.js +4 -4
  7. package/dist/aws-eventbridge.js +3 -3
  8. package/dist/aws-lambda.js +31 -54
  9. package/dist/aws-s3.js +1 -1
  10. package/dist/aws-secrets-manager.js +1 -1
  11. package/dist/aws-sns.js +2 -2
  12. package/dist/aws-sqs.js +20 -11
  13. package/dist/aws-ssm.js +1 -1
  14. package/dist/axios.js +13 -27
  15. package/dist/bigquery-python.js +3 -3
  16. package/dist/bigquery.js +1 -1
  17. package/dist/{chunk-NYWZHQBJ.js → chunk-GK4BILHB.js} +8 -9
  18. package/dist/cloudflare-workers.js +26 -35
  19. package/dist/drizzle.js +15 -20
  20. package/dist/express.js +3 -6
  21. package/dist/fabrication.d.ts +1 -0
  22. package/dist/fabrication.js +36 -0
  23. package/dist/factory-bot.d.ts +1 -0
  24. package/dist/factory-bot.js +36 -0
  25. package/dist/faraday.js +4 -5
  26. package/dist/fastapi.js +21 -5
  27. package/dist/fastify.js +10 -14
  28. package/dist/fetch.js +3 -4
  29. package/dist/flask-restx.js +19 -12
  30. package/dist/gcs.js +1 -1
  31. package/dist/graphql-ruby.js +20 -16
  32. package/dist/hono.js +17 -27
  33. package/dist/httpx.js +3 -5
  34. package/dist/minitest.d.ts +1 -0
  35. package/dist/minitest.js +59 -0
  36. package/dist/mongoose.js +7 -7
  37. package/dist/nestjs-graphql.js +14 -25
  38. package/dist/nestjs-microservices.js +9 -10
  39. package/dist/nestjs-rest.js +42 -22
  40. package/dist/net-http.js +5 -6
  41. package/dist/nextjs.js +16 -17
  42. package/dist/node.js +144 -28
  43. package/dist/package-exports.js +1 -1
  44. package/dist/pg.js +3 -2
  45. package/dist/prisma.js +281 -48
  46. package/dist/pytest.d.ts +1 -0
  47. package/dist/pytest.js +81 -0
  48. package/dist/rails.js +46 -27
  49. package/dist/react-query.js +1 -1
  50. package/dist/react-router.js +21 -48
  51. package/dist/react.js +14 -5
  52. package/dist/redis.js +1 -1
  53. package/dist/requests.js +4 -5
  54. package/dist/rspec.d.ts +1 -0
  55. package/dist/rspec.js +135 -0
  56. package/dist/sqlalchemy.js +1 -1
  57. package/dist/sqlmodel.js +7 -8
  58. package/dist/ts-rest.js +6 -8
  59. package/dist/typeorm.d.ts +1 -0
  60. package/dist/typeorm.js +291 -0
  61. package/dist/vitest.d.ts +1 -0
  62. package/dist/vitest.js +64 -0
  63. package/package.json +47 -11
@@ -45,12 +45,12 @@ var JSX_ROUTES = {
45
45
  };
46
46
  var optionsSchema = z.object({
47
47
  /**
48
- * Helpers this project throws HTTP errors through, as
49
- * `throw myHelper(new HttpError.NotFound(), body)`. React Router
50
- * declares no such helper, so nothing is assumed by default and a
51
- * project that installs this pack never matches a call on a name some
52
- * other codebase happened to use. The thrown argument's class name
53
- * gives the status, read against the `http-errors` constructors.
48
+ * Helpers this project throws HTTP errors through, as in
49
+ * `throw myHelper(new HttpError.NotFound(), body)`. React Router has
50
+ * no such helper, so the list is empty by default, and the pack never
51
+ * matches a call on a name another codebase happened to use. The
52
+ * status comes from the class name of the thrown argument, looked up
53
+ * in the `http-errors` constructors.
54
54
  */
55
55
  errorHelpers: z.array(z.string()).optional()
56
56
  }).strict();
@@ -62,32 +62,16 @@ function reactRouterFramework(options = {}) {
62
62
  discovery: [
63
63
  {
64
64
  kind: "loader",
65
+ // The README explains why no route comes from the file name.
65
66
  match: { type: "namedExport", names: ["loader"] },
66
- // No route derived from the filename. React Router only
67
- // reads routes that way when the project opted in by
68
- // importing @react-router/fs-routes, and the pack language
69
- // has no way to say "only when that import is there". A
70
- // loader whose route came out of a guess pairs with whatever
71
- // consumer matches the guess, which is worse than pairing
72
- // with nothing.
73
- // Empty gate: route files often re-export `loader` /
74
- // `action` from non-router-importing modules
75
- // (server-side data functions, shared util re-exports).
76
- // A heuristic gate would miss those. The dispatch only
77
- // looks at named exports, so paying for the walk on
78
- // every file is fine.
67
+ // Route files often re-export `loader` and `action` from modules
68
+ // that never import the router. The match reads only named
69
+ // exports, so running it on every file costs little.
79
70
  requiresImport: []
80
71
  },
81
72
  {
82
73
  kind: "action",
83
74
  match: { type: "namedExport", names: ["action"] },
84
- // No route derived from the filename. React Router only
85
- // reads routes that way when the project opted in by
86
- // importing @react-router/fs-routes, and the pack language
87
- // has no way to say "only when that import is there". A
88
- // loader whose route came out of a guess pairs with whatever
89
- // consumer matches the guess, which is worse than pairing
90
- // with nothing.
91
75
  requiresImport: []
92
76
  },
93
77
  {
@@ -96,10 +80,9 @@ function reactRouterFramework(options = {}) {
96
80
  requiresImport: []
97
81
  },
98
82
  {
99
- // The route tree the app declares in its own JSX, which is how
100
- // most React Router apps say what serves which URL. Gated on
101
- // the router import, since the whole pattern is written with
102
- // names that come out of it.
83
+ // Most React Router apps declare their routes in JSX. Every name
84
+ // this pattern matches comes from the router, so it needs the
85
+ // router import.
103
86
  kind: "component",
104
87
  match: JSX_ROUTES,
105
88
  requiresImport: ROUTER_MODULES
@@ -112,9 +95,8 @@ function reactRouterFramework(options = {}) {
112
95
  match: {
113
96
  type: "functionCall",
114
97
  functionName: "json",
115
- // These names come from the router, so a same-named helper
116
- // the project wrote is a different function, with its own
117
- // argument order, and must not match.
98
+ // A project helper with the same name may order its arguments
99
+ // differently, so only the router's own helpers match.
118
100
  requiresImport: RESPONSE_MODULES
119
101
  },
120
102
  extraction: {
@@ -128,9 +110,6 @@ function reactRouterFramework(options = {}) {
128
110
  match: {
129
111
  type: "functionCall",
130
112
  functionName: "data",
131
- // These names come from the router, so a same-named helper
132
- // the project wrote is a different function, with its own
133
- // argument order, and must not match.
134
113
  requiresImport: RESPONSE_MODULES
135
114
  },
136
115
  extraction: {
@@ -144,9 +123,6 @@ function reactRouterFramework(options = {}) {
144
123
  match: {
145
124
  type: "functionCall",
146
125
  functionName: "redirect",
147
- // These names come from the router, so a same-named helper
148
- // the project wrote is a different function, with its own
149
- // argument order, and must not match.
150
126
  requiresImport: RESPONSE_MODULES
151
127
  },
152
128
  extraction: {
@@ -155,25 +131,22 @@ function reactRouterFramework(options = {}) {
155
131
  }
156
132
  },
157
133
  {
158
- // What a routed component renders. The route says which
159
- // URL reaches this component, and the JSX it returns is what
160
- // that URL renders, so the pack reads both rather than
161
- // reporting the route and nothing behind it.
134
+ // The route gives the URL and the returned JSX gives what that
135
+ // URL renders, so a routed component records both.
162
136
  kind: "render",
163
137
  match: { type: "jsxReturn" },
164
138
  extraction: {}
165
139
  },
166
140
  {
167
- // Loaders return data directly
141
+ // A loader can return plain data without a helper.
168
142
  kind: "return",
169
143
  match: { type: "returnShape" },
170
144
  extraction: {
171
145
  body: { from: "argument", position: 0 }
172
146
  }
173
147
  },
174
- // A project's error helper puts the status in the class name of its
175
- // argument, so resolve through `argumentConstructor` rather
176
- // than taking the argument's raw source text as a status value.
148
+ // The status is in the class name of the helper's argument, so it is
149
+ // read through `argumentConstructor`.
177
150
  ...(options.errorHelpers ?? []).map((helper) => ({
178
151
  kind: "throw",
179
152
  match: {
@@ -209,7 +182,7 @@ var declares = {
209
182
  { ecosystem: "npm", name: "react-router" },
210
183
  { ecosystem: "npm", name: "react-router-dom" }
211
184
  ],
212
- reads: "React Router loaders / actions / routes."
185
+ reads: "React Router loaders, actions and routes."
213
186
  };
214
187
  var index_default = reactRouterFramework;
215
188
  export {
package/dist/react.js CHANGED
@@ -182,8 +182,8 @@ function disambiguateHandlers(raw, componentName) {
182
182
  kind: "handler",
183
183
  name,
184
184
  inputMapping: EVENT_HANDLER_INPUT,
185
- // The element and prop a handler hangs off already shape the
186
- // summary name; no reader consumes them as metadata (#462).
185
+ // The element and prop are already in the summary name, and nothing
186
+ // reads them from metadata (#462).
187
187
  metadata: {
188
188
  react: {
189
189
  kind: "handler",
@@ -226,8 +226,8 @@ function synthesizeUseEffects(componentName, parentFunc, ctx) {
226
226
  kind: "effect",
227
227
  component: componentName,
228
228
  index,
229
- // `null` deps means the second argument was absent
230
- // (re-runs every render); `[]` means mount-only.
229
+ // `null` means no deps argument, so the effect runs after every
230
+ // render. `[]` means it runs on mount only.
231
231
  deps
232
232
  }
233
233
  }
@@ -236,6 +236,7 @@ function synthesizeUseEffects(componentName, parentFunc, ctx) {
236
236
  }
237
237
  return out;
238
238
  }
239
+ var PASS_THROUGH = ["useCallback", "memo", "forwardRef"];
239
240
  function reactFramework() {
240
241
  return {
241
242
  name: "react",
@@ -297,6 +298,14 @@ function reactFramework() {
297
298
  type: "componentProps",
298
299
  paramPosition: 0
299
300
  },
301
+ // Each hands back the function it was given, so a call through the
302
+ // result runs that function. `useMemo` and `lazy` are left out: one
303
+ // returns what its argument returns, the other a module's export.
304
+ transparentWrappers: PASS_THROUGH.map((name) => ({
305
+ module: "react",
306
+ name,
307
+ argument: 0
308
+ })),
300
309
  subUnits: reactSubUnits,
301
310
  discoverUnits: (sourceFile, ctx) => [
302
311
  ...reactComponentExports(sourceFile, ctx),
@@ -308,7 +317,7 @@ var declares = {
308
317
  kind: "framework",
309
318
  package: "@suss/framework-react",
310
319
  dependencies: [{ ecosystem: "npm", name: "react" }],
311
- reads: "React function components, event handlers, `useEffect` bodies."
320
+ reads: "React function components, event handlers and `useEffect` bodies."
312
321
  };
313
322
  var index_default = reactFramework;
314
323
  export {
package/dist/redis.js CHANGED
@@ -95,7 +95,7 @@ var declares = {
95
95
  { ecosystem: "npm", name: "iovalkey" },
96
96
  { ecosystem: "npm", name: "redis" }
97
97
  ],
98
- reads: "Redis, Valkey and node-redis commands, emits storage-access interactions."
98
+ reads: "Redis, Valkey and node-redis commands. Each one becomes a storage-access interaction."
99
99
  };
100
100
  var index_default = redisFramework;
101
101
  export {
package/dist/requests.js CHANGED
@@ -18,9 +18,8 @@ function requestsClient() {
18
18
  type: "clientCall",
19
19
  importModule: ["requests"],
20
20
  verbAttributeNames: VERB_FUNCTIONS,
21
- // Every one of them takes the URL first, and `url=` is the
22
- // keyword for all of them but `request`, which takes `url`
23
- // second and still calls it that.
21
+ // The verb functions take the URL first and `request` takes it
22
+ // second. All of them accept it as `url=`.
24
23
  url: { position: 0, keyword: "url" },
25
24
  methodCall: {
26
25
  attribute: "request",
@@ -29,8 +28,8 @@ function requestsClient() {
29
28
  urlPosition: 1
30
29
  },
31
30
  receiverConstructors: ["Session"],
32
- // What a caller reads off the response it got back. A test on
33
- // one of these says which statuses the caller handles.
31
+ // A caller's condition on one of these members shows which
32
+ // statuses it handles.
34
33
  response: {
35
34
  statusCode: ["status_code"],
36
35
  success: ["ok"],
@@ -0,0 +1 @@
1
+ export { declares, default, optionsSchema } from '@suss/framework-rspec';
package/dist/rspec.js ADDED
@@ -0,0 +1,135 @@
1
+ // ../framework/rspec/dist/index.js
2
+ import { z } from "zod";
3
+ var optionsSchema = z.object({
4
+ /**
5
+ * The spec files to read, matched on whole path segments from the
6
+ * end. `suss extract --intent` fills it in from the tests the PRDs
7
+ * list, so a run reads those files and no others. Left out, every
8
+ * `*_spec.rb` file is read.
9
+ */
10
+ files: z.array(z.string().min(1)).optional()
11
+ }).strict();
12
+ var RUNNER_METHODS = [
13
+ "expect",
14
+ "is_expected",
15
+ "allow",
16
+ "allow_any_instance_of",
17
+ "expect_any_instance_of",
18
+ "receive",
19
+ "receive_messages",
20
+ "have_received",
21
+ "double",
22
+ "instance_double",
23
+ "class_double",
24
+ "object_double",
25
+ "spy",
26
+ "instance_spy",
27
+ "stub_const",
28
+ "eq",
29
+ "eql",
30
+ "equal",
31
+ "be",
32
+ "be_a",
33
+ "be_an",
34
+ "be_nil",
35
+ "be_truthy",
36
+ "be_falsey",
37
+ "be_within",
38
+ "include",
39
+ "match",
40
+ "match_array",
41
+ "contain_exactly",
42
+ "raise_error",
43
+ "change",
44
+ "have_attributes",
45
+ "an_instance_of",
46
+ "a_kind_of",
47
+ "anything",
48
+ "hash_including",
49
+ "satisfy",
50
+ "output",
51
+ "all"
52
+ ];
53
+ function rspecFramework(options = {}) {
54
+ return {
55
+ name: "rspec",
56
+ protocol: "in-process",
57
+ discovery: [],
58
+ tests: [
59
+ {
60
+ filePatterns: ["*_spec.rb"],
61
+ receiver: "RSpec",
62
+ groupNames: ["describe", "context", "feature", "example_group"],
63
+ skippedGroupNames: ["xdescribe", "xcontext", "xfeature"],
64
+ sharedGroupNames: [
65
+ "shared_examples",
66
+ "shared_examples_for",
67
+ "shared_context"
68
+ ],
69
+ sharedIncludes: [
70
+ { method: "it_behaves_like", nestedTitle: "behaves like" },
71
+ {
72
+ method: "it_should_behave_like",
73
+ nestedTitle: "it should behave like"
74
+ },
75
+ { method: "include_examples" },
76
+ { method: "include_context" }
77
+ ],
78
+ exampleNames: ["it", "specify", "example", "scenario", "its"],
79
+ skippedExampleNames: [
80
+ "xit",
81
+ "xspecify",
82
+ "xexample",
83
+ "xscenario",
84
+ "skip",
85
+ "pending"
86
+ ],
87
+ skipStatements: ["skip", "pending"],
88
+ skipMetadata: ["skip"],
89
+ beforeHooks: ["before", "prepend_before", "append_before"],
90
+ lazyValues: ["let"],
91
+ eagerValues: ["let!"],
92
+ subjectNames: { lazy: ["subject"], eager: ["subject!"] },
93
+ subjectReads: ["subject", "is_expected"],
94
+ subjectValue: "subject",
95
+ describedClass: "described_class",
96
+ runnerMethods: RUNNER_METHODS,
97
+ predicateMatchers: [
98
+ { prefix: "be_", methodPrefix: "", methodSuffix: "?" },
99
+ { prefix: "have_", methodPrefix: "has_", methodSuffix: "?" }
100
+ ],
101
+ expectations: {
102
+ starts: ["expect"],
103
+ onSubject: "is_expected",
104
+ runs: ["to", "not_to", "to_not"]
105
+ },
106
+ mocks: {
107
+ targets: [
108
+ "allow",
109
+ "expect",
110
+ "allow_any_instance_of",
111
+ "expect_any_instance_of"
112
+ ],
113
+ expectations: ["to", "not_to", "to_not"],
114
+ messages: ["receive", "receive_messages"],
115
+ constantStubs: ["stub_const"],
116
+ doubles: ["instance_double", "class_double"],
117
+ constantDoubleMethod: "as_stubbed_const"
118
+ },
119
+ ...options.files !== void 0 ? { files: options.files } : {}
120
+ }
121
+ ]
122
+ };
123
+ }
124
+ var declares = {
125
+ kind: "framework",
126
+ package: "@suss/framework-rspec",
127
+ dependencies: [],
128
+ reads: "RSpec examples, named by their group titles, so a PRD scenario can say which test covers it."
129
+ };
130
+ var index_default = rspecFramework;
131
+ export {
132
+ declares,
133
+ index_default as default,
134
+ optionsSchema
135
+ };
@@ -2,7 +2,7 @@ import {
2
2
  declares,
3
3
  index_default,
4
4
  optionsSchema
5
- } from "./chunk-NYWZHQBJ.js";
5
+ } from "./chunk-GK4BILHB.js";
6
6
  export {
7
7
  declares,
8
8
  index_default as default,
package/dist/sqlmodel.js CHANGED
@@ -2,16 +2,15 @@ import {
2
2
  sqlalchemyModels,
3
3
  sqlalchemyRawSql,
4
4
  sqlalchemyStorage
5
- } from "./chunk-NYWZHQBJ.js";
5
+ } from "./chunk-GK4BILHB.js";
6
6
 
7
7
  // ../framework/sqlmodel/dist/index.js
8
8
  import { z } from "zod";
9
9
  import { storageSystemOption } from "@suss/extractor";
10
10
  var optionsSchema = z.object({
11
11
  /**
12
- * Which database is behind the engine. SQLModel talks to all of them
13
- * and the URL says which, so this is the project's own choice rather
14
- * than something the library settles.
12
+ * Which database is behind the engine. SQLModel works with several,
13
+ * and the connection URL picks one, so the project has to set this.
15
14
  */
16
15
  storageSystem: storageSystemOption
17
16
  }).strict();
@@ -60,9 +59,9 @@ function sqlmodelStorage(options) {
60
59
  module: "sqlmodel",
61
60
  queryTypes: ["Select", "SelectOfScalar", "Update", "Delete", "Insert"],
62
61
  writes: ["update", "delete", "insert", "commit"],
63
- // `select(Item).where(...)` imports the constructor from the package
64
- // root, so there is no project method in between whose return says
65
- // what it is.
62
+ // `select(Item).where(...)` calls a constructor imported from the
63
+ // package root, so there is no project method in the chain whose
64
+ // return type the adapter could read.
66
65
  queryFunctions: ["select", "insert", "update", "delete"],
67
66
  valueMethods: ["values"],
68
67
  storageSystem: options.storageSystem
@@ -114,7 +113,7 @@ var declares = {
114
113
  kind: "effects",
115
114
  package: "@suss/framework-sqlmodel",
116
115
  dependencies: [{ ecosystem: "pypi", name: "sqlmodel" }],
117
- reads: "SQLModel calls (Python): says which types a query comes back as and which methods write, under the modules SQLModel exports them from, and includes the SQLAlchemy patterns a SQLModel project also reaches.",
116
+ reads: "SQLModel calls (Python). The pack declares which types a query returns and which methods write, under the modules SQLModel exports them from. It also includes the SQLAlchemy patterns, since a SQLModel project reaches those too.",
118
117
  configuration: {
119
118
  file: "suss.sqlmodel.json",
120
119
  example: { storageSystem: "postgresql" },
package/dist/ts-rest.js CHANGED
@@ -17,10 +17,8 @@ function tsRestFramework() {
17
17
  method: { type: "fromContract" },
18
18
  path: { type: "fromContract" }
19
19
  },
20
- // Pre-filter to ts-rest server callers. The `@ts-rest/`
21
- // prefix sweeps any of the framework's adapters in one
22
- // go (`@ts-rest/express` / `@ts-rest/fastify` /
23
- // `@ts-rest/nest` / etc.).
20
+ // The `@ts-rest` prefix matches every server adapter, such as
21
+ // `@ts-rest/express` and `@ts-rest/fastify`.
24
22
  requiresImport: ["@ts-rest"]
25
23
  },
26
24
  {
@@ -50,9 +48,9 @@ function tsRestFramework() {
50
48
  body: { from: "property", name: "body" }
51
49
  }
52
50
  },
53
- // A consumer of a ts-rest client finishes the way any function
54
- // does, by returning or throwing. Without these two it matches
55
- // no terminal at all and its summary says nothing.
51
+ // A function that uses a ts-rest client ends by returning or
52
+ // throwing. Without these two it would not match any terminal, and
53
+ // its summary would have no transitions.
56
54
  { kind: "return", match: { type: "returnStatement" }, extraction: {} },
57
55
  { kind: "throw", match: { type: "throwExpression" }, extraction: {} }
58
56
  ],
@@ -87,7 +85,7 @@ var declares = {
87
85
  kind: "framework",
88
86
  package: "@suss/framework-ts-rest",
89
87
  dependencies: [{ ecosystem: "npm", name: "@ts-rest/core" }],
90
- reads: "ts-rest providers + clients (contract-backed)."
88
+ reads: "ts-rest handlers and clients, and the contract both sides are built from."
91
89
  };
92
90
  var index_default = tsRestFramework;
93
91
  export {
@@ -0,0 +1 @@
1
+ export { declares, default, optionsSchema } from '@suss/framework-typeorm';