@slates-integrations/aws-lambda 0.2.0-rc.6

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.
Files changed (37) hide show
  1. package/README.md +89 -0
  2. package/docs/SPEC.md +91 -0
  3. package/logo.svg +1 -0
  4. package/package.json +21 -0
  5. package/slate.json +23 -0
  6. package/src/auth.ts +51 -0
  7. package/src/config.ts +8 -0
  8. package/src/index.ts +50 -0
  9. package/src/lib/aws-signer.ts +158 -0
  10. package/src/lib/client.ts +919 -0
  11. package/src/lib/errors.ts +101 -0
  12. package/src/lib/helpers.ts +15 -0
  13. package/src/spec.ts +13 -0
  14. package/src/tools/configure-async-invocation.ts +134 -0
  15. package/src/tools/create-function.ts +127 -0
  16. package/src/tools/delete-function.ts +41 -0
  17. package/src/tools/get-account-settings.ts +45 -0
  18. package/src/tools/get-function.ts +104 -0
  19. package/src/tools/index.ts +19 -0
  20. package/src/tools/invoke-function.ts +94 -0
  21. package/src/tools/list-functions.ts +88 -0
  22. package/src/tools/manage-alias.ts +159 -0
  23. package/src/tools/manage-concurrency.ts +141 -0
  24. package/src/tools/manage-durable-execution.ts +209 -0
  25. package/src/tools/manage-event-source-mapping.ts +193 -0
  26. package/src/tools/manage-function-url.ts +132 -0
  27. package/src/tools/manage-layer.ts +186 -0
  28. package/src/tools/manage-permission.ts +94 -0
  29. package/src/tools/manage-recursion-config.ts +63 -0
  30. package/src/tools/manage-runtime-management.ts +85 -0
  31. package/src/tools/manage-tags.ts +72 -0
  32. package/src/tools/publish-version.ts +83 -0
  33. package/src/tools/update-function.ts +156 -0
  34. package/src/triggers/function-changes.ts +113 -0
  35. package/src/triggers/inbound-webhook.ts +67 -0
  36. package/src/triggers/index.ts +2 -0
  37. package/tsconfig.json +23 -0
@@ -0,0 +1,85 @@
1
+ import { SlateTool } from 'slates';
2
+ import { createClient } from '../lib/helpers';
3
+ import { lambdaServiceError } from '../lib/errors';
4
+ import { spec } from '../spec';
5
+ import { z } from 'zod';
6
+
7
+ export let manageRuntimeManagement = SlateTool.create(spec, {
8
+ name: 'Manage Runtime Management',
9
+ key: 'manage_runtime_management',
10
+ description: `Get or set the runtime update mode for a Lambda function version. Runtime management controls whether Lambda applies runtime patches automatically, on function updates, or pins a manual runtime version ARN.`,
11
+ instructions: [
12
+ 'Use action "get" to read the current runtime update mode.',
13
+ 'Use action "set" with updateRuntimeOn. Manual mode also requires runtimeVersionArn.'
14
+ ]
15
+ })
16
+ .input(
17
+ z.object({
18
+ action: z.enum(['get', 'set']).describe('Operation to perform'),
19
+ functionName: z.string().describe('Function name or ARN'),
20
+ qualifier: z
21
+ .string()
22
+ .optional()
23
+ .describe('Function version. Defaults to $LATEST when omitted'),
24
+ updateRuntimeOn: z
25
+ .enum(['Auto', 'Manual', 'FunctionUpdate'])
26
+ .optional()
27
+ .describe('Runtime update mode to set'),
28
+ runtimeVersionArn: z
29
+ .string()
30
+ .optional()
31
+ .describe('Runtime version ARN required when updateRuntimeOn is Manual')
32
+ })
33
+ )
34
+ .output(
35
+ z.object({
36
+ functionArn: z.string().optional().describe('Function ARN'),
37
+ updateRuntimeOn: z.string().optional().describe('Runtime update mode'),
38
+ runtimeVersionArn: z
39
+ .string()
40
+ .nullable()
41
+ .optional()
42
+ .describe('Pinned runtime version ARN when manual mode is configured')
43
+ })
44
+ )
45
+ .handleInvocation(async ctx => {
46
+ let client = createClient(ctx.config, ctx.auth);
47
+ let { action, functionName, qualifier } = ctx.input;
48
+
49
+ if (action === 'get') {
50
+ let result = await client.getRuntimeManagementConfig(functionName, qualifier);
51
+ return {
52
+ output: {
53
+ functionArn: result.FunctionArn,
54
+ updateRuntimeOn: result.UpdateRuntimeOn,
55
+ runtimeVersionArn: result.RuntimeVersionArn ?? null
56
+ },
57
+ message: `Runtime management for **${functionName}** is **${result.UpdateRuntimeOn}**.`
58
+ };
59
+ }
60
+
61
+ if (!ctx.input.updateRuntimeOn) {
62
+ throw lambdaServiceError('updateRuntimeOn is required for set.');
63
+ }
64
+ if (ctx.input.updateRuntimeOn === 'Manual' && !ctx.input.runtimeVersionArn) {
65
+ throw lambdaServiceError('runtimeVersionArn is required when updateRuntimeOn is Manual.');
66
+ }
67
+
68
+ let params: Record<string, any> = {
69
+ UpdateRuntimeOn: ctx.input.updateRuntimeOn
70
+ };
71
+ if (ctx.input.runtimeVersionArn) {
72
+ params['RuntimeVersionArn'] = ctx.input.runtimeVersionArn;
73
+ }
74
+
75
+ let result = await client.putRuntimeManagementConfig(functionName, params, qualifier);
76
+ return {
77
+ output: {
78
+ functionArn: result.FunctionArn,
79
+ updateRuntimeOn: result.UpdateRuntimeOn,
80
+ runtimeVersionArn: result.RuntimeVersionArn ?? null
81
+ },
82
+ message: `Set runtime management for **${functionName}** to **${result.UpdateRuntimeOn}**.`
83
+ };
84
+ })
85
+ .build();
@@ -0,0 +1,72 @@
1
+ import { SlateTool } from 'slates';
2
+ import { createClient } from '../lib/helpers';
3
+ import { lambdaServiceError } from '../lib/errors';
4
+ import { spec } from '../spec';
5
+ import { z } from 'zod';
6
+
7
+ export let manageTags = SlateTool.create(spec, {
8
+ name: 'Manage Tags',
9
+ key: 'manage_tags',
10
+ description: `List, add, or remove tags on a Lambda function. Tags are key-value pairs used for organization, cost allocation, and access control. Provide the full function ARN for tag operations.`,
11
+ instructions: [
12
+ 'Use **action** "list" to view tags, "add" to set tags, or "remove" to delete tag keys.',
13
+ 'The resourceArn must be the full function ARN (e.g., arn:aws:lambda:us-east-1:123456789012:function:my-func).'
14
+ ]
15
+ })
16
+ .input(
17
+ z.object({
18
+ action: z.enum(['list', 'add', 'remove']).describe('Operation to perform'),
19
+ resourceArn: z.string().describe('Full ARN of the Lambda resource'),
20
+ tags: z
21
+ .record(z.string(), z.string())
22
+ .optional()
23
+ .describe('Tags to add as key-value pairs (for add action)'),
24
+ tagKeys: z
25
+ .array(z.string())
26
+ .optional()
27
+ .describe('Tag keys to remove (for remove action)')
28
+ })
29
+ )
30
+ .output(
31
+ z.object({
32
+ tags: z
33
+ .record(z.string(), z.string())
34
+ .optional()
35
+ .describe('Current tags on the resource'),
36
+ updated: z.boolean().optional()
37
+ })
38
+ )
39
+ .handleInvocation(async ctx => {
40
+ let client = createClient(ctx.config, ctx.auth);
41
+ let { action, resourceArn } = ctx.input;
42
+
43
+ if (action === 'list') {
44
+ let result = await client.listTags(resourceArn);
45
+ return {
46
+ output: { tags: result.Tags || {} },
47
+ message: `Found **${Object.keys(result.Tags || {}).length}** tag(s).`
48
+ };
49
+ }
50
+
51
+ if (action === 'add') {
52
+ if (!ctx.input.tags || Object.keys(ctx.input.tags).length === 0) {
53
+ throw lambdaServiceError('tags are required for the add action');
54
+ }
55
+ await client.tagResource(resourceArn, ctx.input.tags);
56
+ return {
57
+ output: { updated: true, tags: ctx.input.tags },
58
+ message: `Added **${Object.keys(ctx.input.tags).length}** tag(s) to the resource.`
59
+ };
60
+ }
61
+
62
+ // remove
63
+ if (!ctx.input.tagKeys || ctx.input.tagKeys.length === 0) {
64
+ throw lambdaServiceError('tagKeys are required for the remove action');
65
+ }
66
+ await client.untagResource(resourceArn, ctx.input.tagKeys);
67
+ return {
68
+ output: { updated: true },
69
+ message: `Removed **${ctx.input.tagKeys.length}** tag(s) from the resource.`
70
+ };
71
+ })
72
+ .build();
@@ -0,0 +1,83 @@
1
+ import { SlateTool } from 'slates';
2
+ import { createClient } from '../lib/helpers';
3
+ import { spec } from '../spec';
4
+ import { z } from 'zod';
5
+
6
+ export let publishVersion = SlateTool.create(spec, {
7
+ name: 'Publish Version',
8
+ key: 'publish_version',
9
+ description: `Publish an immutable version from the current \`$LATEST\` code and configuration. Optionally list all published versions of a function by setting **listOnly** to true.`,
10
+ instructions: [
11
+ 'Use codeSha256 to ensure you are publishing the expected code revision.',
12
+ 'Published versions are immutable and can be referenced by aliases.'
13
+ ]
14
+ })
15
+ .input(
16
+ z.object({
17
+ functionName: z.string().describe('Function name or ARN'),
18
+ listOnly: z
19
+ .boolean()
20
+ .optional()
21
+ .describe('Set to true to only list versions without publishing'),
22
+ description: z.string().optional().describe('Version description'),
23
+ codeSha256: z
24
+ .string()
25
+ .optional()
26
+ .describe('Expected SHA256 hash of the code to publish (validation)')
27
+ })
28
+ )
29
+ .output(
30
+ z.object({
31
+ version: z.string().optional().describe('Published version number'),
32
+ functionArn: z.string().optional().describe('Version-specific ARN'),
33
+ description: z.string().optional().describe('Version description'),
34
+ codeSha256: z.string().optional().describe('SHA256 of the published code'),
35
+ versions: z
36
+ .array(
37
+ z.object({
38
+ version: z.string().optional(),
39
+ functionArn: z.string().optional(),
40
+ description: z.string().optional(),
41
+ lastModified: z.string().optional(),
42
+ runtime: z.string().optional()
43
+ })
44
+ )
45
+ .optional()
46
+ .describe('List of versions (when listOnly is true)')
47
+ })
48
+ )
49
+ .handleInvocation(async ctx => {
50
+ let client = createClient(ctx.config, ctx.auth);
51
+
52
+ if (ctx.input.listOnly) {
53
+ let result = await client.listVersionsByFunction(ctx.input.functionName);
54
+ let versions = (result.Versions || []).map((v: any) => ({
55
+ version: v.Version,
56
+ functionArn: v.FunctionArn,
57
+ description: v.Description,
58
+ lastModified: v.LastModified,
59
+ runtime: v.Runtime
60
+ }));
61
+ return {
62
+ output: { versions },
63
+ message: `Found **${versions.length}** version(s) for **${ctx.input.functionName}**.`
64
+ };
65
+ }
66
+
67
+ let params: Record<string, any> = {};
68
+ if (ctx.input.description) params['Description'] = ctx.input.description;
69
+ if (ctx.input.codeSha256) params['CodeSha256'] = ctx.input.codeSha256;
70
+
71
+ let result = await client.publishVersion(ctx.input.functionName, params);
72
+
73
+ return {
74
+ output: {
75
+ version: result.Version,
76
+ functionArn: result.FunctionArn,
77
+ description: result.Description,
78
+ codeSha256: result.CodeSha256
79
+ },
80
+ message: `Published version **${result.Version}** for **${ctx.input.functionName}**.`
81
+ };
82
+ })
83
+ .build();
@@ -0,0 +1,156 @@
1
+ import { SlateTool } from 'slates';
2
+ import { createClient } from '../lib/helpers';
3
+ import { lambdaServiceError } from '../lib/errors';
4
+ import { spec } from '../spec';
5
+ import { z } from 'zod';
6
+
7
+ export let updateFunction = SlateTool.create(spec, {
8
+ name: 'Update Function',
9
+ key: 'update_function',
10
+ description: `Update a Lambda function's code and/or configuration. Provide **code** fields to update the deployment package, or **configuration** fields to modify settings like runtime, handler, memory, timeout, environment variables, layers, and VPC. Both can be updated in a single call.`,
11
+ instructions: [
12
+ 'Code and configuration updates are applied separately via two API calls if both are provided.',
13
+ 'Environment variables replace the entire set — provide all variables you want to keep.'
14
+ ]
15
+ })
16
+ .input(
17
+ z.object({
18
+ functionName: z.string().describe('Function name or ARN'),
19
+ code: z
20
+ .object({
21
+ s3Bucket: z
22
+ .string()
23
+ .optional()
24
+ .describe('S3 bucket containing the new code package'),
25
+ s3Key: z.string().optional().describe('S3 object key'),
26
+ s3ObjectVersion: z.string().optional().describe('S3 object version'),
27
+ imageUri: z.string().optional().describe('Container image URI from ECR'),
28
+ zipFile: z.string().optional().describe('Base64-encoded ZIP file content'),
29
+ publish: z
30
+ .boolean()
31
+ .optional()
32
+ .describe('Publish a new version after updating code'),
33
+ architectures: z
34
+ .array(z.enum(['x86_64', 'arm64']))
35
+ .optional()
36
+ .describe('Target architecture')
37
+ })
38
+ .optional()
39
+ .describe('New function code'),
40
+ configuration: z
41
+ .object({
42
+ role: z.string().optional().describe('New execution role ARN'),
43
+ runtime: z.string().optional().describe('New runtime'),
44
+ handler: z.string().optional().describe('New handler'),
45
+ description: z.string().optional().describe('New description'),
46
+ timeout: z.number().optional().describe('New timeout in seconds'),
47
+ memorySize: z.number().optional().describe('New memory size in MB'),
48
+ environment: z
49
+ .record(z.string(), z.string())
50
+ .optional()
51
+ .describe('New environment variables (replaces existing)'),
52
+ layers: z
53
+ .array(z.string())
54
+ .optional()
55
+ .describe('New layer ARNs (replaces existing)'),
56
+ ephemeralStorageSize: z.number().optional().describe('New /tmp storage in MB'),
57
+ tracingMode: z
58
+ .enum(['Active', 'PassThrough'])
59
+ .optional()
60
+ .describe('X-Ray tracing mode'),
61
+ vpcSubnetIds: z.array(z.string()).optional().describe('VPC subnet IDs'),
62
+ vpcSecurityGroupIds: z
63
+ .array(z.string())
64
+ .optional()
65
+ .describe('VPC security group IDs')
66
+ })
67
+ .optional()
68
+ .describe('Configuration changes')
69
+ })
70
+ )
71
+ .output(
72
+ z.object({
73
+ functionName: z.string().describe('Name of the updated function'),
74
+ functionArn: z.string().describe('ARN of the updated function'),
75
+ runtime: z.string().optional().describe('Runtime after update'),
76
+ state: z.string().optional().describe('Function state after update'),
77
+ version: z.string().optional().describe('Published version (if code was published)'),
78
+ lastModified: z.string().optional().describe('Last modified timestamp'),
79
+ codeUpdated: z.boolean().describe('Whether the code was updated'),
80
+ configUpdated: z.boolean().describe('Whether the configuration was updated')
81
+ })
82
+ )
83
+ .handleInvocation(async ctx => {
84
+ let client = createClient(ctx.config, ctx.auth);
85
+ let codeUpdated = false;
86
+ let configUpdated = false;
87
+ let lastResult: any = {};
88
+
89
+ if (!ctx.input.code && !ctx.input.configuration) {
90
+ throw lambdaServiceError('Provide code, configuration, or both to update a function.');
91
+ }
92
+
93
+ if (ctx.input.code) {
94
+ let codeParams: Record<string, any> = {};
95
+ if (ctx.input.code.s3Bucket) codeParams['S3Bucket'] = ctx.input.code.s3Bucket;
96
+ if (ctx.input.code.s3Key) codeParams['S3Key'] = ctx.input.code.s3Key;
97
+ if (ctx.input.code.s3ObjectVersion)
98
+ codeParams['S3ObjectVersion'] = ctx.input.code.s3ObjectVersion;
99
+ if (ctx.input.code.imageUri) codeParams['ImageUri'] = ctx.input.code.imageUri;
100
+ if (ctx.input.code.zipFile) codeParams['ZipFile'] = ctx.input.code.zipFile;
101
+ if (ctx.input.code.publish !== undefined) codeParams['Publish'] = ctx.input.code.publish;
102
+ if (ctx.input.code.architectures)
103
+ codeParams['Architectures'] = ctx.input.code.architectures;
104
+
105
+ lastResult = await client.updateFunctionCode(ctx.input.functionName, codeParams);
106
+ codeUpdated = true;
107
+ }
108
+
109
+ if (ctx.input.configuration) {
110
+ let configParams: Record<string, any> = {};
111
+ let cfg = ctx.input.configuration;
112
+
113
+ if (cfg.role) configParams['Role'] = cfg.role;
114
+ if (cfg.runtime) configParams['Runtime'] = cfg.runtime;
115
+ if (cfg.handler) configParams['Handler'] = cfg.handler;
116
+ if (cfg.description !== undefined) configParams['Description'] = cfg.description;
117
+ if (cfg.timeout) configParams['Timeout'] = cfg.timeout;
118
+ if (cfg.memorySize) configParams['MemorySize'] = cfg.memorySize;
119
+ if (cfg.environment) configParams['Environment'] = { Variables: cfg.environment };
120
+ if (cfg.layers) configParams['Layers'] = cfg.layers;
121
+ if (cfg.ephemeralStorageSize)
122
+ configParams['EphemeralStorage'] = { Size: cfg.ephemeralStorageSize };
123
+ if (cfg.tracingMode) configParams['TracingConfig'] = { Mode: cfg.tracingMode };
124
+ if (cfg.vpcSubnetIds || cfg.vpcSecurityGroupIds) {
125
+ configParams['VpcConfig'] = {
126
+ SubnetIds: cfg.vpcSubnetIds || [],
127
+ SecurityGroupIds: cfg.vpcSecurityGroupIds || []
128
+ };
129
+ }
130
+
131
+ lastResult = await client.updateFunctionConfiguration(
132
+ ctx.input.functionName,
133
+ configParams
134
+ );
135
+ configUpdated = true;
136
+ }
137
+
138
+ let parts: string[] = [];
139
+ if (codeUpdated) parts.push('code');
140
+ if (configUpdated) parts.push('configuration');
141
+
142
+ return {
143
+ output: {
144
+ functionName: lastResult.FunctionName || ctx.input.functionName,
145
+ functionArn: lastResult.FunctionArn || '',
146
+ runtime: lastResult.Runtime,
147
+ state: lastResult.State,
148
+ version: lastResult.Version,
149
+ lastModified: lastResult.LastModified,
150
+ codeUpdated,
151
+ configUpdated
152
+ },
153
+ message: `Updated ${parts.join(' and ')} for function **${lastResult.FunctionName || ctx.input.functionName}**.`
154
+ };
155
+ })
156
+ .build();
@@ -0,0 +1,113 @@
1
+ import { SlateTrigger, SlateDefaultPollingIntervalSeconds } from 'slates';
2
+ import { createClient } from '../lib/helpers';
3
+ import { spec } from '../spec';
4
+ import { z } from 'zod';
5
+
6
+ export let functionChanges = SlateTrigger.create(spec, {
7
+ name: 'Function Changes',
8
+ key: 'function_changes',
9
+ description:
10
+ 'Polls for new, updated, or deleted Lambda functions by comparing snapshots of the function list. Detects when functions are created, modified (code or configuration changes), or removed.'
11
+ })
12
+ .input(
13
+ z.object({
14
+ changeType: z
15
+ .enum(['created', 'updated', 'deleted'])
16
+ .describe('Type of change detected'),
17
+ functionName: z.string().describe('Name of the affected function'),
18
+ functionArn: z.string().describe('ARN of the affected function'),
19
+ runtime: z.string().optional().describe('Function runtime'),
20
+ lastModified: z.string().optional().describe('Last modified timestamp'),
21
+ state: z.string().optional().describe('Function state'),
22
+ codeSha256: z.string().optional().describe('SHA256 of the deployment package'),
23
+ version: z.string().optional().describe('Function version')
24
+ })
25
+ )
26
+ .output(
27
+ z.object({
28
+ functionName: z.string().describe('Name of the affected function'),
29
+ functionArn: z.string().describe('ARN of the affected function'),
30
+ runtime: z.string().optional().describe('Function runtime'),
31
+ lastModified: z.string().optional().describe('Last modified timestamp'),
32
+ state: z.string().optional().describe('Function state'),
33
+ codeSha256: z.string().optional().describe('SHA256 of the deployment package'),
34
+ version: z.string().optional().describe('Function version')
35
+ })
36
+ )
37
+ .polling({
38
+ options: {
39
+ intervalInSeconds: SlateDefaultPollingIntervalSeconds
40
+ },
41
+
42
+ pollEvents: async ctx => {
43
+ let client = createClient(ctx.config, ctx.auth);
44
+
45
+ let allFunctions: any[] = [];
46
+ let marker: string | undefined;
47
+ do {
48
+ let result = await client.listFunctions(marker, 50);
49
+ allFunctions.push(...(result.Functions || []));
50
+ marker = result.NextMarker;
51
+ } while (marker);
52
+
53
+ let currentMap: Record<string, any> = {};
54
+ for (let fn of allFunctions) {
55
+ currentMap[fn.FunctionArn] = {
56
+ functionName: fn.FunctionName,
57
+ functionArn: fn.FunctionArn,
58
+ runtime: fn.Runtime,
59
+ lastModified: fn.LastModified,
60
+ state: fn.State,
61
+ codeSha256: fn.CodeSha256,
62
+ version: fn.Version
63
+ };
64
+ }
65
+
66
+ let previousMap: Record<string, any> = ctx.state?.functionMap || {};
67
+ let inputs: any[] = [];
68
+
69
+ // Detect new and updated functions
70
+ for (let [arn, current] of Object.entries(currentMap)) {
71
+ let previous = previousMap[arn];
72
+ if (!previous) {
73
+ inputs.push({ changeType: 'created' as const, ...current });
74
+ } else if (
75
+ previous.lastModified !== current.lastModified ||
76
+ previous.codeSha256 !== current.codeSha256
77
+ ) {
78
+ inputs.push({ changeType: 'updated' as const, ...current });
79
+ }
80
+ }
81
+
82
+ // Detect deleted functions
83
+ for (let [arn, previous] of Object.entries(previousMap)) {
84
+ if (!currentMap[arn]) {
85
+ inputs.push({ changeType: 'deleted' as const, ...previous });
86
+ }
87
+ }
88
+
89
+ return {
90
+ inputs,
91
+ updatedState: {
92
+ functionMap: currentMap
93
+ }
94
+ };
95
+ },
96
+
97
+ handleEvent: async ctx => {
98
+ return {
99
+ type: `function.${ctx.input.changeType}`,
100
+ id: `${ctx.input.functionArn}-${ctx.input.changeType}-${ctx.input.lastModified || Date.now()}`,
101
+ output: {
102
+ functionName: ctx.input.functionName,
103
+ functionArn: ctx.input.functionArn,
104
+ runtime: ctx.input.runtime,
105
+ lastModified: ctx.input.lastModified,
106
+ state: ctx.input.state,
107
+ codeSha256: ctx.input.codeSha256,
108
+ version: ctx.input.version
109
+ }
110
+ };
111
+ }
112
+ })
113
+ .build();
@@ -0,0 +1,67 @@
1
+ import { SlateTrigger } from 'slates';
2
+ import { spec } from '../spec';
3
+ import { z } from 'zod';
4
+
5
+ /**
6
+ * Generic inbound webhook for providers without a tailored webhook trigger yet.
7
+ * POST JSON is parsed into `payload` (non-objects are wrapped as { _value }).
8
+ * Refine in the workflow mapper or replace with a provider-specific trigger.
9
+ */
10
+ export let inboundWebhook = SlateTrigger.create(spec, {
11
+ name: 'Inbound Webhook',
12
+ key: 'inbound_webhook',
13
+ description:
14
+ 'Receives HTTP POST at the Slates webhook URL. Parses JSON into payload (or stores raw body if not JSON). Configure your provider to POST here when supported.'
15
+ })
16
+ .input(
17
+ z.object({
18
+ payload: z
19
+ .record(z.string(), z.any())
20
+ .describe('Parsed JSON object from the request body'),
21
+ rawBody: z.string().optional().describe('Raw body when JSON parsing failed'),
22
+ contentType: z.string().optional().describe('Content-Type header')
23
+ })
24
+ )
25
+ .output(
26
+ z.object({
27
+ payload: z.record(z.string(), z.any()),
28
+ rawBody: z.string().optional()
29
+ })
30
+ )
31
+ .webhook({
32
+ handleRequest: async ctx => {
33
+ let contentType = ctx.request.headers.get('content-type') ?? '';
34
+ let text = await ctx.request.text();
35
+ if (!text || !text.trim()) {
36
+ return {
37
+ inputs: [{ payload: {}, contentType }]
38
+ };
39
+ }
40
+ try {
41
+ let parsed = JSON.parse(text);
42
+ let payload =
43
+ parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)
44
+ ? parsed
45
+ : { _value: parsed };
46
+ return {
47
+ inputs: [{ payload, contentType }]
48
+ };
49
+ } catch {
50
+ return {
51
+ inputs: [{ payload: {}, rawBody: text, contentType }]
52
+ };
53
+ }
54
+ },
55
+
56
+ handleEvent: async ctx => {
57
+ return {
58
+ type: 'webhook.inbound',
59
+ id: `inbound-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`,
60
+ output: {
61
+ payload: ctx.input.payload,
62
+ rawBody: ctx.input.rawBody
63
+ }
64
+ };
65
+ }
66
+ })
67
+ .build();
@@ -0,0 +1,2 @@
1
+ export { functionChanges } from './function-changes';
2
+ export * from './inbound-webhook';
package/tsconfig.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "types": ["node"],
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+ "moduleResolution": "bundler",
11
+
12
+ "noEmit": true,
13
+ "strict": true,
14
+ "skipLibCheck": true,
15
+ "noFallthroughCasesInSwitch": true,
16
+ "noUncheckedIndexedAccess": true,
17
+ "noImplicitOverride": true,
18
+ "noUnusedLocals": false,
19
+ "noUnusedParameters": false,
20
+ "noPropertyAccessFromIndexSignature": false
21
+ },
22
+ "include": ["src"]
23
+ }