@suss/contract-wrangler 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.
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # @suss/contract-wrangler
2
2
 
3
- Part of [suss](https://github.com/nimbuscloud-ai/suss), which reads both sides of every call in a repository and says where the two disagree.
3
+ Part of [suss](https://github.com/nimbuscloud-ai/suss), which reads both sides of every call in a repository and reports where the two disagree.
4
4
 
5
- Reads a Cloudflare `wrangler.toml` (or `wrangler.jsonc`) and says what the Worker it deploys is given.
5
+ This package reads a Cloudflare `wrangler.toml` (or `wrangler.jsonc`) and records what the Worker it deploys is given.
6
6
 
7
7
  ## What this package is
8
8
 
9
- A contract source. `suss contract --from wrangler services/api` reads the document and emits the declared side of four boundaries:
9
+ A contract source. `suss contract --from wrangler services/api` reads the document and records the declared side of four boundaries:
10
10
 
11
11
  ```toml
12
12
  name = "greeting-router"
@@ -31,39 +31,39 @@ queue = "greeting-events"
31
31
  | `[[queues.producers]]`, `[[queues.consumers]]` | one channel each, on `cloudflare-queues` |
32
32
  | `[env.<name>]` | the same Worker deployed again, with the top-level document as its default |
33
33
 
34
- A store's container is the binding name, `SESSIONS`, because that is what an access in the Worker spells (`env.SESSIONS.get(...)`), and `@suss/framework-cloudflare-workers` records the same name on the code side, so the storage check pairs the two. The resource's own name, the KV namespace id or the bucket name, goes on the summary as `physicalTable`, the same split a Prisma model has between the model and the table it maps to.
34
+ A store's container is the binding name, `SESSIONS`, because that is what an access in the Worker uses (`env.SESSIONS.get(...)`). `@suss/framework-cloudflare-workers` records the same name on the code side, so the storage check pairs the two. The resource's own name, meaning the KV namespace id or the bucket name, goes on the summary as `physicalTable`. A Prisma model splits the same way, between the model name and the table it maps to.
35
35
 
36
- Binding names are also listed on the runtime-config contract, beside the `[vars]` names: a binding is a property of the same `env` object, so a Worker that reads `env.AUDIT_KV` without a binding block for it gets the same finding as a read of a variable nobody set.
36
+ Binding names are also listed on the runtime-config contract, next to the `[vars]` names. A binding is a property of the same `env` object, so a Worker that reads `env.AUDIT_KV` with no binding block for it gets the same finding as a read of a variable nobody set.
37
37
 
38
- ## The values, not only the names
38
+ ## Variable values
39
39
 
40
- A runtime-config contract usually records which variables a runtime supplies, and that is enough to say whether code reads one nobody set. A Worker needs more, because it addresses its stores through a variable:
40
+ A runtime-config contract usually records which variables a runtime supplies, and that is enough to tell whether code reads one nobody set. A Worker needs more, because it addresses its stores through a variable:
41
41
 
42
42
  ```ts
43
43
  await dynamoRequest(env, aws, "Query", { TableName: env.EDITION_TABLE });
44
44
  ```
45
45
 
46
- Nothing at that call site says which table. The container comes out as `{EDITION_TABLE}`, which pairs with nothing until something says what the variable is. `[vars]` says it, so the summary keeps the values under `metadata.runtimeContract.envVarValues`, and the storage check grounds the access through the runtime that runs it.
46
+ Nothing at that call site shows which table. The container comes out as `{EDITION_TABLE}`, which does not pair with anything until something gives the variable's value. `[vars]` gives it, so the summary keeps the values under `metadata.runtimeContract.envVarValues`, and the storage check grounds the access through the runtime that runs it.
47
47
 
48
- A value the document does not state stays out. A secret is set with `wrangler secret put` or by the deployment's own tooling, never in the file, so the reader has nothing to record and the container stays unresolved.
48
+ A value the document does not set is left out. A secret is set with `wrangler secret put` or by the deployment's own tooling, and never in the file, so the reader has nothing to record and the container stays unresolved.
49
49
 
50
50
  ## Environments
51
51
 
52
- `[env.staging]` deploys the same Worker again under its own name, `greeting-router-staging` unless the block gives a `name`. Each one gets its own summary.
52
+ `[env.staging]` deploys the same Worker again under its own name, which is `greeting-router-staging` unless the block sets a `name`. Each environment gets its own summary.
53
53
 
54
- Wrangler replaces a whole block rather than merging it, so an environment that declares `[env.staging.vars]` supplies those variables and none of the top-level ones. A variable that comes through untouched is recorded as `globals`, which is the same provenance a SAM `Globals` section gets, and the checker judges it once for the document instead of once per deployment.
54
+ Wrangler replaces a whole block instead of merging it, so an environment that declares `[env.staging.vars]` gets those variables and none of the top-level ones. A variable that comes through unchanged is recorded as `globals`, the same provenance a SAM `Globals` section gets, and the checker judges it once for the document instead of once per deployment.
55
55
 
56
56
  ## Code scope
57
57
 
58
- The Worker's code is the directory the document is in, and `main` says which file it enters. `main` often points at a bundle a build step writes rather than at source; an entry that matches no file leaves the directory in charge, which is what places the code either way.
58
+ The Worker's code is the directory the document is in, and `main` gives the entry file. `main` often points at a bundle that a build step writes, outside the source. If the entry matches no file, the directory alone decides the code scope, so the code is placed correctly either way.
59
59
 
60
60
  ## Out of scope for now
61
61
 
62
- - **Durable Object and service bindings.** `[[durable_objects.bindings]]` and `[[services]]` are boundaries between two Workers, and pairing them needs both sides of a deployment suss does not read yet.
63
- - **Routes.** `routes` says which hostnames reach the Worker. The code side states no path, so a declared route would pair with nothing.
64
- - **Cron expressions.** `[triggers] crons` says when a Worker runs, not what it exchanges.
65
- - **`.dev.vars`.** It is local development configuration, and it is not what a deployment supplies.
62
+ - **Durable Object and service bindings.** `[[durable_objects.bindings]]` and `[[services]]` are boundaries between two Workers, and pairing them needs both sides of a deployment, which suss does not read yet.
63
+ - **Routes.** `routes` lists the hostnames that reach the Worker. The code side does not state a path, so a declared route would not pair with anything.
64
+ - **Cron expressions.** `[triggers] crons` sets when a Worker runs, and says nothing about what it exchanges.
65
+ - **`.dev.vars`.** It is configuration for local development, and a deployment does not supply it.
66
66
 
67
67
  ## Where it fits in suss
68
68
 
69
- Depends on `@suss/behavioral-ir` and `@suss/ir-core` for the bindings it writes. `@suss/framework-cloudflare-workers` reads the code side: the triggers the Worker serves and the variables it reads. The storage check in `@suss/checker` pairs a store this reader declares, or one a Terraform module declares, against the accesses that reach it.
69
+ The package depends on `@suss/behavioral-ir` and `@suss/ir-core` for the bindings it writes. `@suss/framework-cloudflare-workers` reads the code side: the triggers the Worker serves and the variables it reads. The storage check in `@suss/checker` pairs a store this reader declares, or one a Terraform module declares, with the accesses that reach it.
package/dist/index.cjs CHANGED
@@ -98,8 +98,8 @@ function storeSummary(store, entry, context) {
98
98
  recognition: RECOGNITION,
99
99
  storageSystem: store.storageSystem,
100
100
  scope: "default",
101
- // The binding name is what an access in the Worker spells, so
102
- // it is the name the storage check pairs on.
101
+ // An access in the Worker uses the binding name, so the storage
102
+ // check has to pair on it.
103
103
  container: boundAs ?? resourceName,
104
104
  accessPath: null
105
105
  })
@@ -164,9 +164,9 @@ function queueSide(entries, side, context) {
164
164
  continue;
165
165
  }
166
166
  summaries.push({
167
- // A producer block declares a queue the Worker sends to, which is
168
- // a channel rather than a unit that runs, so it is a library. A
169
- // consumer block says this Worker drains that queue.
167
+ // A producer block declares a queue the Worker sends to. Nothing
168
+ // runs there, so it is a library. A consumer block means this
169
+ // Worker drains the queue.
170
170
  kind: side === "consumers" ? "consumer" : "library",
171
171
  location: {
172
172
  file: context.sourceFile,
package/dist/index.d.cts CHANGED
@@ -3,15 +3,14 @@ import { BehavioralSummary } from '@suss/behavioral-ir';
3
3
  /**
4
4
  * Finding and parsing a Wrangler configuration document.
5
5
  *
6
- * Wrangler accepts the same configuration in two spellings, TOML and
7
- * JSONC, and prefers the JSON one where a project has both. This module
8
- * settles which file a path means and hands back the parsed object; the
9
- * shape of what is in it is `translate.ts`'s problem.
6
+ * Wrangler accepts the same configuration as TOML or JSONC, and uses the
7
+ * JSON file when a project has both. This module picks the file for a
8
+ * path and returns the parsed object. It does not check the object's
9
+ * contents beyond it being an object.
10
10
  */
11
11
  /**
12
- * The names Wrangler looks for, in the order it prefers them. Wrangler
13
- * defines the list, so a project that spells its file otherwise passes
14
- * the path to that file outright.
12
+ * The file names Wrangler looks for, in its order of preference. A
13
+ * project whose file has another name passes that file's path directly.
15
14
  */
16
15
  declare const CONFIGURATION_FILE_NAMES: string[];
17
16
  /** Whether a file is one Wrangler would read as a Worker's configuration. */
@@ -44,24 +43,22 @@ type DocumentLocation = {
44
43
  kind: "missing";
45
44
  };
46
45
  /**
47
- * The configuration file a path means. A path may be the file itself or
48
- * the directory a Worker lives in, since that is where a person points
49
- * when they mean the Worker rather than one of its two spellings.
46
+ * Finds the configuration file for a path. The path may be the file
47
+ * itself or the Worker's directory, which is searched in Wrangler's
48
+ * order of preference.
50
49
  */
51
50
  declare function locateConfigurationFile(target: string): DocumentLocation;
52
- /** The document a file contains, read by whichever parser its suffix asks for. */
51
+ /** Parses a configuration file as TOML or JSONC, chosen by its extension. */
53
52
  declare function loadConfigurationDocument(file: string): WranglerDocument;
54
53
 
55
54
  /**
56
55
  * The stores and queues a Worker is bound to, as boundary summaries.
57
56
  *
58
- * Every binding block in a Wrangler document has the same two halves:
59
- * `binding`, the name the code reads it under, and a key giving the
60
- * resource's own name. The code says the first, the deployment declares
61
- * the second, so a summary keeps both: the container is the binding
62
- * name, which is what an access in the Worker spells (`env.SESSIONS`),
63
- * and `physicalTable` is the resource's own name, the same split a
64
- * Prisma model has between the model and the table it maps to.
57
+ * Every binding block in a Wrangler document has two parts: `binding`,
58
+ * the name the code reads the resource under, and a key with the
59
+ * resource's own name. Code only ever uses the first (`env.SESSIONS`),
60
+ * so the binding name becomes the container the storage check pairs
61
+ * on, and the resource's own name goes on `physicalTable`.
65
62
  */
66
63
 
67
64
  /** The manifest language recorded on every binding this reader writes. */
@@ -71,14 +68,15 @@ declare const RECOGNITION = "wrangler";
71
68
  * The configuration contract a Wrangler document declares.
72
69
  *
73
70
  * A Worker reads its variables off the second argument to every
74
- * trigger, so the channel is the same one a Node process reads through
75
- * `process.env`, and the summary is the same shape the CloudFormation
76
- * reader writes for a Lambda: a runtime-config provider, one per
77
- * deployment, keyed on the deployable unit.
71
+ * trigger. That is the same channel a Node process reads through
72
+ * `process.env`, so the summary follows what the CloudFormation reader
73
+ * writes for a Lambda: one runtime-config provider per deployment, keyed
74
+ * on the deployable unit.
78
75
  *
79
- * The values go on it as well as the names. A Worker addresses a store
80
- * through a variable rather than by name, `TableName: env.EDITION_TABLE`,
81
- * so the value is what says which store the access reaches.
76
+ * The summary records variable values as well as names. A Worker
77
+ * addresses a store through a variable, as in
78
+ * `TableName: env.EDITION_TABLE`, so only the value shows which store
79
+ * the access reaches.
82
80
  */
83
81
 
84
82
  /** One Worker as deployed: an environment's document over the default one. */
@@ -89,7 +87,7 @@ interface Deployment {
89
87
  environment: string | null;
90
88
  /** The environment's document with the top-level one behind it. */
91
89
  merged: WranglerDocument;
92
- /** Which variables the top-level document supplied rather than this one. */
90
+ /** The variables and bindings this environment takes from the top level. */
93
91
  inherited: Set<string>;
94
92
  }
95
93
  /**
@@ -104,14 +102,12 @@ declare function environmentDocuments(document: WranglerDocument): Deployment[];
104
102
  * @suss/contract-wrangler: behavioral summaries from a Wrangler
105
103
  * configuration document.
106
104
  *
107
- * A `wrangler.toml` deploys a working Worker, and before this reader
108
- * existed suss saw none of it: no deployable unit, no configuration
109
- * contract, no stores and no queues. `name` and `main` give the unit
110
- * and the code it runs, `vars` gives the configuration, the three
111
- * binding blocks give stores, `queues` gives the channels it sends on
112
- * and drains, and each `env.<name>` deploys the same Worker again with
113
- * the top-level document as its default. The README says how each half
114
- * pairs.
105
+ * `name` and `main` give the deployable unit and the code it runs.
106
+ * `vars` gives its configuration, the three binding blocks give its
107
+ * stores, and `queues` gives the channels it sends on and drains. Each
108
+ * `env.<name>` deploys the same Worker again, with the top-level
109
+ * document as its default. The README describes how each summary pairs
110
+ * with the code side.
115
111
  */
116
112
 
117
113
  interface WranglerToSummariesOptions {
@@ -128,7 +124,10 @@ declare function wranglerToSummaries(document: WranglerDocument, options: {
128
124
  source: string;
129
125
  codeScope: string;
130
126
  }): BehavioralSummary[];
131
- /** The path may be the document itself or the directory the Worker is in. */
127
+ /**
128
+ * Reads a Wrangler document and returns its summaries. The path may be
129
+ * the document itself or the directory the Worker is in.
130
+ */
132
131
  declare function wranglerFileToSummaries(target: string, options?: WranglerToSummariesOptions): BehavioralSummary[];
133
132
 
134
133
  export { CONFIGURATION_FILE_NAMES, type DocumentLocation, RECOGNITION, type WranglerDocument, type WranglerRecord, type WranglerToSummariesOptions, environmentDocuments, isConfigurationFile, loadConfigurationDocument, locateConfigurationFile, wranglerFileToSummaries, wranglerToSummaries };
package/dist/index.d.ts CHANGED
@@ -3,15 +3,14 @@ import { BehavioralSummary } from '@suss/behavioral-ir';
3
3
  /**
4
4
  * Finding and parsing a Wrangler configuration document.
5
5
  *
6
- * Wrangler accepts the same configuration in two spellings, TOML and
7
- * JSONC, and prefers the JSON one where a project has both. This module
8
- * settles which file a path means and hands back the parsed object; the
9
- * shape of what is in it is `translate.ts`'s problem.
6
+ * Wrangler accepts the same configuration as TOML or JSONC, and uses the
7
+ * JSON file when a project has both. This module picks the file for a
8
+ * path and returns the parsed object. It does not check the object's
9
+ * contents beyond it being an object.
10
10
  */
11
11
  /**
12
- * The names Wrangler looks for, in the order it prefers them. Wrangler
13
- * defines the list, so a project that spells its file otherwise passes
14
- * the path to that file outright.
12
+ * The file names Wrangler looks for, in its order of preference. A
13
+ * project whose file has another name passes that file's path directly.
15
14
  */
16
15
  declare const CONFIGURATION_FILE_NAMES: string[];
17
16
  /** Whether a file is one Wrangler would read as a Worker's configuration. */
@@ -44,24 +43,22 @@ type DocumentLocation = {
44
43
  kind: "missing";
45
44
  };
46
45
  /**
47
- * The configuration file a path means. A path may be the file itself or
48
- * the directory a Worker lives in, since that is where a person points
49
- * when they mean the Worker rather than one of its two spellings.
46
+ * Finds the configuration file for a path. The path may be the file
47
+ * itself or the Worker's directory, which is searched in Wrangler's
48
+ * order of preference.
50
49
  */
51
50
  declare function locateConfigurationFile(target: string): DocumentLocation;
52
- /** The document a file contains, read by whichever parser its suffix asks for. */
51
+ /** Parses a configuration file as TOML or JSONC, chosen by its extension. */
53
52
  declare function loadConfigurationDocument(file: string): WranglerDocument;
54
53
 
55
54
  /**
56
55
  * The stores and queues a Worker is bound to, as boundary summaries.
57
56
  *
58
- * Every binding block in a Wrangler document has the same two halves:
59
- * `binding`, the name the code reads it under, and a key giving the
60
- * resource's own name. The code says the first, the deployment declares
61
- * the second, so a summary keeps both: the container is the binding
62
- * name, which is what an access in the Worker spells (`env.SESSIONS`),
63
- * and `physicalTable` is the resource's own name, the same split a
64
- * Prisma model has between the model and the table it maps to.
57
+ * Every binding block in a Wrangler document has two parts: `binding`,
58
+ * the name the code reads the resource under, and a key with the
59
+ * resource's own name. Code only ever uses the first (`env.SESSIONS`),
60
+ * so the binding name becomes the container the storage check pairs
61
+ * on, and the resource's own name goes on `physicalTable`.
65
62
  */
66
63
 
67
64
  /** The manifest language recorded on every binding this reader writes. */
@@ -71,14 +68,15 @@ declare const RECOGNITION = "wrangler";
71
68
  * The configuration contract a Wrangler document declares.
72
69
  *
73
70
  * A Worker reads its variables off the second argument to every
74
- * trigger, so the channel is the same one a Node process reads through
75
- * `process.env`, and the summary is the same shape the CloudFormation
76
- * reader writes for a Lambda: a runtime-config provider, one per
77
- * deployment, keyed on the deployable unit.
71
+ * trigger. That is the same channel a Node process reads through
72
+ * `process.env`, so the summary follows what the CloudFormation reader
73
+ * writes for a Lambda: one runtime-config provider per deployment, keyed
74
+ * on the deployable unit.
78
75
  *
79
- * The values go on it as well as the names. A Worker addresses a store
80
- * through a variable rather than by name, `TableName: env.EDITION_TABLE`,
81
- * so the value is what says which store the access reaches.
76
+ * The summary records variable values as well as names. A Worker
77
+ * addresses a store through a variable, as in
78
+ * `TableName: env.EDITION_TABLE`, so only the value shows which store
79
+ * the access reaches.
82
80
  */
83
81
 
84
82
  /** One Worker as deployed: an environment's document over the default one. */
@@ -89,7 +87,7 @@ interface Deployment {
89
87
  environment: string | null;
90
88
  /** The environment's document with the top-level one behind it. */
91
89
  merged: WranglerDocument;
92
- /** Which variables the top-level document supplied rather than this one. */
90
+ /** The variables and bindings this environment takes from the top level. */
93
91
  inherited: Set<string>;
94
92
  }
95
93
  /**
@@ -104,14 +102,12 @@ declare function environmentDocuments(document: WranglerDocument): Deployment[];
104
102
  * @suss/contract-wrangler: behavioral summaries from a Wrangler
105
103
  * configuration document.
106
104
  *
107
- * A `wrangler.toml` deploys a working Worker, and before this reader
108
- * existed suss saw none of it: no deployable unit, no configuration
109
- * contract, no stores and no queues. `name` and `main` give the unit
110
- * and the code it runs, `vars` gives the configuration, the three
111
- * binding blocks give stores, `queues` gives the channels it sends on
112
- * and drains, and each `env.<name>` deploys the same Worker again with
113
- * the top-level document as its default. The README says how each half
114
- * pairs.
105
+ * `name` and `main` give the deployable unit and the code it runs.
106
+ * `vars` gives its configuration, the three binding blocks give its
107
+ * stores, and `queues` gives the channels it sends on and drains. Each
108
+ * `env.<name>` deploys the same Worker again, with the top-level
109
+ * document as its default. The README describes how each summary pairs
110
+ * with the code side.
115
111
  */
116
112
 
117
113
  interface WranglerToSummariesOptions {
@@ -128,7 +124,10 @@ declare function wranglerToSummaries(document: WranglerDocument, options: {
128
124
  source: string;
129
125
  codeScope: string;
130
126
  }): BehavioralSummary[];
131
- /** The path may be the document itself or the directory the Worker is in. */
127
+ /**
128
+ * Reads a Wrangler document and returns its summaries. The path may be
129
+ * the document itself or the directory the Worker is in.
130
+ */
132
131
  declare function wranglerFileToSummaries(target: string, options?: WranglerToSummariesOptions): BehavioralSummary[];
133
132
 
134
133
  export { CONFIGURATION_FILE_NAMES, type DocumentLocation, RECOGNITION, type WranglerDocument, type WranglerRecord, type WranglerToSummariesOptions, environmentDocuments, isConfigurationFile, loadConfigurationDocument, locateConfigurationFile, wranglerFileToSummaries, wranglerToSummaries };
package/dist/index.js CHANGED
@@ -57,8 +57,8 @@ function storeSummary(store, entry, context) {
57
57
  recognition: RECOGNITION,
58
58
  storageSystem: store.storageSystem,
59
59
  scope: "default",
60
- // The binding name is what an access in the Worker spells, so
61
- // it is the name the storage check pairs on.
60
+ // An access in the Worker uses the binding name, so the storage
61
+ // check has to pair on it.
62
62
  container: boundAs ?? resourceName,
63
63
  accessPath: null
64
64
  })
@@ -123,9 +123,9 @@ function queueSide(entries, side, context) {
123
123
  continue;
124
124
  }
125
125
  summaries.push({
126
- // A producer block declares a queue the Worker sends to, which is
127
- // a channel rather than a unit that runs, so it is a library. A
128
- // consumer block says this Worker drains that queue.
126
+ // A producer block declares a queue the Worker sends to. Nothing
127
+ // runs there, so it is a library. A consumer block means this
128
+ // Worker drains the queue.
129
129
  kind: side === "consumers" ? "consumer" : "library",
130
130
  location: {
131
131
  file: context.sourceFile,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/contract-wrangler",
3
- "version": "0.33.0",
3
+ "version": "0.34.0",
4
4
  "type": "module",
5
5
  "description": "Read a Cloudflare wrangler.toml or wrangler.jsonc and check the bindings it declares against the Worker code that uses them.",
6
6
  "keywords": [
@@ -14,7 +14,7 @@
14
14
  ],
15
15
  "license": "Apache-2.0",
16
16
  "author": "Nimbus Cloud AI LLC",
17
- "homepage": "https://github.com/nimbuscloud-ai/suss/tree/main/packages/contract/wrangler#readme",
17
+ "homepage": "https://suss.sh",
18
18
  "repository": {
19
19
  "type": "git",
20
20
  "url": "git+https://github.com/nimbuscloud-ai/suss.git",
@@ -47,8 +47,8 @@
47
47
  "typecheck": "tsc --noEmit"
48
48
  },
49
49
  "dependencies": {
50
- "@suss/behavioral-ir": "0.33.0",
51
- "@suss/ir-core": "0.33.0",
50
+ "@suss/behavioral-ir": "0.34.0",
51
+ "@suss/ir-core": "0.34.0",
52
52
  "jsonc-parser": "^3.3.1",
53
53
  "smol-toml": "^1.4.2"
54
54
  },