@suss/packs 0.35.1 → 0.36.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/dist/apollo-client.js +14 -3
- package/dist/aws-lambda-python.d.ts +1 -0
- package/dist/aws-lambda-python.js +10 -0
- package/dist/aws-lambda-ruby.d.ts +1 -0
- package/dist/aws-lambda-ruby.js +10 -0
- package/dist/aws-lambda.js +4 -427
- package/dist/chunk-63BQSSQT.js +544 -0
- package/dist/graphql-ruby.js +18 -8
- package/dist/node.js +78 -106
- package/dist/rails.js +31 -10
- package/package.json +19 -10
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
// ../framework/aws-lambda/dist/index.js
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { compile, declarationsIn } from "@suss/recognize";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import {
|
|
7
|
+
loadServerlessDocument,
|
|
8
|
+
SERVICE_FILE_NAMES,
|
|
9
|
+
translateService
|
|
10
|
+
} from "@suss/contract-serverless";
|
|
11
|
+
import {
|
|
12
|
+
loadTemplateTree,
|
|
13
|
+
qualifiedLogicalId,
|
|
14
|
+
readAppSyncResolvers,
|
|
15
|
+
readServerlessFunctions,
|
|
16
|
+
unfollowedStackMessage
|
|
17
|
+
} from "@suss/manifest-aws";
|
|
18
|
+
import { messageBusBinding, unitInvocationBinding } from "@suss/behavioral-ir";
|
|
19
|
+
import { constructedFrom, unitInvokes } from "@suss/recognize";
|
|
20
|
+
var TEMPLATE_NAMES = [
|
|
21
|
+
"template.yaml",
|
|
22
|
+
"template.yml",
|
|
23
|
+
"template.json",
|
|
24
|
+
...SERVICE_FILE_NAMES
|
|
25
|
+
];
|
|
26
|
+
var isServiceFile = (file) => SERVICE_FILE_NAMES.includes(path.basename(file));
|
|
27
|
+
function manifestTree(manifestPath) {
|
|
28
|
+
if (!isServiceFile(manifestPath)) {
|
|
29
|
+
return loadTemplateTree(manifestPath);
|
|
30
|
+
}
|
|
31
|
+
const translated = translateService(loadServerlessDocument(manifestPath));
|
|
32
|
+
return {
|
|
33
|
+
documents: [
|
|
34
|
+
{
|
|
35
|
+
path: path.resolve(manifestPath),
|
|
36
|
+
stackPath: [],
|
|
37
|
+
template: translated.functions
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
unfollowed: []
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function modulePathsOf(resolvedModule) {
|
|
44
|
+
const directory = path.dirname(resolvedModule);
|
|
45
|
+
const base = path.basename(resolvedModule);
|
|
46
|
+
if (!base.includes(".")) {
|
|
47
|
+
return [resolvedModule];
|
|
48
|
+
}
|
|
49
|
+
return [resolvedModule, path.join(directory, ...base.split("."))];
|
|
50
|
+
}
|
|
51
|
+
var dirToTemplate = /* @__PURE__ */ new Map();
|
|
52
|
+
var templateToIndex = /* @__PURE__ */ new Map();
|
|
53
|
+
var templateToDocuments = /* @__PURE__ */ new Map();
|
|
54
|
+
function findTemplate(startDir) {
|
|
55
|
+
const chain = [];
|
|
56
|
+
let dir = startDir;
|
|
57
|
+
while (true) {
|
|
58
|
+
const cached = dirToTemplate.get(dir);
|
|
59
|
+
if (cached !== void 0) {
|
|
60
|
+
for (const d of chain) {
|
|
61
|
+
dirToTemplate.set(d, cached);
|
|
62
|
+
}
|
|
63
|
+
return cached;
|
|
64
|
+
}
|
|
65
|
+
chain.push(dir);
|
|
66
|
+
const found = TEMPLATE_NAMES.map((name) => path.join(dir, name)).find(
|
|
67
|
+
(candidate) => fs.existsSync(candidate)
|
|
68
|
+
) ?? null;
|
|
69
|
+
if (found !== null) {
|
|
70
|
+
for (const d of chain) {
|
|
71
|
+
dirToTemplate.set(d, found);
|
|
72
|
+
}
|
|
73
|
+
return found;
|
|
74
|
+
}
|
|
75
|
+
const parent = path.dirname(dir);
|
|
76
|
+
if (parent === dir) {
|
|
77
|
+
for (const d of chain) {
|
|
78
|
+
dirToTemplate.set(d, null);
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
dir = parent;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function indexForTemplate(templatePath) {
|
|
86
|
+
const cached = templateToIndex.get(templatePath);
|
|
87
|
+
if (cached !== void 0) {
|
|
88
|
+
return cached;
|
|
89
|
+
}
|
|
90
|
+
const index = /* @__PURE__ */ new Map();
|
|
91
|
+
const documents = /* @__PURE__ */ new Set([templatePath]);
|
|
92
|
+
try {
|
|
93
|
+
const tree = manifestTree(templatePath);
|
|
94
|
+
for (const document of tree.documents) {
|
|
95
|
+
documents.add(document.path);
|
|
96
|
+
}
|
|
97
|
+
for (const stack of tree.unfollowed) {
|
|
98
|
+
process.stderr.write(
|
|
99
|
+
`[suss] aws-lambda: ${unfollowedStackMessage(stack)}
|
|
100
|
+
`
|
|
101
|
+
);
|
|
102
|
+
if (stack.templatePath !== null) {
|
|
103
|
+
documents.add(stack.templatePath);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
for (const document of tree.documents) {
|
|
107
|
+
const templateDir = path.dirname(document.path);
|
|
108
|
+
const fieldsByFunction = groupFieldsByFunction(
|
|
109
|
+
readAppSyncResolvers(document.template)
|
|
110
|
+
);
|
|
111
|
+
for (const fn of readServerlessFunctions(document.template)) {
|
|
112
|
+
const resolvedModule = path.resolve(
|
|
113
|
+
templateDir,
|
|
114
|
+
fn.codeUri,
|
|
115
|
+
fn.modulePath
|
|
116
|
+
);
|
|
117
|
+
const entry = {
|
|
118
|
+
functionLogicalId: qualifiedLogicalId(
|
|
119
|
+
document.stackPath,
|
|
120
|
+
fn.logicalId
|
|
121
|
+
),
|
|
122
|
+
graphqlFields: fieldsByFunction.get(fn.logicalId) ?? [],
|
|
123
|
+
handler: fn.handler,
|
|
124
|
+
exportName: fn.exportName,
|
|
125
|
+
httpRoutes: fn.httpRoutes,
|
|
126
|
+
nonHttpEvents: fn.nonHttpEvents
|
|
127
|
+
};
|
|
128
|
+
for (const modulePath of modulePathsOf(resolvedModule)) {
|
|
129
|
+
index.set(modulePath, [...index.get(modulePath) ?? [], entry]);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} catch (err) {
|
|
134
|
+
process.stderr.write(
|
|
135
|
+
`[suss] aws-lambda: failed to read ${templatePath}: ${err instanceof Error ? err.message : String(err)}
|
|
136
|
+
`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
templateToIndex.set(templatePath, index);
|
|
140
|
+
templateToDocuments.set(templatePath, [...documents]);
|
|
141
|
+
return index;
|
|
142
|
+
}
|
|
143
|
+
function toModulePath(filePath) {
|
|
144
|
+
const resolved = path.resolve(filePath);
|
|
145
|
+
const ext = path.extname(resolved);
|
|
146
|
+
return ext === "" ? resolved : resolved.slice(0, -ext.length);
|
|
147
|
+
}
|
|
148
|
+
function groupFieldsByFunction(bindings) {
|
|
149
|
+
const out = /* @__PURE__ */ new Map();
|
|
150
|
+
for (const binding of bindings) {
|
|
151
|
+
for (const logicalId of binding.lambdaFunctionLogicalIds) {
|
|
152
|
+
const fields = out.get(logicalId) ?? [];
|
|
153
|
+
fields.push({ typeName: binding.typeName, fieldName: binding.fieldName });
|
|
154
|
+
out.set(logicalId, fields);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return out;
|
|
158
|
+
}
|
|
159
|
+
function handlersForFile(filePath) {
|
|
160
|
+
const templatePath = findTemplate(path.dirname(path.resolve(filePath)));
|
|
161
|
+
if (templatePath === null) {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
const index = indexForTemplate(templatePath);
|
|
165
|
+
return index.get(toModulePath(filePath)) ?? [];
|
|
166
|
+
}
|
|
167
|
+
function templatesForFiles(files) {
|
|
168
|
+
const roots = /* @__PURE__ */ new Set();
|
|
169
|
+
for (const file of files) {
|
|
170
|
+
const templatePath = findTemplate(path.dirname(path.resolve(file)));
|
|
171
|
+
if (templatePath !== null) {
|
|
172
|
+
roots.add(templatePath);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const documents = /* @__PURE__ */ new Set();
|
|
176
|
+
for (const root of roots) {
|
|
177
|
+
indexForTemplate(root);
|
|
178
|
+
for (const document of templateToDocuments.get(root) ?? [root]) {
|
|
179
|
+
documents.add(document);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return [...documents];
|
|
183
|
+
}
|
|
184
|
+
var HTTP_TERMINALS = [
|
|
185
|
+
{
|
|
186
|
+
// `return { statusCode, body, headers? }`, at the return or in a helper
|
|
187
|
+
// the adapter follows. `body` contains the serialized payload, so
|
|
188
|
+
// `JSON.stringify(x)` is unwrapped to the shape of `x`.
|
|
189
|
+
kind: "response",
|
|
190
|
+
match: { type: "returnShape", requiredProperties: ["statusCode"] },
|
|
191
|
+
extraction: {
|
|
192
|
+
statusCode: { from: "property", name: "statusCode" },
|
|
193
|
+
body: { from: "property", name: "body", unwrapJsonStringify: true }
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
// `return { batchItemFailures }` makes Lambda retry the records it
|
|
198
|
+
// lists. Lambda defines this shape, so the pack can match on it the
|
|
199
|
+
// same way it matches the envelope above.
|
|
200
|
+
kind: "return",
|
|
201
|
+
match: {
|
|
202
|
+
type: "returnShape",
|
|
203
|
+
requiredProperties: ["batchItemFailures"]
|
|
204
|
+
},
|
|
205
|
+
extraction: {}
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
// An uncaught throw becomes a throw-output transition. API Gateway
|
|
209
|
+
// turns it into a 5xx, and the platform picks which one.
|
|
210
|
+
kind: "throw",
|
|
211
|
+
match: { type: "throwExpression" },
|
|
212
|
+
extraction: {}
|
|
213
|
+
}
|
|
214
|
+
];
|
|
215
|
+
var NON_HTTP_TERMINALS = [
|
|
216
|
+
...HTTP_TERMINALS,
|
|
217
|
+
{
|
|
218
|
+
kind: "return",
|
|
219
|
+
match: { type: "returnShape" },
|
|
220
|
+
extraction: {}
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
// A queue consumer acknowledges a batch by returning without an error.
|
|
224
|
+
// Without this pattern the handler has no terminal, so it gets no
|
|
225
|
+
// transition and everything it does is lost with it.
|
|
226
|
+
kind: "return",
|
|
227
|
+
match: { type: "functionFallthrough" },
|
|
228
|
+
extraction: {}
|
|
229
|
+
}
|
|
230
|
+
];
|
|
231
|
+
var METADATA_NAMESPACE = "awsLambda";
|
|
232
|
+
function deployableUnit(entry) {
|
|
233
|
+
return { deploymentTarget: "lambda", instanceName: entry.functionLogicalId };
|
|
234
|
+
}
|
|
235
|
+
function httpRouteUnits(entry, func) {
|
|
236
|
+
const units = [];
|
|
237
|
+
for (const route of entry.httpRoutes) {
|
|
238
|
+
if (route.method === "ANY") {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
units.push({
|
|
242
|
+
func,
|
|
243
|
+
kind: "handler",
|
|
244
|
+
name: `${entry.functionLogicalId}.${entry.exportName}`,
|
|
245
|
+
routeInfo: { method: route.method, path: route.path },
|
|
246
|
+
deployableUnit: deployableUnit(entry),
|
|
247
|
+
metadata: {
|
|
248
|
+
[METADATA_NAMESPACE]: {
|
|
249
|
+
handler: entry.handler,
|
|
250
|
+
eventId: route.eventId,
|
|
251
|
+
apiEventType: route.eventType
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
return units;
|
|
257
|
+
}
|
|
258
|
+
var OPERATION_ROOT_TYPES = ["Query", "Mutation", "Subscription"];
|
|
259
|
+
function operationFields(entry) {
|
|
260
|
+
return entry.graphqlFields.filter(
|
|
261
|
+
(f) => OPERATION_ROOT_TYPES.includes(f.typeName)
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
function typeFields(entry) {
|
|
265
|
+
return entry.graphqlFields.filter(
|
|
266
|
+
(f) => !OPERATION_ROOT_TYPES.includes(f.typeName)
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
function graphqlResolverUnits(entry, func) {
|
|
270
|
+
return operationFields(entry).map((field) => ({
|
|
271
|
+
func,
|
|
272
|
+
kind: "handler",
|
|
273
|
+
name: `${entry.functionLogicalId}.${entry.exportName}`,
|
|
274
|
+
resolverInfo: { typeName: field.typeName, fieldName: field.fieldName },
|
|
275
|
+
deployableUnit: deployableUnit(entry),
|
|
276
|
+
metadata: {
|
|
277
|
+
[METADATA_NAMESPACE]: {
|
|
278
|
+
handler: entry.handler,
|
|
279
|
+
recognition: "appsync-resolver"
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}));
|
|
283
|
+
}
|
|
284
|
+
function accountingUnit(entry, func) {
|
|
285
|
+
const eventTypes = accountedEventTypes(entry);
|
|
286
|
+
const backs = typeFields(entry);
|
|
287
|
+
const wire = messageBusWire(eventTypes);
|
|
288
|
+
const unit = deployableUnit(entry);
|
|
289
|
+
return {
|
|
290
|
+
func,
|
|
291
|
+
kind: "handler",
|
|
292
|
+
name: `${entry.functionLogicalId}.${entry.exportName}`,
|
|
293
|
+
terminals: NON_HTTP_TERMINALS,
|
|
294
|
+
...wire !== null ? { channelInfo: { messageBus: wire, channel: null } } : { invocationInfo: unit },
|
|
295
|
+
deployableUnit: unit,
|
|
296
|
+
metadata: {
|
|
297
|
+
[METADATA_NAMESPACE]: {
|
|
298
|
+
handler: entry.handler,
|
|
299
|
+
recognition: "recognized-not-http",
|
|
300
|
+
eventTypes,
|
|
301
|
+
...backs.length > 0 ? { graphqlTypeFields: backs } : {}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
var EVENT_WIRES = {
|
|
307
|
+
SQS: "aws_sqs",
|
|
308
|
+
SNS: "aws.sns",
|
|
309
|
+
S3: "s3",
|
|
310
|
+
Schedule: "eventbridge",
|
|
311
|
+
ScheduleV2: "eventbridge",
|
|
312
|
+
EventBridgeRule: "eventbridge",
|
|
313
|
+
CloudWatchEvent: "eventbridge"
|
|
314
|
+
};
|
|
315
|
+
function messageBusWire(eventTypes) {
|
|
316
|
+
const wires = new Set(
|
|
317
|
+
eventTypes.map((type) => EVENT_WIRES[type] ?? null)
|
|
318
|
+
);
|
|
319
|
+
if (wires.size !== 1) {
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
const [wire] = wires;
|
|
323
|
+
return wire ?? null;
|
|
324
|
+
}
|
|
325
|
+
function accountedEventTypes(entry) {
|
|
326
|
+
return [
|
|
327
|
+
...entry.nonHttpEvents.map((e) => e.eventType),
|
|
328
|
+
...entry.httpRoutes.filter((r) => r.method === "ANY").map((r) => r.eventType)
|
|
329
|
+
];
|
|
330
|
+
}
|
|
331
|
+
function hasBindableRoute(entry) {
|
|
332
|
+
return entry.httpRoutes.some((r) => r.method !== "ANY");
|
|
333
|
+
}
|
|
334
|
+
function awsLambdaDiscovery() {
|
|
335
|
+
return (sourceFile, ctx) => {
|
|
336
|
+
const sf = sourceFile;
|
|
337
|
+
const tsCtx = ctx;
|
|
338
|
+
const filePath = tsCtx.getFilePath(sf);
|
|
339
|
+
const entries = handlersForFile(filePath);
|
|
340
|
+
if (entries.length === 0) {
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
const exported = /* @__PURE__ */ new Map();
|
|
344
|
+
for (const { name, func } of tsCtx.exportedFunctions(sf)) {
|
|
345
|
+
exported.set(name, func);
|
|
346
|
+
}
|
|
347
|
+
const units = [];
|
|
348
|
+
for (const entry of entries) {
|
|
349
|
+
const func = exported.get(entry.exportName);
|
|
350
|
+
if (func === void 0) {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
units.push(...httpRouteUnits(entry, func));
|
|
354
|
+
units.push(...graphqlResolverUnits(entry, func));
|
|
355
|
+
const unaccountedEvents = accountedEventTypes(entry);
|
|
356
|
+
const boundToNothing = !hasBindableRoute(entry) && operationFields(entry).length === 0;
|
|
357
|
+
if (unaccountedEvents.length > 0 || boundToNothing) {
|
|
358
|
+
units.push(accountingUnit(entry, func));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return units;
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
function lambdaEntryPoints(recognition) {
|
|
365
|
+
return (filePath) => handlersForFile(filePath).map((entry) => ({
|
|
366
|
+
name: entry.exportName,
|
|
367
|
+
units: [handlerUnit(entry, recognition)]
|
|
368
|
+
}));
|
|
369
|
+
}
|
|
370
|
+
function handlerUnit(entry, recognition) {
|
|
371
|
+
const eventTypes = [
|
|
372
|
+
...entry.httpRoutes.map((route) => route.eventType),
|
|
373
|
+
...entry.nonHttpEvents.map((event) => event.eventType)
|
|
374
|
+
];
|
|
375
|
+
return {
|
|
376
|
+
kind: "handler",
|
|
377
|
+
name: `${entry.functionLogicalId}.${entry.exportName}`,
|
|
378
|
+
binding: handlerBinding(entry, eventTypes, recognition),
|
|
379
|
+
deployableUnit: deployableUnit(entry),
|
|
380
|
+
metadata: {
|
|
381
|
+
[METADATA_NAMESPACE]: {
|
|
382
|
+
handler: entry.handler,
|
|
383
|
+
recognition: "handler-entry",
|
|
384
|
+
eventTypes,
|
|
385
|
+
...entry.graphqlFields.length > 0 ? { graphqlTypeFields: entry.graphqlFields } : {}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
function handlerBinding(entry, eventTypes, recognition) {
|
|
391
|
+
const wire = entry.graphqlFields.length > 0 ? null : messageBusWire(eventTypes);
|
|
392
|
+
if (wire !== null) {
|
|
393
|
+
return messageBusBinding({ recognition, messageBus: wire, channel: null });
|
|
394
|
+
}
|
|
395
|
+
return unitInvocationBinding({ recognition, ...deployableUnit(entry) });
|
|
396
|
+
}
|
|
397
|
+
var LAMBDA = "@aws-sdk/client-lambda";
|
|
398
|
+
var INSIDE_THE_COMMAND = (named) => ({
|
|
399
|
+
send: {
|
|
400
|
+
input: {
|
|
401
|
+
at: 0,
|
|
402
|
+
of: [
|
|
403
|
+
{
|
|
404
|
+
to: "argument",
|
|
405
|
+
at: 0,
|
|
406
|
+
origin: constructedFrom({ from: [LAMBDA], named })
|
|
407
|
+
}
|
|
408
|
+
]
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
function invokeDeclarations() {
|
|
413
|
+
return [
|
|
414
|
+
unitInvokes({
|
|
415
|
+
platform: "lambda",
|
|
416
|
+
client: constructedFrom(LAMBDA),
|
|
417
|
+
named: ["FunctionName"],
|
|
418
|
+
payload: "Payload"
|
|
419
|
+
}).methods(INSIDE_THE_COMMAND(["InvokeCommand"])).example(
|
|
420
|
+
'client.send(new InvokeCommand({ FunctionName: "Worker", Payload: "{}" }))'
|
|
421
|
+
),
|
|
422
|
+
unitInvokes({
|
|
423
|
+
platform: "lambda",
|
|
424
|
+
client: constructedFrom(LAMBDA),
|
|
425
|
+
named: ["FunctionName"],
|
|
426
|
+
payload: "InvokeArgs"
|
|
427
|
+
}).methods(INSIDE_THE_COMMAND(["InvokeAsyncCommand"])).example(
|
|
428
|
+
'client.send(new InvokeAsyncCommand({ FunctionName: "Worker", InvokeArgs: "{}" }))'
|
|
429
|
+
)
|
|
430
|
+
];
|
|
431
|
+
}
|
|
432
|
+
var optionsSchema = z.object({}).strict();
|
|
433
|
+
function awsLambdaFramework(_options = {}) {
|
|
434
|
+
const invokes = invokeDeclarations();
|
|
435
|
+
return {
|
|
436
|
+
name: "aws-lambda",
|
|
437
|
+
protocol: "http",
|
|
438
|
+
// The adapter cannot read Sentry's wrapper inside the library, so the
|
|
439
|
+
// pack declares that the handler is argument 0. A wrapper written in
|
|
440
|
+
// the project needs no entry, because the adapter reads its body.
|
|
441
|
+
transparentWrappers: [
|
|
442
|
+
{ module: "@sentry/aws-serverless", name: "wrapHandler", argument: 0 }
|
|
443
|
+
],
|
|
444
|
+
languages: ["typescript", "javascript"],
|
|
445
|
+
// The routing is in the template, so the callback finds handlers
|
|
446
|
+
// there and the pattern list stays empty.
|
|
447
|
+
discovery: [],
|
|
448
|
+
discoverUnits: awsLambdaDiscovery(),
|
|
449
|
+
// Discovery reads the templates, so they go into the cache key and an
|
|
450
|
+
// edited template makes the next run read the project again.
|
|
451
|
+
discoveryInputs: templatesForFiles,
|
|
452
|
+
// No import gate: a JavaScript handler imports nothing from
|
|
453
|
+
// `aws-lambda`, so the template acts as the gate. See the README.
|
|
454
|
+
// A unit that is not HTTP gets a longer list from the discovery
|
|
455
|
+
// callback in place of this one.
|
|
456
|
+
terminals: HTTP_TERMINALS,
|
|
457
|
+
inputMapping: {
|
|
458
|
+
type: "positionalParams",
|
|
459
|
+
params: [
|
|
460
|
+
{ position: 0, role: "event" },
|
|
461
|
+
{ position: 1, role: "context" }
|
|
462
|
+
]
|
|
463
|
+
},
|
|
464
|
+
// A proxy integration passes the handler API Gateway's event, with the
|
|
465
|
+
// body as a string the handler parses. A read of the body records that
|
|
466
|
+
// the handler took all of it, with no field name.
|
|
467
|
+
requestSpelling: {
|
|
468
|
+
headers: { path: ["event", "headers"], saysWhichField: true },
|
|
469
|
+
query: {
|
|
470
|
+
path: ["event", "queryStringParameters"],
|
|
471
|
+
saysWhichField: true
|
|
472
|
+
},
|
|
473
|
+
params: { path: ["event", "pathParameters"], saysWhichField: true },
|
|
474
|
+
body: { path: ["event", "body"], saysWhichField: false }
|
|
475
|
+
},
|
|
476
|
+
// Powertools reads these variables from inside node_modules, where no
|
|
477
|
+
// walk over the project goes, so without this entry they look unread.
|
|
478
|
+
libraryEnvVars: [
|
|
479
|
+
{
|
|
480
|
+
module: "@aws-lambda-powertools/",
|
|
481
|
+
prefixes: ["POWERTOOLS_"]
|
|
482
|
+
}
|
|
483
|
+
],
|
|
484
|
+
// A pack-level `requiresImport` would gate `discoverUnits` too. Each
|
|
485
|
+
// declaration already checks which SDK its command class came from.
|
|
486
|
+
invocationRecognizers: invokes.map(
|
|
487
|
+
(match) => compile(match.declared, "@suss/framework-aws-lambda")
|
|
488
|
+
),
|
|
489
|
+
declarations: declarationsIn(invokes)
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
var declares = {
|
|
493
|
+
kind: "framework",
|
|
494
|
+
package: "@suss/framework-aws-lambda",
|
|
495
|
+
dependencies: [{ ecosystem: "npm", name: "@types/aws-lambda" }],
|
|
496
|
+
reads: "AWS Lambda HTTP handlers, paired with the routes a SAM template or serverless.yml declares."
|
|
497
|
+
};
|
|
498
|
+
function awsLambdaPython() {
|
|
499
|
+
const name = "aws-lambda-python";
|
|
500
|
+
return {
|
|
501
|
+
name,
|
|
502
|
+
protocol: "http",
|
|
503
|
+
discovery: [],
|
|
504
|
+
discoveryInputs: templatesForFiles,
|
|
505
|
+
entryPoints: lambdaEntryPoints(name),
|
|
506
|
+
// Powertools reads these from its own installed package.
|
|
507
|
+
libraryEnvVars: [
|
|
508
|
+
{ module: "aws_lambda_powertools", prefixes: ["POWERTOOLS_"] }
|
|
509
|
+
]
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
var declaresPython = {
|
|
513
|
+
kind: "framework",
|
|
514
|
+
package: "@suss/framework-aws-lambda",
|
|
515
|
+
dependencies: [],
|
|
516
|
+
reads: "AWS Lambda handlers written in Python, found from the Handler a SAM template or serverless.yml gives each function."
|
|
517
|
+
};
|
|
518
|
+
function awsLambdaRuby() {
|
|
519
|
+
const name = "aws-lambda-ruby";
|
|
520
|
+
return {
|
|
521
|
+
name,
|
|
522
|
+
protocol: "http",
|
|
523
|
+
discovery: [],
|
|
524
|
+
discoveryInputs: templatesForFiles,
|
|
525
|
+
entryPoints: lambdaEntryPoints(name)
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
var declaresRuby = {
|
|
529
|
+
kind: "framework",
|
|
530
|
+
package: "@suss/framework-aws-lambda",
|
|
531
|
+
dependencies: [],
|
|
532
|
+
reads: "AWS Lambda handlers written in Ruby, found from the Handler a SAM template or serverless.yml gives each function, including a class method written `file.Module::Class.method`."
|
|
533
|
+
};
|
|
534
|
+
var index_default = awsLambdaFramework;
|
|
535
|
+
|
|
536
|
+
export {
|
|
537
|
+
optionsSchema,
|
|
538
|
+
declares,
|
|
539
|
+
awsLambdaPython,
|
|
540
|
+
declaresPython,
|
|
541
|
+
awsLambdaRuby,
|
|
542
|
+
declaresRuby,
|
|
543
|
+
index_default
|
|
544
|
+
};
|
package/dist/graphql-ruby.js
CHANGED
|
@@ -4,11 +4,11 @@ import { z } from "zod";
|
|
|
4
4
|
import { SCALAR_SHAPES } from "@suss/contract-graphql";
|
|
5
5
|
var optionsSchema = z.object({
|
|
6
6
|
/**
|
|
7
|
-
* The directory the pack looks under for
|
|
8
|
-
* `resolver:` field references
|
|
9
|
-
* `path.join(repoRoot, "app/graphql")`. A relative path
|
|
10
|
-
* against `configDirectory`, or the working directory when
|
|
11
|
-
* unset.
|
|
7
|
+
* The directory the pack looks under for a class a `mutation:` or
|
|
8
|
+
* `resolver:` field references when no file in the run declares it,
|
|
9
|
+
* for example `path.join(repoRoot, "app/graphql")`. A relative path
|
|
10
|
+
* resolves against `configDirectory`, or the working directory when
|
|
11
|
+
* that is unset.
|
|
12
12
|
*/
|
|
13
13
|
root: z.string(),
|
|
14
14
|
/**
|
|
@@ -29,6 +29,7 @@ var optionsSchema = z.object({
|
|
|
29
29
|
var GRAPHQL_RUBY_ROOT_CLASS_NAMES = [
|
|
30
30
|
"GraphQL::Schema::Object",
|
|
31
31
|
"GraphQL::Schema::Mutation",
|
|
32
|
+
"GraphQL::Schema::RelayClassicMutation",
|
|
32
33
|
"GraphQL::Schema::Resolver",
|
|
33
34
|
"GraphQL::Schema::Interface",
|
|
34
35
|
"GraphQL::Schema::InputObject",
|
|
@@ -69,6 +70,14 @@ function graphqlRubyFramework(options) {
|
|
|
69
70
|
argumentCallName: "argument",
|
|
70
71
|
wiringKeywords: ["mutation", "resolver"],
|
|
71
72
|
resolverMethodName: "resolve",
|
|
73
|
+
resolverMethodKeyword: "resolver_method",
|
|
74
|
+
// A Mutation or Resolver runs ready?, then authorized?, then resolve.
|
|
75
|
+
// A hook that returns false, or [false, payload], stops the field,
|
|
76
|
+
// and the payload comes back in its place.
|
|
77
|
+
resolverHooks: {
|
|
78
|
+
methods: ["ready?", "authorized?"],
|
|
79
|
+
answerIsListHead: true
|
|
80
|
+
},
|
|
72
81
|
ancestryRootClassNames: [...GRAPHQL_RUBY_ROOT_CLASS_NAMES],
|
|
73
82
|
requiredKeyword: "required",
|
|
74
83
|
// graphql-ruby registers an argument as required unless the call
|
|
@@ -124,13 +133,14 @@ var declares = {
|
|
|
124
133
|
kind: "framework",
|
|
125
134
|
package: "@suss/framework-graphql-ruby",
|
|
126
135
|
dependencies: [{ ecosystem: "rubygems", name: "graphql" }],
|
|
127
|
-
reads: "graphql-ruby class-based field DSL (Ruby). The pack follows `mutation:` and `resolver:` wiring
|
|
136
|
+
reads: "graphql-ruby class-based field DSL (Ruby). The pack follows `mutation:` and `resolver:` wiring to the referenced class, wherever the run declares it, and reads its arguments, its payload, its `resolve` method and the `ready?` and `authorized?` hooks that run first. It follows `resolver_method:` to the type's own method. It also records the model a resolver reads through `dataloader`, when a storage pack in the run recognizes that model.",
|
|
128
137
|
configuration: {
|
|
129
138
|
file: "suss.graphql-ruby.json",
|
|
130
139
|
example: { root: "app/graphql" },
|
|
131
140
|
required: true,
|
|
132
|
-
why: "the directory a field's wired class is looked up under, read relative to this file. A Rails app generated by the library keeps its types and mutations under app/graphql.",
|
|
133
|
-
defaults: { root: "app/graphql" }
|
|
141
|
+
why: "the directory a field's wired class is looked up under when no file in the run declares it, read relative to this file. A Rails app generated by the library keeps its types and mutations under app/graphql.",
|
|
142
|
+
defaults: { root: "app/graphql" },
|
|
143
|
+
paths: ["root"]
|
|
134
144
|
}
|
|
135
145
|
};
|
|
136
146
|
var index_default = graphqlRubyFramework;
|