@suss/contract-aws-apigateway 0.0.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.
- package/LICENSE +205 -0
- package/README.md +34 -0
- package/dist/index.cjs +585 -0
- package/dist/index.d.cts +184 -0
- package/dist/index.d.ts +184 -0
- package/dist/index.js +557 -0
- package/package.json +58 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
// src/http.ts
|
|
2
|
+
import { restBinding as restBinding2 } from "@suss/behavioral-ir";
|
|
3
|
+
|
|
4
|
+
// src/shared.ts
|
|
5
|
+
import { restBinding } from "@suss/behavioral-ir";
|
|
6
|
+
var FRAMEWORK = "apigateway";
|
|
7
|
+
var PROTOCOL = "http";
|
|
8
|
+
function throttleEnforces(throttle) {
|
|
9
|
+
const enforces = (v) => v !== void 0 && v > 0;
|
|
10
|
+
return enforces(throttle.burstLimit) || enforces(throttle.rateLimit);
|
|
11
|
+
}
|
|
12
|
+
function integrationCanTimeOut(integration) {
|
|
13
|
+
return integration.type !== "mock" && integration.type !== "unknown";
|
|
14
|
+
}
|
|
15
|
+
function integrationCanFail(integration) {
|
|
16
|
+
return integration.type !== "mock" && integration.type !== "unknown";
|
|
17
|
+
}
|
|
18
|
+
function buildCorsPreflightSummary(options) {
|
|
19
|
+
const { apiId, path, cors, sourceFile, extraMetadata } = options;
|
|
20
|
+
const ownerKey = `${apiId}:OPTIONS:${path}:cors`;
|
|
21
|
+
const headers = {
|
|
22
|
+
"Access-Control-Allow-Origin": {
|
|
23
|
+
type: "literal",
|
|
24
|
+
value: cors.allowOrigins.join(",")
|
|
25
|
+
},
|
|
26
|
+
"Access-Control-Allow-Methods": {
|
|
27
|
+
type: "literal",
|
|
28
|
+
value: cors.allowMethods.join(",")
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
if (cors.allowHeaders !== void 0 && cors.allowHeaders.length > 0) {
|
|
32
|
+
headers["Access-Control-Allow-Headers"] = {
|
|
33
|
+
type: "literal",
|
|
34
|
+
value: cors.allowHeaders.join(",")
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (cors.exposeHeaders !== void 0 && cors.exposeHeaders.length > 0) {
|
|
38
|
+
headers["Access-Control-Expose-Headers"] = {
|
|
39
|
+
type: "literal",
|
|
40
|
+
value: cors.exposeHeaders.join(",")
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (cors.allowCredentials === true) {
|
|
44
|
+
headers["Access-Control-Allow-Credentials"] = {
|
|
45
|
+
type: "literal",
|
|
46
|
+
value: "true"
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (cors.maxAge !== void 0) {
|
|
50
|
+
headers["Access-Control-Max-Age"] = {
|
|
51
|
+
type: "literal",
|
|
52
|
+
value: String(cors.maxAge)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const transition = {
|
|
56
|
+
id: `${ownerKey}:platform:204`,
|
|
57
|
+
conditions: [
|
|
58
|
+
{
|
|
59
|
+
type: "opaque",
|
|
60
|
+
sourceText: "aws:apigateway:cors-preflight",
|
|
61
|
+
reason: "externalFunction"
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
output: {
|
|
65
|
+
type: "response",
|
|
66
|
+
statusCode: { type: "literal", value: 204 },
|
|
67
|
+
body: null,
|
|
68
|
+
headers
|
|
69
|
+
},
|
|
70
|
+
effects: [],
|
|
71
|
+
location: { start: 0, end: 0 },
|
|
72
|
+
isDefault: false,
|
|
73
|
+
confidence: { source: "derived", level: "high" },
|
|
74
|
+
metadata: {
|
|
75
|
+
source: "aws::apigateway::platform",
|
|
76
|
+
platform: "apiGateway",
|
|
77
|
+
causes: ["cors-preflight"],
|
|
78
|
+
...cors.configRef !== void 0 ? { configRefs: [cors.configRef] } : {}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
kind: "handler",
|
|
83
|
+
location: {
|
|
84
|
+
file: `${sourceFile}:${ownerKey}`,
|
|
85
|
+
range: { start: 0, end: 0 },
|
|
86
|
+
exportName: null
|
|
87
|
+
},
|
|
88
|
+
identity: {
|
|
89
|
+
name: ownerKey,
|
|
90
|
+
exportPath: null,
|
|
91
|
+
boundaryBinding: restBinding({
|
|
92
|
+
transport: PROTOCOL,
|
|
93
|
+
method: "OPTIONS",
|
|
94
|
+
path,
|
|
95
|
+
recognition: FRAMEWORK
|
|
96
|
+
})
|
|
97
|
+
},
|
|
98
|
+
inputs: [],
|
|
99
|
+
transitions: [transition],
|
|
100
|
+
gaps: [],
|
|
101
|
+
confidence: { source: "derived", level: "high" },
|
|
102
|
+
metadata: {
|
|
103
|
+
apiId,
|
|
104
|
+
synthetic: "cors-preflight",
|
|
105
|
+
...extraMetadata ?? {}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/transitions.ts
|
|
111
|
+
function makeContribution(cause, configRef) {
|
|
112
|
+
if (configRef === void 0) {
|
|
113
|
+
return { cause };
|
|
114
|
+
}
|
|
115
|
+
return { cause, configRef };
|
|
116
|
+
}
|
|
117
|
+
function handlerTransition(args) {
|
|
118
|
+
return {
|
|
119
|
+
id: `${args.ownerKey}:integration:${args.statusCode}`,
|
|
120
|
+
conditions: [],
|
|
121
|
+
output: {
|
|
122
|
+
type: "response",
|
|
123
|
+
statusCode: { type: "literal", value: args.statusCode },
|
|
124
|
+
body: null,
|
|
125
|
+
headers: {}
|
|
126
|
+
},
|
|
127
|
+
effects: [],
|
|
128
|
+
location: { start: 0, end: 0 },
|
|
129
|
+
isDefault: false,
|
|
130
|
+
confidence: { source: "derived", level: "high" },
|
|
131
|
+
metadata: {
|
|
132
|
+
source: args.source,
|
|
133
|
+
...args.configRef !== void 0 ? { configRef: args.configRef } : {}
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function platformTransition(args) {
|
|
138
|
+
if (args.contributions.length === 0) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
const causes = unique(args.contributions.map((c) => c.cause));
|
|
142
|
+
const refs = args.contributions.map((c) => c.configRef).filter((r) => r !== void 0);
|
|
143
|
+
const predicate = {
|
|
144
|
+
type: "opaque",
|
|
145
|
+
sourceText: `aws:apigateway:status-${args.statusCode}`,
|
|
146
|
+
reason: "externalFunction"
|
|
147
|
+
};
|
|
148
|
+
return {
|
|
149
|
+
id: `${args.ownerKey}:platform:${args.statusCode}`,
|
|
150
|
+
conditions: [predicate],
|
|
151
|
+
output: {
|
|
152
|
+
type: "response",
|
|
153
|
+
statusCode: { type: "literal", value: args.statusCode },
|
|
154
|
+
body: null,
|
|
155
|
+
headers: {}
|
|
156
|
+
},
|
|
157
|
+
effects: [],
|
|
158
|
+
location: { start: 0, end: 0 },
|
|
159
|
+
isDefault: false,
|
|
160
|
+
confidence: { source: "derived", level: "high" },
|
|
161
|
+
metadata: {
|
|
162
|
+
source: "aws::apigateway::platform",
|
|
163
|
+
platform: "apiGateway",
|
|
164
|
+
causes,
|
|
165
|
+
...refs.length > 0 ? { configRefs: refs } : {}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function unique(values) {
|
|
170
|
+
return Array.from(new Set(values));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/http.ts
|
|
174
|
+
function httpApiToSummaries(config) {
|
|
175
|
+
const sourceFile = config.source ?? `aws-apigateway:${config.id}`;
|
|
176
|
+
const summaries = [];
|
|
177
|
+
for (const route of config.routes) {
|
|
178
|
+
const parsed = parseRouteKey(route.routeKey);
|
|
179
|
+
if (parsed === null) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
summaries.push(buildRouteSummary(route, parsed, config, sourceFile));
|
|
183
|
+
}
|
|
184
|
+
if (config.cors !== void 0) {
|
|
185
|
+
for (const path of uniquePaths(config.routes)) {
|
|
186
|
+
summaries.push(
|
|
187
|
+
buildCorsPreflightSummary({
|
|
188
|
+
apiId: config.id,
|
|
189
|
+
path,
|
|
190
|
+
cors: config.cors,
|
|
191
|
+
sourceFile,
|
|
192
|
+
extraMetadata: { apiVersion: "v2" }
|
|
193
|
+
})
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return summaries;
|
|
198
|
+
}
|
|
199
|
+
function parseRouteKey(routeKey) {
|
|
200
|
+
const trimmed = routeKey.trim();
|
|
201
|
+
if (trimmed === "" || trimmed === "$default") {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
const space = trimmed.indexOf(" ");
|
|
205
|
+
if (space < 0) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
const method = trimmed.slice(0, space).toUpperCase();
|
|
209
|
+
const path = trimmed.slice(space + 1).trim();
|
|
210
|
+
if (method === "" || path === "") {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
return { method, path };
|
|
214
|
+
}
|
|
215
|
+
function buildRouteSummary(route, parsed, api, sourceFile) {
|
|
216
|
+
const ownerKey = route.name ?? `${api.id}:${parsed.method}:${parsed.path}`;
|
|
217
|
+
const transitions = [];
|
|
218
|
+
for (const code of route.integration.statusCodes) {
|
|
219
|
+
transitions.push(
|
|
220
|
+
handlerTransition({
|
|
221
|
+
ownerKey,
|
|
222
|
+
statusCode: code,
|
|
223
|
+
source: `aws::apigateway::integration.${route.integration.type}`,
|
|
224
|
+
configRef: route.integration.configRef
|
|
225
|
+
})
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
const platformByStatus = collectPlatformContributions(route, api);
|
|
229
|
+
for (const [status, contributions] of platformByStatus) {
|
|
230
|
+
const transition = platformTransition({
|
|
231
|
+
ownerKey,
|
|
232
|
+
statusCode: status,
|
|
233
|
+
contributions
|
|
234
|
+
});
|
|
235
|
+
if (transition !== null) {
|
|
236
|
+
transitions.push(transition);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (transitions.length === 0) {
|
|
240
|
+
transitions.push({
|
|
241
|
+
id: `${ownerKey}:integration:default`,
|
|
242
|
+
conditions: [],
|
|
243
|
+
output: {
|
|
244
|
+
type: "response",
|
|
245
|
+
statusCode: null,
|
|
246
|
+
body: null,
|
|
247
|
+
headers: {}
|
|
248
|
+
},
|
|
249
|
+
effects: [],
|
|
250
|
+
location: { start: 0, end: 0 },
|
|
251
|
+
isDefault: true,
|
|
252
|
+
confidence: { source: "derived", level: "low" },
|
|
253
|
+
metadata: {
|
|
254
|
+
source: `aws::apigateway::integration.${route.integration.type}`
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
kind: "handler",
|
|
260
|
+
location: {
|
|
261
|
+
file: `${sourceFile}:${ownerKey}`,
|
|
262
|
+
range: { start: 0, end: 0 },
|
|
263
|
+
exportName: null
|
|
264
|
+
},
|
|
265
|
+
identity: {
|
|
266
|
+
name: ownerKey,
|
|
267
|
+
exportPath: null,
|
|
268
|
+
boundaryBinding: restBinding2({
|
|
269
|
+
transport: PROTOCOL,
|
|
270
|
+
method: parsed.method,
|
|
271
|
+
path: parsed.path,
|
|
272
|
+
recognition: FRAMEWORK
|
|
273
|
+
})
|
|
274
|
+
},
|
|
275
|
+
inputs: [],
|
|
276
|
+
transitions,
|
|
277
|
+
gaps: [],
|
|
278
|
+
confidence: { source: "derived", level: "high" },
|
|
279
|
+
metadata: {
|
|
280
|
+
apiId: api.id,
|
|
281
|
+
apiVersion: "v2",
|
|
282
|
+
integrationType: route.integration.type,
|
|
283
|
+
http: {
|
|
284
|
+
declaredContract: {
|
|
285
|
+
framework: FRAMEWORK,
|
|
286
|
+
provenance: "independent",
|
|
287
|
+
responses: route.integration.statusCodes.map((statusCode) => ({
|
|
288
|
+
statusCode
|
|
289
|
+
}))
|
|
290
|
+
},
|
|
291
|
+
// Additive pointer to the code that implements this route (SAM
|
|
292
|
+
// Lambda proxy Handler), so a checker can correlate the declared
|
|
293
|
+
// route with the extracted handler summary carrying the same
|
|
294
|
+
// REST binding.
|
|
295
|
+
...route.implementingHandler !== void 0 ? { implementingHandler: route.implementingHandler } : {}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
function collectPlatformContributions(route, api) {
|
|
301
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
302
|
+
const add = (status, contribution) => {
|
|
303
|
+
const existing = buckets.get(status);
|
|
304
|
+
if (existing === void 0) {
|
|
305
|
+
buckets.set(status, [contribution]);
|
|
306
|
+
} else {
|
|
307
|
+
existing.push(contribution);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
const authorizer = resolveAuthorizer(route, api);
|
|
311
|
+
if (authorizer !== null) {
|
|
312
|
+
if (authorizer.identitySourceRequired !== false) {
|
|
313
|
+
add(401, makeContribution("authorization", authorizer.configRef));
|
|
314
|
+
}
|
|
315
|
+
add(403, makeContribution("authorization", authorizer.configRef));
|
|
316
|
+
}
|
|
317
|
+
const throttle = resolveThrottle(route, api);
|
|
318
|
+
if (throttle !== null && throttleEnforces(throttle)) {
|
|
319
|
+
add(429, makeContribution("throttle", throttle.configRef));
|
|
320
|
+
}
|
|
321
|
+
if (integrationCanTimeOut(route.integration)) {
|
|
322
|
+
add(
|
|
323
|
+
504,
|
|
324
|
+
makeContribution("integration-timeout", route.integration.configRef)
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
if (integrationCanFail(route.integration)) {
|
|
328
|
+
add(
|
|
329
|
+
502,
|
|
330
|
+
makeContribution("integration-failure", route.integration.configRef)
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
return buckets;
|
|
334
|
+
}
|
|
335
|
+
function resolveAuthorizer(route, api) {
|
|
336
|
+
if (route.authorizer === null) {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
if (route.authorizer !== void 0) {
|
|
340
|
+
return route.authorizer;
|
|
341
|
+
}
|
|
342
|
+
return api.defaultAuthorizer ?? null;
|
|
343
|
+
}
|
|
344
|
+
function resolveThrottle(route, api) {
|
|
345
|
+
if (route.throttle !== void 0) {
|
|
346
|
+
return route.throttle;
|
|
347
|
+
}
|
|
348
|
+
return api.defaultThrottle ?? null;
|
|
349
|
+
}
|
|
350
|
+
function uniquePaths(routes) {
|
|
351
|
+
const seen = /* @__PURE__ */ new Set();
|
|
352
|
+
const out = [];
|
|
353
|
+
for (const r of routes) {
|
|
354
|
+
const parsed = parseRouteKey(r.routeKey);
|
|
355
|
+
if (parsed === null || seen.has(parsed.path)) {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
seen.add(parsed.path);
|
|
359
|
+
out.push(parsed.path);
|
|
360
|
+
}
|
|
361
|
+
return out;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// src/rest.ts
|
|
365
|
+
import { restBinding as restBinding3 } from "@suss/behavioral-ir";
|
|
366
|
+
function restApiToSummaries(config) {
|
|
367
|
+
const sourceFile = config.source ?? `aws-apigateway:${config.id}`;
|
|
368
|
+
const summaries = [];
|
|
369
|
+
for (const endpoint of config.endpoints) {
|
|
370
|
+
summaries.push(buildEndpointSummary(endpoint, config, sourceFile));
|
|
371
|
+
}
|
|
372
|
+
if (config.cors !== void 0) {
|
|
373
|
+
for (const endpoint of dedupeByPath(config.endpoints)) {
|
|
374
|
+
summaries.push(
|
|
375
|
+
buildCorsPreflightSummary({
|
|
376
|
+
apiId: config.id,
|
|
377
|
+
path: endpoint.path,
|
|
378
|
+
cors: config.cors,
|
|
379
|
+
sourceFile
|
|
380
|
+
})
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return summaries;
|
|
385
|
+
}
|
|
386
|
+
function buildEndpointSummary(endpoint, api, sourceFile) {
|
|
387
|
+
const method = endpoint.method.toUpperCase();
|
|
388
|
+
const ownerKey = endpoint.name ?? `${api.id}:${method}:${endpoint.path}`;
|
|
389
|
+
const transitions = [];
|
|
390
|
+
for (const code of endpoint.integration.statusCodes) {
|
|
391
|
+
transitions.push(
|
|
392
|
+
handlerTransition({
|
|
393
|
+
ownerKey,
|
|
394
|
+
statusCode: code,
|
|
395
|
+
source: `aws::apigateway::integration.${endpoint.integration.type}`,
|
|
396
|
+
configRef: endpoint.integration.configRef
|
|
397
|
+
})
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
const platformByStatus = collectPlatformContributions2(endpoint, api);
|
|
401
|
+
for (const [status, contributions] of platformByStatus) {
|
|
402
|
+
const transition = platformTransition({
|
|
403
|
+
ownerKey,
|
|
404
|
+
statusCode: status,
|
|
405
|
+
contributions
|
|
406
|
+
});
|
|
407
|
+
if (transition !== null) {
|
|
408
|
+
transitions.push(transition);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (transitions.length === 0) {
|
|
412
|
+
transitions.push({
|
|
413
|
+
id: `${ownerKey}:integration:default`,
|
|
414
|
+
conditions: [],
|
|
415
|
+
output: {
|
|
416
|
+
type: "response",
|
|
417
|
+
statusCode: null,
|
|
418
|
+
body: null,
|
|
419
|
+
headers: {}
|
|
420
|
+
},
|
|
421
|
+
effects: [],
|
|
422
|
+
location: { start: 0, end: 0 },
|
|
423
|
+
isDefault: true,
|
|
424
|
+
confidence: { source: "derived", level: "low" },
|
|
425
|
+
metadata: {
|
|
426
|
+
source: `aws::apigateway::integration.${endpoint.integration.type}`
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
return {
|
|
431
|
+
kind: "handler",
|
|
432
|
+
location: {
|
|
433
|
+
file: `${sourceFile}:${ownerKey}`,
|
|
434
|
+
range: { start: 0, end: 0 },
|
|
435
|
+
exportName: null
|
|
436
|
+
},
|
|
437
|
+
identity: {
|
|
438
|
+
name: ownerKey,
|
|
439
|
+
exportPath: null,
|
|
440
|
+
boundaryBinding: restBinding3({
|
|
441
|
+
transport: PROTOCOL,
|
|
442
|
+
method,
|
|
443
|
+
path: endpoint.path,
|
|
444
|
+
recognition: FRAMEWORK
|
|
445
|
+
})
|
|
446
|
+
},
|
|
447
|
+
inputs: [],
|
|
448
|
+
transitions,
|
|
449
|
+
gaps: [],
|
|
450
|
+
confidence: { source: "derived", level: "high" },
|
|
451
|
+
metadata: {
|
|
452
|
+
apiId: api.id,
|
|
453
|
+
integrationType: endpoint.integration.type,
|
|
454
|
+
http: {
|
|
455
|
+
// The integration's declared status codes (from MethodResponses
|
|
456
|
+
// in the CFN template) are the declared contract here. Platform-
|
|
457
|
+
// injected transitions (authorizer 401/403, throttle 429, etc.)
|
|
458
|
+
// come from SEPARATE config fields and so are independent from
|
|
459
|
+
// this contract — contract-consistency comparison is meaningful
|
|
460
|
+
// and will surface template-internal inconsistencies.
|
|
461
|
+
declaredContract: {
|
|
462
|
+
framework: FRAMEWORK,
|
|
463
|
+
provenance: "independent",
|
|
464
|
+
responses: endpoint.integration.statusCodes.map((statusCode) => ({
|
|
465
|
+
statusCode
|
|
466
|
+
}))
|
|
467
|
+
},
|
|
468
|
+
// Additive pointer to the code that implements this endpoint
|
|
469
|
+
// (SAM Lambda proxy Handler), so a checker can correlate the
|
|
470
|
+
// declared route with the extracted handler summary carrying the
|
|
471
|
+
// same REST binding.
|
|
472
|
+
...endpoint.implementingHandler !== void 0 ? { implementingHandler: endpoint.implementingHandler } : {}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
function collectPlatformContributions2(endpoint, api) {
|
|
478
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
479
|
+
const add = (status, contribution) => {
|
|
480
|
+
const existing = buckets.get(status);
|
|
481
|
+
if (existing === void 0) {
|
|
482
|
+
buckets.set(status, [contribution]);
|
|
483
|
+
} else {
|
|
484
|
+
existing.push(contribution);
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
const authorizer = resolveAuthorizer2(endpoint, api);
|
|
488
|
+
if (authorizer !== null) {
|
|
489
|
+
if (authorizer.identitySourceRequired !== false) {
|
|
490
|
+
add(401, makeContribution("authorization", authorizer.configRef));
|
|
491
|
+
}
|
|
492
|
+
add(403, makeContribution("authorization", authorizer.configRef));
|
|
493
|
+
}
|
|
494
|
+
if (endpoint.apiKeyRequired === true) {
|
|
495
|
+
add(403, makeContribution("api-key", endpoint.configRef));
|
|
496
|
+
}
|
|
497
|
+
if (endpoint.requestValidation !== void 0) {
|
|
498
|
+
const v = endpoint.requestValidation;
|
|
499
|
+
if (v.body === true || v.params === true || v.headers === true) {
|
|
500
|
+
add(
|
|
501
|
+
400,
|
|
502
|
+
makeContribution(
|
|
503
|
+
"request-validation",
|
|
504
|
+
v.configRef ?? endpoint.configRef
|
|
505
|
+
)
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
const throttle = resolveThrottle2(endpoint, api);
|
|
510
|
+
if (throttle !== null && throttleEnforces(throttle)) {
|
|
511
|
+
add(429, makeContribution("throttle", throttle.configRef));
|
|
512
|
+
}
|
|
513
|
+
if (integrationCanTimeOut(endpoint.integration)) {
|
|
514
|
+
add(
|
|
515
|
+
504,
|
|
516
|
+
makeContribution("integration-timeout", endpoint.integration.configRef)
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
if (integrationCanFail(endpoint.integration)) {
|
|
520
|
+
add(
|
|
521
|
+
502,
|
|
522
|
+
makeContribution("integration-failure", endpoint.integration.configRef)
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
return buckets;
|
|
526
|
+
}
|
|
527
|
+
function resolveAuthorizer2(endpoint, api) {
|
|
528
|
+
if (endpoint.authorizer === null) {
|
|
529
|
+
return null;
|
|
530
|
+
}
|
|
531
|
+
if (endpoint.authorizer !== void 0) {
|
|
532
|
+
return endpoint.authorizer;
|
|
533
|
+
}
|
|
534
|
+
return api.defaultAuthorizer ?? null;
|
|
535
|
+
}
|
|
536
|
+
function resolveThrottle2(endpoint, api) {
|
|
537
|
+
if (endpoint.throttle !== void 0) {
|
|
538
|
+
return endpoint.throttle;
|
|
539
|
+
}
|
|
540
|
+
return api.defaultThrottle ?? null;
|
|
541
|
+
}
|
|
542
|
+
function dedupeByPath(endpoints) {
|
|
543
|
+
const seen = /* @__PURE__ */ new Set();
|
|
544
|
+
const out = [];
|
|
545
|
+
for (const e of endpoints) {
|
|
546
|
+
if (seen.has(e.path)) {
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
seen.add(e.path);
|
|
550
|
+
out.push(e);
|
|
551
|
+
}
|
|
552
|
+
return out;
|
|
553
|
+
}
|
|
554
|
+
export {
|
|
555
|
+
httpApiToSummaries,
|
|
556
|
+
restApiToSummaries
|
|
557
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@suss/contract-aws-apigateway",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Generate suss behavioral summaries for AWS API Gateway resources (REST + HTTP API), independent of how they were deployed.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"suss",
|
|
8
|
+
"behavioral-summary",
|
|
9
|
+
"aws",
|
|
10
|
+
"api-gateway",
|
|
11
|
+
"apigateway"
|
|
12
|
+
],
|
|
13
|
+
"license": "Apache-2.0",
|
|
14
|
+
"author": "Nimbus Cloud AI LLC",
|
|
15
|
+
"homepage": "https://github.com/nimbuscloud-ai/suss/tree/main/packages/contract/aws-apigateway#readme",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/nimbuscloud-ai/suss.git",
|
|
19
|
+
"directory": "packages/contract/aws-apigateway"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/nimbuscloud-ai/suss/issues"
|
|
23
|
+
},
|
|
24
|
+
"main": "./dist/index.cjs",
|
|
25
|
+
"module": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js",
|
|
31
|
+
"require": "./dist/index.cjs"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"README.md",
|
|
37
|
+
"LICENSE"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsup",
|
|
41
|
+
"test": "vitest run",
|
|
42
|
+
"test:coverage": "vitest run --coverage",
|
|
43
|
+
"lint": "biome check .",
|
|
44
|
+
"lint:fix": "biome check --write .",
|
|
45
|
+
"typecheck": "tsc --noEmit"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@suss/behavioral-ir": "0.0.1"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"tsup": "*",
|
|
52
|
+
"typescript": "*",
|
|
53
|
+
"vitest": "*"
|
|
54
|
+
},
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
}
|
|
58
|
+
}
|