@gate-forge/pack-http 0.7.1 → 0.9.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
@@ -10,6 +10,12 @@ classification signals** (dogfood remediation phase 4):
10
10
  Fastify and Hono registrations (import-disambiguated, the pack-auth
11
11
  convention), and NestJS `@Controller('accounts')` + `@Get/@Post/@Put/
12
12
  @Patch/@Delete/@All('…')` decorators.
13
+ `app`/`server`/`router` are server names by convention; an `api.<verb>()`
14
+ call counts as a registration **only** where the file imports a server
15
+ framework (express/fastify/hono), because that is also how an axios
16
+ instance, a Playwright request context, or an api-client module spells
17
+ its calls — reading those as routes minted server routes out of test
18
+ and client call sites.
13
19
  - **Frontend API-client calls** (bounded static dataflow, plan phase 3):
14
20
  direct literal `fetch`/Axios, `fetch(url, { method })`, Axios config
15
21
  objects and instances, configured client symbols (`apiClient.get`),
@@ -22,6 +28,17 @@ classification signals** (dogfood remediation phase 4):
22
28
  disappear and never default to GET. A modeled Axios instance creation
23
29
  with a proven literal `baseURL` joins that base into the emitted call
24
30
  path (see *Instance baseURL joining* below).
31
+ - **The verb of `fetch(url, options)`** comes from a bounded
32
+ request-options model, never from "the options were not inline": an
33
+ inline object (`method` read with last-writer-wins, so a spread and
34
+ the override after it both participate), a module-scope constant
35
+ naming one, or a call to a scanned function whose body is a single
36
+ `return <expression>` with its first parameter bound to that argument
37
+ (so a forwarder resolves through it). Options that provably carry no
38
+ `method` keep the platform default — GET really is what the call
39
+ sends. Options the model cannot read yield a typed
40
+ `HTTP_METHOD_DYNAMIC` entry and **no call fact**: before, such a call
41
+ was recorded as a GET and joined a route the server never serves.
25
42
 
26
43
  ## Client-scan configuration
27
44
 
@@ -109,6 +126,59 @@ wrapper's paths fully, or use a builder with `base`). The
109
126
  `urlBuilders[].base` channel is unchanged: it is configuration-declared,
110
127
  so the builder's base is part of the resolved value itself.
111
128
 
129
+ ### Response field reads (bounded, file-local)
130
+
131
+ Every frontend-call fact carries `attributes.responseReads`: the fields
132
+ the call site reads off that call's own response, each with the location
133
+ of the read. It is the frontend half of the dropped-response-field proof
134
+ (a merged frontend read `invoice.dueDate` after the FastAPI model had
135
+ dropped it; every test mocked the response and the screen showed
136
+ nothing), and it is collected by the same bounded pass that resolves the
137
+ call target — no execution, no type checker, no cross-file inference.
138
+
139
+ The model is deliberately small:
140
+
141
+ - the call's own awaited result is followed through `await` and
142
+ parentheses; `<result>.data` is the payload (the axios/kit envelope) and
143
+ `<result>` alone is the envelope, so `res.status`/`res.headers` are not
144
+ fields;
145
+ - a name bound from either of those (`const r = await call`,
146
+ `const { data: d } = await call`, `const d = (await call).data`) is
147
+ followed by name inside the enclosing function-like — nested
148
+ function-likes included, so a `useEffect` callback still counts;
149
+ - `holder.<field>`, `holder.data.<field>`, `holder.data['<field>']` and
150
+ `holder['<field>']` are reads, as is every key of an object
151
+ destructuring of a holder or of `<holder>.data`;
152
+ - a `let` holder (reassigned before the read), a computed key
153
+ (`d[key]`), and a JavaScript member (`data.map`, `data.length`,
154
+ `status`) are never reads;
155
+ - a branch that opens on the call's OWN envelope (`ok`, `status`) is read
156
+ for its POLARITY, and only the failure arm is dropped: the body a
157
+ failure branch reads (`res.data?.detail`) is the server's ERROR
158
+ envelope, never the success model. `if (res.ok)`, `if (res.ok ===
159
+ true)`, `if (res.status >= 400)`, `> 399`, `!== 200`, `!== 201`, `< 400`,
160
+ `=== 200`, `<= 299` and their `!` negations all decide which arm runs,
161
+ so the success arm keeps its reads — `if (res.ok) setItems(res.data.
162
+ items)` is exactly the shape where a dropped field hides. A compound
163
+ condition (`!res.ok || res.status >= 500`), a comparison this pass
164
+ cannot read (`>= Math.min(400, limit)`) and a `statusText` test are
165
+ undecidable, so NEITHER arm is collected. A ternary is the same guard as
166
+ an `if` — `res.ok ? res.data.items : res.data?.detail` keeps the
167
+ success arm — and a ternary on anything else is not a guard at all: both
168
+ arms are ordinary success-path reads. A read after an early-return guard
169
+ (`if (!res.ok) { throw … }` … then the code) is collected as usual;
170
+ - a read that is one operand of a `||` / `??` chain carries that chain's
171
+ index, so the check judges the chain as the ONE decision it is:
172
+ `res.data?.invoice_id || res.data?.invoice?.id` is silent when the
173
+ model declares `invoice_id`, because `invoice` is its defensive
174
+ fallback. A chain in which no operand is declared is reported whole.
175
+
176
+ `gateforge check` compares these reads against the response model the
177
+ joined backend route declares and emits one **non-blocking**
178
+ `RESPONSE_FIELD_MISSING_FROM_MODEL` advisory per undeclared field. A call
179
+ nobody consumes emits no attribute at all, so it stays byte-identical to
180
+ before.
181
+
112
182
  ### Scan scoping (optional, strict, back-compatible)
113
183
 
114
184
  All scoping keys are OPTIONAL; a config without them scans exactly as
@@ -147,6 +147,25 @@ export declare function clientSymbolActiveIn(config: ClientScanConfig, name: str
147
147
  * scope, `<symbol>.<verb>(path, handler)` can only be a router).
148
148
  */
149
149
  export declare function activeClientSymbolNamesIn(config: ClientScanConfig, file: string): string[];
150
+ /**
151
+ * One field the call site reads off this call's response: the name
152
+ * exactly as the code writes it, and where that read happens. Collected
153
+ * by the same bounded static pass that resolves the call target —
154
+ * `.data.<field>`, `.data['<field>']`, and destructuring of the awaited
155
+ * result or of its payload, inside the enclosing function, same file.
156
+ */
157
+ export interface ResponseRead {
158
+ field: string;
159
+ location: Location;
160
+ /**
161
+ * Index of the `||` / `??` fallback chain this operand belongs to, in
162
+ * source order within the call; absent for a read that stands alone.
163
+ * `res.data?.invoice_id || res.data?.invoice?.id` is one decision about
164
+ * ONE result, so its operands are judged together by the response-model
165
+ * check instead of one by one.
166
+ */
167
+ chain?: number;
168
+ }
150
169
  /** One discovered frontend call (one row per source callsite). */
151
170
  export interface ClientCall {
152
171
  method: HttpMethod;
@@ -166,6 +185,13 @@ export interface ClientCall {
166
185
  /** Producing client, e.g. `fetch`, `axios`, `apiClient`, `apiGet`. */
167
186
  framework: string;
168
187
  location: Location;
188
+ /**
189
+ * Fields this call site reads off the response, in source order.
190
+ * Absent when the code reads none — the attribute is minted only when
191
+ * it says something, so a call with no read stays byte-identical to
192
+ * the pre-feature contract.
193
+ */
194
+ responseReads?: ResponseRead[];
169
195
  }
170
196
  export interface ClientScanUnresolved {
171
197
  code: typeof FRONTEND_CALL_TARGET_UNRESOLVED | typeof HTTP_METHOD_DYNAMIC | typeof HTTP_PATH_DYNAMIC;
@@ -1 +1 @@
1
- {"version":3,"file":"client-calls.d.ts","sourceRoot":"","sources":["../src/client-calls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAKH,OAAO,EACL,+BAA+B,EAC/B,mBAAmB,EACnB,iBAAiB,EAElB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAED,4EAA4E;AAC5E,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB;IAC7D,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,yEAAyE;AACzE,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB;IAChE,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,uEAAuE;AACvE,MAAM,WAAW,gBAAiB,SAAQ,mBAAmB;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,aAAa,CAAC,EAAE,aAAa,CAAC,MAAM,GAAG,kBAAkB,CAAC,CAAC;IAC3D;;;OAGG;IACH,gBAAgB,CAAC,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;IACxD,oEAAoE;IACpE,WAAW,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC9C,yEAAyE;IACzE,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;;;OAIG;IACH,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;;;OAIG;IACH,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,eAAO,MAAM,0BAA0B,EAAE,gBAAqB,CAAC;AAE/D;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAErF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAErF;AAoBD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGlG;AAsCD;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAO1F;AAED,kEAAkE;AAClE,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,OAAO,+BAA+B,GAAG,OAAO,mBAAmB,GAAG,OAAO,iBAAiB,CAAC;IACrG,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,UAAU,EAAE,oBAAoB,EAAE,CAAC;CACpC;AAwLD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,gBAAgB,EACxB,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GACxC,gBAAgB,CA6BlB;AA6uBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,gBAAgB,CA8DhF"}
1
+ {"version":3,"file":"client-calls.d.ts","sourceRoot":"","sources":["../src/client-calls.ts"],"names":[],"mappings":"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"}