@suss/packs 0.11.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/LICENSE +205 -0
- package/README.md +29 -0
- package/dist/activerecord.d.ts +30 -0
- package/dist/activerecord.js +45 -0
- package/dist/apollo-client.d.ts +1 -0
- package/dist/apollo-client.js +137 -0
- package/dist/apollo.d.ts +1 -0
- package/dist/apollo.js +90 -0
- package/dist/aws-dynamodb.d.ts +1 -0
- package/dist/aws-dynamodb.js +386 -0
- package/dist/aws-eventbridge.d.ts +1 -0
- package/dist/aws-eventbridge.js +240 -0
- package/dist/aws-lambda.d.ts +1 -0
- package/dist/aws-lambda.js +382 -0
- package/dist/aws-s3.d.ts +1 -0
- package/dist/aws-s3.js +120 -0
- package/dist/aws-sqs.d.ts +1 -0
- package/dist/aws-sqs.js +296 -0
- package/dist/axios.d.ts +1 -0
- package/dist/axios.js +107 -0
- package/dist/cloudflare-workers.d.ts +1 -0
- package/dist/cloudflare-workers.js +562 -0
- package/dist/drizzle.d.ts +1 -0
- package/dist/drizzle.js +383 -0
- package/dist/express.d.ts +1 -0
- package/dist/express.js +124 -0
- package/dist/fastapi.d.ts +1 -0
- package/dist/fastapi.js +42 -0
- package/dist/fastify.d.ts +1 -0
- package/dist/fastify.js +116 -0
- package/dist/fetch.d.ts +1 -0
- package/dist/fetch.js +64 -0
- package/dist/flask-restx.d.ts +1 -0
- package/dist/flask-restx.js +59 -0
- package/dist/gcs.d.ts +1 -0
- package/dist/gcs.js +134 -0
- package/dist/graphql-ruby.d.ts +1 -0
- package/dist/graphql-ruby.js +84 -0
- package/dist/hono.d.ts +1 -0
- package/dist/hono.js +184 -0
- package/dist/mongoose.d.ts +1 -0
- package/dist/mongoose.js +379 -0
- package/dist/nestjs-graphql.d.ts +1 -0
- package/dist/nestjs-graphql.js +77 -0
- package/dist/nestjs-rest.d.ts +1 -0
- package/dist/nestjs-rest.js +89 -0
- package/dist/nextjs.d.ts +1 -0
- package/dist/nextjs.js +154 -0
- package/dist/node.d.ts +1 -0
- package/dist/node.js +667 -0
- package/dist/prisma.d.ts +1 -0
- package/dist/prisma.js +448 -0
- package/dist/react-router.d.ts +1 -0
- package/dist/react-router.js +197 -0
- package/dist/react.d.ts +1 -0
- package/dist/react.js +221 -0
- package/dist/redis.d.ts +1 -0
- package/dist/redis.js +93 -0
- package/dist/sqlalchemy.d.ts +1 -0
- package/dist/sqlalchemy.js +48 -0
- package/dist/ts-rest.d.ts +1 -0
- package/dist/ts-rest.js +89 -0
- package/package.json +208 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
// ../framework/aws-lambda/dist/index.js
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import {
|
|
5
|
+
loadTemplateTree,
|
|
6
|
+
qualifiedLogicalId,
|
|
7
|
+
readAppSyncResolvers,
|
|
8
|
+
readServerlessFunctions,
|
|
9
|
+
unfollowedStackMessage
|
|
10
|
+
} from "@suss/manifest-aws";
|
|
11
|
+
var TEMPLATE_NAMES = ["template.yaml", "template.yml", "template.json"];
|
|
12
|
+
var dirToTemplate = /* @__PURE__ */ new Map();
|
|
13
|
+
var templateToIndex = /* @__PURE__ */ new Map();
|
|
14
|
+
function findTemplate(startDir) {
|
|
15
|
+
const chain = [];
|
|
16
|
+
let dir = startDir;
|
|
17
|
+
while (true) {
|
|
18
|
+
const cached = dirToTemplate.get(dir);
|
|
19
|
+
if (cached !== void 0) {
|
|
20
|
+
for (const d of chain) {
|
|
21
|
+
dirToTemplate.set(d, cached);
|
|
22
|
+
}
|
|
23
|
+
return cached;
|
|
24
|
+
}
|
|
25
|
+
chain.push(dir);
|
|
26
|
+
const found = TEMPLATE_NAMES.map((name) => path.join(dir, name)).find(
|
|
27
|
+
(candidate) => fs.existsSync(candidate)
|
|
28
|
+
) ?? null;
|
|
29
|
+
if (found !== null) {
|
|
30
|
+
for (const d of chain) {
|
|
31
|
+
dirToTemplate.set(d, found);
|
|
32
|
+
}
|
|
33
|
+
return found;
|
|
34
|
+
}
|
|
35
|
+
const parent = path.dirname(dir);
|
|
36
|
+
if (parent === dir) {
|
|
37
|
+
for (const d of chain) {
|
|
38
|
+
dirToTemplate.set(d, null);
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
dir = parent;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function indexForTemplate(templatePath) {
|
|
46
|
+
const cached = templateToIndex.get(templatePath);
|
|
47
|
+
if (cached !== void 0) {
|
|
48
|
+
return cached;
|
|
49
|
+
}
|
|
50
|
+
const index = /* @__PURE__ */ new Map();
|
|
51
|
+
try {
|
|
52
|
+
const tree = loadTemplateTree(templatePath);
|
|
53
|
+
for (const stack of tree.unfollowed) {
|
|
54
|
+
process.stderr.write(
|
|
55
|
+
`[suss] aws-lambda: ${unfollowedStackMessage(stack)}
|
|
56
|
+
`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
for (const document of tree.documents) {
|
|
60
|
+
const templateDir = path.dirname(document.path);
|
|
61
|
+
const fieldsByFunction = groupFieldsByFunction(
|
|
62
|
+
readAppSyncResolvers(document.template)
|
|
63
|
+
);
|
|
64
|
+
for (const fn of readServerlessFunctions(document.template)) {
|
|
65
|
+
const resolvedModule = path.resolve(
|
|
66
|
+
templateDir,
|
|
67
|
+
fn.codeUri,
|
|
68
|
+
fn.modulePath
|
|
69
|
+
);
|
|
70
|
+
const list = index.get(resolvedModule) ?? [];
|
|
71
|
+
list.push({
|
|
72
|
+
functionLogicalId: qualifiedLogicalId(
|
|
73
|
+
document.stackPath,
|
|
74
|
+
fn.logicalId
|
|
75
|
+
),
|
|
76
|
+
graphqlFields: fieldsByFunction.get(fn.logicalId) ?? [],
|
|
77
|
+
handler: fn.handler,
|
|
78
|
+
exportName: fn.exportName,
|
|
79
|
+
httpRoutes: fn.httpRoutes,
|
|
80
|
+
nonHttpEvents: fn.nonHttpEvents
|
|
81
|
+
});
|
|
82
|
+
index.set(resolvedModule, list);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} catch (err) {
|
|
86
|
+
process.stderr.write(
|
|
87
|
+
`[suss] aws-lambda: failed to read ${templatePath}: ${err instanceof Error ? err.message : String(err)}
|
|
88
|
+
`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
templateToIndex.set(templatePath, index);
|
|
92
|
+
return index;
|
|
93
|
+
}
|
|
94
|
+
function toModulePath(filePath) {
|
|
95
|
+
const resolved = path.resolve(filePath);
|
|
96
|
+
const ext = path.extname(resolved);
|
|
97
|
+
return ext === "" ? resolved : resolved.slice(0, -ext.length);
|
|
98
|
+
}
|
|
99
|
+
function groupFieldsByFunction(bindings) {
|
|
100
|
+
const out = /* @__PURE__ */ new Map();
|
|
101
|
+
for (const binding of bindings) {
|
|
102
|
+
for (const logicalId of binding.lambdaFunctionLogicalIds) {
|
|
103
|
+
const fields = out.get(logicalId) ?? [];
|
|
104
|
+
fields.push({ typeName: binding.typeName, fieldName: binding.fieldName });
|
|
105
|
+
out.set(logicalId, fields);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
function handlersForFile(filePath) {
|
|
111
|
+
const templatePath = findTemplate(path.dirname(path.resolve(filePath)));
|
|
112
|
+
if (templatePath === null) {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
const index = indexForTemplate(templatePath);
|
|
116
|
+
return index.get(toModulePath(filePath)) ?? [];
|
|
117
|
+
}
|
|
118
|
+
var HTTP_TERMINALS = [
|
|
119
|
+
{
|
|
120
|
+
// `return { statusCode, body, headers? }`, written at the return site or
|
|
121
|
+
// built by a helper the adapter follows into. `body` contains the
|
|
122
|
+
// serialized payload, so unwrap `JSON.stringify(x)` to the shape of `x`.
|
|
123
|
+
kind: "response",
|
|
124
|
+
match: { type: "returnShape", requiredProperties: ["statusCode"] },
|
|
125
|
+
extraction: {
|
|
126
|
+
statusCode: { from: "property", name: "statusCode" },
|
|
127
|
+
body: { from: "property", name: "body", unwrapJsonStringify: true }
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
// `return { batchItemFailures }`, which is how a consumer tells Lambda
|
|
132
|
+
// which records to retry and which to drop. Lambda defines this shape, so
|
|
133
|
+
// the pack can match on it, the same way it matches the envelope above.
|
|
134
|
+
kind: "return",
|
|
135
|
+
match: {
|
|
136
|
+
type: "returnShape",
|
|
137
|
+
requiredProperties: ["batchItemFailures"]
|
|
138
|
+
},
|
|
139
|
+
extraction: {}
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
// `throw new SomeError(...)`. An uncaught throw becomes a
|
|
143
|
+
// throw-output transition; API Gateway maps it to a 5xx, but the
|
|
144
|
+
// specific status is the platform's, not the handler's.
|
|
145
|
+
kind: "throw",
|
|
146
|
+
match: { type: "throwExpression" },
|
|
147
|
+
extraction: {}
|
|
148
|
+
}
|
|
149
|
+
];
|
|
150
|
+
var NON_HTTP_TERMINALS = [
|
|
151
|
+
...HTTP_TERMINALS,
|
|
152
|
+
{
|
|
153
|
+
kind: "return",
|
|
154
|
+
match: { type: "returnShape" },
|
|
155
|
+
extraction: {}
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
// A queue consumer acknowledges a batch by not throwing: it processes the
|
|
159
|
+
// records, falls off the end, and Lambda reads the absence of an error as
|
|
160
|
+
// the ack. Without this the handler has no terminal, so it gets no
|
|
161
|
+
// transition, and everything it does goes with it.
|
|
162
|
+
kind: "return",
|
|
163
|
+
match: { type: "functionFallthrough" },
|
|
164
|
+
extraction: {}
|
|
165
|
+
}
|
|
166
|
+
];
|
|
167
|
+
var METADATA_NAMESPACE = "awsLambda";
|
|
168
|
+
function deployableUnit(entry) {
|
|
169
|
+
return { deploymentTarget: "lambda", instanceName: entry.functionLogicalId };
|
|
170
|
+
}
|
|
171
|
+
function httpRouteUnits(entry, func) {
|
|
172
|
+
const units = [];
|
|
173
|
+
for (const route of entry.httpRoutes) {
|
|
174
|
+
if (route.method === "ANY") {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
units.push({
|
|
178
|
+
func,
|
|
179
|
+
kind: "handler",
|
|
180
|
+
name: `${entry.functionLogicalId}.${entry.exportName}`,
|
|
181
|
+
routeInfo: { method: route.method, path: route.path },
|
|
182
|
+
deployableUnit: deployableUnit(entry),
|
|
183
|
+
metadata: {
|
|
184
|
+
[METADATA_NAMESPACE]: {
|
|
185
|
+
handler: entry.handler,
|
|
186
|
+
eventId: route.eventId,
|
|
187
|
+
apiEventType: route.eventType
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return units;
|
|
193
|
+
}
|
|
194
|
+
var OPERATION_ROOT_TYPES = ["Query", "Mutation", "Subscription"];
|
|
195
|
+
function operationFields(entry) {
|
|
196
|
+
return entry.graphqlFields.filter(
|
|
197
|
+
(f) => OPERATION_ROOT_TYPES.includes(f.typeName)
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
function typeFields(entry) {
|
|
201
|
+
return entry.graphqlFields.filter(
|
|
202
|
+
(f) => !OPERATION_ROOT_TYPES.includes(f.typeName)
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
function graphqlResolverUnits(entry, func) {
|
|
206
|
+
return operationFields(entry).map((field) => ({
|
|
207
|
+
func,
|
|
208
|
+
kind: "handler",
|
|
209
|
+
name: `${entry.functionLogicalId}.${entry.exportName}`,
|
|
210
|
+
resolverInfo: { typeName: field.typeName, fieldName: field.fieldName },
|
|
211
|
+
deployableUnit: deployableUnit(entry),
|
|
212
|
+
metadata: {
|
|
213
|
+
[METADATA_NAMESPACE]: {
|
|
214
|
+
handler: entry.handler,
|
|
215
|
+
recognition: "appsync-resolver"
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}));
|
|
219
|
+
}
|
|
220
|
+
function accountingUnit(entry, func, channel) {
|
|
221
|
+
const eventTypes = accountedEventTypes(entry);
|
|
222
|
+
const backs = typeFields(entry);
|
|
223
|
+
const wire = channel !== null ? "aws_sqs" : messageBusWire(eventTypes);
|
|
224
|
+
return {
|
|
225
|
+
func,
|
|
226
|
+
kind: "handler",
|
|
227
|
+
name: `${entry.functionLogicalId}.${entry.exportName}`,
|
|
228
|
+
terminals: NON_HTTP_TERMINALS,
|
|
229
|
+
...wire !== null ? { channelInfo: { messageBus: wire, channel } } : {},
|
|
230
|
+
deployableUnit: deployableUnit(entry),
|
|
231
|
+
metadata: {
|
|
232
|
+
[METADATA_NAMESPACE]: {
|
|
233
|
+
handler: entry.handler,
|
|
234
|
+
recognition: "recognized-not-http",
|
|
235
|
+
eventTypes,
|
|
236
|
+
...backs.length > 0 ? { graphqlTypeFields: backs } : {}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function subjectChannel(entry, sf, tsCtx, factories) {
|
|
242
|
+
const consumesSqs = entry.nonHttpEvents.some((e) => e.eventType === "SQS");
|
|
243
|
+
if (!consumesSqs) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
for (const factory of factories) {
|
|
247
|
+
const subject = tsCtx.exportedCallConfigString(
|
|
248
|
+
sf,
|
|
249
|
+
entry.exportName,
|
|
250
|
+
factory
|
|
251
|
+
);
|
|
252
|
+
if (subject !== null) {
|
|
253
|
+
return subject;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
var EVENT_WIRES = {
|
|
259
|
+
SQS: "aws_sqs",
|
|
260
|
+
SNS: "aws.sns",
|
|
261
|
+
S3: "s3",
|
|
262
|
+
Schedule: "eventbridge",
|
|
263
|
+
ScheduleV2: "eventbridge",
|
|
264
|
+
EventBridgeRule: "eventbridge",
|
|
265
|
+
CloudWatchEvent: "eventbridge"
|
|
266
|
+
};
|
|
267
|
+
function messageBusWire(eventTypes) {
|
|
268
|
+
const wires = new Set(
|
|
269
|
+
eventTypes.map((type) => EVENT_WIRES[type] ?? null)
|
|
270
|
+
);
|
|
271
|
+
if (wires.size !== 1) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
const [wire] = wires;
|
|
275
|
+
return wire ?? null;
|
|
276
|
+
}
|
|
277
|
+
function accountedEventTypes(entry) {
|
|
278
|
+
return [
|
|
279
|
+
...entry.nonHttpEvents.map((e) => e.eventType),
|
|
280
|
+
...entry.httpRoutes.filter((r) => r.method === "ANY").map((r) => r.eventType)
|
|
281
|
+
];
|
|
282
|
+
}
|
|
283
|
+
function hasBindableRoute(entry) {
|
|
284
|
+
return entry.httpRoutes.some((r) => r.method !== "ANY");
|
|
285
|
+
}
|
|
286
|
+
function awsLambdaDiscovery(subjectFactories = []) {
|
|
287
|
+
return (sourceFile, ctx) => {
|
|
288
|
+
const sf = sourceFile;
|
|
289
|
+
const tsCtx = ctx;
|
|
290
|
+
const filePath = tsCtx.getFilePath(sf);
|
|
291
|
+
const entries = handlersForFile(filePath);
|
|
292
|
+
if (entries.length === 0) {
|
|
293
|
+
return [];
|
|
294
|
+
}
|
|
295
|
+
const exported = /* @__PURE__ */ new Map();
|
|
296
|
+
for (const { name, func } of tsCtx.exportedFunctions(sf)) {
|
|
297
|
+
exported.set(name, func);
|
|
298
|
+
}
|
|
299
|
+
const units = [];
|
|
300
|
+
for (const entry of entries) {
|
|
301
|
+
const func = exported.get(entry.exportName);
|
|
302
|
+
if (func === void 0) {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
units.push(...httpRouteUnits(entry, func));
|
|
306
|
+
units.push(...graphqlResolverUnits(entry, func));
|
|
307
|
+
const unaccountedEvents = accountedEventTypes(entry);
|
|
308
|
+
const boundToNothing = !hasBindableRoute(entry) && operationFields(entry).length === 0;
|
|
309
|
+
if (unaccountedEvents.length > 0 || boundToNothing) {
|
|
310
|
+
units.push(
|
|
311
|
+
accountingUnit(
|
|
312
|
+
entry,
|
|
313
|
+
func,
|
|
314
|
+
subjectChannel(entry, sf, tsCtx, subjectFactories)
|
|
315
|
+
)
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return units;
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function awsLambdaFramework(options = {}) {
|
|
323
|
+
return {
|
|
324
|
+
name: "aws-lambda",
|
|
325
|
+
protocol: "http",
|
|
326
|
+
// Sentry's wrapper is a library call whose body the adapter cannot
|
|
327
|
+
// read, so the pack states the judgment: the handler is argument 0.
|
|
328
|
+
// Project-local wrappers need no declaration; the adapter derives
|
|
329
|
+
// those by reading the factory body.
|
|
330
|
+
transparentWrappers: [
|
|
331
|
+
{
|
|
332
|
+
callee: "Sentry.wrapHandler",
|
|
333
|
+
argument: 0,
|
|
334
|
+
module: "@sentry/aws-serverless"
|
|
335
|
+
}
|
|
336
|
+
],
|
|
337
|
+
languages: ["typescript", "javascript"],
|
|
338
|
+
// No data-driven discovery: routing lives in the SAM/CFN template,
|
|
339
|
+
// not in code. The callback resolves handlers against the template.
|
|
340
|
+
discovery: [],
|
|
341
|
+
discoverUnits: awsLambdaDiscovery(options.subjectFactories ?? []),
|
|
342
|
+
// No import gate, on purpose.
|
|
343
|
+
//
|
|
344
|
+
// A TypeScript handler writes `import type { APIGatewayProxyHandlerV2 }
|
|
345
|
+
// from "aws-lambda"`, which TypeScript resolves to
|
|
346
|
+
// `@types/aws-lambda`, to annotate its export. A JavaScript handler
|
|
347
|
+
// has nothing to annotate and writes no such import. Gating
|
|
348
|
+
// discovery on it therefore meant "TypeScript handlers that bothered
|
|
349
|
+
// to annotate", and every JavaScript Lambda service extracted
|
|
350
|
+
// nothing.
|
|
351
|
+
//
|
|
352
|
+
// The SAM template is the gate instead, and a better one, because it
|
|
353
|
+
// says which handlers outright. `discoverUnits` looks each file up in
|
|
354
|
+
// the template reachable from it, and a directory with no template
|
|
355
|
+
// resolves to null once and stays memoized.
|
|
356
|
+
// Route units extract against these. Non-HTTP accounting units get their
|
|
357
|
+
// own wider list, attached per unit by the discovery callback.
|
|
358
|
+
terminals: HTTP_TERMINALS,
|
|
359
|
+
// Lambda's handler signature is `(event, context, callback?)`. The
|
|
360
|
+
// HTTP request data (path params, query, body) lives on `event`;
|
|
361
|
+
// decomposing it into typed inputs is a follow-up.
|
|
362
|
+
inputMapping: {
|
|
363
|
+
type: "positionalParams",
|
|
364
|
+
params: [
|
|
365
|
+
{ position: 0, role: "event" },
|
|
366
|
+
{ position: 1, role: "context" }
|
|
367
|
+
]
|
|
368
|
+
},
|
|
369
|
+
// The powertools read their configuration from inside node_modules,
|
|
370
|
+
// so a template that declares these has a reader no walk ever sees.
|
|
371
|
+
libraryEnvVars: [
|
|
372
|
+
{
|
|
373
|
+
module: "@aws-lambda-powertools/",
|
|
374
|
+
prefixes: ["POWERTOOLS_"]
|
|
375
|
+
}
|
|
376
|
+
]
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
var index_default = awsLambdaFramework;
|
|
380
|
+
export {
|
|
381
|
+
index_default as default
|
|
382
|
+
};
|
package/dist/aws-s3.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@suss/framework-aws-s3';
|
package/dist/aws-s3.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// ../framework/aws-s3/dist/index.js
|
|
2
|
+
import { Node as N } from "ts-morph";
|
|
3
|
+
import { readName, rootIdentifier } from "@suss/adapter-typescript";
|
|
4
|
+
import { storageBinding } from "@suss/behavioral-ir";
|
|
5
|
+
var RECOGNITION = "@suss/framework-aws-s3";
|
|
6
|
+
var COMMAND_MODULE = "@aws-sdk/client-s3";
|
|
7
|
+
var COMMANDS = {
|
|
8
|
+
GetObjectCommand: "read",
|
|
9
|
+
HeadObjectCommand: "read",
|
|
10
|
+
ListObjectsV2Command: "read",
|
|
11
|
+
ListObjectsCommand: "read",
|
|
12
|
+
PutObjectCommand: "write",
|
|
13
|
+
DeleteObjectCommand: "write",
|
|
14
|
+
DeleteObjectsCommand: "write",
|
|
15
|
+
CopyObjectCommand: "write",
|
|
16
|
+
// A large object goes up in parts, and each command in that sequence
|
|
17
|
+
// writes the same object.
|
|
18
|
+
CreateMultipartUploadCommand: "write",
|
|
19
|
+
UploadPartCommand: "write",
|
|
20
|
+
CompleteMultipartUploadCommand: "write",
|
|
21
|
+
AbortMultipartUploadCommand: "write"
|
|
22
|
+
};
|
|
23
|
+
function s3Recognizer(call, ctx) {
|
|
24
|
+
const callNode = call;
|
|
25
|
+
const recognizerCtx = ctx;
|
|
26
|
+
const resolve = recognizerCtx.resolveWrittenValue ?? (() => null);
|
|
27
|
+
const built = commandArgument(callNode, resolve);
|
|
28
|
+
if (built === null) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const ctorExpr = built.getExpression();
|
|
32
|
+
const commandName = N.isPropertyAccessExpression(ctorExpr) ? ctorExpr.getName() : ctorExpr.getText();
|
|
33
|
+
const kind = COMMANDS[commandName];
|
|
34
|
+
if (kind === void 0 || !fromS3Module(ctorExpr, recognizerCtx)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const input = built.getArguments()[0];
|
|
38
|
+
if (input === void 0 || !N.isObjectLiteralExpression(input)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const key = nameOfProperty(input, "Key", resolve);
|
|
42
|
+
const prefix = nameOfProperty(input, "Prefix", resolve);
|
|
43
|
+
const addressed = key ?? prefix;
|
|
44
|
+
return [
|
|
45
|
+
{
|
|
46
|
+
type: "interaction",
|
|
47
|
+
binding: storageBinding({
|
|
48
|
+
recognition: RECOGNITION,
|
|
49
|
+
storageSystem: "s3",
|
|
50
|
+
transport: "aws-sdk",
|
|
51
|
+
scope: "default",
|
|
52
|
+
container: nameOfProperty(input, "Bucket", resolve),
|
|
53
|
+
accessPath: null
|
|
54
|
+
}),
|
|
55
|
+
callee: callNode.getExpression().getText(),
|
|
56
|
+
interaction: {
|
|
57
|
+
class: "storage-access",
|
|
58
|
+
kind,
|
|
59
|
+
// An object has no fields, so a call says nothing about any.
|
|
60
|
+
fields: [],
|
|
61
|
+
operation: commandName,
|
|
62
|
+
...addressed !== null ? { selector: [addressed] } : {}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
function commandArgument(call, resolve) {
|
|
68
|
+
for (const arg of call.getArguments()) {
|
|
69
|
+
if (N.isNewExpression(arg)) {
|
|
70
|
+
return arg;
|
|
71
|
+
}
|
|
72
|
+
if (!N.isIdentifier(arg) || !typedAsCommand(arg)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const written = resolve(arg);
|
|
76
|
+
if (written !== null && N.isNewExpression(written)) {
|
|
77
|
+
return written;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
function typedAsCommand(value) {
|
|
83
|
+
const declared = value.getType().getSymbol();
|
|
84
|
+
return declared !== void 0 && COMMANDS[declared.getName()] !== void 0;
|
|
85
|
+
}
|
|
86
|
+
function fromS3Module(ctorExpr, ctx) {
|
|
87
|
+
const target = N.isPropertyAccessExpression(ctorExpr) ? rootIdentifier(ctorExpr) : ctorExpr;
|
|
88
|
+
return target !== null && ctx.isImportedFrom(target, COMMAND_MODULE);
|
|
89
|
+
}
|
|
90
|
+
function nameOfProperty(input, name, resolve) {
|
|
91
|
+
const written = property(input, name);
|
|
92
|
+
return written === null ? null : readName(written, { resolve, unsettled: "reference" });
|
|
93
|
+
}
|
|
94
|
+
function property(input, name) {
|
|
95
|
+
if (!N.isObjectLiteralExpression(input)) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
for (const prop of input.getProperties()) {
|
|
99
|
+
if (N.isPropertyAssignment(prop) && prop.getName() === name) {
|
|
100
|
+
return prop.getInitializer() ?? null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
function s3Framework() {
|
|
106
|
+
return {
|
|
107
|
+
name: "aws-s3",
|
|
108
|
+
protocol: "s3",
|
|
109
|
+
languages: ["typescript", "javascript"],
|
|
110
|
+
discovery: [],
|
|
111
|
+
terminals: [],
|
|
112
|
+
inputMapping: { type: "positionalParams", params: [] },
|
|
113
|
+
requiresImport: [COMMAND_MODULE],
|
|
114
|
+
invocationRecognizers: [s3Recognizer]
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
var index_default = s3Framework;
|
|
118
|
+
export {
|
|
119
|
+
index_default as default
|
|
120
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@suss/framework-aws-sqs';
|