s3db.js 13.5.1 → 13.6.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 (108) hide show
  1. package/README.md +89 -19
  2. package/dist/{s3db.cjs.js → s3db.cjs} +29780 -24384
  3. package/dist/s3db.cjs.map +1 -0
  4. package/dist/s3db.es.js +24263 -18860
  5. package/dist/s3db.es.js.map +1 -1
  6. package/package.json +227 -21
  7. package/src/concerns/id.js +90 -6
  8. package/src/concerns/index.js +2 -1
  9. package/src/concerns/password-hashing.js +150 -0
  10. package/src/database.class.js +4 -0
  11. package/src/plugins/api/auth/basic-auth.js +23 -1
  12. package/src/plugins/api/auth/index.js +49 -3
  13. package/src/plugins/api/auth/oauth2-auth.js +171 -0
  14. package/src/plugins/api/auth/oidc-auth.js +789 -0
  15. package/src/plugins/api/auth/oidc-client.js +462 -0
  16. package/src/plugins/api/auth/path-auth-matcher.js +284 -0
  17. package/src/plugins/api/concerns/event-emitter.js +134 -0
  18. package/src/plugins/api/concerns/failban-manager.js +651 -0
  19. package/src/plugins/api/concerns/guards-helpers.js +402 -0
  20. package/src/plugins/api/concerns/metrics-collector.js +346 -0
  21. package/src/plugins/api/concerns/opengraph-helper.js +116 -0
  22. package/src/plugins/api/concerns/state-machine.js +288 -0
  23. package/src/plugins/api/index.js +514 -54
  24. package/src/plugins/api/middlewares/failban.js +305 -0
  25. package/src/plugins/api/middlewares/rate-limit.js +301 -0
  26. package/src/plugins/api/middlewares/request-id.js +74 -0
  27. package/src/plugins/api/middlewares/security-headers.js +120 -0
  28. package/src/plugins/api/middlewares/session-tracking.js +194 -0
  29. package/src/plugins/api/routes/auth-routes.js +23 -3
  30. package/src/plugins/api/routes/resource-routes.js +71 -29
  31. package/src/plugins/api/server.js +1017 -94
  32. package/src/plugins/api/utils/guards.js +213 -0
  33. package/src/plugins/api/utils/mime-types.js +154 -0
  34. package/src/plugins/api/utils/openapi-generator.js +44 -11
  35. package/src/plugins/api/utils/path-matcher.js +173 -0
  36. package/src/plugins/api/utils/static-filesystem.js +262 -0
  37. package/src/plugins/api/utils/static-s3.js +231 -0
  38. package/src/plugins/api/utils/template-engine.js +262 -0
  39. package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +853 -0
  40. package/src/plugins/cloud-inventory/drivers/aws-driver.js +2554 -0
  41. package/src/plugins/cloud-inventory/drivers/azure-driver.js +637 -0
  42. package/src/plugins/cloud-inventory/drivers/base-driver.js +99 -0
  43. package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +620 -0
  44. package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +698 -0
  45. package/src/plugins/cloud-inventory/drivers/gcp-driver.js +645 -0
  46. package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +559 -0
  47. package/src/plugins/cloud-inventory/drivers/linode-driver.js +614 -0
  48. package/src/plugins/cloud-inventory/drivers/mock-drivers.js +449 -0
  49. package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +771 -0
  50. package/src/plugins/cloud-inventory/drivers/oracle-driver.js +768 -0
  51. package/src/plugins/cloud-inventory/drivers/vultr-driver.js +636 -0
  52. package/src/plugins/cloud-inventory/index.js +20 -0
  53. package/src/plugins/cloud-inventory/registry.js +146 -0
  54. package/src/plugins/cloud-inventory/terraform-exporter.js +362 -0
  55. package/src/plugins/cloud-inventory.plugin.js +1333 -0
  56. package/src/plugins/concerns/plugin-dependencies.js +61 -1
  57. package/src/plugins/eventual-consistency/analytics.js +1 -0
  58. package/src/plugins/identity/README.md +335 -0
  59. package/src/plugins/identity/concerns/mfa-manager.js +204 -0
  60. package/src/plugins/identity/concerns/password.js +138 -0
  61. package/src/plugins/identity/concerns/resource-schemas.js +273 -0
  62. package/src/plugins/identity/concerns/token-generator.js +172 -0
  63. package/src/plugins/identity/email-service.js +422 -0
  64. package/src/plugins/identity/index.js +1052 -0
  65. package/src/plugins/identity/oauth2-server.js +1033 -0
  66. package/src/plugins/identity/oidc-discovery.js +285 -0
  67. package/src/plugins/identity/rsa-keys.js +323 -0
  68. package/src/plugins/identity/server.js +500 -0
  69. package/src/plugins/identity/session-manager.js +453 -0
  70. package/src/plugins/identity/ui/layouts/base.js +251 -0
  71. package/src/plugins/identity/ui/middleware.js +135 -0
  72. package/src/plugins/identity/ui/pages/admin/client-form.js +247 -0
  73. package/src/plugins/identity/ui/pages/admin/clients.js +179 -0
  74. package/src/plugins/identity/ui/pages/admin/dashboard.js +181 -0
  75. package/src/plugins/identity/ui/pages/admin/user-form.js +283 -0
  76. package/src/plugins/identity/ui/pages/admin/users.js +263 -0
  77. package/src/plugins/identity/ui/pages/consent.js +262 -0
  78. package/src/plugins/identity/ui/pages/forgot-password.js +104 -0
  79. package/src/plugins/identity/ui/pages/login.js +144 -0
  80. package/src/plugins/identity/ui/pages/mfa-backup-codes.js +180 -0
  81. package/src/plugins/identity/ui/pages/mfa-enrollment.js +187 -0
  82. package/src/plugins/identity/ui/pages/mfa-verification.js +178 -0
  83. package/src/plugins/identity/ui/pages/oauth-error.js +225 -0
  84. package/src/plugins/identity/ui/pages/profile.js +361 -0
  85. package/src/plugins/identity/ui/pages/register.js +226 -0
  86. package/src/plugins/identity/ui/pages/reset-password.js +128 -0
  87. package/src/plugins/identity/ui/pages/verify-email.js +172 -0
  88. package/src/plugins/identity/ui/routes.js +2541 -0
  89. package/src/plugins/identity/ui/styles/main.css +465 -0
  90. package/src/plugins/index.js +4 -1
  91. package/src/plugins/ml/base-model.class.js +32 -7
  92. package/src/plugins/ml/classification-model.class.js +1 -1
  93. package/src/plugins/ml/timeseries-model.class.js +3 -1
  94. package/src/plugins/ml.plugin.js +124 -32
  95. package/src/plugins/shared/error-handler.js +147 -0
  96. package/src/plugins/shared/index.js +9 -0
  97. package/src/plugins/shared/middlewares/compression.js +117 -0
  98. package/src/plugins/shared/middlewares/cors.js +49 -0
  99. package/src/plugins/shared/middlewares/index.js +11 -0
  100. package/src/plugins/shared/middlewares/logging.js +54 -0
  101. package/src/plugins/shared/middlewares/rate-limit.js +73 -0
  102. package/src/plugins/shared/middlewares/security.js +158 -0
  103. package/src/plugins/shared/response-formatter.js +264 -0
  104. package/src/plugins/tfstate/README.md +126 -126
  105. package/src/resource.class.js +140 -12
  106. package/src/schema.class.js +30 -1
  107. package/src/validator.class.js +57 -6
  108. package/dist/s3db.cjs.js.map +0 -1
@@ -0,0 +1,146 @@
1
+ import { BaseCloudDriver } from './drivers/base-driver.js';
2
+ import { AwsInventoryDriver } from './drivers/aws-driver.js';
3
+ import { GcpInventoryDriver } from './drivers/gcp-driver.js';
4
+ import { VultrInventoryDriver } from './drivers/vultr-driver.js';
5
+ import { DigitalOceanInventoryDriver } from './drivers/digitalocean-driver.js';
6
+ import { OracleInventoryDriver } from './drivers/oracle-driver.js';
7
+ import { AzureInventoryDriver } from './drivers/azure-driver.js';
8
+ import { LinodeInventoryDriver } from './drivers/linode-driver.js';
9
+ import { HetznerInventoryDriver } from './drivers/hetzner-driver.js';
10
+ import { AlibabaInventoryDriver } from './drivers/alibaba-driver.js';
11
+ import { CloudflareInventoryDriver } from './drivers/cloudflare-driver.js';
12
+ import { MongoDBAtlasInventoryDriver } from './drivers/mongodb-atlas-driver.js';
13
+ import {
14
+ AwsMockDriver,
15
+ GcpMockDriver,
16
+ DigitalOceanMockDriver,
17
+ OracleMockDriver,
18
+ AzureMockDriver,
19
+ VultrMockDriver,
20
+ LinodeMockDriver,
21
+ HetznerMockDriver,
22
+ AlibabaMockDriver,
23
+ CloudflareMockDriver,
24
+ MongoDBAtlasMockDriver
25
+ } from './drivers/mock-drivers.js';
26
+
27
+ const CLOUD_DRIVERS = new Map();
28
+
29
+ /**
30
+ * Register a cloud inventory driver.
31
+ * @param {string} name
32
+ * @param {Function} factory - (options) => BaseCloudDriver instance
33
+ */
34
+ export function registerCloudDriver(name, factory) {
35
+ if (!name || typeof name !== 'string') {
36
+ throw new Error('registerCloudDriver: name must be a non-empty string');
37
+ }
38
+
39
+ if (typeof factory !== 'function') {
40
+ throw new Error(`registerCloudDriver("${name}") expects a factory function`);
41
+ }
42
+
43
+ CLOUD_DRIVERS.set(name, factory);
44
+ }
45
+
46
+ /**
47
+ * Instantiate a driver using the registry.
48
+ * @param {string} name
49
+ * @param {Object} options
50
+ * @returns {BaseCloudDriver}
51
+ */
52
+ export function createCloudDriver(name, options) {
53
+ if (!CLOUD_DRIVERS.has(name)) {
54
+ throw new Error(`Cloud driver "${name}" is not registered. Registered drivers: ${[...CLOUD_DRIVERS.keys()].join(', ') || 'none'}`);
55
+ }
56
+
57
+ const driver = CLOUD_DRIVERS.get(name)(options);
58
+ if (!(driver instanceof BaseCloudDriver)) {
59
+ throw new Error(`Driver "${name}" factory must return an instance of BaseCloudDriver`);
60
+ }
61
+ return driver;
62
+ }
63
+
64
+ /**
65
+ * List registered driver names.
66
+ * @returns {string[]}
67
+ */
68
+ export function listCloudDrivers() {
69
+ return [...CLOUD_DRIVERS.keys()];
70
+ }
71
+
72
+ /**
73
+ * Utility for validating a cloud definition.
74
+ * Ensures driver exists and required fields are present.
75
+ * @param {Object} cloud
76
+ */
77
+ export function validateCloudDefinition(cloud) {
78
+ if (!cloud || typeof cloud !== 'object') {
79
+ throw new Error('Each cloud configuration must be an object');
80
+ }
81
+
82
+ const { driver, credentials } = cloud;
83
+ if (!driver || typeof driver !== 'string') {
84
+ throw new Error('Cloud configuration requires a "driver" string');
85
+ }
86
+
87
+ if (!CLOUD_DRIVERS.has(driver)) {
88
+ throw new Error(`Cloud driver "${driver}" is not registered`);
89
+ }
90
+
91
+ if (!credentials || typeof credentials !== 'object') {
92
+ throw new Error(`Cloud "${driver}" requires a credentials object`);
93
+ }
94
+ }
95
+
96
+ // Register a no-op driver as a placeholder / mock implementation.
97
+ registerCloudDriver('noop', (options = {}) => {
98
+ class NoopDriver extends BaseCloudDriver {
99
+ async listResources() {
100
+ const { sampleResources = [] } = options.config || {};
101
+ return Array.isArray(sampleResources) ? sampleResources : [];
102
+ }
103
+ }
104
+ return new NoopDriver({
105
+ ...options,
106
+ driver: options.driver || 'noop'
107
+ });
108
+ });
109
+
110
+ function registerMockDriver(names, DriverClass) {
111
+ const list = Array.isArray(names) ? names : [names];
112
+ for (const name of list) {
113
+ registerCloudDriver(name, (options = {}) => new DriverClass(options));
114
+ }
115
+ }
116
+
117
+ registerCloudDriver('aws', (options = {}) => new AwsInventoryDriver(options));
118
+ registerCloudDriver('gcp', (options = {}) => new GcpInventoryDriver(options));
119
+ registerCloudDriver('vultr', (options = {}) => new VultrInventoryDriver(options));
120
+ registerCloudDriver('digitalocean', (options = {}) => new DigitalOceanInventoryDriver(options));
121
+ registerCloudDriver('do', (options = {}) => new DigitalOceanInventoryDriver(options));
122
+ registerCloudDriver('oracle', (options = {}) => new OracleInventoryDriver(options));
123
+ registerCloudDriver('oci', (options = {}) => new OracleInventoryDriver(options));
124
+ registerCloudDriver('azure', (options = {}) => new AzureInventoryDriver(options));
125
+ registerCloudDriver('az', (options = {}) => new AzureInventoryDriver(options));
126
+ registerCloudDriver('linode', (options = {}) => new LinodeInventoryDriver(options));
127
+ registerCloudDriver('hetzner', (options = {}) => new HetznerInventoryDriver(options));
128
+ registerCloudDriver('alibaba', (options = {}) => new AlibabaInventoryDriver(options));
129
+ registerCloudDriver('aliyun', (options = {}) => new AlibabaInventoryDriver(options));
130
+ registerCloudDriver('cloudflare', (options = {}) => new CloudflareInventoryDriver(options));
131
+ registerCloudDriver('cf', (options = {}) => new CloudflareInventoryDriver(options));
132
+ registerCloudDriver('mongodb-atlas', (options = {}) => new MongoDBAtlasInventoryDriver(options));
133
+ registerCloudDriver('atlas', (options = {}) => new MongoDBAtlasInventoryDriver(options));
134
+ registerMockDriver('aws-mock', AwsMockDriver);
135
+ registerMockDriver('gcp-mock', GcpMockDriver);
136
+ registerMockDriver('vultr-mock', VultrMockDriver);
137
+ registerMockDriver(['digitalocean-mock', 'do-mock'], DigitalOceanMockDriver);
138
+ registerMockDriver(['oracle-mock', 'oci-mock'], OracleMockDriver);
139
+ registerMockDriver(['azure-mock', 'az-mock'], AzureMockDriver);
140
+ registerMockDriver('linode-mock', LinodeMockDriver);
141
+ registerMockDriver('hetzner-mock', HetznerMockDriver);
142
+ registerMockDriver(['alibaba-mock', 'aliyun-mock'], AlibabaMockDriver);
143
+ registerMockDriver(['cloudflare-mock', 'cf-mock'], CloudflareMockDriver);
144
+ registerMockDriver(['mongodb-atlas-mock', 'atlas-mock'], MongoDBAtlasMockDriver);
145
+
146
+ export { BaseCloudDriver } from './drivers/base-driver.js';
@@ -0,0 +1,362 @@
1
+ /**
2
+ * Terraform/OpenTofu State Exporter for Cloud Inventory
3
+ *
4
+ * Converts discovered cloud resources into Terraform/OpenTofu state format (.tfstate)
5
+ * Enables "brownfield to IaC" workflows - import existing infrastructure into Terraform management
6
+ *
7
+ * @module terraform-exporter
8
+ */
9
+
10
+ /**
11
+ * Map cloud resource types to Terraform resource types
12
+ * Format: 'provider.service.resourceType' → 'terraform_resource_type'
13
+ */
14
+ const RESOURCE_TYPE_MAP = {
15
+ // AWS
16
+ 'aws.ec2.instance': 'aws_instance',
17
+ 'aws.s3.bucket': 'aws_s3_bucket',
18
+ 'aws.rds.instance': 'aws_db_instance',
19
+ 'aws.dynamodb.table': 'aws_dynamodb_table',
20
+ 'aws.lambda.function': 'aws_lambda_function',
21
+ 'aws.vpc.vpc': 'aws_vpc',
22
+ 'aws.vpc.subnet': 'aws_subnet',
23
+ 'aws.vpc.securitygroup': 'aws_security_group',
24
+ 'aws.vpc.routetable': 'aws_route_table',
25
+ 'aws.vpc.internetgateway': 'aws_internet_gateway',
26
+ 'aws.vpc.natgateway': 'aws_nat_gateway',
27
+ 'aws.elb.loadbalancer': 'aws_elb',
28
+ 'aws.elbv2.loadbalancer': 'aws_lb',
29
+ 'aws.elbv2.targetgroup': 'aws_lb_target_group',
30
+ 'aws.iam.user': 'aws_iam_user',
31
+ 'aws.iam.role': 'aws_iam_role',
32
+ 'aws.iam.policy': 'aws_iam_policy',
33
+ 'aws.kms.key': 'aws_kms_key',
34
+ 'aws.secretsmanager.secret': 'aws_secretsmanager_secret',
35
+ 'aws.ecs.cluster': 'aws_ecs_cluster',
36
+ 'aws.ecs.service': 'aws_ecs_service',
37
+ 'aws.ecs.taskdefinition': 'aws_ecs_task_definition',
38
+ 'aws.eks.cluster': 'aws_eks_cluster',
39
+ 'aws.eks.nodegroup': 'aws_eks_node_group',
40
+ 'aws.ecr.repository': 'aws_ecr_repository',
41
+ 'aws.route53.hostedzone': 'aws_route53_zone',
42
+ 'aws.cloudfront.distribution': 'aws_cloudfront_distribution',
43
+ 'aws.sqs.queue': 'aws_sqs_queue',
44
+ 'aws.sns.topic': 'aws_sns_topic',
45
+ 'aws.kinesis.stream': 'aws_kinesis_stream',
46
+ 'aws.ebs.volume': 'aws_ebs_volume',
47
+ 'aws.ebs.snapshot': 'aws_ebs_snapshot',
48
+ 'aws.efs.filesystem': 'aws_efs_file_system',
49
+ 'aws.elasticache.cluster': 'aws_elasticache_cluster',
50
+
51
+ // GCP
52
+ 'gcp.compute.instance': 'google_compute_instance',
53
+ 'gcp.storage.bucket': 'google_storage_bucket',
54
+ 'gcp.sql.instance': 'google_sql_database_instance',
55
+ 'gcp.gke.cluster': 'google_container_cluster',
56
+ 'gcp.compute.network': 'google_compute_network',
57
+ 'gcp.compute.subnetwork': 'google_compute_subnetwork',
58
+ 'gcp.compute.firewall': 'google_compute_firewall',
59
+ 'gcp.bigquery.dataset': 'google_bigquery_dataset',
60
+ 'gcp.pubsub.topic': 'google_pubsub_topic',
61
+ 'gcp.pubsub.subscription': 'google_pubsub_subscription',
62
+ 'gcp.functions.function': 'google_cloudfunctions_function',
63
+ 'gcp.run.service': 'google_cloud_run_service',
64
+ 'gcp.iam.serviceaccount': 'google_service_account',
65
+ 'gcp.kms.keyring': 'google_kms_key_ring',
66
+ 'gcp.secretmanager.secret': 'google_secret_manager_secret',
67
+
68
+ // Azure
69
+ 'azure.vm': 'azurerm_virtual_machine',
70
+ 'azure.vm.vmss': 'azurerm_virtual_machine_scale_set',
71
+ 'azure.storage.account': 'azurerm_storage_account',
72
+ 'azure.sql.server': 'azurerm_sql_server',
73
+ 'azure.sql.database': 'azurerm_sql_database',
74
+ 'azure.aks.cluster': 'azurerm_kubernetes_cluster',
75
+ 'azure.network.vnet': 'azurerm_virtual_network',
76
+ 'azure.network.subnet': 'azurerm_subnet',
77
+ 'azure.network.nsg': 'azurerm_network_security_group',
78
+ 'azure.network.loadbalancer': 'azurerm_lb',
79
+ 'azure.network.publicip': 'azurerm_public_ip',
80
+ 'azure.containerregistry': 'azurerm_container_registry',
81
+ 'azure.cosmosdb.account': 'azurerm_cosmosdb_account',
82
+ 'azure.identity.userassigned': 'azurerm_user_assigned_identity',
83
+ 'azure.dns.zone': 'azurerm_dns_zone',
84
+
85
+ // DigitalOcean
86
+ 'do.droplet': 'digitalocean_droplet',
87
+ 'do.kubernetes.cluster': 'digitalocean_kubernetes_cluster',
88
+ 'do.volume': 'digitalocean_volume',
89
+ 'do.loadbalancer': 'digitalocean_loadbalancer',
90
+ 'do.firewall': 'digitalocean_firewall',
91
+ 'do.vpc': 'digitalocean_vpc',
92
+ 'do.spaces.bucket': 'digitalocean_spaces_bucket',
93
+ 'do.database.cluster': 'digitalocean_database_cluster',
94
+ 'do.domain': 'digitalocean_domain',
95
+ 'do.cdn': 'digitalocean_cdn',
96
+ 'do.registry': 'digitalocean_container_registry',
97
+
98
+ // Alibaba Cloud
99
+ 'alibaba.ecs.instance': 'alicloud_instance',
100
+ 'alibaba.oss.bucket': 'alicloud_oss_bucket',
101
+ 'alibaba.rds.instance': 'alicloud_db_instance',
102
+ 'alibaba.ack.cluster': 'alicloud_cs_kubernetes',
103
+ 'alibaba.vpc.vpc': 'alicloud_vpc',
104
+ 'alibaba.vpc.vswitch': 'alicloud_vswitch',
105
+ 'alibaba.slb.loadbalancer': 'alicloud_slb',
106
+ 'alibaba.redis.instance': 'alicloud_kvstore_instance',
107
+ 'alibaba.cdn.distribution': 'alicloud_cdn_domain',
108
+ 'alibaba.dns.domain': 'alicloud_dns',
109
+ 'alibaba.ecs.securitygroup': 'alicloud_security_group',
110
+ 'alibaba.ecs.snapshot': 'alicloud_snapshot',
111
+ 'alibaba.ess.scalinggroup': 'alicloud_ess_scaling_group',
112
+ 'alibaba.ess.scalingconfiguration': 'alicloud_ess_scaling_configuration',
113
+ 'alibaba.natgateway': 'alicloud_nat_gateway',
114
+ 'alibaba.acr.repository': 'alicloud_cr_repo',
115
+
116
+ // Linode
117
+ 'linode.compute.instance': 'linode_instance',
118
+ 'linode.lke.cluster': 'linode_lke_cluster',
119
+ 'linode.volume': 'linode_volume',
120
+ 'linode.nodebalancer': 'linode_nodebalancer',
121
+ 'linode.firewall': 'linode_firewall',
122
+ 'linode.domain': 'linode_domain',
123
+ 'linode.objectstorage.bucket': 'linode_object_storage_bucket',
124
+ 'linode.database': 'linode_database_mysql', // or postgresql, mongodb
125
+
126
+ // Hetzner
127
+ 'hetzner.server': 'hetzner_server',
128
+ 'hetzner.volume': 'hetzner_volume',
129
+ 'hetzner.network': 'hetzner_network',
130
+ 'hetzner.loadbalancer': 'hetzner_load_balancer',
131
+ 'hetzner.firewall': 'hetzner_firewall',
132
+ 'hetzner.floatingip': 'hetzner_floating_ip',
133
+ 'hetzner.primaryip': 'hetzner_primary_ip',
134
+ 'hetzner.sshkey': 'hetzner_ssh_key',
135
+ 'hetzner.placementgroup': 'hetzner_placement_group',
136
+
137
+ // Vultr
138
+ 'vultr.instance': 'vultr_instance',
139
+ 'vultr.kubernetes.cluster': 'vultr_kubernetes',
140
+ 'vultr.blockstorage': 'vultr_block_storage',
141
+ 'vultr.loadbalancer': 'vultr_load_balancer',
142
+ 'vultr.firewall.group': 'vultr_firewall_group',
143
+ 'vultr.vpc': 'vultr_vpc',
144
+ 'vultr.objectstorage': 'vultr_object_storage',
145
+ 'vultr.database': 'vultr_database',
146
+
147
+ // Oracle Cloud
148
+ 'oci.compute.instance': 'oci_core_instance',
149
+ 'oci.objectstorage.bucket': 'oci_objectstorage_bucket',
150
+ 'oci.database.autonomousdatabase': 'oci_database_autonomous_database',
151
+ 'oci.database.dbsystem': 'oci_database_db_system',
152
+ 'oci.kubernetes.cluster': 'oci_containerengine_cluster',
153
+ 'oci.vcn': 'oci_core_vcn',
154
+ 'oci.vcn.subnet': 'oci_core_subnet',
155
+ 'oci.compute.volume': 'oci_core_volume',
156
+ 'oci.filestorage.filesystem': 'oci_file_storage_file_system',
157
+ 'oci.loadbalancer': 'oci_load_balancer',
158
+ 'oci.dns.zone': 'oci_dns_zone',
159
+
160
+ // Cloudflare
161
+ 'cloudflare.workers.script': 'cloudflare_worker_script',
162
+ 'cloudflare.r2.bucket': 'cloudflare_r2_bucket',
163
+ 'cloudflare.pages.project': 'cloudflare_pages_project',
164
+ 'cloudflare.d1.database': 'cloudflare_d1_database',
165
+ 'cloudflare.kv.namespace': 'cloudflare_workers_kv_namespace',
166
+ 'cloudflare.durable-objects.namespace': 'cloudflare_workers_durable_object_namespace',
167
+ 'cloudflare.zone': 'cloudflare_zone',
168
+ 'cloudflare.dns.record': 'cloudflare_record',
169
+ 'cloudflare.loadbalancer': 'cloudflare_load_balancer',
170
+ 'cloudflare.ssl.certificate': 'cloudflare_origin_ca_certificate',
171
+ 'cloudflare.waf.ruleset': 'cloudflare_ruleset',
172
+ 'cloudflare.access.application': 'cloudflare_access_application',
173
+ 'cloudflare.access.policy': 'cloudflare_access_policy',
174
+
175
+ // MongoDB Atlas
176
+ 'mongodb-atlas.project': 'mongodbatlas_project',
177
+ 'mongodb-atlas.cluster': 'mongodbatlas_cluster',
178
+ 'mongodb-atlas.serverless': 'mongodbatlas_serverless_instance',
179
+ 'mongodb-atlas.user': 'mongodbatlas_database_user',
180
+ 'mongodb-atlas.accesslist': 'mongodbatlas_project_ip_access_list',
181
+ 'mongodb-atlas.backup': 'mongodbatlas_cloud_backup_snapshot',
182
+ 'mongodb-atlas.alert': 'mongodbatlas_alert_configuration',
183
+ 'mongodb-atlas.datalake': 'mongodbatlas_data_lake',
184
+ 'mongodb-atlas.search.index': 'mongodbatlas_search_index',
185
+ 'mongodb-atlas.customrole': 'mongodbatlas_custom_db_role',
186
+ 'mongodb-atlas.event': 'mongodbatlas_event_trigger'
187
+ };
188
+
189
+ /**
190
+ * Get Terraform provider name from cloud resource type
191
+ * @param {string} resourceType - Cloud inventory resource type (e.g., 'aws.ec2.instance')
192
+ * @returns {string} - Provider name (e.g., 'registry.terraform.io/hashicorp/aws')
193
+ */
194
+ function getProviderConfig(resourceType) {
195
+ const provider = resourceType.split('.')[0];
196
+
197
+ const providerMap = {
198
+ 'aws': 'registry.terraform.io/hashicorp/aws',
199
+ 'gcp': 'registry.terraform.io/hashicorp/google',
200
+ 'azure': 'registry.terraform.io/hashicorp/azurerm',
201
+ 'do': 'registry.terraform.io/digitalocean/digitalocean',
202
+ 'alibaba': 'registry.terraform.io/aliyun/alicloud',
203
+ 'linode': 'registry.terraform.io/linode/linode',
204
+ 'hetzner': 'registry.terraform.io/hetznercloud/hetzner',
205
+ 'vultr': 'registry.terraform.io/vultr/vultr',
206
+ 'oci': 'registry.terraform.io/hashicorp/oci',
207
+ 'oracle': 'registry.terraform.io/hashicorp/oci',
208
+ 'cloudflare': 'registry.terraform.io/cloudflare/cloudflare',
209
+ 'mongodb-atlas': 'registry.terraform.io/mongodb/mongodbatlas'
210
+ };
211
+
212
+ return providerMap[provider] || `registry.terraform.io/hashicorp/${provider}`;
213
+ }
214
+
215
+ /**
216
+ * Convert cloud inventory resource to Terraform resource format
217
+ * @param {Object} resource - Cloud inventory resource
218
+ * @returns {Object|null} - Terraform resource or null if not mappable
219
+ */
220
+ export function convertToTerraformResource(resource) {
221
+ const { resourceType, resourceId, configuration } = resource;
222
+
223
+ // Map resource type
224
+ const tfType = RESOURCE_TYPE_MAP[resourceType];
225
+ if (!tfType) {
226
+ return null; // Skip unmapped resource types
227
+ }
228
+
229
+ // Generate resource name from resourceId (sanitize for Terraform)
230
+ const resourceName = sanitizeResourceName(resourceId);
231
+
232
+ // Get provider configuration
233
+ const providerFqn = getProviderConfig(resourceType);
234
+
235
+ return {
236
+ mode: 'managed',
237
+ type: tfType,
238
+ name: resourceName,
239
+ provider: providerFqn,
240
+ instances: [
241
+ {
242
+ schema_version: 0,
243
+ attributes: configuration || {},
244
+ private: 'bnVsbA==', // base64("null")
245
+ dependencies: []
246
+ }
247
+ ]
248
+ };
249
+ }
250
+
251
+ /**
252
+ * Sanitize resource ID for Terraform resource name
253
+ * Terraform names must: start with letter, contain only letters/numbers/underscores/hyphens
254
+ * @param {string} resourceId
255
+ * @returns {string}
256
+ */
257
+ function sanitizeResourceName(resourceId) {
258
+ let name = String(resourceId)
259
+ .toLowerCase()
260
+ .replace(/[^a-z0-9_-]/g, '_')
261
+ .replace(/^[^a-z]+/, '') // Remove leading non-letters
262
+ .replace(/_+/g, '_')
263
+ .replace(/^_+|_+$/g, ''); // Trim underscores
264
+
265
+ // Ensure it starts with a letter
266
+ if (!name || !/^[a-z]/.test(name)) {
267
+ name = `resource_${name}`;
268
+ }
269
+
270
+ // Limit length
271
+ return name.slice(0, 64) || 'resource';
272
+ }
273
+
274
+ /**
275
+ * Export cloud inventory snapshots to Terraform state format
276
+ * @param {Array} snapshots - Array of cloud inventory snapshot records
277
+ * @param {Object} options - Export options
278
+ * @param {string} options.terraformVersion - Terraform version (default: '1.5.0')
279
+ * @param {string} options.lineage - State lineage UUID (default: auto-generated)
280
+ * @param {number} options.serial - State serial number (default: 1)
281
+ * @param {Object} options.outputs - Terraform outputs (default: {})
282
+ * @param {Array<string>} options.resourceTypes - Filter by cloud resource types
283
+ * @param {Array<string>} options.providers - Filter by provider
284
+ * @returns {Object} - Terraform state object
285
+ */
286
+ export function exportToTerraformState(snapshots, options = {}) {
287
+ const {
288
+ terraformVersion = '1.5.0',
289
+ lineage = generateLineage(),
290
+ serial = 1,
291
+ outputs = {},
292
+ resourceTypes = [],
293
+ providers = []
294
+ } = options;
295
+
296
+ // Filter snapshots
297
+ let filteredSnapshots = snapshots;
298
+
299
+ if (resourceTypes.length > 0) {
300
+ filteredSnapshots = filteredSnapshots.filter(s =>
301
+ resourceTypes.includes(s.resourceType)
302
+ );
303
+ }
304
+
305
+ if (providers.length > 0) {
306
+ filteredSnapshots = filteredSnapshots.filter(s => {
307
+ const provider = s.resourceType.split('.')[0];
308
+ return providers.includes(provider);
309
+ });
310
+ }
311
+
312
+ // Convert to Terraform resources
313
+ const tfResources = [];
314
+ const skipped = [];
315
+
316
+ for (const snapshot of filteredSnapshots) {
317
+ const tfResource = convertToTerraformResource(snapshot);
318
+ if (tfResource) {
319
+ tfResources.push(tfResource);
320
+ } else {
321
+ skipped.push(snapshot.resourceType);
322
+ }
323
+ }
324
+
325
+ // Construct Terraform state
326
+ const state = {
327
+ version: 4,
328
+ terraform_version: terraformVersion,
329
+ serial,
330
+ lineage,
331
+ outputs,
332
+ resources: tfResources
333
+ };
334
+
335
+ return {
336
+ state,
337
+ stats: {
338
+ total: snapshots.length,
339
+ converted: tfResources.length,
340
+ skipped: skipped.length,
341
+ skippedTypes: [...new Set(skipped)]
342
+ }
343
+ };
344
+ }
345
+
346
+ /**
347
+ * Generate a UUID for Terraform state lineage
348
+ * @returns {string}
349
+ */
350
+ function generateLineage() {
351
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
352
+ const r = Math.random() * 16 | 0;
353
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
354
+ return v.toString(16);
355
+ });
356
+ }
357
+
358
+ export default {
359
+ convertToTerraformResource,
360
+ exportToTerraformState,
361
+ RESOURCE_TYPE_MAP
362
+ };