@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
@@ -2,17 +2,23 @@
2
2
  import { z } from "zod";
3
3
  var optionsSchema = z.object({
4
4
  /**
5
- * Class decorators this project composes `@Controller()` into, for
6
- * the cases the adapter cannot follow on its own.
7
- *
8
- * A wrapper written in the project needs no entry here: the adapter
9
- * resolves a class decorator to the function behind it and accepts it
10
- * when calling that function calls `Controller` from
11
- * `@nestjs/common`. What is left for this option is a wrapper whose
12
- * body is not in the project, so there is nothing to read.
5
+ * Class decorators that wrap `@Controller()` in a package outside the
6
+ * project. A dependency stub fills this. A wrapper written in the
7
+ * project needs no entry, because the adapter reads its body and
8
+ * sees it call `Controller` from `@nestjs/common`.
13
9
  */
14
10
  classDecorators: z.array(z.string()).optional()
15
11
  }).strict();
12
+ var REQUEST_METHODS = {
13
+ 0: "GET",
14
+ 1: "POST",
15
+ 2: "PUT",
16
+ 3: "DELETE",
17
+ 4: "PATCH",
18
+ 5: "*",
19
+ 6: "OPTIONS",
20
+ 7: "HEAD"
21
+ };
16
22
  function nestjsRestFramework(options = {}) {
17
23
  return {
18
24
  name: "nestjs-rest",
@@ -24,8 +30,8 @@ function nestjsRestFramework(options = {}) {
24
30
  match: {
25
31
  type: "decoratedRoute",
26
32
  importModule: "@nestjs/common",
27
- // First match wins, so the framework's own decorator is
28
- // tried before any wrapper a project names.
33
+ // The first match wins, so the framework's own decorator is
34
+ // tried before any wrapper.
29
35
  classDecorators: ["Controller", ...options.classDecorators ?? []],
30
36
  methodDecoratorRouteMap: {
31
37
  Get: "GET",
@@ -36,17 +42,34 @@ function nestjsRestFramework(options = {}) {
36
42
  Options: "OPTIONS",
37
43
  Head: "HEAD",
38
44
  All: "*"
45
+ },
46
+ // Nest sends 201 for a POST and 200 for every other verb, and
47
+ // `@HttpCode(n)` sets the status for one handler.
48
+ defaultStatusCodes: { Post: 201 },
49
+ statusCodeDecorator: "HttpCode",
50
+ globalPrefix: {
51
+ method: "setGlobalPrefix",
52
+ application: {
53
+ importModule: "@nestjs/core",
54
+ importName: "NestFactory",
55
+ factory: "create"
56
+ },
57
+ // An exclusion is a path, or a `RouteInfo` whose method is a
58
+ // member of Nest's `RequestMethod` enum.
59
+ exclude: {
60
+ option: "exclude",
61
+ pathKey: "path",
62
+ methodKey: "method",
63
+ methods: REQUEST_METHODS
64
+ }
39
65
  }
40
66
  },
41
67
  requiresImport: ["@nestjs/common"]
42
68
  }
43
69
  ],
44
70
  terminals: [
45
- // NestJS controllers serialise the returned value as the
46
- // response body and pick a 200 default unless the method
47
- // declares `@HttpCode(N)` (deferred: that decorator is
48
- // metadata-only). Match bare returns and treat object literals
49
- // / identifiers / awaited calls as 200 responses.
71
+ // NestJS sends the returned value as the body. The route's own
72
+ // status, from its verb or `@HttpCode`, replaces this 200.
50
73
  {
51
74
  kind: "response",
52
75
  match: { type: "returnStatement", excludeCallReturns: false },
@@ -55,19 +78,16 @@ function nestjsRestFramework(options = {}) {
55
78
  }
56
79
  },
57
80
  {
58
- // `throw new HttpException(msg, status)` / `throw new
59
- // BadRequestException()` etc. NestJS maps these to HTTP
60
- // responses; v0 records the exception type so downstream
61
- // contract-checking can pair it with the wire status that
62
- // the framework would emit.
81
+ // `throw new BadRequestException()`: NestJS turns the exception
82
+ // into a response. The throw records the exception type, and the
83
+ // contract check pairs it with the status NestJS would send.
63
84
  kind: "throw",
64
85
  match: { type: "throwExpression" },
65
86
  extraction: {}
66
87
  },
67
88
  {
68
89
  // A method that runs off the end returns undefined, and Nest
69
- // sends the same 200 with an empty body it sends for a bare
70
- // `return;`. Only `@HttpCode(N)` changes that.
90
+ // sends the same empty response it sends for a bare `return;`.
71
91
  kind: "response",
72
92
  match: { type: "functionFallthrough" },
73
93
  extraction: {
package/dist/net-http.js CHANGED
@@ -28,9 +28,9 @@ function netHttpClient() {
28
28
  {
29
29
  constantName: "Net::HTTP",
30
30
  verbMethodNames: VERB_METHODS,
31
- // Every one of these takes a URI object rather than a string.
32
- // `URI(...)` and `URI.parse(...)` belong to Ruby itself, so the
33
- // adapter's value tables read them and the pack says nothing.
31
+ // These methods take a URI object. `URI(...)` and `URI.parse(...)`
32
+ // are part of Ruby, so the adapter already evaluates them and the
33
+ // pack adds nothing for them.
34
34
  url: { position: 0 },
35
35
  receiverBuilders: ["new"],
36
36
  requestObject: {
@@ -38,9 +38,8 @@ function netHttpClient() {
38
38
  constructors: REQUEST_CLASSES,
39
39
  urlPosition: 0
40
40
  },
41
- // The status comes back as a string, which is why a caller
42
- // writes `response.code.to_i == 404`, read the same as any
43
- // other member.
41
+ // `code` is a string, so callers write `response.code.to_i == 404`.
42
+ // That comparison is read the same way as one on any other member.
44
43
  response: {
45
44
  statusCode: ["code"],
46
45
  body: ["body"],
package/dist/nextjs.js CHANGED
@@ -36,9 +36,9 @@ function actionUnit(func, name, module) {
36
36
  name,
37
37
  terminals: ACTION_TERMINALS,
38
38
  inputMapping: { type: "allPositional" },
39
- // The identity intent and the keyed pairing pass refer to the
40
- // action by. The path is absolute here and the CLI makes it
41
- // project-relative with every other path on the summary.
39
+ // Intent files and keyed pairing find the action by module and export
40
+ // name. The path is absolute here, and the CLI makes it relative to
41
+ // the project along with every other path on the summary.
42
42
  functionCallInfo: { module, exportName: name }
43
43
  };
44
44
  }
@@ -152,8 +152,8 @@ function nextjsFramework() {
152
152
  exportNames: ["default"]
153
153
  },
154
154
  bindingExtraction: {
155
- // One export serves every method, which is what the wildcard says.
156
- // Pairing matches it against whichever method a consumer used.
155
+ // One default export serves every method, so pairing matches it
156
+ // against whichever method a caller uses.
157
157
  method: { type: "literal", value: "*" },
158
158
  path: PAGES_ROUTES
159
159
  }
@@ -177,7 +177,7 @@ function nextjsFramework() {
177
177
  },
178
178
  {
179
179
  // Response.json(body, { status }), the platform's own, which a
180
- // handler can return without importing anything.
180
+ // handler can call without an import.
181
181
  kind: "response",
182
182
  match: { type: "functionCall", functionName: "Response.json" },
183
183
  extraction: {
@@ -187,8 +187,8 @@ function nextjsFramework() {
187
187
  }
188
188
  },
189
189
  {
190
- // new Response(body, { status }), which a handler returns for
191
- // anything that is not JSON: a redirect, a stream, plain text.
190
+ // new Response(body, { status }), which a handler returns for a
191
+ // body that is not JSON, such as a stream or plain text.
192
192
  kind: "response",
193
193
  match: { type: "functionCall", functionName: "Response" },
194
194
  extraction: {
@@ -198,9 +198,8 @@ function nextjsFramework() {
198
198
  }
199
199
  },
200
200
  {
201
- // NextResponse.redirect(url) sends a 307 unless the caller says
202
- // otherwise, and the alternative is written as an init object
203
- // this pack does not read yet.
201
+ // NextResponse.redirect(url) sends a 307 by default. Another status
202
+ // goes in an init object, which this pack does not read yet.
204
203
  kind: "response",
205
204
  match: {
206
205
  type: "functionCall",
@@ -210,8 +209,8 @@ function nextjsFramework() {
210
209
  extraction: { defaultStatusCode: 307 }
211
210
  },
212
211
  {
213
- // A pages handler writes to the response it was handed, the
214
- // same shape Express uses.
212
+ // A pages handler writes to the response object passed to it, as
213
+ // an Express handler does.
215
214
  kind: "response",
216
215
  match: {
217
216
  type: "parameterMethodCall",
@@ -241,9 +240,9 @@ function nextjsFramework() {
241
240
  extraction: {}
242
241
  }
243
242
  ],
244
- // A route handler takes the request first. The app directory hands
245
- // it a context object holding the route parameters; a pages handler
246
- // takes the response object there instead.
243
+ // Both kinds of handler take the request first. An app handler gets a
244
+ // context object with the route parameters second, and a pages handler
245
+ // gets the response object in that position.
247
246
  inputMapping: {
248
247
  type: "positionalParams",
249
248
  params: [
@@ -257,7 +256,7 @@ var declares = {
257
256
  kind: "framework",
258
257
  package: "@suss/framework-nextjs",
259
258
  dependencies: [{ ecosystem: "npm", name: "next" }],
260
- reads: `Next.js route handlers, pages, and server actions; the route comes from where the file is on disk, and a \`"use server"\` function becomes an action unit.`
259
+ reads: `Next.js route handlers, pages and server actions. The route comes from where the file is on disk, and a \`"use server"\` function becomes an action unit.`
261
260
  };
262
261
  var index_default = nextjsFramework;
263
262
  export {
package/dist/node.js CHANGED
@@ -25,12 +25,17 @@ import {
25
25
  } from "@suss/adapter-typescript";
26
26
  import { Node } from "ts-morph";
27
27
  import {
28
- Node as Node2
28
+ Node as Node2,
29
+ SyntaxKind as SyntaxKind3
29
30
  } from "ts-morph";
31
+ import { numberValueOf } from "@suss/adapter-typescript";
30
32
  import {
31
33
  Node as Node3
32
34
  } from "ts-morph";
33
35
  import { functionCallBinding } from "@suss/behavioral-ir";
36
+ import { Node as Node4 } from "ts-morph";
37
+ import { writtenPayloadOf } from "@suss/adapter-typescript";
38
+ import { ioBinding } from "@suss/behavioral-ir";
34
39
  var RECOGNITION = "@suss/runtime-node";
35
40
  function configBinding(where) {
36
41
  return runtimeConfigBinding({ recognition: RECOGNITION, ...where });
@@ -127,12 +132,12 @@ function schemaEnvReads(call, resolution) {
127
132
  if (!readerCouldFire(call, reader, written) || !callMatches(call, reader, resolution)) {
128
133
  continue;
129
134
  }
130
- const environment = environmentArgument(call, reader, resolution);
131
- if (environment === null || !resolution.isEnvironmentValue(environment)) {
135
+ const reads = readsFromSchema(call, reader, resolution);
136
+ if (reads.length === 0) {
132
137
  continue;
133
138
  }
134
- const reads = readsFromSchema(call, reader, resolution);
135
- if (reads.length > 0) {
139
+ const environment = environmentArgument(call, reader, resolution);
140
+ if (environment !== null && resolution.isEnvironmentValue(environment)) {
136
141
  return reads;
137
142
  }
138
143
  }
@@ -396,9 +401,9 @@ function bindingRead(element) {
396
401
  return [
397
402
  {
398
403
  name,
399
- // A binding default supplies the value the variable is missing,
400
- // which is what `??` does for the other two spellings.
401
- defaulted: element.getInitializer() !== void 0,
404
+ // A binding default covers a missing variable the same way `??`
405
+ // does after a dotted read.
406
+ defaulted: element.getInitializer() !== void 0 || isDefaultedAt(element),
402
407
  node: element
403
408
  }
404
409
  ];
@@ -464,8 +469,8 @@ function readsThroughHelperCall(call, resolution) {
464
469
  defaulted: atSites || wrapped,
465
470
  node: call
466
471
  })),
467
- // A config module parses its schema once and every handler calls
468
- // the function around it, so the call reports what the parse reads.
472
+ // A config module often parses its schema inside a function that
473
+ // every handler calls, so each call reports the keys that parse reads.
469
474
  ...anchoredAt(schemaEnvReadsInside(callee, resolution), call)
470
475
  ];
471
476
  }
@@ -508,7 +513,7 @@ function namesReadAtSites(fn, resolution) {
508
513
  if (resolution === void 0) {
509
514
  return () => null;
510
515
  }
511
- const namers = resolution.envNamers(fn.getSourceFile());
516
+ const namers = resolution.envNamers(fn.getProject());
512
517
  return (parameter) => namers.sitesNaming(parameter);
513
518
  }
514
519
  function directEnvReads(fn, parameter) {
@@ -612,10 +617,14 @@ var OPAQUE_PROPERTY_NAMES = /* @__PURE__ */ new Set([
612
617
  "execArgv"
613
618
  ]);
614
619
  function makeProcessSurfaceRecognizer(where) {
615
- return (access, _ctx) => {
620
+ return (access, ctx) => {
616
621
  const node = access;
617
622
  if (Node2.isPropertyAccessExpression(node)) {
618
- return recognizeProperty(node, where);
623
+ return recognizeProperty(
624
+ node,
625
+ where,
626
+ ctx?.resolution
627
+ );
619
628
  }
620
629
  if (Node2.isElementAccessExpression(node)) {
621
630
  return recognizeElementAccess(node, where);
@@ -623,7 +632,7 @@ function makeProcessSurfaceRecognizer(where) {
623
632
  return null;
624
633
  };
625
634
  }
626
- function recognizeProperty(node, where) {
635
+ function recognizeProperty(node, where, resolution) {
627
636
  if (isProcessEnvVarRead(node)) {
628
637
  return null;
629
638
  }
@@ -635,8 +644,25 @@ function recognizeProperty(node, where) {
635
644
  if (isProcessIdentifier(subject) && OPAQUE_PROPERTY_NAMES.has(name)) {
636
645
  return [opaqueRuntimeRead(node.getText())];
637
646
  }
647
+ if (isProcessIdentifier(subject) && name === "exitCode") {
648
+ return exitCodeSet(node, resolution);
649
+ }
638
650
  return null;
639
651
  }
652
+ function exitCodeSet(node, resolution) {
653
+ const assignment = node.getParent();
654
+ if (!Node2.isBinaryExpression(assignment) || assignment.getLeft() !== node || assignment.getOperatorToken().getKind() !== SyntaxKind3.EqualsToken) {
655
+ return null;
656
+ }
657
+ const written = assignment.getRight();
658
+ return [
659
+ {
660
+ type: "stateChange",
661
+ variable: node.getText(),
662
+ newValue: numberValueOf(written, resolution) ?? written.getText()
663
+ }
664
+ ];
665
+ }
640
666
  function recognizeElementAccess(node, where) {
641
667
  const subject = node.getExpression();
642
668
  if (!Node2.isPropertyAccessExpression(subject)) {
@@ -767,9 +793,8 @@ var schedulingRecognizer = (call, _ctx) => {
767
793
  };
768
794
  var SCHEDULED_CALLBACK_INPUT = {
769
795
  type: "positionalParams",
770
- // Timer callbacks receive whatever `...args` were passed at the
771
- // schedule site. v0 doesn't track these positions individually,
772
- // packs that need argument-shape modeling can layer it on top.
796
+ // A timer callback receives the extra arguments passed to the
797
+ // scheduling call. Those are not mapped to its parameters yet.
773
798
  params: []
774
799
  };
775
800
  function nodeSchedulingSubUnits(parent, _ctx) {
@@ -797,8 +822,8 @@ function nodeSchedulingSubUnits(parent, _ctx) {
797
822
  out.push({
798
823
  func: arg,
799
824
  kind: "scheduled-callback",
800
- // Naming convention: `<parent>.<via>#<index>`. Multiple
801
- // setImmediate calls in the same parent get distinct indices.
825
+ // The index keeps two calls to the same primitive in one parent
826
+ // apart.
802
827
  name: `${parent.name}.${primitive.via}#${idx}`,
803
828
  inputMapping: SCHEDULED_CALLBACK_INPUT,
804
829
  metadata: {
@@ -810,11 +835,84 @@ function nodeSchedulingSubUnits(parent, _ctx) {
810
835
  });
811
836
  return out;
812
837
  }
813
- var PACK_VERSION = "0.2.0";
838
+ var RECOGNITION2 = "@suss/runtime-node";
839
+ var PROCESS_STREAMS = ["process.stdout", "process.stderr"];
840
+ var CONSOLE_STREAMS = {
841
+ log: "stdout",
842
+ info: "stdout",
843
+ debug: "stdout",
844
+ table: "stdout",
845
+ dir: "stdout",
846
+ warn: "stderr",
847
+ error: "stderr",
848
+ trace: "stderr"
849
+ };
850
+ var CONSOLE_TYPES = ["Console"];
851
+ var STREAM_TYPES = ["NodeJS.WriteStream", "NodeJS.WritableStream"];
852
+ var streamWriteRecognizer = (call, ctx) => {
853
+ const written = call;
854
+ if (!Node4.isCallExpression(written)) {
855
+ return null;
856
+ }
857
+ const callee = written.getExpression();
858
+ if (!Node4.isPropertyAccessExpression(callee)) {
859
+ return null;
860
+ }
861
+ const resolution = ctx?.resolution;
862
+ const target = targetOf(callee.getExpression(), callee.getName(), resolution);
863
+ if (target === void 0) {
864
+ return null;
865
+ }
866
+ const payload = writtenPayloadOf(written.getArguments());
867
+ const effect = {
868
+ type: "interaction",
869
+ binding: ioBinding({ recognition: RECOGNITION2, target }),
870
+ callee: callee.getText(),
871
+ groupId: callSiteId(written),
872
+ interaction: {
873
+ class: "stream-write",
874
+ payload: payload.shape,
875
+ serialized: payload.serialized
876
+ }
877
+ };
878
+ return [effect];
879
+ };
880
+ function targetOf(receiver, method, resolution) {
881
+ const consoleStream = Object.hasOwn(CONSOLE_STREAMS, method) ? CONSOLE_STREAMS[method] : void 0;
882
+ if (consoleStream !== void 0) {
883
+ if (Node4.isIdentifier(receiver) && receiver.getText() === "console") {
884
+ return consoleStream;
885
+ }
886
+ return viaParameter(receiver, CONSOLE_TYPES, resolution);
887
+ }
888
+ if (method !== "write") {
889
+ return void 0;
890
+ }
891
+ if (Node4.isPropertyAccessExpression(receiver)) {
892
+ const spelled = receiver.getText();
893
+ return PROCESS_STREAMS.includes(spelled) ? streamName(spelled) : void 0;
894
+ }
895
+ const path = viaParameter(receiver, STREAM_TYPES, resolution);
896
+ return path === null || path === void 0 ? path : streamName(path);
897
+ }
898
+ function viaParameter(receiver, annotations, resolution) {
899
+ if (resolution === void 0 || !Node4.isIdentifier(receiver)) {
900
+ return void 0;
901
+ }
902
+ return resolution.streamPathOf(receiver, annotations);
903
+ }
904
+ function streamName(path) {
905
+ return path.slice(path.lastIndexOf(".") + 1);
906
+ }
907
+ function callSiteId(call) {
908
+ const { line, column } = call.getSourceFile().getLineAndColumnAtPos(call.getStart());
909
+ return `${line}:${column}`;
910
+ }
911
+ var PACK_VERSION = "0.3.0";
814
912
  var optionsSchema = z.object({
815
913
  /**
816
- * Deployment context for runtime-config reads (process.env.X,
817
- * process.argv). Left off the binding unless a run sets it.
914
+ * The kind of deployment that runtime-config reads such as
915
+ * `process.env.X` belong to. Left off the binding unless a run sets it.
818
916
  */
819
917
  deploymentTarget: z.enum(["lambda", "ecs-task", "container", "k8s-deployment"]).optional(),
820
918
  /**
@@ -823,6 +921,22 @@ var optionsSchema = z.object({
823
921
  */
824
922
  instanceName: z.string().optional()
825
923
  }).strict();
924
+ var PROCESS_EXIT = {
925
+ kind: "exit",
926
+ inEveryUnit: true,
927
+ match: { type: "functionCall", functionName: "process.exit" },
928
+ extraction: {
929
+ statusCode: { from: "argument", position: 0 },
930
+ defaultStatusCode: 0
931
+ }
932
+ };
933
+ var PARSE_ARGS = ["node:util", "util"].map((module) => ({
934
+ module,
935
+ name: "parseArgs",
936
+ argument: 0,
937
+ argsKey: "args",
938
+ optionsKey: "options"
939
+ }));
826
940
  function nodeRuntimePack(options = {}) {
827
941
  const configOptions = {
828
942
  ...options.deploymentTarget !== void 0 ? { deploymentTarget: options.deploymentTarget } : {},
@@ -836,14 +950,16 @@ function nodeRuntimePack(options = {}) {
836
950
  protocol: "in-process",
837
951
  languages: ["typescript", "javascript"],
838
952
  discovery: [],
839
- terminals: [],
953
+ terminals: [PROCESS_EXIT],
954
+ exitCodeWrites: ["process.exitCode"],
955
+ argumentParsers: PARSE_ARGS,
840
956
  inputMapping: { type: "positionalParams", params: [] },
841
- invocationRecognizers: [schedulingRecognizer],
957
+ invocationRecognizers: [schedulingRecognizer, streamWriteRecognizer],
842
958
  environmentObjects: ["process.env"],
959
+ streamObjects: PROCESS_STREAMS,
843
960
  accessRecognizers: [
844
- // `envRecognizer` owns `process.env.X`; `processRecognizer`
845
- // owns the rest of the process surface and skips env reads,
846
- // together they partition `process.*` without duplication.
961
+ // envRecognizer handles `process.env.X` and processRecognizer skips
962
+ // it, so each `process.*` read produces one effect.
847
963
  envRecognizer,
848
964
  processRecognizer,
849
965
  importMetaRecognizer,
@@ -857,7 +973,7 @@ var declares = {
857
973
  package: "@suss/runtime-node",
858
974
  dependencies: [],
859
975
  shippedWith: "typescript",
860
- reads: "Node.js runtime primitives, scheduling, the `process` surface (incl. `process.env.X` config-read interactions and the keys of a schema parsed against `process.env`), module-loading globals as metadata-read interactions, emitted as interaction effects."
976
+ reads: "Node.js runtime primitives, scheduling and the `process` surface, each recorded as an interaction effect. A `process.env.X` read, or a key of a schema parsed against `process.env`, becomes a config-read interaction. A module-loading global becomes a metadata-read interaction. A `console` call or a write to `process.stdout` or `process.stderr` becomes a stream-write interaction on the stream it goes to."
861
977
  };
862
978
  var index_default = nodeRuntimePack;
863
979
  export {
@@ -33,7 +33,7 @@ var declares = {
33
33
  kind: "framework",
34
34
  package: "@suss/framework-package-exports",
35
35
  dependencies: [],
36
- reads: "The boundary between packages in one workspace: public exports on the provider side, imports of them on the consumer side. Reads the workspace manifest, so it needs no per-project package list."
36
+ reads: "The boundary between packages in one workspace: public exports on the provider side, and imports of them on the consumer side. The pack reads the workspace manifest, so nobody has to list the packages for each project."
37
37
  };
38
38
  var index_default = packageExportsFramework;
39
39
  export {
package/dist/pg.js CHANGED
@@ -2,7 +2,8 @@
2
2
  import { declaredBy, pack, sqlStatements } from "@suss/recognize";
3
3
  var CLIENT_MODULES = ["pg", "@types/pg"];
4
4
  var STATEMENT = {
5
- statement: [{ at: 0 }, { at: 0, property: ["text"] }]
5
+ statement: [{ at: 0 }, { at: 0, property: ["text"] }],
6
+ parameters: [{ at: 1 }, { at: 0, property: ["values"] }]
6
7
  };
7
8
  var QUERIES = sqlStatements({
8
9
  system: "postgresql",
@@ -19,7 +20,7 @@ var declares = {
19
20
  kind: "effects",
20
21
  package: "@suss/framework-pg",
21
22
  dependencies: [{ ecosystem: "npm", name: "pg" }],
22
- reads: "node-postgres queries, emits storage-access interactions with the tables each statement touches."
23
+ reads: "node-postgres queries. Each one becomes a storage-access interaction on the tables its statement touches."
23
24
  };
24
25
  var index_default = pgFramework;
25
26
  export {