@suss/packs 0.33.1 → 0.34.1

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 (64) hide show
  1. package/README.md +10 -11
  2. package/dist/activerecord.d.ts +7 -0
  3. package/dist/activerecord.js +53 -5
  4. package/dist/aiohttp.js +20 -9
  5. package/dist/apollo-client.js +26 -39
  6. package/dist/apollo.js +12 -20
  7. package/dist/aws-dynamodb.js +4 -4
  8. package/dist/aws-eventbridge.js +3 -3
  9. package/dist/aws-lambda.js +31 -54
  10. package/dist/aws-s3.js +1 -1
  11. package/dist/aws-secrets-manager.js +1 -1
  12. package/dist/aws-sns.js +2 -2
  13. package/dist/aws-sqs.js +20 -11
  14. package/dist/aws-ssm.js +1 -1
  15. package/dist/axios.js +36 -37
  16. package/dist/bigquery-python.js +3 -3
  17. package/dist/bigquery.js +1 -1
  18. package/dist/{chunk-NYWZHQBJ.js → chunk-GK4BILHB.js} +8 -9
  19. package/dist/cloudflare-workers.js +26 -35
  20. package/dist/drizzle.js +15 -20
  21. package/dist/express.js +3 -6
  22. package/dist/fabrication.d.ts +1 -0
  23. package/dist/fabrication.js +36 -0
  24. package/dist/factory-bot.d.ts +1 -0
  25. package/dist/factory-bot.js +36 -0
  26. package/dist/faraday.js +8 -6
  27. package/dist/fastapi.js +123 -5
  28. package/dist/fastify.js +10 -14
  29. package/dist/fetch.js +12 -6
  30. package/dist/flask-restx.js +19 -12
  31. package/dist/gcs.js +1 -1
  32. package/dist/graphql-ruby.js +20 -16
  33. package/dist/hono.js +17 -27
  34. package/dist/httpx.js +8 -6
  35. package/dist/minitest.d.ts +1 -0
  36. package/dist/minitest.js +59 -0
  37. package/dist/mongoose.js +7 -7
  38. package/dist/nestjs-graphql.js +14 -25
  39. package/dist/nestjs-microservices.js +9 -10
  40. package/dist/nestjs-rest.js +193 -23
  41. package/dist/net-http.js +9 -7
  42. package/dist/nextjs.js +16 -17
  43. package/dist/node.js +139 -23
  44. package/dist/package-exports.js +1 -1
  45. package/dist/pg.js +3 -2
  46. package/dist/prisma.js +375 -56
  47. package/dist/pytest.d.ts +1 -0
  48. package/dist/pytest.js +81 -0
  49. package/dist/rails.js +1792 -132
  50. package/dist/react-query.js +1 -1
  51. package/dist/react-router.js +21 -48
  52. package/dist/react.js +14 -5
  53. package/dist/redis.js +1 -1
  54. package/dist/requests.js +27 -19
  55. package/dist/rspec.d.ts +1 -0
  56. package/dist/rspec.js +135 -0
  57. package/dist/sqlalchemy.js +1 -1
  58. package/dist/sqlmodel.js +7 -8
  59. package/dist/ts-rest.js +6 -8
  60. package/dist/typeorm.d.ts +1 -0
  61. package/dist/typeorm.js +291 -0
  62. package/dist/vitest.d.ts +1 -0
  63. package/dist/vitest.js +64 -0
  64. package/package.json +47 -11
package/dist/fastapi.js CHANGED
@@ -1,11 +1,88 @@
1
1
  // ../framework/fastapi/dist/index.js
2
2
  import { z } from "zod";
3
+ var STARLETTE_STATUS = {
4
+ HTTP_100_CONTINUE: 100,
5
+ HTTP_101_SWITCHING_PROTOCOLS: 101,
6
+ HTTP_102_PROCESSING: 102,
7
+ HTTP_103_EARLY_HINTS: 103,
8
+ HTTP_200_OK: 200,
9
+ HTTP_201_CREATED: 201,
10
+ HTTP_202_ACCEPTED: 202,
11
+ HTTP_203_NON_AUTHORITATIVE_INFORMATION: 203,
12
+ HTTP_204_NO_CONTENT: 204,
13
+ HTTP_205_RESET_CONTENT: 205,
14
+ HTTP_206_PARTIAL_CONTENT: 206,
15
+ HTTP_207_MULTI_STATUS: 207,
16
+ HTTP_208_ALREADY_REPORTED: 208,
17
+ HTTP_226_IM_USED: 226,
18
+ HTTP_300_MULTIPLE_CHOICES: 300,
19
+ HTTP_301_MOVED_PERMANENTLY: 301,
20
+ HTTP_302_FOUND: 302,
21
+ HTTP_303_SEE_OTHER: 303,
22
+ HTTP_304_NOT_MODIFIED: 304,
23
+ HTTP_305_USE_PROXY: 305,
24
+ HTTP_306_RESERVED: 306,
25
+ HTTP_307_TEMPORARY_REDIRECT: 307,
26
+ HTTP_308_PERMANENT_REDIRECT: 308,
27
+ HTTP_400_BAD_REQUEST: 400,
28
+ HTTP_401_UNAUTHORIZED: 401,
29
+ HTTP_402_PAYMENT_REQUIRED: 402,
30
+ HTTP_403_FORBIDDEN: 403,
31
+ HTTP_404_NOT_FOUND: 404,
32
+ HTTP_405_METHOD_NOT_ALLOWED: 405,
33
+ HTTP_406_NOT_ACCEPTABLE: 406,
34
+ HTTP_407_PROXY_AUTHENTICATION_REQUIRED: 407,
35
+ HTTP_408_REQUEST_TIMEOUT: 408,
36
+ HTTP_409_CONFLICT: 409,
37
+ HTTP_410_GONE: 410,
38
+ HTTP_411_LENGTH_REQUIRED: 411,
39
+ HTTP_412_PRECONDITION_FAILED: 412,
40
+ HTTP_413_REQUEST_ENTITY_TOO_LARGE: 413,
41
+ HTTP_413_CONTENT_TOO_LARGE: 413,
42
+ HTTP_414_REQUEST_URI_TOO_LONG: 414,
43
+ HTTP_414_URI_TOO_LONG: 414,
44
+ HTTP_415_UNSUPPORTED_MEDIA_TYPE: 415,
45
+ HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE: 416,
46
+ HTTP_416_RANGE_NOT_SATISFIABLE: 416,
47
+ HTTP_417_EXPECTATION_FAILED: 417,
48
+ HTTP_418_IM_A_TEAPOT: 418,
49
+ HTTP_421_MISDIRECTED_REQUEST: 421,
50
+ HTTP_422_UNPROCESSABLE_ENTITY: 422,
51
+ HTTP_422_UNPROCESSABLE_CONTENT: 422,
52
+ HTTP_423_LOCKED: 423,
53
+ HTTP_424_FAILED_DEPENDENCY: 424,
54
+ HTTP_425_TOO_EARLY: 425,
55
+ HTTP_426_UPGRADE_REQUIRED: 426,
56
+ HTTP_428_PRECONDITION_REQUIRED: 428,
57
+ HTTP_429_TOO_MANY_REQUESTS: 429,
58
+ HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE: 431,
59
+ HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS: 451,
60
+ HTTP_500_INTERNAL_SERVER_ERROR: 500,
61
+ HTTP_501_NOT_IMPLEMENTED: 501,
62
+ HTTP_502_BAD_GATEWAY: 502,
63
+ HTTP_503_SERVICE_UNAVAILABLE: 503,
64
+ HTTP_504_GATEWAY_TIMEOUT: 504,
65
+ HTTP_505_HTTP_VERSION_NOT_SUPPORTED: 505,
66
+ HTTP_506_VARIANT_ALSO_NEGOTIATES: 506,
67
+ HTTP_507_INSUFFICIENT_STORAGE: 507,
68
+ HTTP_508_LOOP_DETECTED: 508,
69
+ HTTP_510_NOT_EXTENDED: 510,
70
+ HTTP_511_NETWORK_AUTHENTICATION_REQUIRED: 511
71
+ };
72
+ var STATUS_MODULES = ["fastapi.status", "starlette.status"];
73
+ var STATUS_CONSTANTS = Object.fromEntries(
74
+ STATUS_MODULES.flatMap(
75
+ (module) => Object.entries(STARLETTE_STATUS).map(([name, status]) => [
76
+ `${module}.${name}`,
77
+ status
78
+ ])
79
+ )
80
+ );
3
81
  var optionsSchema = z.object({
4
82
  /**
5
- * Modules a project's own wrapper re-exports FastAPI's constructors
6
- * from. FastAPI's own module is always accepted. The wrapper's name
7
- * is the project's own choice, so it is supplied by whoever
8
- * configures the pack rather than hardcoded here.
83
+ * Modules that re-export FastAPI's constructors, on top of `fastapi`
84
+ * itself. A dependency stub fills this in; a project's config file
85
+ * may not set it.
9
86
  */
10
87
  wrapperModules: z.array(z.string()).optional()
11
88
  }).strict();
@@ -18,10 +95,42 @@ var VERB_ATTRIBUTE_NAMES = {
18
95
  head: "HEAD",
19
96
  options: "OPTIONS"
20
97
  };
98
+ var PARAMETER_SOURCES = {
99
+ Header: { role: "headers", underscoresAs: "-" },
100
+ Query: { role: "queryParams" },
101
+ Path: { role: "pathParams" },
102
+ Body: { role: "requestBody" },
103
+ Form: { role: "requestBody" },
104
+ Cookie: { role: "cookies" }
105
+ };
106
+ var REQUEST_SPELLING = {
107
+ headers: { path: ["headers"], saysWhichField: true },
108
+ query: { path: ["queryParams"], saysWhichField: true },
109
+ params: { path: ["pathParams"], saysWhichField: true },
110
+ body: { path: ["requestBody"], saysWhichField: true }
111
+ };
112
+ var INJECTED_PARAMETER_TYPES = [
113
+ "fastapi.Request",
114
+ "fastapi.Response",
115
+ "fastapi.BackgroundTasks",
116
+ "fastapi.WebSocket",
117
+ "fastapi.requests.Request",
118
+ "fastapi.requests.HTTPConnection",
119
+ "fastapi.responses.Response",
120
+ "fastapi.background.BackgroundTasks",
121
+ "fastapi.websockets.WebSocket",
122
+ "fastapi.security.SecurityScopes",
123
+ "starlette.requests.Request",
124
+ "starlette.requests.HTTPConnection",
125
+ "starlette.responses.Response",
126
+ "starlette.background.BackgroundTasks",
127
+ "starlette.websockets.WebSocket"
128
+ ];
21
129
  function fastapiFramework(options = {}) {
22
130
  return {
23
131
  name: "fastapi",
24
132
  protocol: "http",
133
+ requestSpelling: REQUEST_SPELLING,
25
134
  ...options.wrapperModules !== void 0 ? { projectModules: options.wrapperModules } : {},
26
135
  discovery: [
27
136
  {
@@ -33,10 +142,19 @@ function fastapiFramework(options = {}) {
33
142
  // FastAPI resolves both of these itself and calls the handler with
34
143
  // the result, so a parameter defaulted to one is never sent.
35
144
  injectedParameterCallees: ["Depends", "Security"],
145
+ // FastAPI also hands a route these objects by the annotation alone.
146
+ injectedParameterTypes: INJECTED_PARAMETER_TYPES,
147
+ parameterSources: PARAMETER_SOURCES,
148
+ parameterAliasKeyword: "alias",
36
149
  // FastAPI returns 200 for a route that declares no status.
37
150
  defaultStatusCode: 200,
151
+ // FastAPI checks every declared request input before the handler
152
+ // runs, and responds to a bad one itself.
153
+ validationFailureStatus: 422,
154
+ statusCodeConstants: STATUS_CONSTANTS,
38
155
  responseModelKeyword: "response_model",
39
156
  statusCodeKeyword: "status_code",
157
+ responsesKeyword: "responses",
40
158
  // FastAPI re-exports Starlette's class, and a project may import
41
159
  // it from either module.
42
160
  responseStatusCalls: [
@@ -106,7 +224,7 @@ var declares = {
106
224
  kind: "framework",
107
225
  package: "@suss/framework-fastapi",
108
226
  dependencies: [{ ecosystem: "pypi", name: "fastapi" }],
109
- reads: `FastAPI routes (Python): the verb comes from the decorator's own attribute name, \`APIRouter\` prefixes are composed one \`include_router\` hop deep, and \`response_model\` / \`status_code\` are taken as the declared contract.`
227
+ reads: `FastAPI routes (Python). The verb comes from the decorator's attribute name, \`APIRouter\` prefixes are composed one \`include_router\` hop deep, and \`response_model\` and \`status_code\` are taken as the declared contract.`
110
228
  };
111
229
  var index_default = fastapiFramework;
112
230
  export {
package/dist/fastify.js CHANGED
@@ -17,8 +17,8 @@ function fastifyFramework(_options = {}) {
17
17
  name: "fastify",
18
18
  protocol: "http",
19
19
  languages: ["typescript", "javascript"],
20
- // Fastify exposes the routable via either default `Fastify` or
21
- // named `fastify()`. Both drive handler registration the same way.
20
+ // Projects import the app factory as the default `Fastify` or the
21
+ // named `fastify`, and both register routes the same way.
22
22
  discovery: [
23
23
  ...httpRouteDiscovery({
24
24
  importModule: "fastify",
@@ -48,7 +48,7 @@ function fastifyFramework(_options = {}) {
48
48
  }
49
49
  },
50
50
  {
51
- // reply.status(N).send(body), `.status` is the Express-style alias
51
+ // reply.status(N).send(body), the alias for `code`
52
52
  kind: "response",
53
53
  match: {
54
54
  type: "parameterMethodCall",
@@ -74,9 +74,8 @@ function fastifyFramework(_options = {}) {
74
74
  }
75
75
  },
76
76
  {
77
- // reply.redirect(url) or reply.redirect(N, url)
78
- // Arg 0 is a status code only in the 2-arg form; minArgs prevents
79
- // extracting the URL string as a status code in the 1-arg form.
77
+ // reply.redirect(url) or reply.redirect(N, url). `minArgs` keeps
78
+ // the URL in the one-argument form from being read as a status.
80
79
  kind: "response",
81
80
  match: {
82
81
  type: "parameterMethodCall",
@@ -89,19 +88,16 @@ function fastifyFramework(_options = {}) {
89
88
  }
90
89
  },
91
90
  {
92
- // throw new Error(...) / throw httpErrors.notFound() / etc.
93
- // Status code is left for the consumer to infer from exception type;
94
- // Fastify error libraries vary too widely to map here.
91
+ // throw httpErrors.notFound(). Fastify error libraries differ too
92
+ // much to map a status here, so the throw does not record one.
95
93
  kind: "throw",
96
94
  match: { type: "throwExpression" },
97
95
  extraction: {}
98
96
  },
99
97
  {
100
- // `return user`, `return { id, name }`, `return await db.find(id)`,
101
- // Fastify serialises the returned value as a 200 response body.
102
- // `excludeCallReturns: true` keeps `return reply.send(...)` out
103
- // of this branch: that call already lands as a parameterMethodCall
104
- // terminal above, and matching it here would double-fire.
98
+ // `return user`: Fastify sends the value as a 200 body. Without
99
+ // `excludeCallReturns`, `return reply.send(...)` would match here
100
+ // and again as the `send` terminal above.
105
101
  kind: "response",
106
102
  match: {
107
103
  type: "returnStatement",
package/dist/fetch.js CHANGED
@@ -19,12 +19,12 @@ function webFetchPack() {
19
19
  property: "method",
20
20
  default: "GET"
21
21
  },
22
- path: { type: "fromArgument", position: 0 }
22
+ path: { type: "fromArgument", position: 0 },
23
+ options: { position: 1 }
23
24
  },
24
- // No import gate: `fetch` is a global. Walk every file,
25
- // the discovery dispatch is itself cheap (just looks for
26
- // call expressions named `fetch`), so the lack of pre-
27
- // filter doesn't blow up perf.
25
+ // `fetch` is a global, so there is no import to gate on. Walking
26
+ // every file stays cheap because discovery only looks for calls
27
+ // named `fetch`.
28
28
  requiresImport: []
29
29
  }
30
30
  ],
@@ -55,7 +55,12 @@ function webFetchPack() {
55
55
  { name: "text", access: "method", semantics: { type: "body" } },
56
56
  { name: "body", access: "property", semantics: { type: "body" } },
57
57
  { name: "headers", access: "property", semantics: { type: "headers" } }
58
- ]
58
+ ],
59
+ // fetch follows 301, 302, 303, 307 and 308, so the caller sees the
60
+ // final response, unless the call passes `redirect: "manual"` or
61
+ // `redirect: "error"`.
62
+ redirectDelivery: "followed",
63
+ redirectOption: { name: "redirect", handsBack: ["manual", "error"] }
59
64
  };
60
65
  }
61
66
  var declares = {
@@ -63,6 +68,7 @@ var declares = {
63
68
  package: "@suss/client-web",
64
69
  dependencies: [],
65
70
  shippedWith: "typescript",
71
+ sourcePattern: /(?:^|[^.\w$]|\b(?:window|globalThis|self)\.)fetch\s*\(/m,
66
72
  reads: "Global `fetch` call sites."
67
73
  };
68
74
  var index_default = webFetchPack;
@@ -2,10 +2,9 @@
2
2
  import { z } from "zod";
3
3
  var optionsSchema = z.object({
4
4
  /**
5
- * Modules a project's own wrapper re-exports flask-restx's route
6
- * decorator from. flask-restx's own module is always accepted. The
7
- * wrapper's name is the project's own choice, so it is supplied by
8
- * whoever configures the pack rather than hardcoded here.
5
+ * Modules that re-export flask-restx's route decorator, on top of
6
+ * `flask_restx` itself. A dependency stub fills this in; a project's
7
+ * config file may not set it.
9
8
  */
10
9
  wrapperModules: z.array(z.string()).optional()
11
10
  }).strict();
@@ -30,8 +29,8 @@ function flaskRestxFramework(options = {}) {
30
29
  decoratorName: "route",
31
30
  verbMethodNames: VERB_METHOD_NAMES,
32
31
  pathParamSyntax: "flaskConverters",
33
- // What each of these settings means for a written prefix is the
34
- // grid in the Python adapter's README.
32
+ // The Python adapter's README has a table of what each of these
33
+ // settings does to a written prefix.
35
34
  pathRepeatedSlashes: "merged",
36
35
  routerComposition: {
37
36
  routerConstructorName: "Namespace",
@@ -53,11 +52,9 @@ function flaskRestxFramework(options = {}) {
53
52
  }
54
53
  }
55
54
  },
56
- // Flask returns 200 from a resource method that returns a value
57
- // and sets no status of its own.
58
55
  defaultStatusCode: 200,
59
56
  // Flask reads a status out of `return body, 201`, so the 200
60
- // above only applies where the body writes no status of its own.
57
+ // above applies only when the return writes no status.
61
58
  statusFromReturnedTuple: true,
62
59
  // flask-restx re-exports Flask's function, which re-exports
63
60
  // Werkzeug's, and a project may import it from any of the three.
@@ -70,8 +67,8 @@ function flaskRestxFramework(options = {}) {
70
67
  {
71
68
  type: "decoratedWrapper",
72
69
  attribute: "before_request",
73
- // A blueprint's own hook is not read: a route is decorated on
74
- // a namespace, and the blueprint it is served under is a mount.
70
+ // A blueprint's own hook is skipped, because a route is decorated
71
+ // on a namespace and the blueprint only mounts it.
75
72
  registrars: [
76
73
  {
77
74
  constructorName: "Flask",
@@ -100,7 +97,17 @@ function flaskRestxFramework(options = {}) {
100
97
  }
101
98
  ]
102
99
  }
103
- ]
100
+ ],
101
+ // A resource method reads the request off Flask's module-level object.
102
+ requestObjects: [{ module: "flask", name: "request" }],
103
+ // A path parameter is handed to the method, and the rest is read off
104
+ // Flask's `request`: `request.args` for the query string.
105
+ requestSpelling: {
106
+ headers: { path: ["request", "headers"], saysWhichField: true },
107
+ query: { path: ["request", "args"], saysWhichField: true },
108
+ params: { path: ["pathParams"], saysWhichField: true },
109
+ body: { path: ["request", "json"], saysWhichField: true }
110
+ }
104
111
  };
105
112
  }
106
113
  var declares = {
package/dist/gcs.js CHANGED
@@ -53,7 +53,7 @@ var declares = {
53
53
  kind: "effects",
54
54
  package: "@suss/framework-gcs",
55
55
  dependencies: [{ ecosystem: "npm", name: "@google-cloud/storage" }],
56
- reads: "Google Cloud Storage calls, emits storage-access interactions."
56
+ reads: "Google Cloud Storage calls. Each one becomes a storage-access interaction."
57
57
  };
58
58
  var index_default = gcsFramework;
59
59
  export {
@@ -4,20 +4,24 @@ import { z } from "zod";
4
4
  import { SCALAR_SHAPES } from "@suss/contract-graphql";
5
5
  var optionsSchema = z.object({
6
6
  /**
7
- * Directory a `mutation:` / `resolver:` field's referenced class is
8
- * looked up under, e.g. `path.join(repoRoot, "app/graphql")`.
9
- * Written relative, it is read relative to `configDirectory`, and
10
- * relative to the working directory when there is none.
7
+ * The directory the pack looks under for the class a `mutation:` or
8
+ * `resolver:` field references, for example
9
+ * `path.join(repoRoot, "app/graphql")`. A relative path resolves
10
+ * against `configDirectory`, or the working directory when that is
11
+ * unset.
11
12
  */
12
13
  root: z.string(),
13
14
  /**
14
- * The directory a relative `root` is read against: the config file's
15
- * own directory, or the directory the run reads when the options came
16
- * without one. The CLI supplies this; it is not written in the file
17
- * itself.
15
+ * The CLI fills this in with the config file's directory, or with the
16
+ * directory the run reads when there is no file. A config file does
17
+ * not set it.
18
18
  */
19
19
  configDirectory: z.string().optional(),
20
- /** Base classes beyond `Types::BaseObject` that also mark a class as declaring GraphQL fields. */
20
+ /**
21
+ * Base classes beyond the generated `Types::Base*` ones whose
22
+ * subclasses declare GraphQL fields. A config file may not set this; a
23
+ * dependency stub fills it.
24
+ */
21
25
  baseClassNames: z.array(z.string()).optional(),
22
26
  /** The schema-wide camelCase default. A `field` or `argument` call's own `camelize:` keyword overrides it for that one name. */
23
27
  camelize: z.boolean().optional()
@@ -72,10 +76,9 @@ function graphqlRubyFramework(options) {
72
76
  requiredDefault: true,
73
77
  camelizeKeyword: "camelize",
74
78
  camelizeDefault: options.camelize ?? true,
75
- // The standard scalars come from the shared SDL table, so a
76
- // contract read from Ruby compares against one read from SDL
77
- // without a vocabulary mismatch. graphql-ruby accepts Ruby's own
78
- // `Integer` class as a synonym for its `Int` scalar.
79
+ // The standard scalars come from the shared SDL table, so a contract
80
+ // read from Ruby uses the same shapes as one read from SDL.
81
+ // graphql-ruby also accepts Ruby's `Integer` class for `Int`.
79
82
  scalars: {
80
83
  ...SCALAR_SHAPES,
81
84
  Integer: { type: "number" }
@@ -98,7 +101,7 @@ function graphqlRubyFramework(options) {
98
101
  discovery: [pattern],
99
102
  // A resolver that reads a model through the dataloader passes the model
100
103
  // as an argument, so the read is recorded against that model when a
101
- // storage pack in the same run says it is one.
104
+ // storage pack in the same run recognizes it as one.
102
105
  loaders: [
103
106
  {
104
107
  loader: "dataloader",
@@ -121,12 +124,13 @@ var declares = {
121
124
  kind: "framework",
122
125
  package: "@suss/framework-graphql-ruby",
123
126
  dependencies: [{ ecosystem: "rubygems", name: "graphql" }],
124
- reads: "graphql-ruby class-based field DSL (Ruby), including `mutation:` / `resolver:` wiring one hop out to what the referenced class itself declares, and the model a resolver reads through `dataloader` when a storage pack in the run recognizes it.",
127
+ reads: "graphql-ruby class-based field DSL (Ruby). The pack follows `mutation:` and `resolver:` wiring one hop out to what the referenced class declares. It also records the model a resolver reads through `dataloader`, when a storage pack in the run recognizes that model.",
125
128
  configuration: {
126
129
  file: "suss.graphql-ruby.json",
127
130
  example: { root: "app/graphql" },
128
131
  required: true,
129
- why: "the directory a field's wired class is looked up under, read relative to this file. A Rails app generated by the library keeps its types and mutations under app/graphql."
132
+ why: "the directory a field's wired class is looked up under, read relative to this file. A Rails app generated by the library keeps its types and mutations under app/graphql.",
133
+ defaults: { root: "app/graphql" }
130
134
  }
131
135
  };
132
136
  var index_default = graphqlRubyFramework;
package/dist/hono.js CHANGED
@@ -37,21 +37,15 @@ function honoFramework(_options = {}) {
37
37
  name: "hono",
38
38
  protocol: "http",
39
39
  languages: ["typescript", "javascript"],
40
- // createRoute wraps its config without changing it; the call IS the
41
- // route object. Declared here because the wrapper's body lives in
42
- // the library, where nobody can read it.
40
+ // `createRoute` returns its config unchanged, so the call is the route
41
+ // object. The pack has to declare this because the function's body is
42
+ // in the library, where suss cannot read it.
43
43
  transparentWrappers: [
44
- { callee: "createRoute", argument: 0, module: "@hono/zod-openapi" }
44
+ { module: "@hono/zod-openapi", name: "createRoute", argument: 0 }
45
45
  ],
46
- // `new Hono()` and `new OpenAPIHono()` both register the same way.
47
- // Sub-apps mounted with `app.route(prefix, sub)` compose the
48
- // prefix into a route declared on the sub-app, following the sub-app
49
- // through an import when it's declared in another file. A mount
50
- // nested more than one level deep composes too, since the same
51
- // index a mount resolves to is asked again for its own mount; a
52
- // mount the resolution store can't follow to a concrete sub-app,
53
- // or whose prefix isn't a string literal, leaves the route's path
54
- // as written.
46
+ // A route on a sub-app mounted with `app.route(prefix, sub)` gets the
47
+ // prefix in front of its path, at any depth of nesting. The README
48
+ // covers the mounts that leave a path as written.
55
49
  discovery: [
56
50
  ...httpRouteDiscovery({
57
51
  importModule: "hono",
@@ -66,11 +60,9 @@ function honoFramework(_options = {}) {
66
60
  mount: { method: "route", prefixPosition: 0, targetPosition: 1 }
67
61
  }),
68
62
  {
69
- // app.openapi(route, handler), where the route is a
70
- // createRoute({ method, path, ... }) object that usually lives
71
- // on a shared contract in another file. The fact layer follows
72
- // the reference, and the route object has its own method and path on
73
- // it.
63
+ // `app.openapi(route, handler)`, where the route is often a
64
+ // `createRoute({ method, path })` object in another file. The
65
+ // method and path come off that object.
74
66
  kind: "handler",
75
67
  match: {
76
68
  type: "registrationCall",
@@ -109,9 +101,9 @@ function honoFramework(_options = {}) {
109
101
  importNames: ["Hono", "OpenAPIHono"],
110
102
  methods: METHODS
111
103
  }),
112
- // The createRoute object registered alongside the handler declares
113
- // the endpoint's responses, so a handler returning a status the
114
- // route never declares is a contract finding, not a style choice.
104
+ // The `createRoute` object lists the endpoint's responses, so a
105
+ // handler that returns a status missing from that list gets a
106
+ // contract finding.
115
107
  contractReading: {
116
108
  discovery: {
117
109
  importModule: "@hono/zod-openapi",
@@ -190,8 +182,8 @@ function honoFramework(_options = {}) {
190
182
  extraction: { defaultStatusCode: 404 }
191
183
  },
192
184
  {
193
- // throw new HTTPException(status, { message }); hono sends the
194
- // exception's status as the wire response.
185
+ // throw new HTTPException(status, { message }): Hono sends the
186
+ // exception's status as the response.
195
187
  kind: "throw",
196
188
  match: { type: "throwExpression" },
197
189
  extraction: {
@@ -203,14 +195,12 @@ function honoFramework(_options = {}) {
203
195
  producesResponse: true
204
196
  }
205
197
  ],
206
- // One parameter, the context, carrying the request and the response
207
- // methods together.
208
198
  inputMapping: {
209
199
  type: "positionalParams",
210
200
  params: [{ position: 0, role: "context" }]
211
201
  },
212
- // `c.req.header("x-tenant-id")` puts the field in the argument, and
213
- // a read records the method without it, so no read says which field.
202
+ // In `c.req.header("x-tenant-id")` the field is an argument, and the
203
+ // recorded read keeps only the method, so it loses which field it was.
214
204
  requestSpelling: {
215
205
  headers: { path: ["context", "req", "header"], saysWhichField: false },
216
206
  query: { path: ["context", "req", "query"], saysWhichField: false },
package/dist/httpx.js CHANGED
@@ -25,20 +25,22 @@ function httpxClient() {
25
25
  methodKeyword: "method",
26
26
  urlPosition: 1
27
27
  },
28
- // Both are written as a context manager as often as they are
29
- // assigned, and the adapter reads a `with ... as` the same way.
30
28
  receiverConstructors: ["Client", "AsyncClient"],
31
- // httpx spells the response the way requests does, which is
32
- // deliberate on its part.
29
+ // httpx copies the requests response API, with `is_success` in
30
+ // place of `ok`.
33
31
  response: {
34
32
  statusCode: ["status_code"],
35
33
  success: ["is_success"],
36
34
  body: ["json", "text", "content"],
37
- failureDelivery: "response"
35
+ failureDelivery: "response",
36
+ // httpx hands a 3xx back unless the call passes
37
+ // `follow_redirects=True`.
38
+ redirectDelivery: "response",
39
+ redirectOption: { name: "follow_redirects", handsBack: [false] }
38
40
  }
39
41
  }
40
42
  ],
41
- // Both classes document `__enter__` as giving back the client, so
43
+ // Both classes document `__enter__` as returning the client, so
42
44
  // `with httpx.Client() as c` puts the constructed client in c.
43
45
  contextManagers: [
44
46
  { module: "httpx", returnsSelf: ["Client", "AsyncClient"] }
@@ -0,0 +1 @@
1
+ export { declares, default, optionsSchema } from '@suss/framework-minitest';
@@ -0,0 +1,59 @@
1
+ // ../framework/minitest/dist/index.js
2
+ import { z } from "zod";
3
+ var optionsSchema = z.object({
4
+ /**
5
+ * The test 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
+ * `*_test.rb` file is read.
9
+ */
10
+ files: z.array(z.string().min(1)).optional()
11
+ }).strict();
12
+ var TEST_CASE_CLASSES = [
13
+ "Minitest::Test",
14
+ "ActiveSupport::TestCase",
15
+ "ActionDispatch::IntegrationTest",
16
+ "ActionDispatch::SystemTestCase",
17
+ "ActionController::TestCase",
18
+ "ActionMailer::TestCase",
19
+ "ActionView::TestCase",
20
+ "ActiveJob::TestCase",
21
+ "ActionCable::TestCase",
22
+ "ActionCable::Channel::TestCase",
23
+ "ActionCable::Connection::TestCase",
24
+ "Rails::Generators::TestCase"
25
+ ];
26
+ function minitestFramework(options = {}) {
27
+ return {
28
+ name: "minitest",
29
+ protocol: "in-process",
30
+ discovery: [],
31
+ testClasses: [
32
+ {
33
+ filePatterns: ["*_test.rb"],
34
+ baseClassNames: TEST_CASE_CLASSES,
35
+ testMethodPrefix: "test_",
36
+ testBlockMethods: ["test"],
37
+ blockTestName: { prefix: "test_", spacesAs: "_" },
38
+ setupBlockMethods: ["setup"],
39
+ setupMethodNames: ["setup", "before_setup"],
40
+ skipStatements: ["skip"],
41
+ assertionPrefixes: ["assert", "refute", "must_", "wont_", "flunk"],
42
+ stubMethods: ["stub", "stubs", "expects"],
43
+ ...options.files !== void 0 ? { files: options.files } : {}
44
+ }
45
+ ]
46
+ };
47
+ }
48
+ var declares = {
49
+ kind: "framework",
50
+ package: "@suss/framework-minitest",
51
+ dependencies: [],
52
+ reads: "Minitest and Rails test cases (Ruby), named by class and test method, so a PRD scenario can say which test covers it."
53
+ };
54
+ var index_default = minitestFramework;
55
+ export {
56
+ declares,
57
+ index_default as default,
58
+ optionsSchema
59
+ };
package/dist/mongoose.js CHANGED
@@ -97,9 +97,9 @@ var METHODS = {
97
97
  },
98
98
  findByIdAndDelete: { kind: "write", selector: BY_ID, fields: WHOLE_DOCUMENT },
99
99
  findOneAndReplace: { kind: "write", selector: filter(0), fields: payload(1) },
100
- // A document can be changed after it was built, which this does not
101
- // track, so a save is not reported as touching the fields the
102
- // constructor happened to state.
100
+ // The pack does not track changes to a document after it is built, so
101
+ // the constructor's fields may be stale by the save. A save touches
102
+ // every field.
103
103
  save: { kind: "write", fields: WHOLE_DOCUMENT }
104
104
  };
105
105
  var MODEL_FACTORY = "model";
@@ -131,9 +131,9 @@ function pluralized(modelName) {
131
131
  }
132
132
  var optionsSchema = z.object({
133
133
  /**
134
- * Scope label for the storage binding. Defaults to `"default"`. Set
135
- * this when a project keeps more than one MongoDB connection and
136
- * wants their accesses paired separately.
134
+ * Scope for the storage binding, `"default"` when unset. Set it when a
135
+ * project has more than one MongoDB connection and their accesses
136
+ * should pair separately.
137
137
  */
138
138
  scope: scopeOption.optional()
139
139
  }).strict();
@@ -156,7 +156,7 @@ var declares = {
156
156
  kind: "effects",
157
157
  package: "@suss/framework-mongoose",
158
158
  dependencies: [{ ecosystem: "npm", name: "mongoose" }],
159
- reads: `Mongoose model calls, emits storage-access interactions against the collection a model's \`.model(...)\` call declares.`
159
+ reads: `Mongoose model calls. Each one becomes a storage-access interaction on the collection that the model's \`.model(...)\` call declares.`
160
160
  };
161
161
  var index_default = mongooseFramework;
162
162
  export {