@unrdf/serverless 26.4.2

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.
@@ -0,0 +1,363 @@
1
+ /**
2
+ * @fileoverview UNRDF CDK Stack - Infrastructure as Code for RDF Applications
3
+ *
4
+ * @description
5
+ * Defines AWS infrastructure for serverless UNRDF deployments including:
6
+ * - Lambda functions for RDF query execution
7
+ * - DynamoDB tables for RDF triple storage
8
+ * - API Gateway for REST endpoints
9
+ * - CloudFront CDN for global distribution
10
+ *
11
+ * @module serverless/cdk/unrdf-stack
12
+ * @version 1.0.0
13
+ * @license MIT
14
+ */
15
+
16
+ import { Stack, Duration, RemovalPolicy, CfnOutput } from 'aws-cdk-lib';
17
+ import { Runtime, Function as LambdaFunction, Code, LayerVersion } from 'aws-cdk-lib/aws-lambda';
18
+ import { RestApi, LambdaIntegration, Cors, EndpointType } from 'aws-cdk-lib/aws-apigateway';
19
+ import { Table, AttributeType, BillingMode, StreamViewType } from 'aws-cdk-lib/aws-dynamodb';
20
+ import {
21
+ Distribution,
22
+ OriginAccessIdentity as _OriginAccessIdentity,
23
+ ViewerProtocolPolicy,
24
+ } from 'aws-cdk-lib/aws-cloudfront';
25
+ import { RestApiOrigin } from 'aws-cdk-lib/aws-cloudfront-origins';
26
+ import { Policy as _Policy, PolicyStatement, Effect } from 'aws-cdk-lib/aws-iam';
27
+ import { z } from 'zod';
28
+
29
+ /**
30
+ * Configuration schema for UNRDF stack
31
+ * @typedef {Object} UNRDFStackConfig
32
+ * @property {string} environment - Deployment environment (dev, staging, prod)
33
+ * @property {number} memorySizeMb - Lambda memory allocation in MB
34
+ * @property {number} timeoutSeconds - Lambda timeout in seconds
35
+ * @property {boolean} enableCdn - Enable CloudFront CDN
36
+ * @property {boolean} enableStreaming - Enable DynamoDB streams
37
+ */
38
+ const StackConfigSchema = z.object({
39
+ environment: z.enum(['dev', 'staging', 'prod']).default('dev'),
40
+ memorySizeMb: z.number().min(128).max(10240).default(1024),
41
+ timeoutSeconds: z.number().min(3).max(900).default(30),
42
+ enableCdn: z.boolean().default(true),
43
+ enableStreaming: z.boolean().default(false),
44
+ tableName: z.string().optional(),
45
+ apiName: z.string().optional(),
46
+ });
47
+
48
+ /**
49
+ * UNRDF Serverless Stack
50
+ *
51
+ * @class UNRDFStack
52
+ * @extends {Stack}
53
+ *
54
+ * @description
55
+ * Creates a complete serverless infrastructure for UNRDF applications with:
56
+ * - Auto-scaling Lambda functions
57
+ * - DynamoDB for persistent RDF storage
58
+ * - API Gateway with CORS support
59
+ * - Optional CloudFront CDN
60
+ *
61
+ * @example
62
+ * ```javascript
63
+ * import { App } from 'aws-cdk-lib';
64
+ * import { UNRDFStack } from '@unrdf/serverless/cdk';
65
+ *
66
+ * const app = new App();
67
+ * new UNRDFStack(app, 'MyUNRDFApp', {
68
+ * environment: 'prod',
69
+ * memorySizeMb: 2048,
70
+ * enableCdn: true
71
+ * });
72
+ * ```
73
+ */
74
+ export class UNRDFStack extends Stack {
75
+ /**
76
+ * DynamoDB table for RDF triples
77
+ * @type {Table}
78
+ */
79
+ triplesTable;
80
+
81
+ /**
82
+ * Lambda function for query execution
83
+ * @type {LambdaFunction}
84
+ */
85
+ queryFunction;
86
+
87
+ /**
88
+ * API Gateway REST API
89
+ * @type {RestApi}
90
+ */
91
+ api;
92
+
93
+ /**
94
+ * CloudFront distribution (optional)
95
+ * @type {Distribution|null}
96
+ */
97
+ distribution = null;
98
+
99
+ /**
100
+ * Create UNRDF Stack
101
+ *
102
+ * @param {import('constructs').Construct} scope - CDK scope
103
+ * @param {string} id - Stack ID
104
+ * @param {Object} props - Stack properties
105
+ * @param {Object} [props.config] - UNRDF configuration
106
+ */
107
+ constructor(scope, id, props = {}) {
108
+ super(scope, id, props);
109
+
110
+ // Validate configuration
111
+ const config = StackConfigSchema.parse(props.config || {});
112
+
113
+ // Create DynamoDB table for RDF triples
114
+ this.triplesTable = this.createTriplesTable(config);
115
+
116
+ // Create Lambda layer with dependencies
117
+ const layer = this.createDependencyLayer();
118
+
119
+ // Create Lambda functions
120
+ this.queryFunction = this.createQueryFunction(config, layer);
121
+ this.createIngestFunction(config, layer);
122
+
123
+ // Grant DynamoDB access to Lambda
124
+ this.triplesTable.grantReadWriteData(this.queryFunction);
125
+
126
+ // Create API Gateway
127
+ this.api = this.createApiGateway(config);
128
+
129
+ // Create CloudFront CDN if enabled
130
+ if (config.enableCdn) {
131
+ this.distribution = this.createCdnDistribution();
132
+ }
133
+
134
+ // Output deployment information
135
+ this.createOutputs(config);
136
+ }
137
+
138
+ /**
139
+ * Create DynamoDB table for RDF triple storage
140
+ *
141
+ * @private
142
+ * @param {Object} config - Stack configuration
143
+ * @returns {Table} DynamoDB table
144
+ */
145
+ createTriplesTable(config) {
146
+ const table = new Table(this, 'TriplesTable', {
147
+ tableName: config.tableName || `unrdf-triples-${config.environment}`,
148
+ partitionKey: { name: 'subject', type: AttributeType.STRING },
149
+ sortKey: { name: 'predicate_object', type: AttributeType.STRING },
150
+ billingMode: BillingMode.PAY_PER_REQUEST,
151
+ stream: config.enableStreaming ? StreamViewType.NEW_AND_OLD_IMAGES : undefined,
152
+ removalPolicy: config.environment === 'prod' ? RemovalPolicy.RETAIN : RemovalPolicy.DESTROY,
153
+ pointInTimeRecovery: config.environment === 'prod',
154
+ });
155
+
156
+ // Add GSI for predicate queries
157
+ table.addGlobalSecondaryIndex({
158
+ indexName: 'predicate-index',
159
+ partitionKey: { name: 'predicate', type: AttributeType.STRING },
160
+ sortKey: { name: 'subject_object', type: AttributeType.STRING },
161
+ });
162
+
163
+ // Add GSI for object queries
164
+ table.addGlobalSecondaryIndex({
165
+ indexName: 'object-index',
166
+ partitionKey: { name: 'object', type: AttributeType.STRING },
167
+ sortKey: { name: 'subject_predicate', type: AttributeType.STRING },
168
+ });
169
+
170
+ return table;
171
+ }
172
+
173
+ /**
174
+ * Create Lambda layer with UNRDF dependencies
175
+ *
176
+ * @private
177
+ * @returns {LayerVersion} Lambda layer
178
+ */
179
+ createDependencyLayer() {
180
+ return new LayerVersion(this, 'UNRDFLayer', {
181
+ code: Code.fromAsset('dist/layer'),
182
+ compatibleRuntimes: [Runtime.NODEJS_20_X],
183
+ description: 'UNRDF core dependencies and utilities',
184
+ });
185
+ }
186
+
187
+ /**
188
+ * Create Lambda function for SPARQL query execution
189
+ *
190
+ * @private
191
+ * @param {Object} config - Stack configuration
192
+ * @param {LayerVersion} layer - Dependency layer
193
+ * @returns {LambdaFunction} Query function
194
+ */
195
+ createQueryFunction(config, layer) {
196
+ const fn = new LambdaFunction(this, 'QueryFunction', {
197
+ functionName: `unrdf-query-${config.environment}`,
198
+ runtime: Runtime.NODEJS_20_X,
199
+ handler: 'index.handler',
200
+ code: Code.fromAsset('dist/lambda/query'),
201
+ layers: [layer],
202
+ memorySize: config.memorySizeMb,
203
+ timeout: Duration.seconds(config.timeoutSeconds),
204
+ environment: {
205
+ TRIPLES_TABLE: this.triplesTable.tableName,
206
+ ENVIRONMENT: config.environment,
207
+ NODE_ENV: 'production',
208
+ },
209
+ reservedConcurrentExecutions: config.environment === 'prod' ? 100 : 10,
210
+ });
211
+
212
+ // Add X-Ray tracing
213
+ fn.addToRolePolicy(
214
+ new PolicyStatement({
215
+ effect: Effect.ALLOW,
216
+ actions: ['xray:PutTraceSegments', 'xray:PutTelemetryRecords'],
217
+ resources: ['*'],
218
+ })
219
+ );
220
+
221
+ return fn;
222
+ }
223
+
224
+ /**
225
+ * Create Lambda function for RDF data ingestion
226
+ *
227
+ * @private
228
+ * @param {Object} config - Stack configuration
229
+ * @param {LayerVersion} layer - Dependency layer
230
+ * @returns {LambdaFunction} Ingest function
231
+ */
232
+ createIngestFunction(config, layer) {
233
+ const fn = new LambdaFunction(this, 'IngestFunction', {
234
+ functionName: `unrdf-ingest-${config.environment}`,
235
+ runtime: Runtime.NODEJS_20_X,
236
+ handler: 'index.handler',
237
+ code: Code.fromAsset('dist/lambda/ingest'),
238
+ layers: [layer],
239
+ memorySize: config.memorySizeMb * 2, // Double memory for batch operations
240
+ timeout: Duration.seconds(Math.min(config.timeoutSeconds * 2, 900)),
241
+ environment: {
242
+ TRIPLES_TABLE: this.triplesTable.tableName,
243
+ ENVIRONMENT: config.environment,
244
+ BATCH_SIZE: '100',
245
+ },
246
+ });
247
+
248
+ this.triplesTable.grantReadWriteData(fn);
249
+
250
+ return fn;
251
+ }
252
+
253
+ /**
254
+ * Create API Gateway REST API
255
+ *
256
+ * @private
257
+ * @param {Object} config - Stack configuration
258
+ * @returns {RestApi} API Gateway
259
+ */
260
+ createApiGateway(config) {
261
+ const api = new RestApi(this, 'UNRDFApi', {
262
+ restApiName: config.apiName || `unrdf-api-${config.environment}`,
263
+ description: 'UNRDF Serverless API for RDF operations',
264
+ endpointTypes: [EndpointType.REGIONAL],
265
+ defaultCorsPreflightOptions: {
266
+ allowOrigins: Cors.ALL_ORIGINS,
267
+ allowMethods: Cors.ALL_METHODS,
268
+ allowHeaders: ['Content-Type', 'Authorization'],
269
+ },
270
+ deployOptions: {
271
+ stageName: config.environment,
272
+ tracingEnabled: true,
273
+ metricsEnabled: true,
274
+ },
275
+ });
276
+
277
+ // Add /query endpoint
278
+ const queryResource = api.root.addResource('query');
279
+ queryResource.addMethod('POST', new LambdaIntegration(this.queryFunction), {
280
+ apiKeyRequired: config.environment === 'prod',
281
+ });
282
+
283
+ // Add /health endpoint
284
+ const healthResource = api.root.addResource('health');
285
+ healthResource.addMethod('GET', new LambdaIntegration(this.queryFunction));
286
+
287
+ return api;
288
+ }
289
+
290
+ /**
291
+ * Create CloudFront CDN distribution
292
+ *
293
+ * @private
294
+ * @returns {Distribution} CloudFront distribution
295
+ */
296
+ createCdnDistribution() {
297
+ const distribution = new Distribution(this, 'UNRDFDistribution', {
298
+ defaultBehavior: {
299
+ origin: new RestApiOrigin(this.api),
300
+ viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
301
+ compress: true,
302
+ },
303
+ comment: 'UNRDF API CDN distribution',
304
+ });
305
+
306
+ return distribution;
307
+ }
308
+
309
+ /**
310
+ * Create CloudFormation outputs
311
+ *
312
+ * @private
313
+ * @param {Object} config - Stack configuration
314
+ */
315
+ createOutputs(config) {
316
+ new CfnOutput(this, 'ApiEndpoint', {
317
+ value: this.api.url,
318
+ description: 'API Gateway endpoint URL',
319
+ exportName: `unrdf-api-endpoint-${config.environment}`,
320
+ });
321
+
322
+ new CfnOutput(this, 'TriplesTableName', {
323
+ value: this.triplesTable.tableName,
324
+ description: 'DynamoDB triples table name',
325
+ exportName: `unrdf-triples-table-${config.environment}`,
326
+ });
327
+
328
+ new CfnOutput(this, 'QueryFunctionArn', {
329
+ value: this.queryFunction.functionArn,
330
+ description: 'Query Lambda function ARN',
331
+ exportName: `unrdf-query-function-${config.environment}`,
332
+ });
333
+
334
+ if (this.distribution) {
335
+ new CfnOutput(this, 'DistributionDomain', {
336
+ value: this.distribution.distributionDomainName,
337
+ description: 'CloudFront distribution domain',
338
+ exportName: `unrdf-cdn-domain-${config.environment}`,
339
+ });
340
+ }
341
+ }
342
+ }
343
+
344
+ /**
345
+ * Create UNRDF stack from configuration
346
+ *
347
+ * @param {import('constructs').Construct} scope - CDK app
348
+ * @param {string} id - Stack identifier
349
+ * @param {Object} config - Stack configuration
350
+ * @returns {UNRDFStack} Initialized stack
351
+ *
352
+ * @example
353
+ * ```javascript
354
+ * const stack = createUNRDFStack(app, 'Production', {
355
+ * environment: 'prod',
356
+ * memorySizeMb: 2048,
357
+ * enableCdn: true
358
+ * });
359
+ * ```
360
+ */
361
+ export function createUNRDFStack(scope, id, config) {
362
+ return new UNRDFStack(scope, id, { config });
363
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @fileoverview Deployment Module Exports
3
+ * @module serverless/deploy
4
+ */
5
+
6
+ export {
7
+ LambdaBundler,
8
+ createDefaultBundlerConfig,
9
+ bundleUNRDFFunctions,
10
+ } from './lambda-bundler.mjs';
@@ -0,0 +1,310 @@
1
+ /**
2
+ * @fileoverview Lambda Function Bundler - esbuild integration for UNRDF
3
+ *
4
+ * @description
5
+ * Bundles UNRDF applications into optimized Lambda deployment packages using esbuild.
6
+ * Handles dependency resolution, minification, and tree-shaking for minimal cold starts.
7
+ *
8
+ * @module serverless/deploy/lambda-bundler
9
+ * @version 1.0.0
10
+ * @license MIT
11
+ */
12
+
13
+ import { build } from 'esbuild';
14
+ import { createWriteStream, promises as fs } from 'node:fs';
15
+ import { join, dirname as _dirname } from 'node:path';
16
+ import { createGzip } from 'node:zlib';
17
+ import { pipeline } from 'node:stream/promises';
18
+ import { z } from 'zod';
19
+
20
+ /**
21
+ * Bundler configuration schema
22
+ * @typedef {Object} BundlerConfig
23
+ * @property {string} entryPoint - Entry point file path
24
+ * @property {string} outDir - Output directory
25
+ * @property {boolean} minify - Enable minification
26
+ * @property {boolean} sourcemap - Generate source maps
27
+ * @property {string[]} external - External dependencies
28
+ * @property {Object} define - Environment variable definitions
29
+ */
30
+ const BundlerConfigSchema = z.object({
31
+ entryPoint: z.string(),
32
+ outDir: z.string(),
33
+ minify: z.boolean().default(true),
34
+ sourcemap: z.boolean().default(false),
35
+ external: z.array(z.string()).default(['@aws-sdk/*']),
36
+ define: z.record(z.string()).default({}),
37
+ platform: z.enum(['node', 'browser']).default('node'),
38
+ target: z.string().default('node20'),
39
+ });
40
+
41
+ /**
42
+ * Bundle metadata
43
+ * @typedef {Object} BundleMetadata
44
+ * @property {string} outputPath - Bundled file path
45
+ * @property {number} sizeBytes - Bundle size in bytes
46
+ * @property {number} gzipSizeBytes - Gzipped size in bytes
47
+ * @property {string[]} dependencies - Included dependencies
48
+ * @property {number} buildTimeMs - Build duration in milliseconds
49
+ */
50
+
51
+ /**
52
+ * Lambda Function Bundler
53
+ *
54
+ * @class LambdaBundler
55
+ *
56
+ * @description
57
+ * High-performance bundler for Lambda functions with:
58
+ * - Tree-shaking for minimal bundle size
59
+ * - Automatic external dependency detection
60
+ * - Source map generation for debugging
61
+ * - Gzip compression for deployment
62
+ *
63
+ * @example
64
+ * ```javascript
65
+ * const bundler = new LambdaBundler({
66
+ * entryPoint: './src/handler.mjs',
67
+ * outDir: './dist/lambda',
68
+ * minify: true
69
+ * });
70
+ *
71
+ * const metadata = await bundler.bundle();
72
+ * console.log(`Bundle size: ${metadata.sizeBytes} bytes`);
73
+ * ```
74
+ */
75
+ export class LambdaBundler {
76
+ /**
77
+ * Bundler configuration
78
+ * @type {Object}
79
+ * @private
80
+ */
81
+ #config;
82
+
83
+ /**
84
+ * Create Lambda bundler
85
+ *
86
+ * @param {Object} config - Bundler configuration
87
+ */
88
+ constructor(config) {
89
+ this.#config = BundlerConfigSchema.parse(config);
90
+ }
91
+
92
+ /**
93
+ * Bundle Lambda function
94
+ *
95
+ * @returns {Promise<BundleMetadata>} Bundle metadata
96
+ *
97
+ * @throws {Error} If bundling fails
98
+ *
99
+ * @example
100
+ * ```javascript
101
+ * const metadata = await bundler.bundle();
102
+ * ```
103
+ */
104
+ async bundle() {
105
+ const startTime = Date.now();
106
+
107
+ try {
108
+ // Ensure output directory exists
109
+ await fs.mkdir(this.#config.outDir, { recursive: true });
110
+
111
+ // Build with esbuild
112
+ const result = await build({
113
+ entryPoints: [this.#config.entryPoint],
114
+ bundle: true,
115
+ platform: this.#config.platform,
116
+ target: this.#config.target,
117
+ format: 'esm',
118
+ outdir: this.#config.outDir,
119
+ minify: this.#config.minify,
120
+ sourcemap: this.#config.sourcemap,
121
+ external: this.#config.external,
122
+ define: this.#config.define,
123
+ treeShaking: true,
124
+ metafile: true,
125
+ logLevel: 'info',
126
+ });
127
+
128
+ const outputPath = join(this.#config.outDir, 'index.js');
129
+ const stats = await fs.stat(outputPath);
130
+
131
+ // Create gzipped version
132
+ const gzipPath = `${outputPath}.gz`;
133
+ await this.#gzipFile(outputPath, gzipPath);
134
+ const gzipStats = await fs.stat(gzipPath);
135
+
136
+ // Extract dependencies from metafile
137
+ const dependencies = this.#extractDependencies(result.metafile);
138
+
139
+ const buildTimeMs = Date.now() - startTime;
140
+
141
+ return {
142
+ outputPath,
143
+ sizeBytes: stats.size,
144
+ gzipSizeBytes: gzipStats.size,
145
+ dependencies,
146
+ buildTimeMs,
147
+ };
148
+ } catch (error) {
149
+ throw new Error(`Bundle failed: ${error.message}`, { cause: error });
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Bundle multiple Lambda functions
155
+ *
156
+ * @param {Object[]} configs - Array of bundler configurations
157
+ * @returns {Promise<BundleMetadata[]>} Array of bundle metadata
158
+ *
159
+ * @example
160
+ * ```javascript
161
+ * const results = await LambdaBundler.bundleAll([
162
+ * { entryPoint: './src/query.mjs', outDir: './dist/query' },
163
+ * { entryPoint: './src/ingest.mjs', outDir: './dist/ingest' }
164
+ * ]);
165
+ * ```
166
+ */
167
+ static async bundleAll(configs) {
168
+ const bundlers = configs.map(config => new LambdaBundler(config));
169
+ return Promise.all(bundlers.map(bundler => bundler.bundle()));
170
+ }
171
+
172
+ /**
173
+ * Gzip a file
174
+ *
175
+ * @private
176
+ * @param {string} inputPath - Input file path
177
+ * @param {string} outputPath - Output gzip path
178
+ * @returns {Promise<void>}
179
+ */
180
+ async #gzipFile(inputPath, outputPath) {
181
+ const input = (await import('node:fs')).createReadStream(inputPath);
182
+ const output = createWriteStream(outputPath);
183
+ const gzip = createGzip({ level: 9 });
184
+
185
+ await pipeline(input, gzip, output);
186
+ }
187
+
188
+ /**
189
+ * Extract dependencies from esbuild metafile
190
+ *
191
+ * @private
192
+ * @param {Object} metafile - esbuild metafile
193
+ * @returns {string[]} List of dependencies
194
+ */
195
+ #extractDependencies(metafile) {
196
+ const deps = new Set();
197
+
198
+ for (const input of Object.keys(metafile.inputs || {})) {
199
+ if (input.includes('node_modules')) {
200
+ const match = input.match(/node_modules\/(@[^/]+\/[^/]+|[^/]+)/);
201
+ if (match) {
202
+ deps.add(match[1]);
203
+ }
204
+ }
205
+ }
206
+
207
+ return Array.from(deps).sort();
208
+ }
209
+
210
+ /**
211
+ * Analyze bundle size and composition
212
+ *
213
+ * @param {string} metafilePath - Path to esbuild metafile
214
+ * @returns {Promise<Object>} Bundle analysis
215
+ *
216
+ * @example
217
+ * ```javascript
218
+ * const analysis = await LambdaBundler.analyzeBundleSize('./dist/metafile.json');
219
+ * console.log('Largest dependencies:', analysis.largestDeps);
220
+ * ```
221
+ */
222
+ static async analyzeBundleSize(metafilePath) {
223
+ const content = await fs.readFile(metafilePath, 'utf-8');
224
+ const metafile = JSON.parse(content);
225
+
226
+ const inputs = metafile.inputs || {};
227
+ const sizeByModule = {};
228
+
229
+ for (const [path, data] of Object.entries(inputs)) {
230
+ const bytes = data.bytes || 0;
231
+ const moduleName = path.includes('node_modules')
232
+ ? path.match(/node_modules\/(@[^/]+\/[^/]+|[^/]+)/)?.[1] || 'unknown'
233
+ : 'application';
234
+
235
+ sizeByModule[moduleName] = (sizeByModule[moduleName] || 0) + bytes;
236
+ }
237
+
238
+ const sorted = Object.entries(sizeByModule)
239
+ .sort(([, a], [, b]) => b - a)
240
+ .slice(0, 10);
241
+
242
+ const totalSize = Object.values(sizeByModule).reduce((sum, size) => sum + size, 0);
243
+
244
+ return {
245
+ totalSizeBytes: totalSize,
246
+ largestDeps: sorted.map(([name, bytes]) => ({
247
+ name,
248
+ bytes,
249
+ percentage: ((bytes / totalSize) * 100).toFixed(2),
250
+ })),
251
+ moduleCount: Object.keys(sizeByModule).length,
252
+ };
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Create default UNRDF Lambda bundler configuration
258
+ *
259
+ * @param {string} functionName - Lambda function name (query, ingest, etc.)
260
+ * @param {Object} options - Additional options
261
+ * @returns {Object} Bundler configuration
262
+ *
263
+ * @example
264
+ * ```javascript
265
+ * const config = createDefaultBundlerConfig('query', { minify: true });
266
+ * const bundler = new LambdaBundler(config);
267
+ * ```
268
+ */
269
+ export function createDefaultBundlerConfig(functionName, options = {}) {
270
+ return {
271
+ entryPoint: `./src/lambda/${functionName}/index.mjs`,
272
+ outDir: `./dist/lambda/${functionName}`,
273
+ minify: true,
274
+ sourcemap: false,
275
+ external: ['@aws-sdk/*'],
276
+ define: {
277
+ 'process.env.NODE_ENV': '"production"',
278
+ 'process.env.FUNCTION_NAME': `"${functionName}"`,
279
+ },
280
+ ...options,
281
+ };
282
+ }
283
+
284
+ /**
285
+ * Bundle all UNRDF Lambda functions
286
+ *
287
+ * @param {Object} options - Bundle options
288
+ * @returns {Promise<Map<string, BundleMetadata>>} Map of function name to metadata
289
+ *
290
+ * @example
291
+ * ```javascript
292
+ * const results = await bundleUNRDFFunctions({ minify: true });
293
+ * for (const [name, metadata] of results) {
294
+ * console.log(`${name}: ${metadata.sizeBytes} bytes`);
295
+ * }
296
+ * ```
297
+ */
298
+ export async function bundleUNRDFFunctions(options = {}) {
299
+ const functions = ['query', 'ingest'];
300
+ const results = new Map();
301
+
302
+ for (const fn of functions) {
303
+ const config = createDefaultBundlerConfig(fn, options);
304
+ const bundler = new LambdaBundler(config);
305
+ const metadata = await bundler.bundle();
306
+ results.set(fn, metadata);
307
+ }
308
+
309
+ return results;
310
+ }