@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.
@@ -9,6 +9,7 @@ var DOCUMENT_HOOKS = [
9
9
  { hookName: "useMutation", operationType: "mutation" },
10
10
  { hookName: "useSubscription", operationType: "subscription" }
11
11
  ];
12
+ var CLIENT_OPTION = { argument: 1, property: "client" };
12
13
  var optionsSchema = z.object({
13
14
  /**
14
15
  * The provider workspace each client talks to, keyed by the endpoint
@@ -26,7 +27,13 @@ var optionsSchema = z.object({
26
27
  */
27
28
  operationScopes: z.array(
28
29
  z.object({ files: z.array(z.string()), workspace: z.string() }).strict()
29
- ).optional()
30
+ ).optional(),
31
+ /**
32
+ * What to do with an operation whose client neither `clients` nor
33
+ * `operationScopes` ties to a service, when the project builds
34
+ * clients for more than one endpoint. `unchecked` is the default.
35
+ */
36
+ unboundClients: z.enum(["unchecked", "pairWithAll"]).optional()
30
37
  }).strict();
31
38
  function apolloClientPack(options = {}) {
32
39
  return {
@@ -41,7 +48,8 @@ function apolloClientPack(options = {}) {
41
48
  match: {
42
49
  type: "graphqlHookCall",
43
50
  importModule: "@apollo/client",
44
- hooks: [...DOCUMENT_HOOKS]
51
+ hooks: [...DOCUMENT_HOOKS],
52
+ clientOption: CLIENT_OPTION
45
53
  },
46
54
  // requiresImport matches by prefix, so it also admits
47
55
  // `@apollo/client/react`. importModule above matches exactly.
@@ -53,7 +61,8 @@ function apolloClientPack(options = {}) {
53
61
  match: {
54
62
  type: "graphqlHookCall",
55
63
  importModule: "@apollo/client/react",
56
- hooks: [...DOCUMENT_HOOKS]
64
+ hooks: [...DOCUMENT_HOOKS],
65
+ clientOption: CLIENT_OPTION
57
66
  },
58
67
  requiresImport: ["@apollo/client"]
59
68
  },
@@ -94,6 +103,7 @@ function apolloClientPack(options = {}) {
94
103
  importModule: "@apollo/client",
95
104
  importName: "ApolloClient",
96
105
  uriProperty: "uri",
106
+ linkProperty: "link",
97
107
  // With a registry from `createFragmentRegistry` on the cache, a
98
108
  // document can spread a fragment it does not define, and the
99
109
  // client fills it in at run time.
@@ -119,6 +129,7 @@ function apolloClientPack(options = {}) {
119
129
  ],
120
130
  ...options.clients !== void 0 ? { graphqlClientBindings: options.clients } : {},
121
131
  ...options.operationScopes !== void 0 ? { graphqlOperationScopes: options.operationScopes } : {},
132
+ ...options.unboundClients !== void 0 ? { graphqlUnboundClients: options.unboundClients } : {},
122
133
  terminals: [
123
134
  {
124
135
  kind: "return",
@@ -0,0 +1 @@
1
+ export { declaresPython as declares, awsLambdaPython as default, optionsSchema } from '@suss/framework-aws-lambda';
@@ -0,0 +1,10 @@
1
+ import {
2
+ awsLambdaPython,
3
+ declaresPython,
4
+ optionsSchema
5
+ } from "./chunk-63BQSSQT.js";
6
+ export {
7
+ declaresPython as declares,
8
+ awsLambdaPython as default,
9
+ optionsSchema
10
+ };
@@ -0,0 +1 @@
1
+ export { declaresRuby as declares, awsLambdaRuby as default, optionsSchema } from '@suss/framework-aws-lambda';
@@ -0,0 +1,10 @@
1
+ import {
2
+ awsLambdaRuby,
3
+ declaresRuby,
4
+ optionsSchema
5
+ } from "./chunk-63BQSSQT.js";
6
+ export {
7
+ declaresRuby as declares,
8
+ awsLambdaRuby as default,
9
+ optionsSchema
10
+ };
@@ -1,431 +1,8 @@
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
1
  import {
7
- loadTemplateTree,
8
- qualifiedLogicalId,
9
- readAppSyncResolvers,
10
- readServerlessFunctions,
11
- unfollowedStackMessage
12
- } from "@suss/manifest-aws";
13
- import { constructedFrom, unitInvokes } from "@suss/recognize";
14
- var TEMPLATE_NAMES = ["template.yaml", "template.yml", "template.json"];
15
- var dirToTemplate = /* @__PURE__ */ new Map();
16
- var templateToIndex = /* @__PURE__ */ new Map();
17
- var templateToDocuments = /* @__PURE__ */ new Map();
18
- function findTemplate(startDir) {
19
- const chain = [];
20
- let dir = startDir;
21
- while (true) {
22
- const cached = dirToTemplate.get(dir);
23
- if (cached !== void 0) {
24
- for (const d of chain) {
25
- dirToTemplate.set(d, cached);
26
- }
27
- return cached;
28
- }
29
- chain.push(dir);
30
- const found = TEMPLATE_NAMES.map((name) => path.join(dir, name)).find(
31
- (candidate) => fs.existsSync(candidate)
32
- ) ?? null;
33
- if (found !== null) {
34
- for (const d of chain) {
35
- dirToTemplate.set(d, found);
36
- }
37
- return found;
38
- }
39
- const parent = path.dirname(dir);
40
- if (parent === dir) {
41
- for (const d of chain) {
42
- dirToTemplate.set(d, null);
43
- }
44
- return null;
45
- }
46
- dir = parent;
47
- }
48
- }
49
- function indexForTemplate(templatePath) {
50
- const cached = templateToIndex.get(templatePath);
51
- if (cached !== void 0) {
52
- return cached;
53
- }
54
- const index = /* @__PURE__ */ new Map();
55
- const documents = /* @__PURE__ */ new Set([templatePath]);
56
- try {
57
- const tree = loadTemplateTree(templatePath);
58
- for (const document of tree.documents) {
59
- documents.add(document.path);
60
- }
61
- for (const stack of tree.unfollowed) {
62
- process.stderr.write(
63
- `[suss] aws-lambda: ${unfollowedStackMessage(stack)}
64
- `
65
- );
66
- if (stack.templatePath !== null) {
67
- documents.add(stack.templatePath);
68
- }
69
- }
70
- for (const document of tree.documents) {
71
- const templateDir = path.dirname(document.path);
72
- const fieldsByFunction = groupFieldsByFunction(
73
- readAppSyncResolvers(document.template)
74
- );
75
- for (const fn of readServerlessFunctions(document.template)) {
76
- const resolvedModule = path.resolve(
77
- templateDir,
78
- fn.codeUri,
79
- fn.modulePath
80
- );
81
- const list = index.get(resolvedModule) ?? [];
82
- list.push({
83
- functionLogicalId: qualifiedLogicalId(
84
- document.stackPath,
85
- fn.logicalId
86
- ),
87
- graphqlFields: fieldsByFunction.get(fn.logicalId) ?? [],
88
- handler: fn.handler,
89
- exportName: fn.exportName,
90
- httpRoutes: fn.httpRoutes,
91
- nonHttpEvents: fn.nonHttpEvents
92
- });
93
- index.set(resolvedModule, list);
94
- }
95
- }
96
- } catch (err) {
97
- process.stderr.write(
98
- `[suss] aws-lambda: failed to read ${templatePath}: ${err instanceof Error ? err.message : String(err)}
99
- `
100
- );
101
- }
102
- templateToIndex.set(templatePath, index);
103
- templateToDocuments.set(templatePath, [...documents]);
104
- return index;
105
- }
106
- function toModulePath(filePath) {
107
- const resolved = path.resolve(filePath);
108
- const ext = path.extname(resolved);
109
- return ext === "" ? resolved : resolved.slice(0, -ext.length);
110
- }
111
- function groupFieldsByFunction(bindings) {
112
- const out = /* @__PURE__ */ new Map();
113
- for (const binding of bindings) {
114
- for (const logicalId of binding.lambdaFunctionLogicalIds) {
115
- const fields = out.get(logicalId) ?? [];
116
- fields.push({ typeName: binding.typeName, fieldName: binding.fieldName });
117
- out.set(logicalId, fields);
118
- }
119
- }
120
- return out;
121
- }
122
- function handlersForFile(filePath) {
123
- const templatePath = findTemplate(path.dirname(path.resolve(filePath)));
124
- if (templatePath === null) {
125
- return [];
126
- }
127
- const index = indexForTemplate(templatePath);
128
- return index.get(toModulePath(filePath)) ?? [];
129
- }
130
- function templatesForFiles(files) {
131
- const roots = /* @__PURE__ */ new Set();
132
- for (const file of files) {
133
- const templatePath = findTemplate(path.dirname(path.resolve(file)));
134
- if (templatePath !== null) {
135
- roots.add(templatePath);
136
- }
137
- }
138
- const documents = /* @__PURE__ */ new Set();
139
- for (const root of roots) {
140
- indexForTemplate(root);
141
- for (const document of templateToDocuments.get(root) ?? [root]) {
142
- documents.add(document);
143
- }
144
- }
145
- return [...documents];
146
- }
147
- var HTTP_TERMINALS = [
148
- {
149
- // `return { statusCode, body, headers? }`, at the return or in a helper
150
- // the adapter follows. `body` contains the serialized payload, so
151
- // `JSON.stringify(x)` is unwrapped to the shape of `x`.
152
- kind: "response",
153
- match: { type: "returnShape", requiredProperties: ["statusCode"] },
154
- extraction: {
155
- statusCode: { from: "property", name: "statusCode" },
156
- body: { from: "property", name: "body", unwrapJsonStringify: true }
157
- }
158
- },
159
- {
160
- // `return { batchItemFailures }` makes Lambda retry the records it
161
- // lists. Lambda defines this shape, so the pack can match on it the
162
- // same way it matches the envelope above.
163
- kind: "return",
164
- match: {
165
- type: "returnShape",
166
- requiredProperties: ["batchItemFailures"]
167
- },
168
- extraction: {}
169
- },
170
- {
171
- // An uncaught throw becomes a throw-output transition. API Gateway
172
- // turns it into a 5xx, and the platform picks which one.
173
- kind: "throw",
174
- match: { type: "throwExpression" },
175
- extraction: {}
176
- }
177
- ];
178
- var NON_HTTP_TERMINALS = [
179
- ...HTTP_TERMINALS,
180
- {
181
- kind: "return",
182
- match: { type: "returnShape" },
183
- extraction: {}
184
- },
185
- {
186
- // A queue consumer acknowledges a batch by returning without an error.
187
- // Without this pattern the handler has no terminal, so it gets no
188
- // transition and everything it does is lost with it.
189
- kind: "return",
190
- match: { type: "functionFallthrough" },
191
- extraction: {}
192
- }
193
- ];
194
- var METADATA_NAMESPACE = "awsLambda";
195
- function deployableUnit(entry) {
196
- return { deploymentTarget: "lambda", instanceName: entry.functionLogicalId };
197
- }
198
- function httpRouteUnits(entry, func) {
199
- const units = [];
200
- for (const route of entry.httpRoutes) {
201
- if (route.method === "ANY") {
202
- continue;
203
- }
204
- units.push({
205
- func,
206
- kind: "handler",
207
- name: `${entry.functionLogicalId}.${entry.exportName}`,
208
- routeInfo: { method: route.method, path: route.path },
209
- deployableUnit: deployableUnit(entry),
210
- metadata: {
211
- [METADATA_NAMESPACE]: {
212
- handler: entry.handler,
213
- eventId: route.eventId,
214
- apiEventType: route.eventType
215
- }
216
- }
217
- });
218
- }
219
- return units;
220
- }
221
- var OPERATION_ROOT_TYPES = ["Query", "Mutation", "Subscription"];
222
- function operationFields(entry) {
223
- return entry.graphqlFields.filter(
224
- (f) => OPERATION_ROOT_TYPES.includes(f.typeName)
225
- );
226
- }
227
- function typeFields(entry) {
228
- return entry.graphqlFields.filter(
229
- (f) => !OPERATION_ROOT_TYPES.includes(f.typeName)
230
- );
231
- }
232
- function graphqlResolverUnits(entry, func) {
233
- return operationFields(entry).map((field) => ({
234
- func,
235
- kind: "handler",
236
- name: `${entry.functionLogicalId}.${entry.exportName}`,
237
- resolverInfo: { typeName: field.typeName, fieldName: field.fieldName },
238
- deployableUnit: deployableUnit(entry),
239
- metadata: {
240
- [METADATA_NAMESPACE]: {
241
- handler: entry.handler,
242
- recognition: "appsync-resolver"
243
- }
244
- }
245
- }));
246
- }
247
- function accountingUnit(entry, func) {
248
- const eventTypes = accountedEventTypes(entry);
249
- const backs = typeFields(entry);
250
- const wire = messageBusWire(eventTypes);
251
- const unit = deployableUnit(entry);
252
- return {
253
- func,
254
- kind: "handler",
255
- name: `${entry.functionLogicalId}.${entry.exportName}`,
256
- terminals: NON_HTTP_TERMINALS,
257
- ...wire !== null ? { channelInfo: { messageBus: wire, channel: null } } : { invocationInfo: unit },
258
- deployableUnit: unit,
259
- metadata: {
260
- [METADATA_NAMESPACE]: {
261
- handler: entry.handler,
262
- recognition: "recognized-not-http",
263
- eventTypes,
264
- ...backs.length > 0 ? { graphqlTypeFields: backs } : {}
265
- }
266
- }
267
- };
268
- }
269
- var EVENT_WIRES = {
270
- SQS: "aws_sqs",
271
- SNS: "aws.sns",
272
- S3: "s3",
273
- Schedule: "eventbridge",
274
- ScheduleV2: "eventbridge",
275
- EventBridgeRule: "eventbridge",
276
- CloudWatchEvent: "eventbridge"
277
- };
278
- function messageBusWire(eventTypes) {
279
- const wires = new Set(
280
- eventTypes.map((type) => EVENT_WIRES[type] ?? null)
281
- );
282
- if (wires.size !== 1) {
283
- return null;
284
- }
285
- const [wire] = wires;
286
- return wire ?? null;
287
- }
288
- function accountedEventTypes(entry) {
289
- return [
290
- ...entry.nonHttpEvents.map((e) => e.eventType),
291
- ...entry.httpRoutes.filter((r) => r.method === "ANY").map((r) => r.eventType)
292
- ];
293
- }
294
- function hasBindableRoute(entry) {
295
- return entry.httpRoutes.some((r) => r.method !== "ANY");
296
- }
297
- function awsLambdaDiscovery() {
298
- return (sourceFile, ctx) => {
299
- const sf = sourceFile;
300
- const tsCtx = ctx;
301
- const filePath = tsCtx.getFilePath(sf);
302
- const entries = handlersForFile(filePath);
303
- if (entries.length === 0) {
304
- return [];
305
- }
306
- const exported = /* @__PURE__ */ new Map();
307
- for (const { name, func } of tsCtx.exportedFunctions(sf)) {
308
- exported.set(name, func);
309
- }
310
- const units = [];
311
- for (const entry of entries) {
312
- const func = exported.get(entry.exportName);
313
- if (func === void 0) {
314
- continue;
315
- }
316
- units.push(...httpRouteUnits(entry, func));
317
- units.push(...graphqlResolverUnits(entry, func));
318
- const unaccountedEvents = accountedEventTypes(entry);
319
- const boundToNothing = !hasBindableRoute(entry) && operationFields(entry).length === 0;
320
- if (unaccountedEvents.length > 0 || boundToNothing) {
321
- units.push(accountingUnit(entry, func));
322
- }
323
- }
324
- return units;
325
- };
326
- }
327
- var LAMBDA = "@aws-sdk/client-lambda";
328
- var INSIDE_THE_COMMAND = (named) => ({
329
- send: {
330
- input: {
331
- at: 0,
332
- of: [
333
- {
334
- to: "argument",
335
- at: 0,
336
- origin: constructedFrom({ from: [LAMBDA], named })
337
- }
338
- ]
339
- }
340
- }
341
- });
342
- function invokeDeclarations() {
343
- return [
344
- unitInvokes({
345
- platform: "lambda",
346
- client: constructedFrom(LAMBDA),
347
- named: ["FunctionName"],
348
- payload: "Payload"
349
- }).methods(INSIDE_THE_COMMAND(["InvokeCommand"])).example(
350
- 'client.send(new InvokeCommand({ FunctionName: "Worker", Payload: "{}" }))'
351
- ),
352
- unitInvokes({
353
- platform: "lambda",
354
- client: constructedFrom(LAMBDA),
355
- named: ["FunctionName"],
356
- payload: "InvokeArgs"
357
- }).methods(INSIDE_THE_COMMAND(["InvokeAsyncCommand"])).example(
358
- 'client.send(new InvokeAsyncCommand({ FunctionName: "Worker", InvokeArgs: "{}" }))'
359
- )
360
- ];
361
- }
362
- var optionsSchema = z.object({}).strict();
363
- function awsLambdaFramework(_options = {}) {
364
- const invokes = invokeDeclarations();
365
- return {
366
- name: "aws-lambda",
367
- protocol: "http",
368
- // The adapter cannot read Sentry's wrapper inside the library, so the
369
- // pack declares that the handler is argument 0. A wrapper written in
370
- // the project needs no entry, because the adapter reads its body.
371
- transparentWrappers: [
372
- { module: "@sentry/aws-serverless", name: "wrapHandler", argument: 0 }
373
- ],
374
- languages: ["typescript", "javascript"],
375
- // The routing is in the template, so the callback finds handlers
376
- // there and the pattern list stays empty.
377
- discovery: [],
378
- discoverUnits: awsLambdaDiscovery(),
379
- // Discovery reads the templates, so they go into the cache key and an
380
- // edited template makes the next run read the project again.
381
- discoveryInputs: templatesForFiles,
382
- // No import gate: a JavaScript handler imports nothing from
383
- // `aws-lambda`, so the template acts as the gate. See the README.
384
- // A unit that is not HTTP gets a longer list from the discovery
385
- // callback in place of this one.
386
- terminals: HTTP_TERMINALS,
387
- inputMapping: {
388
- type: "positionalParams",
389
- params: [
390
- { position: 0, role: "event" },
391
- { position: 1, role: "context" }
392
- ]
393
- },
394
- // A proxy integration passes the handler API Gateway's event, with the
395
- // body as a string the handler parses. A read of the body records that
396
- // the handler took all of it, with no field name.
397
- requestSpelling: {
398
- headers: { path: ["event", "headers"], saysWhichField: true },
399
- query: {
400
- path: ["event", "queryStringParameters"],
401
- saysWhichField: true
402
- },
403
- params: { path: ["event", "pathParameters"], saysWhichField: true },
404
- body: { path: ["event", "body"], saysWhichField: false }
405
- },
406
- // Powertools reads these variables from inside node_modules, where no
407
- // walk over the project goes, so without this entry they look unread.
408
- libraryEnvVars: [
409
- {
410
- module: "@aws-lambda-powertools/",
411
- prefixes: ["POWERTOOLS_"]
412
- }
413
- ],
414
- // A pack-level `requiresImport` would gate `discoverUnits` too. Each
415
- // declaration already checks which SDK its command class came from.
416
- invocationRecognizers: invokes.map(
417
- (match) => compile(match.declared, "@suss/framework-aws-lambda")
418
- ),
419
- declarations: declarationsIn(invokes)
420
- };
421
- }
422
- var declares = {
423
- kind: "framework",
424
- package: "@suss/framework-aws-lambda",
425
- dependencies: [{ ecosystem: "npm", name: "@types/aws-lambda" }],
426
- reads: "AWS Lambda HTTP handlers, paired with the routes a SAM or CloudFormation template declares."
427
- };
428
- var index_default = awsLambdaFramework;
2
+ declares,
3
+ index_default,
4
+ optionsSchema
5
+ } from "./chunk-63BQSSQT.js";
429
6
  export {
430
7
  declares,
431
8
  index_default as default,