eoapi-cdk 8.1.1 → 8.2.1

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 (62) hide show
  1. package/.jsii +698 -26
  2. package/lib/bastion-host/index.js +1 -1
  3. package/lib/database/index.d.ts +1 -0
  4. package/lib/database/index.js +5 -5
  5. package/lib/index.d.ts +2 -0
  6. package/lib/index.js +3 -1
  7. package/lib/ingestor-api/index.js +1 -1
  8. package/lib/stac-api/index.js +1 -1
  9. package/lib/stac-browser/index.js +1 -1
  10. package/lib/stac-item-loader/index.d.ts +337 -0
  11. package/lib/stac-item-loader/index.js +255 -0
  12. package/lib/stac-item-loader/runtime/Dockerfile +18 -0
  13. package/lib/stac-item-loader/runtime/pyproject.toml +17 -0
  14. package/lib/stac-item-loader/runtime/src/stac_item_loader/handler.py +241 -0
  15. package/lib/stactools-item-generator/index.d.ts +258 -0
  16. package/lib/stactools-item-generator/index.js +208 -0
  17. package/lib/stactools-item-generator/runtime/Dockerfile +20 -0
  18. package/lib/stactools-item-generator/runtime/pyproject.toml +16 -0
  19. package/lib/stactools-item-generator/runtime/src/stactools_item_generator/__init__.py +2 -0
  20. package/lib/stactools-item-generator/runtime/src/stactools_item_generator/handler.py +176 -0
  21. package/lib/stactools-item-generator/runtime/src/stactools_item_generator/item.py +77 -0
  22. package/lib/tipg-api/index.js +1 -1
  23. package/lib/titiler-pgstac-api/index.js +1 -1
  24. package/package.json +1 -1
  25. package/pyproject.toml +45 -0
  26. package/uv.lock +1065 -0
  27. package/.devcontainer/devcontainer.json +0 -4
  28. package/.github/pull_request_template.md +0 -4
  29. package/.github/workflows/build.yaml +0 -73
  30. package/.github/workflows/build_and_release.yaml +0 -13
  31. package/.github/workflows/conventional-pr.yaml +0 -26
  32. package/.github/workflows/deploy.yaml +0 -84
  33. package/.github/workflows/distribute.yaml +0 -46
  34. package/.github/workflows/docs.yaml +0 -26
  35. package/.github/workflows/lint.yaml +0 -26
  36. package/.github/workflows/tox.yaml +0 -26
  37. package/.nvmrc +0 -1
  38. package/.pre-commit-config.yaml +0 -23
  39. package/CHANGELOG.md +0 -471
  40. package/diagrams/bastion_diagram.excalidraw +0 -1416
  41. package/diagrams/bastion_diagram.png +0 -0
  42. package/diagrams/ingestor_diagram.excalidraw +0 -2274
  43. package/diagrams/ingestor_diagram.png +0 -0
  44. package/integration_tests/cdk/README.md +0 -55
  45. package/integration_tests/cdk/app.py +0 -186
  46. package/integration_tests/cdk/cdk.json +0 -32
  47. package/integration_tests/cdk/config.py +0 -52
  48. package/integration_tests/cdk/package-lock.json +0 -42
  49. package/integration_tests/cdk/package.json +0 -7
  50. package/integration_tests/cdk/requirements.txt +0 -7
  51. package/lib/database/lambda/package-lock.json +0 -1324
  52. package/lib/ingestor-api/runtime/tests/conftest.py +0 -270
  53. package/lib/ingestor-api/runtime/tests/test_collection.py +0 -87
  54. package/lib/ingestor-api/runtime/tests/test_collection_endpoint.py +0 -41
  55. package/lib/ingestor-api/runtime/tests/test_ingestor.py +0 -60
  56. package/lib/ingestor-api/runtime/tests/test_registration.py +0 -207
  57. package/lib/ingestor-api/runtime/tests/test_utils.py +0 -35
  58. package/lib/ingestor-api/runtime/tests/test_validators.py +0 -164
  59. package/ruff.toml +0 -23
  60. package/tox.ini +0 -16
  61. package/tsconfig.tsbuildinfo +0 -1
  62. /package/lib/{ingestor-api/runtime/tests → stac-item-loader/runtime/src/stac_item_loader}/__init__.py +0 -0
@@ -0,0 +1,337 @@
1
+ import { aws_ec2 as ec2, aws_lambda as lambda, aws_sqs as sqs, aws_sns as sns } from "aws-cdk-lib";
2
+ import { Construct } from "constructs";
3
+ import { PgStacDatabase } from "../database";
4
+ import { CustomLambdaFunctionProps } from "../utils";
5
+ /**
6
+ * Configuration properties for the StacItemLoader construct.
7
+ *
8
+ * The StacItemLoader is part of a two-phase serverless STAC ingestion pipeline
9
+ * that loads STAC items into a pgstac database. This construct creates
10
+ * the infrastructure for receiving STAC items from multiple sources:
11
+ * 1. SNS messages containing STAC metadata (direct ingestion)
12
+ * 2. S3 event notifications for STAC items uploaded to S3 buckets
13
+ *
14
+ * Items from both sources are batched and inserted into PostgreSQL with the pgstac extension.
15
+ *
16
+ * @example
17
+ * const loader = new StacItemLoader(this, 'ItemLoader', {
18
+ * pgstacDb: database,
19
+ * batchSize: 1000,
20
+ * maxBatchingWindowMinutes: 1,
21
+ * lambdaTimeoutSeconds: 300
22
+ * });
23
+ */
24
+ export interface StacItemLoaderProps {
25
+ /**
26
+ * The PgSTAC database instance to load items into.
27
+ *
28
+ * This database must have the pgstac extension installed and be properly
29
+ * configured with collections before items can be loaded. The loader will
30
+ * use AWS Secrets Manager to securely access database credentials.
31
+ */
32
+ readonly pgstacDb: PgStacDatabase;
33
+ /**
34
+ * VPC into which the lambda should be deployed.
35
+ */
36
+ readonly vpc?: ec2.IVpc;
37
+ /**
38
+ * Subnet into which the lambda should be deployed.
39
+ */
40
+ readonly subnetSelection?: ec2.SubnetSelection;
41
+ /**
42
+ * The lambda runtime to use for the item loading function.
43
+ *
44
+ * The function is implemented in Python and uses pypgstac for database
45
+ * operations. Ensure the runtime version is compatible with the pgstac
46
+ * version specified in the database configuration.
47
+ *
48
+ * @default lambda.Runtime.PYTHON_3_11
49
+ */
50
+ readonly lambdaRuntime?: lambda.Runtime;
51
+ /**
52
+ * The timeout for the item load lambda in seconds.
53
+ *
54
+ * This should accommodate the time needed to process up to `batchSize`
55
+ * items and perform database insertions. The SQS visibility timeout
56
+ * will be set to this value plus 10 seconds.
57
+ *
58
+ * @default 300
59
+ */
60
+ readonly lambdaTimeoutSeconds?: number;
61
+ /**
62
+ * Memory size for the lambda function in MB.
63
+ *
64
+ * Higher memory allocation may improve performance when processing
65
+ * large batches of STAC items, especially for memory-intensive
66
+ * database operations.
67
+ *
68
+ * @default 1024
69
+ */
70
+ readonly memorySize?: number;
71
+ /**
72
+ * SQS batch size for lambda event source.
73
+ *
74
+ * This determines the maximum number of STAC items that will be
75
+ * processed together in a single lambda invocation. Larger batch
76
+ * sizes improve database insertion efficiency but require more
77
+ * memory and longer processing time.
78
+ *
79
+ * **Batching Behavior**: SQS will wait to accumulate up to this many
80
+ * messages before triggering the Lambda, OR until the maxBatchingWindow
81
+ * timeout is reached, whichever comes first. This creates an efficient
82
+ * balance between throughput and latency.
83
+ *
84
+ * @default 500
85
+ */
86
+ readonly batchSize?: number;
87
+ /**
88
+ * Maximum batching window in minutes.
89
+ *
90
+ * Even if the batch size isn't reached, the lambda will be triggered
91
+ * after this time period to ensure timely processing of items.
92
+ * This prevents items from waiting indefinitely in low-volume scenarios.
93
+ *
94
+ * **Important**: This timeout works in conjunction with batchSize - SQS
95
+ * will trigger the Lambda when EITHER the batch size is reached OR this
96
+ * time window expires, ensuring items are processed in a timely manner
97
+ * regardless of volume.
98
+ *
99
+ * @default 1
100
+ */
101
+ readonly maxBatchingWindowMinutes?: number;
102
+ /**
103
+ * Maximum concurrent executions for the StacItemLoader Lambda function
104
+ *
105
+ * This limit will be applied to the Lambda function and will control how
106
+ * many concurrent batches will be released from the SQS queue.
107
+ *
108
+ * @default 2
109
+ */
110
+ readonly maxConcurrency?: number;
111
+ /**
112
+ * Additional environment variables for the lambda function.
113
+ *
114
+ * These will be merged with the default environment variables including
115
+ * PGSTAC_SECRET_ARN. Use this for custom configuration or debugging flags.
116
+ */
117
+ readonly environment?: {
118
+ [key: string]: string;
119
+ };
120
+ /**
121
+ * Can be used to override the default lambda function properties.
122
+ *
123
+ * @default - defined in the construct.
124
+ */
125
+ readonly lambdaFunctionOptions?: CustomLambdaFunctionProps;
126
+ }
127
+ /**
128
+ * AWS CDK Construct for STAC Item Loading Infrastructure
129
+ *
130
+ * The StacItemLoader creates a serverless, event-driven system for loading
131
+ * STAC (SpatioTemporal Asset Catalog) items into a PostgreSQL database with
132
+ * the pgstac extension. This construct supports multiple ingestion pathways
133
+ * for flexible STAC item loading.
134
+ *
135
+ * ## Architecture Overview
136
+ *
137
+ * This construct creates the following AWS resources:
138
+ * - **SNS Topic**: Entry point for STAC items and S3 event notifications
139
+ * - **SQS Queue**: Buffers and batches messages before processing (60-second visibility timeout)
140
+ * - **Dead Letter Queue**: Captures failed loading attempts after 5 retries
141
+ * - **Lambda Function**: Python function that processes batches and inserts items into pgstac
142
+ *
143
+ * ## Data Flow
144
+ *
145
+ * The loader supports two primary data ingestion patterns:
146
+ *
147
+ * ### Direct STAC Item Publishing
148
+ * 1. STAC items (JSON) are published directly to the SNS topic in message bodies
149
+ * 2. The SQS queue collects messages and batches them (up to {batchSize} items or 1 minute window)
150
+ * 3. The Lambda function receives batches, validates items, and inserts into pgstac
151
+ *
152
+ * ### S3 Event-Driven Loading
153
+ * 1. An S3 bucket is configured to send notifications to the SNS topic when json files are created
154
+ * 2. STAC items are uploaded to S3 buckets as JSON/GeoJSON files
155
+ * 3. S3 event notifications are sent to the SNS topic when items are uploaded
156
+ * 4. The Lambda function receives S3 events in the SQS message batch, fetches items from S3, and loads into pgstac
157
+ *
158
+ * ## Batching Behavior
159
+ *
160
+ * The SQS-to-Lambda integration uses intelligent batching to optimize performance:
161
+ *
162
+ * - **Batch Size**: Lambda waits to receive up to `batchSize` messages (default: 500)
163
+ * - **Batching Window**: If fewer than `batchSize` messages are available, Lambda
164
+ * triggers after `maxBatchingWindow` minutes (default: 1 minute)
165
+ * - **Trigger Condition**: Lambda executes when EITHER condition is met first
166
+ * - **Concurrency**: Limited to `maxConcurrency` concurrent executions to prevent database overload
167
+ * - **Partial Failures**: Uses `reportBatchItemFailures` to retry only failed items
168
+ *
169
+ * This approach balances throughput (larger batches = fewer database connections)
170
+ * with latency (time-based triggers prevent indefinite waiting).
171
+ *
172
+ * ## Error Handling and Dead Letter Queue
173
+ *
174
+ * Failed messages are sent to the dead letter queue after 5 processing attempts.
175
+ * **Important**: This construct provides NO automated handling of dead letter queue
176
+ * messages - monitoring, inspection, and reprocessing of failed items is the
177
+ * responsibility of the implementing application.
178
+ *
179
+ * Consider implementing:
180
+ * - CloudWatch alarms on dead letter queue depth
181
+ * - Manual or automated reprocessing workflows
182
+ * - Logging and alerting for failed items
183
+ * - Regular cleanup of old dead letter messages (14-day retention)
184
+ *
185
+ * ## Operational Characteristics
186
+ *
187
+ * - **Scalability**: Lambda scales automatically based on queue depth
188
+ * - **Reliability**: Dead letter queue captures failures for debugging
189
+ * - **Efficiency**: Batching optimizes database operations for high throughput
190
+ * - **Security**: Database credentials accessed via AWS Secrets Manager
191
+ * - **Observability**: CloudWatch logs retained for one week
192
+ *
193
+ * ## Prerequisites
194
+ *
195
+ * Before using this construct, ensure:
196
+ * - The pgstac database has collections loaded (items require existing collection IDs)
197
+ * - Database credentials are stored in AWS Secrets Manager
198
+ * - The pgstac extension is properly installed and configured
199
+ *
200
+ * ## Usage Example
201
+ *
202
+ * ```typescript
203
+ * // Create database first
204
+ * const database = new PgStacDatabase(this, 'Database', {
205
+ * pgstacVersion: '0.9.5'
206
+ * });
207
+ *
208
+ * // Create item loader
209
+ * const loader = new StacItemLoader(this, 'ItemLoader', {
210
+ * pgstacDb: database,
211
+ * batchSize: 1000, // Process up to 1000 items per batch
212
+ * maxBatchingWindowMinutes: 1, // Wait max 1 minute to fill batch
213
+ * lambdaTimeoutSeconds: 300 // Allow up to 300 seconds for database operations
214
+ * });
215
+ *
216
+ * // The topic ARN can be used by other services to publish items
217
+ * new CfnOutput(this, 'LoaderTopicArn', {
218
+ * value: loader.topic.topicArn
219
+ * });
220
+ * ```
221
+ *
222
+ * ## Direct Item Publishing
223
+ *
224
+ * External services can publish STAC items directly to the topic:
225
+ *
226
+ * ```bash
227
+ * aws sns publish --topic-arn $ITEM_LOAD_TOPIC --message '{
228
+ * "type": "Feature",
229
+ * "stac_version": "1.0.0",
230
+ * "id": "example-item",
231
+ * "properties": {"datetime": "2021-01-01T00:00:00Z"},
232
+ * "geometry": {"type": "Polygon", "coordinates": [...]},
233
+ * "collection": "example-collection"
234
+ * }'
235
+ * ```
236
+ *
237
+ * ## S3 Event Configuration
238
+ *
239
+ * To enable S3 event-driven loading, configure S3 bucket notifications to send
240
+ * events to the SNS topic when STAC items (.json or .geojson files) are uploaded:
241
+ *
242
+ * ```typescript
243
+ * // Configure S3 bucket to send notifications to the loader topic
244
+ * bucket.addEventNotification(
245
+ * s3.EventType.OBJECT_CREATED,
246
+ * new s3n.SnsDestination(loader.topic),
247
+ * { suffix: '.json' }
248
+ * );
249
+ *
250
+ * bucket.addEventNotification(
251
+ * s3.EventType.OBJECT_CREATED,
252
+ * new s3n.SnsDestination(loader.topic),
253
+ * { suffix: '.geojson' }
254
+ * );
255
+ * ```
256
+ *
257
+ * When STAC items are uploaded to the configured S3 bucket, the loader will:
258
+ * 1. Receive S3 event notifications via SNS
259
+ * 2. Fetch the STAC item JSON from S3
260
+ * 3. Validate and load the item into the pgstac database
261
+ *
262
+ * ## Monitoring and Troubleshooting
263
+ *
264
+ * - Monitor Lambda logs: `/aws/lambda/{FunctionName}`
265
+ * - **Dead Letter Queue**: Check for failed items - **no automated handling provided**
266
+ * - Use batch item failure reporting for partial batch processing
267
+ * - CloudWatch metrics available for queue depth and Lambda performance
268
+ *
269
+ * ### Dead Letter Queue Management
270
+ *
271
+ * Applications must implement their own dead letter queue monitoring:
272
+ *
273
+ * ```typescript
274
+ * // Example: CloudWatch alarm for dead letter queue depth
275
+ * new cloudwatch.Alarm(this, 'DeadLetterAlarm', {
276
+ * metric: loader.deadLetterQueue.metricApproximateNumberOfVisibleMessages(),
277
+ * threshold: 1,
278
+ * evaluationPeriods: 1
279
+ * });
280
+ *
281
+ * // Example: Lambda to reprocess dead letter messages
282
+ * const reprocessFunction = new lambda.Function(this, 'Reprocess', {
283
+ * // Implementation to fetch and republish failed messages
284
+ * });
285
+ * ```
286
+ *
287
+ */
288
+ export declare class StacItemLoader extends Construct {
289
+ /**
290
+ * The SNS topic that receives STAC items and S3 event notifications for loading.
291
+ *
292
+ * This topic serves as the entry point for two types of events:
293
+ * 1. Direct STAC item JSON documents published by external services
294
+ * 2. S3 event notifications when STAC items are uploaded to configured buckets
295
+ *
296
+ * The topic fans out to the SQS queue for batched processing.
297
+ */
298
+ readonly topic: sns.Topic;
299
+ /**
300
+ * The SQS queue that buffers messages before processing.
301
+ *
302
+ * This queue collects both direct STAC items from SNS and S3 event
303
+ * notifications, batching them for efficient database operations.
304
+ * Configured with a visibility timeout that accommodates Lambda
305
+ * processing time plus buffer.
306
+ */
307
+ readonly queue: sqs.Queue;
308
+ /**
309
+ * Dead letter queue for failed item loading attempts.
310
+ *
311
+ * Messages that fail processing after 5 attempts are sent here
312
+ * for inspection and potential replay. Retains messages for 14 days
313
+ * to allow for debugging and manual intervention.
314
+ *
315
+ * **User Responsibility**: This construct provides NO automated monitoring,
316
+ * alerting, or reprocessing of dead letter queue messages. Applications
317
+ * using this construct must implement their own:
318
+ * - Dead letter queue depth monitoring and alerting
319
+ * - Failed message inspection and debugging workflows
320
+ * - Manual or automated reprocessing mechanisms
321
+ * - Cleanup procedures for old failed messages
322
+ */
323
+ readonly deadLetterQueue: sqs.Queue;
324
+ /**
325
+ * The Lambda function that loads STAC items into the pgstac database.
326
+ *
327
+ * This Python function receives batches of messages from SQS and processes
328
+ * them based on their type:
329
+ * - Direct STAC items: Validates and loads directly into pgstac
330
+ * - S3 events: Fetches STAC items from S3, validates, and loads into pgstac
331
+ *
332
+ * The function connects to PostgreSQL using credentials from Secrets Manager
333
+ * and uses pypgstac for efficient database operations.
334
+ */
335
+ readonly lambdaFunction: lambda.Function;
336
+ constructor(scope: Construct, id: string, props: StacItemLoaderProps);
337
+ }
@@ -0,0 +1,255 @@
1
+ "use strict";
2
+ var _a;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.StacItemLoader = void 0;
5
+ const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
6
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
7
+ const constructs_1 = require("constructs");
8
+ const path = require("path");
9
+ /**
10
+ * AWS CDK Construct for STAC Item Loading Infrastructure
11
+ *
12
+ * The StacItemLoader creates a serverless, event-driven system for loading
13
+ * STAC (SpatioTemporal Asset Catalog) items into a PostgreSQL database with
14
+ * the pgstac extension. This construct supports multiple ingestion pathways
15
+ * for flexible STAC item loading.
16
+ *
17
+ * ## Architecture Overview
18
+ *
19
+ * This construct creates the following AWS resources:
20
+ * - **SNS Topic**: Entry point for STAC items and S3 event notifications
21
+ * - **SQS Queue**: Buffers and batches messages before processing (60-second visibility timeout)
22
+ * - **Dead Letter Queue**: Captures failed loading attempts after 5 retries
23
+ * - **Lambda Function**: Python function that processes batches and inserts items into pgstac
24
+ *
25
+ * ## Data Flow
26
+ *
27
+ * The loader supports two primary data ingestion patterns:
28
+ *
29
+ * ### Direct STAC Item Publishing
30
+ * 1. STAC items (JSON) are published directly to the SNS topic in message bodies
31
+ * 2. The SQS queue collects messages and batches them (up to {batchSize} items or 1 minute window)
32
+ * 3. The Lambda function receives batches, validates items, and inserts into pgstac
33
+ *
34
+ * ### S3 Event-Driven Loading
35
+ * 1. An S3 bucket is configured to send notifications to the SNS topic when json files are created
36
+ * 2. STAC items are uploaded to S3 buckets as JSON/GeoJSON files
37
+ * 3. S3 event notifications are sent to the SNS topic when items are uploaded
38
+ * 4. The Lambda function receives S3 events in the SQS message batch, fetches items from S3, and loads into pgstac
39
+ *
40
+ * ## Batching Behavior
41
+ *
42
+ * The SQS-to-Lambda integration uses intelligent batching to optimize performance:
43
+ *
44
+ * - **Batch Size**: Lambda waits to receive up to `batchSize` messages (default: 500)
45
+ * - **Batching Window**: If fewer than `batchSize` messages are available, Lambda
46
+ * triggers after `maxBatchingWindow` minutes (default: 1 minute)
47
+ * - **Trigger Condition**: Lambda executes when EITHER condition is met first
48
+ * - **Concurrency**: Limited to `maxConcurrency` concurrent executions to prevent database overload
49
+ * - **Partial Failures**: Uses `reportBatchItemFailures` to retry only failed items
50
+ *
51
+ * This approach balances throughput (larger batches = fewer database connections)
52
+ * with latency (time-based triggers prevent indefinite waiting).
53
+ *
54
+ * ## Error Handling and Dead Letter Queue
55
+ *
56
+ * Failed messages are sent to the dead letter queue after 5 processing attempts.
57
+ * **Important**: This construct provides NO automated handling of dead letter queue
58
+ * messages - monitoring, inspection, and reprocessing of failed items is the
59
+ * responsibility of the implementing application.
60
+ *
61
+ * Consider implementing:
62
+ * - CloudWatch alarms on dead letter queue depth
63
+ * - Manual or automated reprocessing workflows
64
+ * - Logging and alerting for failed items
65
+ * - Regular cleanup of old dead letter messages (14-day retention)
66
+ *
67
+ * ## Operational Characteristics
68
+ *
69
+ * - **Scalability**: Lambda scales automatically based on queue depth
70
+ * - **Reliability**: Dead letter queue captures failures for debugging
71
+ * - **Efficiency**: Batching optimizes database operations for high throughput
72
+ * - **Security**: Database credentials accessed via AWS Secrets Manager
73
+ * - **Observability**: CloudWatch logs retained for one week
74
+ *
75
+ * ## Prerequisites
76
+ *
77
+ * Before using this construct, ensure:
78
+ * - The pgstac database has collections loaded (items require existing collection IDs)
79
+ * - Database credentials are stored in AWS Secrets Manager
80
+ * - The pgstac extension is properly installed and configured
81
+ *
82
+ * ## Usage Example
83
+ *
84
+ * ```typescript
85
+ * // Create database first
86
+ * const database = new PgStacDatabase(this, 'Database', {
87
+ * pgstacVersion: '0.9.5'
88
+ * });
89
+ *
90
+ * // Create item loader
91
+ * const loader = new StacItemLoader(this, 'ItemLoader', {
92
+ * pgstacDb: database,
93
+ * batchSize: 1000, // Process up to 1000 items per batch
94
+ * maxBatchingWindowMinutes: 1, // Wait max 1 minute to fill batch
95
+ * lambdaTimeoutSeconds: 300 // Allow up to 300 seconds for database operations
96
+ * });
97
+ *
98
+ * // The topic ARN can be used by other services to publish items
99
+ * new CfnOutput(this, 'LoaderTopicArn', {
100
+ * value: loader.topic.topicArn
101
+ * });
102
+ * ```
103
+ *
104
+ * ## Direct Item Publishing
105
+ *
106
+ * External services can publish STAC items directly to the topic:
107
+ *
108
+ * ```bash
109
+ * aws sns publish --topic-arn $ITEM_LOAD_TOPIC --message '{
110
+ * "type": "Feature",
111
+ * "stac_version": "1.0.0",
112
+ * "id": "example-item",
113
+ * "properties": {"datetime": "2021-01-01T00:00:00Z"},
114
+ * "geometry": {"type": "Polygon", "coordinates": [...]},
115
+ * "collection": "example-collection"
116
+ * }'
117
+ * ```
118
+ *
119
+ * ## S3 Event Configuration
120
+ *
121
+ * To enable S3 event-driven loading, configure S3 bucket notifications to send
122
+ * events to the SNS topic when STAC items (.json or .geojson files) are uploaded:
123
+ *
124
+ * ```typescript
125
+ * // Configure S3 bucket to send notifications to the loader topic
126
+ * bucket.addEventNotification(
127
+ * s3.EventType.OBJECT_CREATED,
128
+ * new s3n.SnsDestination(loader.topic),
129
+ * { suffix: '.json' }
130
+ * );
131
+ *
132
+ * bucket.addEventNotification(
133
+ * s3.EventType.OBJECT_CREATED,
134
+ * new s3n.SnsDestination(loader.topic),
135
+ * { suffix: '.geojson' }
136
+ * );
137
+ * ```
138
+ *
139
+ * When STAC items are uploaded to the configured S3 bucket, the loader will:
140
+ * 1. Receive S3 event notifications via SNS
141
+ * 2. Fetch the STAC item JSON from S3
142
+ * 3. Validate and load the item into the pgstac database
143
+ *
144
+ * ## Monitoring and Troubleshooting
145
+ *
146
+ * - Monitor Lambda logs: `/aws/lambda/{FunctionName}`
147
+ * - **Dead Letter Queue**: Check for failed items - **no automated handling provided**
148
+ * - Use batch item failure reporting for partial batch processing
149
+ * - CloudWatch metrics available for queue depth and Lambda performance
150
+ *
151
+ * ### Dead Letter Queue Management
152
+ *
153
+ * Applications must implement their own dead letter queue monitoring:
154
+ *
155
+ * ```typescript
156
+ * // Example: CloudWatch alarm for dead letter queue depth
157
+ * new cloudwatch.Alarm(this, 'DeadLetterAlarm', {
158
+ * metric: loader.deadLetterQueue.metricApproximateNumberOfVisibleMessages(),
159
+ * threshold: 1,
160
+ * evaluationPeriods: 1
161
+ * });
162
+ *
163
+ * // Example: Lambda to reprocess dead letter messages
164
+ * const reprocessFunction = new lambda.Function(this, 'Reprocess', {
165
+ * // Implementation to fetch and republish failed messages
166
+ * });
167
+ * ```
168
+ *
169
+ */
170
+ class StacItemLoader extends constructs_1.Construct {
171
+ constructor(scope, id, props) {
172
+ super(scope, id);
173
+ const timeoutSeconds = props.lambdaTimeoutSeconds ?? 300;
174
+ const lambdaRuntime = props.lambdaRuntime ?? aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_11;
175
+ const maxConcurrency = props.maxConcurrency ?? 2;
176
+ // Create dead letter queue
177
+ this.deadLetterQueue = new aws_cdk_lib_1.aws_sqs.Queue(this, "DeadLetterQueue", {
178
+ retentionPeriod: aws_cdk_lib_1.Duration.days(14),
179
+ });
180
+ // Create main queue
181
+ this.queue = new aws_cdk_lib_1.aws_sqs.Queue(this, "Queue", {
182
+ visibilityTimeout: aws_cdk_lib_1.Duration.seconds(timeoutSeconds + 10),
183
+ encryption: aws_cdk_lib_1.aws_sqs.QueueEncryption.SQS_MANAGED,
184
+ deadLetterQueue: {
185
+ maxReceiveCount: 5,
186
+ queue: this.deadLetterQueue,
187
+ },
188
+ });
189
+ // Create SNS topic
190
+ this.topic = new aws_cdk_lib_1.aws_sns.Topic(this, "Topic", {
191
+ displayName: `${id}-StacItemLoaderTopic`,
192
+ });
193
+ // Subscribe the queue to the topic
194
+ this.topic.addSubscription(new aws_cdk_lib_1.aws_sns_subscriptions.SqsSubscription(this.queue));
195
+ // Create the lambda function
196
+ this.lambdaFunction = new aws_cdk_lib_1.aws_lambda.Function(this, "Function", {
197
+ runtime: lambdaRuntime,
198
+ handler: "stac_item_loader.handler.handler",
199
+ vpc: props.vpc,
200
+ vpcSubnets: props.subnetSelection,
201
+ code: aws_cdk_lib_1.aws_lambda.Code.fromDockerBuild(path.join(__dirname, ".."), {
202
+ file: "stac-item-loader/runtime/Dockerfile",
203
+ platform: "linux/amd64",
204
+ buildArgs: {
205
+ PYTHON_VERSION: lambdaRuntime.toString().replace("python", ""),
206
+ PGSTAC_VERSION: props.pgstacDb.pgstacVersion,
207
+ },
208
+ }),
209
+ memorySize: props.memorySize ?? 1024,
210
+ timeout: aws_cdk_lib_1.Duration.seconds(timeoutSeconds),
211
+ reservedConcurrentExecutions: maxConcurrency,
212
+ logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.ONE_WEEK,
213
+ environment: {
214
+ PGSTAC_SECRET_ARN: props.pgstacDb.pgstacSecret.secretArn,
215
+ ...props.environment,
216
+ },
217
+ // overwrites defaults with user-provided configurable properties
218
+ ...props.lambdaFunctionOptions,
219
+ });
220
+ // Grant permissions to read the database secret
221
+ props.pgstacDb.pgstacSecret.grantRead(this.lambdaFunction);
222
+ // Add SQS event source to the lambda
223
+ this.lambdaFunction.addEventSource(new aws_cdk_lib_1.aws_lambda_event_sources.SqsEventSource(this.queue, {
224
+ batchSize: props.batchSize ?? 500,
225
+ maxBatchingWindow: aws_cdk_lib_1.Duration.minutes(props.maxBatchingWindowMinutes ?? 1),
226
+ maxConcurrency: maxConcurrency,
227
+ reportBatchItemFailures: true,
228
+ }));
229
+ // Create outputs
230
+ new aws_cdk_lib_1.CfnOutput(this, "TopicArn", {
231
+ value: this.topic.topicArn,
232
+ description: "ARN of the StacItemLoader SNS Topic",
233
+ exportName: "stac-item-loader-topic-arn",
234
+ });
235
+ new aws_cdk_lib_1.CfnOutput(this, "QueueUrl", {
236
+ value: this.queue.queueUrl,
237
+ description: "URL of the StacItemLoader SQS Queue",
238
+ exportName: "stac-item-loader-queue-url",
239
+ });
240
+ new aws_cdk_lib_1.CfnOutput(this, "DeadLetterQueueUrl", {
241
+ value: this.deadLetterQueue.queueUrl,
242
+ description: "URL of the StacItemLoader Dead Letter Queue",
243
+ exportName: "stac-item-loader-deadletter-queue-url",
244
+ });
245
+ new aws_cdk_lib_1.CfnOutput(this, "FunctionName", {
246
+ value: this.lambdaFunction.functionName,
247
+ description: "Name of the StacItemLoader Lambda Function",
248
+ exportName: "stac-item-loader-function-name",
249
+ });
250
+ }
251
+ }
252
+ exports.StacItemLoader = StacItemLoader;
253
+ _a = JSII_RTTI_SYMBOL_1;
254
+ StacItemLoader[_a] = { fqn: "eoapi-cdk.StacItemLoader", version: "8.2.1" };
255
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLDZDQVVxQjtBQUNyQiwyQ0FBdUM7QUFFdkMsNkJBQTZCO0FBc0k3Qjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWdLRztBQUNILE1BQWEsY0FBZSxTQUFRLHNCQUFTO0lBb0QzQyxZQUFZLEtBQWdCLEVBQUUsRUFBVSxFQUFFLEtBQTBCO1FBQ2xFLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFFakIsTUFBTSxjQUFjLEdBQUcsS0FBSyxDQUFDLG9CQUFvQixJQUFJLEdBQUcsQ0FBQztRQUN6RCxNQUFNLGFBQWEsR0FBRyxLQUFLLENBQUMsYUFBYSxJQUFJLHdCQUFNLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQztRQUN4RSxNQUFNLGNBQWMsR0FBRyxLQUFLLENBQUMsY0FBYyxJQUFJLENBQUMsQ0FBQztRQUVqRCwyQkFBMkI7UUFDM0IsSUFBSSxDQUFDLGVBQWUsR0FBRyxJQUFJLHFCQUFHLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxpQkFBaUIsRUFBRTtZQUM1RCxlQUFlLEVBQUUsc0JBQVEsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1NBQ25DLENBQUMsQ0FBQztRQUVILG9CQUFvQjtRQUNwQixJQUFJLENBQUMsS0FBSyxHQUFHLElBQUkscUJBQUcsQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRTtZQUN4QyxpQkFBaUIsRUFBRSxzQkFBUSxDQUFDLE9BQU8sQ0FBQyxjQUFjLEdBQUcsRUFBRSxDQUFDO1lBQ3hELFVBQVUsRUFBRSxx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RUgsSUFBSSx1QkFBUyxDQUFDLElBQUksRUFBRSxvQkFBb0IsRUFBRTtZQUN4QyxLQUFLLEVBQUUsSUFBSSxDQUFDLGVBQWUsQ0FBQyxRQUFRO1lBQ3BDLFdBQVcsRUFBRSw2Q0FBNkM7WUFDMUQsVUFBVSxFQUFFLHVDQUF1QztTQUNwRCxDQUFDLENBQUM7UUFFSCxJQUFJLHVCQUFTLENBQUMsSUFBSSxFQUFFLGNBQWMsRUFBRTtZQUNsQyxLQUFLLEVBQUUsSUFBSSxDQUFDLGNBQWMsQ0FBQyxZQUFZO1lBQ3ZDLFdBQVcsRUFBRSw0Q0FBNEM7WUFDekQsVUFBVSxFQUFFLGdDQUFnQztTQUM3QyxDQUFDLENBQUM7SUFDTCxDQUFDOztBQXJKSCx3Q0FzSkMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBhd3NfZWMyIGFzIGVjMixcbiAgYXdzX2xhbWJkYSBhcyBsYW1iZGEsXG4gIGF3c19zcXMgYXMgc3FzLFxuICBhd3Nfc25zIGFzIHNucyxcbiAgYXdzX3Nuc19zdWJzY3JpcHRpb25zIGFzIHNuc1N1YnNjcmlwdGlvbnMsXG4gIGF3c19sYW1iZGFfZXZlbnRfc291cmNlcyBhcyBsYW1iZGFFdmVudFNvdXJjZXMsXG4gIGF3c19sb2dzIGFzIGxvZ3MsXG4gIER1cmF0aW9uLFxuICBDZm5PdXRwdXQsXG59IGZyb20gXCJhd3MtY2RrLWxpYlwiO1xuaW1wb3J0IHsgQ29uc3RydWN0IH0gZnJvbSBcImNvbnN0cnVjdHNcIjtcbmltcG9ydCB7IFBnU3RhY0RhdGFiYXNlIH0gZnJvbSBcIi4uL2RhdGFiYXNlXCI7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gXCJwYXRoXCI7XG5pbXBvcnQgeyBDdXN0b21MYW1iZGFGdW5jdGlvblByb3BzIH0gZnJvbSBcIi4uL3V0aWxzXCI7XG5cbi8qKlxuICogQ29uZmlndXJhdGlvbiBwcm9wZXJ0aWVzIGZvciB0aGUgU3RhY0l0ZW1Mb2FkZXIgY29uc3RydWN0LlxuICpcbiAqIFRoZSBTdGFjSXRlbUxvYWRlciBpcyBwYXJ0IG9mIGEgdHdvLXBoYXNlIHNlcnZlcmxlc3MgU1RBQyBpbmdlc3Rpb24gcGlwZWxpbmVcbiAqIHRoYXQgbG9hZHMgU1RBQyBpdGVtcyBpbnRvIGEgcGdzdGFjIGRhdGFiYXNlLiBUaGlzIGNvbnN0cnVjdCBjcmVhdGVzXG4gKiB0aGUgaW5mcmFzdHJ1Y3R1cmUgZm9yIHJlY2VpdmluZyBTVEFDIGl0ZW1zIGZyb20gbXVsdGlwbGUgc291cmNlczpcbiAqIDEuIFNOUyBtZXNzYWdlcyBjb250YWluaW5nIFNUQUMgbWV0YWRhdGEgKGRpcmVjdCBpbmdlc3Rpb24pXG4gKiAyLiBTMyBldmVudCBub3RpZmljYXRpb25zIGZvciBTVEFDIGl0ZW1zIHVwbG9hZGVkIHRvIFMzIGJ1Y2tldHNcbiAqXG4gKiBJdGVtcyBmcm9tIGJvdGggc291cmNlcyBhcmUgYmF0Y2hlZCBhbmQgaW5zZXJ0ZWQgaW50byBQb3N0Z3JlU1FMIHdpdGggdGhlIHBnc3RhYyBleHRlbnNpb24uXG4gKlxuICogQGV4YW1wbGVcbiAqIGNvbnN0IGxvYWRlciA9IG5ldyBTdGFjSXRlbUxvYWRlcih0aGlzLCAnSXRlbUxvYWRlcicsIHtcbiAqICAgcGdzdGFjRGI6IGRhdGFiYXNlLFxuICogICBiYXRjaFNpemU6IDEwMDAsXG4gKiAgIG1heEJhdGNoaW5nV2luZG93TWludXRlczogMSxcbiAqICAgbGFtYmRhVGltZW91dFNlY29uZHM6IDMwMFxuICogfSk7XG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU3RhY0l0ZW1Mb2FkZXJQcm9wcyB7XG4gIC8qKlxuICAgKiBUaGUgUGdTVEFDIGRhdGFiYXNlIGluc3RhbmNlIHRvIGxvYWQgaXRlbXMgaW50by5cbiAgICpcbiAgICogVGhpcyBkYXRhYmFzZSBtdXN0IGhhdmUgdGhlIHBnc3RhYyBleHRlbnNpb24gaW5zdGFsbGVkIGFuZCBiZSBwcm9wZXJseVxuICAgKiBjb25maWd1cmVkIHdpdGggY29sbGVjdGlvbnMgYmVmb3JlIGl0ZW1zIGNhbiBiZSBsb2FkZWQuIFRoZSBsb2FkZXIgd2lsbFxuICAgKiB1c2UgQVdTIFNlY3JldHMgTWFuYWdlciB0byBzZWN1cmVseSBhY2Nlc3MgZGF0YWJhc2UgY3JlZGVudGlhbHMuXG4gICAqL1xuICByZWFkb25seSBwZ3N0YWNEYjogUGdTdGFjRGF0YWJhc2U7XG5cbiAgLyoqXG4gICAqIFZQQyBpbnRvIHdoaWNoIHRoZSBsYW1iZGEgc2hvdWxkIGJlIGRlcGxveWVkLlxuICAgKi9cbiAgcmVhZG9ubHkgdnBjPzogZWMyLklWcGM7XG5cbiAgLyoqXG4gICAqIFN1Ym5ldCBpbnRvIHdoaWNoIHRoZSBsYW1iZGEgc2hvdWxkIGJlIGRlcGxveWVkLlxuICAgKi9cbiAgcmVhZG9ubHkgc3VibmV0U2VsZWN0aW9uPzogZWMyLlN1Ym5ldFNlbGVjdGlvbjtcblxuICAvKipcbiAgICogVGhlIGxhbWJkYSBydW50aW1lIHRvIHVzZSBmb3IgdGhlIGl0ZW0gbG9hZGluZyBmdW5jdGlvbi5cbiAgICpcbiAgICogVGhlIGZ1bmN0aW9uIGlzIGltcGxlbWVudGVkIGluIFB5dGhvbiBhbmQgdXNlcyBweXBnc3RhYyBmb3IgZGF0YWJhc2VcbiAgICogb3BlcmF0aW9ucy4gRW5zdXJlIHRoZSBydW50aW1lIHZlcnNpb24gaXMgY29tcGF0aWJsZSB3aXRoIHRoZSBwZ3N0YWNcbiAgICogdmVyc2lvbiBzcGVjaWZpZWQgaW4gdGhlIGRhdGFiYXNlIGNvbmZpZ3VyYXRpb24uXG4gICAqXG4gICAqIEBkZWZhdWx0IGxhbWJkYS5SdW50aW1lLlBZVEhPTl8zXzExXG4gICAqL1xuICByZWFkb25seSBsYW1iZGFSdW50aW1lPzogbGFtYmRhLlJ1bnRpbWU7XG5cbiAgLyoqXG4gICAqIFRoZSB0aW1lb3V0IGZvciB0aGUgaXRlbSBsb2FkIGxhbWJkYSBpbiBzZWNvbmRzLlxuICAgKlxuICAgKiBUaGlzIHNob3VsZCBhY2NvbW1vZGF0ZSB0aGUgdGltZSBuZWVkZWQgdG8gcHJvY2VzcyB1cCB0byBgYmF0Y2hTaXplYFxuICAgKiBpdGVtcyBhbmQgcGVyZm9ybSBkYXRhYmFzZSBpbnNlcnRpb25zLiBUaGUgU1FTIHZpc2liaWxpdHkgdGltZW91dFxuICAgKiB3aWxsIGJlIHNldCB0byB0aGlzIHZhbHVlIHBsdXMgMTAgc2Vjb25kcy5cbiAgICpcbiAgICogQGRlZmF1bHQgMzAwXG4gICAqL1xuICByZWFkb25seSBsYW1iZGFUaW1lb3V0U2Vjb25kcz86IG51bWJlcjtcblxuICAvKipcbiAgICogTWVtb3J5IHNpemUgZm9yIHRoZSBsYW1iZGEgZnVuY3Rpb24gaW4gTUIuXG4gICAqXG4gICAqIEhpZ2hlciBtZW1vcnkgYWxsb2NhdGlvbiBtYXkgaW1wcm92ZSBwZXJmb3JtYW5jZSB3aGVuIHByb2Nlc3NpbmdcbiAgICogbGFyZ2UgYmF0Y2hlcyBvZiBTVEFDIGl0ZW1zLCBlc3BlY2lhbGx5IGZvciBtZW1vcnktaW50ZW5zaXZlXG4gICAqIGRhdGFiYXNlIG9wZXJhdGlvbnMuXG4gICAqXG4gICAqIEBkZWZhdWx0IDEwMjRcbiAgICovXG4gIHJlYWRvbmx5IG1lbW9yeVNpemU/OiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIFNRUyBiYXRjaCBzaXplIGZvciBsYW1iZGEgZXZlbnQgc291cmNlLlxuICAgKlxuICAgKiBUaGlzIGRldGVybWluZXMgdGhlIG1heGltdW0gbnVtYmVyIG9mIFNUQUMgaXRlbXMgdGhhdCB3aWxsIGJlXG4gICAqIHByb2Nlc3NlZCB0b2dldGhlciBpbiBhIHNpbmdsZSBsYW1iZGEgaW52b2NhdGlvbi4gTGFyZ2VyIGJhdGNoXG4gICAqIHNpemVzIGltcHJvdmUgZGF0YWJhc2UgaW5zZXJ0aW9uIGVmZmljaWVuY3kgYnV0IHJlcXVpcmUgbW9yZVxuICAgKiBtZW1vcnkgYW5kIGxvbmdlciBwcm9jZXNzaW5nIHRpbWUuXG4gICAqXG4gICAqICoqQmF0Y2hpbmcgQmVoYXZpb3IqKjogU1FTIHdpbGwgd2FpdCB0byBhY2N1bXVsYXRlIHVwIHRvIHRoaXMgbWFueVxuICAgKiBtZXNzYWdlcyBiZWZvcmUgdHJpZ2dlcmluZyB0aGUgTGFtYmRhLCBPUiB1bnRpbCB0aGUgbWF4QmF0Y2hpbmdXaW5kb3dcbiAgICogdGltZW91dCBpcyByZWFjaGVkLCB3aGljaGV2ZXIgY29tZXMgZmlyc3QuIFRoaXMgY3JlYXRlcyBhbiBlZmZpY2llbnRcbiAgICogYmFsYW5jZSBiZXR3ZWVuIHRocm91Z2hwdXQgYW5kIGxhdGVuY3kuXG4gICAqXG4gICAqIEBkZWZhdWx0IDUwMFxuICAgKi9cbiAgcmVhZG9ubHkgYmF0Y2hTaXplPzogbnVtYmVyO1xuXG4gIC8qKlxuICAgKiBNYXhpbXVtIGJhdGNoaW5nIHdpbmRvdyBpbiBtaW51dGVzLlxuICAgKlxuICAgKiBFdmVuIGlmIHRoZSBiYXRjaCBzaXplIGlzbid0IHJlYWNoZWQsIHRoZSBsYW1iZGEgd2lsbCBiZSB0cmlnZ2VyZWRcbiAgICogYWZ0ZXIgdGhpcyB0aW1lIHBlcmlvZCB0byBlbnN1cmUgdGltZWx5IHByb2Nlc3Npbmcgb2YgaXRlbXMuXG4gICAqIFRoaXMgcHJldmVudHMgaXRlbXMgZnJvbSB3YWl0aW5nIGluZGVmaW5pdGVseSBpbiBsb3ctdm9sdW1lIHNjZW5hcmlvcy5cbiAgICpcbiAgICogKipJbXBvcnRhbnQqKjogVGhpcyB0aW1lb3V0IHdvcmtzIGluIGNvbmp1bmN0aW9uIHdpdGggYmF0Y2hTaXplIC0gU1FTXG4gICAqIHdpbGwgdHJpZ2dlciB0aGUgTGFtYmRhIHdoZW4gRUlUSEVSIHRoZSBiYXRjaCBzaXplIGlzIHJlYWNoZWQgT1IgdGhpc1xuICAgKiB0aW1lIHdpbmRvdyBleHBpcmVzLCBlbnN1cmluZyBpdGVtcyBhcmUgcHJvY2Vzc2VkIGluIGEgdGltZWx5IG1hbm5lclxuICAgKiByZWdhcmRsZXNzIG9mIHZvbHVtZS5cbiAgICpcbiAgICogQGRlZmF1bHQgMVxuICAgKi9cbiAgcmVhZG9ubHkgbWF4QmF0Y2hpbmdXaW5kb3dNaW51dGVzPzogbnVtYmVyO1xuXG4gIC8qKlxuICAgKiBNYXhpbXVtIGNvbmN1cnJlbnQgZXhlY3V0aW9ucyBmb3IgdGhlIFN0YWNJdGVtTG9hZGVyIExhbWJkYSBmdW5jdGlvblxuICAgKlxuICAgKiBUaGlzIGxpbWl0IHdpbGwgYmUgYXBwbGllZCB0byB0aGUgTGFtYmRhIGZ1bmN0aW9uIGFuZCB3aWxsIGNvbnRyb2wgaG93XG4gICAqIG1hbnkgY29uY3VycmVudCBiYXRjaGVzIHdpbGwgYmUgcmVsZWFzZWQgZnJvbSB0aGUgU1FTIHF1ZXVlLlxuICAgKlxuICAgKiBAZGVmYXVsdCAyXG4gICAqL1xuICByZWFkb25seSBtYXhDb25jdXJyZW5jeT86IG51bWJlcjtcblxuICAvKipcbiAgICogQWRkaXRpb25hbCBlbnZpcm9ubWVudCB2YXJpYWJsZXMgZm9yIHRoZSBsYW1iZGEgZnVuY3Rpb24uXG4gICAqXG4gICAqIFRoZXNlIHdpbGwgYmUgbWVyZ2VkIHdpdGggdGhlIGRlZmF1bHQgZW52aXJvbm1lbnQgdmFyaWFibGVzIGluY2x1ZGluZ1xuICAgKiBQR1NUQUNfU0VDUkVUX0FSTi4gVXNlIHRoaXMgZm9yIGN1c3RvbSBjb25maWd1cmF0aW9uIG9yIGRlYnVnZ2luZyBmbGFncy5cbiAgICovXG4gIHJlYWRvbmx5IGVudmlyb25tZW50PzogeyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfTtcblxuICAvKipcbiAgICogQ2FuIGJlIHVzZWQgdG8gb3ZlcnJpZGUgdGhlIGRlZmF1bHQgbGFtYmRhIGZ1bmN0aW9uIHByb3BlcnRpZXMuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gZGVmaW5lZCBpbiB0aGUgY29uc3RydWN0LlxuICAgKi9cbiAgcmVhZG9ubHkgbGFtYmRhRnVuY3Rpb25PcHRpb25zPzogQ3VzdG9tTGFtYmRhRnVuY3Rpb25Qcm9wcztcbn1cblxuLyoqXG4gKiBBV1MgQ0RLIENvbnN0cnVjdCBmb3IgU1RBQyBJdGVtIExvYWRpbmcgSW5mcmFzdHJ1Y3R1cmVcbiAqXG4gKiBUaGUgU3RhY0l0ZW1Mb2FkZXIgY3JlYXRlcyBhIHNlcnZlcmxlc3MsIGV2ZW50LWRyaXZlbiBzeXN0ZW0gZm9yIGxvYWRpbmdcbiAqIFNUQUMgKFNwYXRpb1RlbXBvcmFsIEFzc2V0IENhdGFsb2cpIGl0ZW1zIGludG8gYSBQb3N0Z3JlU1FMIGRhdGFiYXNlIHdpdGhcbiAqIHRoZSBwZ3N0YWMgZXh0ZW5zaW9uLiBUaGlzIGNvbnN0cnVjdCBzdXBwb3J0cyBtdWx0aXBsZSBpbmdlc3Rpb24gcGF0aHdheXNcbiAqIGZvciBmbGV4aWJsZSBTVEFDIGl0ZW0gbG9hZGluZy5cbiAqXG4gKiAjIyBBcmNoaXRlY3R1cmUgT3ZlcnZpZXdcbiAqXG4gKiBUaGlzIGNvbnN0cnVjdCBjcmVhdGVzIHRoZSBmb2xsb3dpbmcgQVdTIHJlc291cmNlczpcbiAqIC0gKipTTlMgVG9waWMqKjogRW50cnkgcG9pbnQgZm9yIFNUQUMgaXRlbXMgYW5kIFMzIGV2ZW50IG5vdGlmaWNhdGlvbnNcbiAqIC0gKipTUVMgUXVldWUqKjogQnVmZmVycyBhbmQgYmF0Y2hlcyBtZXNzYWdlcyBiZWZvcmUgcHJvY2Vzc2luZyAoNjAtc2Vjb25kIHZpc2liaWxpdHkgdGltZW91dClcbiAqIC0gKipEZWFkIExldHRlciBRdWV1ZSoqOiBDYXB0dXJlcyBmYWlsZWQgbG9hZGluZyBhdHRlbXB0cyBhZnRlciA1IHJldHJpZXNcbiAqIC0gKipMYW1iZGEgRnVuY3Rpb24qKjogUHl0aG9uIGZ1bmN0aW9uIHRoYXQgcHJvY2Vzc2VzIGJhdGNoZXMgYW5kIGluc2VydHMgaXRlbXMgaW50byBwZ3N0YWNcbiAqXG4gKiAjIyBEYXRhIEZsb3dcbiAqXG4gKiBUaGUgbG9hZGVyIHN1cHBvcnRzIHR3byBwcmltYXJ5IGRhdGEgaW5nZXN0aW9uIHBhdHRlcm5zOlxuICpcbiAqICMjIyBEaXJlY3QgU1RBQyBJdGVtIFB1Ymxpc2hpbmdcbiAqIDEuIFNUQUMgaXRlbXMgKEpTT04pIGFyZSBwdWJsaXNoZWQgZGlyZWN0bHkgdG8gdGhlIFNOUyB0b3BpYyBpbiBtZXNzYWdlIGJvZGllc1xuICogMi4gVGhlIFNRUyBxdWV1ZSBjb2xsZWN0cyBtZXNzYWdlcyBhbmQgYmF0Y2hlcyB0aGVtICh1cCB0byB7YmF0Y2hTaXplfSBpdGVtcyBvciAxIG1pbnV0ZSB3aW5kb3cpXG4gKiAzLiBUaGUgTGFtYmRhIGZ1bmN0aW9uIHJlY2VpdmVzIGJhdGNoZXMsIHZhbGlkYXRlcyBpdGVtcywgYW5kIGluc2VydHMgaW50byBwZ3N0YWNcbiAqXG4gKiAjIyMgUzMgRXZlbnQtRHJpdmVuIExvYWRpbmdcbiAqIDEuIEFuIFMzIGJ1Y2tldCBpcyBjb25maWd1cmVkIHRvIHNlbmQgbm90aWZpY2F0aW9ucyB0byB0aGUgU05TIHRvcGljIHdoZW4ganNvbiBmaWxlcyBhcmUgY3JlYXRlZFxuICogMi4gU1RBQyBpdGVtcyBhcmUgdXBsb2FkZWQgdG8gUzMgYnVja2V0cyBhcyBKU09OL0dlb0pTT04gZmlsZXNcbiAqIDMuIFMzIGV2ZW50IG5vdGlmaWNhdGlvbnMgYXJlIHNlbnQgdG8gdGhlIFNOUyB0b3BpYyB3aGVuIGl0ZW1zIGFyZSB1cGxvYWRlZFxuICogNC4gVGhlIExhbWJkYSBmdW5jdGlvbiByZWNlaXZlcyBTMyBldmVudHMgaW4gdGhlIFNRUyBtZXNzYWdlIGJhdGNoLCBmZXRjaGVzIGl0ZW1zIGZyb20gUzMsIGFuZCBsb2FkcyBpbnRvIHBnc3RhY1xuICpcbiAqICMjIEJhdGNoaW5nIEJlaGF2aW9yXG4gKlxuICogVGhlIFNRUy10by1MYW1iZGEgaW50ZWdyYXRpb24gdXNlcyBpbnRlbGxpZ2VudCBiYXRjaGluZyB0byBvcHRpbWl6ZSBwZXJmb3JtYW5jZTpcbiAqXG4gKiAtICoqQmF0Y2ggU2l6ZSoqOiBMYW1iZGEgd2FpdHMgdG8gcmVjZWl2ZSB1cCB0byBgYmF0Y2hTaXplYCBtZXNzYWdlcyAoZGVmYXVsdDogNTAwKVxuICogLSAqKkJhdGNoaW5nIFdpbmRvdyoqOiBJZiBmZXdlciB0aGFuIGBiYXRjaFNpemVgIG1lc3NhZ2VzIGFyZSBhdmFpbGFibGUsIExhbWJkYVxuICogICB0cmlnZ2VycyBhZnRlciBgbWF4QmF0Y2hpbmdXaW5kb3dgIG1pbnV0ZXMgKGRlZmF1bHQ6IDEgbWludXRlKVxuICogLSAqKlRyaWdnZXIgQ29uZGl0aW9uKio6IExhbWJkYSBleGVjdXRlcyB3aGVuIEVJVEhFUiBjb25kaXRpb24gaXMgbWV0IGZpcnN0XG4gKiAtICoqQ29uY3VycmVuY3kqKjogTGltaXRlZCB0byBgbWF4Q29uY3VycmVuY3lgIGNvbmN1cnJlbnQgZXhlY3V0aW9ucyB0byBwcmV2ZW50IGRhdGFiYXNlIG92ZXJsb2FkXG4gKiAtICoqUGFydGlhbCBGYWlsdXJlcyoqOiBVc2VzIGByZXBvcnRCYXRjaEl0ZW1GYWlsdXJlc2AgdG8gcmV0cnkgb25seSBmYWlsZWQgaXRlbXNcbiAqXG4gKiBUaGlzIGFwcHJvYWNoIGJhbGFuY2VzIHRocm91Z2hwdXQgKGxhcmdlciBiYXRjaGVzID0gZmV3ZXIgZGF0YWJhc2UgY29ubmVjdGlvbnMpXG4gKiB3aXRoIGxhdGVuY3kgKHRpbWUtYmFzZWQgdHJpZ2dlcnMgcHJldmVudCBpbmRlZmluaXRlIHdhaXRpbmcpLlxuICpcbiAqICMjIEVycm9yIEhhbmRsaW5nIGFuZCBEZWFkIExldHRlciBRdWV1ZVxuICpcbiAqIEZhaWxlZCBtZXNzYWdlcyBhcmUgc2VudCB0byB0aGUgZGVhZCBsZXR0ZXIgcXVldWUgYWZ0ZXIgNSBwcm9jZXNzaW5nIGF0dGVtcHRzLlxuICogKipJbXBvcnRhbnQqKjogVGhpcyBjb25zdHJ1Y3QgcHJvdmlkZXMgTk8gYXV0b21hdGVkIGhhbmRsaW5nIG9mIGRlYWQgbGV0dGVyIHF1ZXVlXG4gKiBtZXNzYWdlcyAtIG1vbml0b3JpbmcsIGluc3BlY3Rpb24sIGFuZCByZXByb2Nlc3Npbmcgb2YgZmFpbGVkIGl0ZW1zIGlzIHRoZVxuICogcmVzcG9uc2liaWxpdHkgb2YgdGhlIGltcGxlbWVudGluZyBhcHBsaWNhdGlvbi5cbiAqXG4gKiBDb25zaWRlciBpbXBsZW1lbnRpbmc6XG4gKiAtIENsb3VkV2F0Y2ggYWxhcm1zIG9uIGRlYWQgbGV0dGVyIHF1ZXVlIGRlcHRoXG4gKiAtIE1hbnVhbCBvciBhdXRvbWF0ZWQgcmVwcm9jZXNzaW5nIHdvcmtmbG93c1xuICogLSBMb2dnaW5nIGFuZCBhbGVydGluZyBmb3IgZmFpbGVkIGl0ZW1zXG4gKiAtIFJlZ3VsYXIgY2xlYW51cCBvZiBvbGQgZGVhZCBsZXR0ZXIgbWVzc2FnZXMgKDE0LWRheSByZXRlbnRpb24pXG4gKlxuICogIyMgT3BlcmF0aW9uYWwgQ2hhcmFjdGVyaXN0aWNzXG4gKlxuICogLSAqKlNjYWxhYmlsaXR5Kio6IExhbWJkYSBzY2FsZXMgYXV0b21hdGljYWxseSBiYXNlZCBvbiBxdWV1ZSBkZXB0aFxuICogLSAqKlJlbGlhYmlsaXR5Kio6IERlYWQgbGV0dGVyIHF1ZXVlIGNhcHR1cmVzIGZhaWx1cmVzIGZvciBkZWJ1Z2dpbmdcbiAqIC0gKipFZmZpY2llbmN5Kio6IEJhdGNoaW5nIG9wdGltaXplcyBkYXRhYmFzZSBvcGVyYXRpb25zIGZvciBoaWdoIHRocm91Z2hwdXRcbiAqIC0gKipTZWN1cml0eSoqOiBEYXRhYmFzZSBjcmVkZW50aWFscyBhY2Nlc3NlZCB2aWEgQVdTIFNlY3JldHMgTWFuYWdlclxuICogLSAqKk9ic2VydmFiaWxpdHkqKjogQ2xvdWRXYXRjaCBsb2dzIHJldGFpbmVkIGZvciBvbmUgd2Vla1xuICpcbiAqICMjIFByZXJlcXVpc2l0ZXNcbiAqXG4gKiBCZWZvcmUgdXNpbmcgdGhpcyBjb25zdHJ1Y3QsIGVuc3VyZTpcbiAqIC0gVGhlIHBnc3RhYyBkYXRhYmFzZSBoYXMgY29sbGVjdGlvbnMgbG9hZGVkIChpdGVtcyByZXF1aXJlIGV4aXN0aW5nIGNvbGxlY3Rpb24gSURzKVxuICogLSBEYXRhYmFzZSBjcmVkZW50aWFscyBhcmUgc3RvcmVkIGluIEFXUyBTZWNyZXRzIE1hbmFnZXJcbiAqIC0gVGhlIHBnc3RhYyBleHRlbnNpb24gaXMgcHJvcGVybHkgaW5zdGFsbGVkIGFuZCBjb25maWd1cmVkXG4gKlxuICogIyMgVXNhZ2UgRXhhbXBsZVxuICpcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIC8vIENyZWF0ZSBkYXRhYmFzZSBmaXJzdFxuICogY29uc3QgZGF0YWJhc2UgPSBuZXcgUGdTdGFjRGF0YWJhc2UodGhpcywgJ0RhdGFiYXNlJywge1xuICogICBwZ3N0YWNWZXJzaW9uOiAnMC45LjUnXG4gKiB9KTtcbiAqXG4gKiAvLyBDcmVhdGUgaXRlbSBsb2FkZXJcbiAqIGNvbnN0IGxvYWRlciA9IG5ldyBTdGFjSXRlbUxvYWRlcih0aGlzLCAnSXRlbUxvYWRlcicsIHtcbiAqICAgcGdzdGFjRGI6IGRhdGFiYXNlLFxuICogICBiYXRjaFNpemU6IDEwMDAsICAgICAgICAgIC8vIFByb2Nlc3MgdXAgdG8gMTAwMCBpdGVtcyBwZXIgYmF0Y2hcbiAqICAgbWF4QmF0Y2hpbmdXaW5kb3dNaW51dGVzOiAxLCAvLyBXYWl0IG1heCAxIG1pbnV0ZSB0byBmaWxsIGJhdGNoXG4gKiAgIGxhbWJkYVRpbWVvdXRTZWNvbmRzOiAzMDAgICAgIC8vIEFsbG93IHVwIHRvIDMwMCBzZWNvbmRzIGZvciBkYXRhYmFzZSBvcGVyYXRpb25zXG4gKiB9KTtcbiAqXG4gKiAvLyBUaGUgdG9waWMgQVJOIGNhbiBiZSB1c2VkIGJ5IG90aGVyIHNlcnZpY2VzIHRvIHB1Ymxpc2ggaXRlbXNcbiAqIG5ldyBDZm5PdXRwdXQodGhpcywgJ0xvYWRlclRvcGljQXJuJywge1xuICogICB2YWx1ZTogbG9hZGVyLnRvcGljLnRvcGljQXJuXG4gKiB9KTtcbiAqIGBgYFxuICpcbiAqICMjIERpcmVjdCBJdGVtIFB1Ymxpc2hpbmdcbiAqXG4gKiBFeHRlcm5hbCBzZXJ2aWNlcyBjYW4gcHVibGlzaCBTVEFDIGl0ZW1zIGRpcmVjdGx5IHRvIHRoZSB0b3BpYzpcbiAqXG4gKiBgYGBiYXNoXG4gKiBhd3Mgc25zIHB1Ymxpc2ggLS10b3BpYy1hcm4gJElURU1fTE9BRF9UT1BJQyAtLW1lc3NhZ2UgJ3tcbiAqICAgXCJ0eXBlXCI6IFwiRmVhdHVyZVwiLFxuICogICBcInN0YWNfdmVyc2lvblwiOiBcIjEuMC4wXCIsXG4gKiAgIFwiaWRcIjogXCJleGFtcGxlLWl0ZW1cIixcbiAqICAgXCJwcm9wZXJ0aWVzXCI6IHtcImRhdGV0aW1lXCI6IFwiMjAyMS0wMS0wMVQwMDowMDowMFpcIn0sXG4gKiAgIFwiZ2VvbWV0cnlcIjoge1widHlwZVwiOiBcIlBvbHlnb25cIiwgXCJjb29yZGluYXRlc1wiOiBbLi4uXX0sXG4gKiAgIFwiY29sbGVjdGlvblwiOiBcImV4YW1wbGUtY29sbGVjdGlvblwiXG4gKiB9J1xuICogYGBgXG4gKlxuICogIyMgUzMgRXZlbnQgQ29uZmlndXJhdGlvblxuICpcbiAqIFRvIGVuYWJsZSBTMyBldmVudC1kcml2ZW4gbG9hZGluZywgY29uZmlndXJlIFMzIGJ1Y2tldCBub3RpZmljYXRpb25zIHRvIHNlbmRcbiAqIGV2ZW50cyB0byB0aGUgU05TIHRvcGljIHdoZW4gU1RBQyBpdGVtcyAoLmpzb24gb3IgLmdlb2pzb24gZmlsZXMpIGFyZSB1cGxvYWRlZDpcbiAqXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiAvLyBDb25maWd1cmUgUzMgYnVja2V0IHRvIHNlbmQgbm90aWZpY2F0aW9ucyB0byB0aGUgbG9hZGVyIHRvcGljXG4gKiBidWNrZXQuYWRkRXZlbnROb3RpZmljYXRpb24oXG4gKiAgIHMzLkV2ZW50VHlwZS5PQkpFQ1RfQ1JFQVRFRCxcbiAqICAgbmV3IHMzbi5TbnNEZXN0aW5hdGlvbihsb2FkZXIudG9waWMpLFxuICogICB7IHN1ZmZpeDogJy5qc29uJyB9XG4gKiApO1xuICpcbiAqIGJ1Y2tldC5hZGRFdmVudE5vdGlmaWNhdGlvbihcbiAqICAgczMuRXZlbnRUeXBlLk9CSkVDVF9DUkVBVEVELFxuICogICBuZXcgczNuLlNuc0Rlc3RpbmF0aW9uKGxvYWRlci50b3BpYyksXG4gKiAgIHsgc3VmZml4OiAnLmdlb2pzb24nIH1cbiAqICk7XG4gKiBgYGBcbiAqXG4gKiBXaGVuIFNUQUMgaXRlbXMgYXJlIHVwbG9hZGVkIHRvIHRoZSBjb25maWd1cmVkIFMzIGJ1Y2tldCwgdGhlIGxvYWRlciB3aWxsOlxuICogMS4gUmVjZWl2ZSBTMyBldmVudCBub3RpZmljYXRpb25zIHZpYSBTTlNcbiAqIDIuIEZldGNoIHRoZSBTVEFDIGl0ZW0gSlNPTiBmcm9tIFMzXG4gKiAzLiBWYWxpZGF0ZSBhbmQgbG9hZCB0aGUgaXRlbSBpbnRvIHRoZSBwZ3N0YWMgZGF0YWJhc2VcbiAqXG4gKiAjIyBNb25pdG9yaW5nIGFuZCBUcm91Ymxlc2hvb3RpbmdcbiAqXG4gKiAtIE1vbml0b3IgTGFtYmRhIGxvZ3M6IGAvYXdzL2xhbWJkYS97RnVuY3Rpb25OYW1lfWBcbiAqIC0gKipEZWFkIExldHRlciBRdWV1ZSoqOiBDaGVjayBmb3IgZmFpbGVkIGl0ZW1zIC0gKipubyBhdXRvbWF0ZWQgaGFuZGxpbmcgcHJvdmlkZWQqKlxuICogLSBVc2UgYmF0Y2ggaXRlbSBmYWlsdXJlIHJlcG9ydGluZyBmb3IgcGFydGlhbCBiYXRjaCBwcm9jZXNzaW5nXG4gKiAtIENsb3VkV2F0Y2ggbWV0cmljcyBhdmFpbGFibGUgZm9yIHF1ZXVlIGRlcHRoIGFuZCBMYW1iZGEgcGVyZm9ybWFuY2VcbiAqXG4gKiAjIyMgRGVhZCBMZXR0ZXIgUXVldWUgTWFuYWdlbWVudFxuICpcbiAqIEFwcGxpY2F0aW9ucyBtdXN0IGltcGxlbWVudCB0aGVpciBvd24gZGVhZCBsZXR0ZXIgcXVldWUgbW9uaXRvcmluZzpcbiAqXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiAvLyBFeGFtcGxlOiBDbG91ZFdhdGNoIGFsYXJtIGZvciBkZWFkIGxldHRlciBxdWV1ZSBkZXB0aFxuICogbmV3IGNsb3Vkd2F0Y2guQWxhcm0odGhpcywgJ0RlYWRMZXR0ZXJBbGFybScsIHtcbiAqICAgbWV0cmljOiBsb2FkZXIuZGVhZExldHRlclF1ZXVlLm1ldHJpY0FwcHJveGltYXRlTnVtYmVyT2ZWaXNpYmxlTWVzc2FnZXMoKSxcbiAqICAgdGhyZXNob2xkOiAxLFxuICogICBldmFsdWF0aW9uUGVyaW9kczogMVxuICogfSk7XG4gKlxuICogLy8gRXhhbXBsZTogTGFtYmRhIHRvIHJlcHJvY2VzcyBkZWFkIGxldHRlciBtZXNzYWdlc1xuICogY29uc3QgcmVwcm9jZXNzRnVuY3Rpb24gPSBuZXcgbGFtYmRhLkZ1bmN0aW9uKHRoaXMsICdSZXByb2Nlc3MnLCB7XG4gKiAgIC8vIEltcGxlbWVudGF0aW9uIHRvIGZldGNoIGFuZCByZXB1Ymxpc2ggZmFpbGVkIG1lc3NhZ2VzXG4gKiB9KTtcbiAqIGBgYFxuICpcbiAqL1xuZXhwb3J0IGNsYXNzIFN0YWNJdGVtTG9hZGVyIGV4dGVuZHMgQ29uc3RydWN0IHtcbiAgLyoqXG4gICAqIFRoZSBTTlMgdG9waWMgdGhhdCByZWNlaXZlcyBTVEFDIGl0ZW1zIGFuZCBTMyBldmVudCBub3RpZmljYXRpb25zIGZvciBsb2FkaW5nLlxuICAgKlxuICAgKiBUaGlzIHRvcGljIHNlcnZlcyBhcyB0aGUgZW50cnkgcG9pbnQgZm9yIHR3byB0eXBlcyBvZiBldmVudHM6XG4gICAqIDEuIERpcmVjdCBTVEFDIGl0ZW0gSlNPTiBkb2N1bWVudHMgcHVibGlzaGVkIGJ5IGV4dGVybmFsIHNlcnZpY2VzXG4gICAqIDIuIFMzIGV2ZW50IG5vdGlmaWNhdGlvbnMgd2hlbiBTVEFDIGl0ZW1zIGFyZSB1cGxvYWRlZCB0byBjb25maWd1cmVkIGJ1Y2tldHNcbiAgICpcbiAgICogVGhlIHRvcGljIGZhbnMgb3V0IHRvIHRoZSBTUVMgcXVldWUgZm9yIGJhdGNoZWQgcHJvY2Vzc2luZy5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSB0b3BpYzogc25zLlRvcGljO1xuXG4gIC8qKlxuICAgKiBUaGUgU1FTIHF1ZXVlIHRoYXQgYnVmZmVycyBtZXNzYWdlcyBiZWZvcmUgcHJvY2Vzc2luZy5cbiAgICpcbiAgICogVGhpcyBxdWV1ZSBjb2xsZWN0cyBib3RoIGRpcmVjdCBTVEFDIGl0ZW1zIGZyb20gU05TIGFuZCBTMyBldmVudFxuICAgKiBub3RpZmljYXRpb25zLCBiYXRjaGluZyB0aGVtIGZvciBlZmZpY2llbnQgZGF0YWJhc2Ugb3BlcmF0aW9ucy5cbiAgICogQ29uZmlndXJlZCB3aXRoIGEgdmlzaWJpbGl0eSB0aW1lb3V0IHRoYXQgYWNjb21tb2RhdGVzIExhbWJkYVxuICAgKiBwcm9jZXNzaW5nIHRpbWUgcGx1cyBidWZmZXIuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgcXVldWU6IHNxcy5RdWV1ZTtcblxuICAvKipcbiAgICogRGVhZCBsZXR0ZXIgcXVldWUgZm9yIGZhaWxlZCBpdGVtIGxvYWRpbmcgYXR0ZW1wdHMuXG4gICAqXG4gICAqIE1lc3NhZ2VzIHRoYXQgZmFpbCBwcm9jZXNzaW5nIGFmdGVyIDUgYXR0ZW1wdHMgYXJlIHNlbnQgaGVyZVxuICAgKiBmb3IgaW5zcGVjdGlvbiBhbmQgcG90ZW50aWFsIHJlcGxheS4gUmV0YWlucyBtZXNzYWdlcyBmb3IgMTQgZGF5c1xuICAgKiB0byBhbGxvdyBmb3IgZGVidWdnaW5nIGFuZCBtYW51YWwgaW50ZXJ2ZW50aW9uLlxuICAgKlxuICAgKiAqKlVzZXIgUmVzcG9uc2liaWxpdHkqKjogVGhpcyBjb25zdHJ1Y3QgcHJvdmlkZXMgTk8gYXV0b21hdGVkIG1vbml0b3JpbmcsXG4gICAqIGFsZXJ0aW5nLCBvciByZXByb2Nlc3Npbmcgb2YgZGVhZCBsZXR0ZXIgcXVldWUgbWVzc2FnZXMuIEFwcGxpY2F0aW9uc1xuICAgKiB1c2luZyB0aGlzIGNvbnN0cnVjdCBtdXN0IGltcGxlbWVudCB0aGVpciBvd246XG4gICAqIC0gRGVhZCBsZXR0ZXIgcXVldWUgZGVwdGggbW9uaXRvcmluZyBhbmQgYWxlcnRpbmdcbiAgICogLSBGYWlsZWQgbWVzc2FnZSBpbnNwZWN0aW9uIGFuZCBkZWJ1Z2dpbmcgd29ya2Zsb3dzXG4gICAqIC0gTWFudWFsIG9yIGF1dG9tYXRlZCByZXByb2Nlc3NpbmcgbWVjaGFuaXNtc1xuICAgKiAtIENsZWFudXAgcHJvY2VkdXJlcyBmb3Igb2xkIGZhaWxlZCBtZXNzYWdlc1xuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGRlYWRMZXR0ZXJRdWV1ZTogc3FzLlF1ZXVlO1xuXG4gIC8qKlxuICAgKiBUaGUgTGFtYmRhIGZ1bmN0aW9uIHRoYXQgbG9hZHMgU1RBQyBpdGVtcyBpbnRvIHRoZSBwZ3N0YWMgZGF0YWJhc2UuXG4gICAqXG4gICAqIFRoaXMgUHl0aG9uIGZ1bmN0aW9uIHJlY2VpdmVzIGJhdGNoZXMgb2YgbWVzc2FnZXMgZnJvbSBTUVMgYW5kIHByb2Nlc3Nlc1xuICAgKiB0aGVtIGJhc2VkIG9uIHRoZWlyIHR5cGU6XG4gICAqIC0gRGlyZWN0IFNUQUMgaXRlbXM6IFZhbGlkYXRlcyBhbmQgbG9hZHMgZGlyZWN0bHkgaW50byBwZ3N0YWNcbiAgICogLSBTMyBldmVudHM6IEZldGNoZXMgU1RBQyBpdGVtcyBmcm9tIFMzLCB2YWxpZGF0ZXMsIGFuZCBsb2FkcyBpbnRvIHBnc3RhY1xuICAgKlxuICAgKiBUaGUgZnVuY3Rpb24gY29ubmVjdHMgdG8gUG9zdGdyZVNRTCB1c2luZyBjcmVkZW50aWFscyBmcm9tIFNlY3JldHMgTWFuYWdlclxuICAgKiBhbmQgdXNlcyBweXBnc3RhYyBmb3IgZWZmaWNpZW50IGRhdGFiYXNlIG9wZXJhdGlvbnMuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgbGFtYmRhRnVuY3Rpb246IGxhbWJkYS5GdW5jdGlvbjtcblxuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogU3RhY0l0ZW1Mb2FkZXJQcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZCk7XG5cbiAgICBjb25zdCB0aW1lb3V0U2Vjb25kcyA9IHByb3BzLmxhbWJkYVRpbWVvdXRTZWNvbmRzID8/IDMwMDtcbiAgICBjb25zdCBsYW1iZGFSdW50aW1lID0gcHJvcHMubGFtYmRhUnVudGltZSA/PyBsYW1iZGEuUnVudGltZS5QWVRIT05fM18xMTtcbiAgICBjb25zdCBtYXhDb25jdXJyZW5jeSA9IHByb3BzLm1heENvbmN1cnJlbmN5ID8/IDI7XG5cbiAgICAvLyBDcmVhdGUgZGVhZCBsZXR0ZXIgcXVldWVcbiAgICB0aGlzLmRlYWRMZXR0ZXJRdWV1ZSA9IG5ldyBzcXMuUXVldWUodGhpcywgXCJEZWFkTGV0dGVyUXVldWVcIiwge1xuICAgICAgcmV0ZW50aW9uUGVyaW9kOiBEdXJhdGlvbi5kYXlzKDE0KSxcbiAgICB9KTtcblxuICAgIC8vIENyZWF0ZSBtYWluIHF1ZXVlXG4gICAgdGhpcy5xdWV1ZSA9IG5ldyBzcXMuUXVldWUodGhpcywgXCJRdWV1ZVwiLCB7XG4gICAgICB2aXNpYmlsaXR5VGltZW91dDogRHVyYXRpb24uc2Vjb25kcyh0aW1lb3V0U2Vjb25kcyArIDEwKSxcbiAgICAgIGVuY3J5cHRpb246IHNxcy5RdWV1ZUVuY3J5cHRpb24uU1FTX01BTkFHRUQsXG4gICAgICBkZWFkTGV0dGVyUXVldWU6IHtcbiAgICAgICAgbWF4UmVjZWl2ZUNvdW50OiA1LFxuICAgICAgICBxdWV1ZTogdGhpcy5kZWFkTGV0dGVyUXVldWUsXG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgLy8gQ3JlYXRlIFNOUyB0b3BpY1xuICAgIHRoaXMudG9waWMgPSBuZXcgc25zLlRvcGljKHRoaXMsIFwiVG9waWNcIiwge1xuICAgICAgZGlzcGxheU5hbWU6IGAke2lkfS1TdGFjSXRlbUxvYWRlclRvcGljYCxcbiAgICB9KTtcblxuICAgIC8vIFN1YnNjcmliZSB0aGUgcXVldWUgdG8gdGhlIHRvcGljXG4gICAgdGhpcy50b3BpYy5hZGRTdWJzY3JpcHRpb24oXG4gICAgICBuZXcgc25zU3Vic2NyaXB0aW9ucy5TcXNTdWJzY3JpcHRpb24odGhpcy5xdWV1ZSlcbiAgICApO1xuXG4gICAgLy8gQ3JlYXRlIHRoZSBsYW1iZGEgZnVuY3Rpb25cbiAgICB0aGlzLmxhbWJkYUZ1bmN0aW9uID0gbmV3IGxhbWJkYS5GdW5jdGlvbih0aGlzLCBcIkZ1bmN0aW9uXCIsIHtcbiAgICAgIHJ1bnRpbWU6IGxhbWJkYVJ1bnRpbWUsXG4gICAgICBoYW5kbGVyOiBcInN0YWNfaXRlbV9sb2FkZXIuaGFuZGxlci5oYW5kbGVyXCIsXG4gICAgICB2cGM6IHByb3BzLnZwYyxcbiAgICAgIHZwY1N1Ym5ldHM6IHByb3BzLnN1Ym5ldFNlbGVjdGlvbixcbiAgICAgIGNvZGU6IGxhbWJkYS5Db2RlLmZyb21Eb2NrZXJCdWlsZChwYXRoLmpvaW4oX19kaXJuYW1lLCBcIi4uXCIpLCB7XG4gICAgICAgIGZpbGU6IFwic3RhYy1pdGVtLWxvYWRlci9ydW50aW1lL0RvY2tlcmZpbGVcIixcbiAgICAgICAgcGxhdGZvcm06IFwibGludXgvYW1kNjRcIixcbiAgICAgICAgYnVpbGRBcmdzOiB7XG4gICAgICAgICAgUFlUSE9OX1ZFUlNJT046IGxhbWJkYVJ1bnRpbWUudG9TdHJpbmcoKS5yZXBsYWNlKFwicHl0aG9uXCIsIFwiXCIpLFxuICAgICAgICAgIFBHU1RBQ19WRVJTSU9OOiBwcm9wcy5wZ3N0YWNEYi5wZ3N0YWNWZXJzaW9uLFxuICAgICAgICB9LFxuICAgICAgfSksXG4gICAgICBtZW1vcnlTaXplOiBwcm9wcy5tZW1vcnlTaXplID8/IDEwMjQsXG4gICAgICB0aW1lb3V0OiBEdXJhdGlvbi5zZWNvbmRzKHRpbWVvdXRTZWNvbmRzKSxcbiAgICAgIHJlc2VydmVkQ29uY3VycmVudEV4ZWN1dGlvbnM6IG1heENvbmN1cnJlbmN5LFxuICAgICAgbG9nUmV0ZW50aW9uOiBsb2dzLlJldGVudGlvbkRheXMuT05FX1dFRUssXG4gICAgICBlbnZpcm9ubWVudDoge1xuICAgICAgICBQR1NUQUNfU0VDUkVUX0FSTjogcHJvcHMucGdzdGFjRGIucGdzdGFjU2VjcmV0LnNlY3JldEFybixcbiAgICAgICAgLi4ucHJvcHMuZW52aXJvbm1lbnQsXG4gICAgICB9LFxuICAgICAgLy8gb3ZlcndyaXRlcyBkZWZhdWx0cyB3aXRoIHVzZXItcHJvdmlkZWQgY29uZmlndXJhYmxlIHByb3BlcnRpZXNcbiAgICAgIC4uLnByb3BzLmxhbWJkYUZ1bmN0aW9uT3B0aW9ucyxcbiAgICB9KTtcblxuICAgIC8vIEdyYW50IHBlcm1pc3Npb25zIHRvIHJlYWQgdGhlIGRhdGFiYXNlIHNlY3JldFxuICAgIHByb3BzLnBnc3RhY0RiLnBnc3RhY1NlY3JldC5ncmFudFJlYWQodGhpcy5sYW1iZGFGdW5jdGlvbik7XG5cbiAgICAvLyBBZGQgU1FTIGV2ZW50IHNvdXJjZSB0byB0aGUgbGFtYmRhXG4gICAgdGhpcy5sYW1iZGFGdW5jdGlvbi5hZGRFdmVudFNvdXJjZShcbiAgICAgIG5ldyBsYW1iZGFFdmVudFNvdXJjZXMuU3FzRXZlbnRTb3VyY2UodGhpcy5xdWV1ZSwge1xuICAgICAgICBiYXRjaFNpemU6IHByb3BzLmJhdGNoU2l6ZSA/PyA1MDAsXG4gICAgICAgIG1heEJhdGNoaW5nV2luZG93OiBEdXJhdGlvbi5taW51dGVzKFxuICAgICAgICAgIHByb3BzLm1heEJhdGNoaW5nV2luZG93TWludXRlcyA/PyAxXG4gICAgICAgICksXG4gICAgICAgIG1heENvbmN1cnJlbmN5OiBtYXhDb25jdXJyZW5jeSxcbiAgICAgICAgcmVwb3J0QmF0Y2hJdGVtRmFpbHVyZXM6IHRydWUsXG4gICAgICB9KVxuICAgICk7XG5cbiAgICAvLyBDcmVhdGUgb3V0cHV0c1xuICAgIG5ldyBDZm5PdXRwdXQodGhpcywgXCJUb3BpY0FyblwiLCB7XG4gICAgICB2YWx1ZTogdGhpcy50b3BpYy50b3BpY0FybixcbiAgICAgIGRlc2NyaXB0aW9uOiBcIkFSTiBvZiB0aGUgU3RhY0l0ZW1Mb2FkZXIgU05TIFRvcGljXCIsXG4gICAgICBleHBvcnROYW1lOiBcInN0YWMtaXRlbS1sb2FkZXItdG9waWMtYXJuXCIsXG4gICAgfSk7XG5cbiAgICBuZXcgQ2ZuT3V0cHV0KHRoaXMsIFwiUXVldWVVcmxcIiwge1xuICAgICAgdmFsdWU6IHRoaXMucXVldWUucXVldWVVcmwsXG4gICAgICBkZXNjcmlwdGlvbjogXCJVUkwgb2YgdGhlIFN0YWNJdGVtTG9hZGVyIFNRUyBRdWV1ZVwiLFxuICAgICAgZXhwb3J0TmFtZTogXCJzdGFjLWl0ZW0tbG9hZGVyLXF1ZXVlLXVybFwiLFxuICAgIH0pO1xuXG4gICAgbmV3IENmbk91dHB1dCh0aGlzLCBcIkRlYWRMZXR0ZXJRdWV1ZVVybFwiLCB7XG4gICAgICB2YWx1ZTogdGhpcy5kZWFkTGV0dGVyUXVldWUucXVldWVVcmwsXG4gICAgICBkZXNjcmlwdGlvbjogXCJVUkwgb2YgdGhlIFN0YWNJdGVtTG9hZGVyIERlYWQgTGV0dGVyIFF1ZXVlXCIsXG4gICAgICBleHBvcnROYW1lOiBcInN0YWMtaXRlbS1sb2FkZXItZGVhZGxldHRlci1xdWV1ZS11cmxcIixcbiAgICB9KTtcblxuICAgIG5ldyBDZm5PdXRwdXQodGhpcywgXCJGdW5jdGlvbk5hbWVcIiwge1xuICAgICAgdmFsdWU6IHRoaXMubGFtYmRhRnVuY3Rpb24uZnVuY3Rpb25OYW1lLFxuICAgICAgZGVzY3JpcHRpb246IFwiTmFtZSBvZiB0aGUgU3RhY0l0ZW1Mb2FkZXIgTGFtYmRhIEZ1bmN0aW9uXCIsXG4gICAgICBleHBvcnROYW1lOiBcInN0YWMtaXRlbS1sb2FkZXItZnVuY3Rpb24tbmFtZVwiLFxuICAgIH0pO1xuICB9XG59XG4iXX0=
@@ -0,0 +1,18 @@
1
+ ARG PYTHON_VERSION=3.11
2
+ FROM public.ecr.aws/lambda/python:${PYTHON_VERSION}
3
+ COPY --from=ghcr.io/astral-sh/uv:0.7.8 /uv /uvx /bin/
4
+
5
+ ENV UV_COMPILE_BYTECODE=1
6
+ ENV PYTHONUNBUFFERED=1
7
+
8
+ WORKDIR /asset
9
+
10
+ COPY stac-item-loader/runtime/pyproject.toml pyproject.toml
11
+ COPY stac-item-loader/runtime/src/stac_item_loader/ stac_item_loader/
12
+
13
+ ARG PGSTAC_VERSION=0.9.6
14
+ RUN uv add --no-sync pypgstac==${PGSTAC_VERSION} && \
15
+ uv export --no-dev --no-editable -o requirements.txt && \
16
+ uv pip install --target /asset -r requirements.txt
17
+
18
+ CMD ["stac_item_loader.handler.handler"]