From a5d68fd820fcb2f9cde4388957cfab094ca26c8b Mon Sep 17 00:00:00 2001 From: jeffhiltz Date: Fri, 19 Dec 2025 11:48:38 -0500 Subject: [PATCH 01/14] initial version of Iceberg-backed Glue Table --- lib/shortcuts/api.md | 25 ++++ lib/shortcuts/glue-iceberg-table.js | 106 +++++++++++++++ lib/shortcuts/index.js | 1 + .../glue-iceberg-table-defaults.json | 50 +++++++ .../glue-iceberg-table-no-defaults.json | 60 +++++++++ ...e-iceberg-table-with-optimizer-custom.json | 93 +++++++++++++ ...iceberg-table-with-optimizer-defaults.json | 73 ++++++++++ test/shortcuts.test.js | 126 ++++++++++++++++++ 8 files changed, 534 insertions(+) create mode 100644 lib/shortcuts/glue-iceberg-table.js create mode 100644 test/fixtures/shortcuts/glue-iceberg-table-defaults.json create mode 100644 test/fixtures/shortcuts/glue-iceberg-table-no-defaults.json create mode 100644 test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json create mode 100644 test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-defaults.json diff --git a/lib/shortcuts/api.md b/lib/shortcuts/api.md index dcfabe1..3a906a3 100644 --- a/lib/shortcuts/api.md +++ b/lib/shortcuts/api.md @@ -12,6 +12,9 @@ a Lambda permission.

GlueDatabase

Create a Glue Database.

+
GlueIcebergTable
+

Create a Glue table backed by Apache Iceberg format on S3.

+
GlueJsonTable

Create a Glue Table backed by line-delimited JSON files on S3.

@@ -202,6 +205,28 @@ const db = new cf.shortcuts.GlueDatabase({ module.exports = cf.merge(myTemplate, db); ``` + + +## GlueIcebergTable +Create a Glue table backed by Apache Iceberg format on S3. + +**Kind**: global class + + +### new GlueIcebergTable(options) + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| options | Object | | Accepts the same options as cloudfriend's [`GlueTable`](https://github.com/mapbox/cloudfriend/blob/master/lib/shortcuts/glue-table.js), though the following additional attributes are either required or hard-wired: | +| options.Location | String | | The physical location of the table. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-storagedescriptor.html#cfn-glue-table-storagedescriptor-location). | +| [options.TableType] | String | 'EXTERNAL_TABLE' | Hard-wired by this shortcut. | +| [options.IcebergVersion] | String | '2' | The table version for the Iceberg table. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-iceberginput.html). | +| [options.EnableOptimizer] | Boolean | false | Whether to enable the snapshot retention optimizer for this Iceberg table. | +| [options.OptimizerRoleArn] | String | | The ARN of the IAM role for the table optimizer to use. Required if EnableOptimizer is true. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). | +| [options.SnapshotRetentionPeriodInDays] | Number | 5 | The number of days to retain snapshots. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). | +| [options.NumberOfSnapshotsToRetain] | Number | 1 | The minimum number of snapshots to retain. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). | +| [options.CleanExpiredFiles] | Boolean | true | Whether to delete expired data files after expiring snapshots. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). | + ## GlueJsonTable diff --git a/lib/shortcuts/glue-iceberg-table.js b/lib/shortcuts/glue-iceberg-table.js new file mode 100644 index 0000000..0fec1f1 --- /dev/null +++ b/lib/shortcuts/glue-iceberg-table.js @@ -0,0 +1,106 @@ +'use strict'; + +const GlueTable = require('./glue-table'); + +/** + * Create a Glue table backed by Apache Iceberg format on S3. + * + * @param {Object} options - Accepts the same options as cloudfriend's + * [`GlueTable`](https://github.com/mapbox/cloudfriend/blob/master/lib/shortcuts/glue-table.js), + * though the following additional attributes are either required or hard-wired: + * @param {String} options.Location - The physical location of the table. See + * [AWS + * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-storagedescriptor.html#cfn-glue-table-storagedescriptor-location). + * @param {String} [options.TableType='EXTERNAL_TABLE'] - Hard-wired by this + * shortcut. + * @param {String} [options.IcebergVersion='2'] - The table version for the + * Iceberg table. See [AWS + * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-iceberginput.html). + * @param {Boolean} [options.EnableOptimizer=false] - Whether to enable the + * snapshot retention optimizer for this Iceberg table. + * @param {String} [options.OptimizerRoleArn=undefined] - The ARN of the IAM + * role for the table optimizer to use. Required if EnableOptimizer is true. + * See [AWS + * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). + * @param {Number} [options.SnapshotRetentionPeriodInDays=5] - The number of + * days to retain snapshots. See [AWS + * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). + * @param {Number} [options.NumberOfSnapshotsToRetain=1] - The minimum number + * of snapshots to retain. See [AWS + * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). + * @param {Boolean} [options.CleanExpiredFiles=true] - Whether to delete + * expired data files after expiring snapshots. See [AWS + * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). + */ +class GlueIcebergTable extends GlueTable { + constructor(options) { + if (!options) throw new Error('Options required'); + const { + Location, + IcebergVersion = '2', + EnableOptimizer = false, + OptimizerRoleArn, + SnapshotRetentionPeriodInDays = 5, + NumberOfSnapshotsToRetain = 1, + CleanExpiredFiles = true + } = options; + + const required = [Location]; + if (required.some((variable) => !variable)) + throw new Error('You must provide a Location'); + + if (EnableOptimizer && !OptimizerRoleArn) + throw new Error('You must provide an OptimizerRoleArn when EnableOptimizer is true'); + + super( + Object.assign( + { + TableType: 'EXTERNAL_TABLE', + Parameters: { EXTERNAL: 'TRUE' } + }, + options + ) + ); + + const logicalName = options.LogicalName; + this.Resources[logicalName].Properties.OpenTableFormatInput = { + IcebergInput: { + MetadataOperation: 'CREATE', + Version: IcebergVersion + } + }; + + // Optionally add TableOptimizer for configuring snapshot retention + if (EnableOptimizer) { + const optimizerLogicalName = `${logicalName}Optimizer`; + this.Resources[optimizerLogicalName] = { + Type: 'AWS::Glue::TableOptimizer', + DependsOn: logicalName, + Properties: { + CatalogId: options.CatalogId || { Ref: 'AWS::AccountId' }, + DatabaseName: options.DatabaseName, + TableName: options.Name, + Type: 'retention', + TableOptimizerConfiguration: { + RoleArn: OptimizerRoleArn, + Enabled: true, + RetentionConfiguration: { + IcebergConfiguration: { + SnapshotRetentionPeriodInDays, + NumberOfSnapshotsToRetain, + CleanExpiredFiles + } + } + } + } + }; + + // Apply Condition to optimizer if specified on the table + if (options.Condition) { + this.Resources[optimizerLogicalName].Condition = options.Condition; + } + } + } +} + +module.exports = GlueIcebergTable; diff --git a/lib/shortcuts/index.js b/lib/shortcuts/index.js index 54e40fe..63fa0f9 100644 --- a/lib/shortcuts/index.js +++ b/lib/shortcuts/index.js @@ -16,6 +16,7 @@ module.exports = { GlueJsonTable: require('./glue-json-table'), GlueOrcTable: require('./glue-orc-table'), GlueParquetTable: require('./glue-parquet-table'), + GlueIcebergTable: require('./glue-iceberg-table'), GluePrestoView: require('./glue-presto-view'), GlueSparkView: require('./glue-spark-view'), hookshot: require('./hookshot'), diff --git a/test/fixtures/shortcuts/glue-iceberg-table-defaults.json b/test/fixtures/shortcuts/glue-iceberg-table-defaults.json new file mode 100644 index 0000000..2d00ccf --- /dev/null +++ b/test/fixtures/shortcuts/glue-iceberg-table-defaults.json @@ -0,0 +1,50 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Metadata": {}, + "Parameters": {}, + "Rules": {}, + "Mappings": {}, + "Conditions": {}, + "Resources": { + "MyTable": { + "Type": "AWS::Glue::Table", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableInput": { + "Description": { + "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" + }, + "Name": "my_table", + "Parameters": { + "EXTERNAL": "TRUE" + }, + "PartitionKeys": [], + "TableType": "EXTERNAL_TABLE", + "StorageDescriptor": { + "Columns": [ + { + "Name": "column", + "Type": "string" + } + ], + "Compressed": false, + "Location": "s3://fake/location", + "NumberOfBuckets": 0, + "SerdeInfo": {}, + "StoredAsSubDirectories": true + } + }, + "OpenTableFormatInput": { + "IcebergInput": { + "MetadataOperation": "CREATE", + "Version": "2" + } + } + } + } + }, + "Outputs": {} +} \ No newline at end of file diff --git a/test/fixtures/shortcuts/glue-iceberg-table-no-defaults.json b/test/fixtures/shortcuts/glue-iceberg-table-no-defaults.json new file mode 100644 index 0000000..3c16713 --- /dev/null +++ b/test/fixtures/shortcuts/glue-iceberg-table-no-defaults.json @@ -0,0 +1,60 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Metadata": {}, + "Parameters": {}, + "Rules": {}, + "Mappings": {}, + "Conditions": { + "Always": { + "Fn::Equals": [ + "1", + "1" + ] + } + }, + "Resources": { + "AnotherThing": { + "Type": "AWS::SNS::Topic" + }, + "MyTable": { + "Type": "AWS::Glue::Table", + "Condition": "Always", + "DependsOn": "AnotherThing", + "Properties": { + "CatalogId": "1234", + "DatabaseName": "my_database", + "TableInput": { + "Description": "my_table description", + "Name": "my_table", + "Owner": "Team", + "Parameters": { + "table": "params" + }, + "PartitionKeys": [], + "Retention": 12, + "TableType": "EXTERNAL_TABLE", + "StorageDescriptor": { + "Columns": [ + { + "Name": "column", + "Type": "string" + } + ], + "Compressed": false, + "Location": "s3://fake/location", + "NumberOfBuckets": 0, + "SerdeInfo": {}, + "StoredAsSubDirectories": true + } + }, + "OpenTableFormatInput": { + "IcebergInput": { + "MetadataOperation": "CREATE", + "Version": "2" + } + } + } + } + }, + "Outputs": {} +} \ No newline at end of file diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json b/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json new file mode 100644 index 0000000..29962ca --- /dev/null +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json @@ -0,0 +1,93 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Metadata": {}, + "Parameters": {}, + "Rules": {}, + "Mappings": {}, + "Conditions": { + "Always": { + "Fn::Equals": [ + "1", + "1" + ] + } + }, + "Resources": { + "OptimizerRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": {} + } + }, + "MyTable": { + "Type": "AWS::Glue::Table", + "Condition": "Always", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableInput": { + "Description": { + "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" + }, + "Name": "my_table", + "Parameters": { + "EXTERNAL": "TRUE" + }, + "PartitionKeys": [], + "TableType": "EXTERNAL_TABLE", + "StorageDescriptor": { + "Columns": [ + { + "Name": "column", + "Type": "string" + } + ], + "Compressed": false, + "Location": "s3://fake/location", + "NumberOfBuckets": 0, + "SerdeInfo": {}, + "StoredAsSubDirectories": true + } + }, + "OpenTableFormatInput": { + "IcebergInput": { + "MetadataOperation": "CREATE", + "Version": "2" + } + } + } + }, + "MyTableOptimizer": { + "Type": "AWS::Glue::TableOptimizer", + "DependsOn": "MyTable", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableName": "my_table", + "Type": "retention", + "TableOptimizerConfiguration": { + "RoleArn": { + "Fn::GetAtt": [ + "OptimizerRole", + "Arn" + ] + }, + "Enabled": true, + "RetentionConfiguration": { + "IcebergConfiguration": { + "SnapshotRetentionPeriodInDays": 7, + "NumberOfSnapshotsToRetain": 3, + "CleanExpiredFiles": false + } + } + } + }, + "Condition": "Always" + } + }, + "Outputs": {} +} \ No newline at end of file diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-defaults.json b/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-defaults.json new file mode 100644 index 0000000..f21a4a8 --- /dev/null +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-defaults.json @@ -0,0 +1,73 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Metadata": {}, + "Parameters": {}, + "Rules": {}, + "Mappings": {}, + "Conditions": {}, + "Resources": { + "MyTable": { + "Type": "AWS::Glue::Table", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableInput": { + "Description": { + "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" + }, + "Name": "my_table", + "Parameters": { + "EXTERNAL": "TRUE" + }, + "PartitionKeys": [], + "TableType": "EXTERNAL_TABLE", + "StorageDescriptor": { + "Columns": [ + { + "Name": "column", + "Type": "string" + } + ], + "Compressed": false, + "Location": "s3://fake/location", + "NumberOfBuckets": 0, + "SerdeInfo": {}, + "StoredAsSubDirectories": true + } + }, + "OpenTableFormatInput": { + "IcebergInput": { + "MetadataOperation": "CREATE", + "Version": "2" + } + } + } + }, + "MyTableOptimizer": { + "Type": "AWS::Glue::TableOptimizer", + "DependsOn": "MyTable", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableName": "my_table", + "Type": "retention", + "TableOptimizerConfiguration": { + "RoleArn": "arn:aws:iam::123456789012:role/OptimizerRole", + "Enabled": true, + "RetentionConfiguration": { + "IcebergConfiguration": { + "SnapshotRetentionPeriodInDays": 5, + "NumberOfSnapshotsToRetain": 1, + "CleanExpiredFiles": true + } + } + } + } + } + }, + "Outputs": {} +} \ No newline at end of file diff --git a/test/shortcuts.test.js b/test/shortcuts.test.js index a51fbb2..4458489 100644 --- a/test/shortcuts.test.js +++ b/test/shortcuts.test.js @@ -1434,6 +1434,132 @@ test('[shortcuts] glue parquet table', (assert) => { assert.end(); }); +test('[shortcuts] glue iceberg table', (assert) => { + assert.throws( + () => new cf.shortcuts.GlueIcebergTable(), + 'Options required', + 'throws without options' + ); + assert.throws( + () => new cf.shortcuts.GlueIcebergTable({}), + /You must provide a Location/, + 'throws without required parameters' + ); + + let db = new cf.shortcuts.GlueIcebergTable({ + LogicalName: 'MyTable', + DatabaseName: 'my_database', + Name: 'my_table', + Columns: [ + { Name: 'column', Type: 'string' } + ], + Location: 's3://fake/location' + }); + + let template = cf.merge(db); + if (update) fixtures.update('glue-iceberg-table-defaults', template); + assert.deepEqual( + noUndefined(template), + fixtures.get('glue-iceberg-table-defaults'), + 'expected resources generated with defaults' + ); + + db = new cf.shortcuts.GlueIcebergTable({ + LogicalName: 'MyTable', + DatabaseName: 'my_database', + Name: 'my_table', + Columns: [ + { Name: 'column', Type: 'string' } + ], + CatalogId: '1234', + Owner: 'Team', + Parameters: { table: 'params' }, + Description: 'my_table description', + Retention: 12, + Location: 's3://fake/location', + IcebergVersion: '2', + Condition: 'Always', + DependsOn: 'AnotherThing' + }); + + template = cf.merge( + { Conditions: { Always: cf.equals('1', '1') } }, + { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } }, + db + ); + if (update) fixtures.update('glue-iceberg-table-no-defaults', template); + assert.deepEqual( + noUndefined(template), + fixtures.get('glue-iceberg-table-no-defaults'), + 'expected resources generated without defaults' + ); + + assert.throws( + () => new cf.shortcuts.GlueIcebergTable({ + LogicalName: 'MyTable', + DatabaseName: 'my_database', + Name: 'my_table', + Columns: [ + { Name: 'column', Type: 'string' } + ], + Location: 's3://fake/location', + EnableOptimizer: true + }), + /You must provide an OptimizerRoleArn when EnableOptimizer is true/, + 'throws when EnableOptimizer is true but OptimizerRoleArn is missing' + ); + + db = new cf.shortcuts.GlueIcebergTable({ + LogicalName: 'MyTable', + DatabaseName: 'my_database', + Name: 'my_table', + Columns: [ + { Name: 'column', Type: 'string' } + ], + Location: 's3://fake/location', + EnableOptimizer: true, + OptimizerRoleArn: 'arn:aws:iam::123456789012:role/OptimizerRole' + }); + + template = cf.merge(db); + if (update) fixtures.update('glue-iceberg-table-with-optimizer-defaults', template); + assert.deepEqual( + noUndefined(template), + fixtures.get('glue-iceberg-table-with-optimizer-defaults'), + 'expected resources generated with optimizer using default retention settings' + ); + + db = new cf.shortcuts.GlueIcebergTable({ + LogicalName: 'MyTable', + DatabaseName: 'my_database', + Name: 'my_table', + Columns: [ + { Name: 'column', Type: 'string' } + ], + Location: 's3://fake/location', + EnableOptimizer: true, + OptimizerRoleArn: cf.getAtt('OptimizerRole', 'Arn'), + SnapshotRetentionPeriodInDays: 7, + NumberOfSnapshotsToRetain: 3, + CleanExpiredFiles: false, + Condition: 'Always' + }); + + template = cf.merge( + { Conditions: { Always: cf.equals('1', '1') } }, + { Resources: { OptimizerRole: { Type: 'AWS::IAM::Role', Properties: { AssumeRolePolicyDocument: {} } } } }, + db + ); + if (update) fixtures.update('glue-iceberg-table-with-optimizer-custom', template); + assert.deepEqual( + noUndefined(template), + fixtures.get('glue-iceberg-table-with-optimizer-custom'), + 'expected resources generated with optimizer using custom retention settings' + ); + + assert.end(); +}); + test('[shortcuts] glue view', (assert) => { assert.throws( () => new cf.shortcuts.GluePrestoView(), From c2f258f74f0bbc038e4ce0a85d99b30adec0850a Mon Sep 17 00:00:00 2001 From: jeffhiltz Date: Fri, 19 Dec 2025 12:30:41 -0500 Subject: [PATCH 02/14] add compaction configuration to Glue Iceberg tables --- lib/shortcuts/api.md | 7 +- lib/shortcuts/glue-iceberg-table.js | 66 +++++++++++- ...ue-iceberg-table-with-both-optimizers.json | 96 +++++++++++++++++ ...-iceberg-table-with-compaction-custom.json | 84 +++++++++++++++ ...ceberg-table-with-compaction-defaults.json | 73 +++++++++++++ ...e-iceberg-table-with-optimizer-custom.json | 2 +- ...iceberg-table-with-optimizer-defaults.json | 2 +- test/shortcuts.test.js | 101 ++++++++++++++++++ 8 files changed, 424 insertions(+), 7 deletions(-) create mode 100644 test/fixtures/shortcuts/glue-iceberg-table-with-both-optimizers.json create mode 100644 test/fixtures/shortcuts/glue-iceberg-table-with-compaction-custom.json create mode 100644 test/fixtures/shortcuts/glue-iceberg-table-with-compaction-defaults.json diff --git a/lib/shortcuts/api.md b/lib/shortcuts/api.md index 3a906a3..c4ebb6e 100644 --- a/lib/shortcuts/api.md +++ b/lib/shortcuts/api.md @@ -222,10 +222,15 @@ Create a Glue table backed by Apache Iceberg format on S3. | [options.TableType] | String | 'EXTERNAL_TABLE' | Hard-wired by this shortcut. | | [options.IcebergVersion] | String | '2' | The table version for the Iceberg table. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-iceberginput.html). | | [options.EnableOptimizer] | Boolean | false | Whether to enable the snapshot retention optimizer for this Iceberg table. | -| [options.OptimizerRoleArn] | String | | The ARN of the IAM role for the table optimizer to use. Required if EnableOptimizer is true. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). | +| [options.OptimizerRoleArn] | String | | The ARN of the IAM role for the retention optimizer to use. Required if EnableOptimizer is true. Can be the same role as CompactionRoleArn if both optimizers are enabled. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). | | [options.SnapshotRetentionPeriodInDays] | Number | 5 | The number of days to retain snapshots. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). | | [options.NumberOfSnapshotsToRetain] | Number | 1 | The minimum number of snapshots to retain. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). | | [options.CleanExpiredFiles] | Boolean | true | Whether to delete expired data files after expiring snapshots. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). | +| [options.EnableCompaction] | Boolean | false | Whether to enable the compaction optimizer for this Iceberg table. | +| [options.CompactionRoleArn] | String | | The ARN of the IAM role for the compaction optimizer to use. Required if EnableCompaction is true. Can be the same role as OptimizerRoleArn if both optimizers are enabled. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). | +| [options.CompactionStrategy] | String | 'binpack' | The compaction strategy: binpack, sort, or z-order. See [AWS documentation](https://docs.aws.amazon.com/glue/latest/dg/enable-compaction.html). | +| [options.MinInputFiles] | Number | 100 | Minimum number of data files before compaction triggers. | +| [options.DeleteFileThreshold] | Number | 1 | Minimum deletes in a file to make it eligible for compaction. | diff --git a/lib/shortcuts/glue-iceberg-table.js b/lib/shortcuts/glue-iceberg-table.js index 0fec1f1..3e84856 100644 --- a/lib/shortcuts/glue-iceberg-table.js +++ b/lib/shortcuts/glue-iceberg-table.js @@ -19,8 +19,9 @@ const GlueTable = require('./glue-table'); * @param {Boolean} [options.EnableOptimizer=false] - Whether to enable the * snapshot retention optimizer for this Iceberg table. * @param {String} [options.OptimizerRoleArn=undefined] - The ARN of the IAM - * role for the table optimizer to use. Required if EnableOptimizer is true. - * See [AWS + * role for the retention optimizer to use. Required if EnableOptimizer is + * true. Can be the same role as CompactionRoleArn if both optimizers are + * enabled. See [AWS * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). * @param {Number} [options.SnapshotRetentionPeriodInDays=5] - The number of * days to retain snapshots. See [AWS @@ -31,6 +32,20 @@ const GlueTable = require('./glue-table'); * @param {Boolean} [options.CleanExpiredFiles=true] - Whether to delete * expired data files after expiring snapshots. See [AWS * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). + * @param {Boolean} [options.EnableCompaction=false] - Whether to enable the + * compaction optimizer for this Iceberg table. + * @param {String} [options.CompactionRoleArn=undefined] - The ARN of the IAM + * role for the compaction optimizer to use. Required if EnableCompaction is + * true. Can be the same role as OptimizerRoleArn if both optimizers are + * enabled. See [AWS + * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). + * @param {String} [options.CompactionStrategy='binpack'] - The compaction + * strategy: binpack, sort, or z-order. See [AWS + * documentation](https://docs.aws.amazon.com/glue/latest/dg/enable-compaction.html). + * @param {Number} [options.MinInputFiles=100] - Minimum number of data files + * before compaction triggers. + * @param {Number} [options.DeleteFileThreshold=1] - Minimum deletes in a file + * to make it eligible for compaction. */ class GlueIcebergTable extends GlueTable { constructor(options) { @@ -42,7 +57,12 @@ class GlueIcebergTable extends GlueTable { OptimizerRoleArn, SnapshotRetentionPeriodInDays = 5, NumberOfSnapshotsToRetain = 1, - CleanExpiredFiles = true + CleanExpiredFiles = true, + EnableCompaction = false, + CompactionRoleArn, + CompactionStrategy = 'binpack', + MinInputFiles = 100, + DeleteFileThreshold = 1 } = options; const required = [Location]; @@ -52,6 +72,13 @@ class GlueIcebergTable extends GlueTable { if (EnableOptimizer && !OptimizerRoleArn) throw new Error('You must provide an OptimizerRoleArn when EnableOptimizer is true'); + if (EnableCompaction && !CompactionRoleArn) + throw new Error('You must provide a CompactionRoleArn when EnableCompaction is true'); + + const validStrategies = ['binpack', 'sort', 'z-order']; + if (!validStrategies.includes(CompactionStrategy)) + throw new Error('CompactionStrategy must be one of: binpack, sort, z-order'); + super( Object.assign( { @@ -72,7 +99,7 @@ class GlueIcebergTable extends GlueTable { // Optionally add TableOptimizer for configuring snapshot retention if (EnableOptimizer) { - const optimizerLogicalName = `${logicalName}Optimizer`; + const optimizerLogicalName = `${logicalName}RetentionOptimizer`; this.Resources[optimizerLogicalName] = { Type: 'AWS::Glue::TableOptimizer', DependsOn: logicalName, @@ -100,6 +127,37 @@ class GlueIcebergTable extends GlueTable { this.Resources[optimizerLogicalName].Condition = options.Condition; } } + + // Optionally add TableOptimizer for compaction + if (EnableCompaction) { + const compactionLogicalName = `${logicalName}CompactionOptimizer`; + this.Resources[compactionLogicalName] = { + Type: 'AWS::Glue::TableOptimizer', + DependsOn: logicalName, + Properties: { + CatalogId: options.CatalogId || { Ref: 'AWS::AccountId' }, + DatabaseName: options.DatabaseName, + TableName: options.Name, + Type: 'compaction', + TableOptimizerConfiguration: { + RoleArn: CompactionRoleArn, + Enabled: true, + CompactionConfiguration: { + IcebergConfiguration: { + Strategy: CompactionStrategy, + MinInputFiles, + DeleteFileThreshold + } + } + } + } + }; + + // Apply Condition to compaction optimizer if specified on the table + if (options.Condition) { + this.Resources[compactionLogicalName].Condition = options.Condition; + } + } } } diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-both-optimizers.json b/test/fixtures/shortcuts/glue-iceberg-table-with-both-optimizers.json new file mode 100644 index 0000000..5874a2c --- /dev/null +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-both-optimizers.json @@ -0,0 +1,96 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Metadata": {}, + "Parameters": {}, + "Rules": {}, + "Mappings": {}, + "Conditions": {}, + "Resources": { + "MyTable": { + "Type": "AWS::Glue::Table", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableInput": { + "Description": { + "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" + }, + "Name": "my_table", + "Parameters": { + "EXTERNAL": "TRUE" + }, + "PartitionKeys": [], + "TableType": "EXTERNAL_TABLE", + "StorageDescriptor": { + "Columns": [ + { + "Name": "column", + "Type": "string" + } + ], + "Compressed": false, + "Location": "s3://fake/location", + "NumberOfBuckets": 0, + "SerdeInfo": {}, + "StoredAsSubDirectories": true + } + }, + "OpenTableFormatInput": { + "IcebergInput": { + "MetadataOperation": "CREATE", + "Version": "2" + } + } + } + }, + "MyTableRetentionOptimizer": { + "Type": "AWS::Glue::TableOptimizer", + "DependsOn": "MyTable", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableName": "my_table", + "Type": "retention", + "TableOptimizerConfiguration": { + "RoleArn": "arn:aws:iam::123456789012:role/RetentionRole", + "Enabled": true, + "RetentionConfiguration": { + "IcebergConfiguration": { + "SnapshotRetentionPeriodInDays": 5, + "NumberOfSnapshotsToRetain": 1, + "CleanExpiredFiles": true + } + } + } + } + }, + "MyTableCompactionOptimizer": { + "Type": "AWS::Glue::TableOptimizer", + "DependsOn": "MyTable", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableName": "my_table", + "Type": "compaction", + "TableOptimizerConfiguration": { + "RoleArn": "arn:aws:iam::123456789012:role/CompactionRole", + "Enabled": true, + "CompactionConfiguration": { + "IcebergConfiguration": { + "Strategy": "sort", + "MinInputFiles": 100, + "DeleteFileThreshold": 1 + } + } + } + } + } + }, + "Outputs": {} +} \ No newline at end of file diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-custom.json b/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-custom.json new file mode 100644 index 0000000..abe9583 --- /dev/null +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-custom.json @@ -0,0 +1,84 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Metadata": {}, + "Parameters": {}, + "Rules": {}, + "Mappings": {}, + "Conditions": {}, + "Resources": { + "CompactionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": {} + } + }, + "MyTable": { + "Type": "AWS::Glue::Table", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableInput": { + "Description": { + "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" + }, + "Name": "my_table", + "Parameters": { + "EXTERNAL": "TRUE" + }, + "PartitionKeys": [], + "TableType": "EXTERNAL_TABLE", + "StorageDescriptor": { + "Columns": [ + { + "Name": "column", + "Type": "string" + } + ], + "Compressed": false, + "Location": "s3://fake/location", + "NumberOfBuckets": 0, + "SerdeInfo": {}, + "StoredAsSubDirectories": true + } + }, + "OpenTableFormatInput": { + "IcebergInput": { + "MetadataOperation": "CREATE", + "Version": "2" + } + } + } + }, + "MyTableCompactionOptimizer": { + "Type": "AWS::Glue::TableOptimizer", + "DependsOn": "MyTable", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableName": "my_table", + "Type": "compaction", + "TableOptimizerConfiguration": { + "RoleArn": { + "Fn::GetAtt": [ + "CompactionRole", + "Arn" + ] + }, + "Enabled": true, + "CompactionConfiguration": { + "IcebergConfiguration": { + "Strategy": "z-order", + "MinInputFiles": 50, + "DeleteFileThreshold": 10 + } + } + } + } + } + }, + "Outputs": {} +} \ No newline at end of file diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-defaults.json b/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-defaults.json new file mode 100644 index 0000000..79476b1 --- /dev/null +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-defaults.json @@ -0,0 +1,73 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Metadata": {}, + "Parameters": {}, + "Rules": {}, + "Mappings": {}, + "Conditions": {}, + "Resources": { + "MyTable": { + "Type": "AWS::Glue::Table", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableInput": { + "Description": { + "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" + }, + "Name": "my_table", + "Parameters": { + "EXTERNAL": "TRUE" + }, + "PartitionKeys": [], + "TableType": "EXTERNAL_TABLE", + "StorageDescriptor": { + "Columns": [ + { + "Name": "column", + "Type": "string" + } + ], + "Compressed": false, + "Location": "s3://fake/location", + "NumberOfBuckets": 0, + "SerdeInfo": {}, + "StoredAsSubDirectories": true + } + }, + "OpenTableFormatInput": { + "IcebergInput": { + "MetadataOperation": "CREATE", + "Version": "2" + } + } + } + }, + "MyTableCompactionOptimizer": { + "Type": "AWS::Glue::TableOptimizer", + "DependsOn": "MyTable", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableName": "my_table", + "Type": "compaction", + "TableOptimizerConfiguration": { + "RoleArn": "arn:aws:iam::123456789012:role/CompactionRole", + "Enabled": true, + "CompactionConfiguration": { + "IcebergConfiguration": { + "Strategy": "binpack", + "MinInputFiles": 100, + "DeleteFileThreshold": 1 + } + } + } + } + } + }, + "Outputs": {} +} \ No newline at end of file diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json b/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json index 29962ca..cc3ed38 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json @@ -59,7 +59,7 @@ } } }, - "MyTableOptimizer": { + "MyTableRetentionOptimizer": { "Type": "AWS::Glue::TableOptimizer", "DependsOn": "MyTable", "Properties": { diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-defaults.json b/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-defaults.json index f21a4a8..742cb3f 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-defaults.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-defaults.json @@ -45,7 +45,7 @@ } } }, - "MyTableOptimizer": { + "MyTableRetentionOptimizer": { "Type": "AWS::Glue::TableOptimizer", "DependsOn": "MyTable", "Properties": { diff --git a/test/shortcuts.test.js b/test/shortcuts.test.js index 4458489..6bb57d4 100644 --- a/test/shortcuts.test.js +++ b/test/shortcuts.test.js @@ -1557,6 +1557,107 @@ test('[shortcuts] glue iceberg table', (assert) => { 'expected resources generated with optimizer using custom retention settings' ); + assert.throws( + () => new cf.shortcuts.GlueIcebergTable({ + LogicalName: 'MyTable', + DatabaseName: 'my_database', + Name: 'my_table', + Columns: [ + { Name: 'column', Type: 'string' } + ], + Location: 's3://fake/location', + EnableCompaction: true + }), + /You must provide a CompactionRoleArn when EnableCompaction is true/, + 'throws when EnableCompaction is true but CompactionRoleArn is missing' + ); + + assert.throws( + () => new cf.shortcuts.GlueIcebergTable({ + LogicalName: 'MyTable', + DatabaseName: 'my_database', + Name: 'my_table', + Columns: [ + { Name: 'column', Type: 'string' } + ], + Location: 's3://fake/location', + EnableCompaction: true, + CompactionRoleArn: 'arn:aws:iam::123456789012:role/CompactionRole', + CompactionStrategy: 'invalid' + }), + /CompactionStrategy must be one of: binpack, sort, z-order/, + 'throws when CompactionStrategy is invalid' + ); + + db = new cf.shortcuts.GlueIcebergTable({ + LogicalName: 'MyTable', + DatabaseName: 'my_database', + Name: 'my_table', + Columns: [ + { Name: 'column', Type: 'string' } + ], + Location: 's3://fake/location', + EnableCompaction: true, + CompactionRoleArn: 'arn:aws:iam::123456789012:role/CompactionRole' + }); + + template = cf.merge(db); + if (update) fixtures.update('glue-iceberg-table-with-compaction-defaults', template); + assert.deepEqual( + noUndefined(template), + fixtures.get('glue-iceberg-table-with-compaction-defaults'), + 'expected resources generated with compaction using default settings' + ); + + db = new cf.shortcuts.GlueIcebergTable({ + LogicalName: 'MyTable', + DatabaseName: 'my_database', + Name: 'my_table', + Columns: [ + { Name: 'column', Type: 'string' } + ], + Location: 's3://fake/location', + EnableCompaction: true, + CompactionRoleArn: cf.getAtt('CompactionRole', 'Arn'), + CompactionStrategy: 'z-order', + MinInputFiles: 50, + DeleteFileThreshold: 10 + }); + + template = cf.merge( + { Resources: { CompactionRole: { Type: 'AWS::IAM::Role', Properties: { AssumeRolePolicyDocument: {} } } } }, + db + ); + if (update) fixtures.update('glue-iceberg-table-with-compaction-custom', template); + assert.deepEqual( + noUndefined(template), + fixtures.get('glue-iceberg-table-with-compaction-custom'), + 'expected resources generated with compaction using custom settings' + ); + + db = new cf.shortcuts.GlueIcebergTable({ + LogicalName: 'MyTable', + DatabaseName: 'my_database', + Name: 'my_table', + Columns: [ + { Name: 'column', Type: 'string' } + ], + Location: 's3://fake/location', + EnableOptimizer: true, + OptimizerRoleArn: 'arn:aws:iam::123456789012:role/RetentionRole', + EnableCompaction: true, + CompactionRoleArn: 'arn:aws:iam::123456789012:role/CompactionRole', + CompactionStrategy: 'sort' + }); + + template = cf.merge(db); + if (update) fixtures.update('glue-iceberg-table-with-both-optimizers', template); + assert.deepEqual( + noUndefined(template), + fixtures.get('glue-iceberg-table-with-both-optimizers'), + 'expected resources generated with both retention and compaction optimizers' + ); + assert.end(); }); From ab163f88d6896546f8c6ec1539ff1fa064b7050c Mon Sep 17 00:00:00 2001 From: jeffhiltz Date: Fri, 19 Dec 2025 14:45:25 -0500 Subject: [PATCH 03/14] add orphan file deletion configuration to Glue Iceberg tables --- lib/shortcuts/api.md | 8 +- lib/shortcuts/glue-iceberg-table.js | 67 +++++++++- ...lue-iceberg-table-with-all-optimizers.json | 117 ++++++++++++++++++ ...erg-table-with-orphan-deletion-custom.json | 83 +++++++++++++ ...g-table-with-orphan-deletion-defaults.json | 71 +++++++++++ test/shortcuts.test.js | 84 +++++++++++++ 6 files changed, 423 insertions(+), 7 deletions(-) create mode 100644 test/fixtures/shortcuts/glue-iceberg-table-with-all-optimizers.json create mode 100644 test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-custom.json create mode 100644 test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-defaults.json diff --git a/lib/shortcuts/api.md b/lib/shortcuts/api.md index c4ebb6e..09e4f43 100644 --- a/lib/shortcuts/api.md +++ b/lib/shortcuts/api.md @@ -222,15 +222,19 @@ Create a Glue table backed by Apache Iceberg format on S3. | [options.TableType] | String | 'EXTERNAL_TABLE' | Hard-wired by this shortcut. | | [options.IcebergVersion] | String | '2' | The table version for the Iceberg table. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-iceberginput.html). | | [options.EnableOptimizer] | Boolean | false | Whether to enable the snapshot retention optimizer for this Iceberg table. | -| [options.OptimizerRoleArn] | String | | The ARN of the IAM role for the retention optimizer to use. Required if EnableOptimizer is true. Can be the same role as CompactionRoleArn if both optimizers are enabled. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). | +| [options.OptimizerRoleArn] | String | | The ARN of the IAM role for the retention optimizer to use. Required if EnableOptimizer is true. Can be the same role as CompactionRoleArn or OrphanFileDeletionRoleArn if multiple optimizers are enabled. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). | | [options.SnapshotRetentionPeriodInDays] | Number | 5 | The number of days to retain snapshots. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). | | [options.NumberOfSnapshotsToRetain] | Number | 1 | The minimum number of snapshots to retain. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). | | [options.CleanExpiredFiles] | Boolean | true | Whether to delete expired data files after expiring snapshots. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). | | [options.EnableCompaction] | Boolean | false | Whether to enable the compaction optimizer for this Iceberg table. | -| [options.CompactionRoleArn] | String | | The ARN of the IAM role for the compaction optimizer to use. Required if EnableCompaction is true. Can be the same role as OptimizerRoleArn if both optimizers are enabled. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). | +| [options.CompactionRoleArn] | String | | The ARN of the IAM role for the compaction optimizer to use. Required if EnableCompaction is true. Can be the same role as OptimizerRoleArn or OrphanFileDeletionRoleArn if multiple optimizers are enabled. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). | | [options.CompactionStrategy] | String | 'binpack' | The compaction strategy: binpack, sort, or z-order. See [AWS documentation](https://docs.aws.amazon.com/glue/latest/dg/enable-compaction.html). | | [options.MinInputFiles] | Number | 100 | Minimum number of data files before compaction triggers. | | [options.DeleteFileThreshold] | Number | 1 | Minimum deletes in a file to make it eligible for compaction. | +| [options.EnableOrphanFileDeletion] | Boolean | false | Whether to enable the orphan file deletion optimizer for this Iceberg table. | +| [options.OrphanFileDeletionRoleArn] | String | | The ARN of the IAM role for the orphan file deletion optimizer to use. Required if EnableOrphanFileDeletion is true. Can be the same role as OptimizerRoleArn or CompactionRoleArn if multiple optimizers are enabled. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). | +| [options.OrphanFileRetentionPeriodInDays] | Number | 3 | The number of days to retain orphan files before deleting them. See [AWS documentation](https://docs.aws.amazon.com/glue/latest/dg/enable-orphan-file-deletion.html). | +| [options.OrphanFileDeletionLocation] | String | | The S3 location to scan for orphan files. Defaults to the table location if not specified. See [AWS documentation](https://docs.aws.amazon.com/glue/latest/dg/enable-orphan-file-deletion.html). | diff --git a/lib/shortcuts/glue-iceberg-table.js b/lib/shortcuts/glue-iceberg-table.js index 3e84856..d9ccf05 100644 --- a/lib/shortcuts/glue-iceberg-table.js +++ b/lib/shortcuts/glue-iceberg-table.js @@ -20,8 +20,8 @@ const GlueTable = require('./glue-table'); * snapshot retention optimizer for this Iceberg table. * @param {String} [options.OptimizerRoleArn=undefined] - The ARN of the IAM * role for the retention optimizer to use. Required if EnableOptimizer is - * true. Can be the same role as CompactionRoleArn if both optimizers are - * enabled. See [AWS + * true. Can be the same role as CompactionRoleArn or OrphanFileDeletionRoleArn + * if multiple optimizers are enabled. See [AWS * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). * @param {Number} [options.SnapshotRetentionPeriodInDays=5] - The number of * days to retain snapshots. See [AWS @@ -36,8 +36,8 @@ const GlueTable = require('./glue-table'); * compaction optimizer for this Iceberg table. * @param {String} [options.CompactionRoleArn=undefined] - The ARN of the IAM * role for the compaction optimizer to use. Required if EnableCompaction is - * true. Can be the same role as OptimizerRoleArn if both optimizers are - * enabled. See [AWS + * true. Can be the same role as OptimizerRoleArn or OrphanFileDeletionRoleArn + * if multiple optimizers are enabled. See [AWS * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). * @param {String} [options.CompactionStrategy='binpack'] - The compaction * strategy: binpack, sort, or z-order. See [AWS @@ -46,6 +46,20 @@ const GlueTable = require('./glue-table'); * before compaction triggers. * @param {Number} [options.DeleteFileThreshold=1] - Minimum deletes in a file * to make it eligible for compaction. + * @param {Boolean} [options.EnableOrphanFileDeletion=false] - Whether to + * enable the orphan file deletion optimizer for this Iceberg table. + * @param {String} [options.OrphanFileDeletionRoleArn=undefined] - The ARN of + * the IAM role for the orphan file deletion optimizer to use. Required if + * EnableOrphanFileDeletion is true. Can be the same role as OptimizerRoleArn + * or CompactionRoleArn if multiple optimizers are enabled. See [AWS + * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). + * @param {Number} [options.OrphanFileRetentionPeriodInDays=3] - The number of + * days to retain orphan files before deleting them. See [AWS + * documentation](https://docs.aws.amazon.com/glue/latest/dg/enable-orphan-file-deletion.html). + * @param {String} [options.OrphanFileDeletionLocation=undefined] - The S3 + * location to scan for orphan files. Defaults to the table location if not + * specified. See [AWS + * documentation](https://docs.aws.amazon.com/glue/latest/dg/enable-orphan-file-deletion.html). */ class GlueIcebergTable extends GlueTable { constructor(options) { @@ -62,7 +76,11 @@ class GlueIcebergTable extends GlueTable { CompactionRoleArn, CompactionStrategy = 'binpack', MinInputFiles = 100, - DeleteFileThreshold = 1 + DeleteFileThreshold = 1, + EnableOrphanFileDeletion = false, + OrphanFileDeletionRoleArn, + OrphanFileRetentionPeriodInDays = 3, + OrphanFileDeletionLocation } = options; const required = [Location]; @@ -75,6 +93,9 @@ class GlueIcebergTable extends GlueTable { if (EnableCompaction && !CompactionRoleArn) throw new Error('You must provide a CompactionRoleArn when EnableCompaction is true'); + if (EnableOrphanFileDeletion && !OrphanFileDeletionRoleArn) + throw new Error('You must provide an OrphanFileDeletionRoleArn when EnableOrphanFileDeletion is true'); + const validStrategies = ['binpack', 'sort', 'z-order']; if (!validStrategies.includes(CompactionStrategy)) throw new Error('CompactionStrategy must be one of: binpack, sort, z-order'); @@ -158,6 +179,42 @@ class GlueIcebergTable extends GlueTable { this.Resources[compactionLogicalName].Condition = options.Condition; } } + + // Optionally add TableOptimizer for orphan file deletion + if (EnableOrphanFileDeletion) { + const orphanLogicalName = `${logicalName}OrphanFileDeletionOptimizer`; + const icebergConfiguration = { + OrphanFileRetentionPeriodInDays + }; + + // Only add Location if specified, otherwise it defaults to table location + if (OrphanFileDeletionLocation) { + icebergConfiguration.Location = OrphanFileDeletionLocation; + } + + this.Resources[orphanLogicalName] = { + Type: 'AWS::Glue::TableOptimizer', + DependsOn: logicalName, + Properties: { + CatalogId: options.CatalogId || { Ref: 'AWS::AccountId' }, + DatabaseName: options.DatabaseName, + TableName: options.Name, + Type: 'orphan_file_deletion', + TableOptimizerConfiguration: { + RoleArn: OrphanFileDeletionRoleArn, + Enabled: true, + OrphanFileDeletionConfiguration: { + IcebergConfiguration: icebergConfiguration + } + } + } + }; + + // Apply Condition to orphan file deletion optimizer if specified on the table + if (options.Condition) { + this.Resources[orphanLogicalName].Condition = options.Condition; + } + } } } diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-all-optimizers.json b/test/fixtures/shortcuts/glue-iceberg-table-with-all-optimizers.json new file mode 100644 index 0000000..fc26a6a --- /dev/null +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-all-optimizers.json @@ -0,0 +1,117 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Metadata": {}, + "Parameters": {}, + "Rules": {}, + "Mappings": {}, + "Conditions": {}, + "Resources": { + "MyTable": { + "Type": "AWS::Glue::Table", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableInput": { + "Description": { + "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" + }, + "Name": "my_table", + "Parameters": { + "EXTERNAL": "TRUE" + }, + "PartitionKeys": [], + "TableType": "EXTERNAL_TABLE", + "StorageDescriptor": { + "Columns": [ + { + "Name": "column", + "Type": "string" + } + ], + "Compressed": false, + "Location": "s3://fake/location", + "NumberOfBuckets": 0, + "SerdeInfo": {}, + "StoredAsSubDirectories": true + } + }, + "OpenTableFormatInput": { + "IcebergInput": { + "MetadataOperation": "CREATE", + "Version": "2" + } + } + } + }, + "MyTableRetentionOptimizer": { + "Type": "AWS::Glue::TableOptimizer", + "DependsOn": "MyTable", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableName": "my_table", + "Type": "retention", + "TableOptimizerConfiguration": { + "RoleArn": "arn:aws:iam::123456789012:role/SharedRole", + "Enabled": true, + "RetentionConfiguration": { + "IcebergConfiguration": { + "SnapshotRetentionPeriodInDays": 5, + "NumberOfSnapshotsToRetain": 1, + "CleanExpiredFiles": true + } + } + } + } + }, + "MyTableCompactionOptimizer": { + "Type": "AWS::Glue::TableOptimizer", + "DependsOn": "MyTable", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableName": "my_table", + "Type": "compaction", + "TableOptimizerConfiguration": { + "RoleArn": "arn:aws:iam::123456789012:role/SharedRole", + "Enabled": true, + "CompactionConfiguration": { + "IcebergConfiguration": { + "Strategy": "binpack", + "MinInputFiles": 100, + "DeleteFileThreshold": 1 + } + } + } + } + }, + "MyTableOrphanFileDeletionOptimizer": { + "Type": "AWS::Glue::TableOptimizer", + "DependsOn": "MyTable", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableName": "my_table", + "Type": "orphan_file_deletion", + "TableOptimizerConfiguration": { + "RoleArn": "arn:aws:iam::123456789012:role/SharedRole", + "Enabled": true, + "OrphanFileDeletionConfiguration": { + "IcebergConfiguration": { + "OrphanFileRetentionPeriodInDays": 3 + } + } + } + } + } + }, + "Outputs": {} +} \ No newline at end of file diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-custom.json b/test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-custom.json new file mode 100644 index 0000000..dd776ab --- /dev/null +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-custom.json @@ -0,0 +1,83 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Metadata": {}, + "Parameters": {}, + "Rules": {}, + "Mappings": {}, + "Conditions": {}, + "Resources": { + "OrphanFileDeletionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": {} + } + }, + "MyTable": { + "Type": "AWS::Glue::Table", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableInput": { + "Description": { + "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" + }, + "Name": "my_table", + "Parameters": { + "EXTERNAL": "TRUE" + }, + "PartitionKeys": [], + "TableType": "EXTERNAL_TABLE", + "StorageDescriptor": { + "Columns": [ + { + "Name": "column", + "Type": "string" + } + ], + "Compressed": false, + "Location": "s3://fake/location", + "NumberOfBuckets": 0, + "SerdeInfo": {}, + "StoredAsSubDirectories": true + } + }, + "OpenTableFormatInput": { + "IcebergInput": { + "MetadataOperation": "CREATE", + "Version": "2" + } + } + } + }, + "MyTableOrphanFileDeletionOptimizer": { + "Type": "AWS::Glue::TableOptimizer", + "DependsOn": "MyTable", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableName": "my_table", + "Type": "orphan_file_deletion", + "TableOptimizerConfiguration": { + "RoleArn": { + "Fn::GetAtt": [ + "OrphanFileDeletionRole", + "Arn" + ] + }, + "Enabled": true, + "OrphanFileDeletionConfiguration": { + "IcebergConfiguration": { + "OrphanFileRetentionPeriodInDays": 7, + "Location": "s3://fake/location/subdir" + } + } + } + } + } + }, + "Outputs": {} +} \ No newline at end of file diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-defaults.json b/test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-defaults.json new file mode 100644 index 0000000..e710c29 --- /dev/null +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-defaults.json @@ -0,0 +1,71 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Metadata": {}, + "Parameters": {}, + "Rules": {}, + "Mappings": {}, + "Conditions": {}, + "Resources": { + "MyTable": { + "Type": "AWS::Glue::Table", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableInput": { + "Description": { + "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" + }, + "Name": "my_table", + "Parameters": { + "EXTERNAL": "TRUE" + }, + "PartitionKeys": [], + "TableType": "EXTERNAL_TABLE", + "StorageDescriptor": { + "Columns": [ + { + "Name": "column", + "Type": "string" + } + ], + "Compressed": false, + "Location": "s3://fake/location", + "NumberOfBuckets": 0, + "SerdeInfo": {}, + "StoredAsSubDirectories": true + } + }, + "OpenTableFormatInput": { + "IcebergInput": { + "MetadataOperation": "CREATE", + "Version": "2" + } + } + } + }, + "MyTableOrphanFileDeletionOptimizer": { + "Type": "AWS::Glue::TableOptimizer", + "DependsOn": "MyTable", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": "my_database", + "TableName": "my_table", + "Type": "orphan_file_deletion", + "TableOptimizerConfiguration": { + "RoleArn": "arn:aws:iam::123456789012:role/OrphanFileDeletionRole", + "Enabled": true, + "OrphanFileDeletionConfiguration": { + "IcebergConfiguration": { + "OrphanFileRetentionPeriodInDays": 3 + } + } + } + } + } + }, + "Outputs": {} +} \ No newline at end of file diff --git a/test/shortcuts.test.js b/test/shortcuts.test.js index 6bb57d4..6a3814a 100644 --- a/test/shortcuts.test.js +++ b/test/shortcuts.test.js @@ -1658,6 +1658,90 @@ test('[shortcuts] glue iceberg table', (assert) => { 'expected resources generated with both retention and compaction optimizers' ); + assert.throws( + () => new cf.shortcuts.GlueIcebergTable({ + LogicalName: 'MyTable', + DatabaseName: 'my_database', + Name: 'my_table', + Columns: [ + { Name: 'column', Type: 'string' } + ], + Location: 's3://fake/location', + EnableOrphanFileDeletion: true + }), + /You must provide an OrphanFileDeletionRoleArn when EnableOrphanFileDeletion is true/, + 'throws when EnableOrphanFileDeletion is true but OrphanFileDeletionRoleArn is missing' + ); + + db = new cf.shortcuts.GlueIcebergTable({ + LogicalName: 'MyTable', + DatabaseName: 'my_database', + Name: 'my_table', + Columns: [ + { Name: 'column', Type: 'string' } + ], + Location: 's3://fake/location', + EnableOrphanFileDeletion: true, + OrphanFileDeletionRoleArn: 'arn:aws:iam::123456789012:role/OrphanFileDeletionRole' + }); + + template = cf.merge(db); + if (update) fixtures.update('glue-iceberg-table-with-orphan-deletion-defaults', template); + assert.deepEqual( + noUndefined(template), + fixtures.get('glue-iceberg-table-with-orphan-deletion-defaults'), + 'expected resources generated with orphan file deletion using default settings' + ); + + db = new cf.shortcuts.GlueIcebergTable({ + LogicalName: 'MyTable', + DatabaseName: 'my_database', + Name: 'my_table', + Columns: [ + { Name: 'column', Type: 'string' } + ], + Location: 's3://fake/location', + EnableOrphanFileDeletion: true, + OrphanFileDeletionRoleArn: cf.getAtt('OrphanFileDeletionRole', 'Arn'), + OrphanFileRetentionPeriodInDays: 7, + OrphanFileDeletionLocation: 's3://fake/location/subdir' + }); + + template = cf.merge( + { Resources: { OrphanFileDeletionRole: { Type: 'AWS::IAM::Role', Properties: { AssumeRolePolicyDocument: {} } } } }, + db + ); + if (update) fixtures.update('glue-iceberg-table-with-orphan-deletion-custom', template); + assert.deepEqual( + noUndefined(template), + fixtures.get('glue-iceberg-table-with-orphan-deletion-custom'), + 'expected resources generated with orphan file deletion using custom settings' + ); + + db = new cf.shortcuts.GlueIcebergTable({ + LogicalName: 'MyTable', + DatabaseName: 'my_database', + Name: 'my_table', + Columns: [ + { Name: 'column', Type: 'string' } + ], + Location: 's3://fake/location', + EnableOptimizer: true, + OptimizerRoleArn: 'arn:aws:iam::123456789012:role/SharedRole', + EnableCompaction: true, + CompactionRoleArn: 'arn:aws:iam::123456789012:role/SharedRole', + EnableOrphanFileDeletion: true, + OrphanFileDeletionRoleArn: 'arn:aws:iam::123456789012:role/SharedRole' + }); + + template = cf.merge(db); + if (update) fixtures.update('glue-iceberg-table-with-all-optimizers', template); + assert.deepEqual( + noUndefined(template), + fixtures.get('glue-iceberg-table-with-all-optimizers'), + 'expected resources generated with all three optimizers using same role' + ); + assert.end(); }); From 7ef9a0f83b9f7769e389a7df43e76242f9bb8c88 Mon Sep 17 00:00:00 2001 From: jeffhiltz Date: Fri, 19 Dec 2025 17:05:14 -0500 Subject: [PATCH 04/14] remove the compaction config which is in the api but not CFN --- lib/shortcuts/api.md | 5 +-- lib/shortcuts/glue-iceberg-table.js | 32 ++++++------------- ...lue-iceberg-table-with-all-optimizers.json | 9 +----- ...ue-iceberg-table-with-both-optimizers.json | 9 +----- ...-iceberg-table-with-compaction-custom.json | 9 +----- ...ceberg-table-with-compaction-defaults.json | 9 +----- test/shortcuts.test.js | 25 ++------------- 7 files changed, 16 insertions(+), 82 deletions(-) diff --git a/lib/shortcuts/api.md b/lib/shortcuts/api.md index 09e4f43..4434da1 100644 --- a/lib/shortcuts/api.md +++ b/lib/shortcuts/api.md @@ -226,11 +226,8 @@ Create a Glue table backed by Apache Iceberg format on S3. | [options.SnapshotRetentionPeriodInDays] | Number | 5 | The number of days to retain snapshots. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). | | [options.NumberOfSnapshotsToRetain] | Number | 1 | The minimum number of snapshots to retain. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). | | [options.CleanExpiredFiles] | Boolean | true | Whether to delete expired data files after expiring snapshots. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). | -| [options.EnableCompaction] | Boolean | false | Whether to enable the compaction optimizer for this Iceberg table. | +| [options.EnableCompaction] | Boolean | false | Whether to enable the compaction optimizer for this Iceberg table. Note: CloudFormation does not support configuring compaction strategy or thresholds; the optimizer will use AWS defaults (binpack strategy). Configuration must be done via AWS CLI/API. See [GitHub issue](https://github.com/aws-cloudformation/cloudformation-coverage-roadmap/issues/2257). | | [options.CompactionRoleArn] | String | | The ARN of the IAM role for the compaction optimizer to use. Required if EnableCompaction is true. Can be the same role as OptimizerRoleArn or OrphanFileDeletionRoleArn if multiple optimizers are enabled. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). | -| [options.CompactionStrategy] | String | 'binpack' | The compaction strategy: binpack, sort, or z-order. See [AWS documentation](https://docs.aws.amazon.com/glue/latest/dg/enable-compaction.html). | -| [options.MinInputFiles] | Number | 100 | Minimum number of data files before compaction triggers. | -| [options.DeleteFileThreshold] | Number | 1 | Minimum deletes in a file to make it eligible for compaction. | | [options.EnableOrphanFileDeletion] | Boolean | false | Whether to enable the orphan file deletion optimizer for this Iceberg table. | | [options.OrphanFileDeletionRoleArn] | String | | The ARN of the IAM role for the orphan file deletion optimizer to use. Required if EnableOrphanFileDeletion is true. Can be the same role as OptimizerRoleArn or CompactionRoleArn if multiple optimizers are enabled. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). | | [options.OrphanFileRetentionPeriodInDays] | Number | 3 | The number of days to retain orphan files before deleting them. See [AWS documentation](https://docs.aws.amazon.com/glue/latest/dg/enable-orphan-file-deletion.html). | diff --git a/lib/shortcuts/glue-iceberg-table.js b/lib/shortcuts/glue-iceberg-table.js index d9ccf05..8b3c7bb 100644 --- a/lib/shortcuts/glue-iceberg-table.js +++ b/lib/shortcuts/glue-iceberg-table.js @@ -33,19 +33,15 @@ const GlueTable = require('./glue-table'); * expired data files after expiring snapshots. See [AWS * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). * @param {Boolean} [options.EnableCompaction=false] - Whether to enable the - * compaction optimizer for this Iceberg table. + * compaction optimizer for this Iceberg table. Note: CloudFormation does not + * support configuring compaction strategy or thresholds; the optimizer will use + * AWS defaults (binpack strategy). Configuration must be done via AWS CLI/API. + * See [GitHub issue](https://github.com/aws-cloudformation/cloudformation-coverage-roadmap/issues/2257). * @param {String} [options.CompactionRoleArn=undefined] - The ARN of the IAM * role for the compaction optimizer to use. Required if EnableCompaction is * true. Can be the same role as OptimizerRoleArn or OrphanFileDeletionRoleArn * if multiple optimizers are enabled. See [AWS * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). - * @param {String} [options.CompactionStrategy='binpack'] - The compaction - * strategy: binpack, sort, or z-order. See [AWS - * documentation](https://docs.aws.amazon.com/glue/latest/dg/enable-compaction.html). - * @param {Number} [options.MinInputFiles=100] - Minimum number of data files - * before compaction triggers. - * @param {Number} [options.DeleteFileThreshold=1] - Minimum deletes in a file - * to make it eligible for compaction. * @param {Boolean} [options.EnableOrphanFileDeletion=false] - Whether to * enable the orphan file deletion optimizer for this Iceberg table. * @param {String} [options.OrphanFileDeletionRoleArn=undefined] - The ARN of @@ -74,9 +70,6 @@ class GlueIcebergTable extends GlueTable { CleanExpiredFiles = true, EnableCompaction = false, CompactionRoleArn, - CompactionStrategy = 'binpack', - MinInputFiles = 100, - DeleteFileThreshold = 1, EnableOrphanFileDeletion = false, OrphanFileDeletionRoleArn, OrphanFileRetentionPeriodInDays = 3, @@ -96,10 +89,6 @@ class GlueIcebergTable extends GlueTable { if (EnableOrphanFileDeletion && !OrphanFileDeletionRoleArn) throw new Error('You must provide an OrphanFileDeletionRoleArn when EnableOrphanFileDeletion is true'); - const validStrategies = ['binpack', 'sort', 'z-order']; - if (!validStrategies.includes(CompactionStrategy)) - throw new Error('CompactionStrategy must be one of: binpack, sort, z-order'); - super( Object.assign( { @@ -150,6 +139,10 @@ class GlueIcebergTable extends GlueTable { } // Optionally add TableOptimizer for compaction + // NOTE: CloudFormation does not support CompactionConfiguration properties + // (strategy, minInputFiles, deleteFileThreshold). These must be configured + // via AWS CLI/API after stack creation, or will use AWS defaults. + // See: https://github.com/aws-cloudformation/cloudformation-coverage-roadmap/issues/2257 if (EnableCompaction) { const compactionLogicalName = `${logicalName}CompactionOptimizer`; this.Resources[compactionLogicalName] = { @@ -162,14 +155,7 @@ class GlueIcebergTable extends GlueTable { Type: 'compaction', TableOptimizerConfiguration: { RoleArn: CompactionRoleArn, - Enabled: true, - CompactionConfiguration: { - IcebergConfiguration: { - Strategy: CompactionStrategy, - MinInputFiles, - DeleteFileThreshold - } - } + Enabled: true } } }; diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-all-optimizers.json b/test/fixtures/shortcuts/glue-iceberg-table-with-all-optimizers.json index fc26a6a..f2d181a 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-with-all-optimizers.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-all-optimizers.json @@ -80,14 +80,7 @@ "Type": "compaction", "TableOptimizerConfiguration": { "RoleArn": "arn:aws:iam::123456789012:role/SharedRole", - "Enabled": true, - "CompactionConfiguration": { - "IcebergConfiguration": { - "Strategy": "binpack", - "MinInputFiles": 100, - "DeleteFileThreshold": 1 - } - } + "Enabled": true } } }, diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-both-optimizers.json b/test/fixtures/shortcuts/glue-iceberg-table-with-both-optimizers.json index 5874a2c..a9fe1d6 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-with-both-optimizers.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-both-optimizers.json @@ -80,14 +80,7 @@ "Type": "compaction", "TableOptimizerConfiguration": { "RoleArn": "arn:aws:iam::123456789012:role/CompactionRole", - "Enabled": true, - "CompactionConfiguration": { - "IcebergConfiguration": { - "Strategy": "sort", - "MinInputFiles": 100, - "DeleteFileThreshold": 1 - } - } + "Enabled": true } } } diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-custom.json b/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-custom.json index abe9583..c4ffe91 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-custom.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-custom.json @@ -68,14 +68,7 @@ "Arn" ] }, - "Enabled": true, - "CompactionConfiguration": { - "IcebergConfiguration": { - "Strategy": "z-order", - "MinInputFiles": 50, - "DeleteFileThreshold": 10 - } - } + "Enabled": true } } } diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-defaults.json b/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-defaults.json index 79476b1..9948d48 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-defaults.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-defaults.json @@ -57,14 +57,7 @@ "Type": "compaction", "TableOptimizerConfiguration": { "RoleArn": "arn:aws:iam::123456789012:role/CompactionRole", - "Enabled": true, - "CompactionConfiguration": { - "IcebergConfiguration": { - "Strategy": "binpack", - "MinInputFiles": 100, - "DeleteFileThreshold": 1 - } - } + "Enabled": true } } } diff --git a/test/shortcuts.test.js b/test/shortcuts.test.js index 6a3814a..f3b3c2a 100644 --- a/test/shortcuts.test.js +++ b/test/shortcuts.test.js @@ -1572,23 +1572,6 @@ test('[shortcuts] glue iceberg table', (assert) => { 'throws when EnableCompaction is true but CompactionRoleArn is missing' ); - assert.throws( - () => new cf.shortcuts.GlueIcebergTable({ - LogicalName: 'MyTable', - DatabaseName: 'my_database', - Name: 'my_table', - Columns: [ - { Name: 'column', Type: 'string' } - ], - Location: 's3://fake/location', - EnableCompaction: true, - CompactionRoleArn: 'arn:aws:iam::123456789012:role/CompactionRole', - CompactionStrategy: 'invalid' - }), - /CompactionStrategy must be one of: binpack, sort, z-order/, - 'throws when CompactionStrategy is invalid' - ); - db = new cf.shortcuts.GlueIcebergTable({ LogicalName: 'MyTable', DatabaseName: 'my_database', @@ -1618,10 +1601,7 @@ test('[shortcuts] glue iceberg table', (assert) => { ], Location: 's3://fake/location', EnableCompaction: true, - CompactionRoleArn: cf.getAtt('CompactionRole', 'Arn'), - CompactionStrategy: 'z-order', - MinInputFiles: 50, - DeleteFileThreshold: 10 + CompactionRoleArn: cf.getAtt('CompactionRole', 'Arn') }); template = cf.merge( @@ -1646,8 +1626,7 @@ test('[shortcuts] glue iceberg table', (assert) => { EnableOptimizer: true, OptimizerRoleArn: 'arn:aws:iam::123456789012:role/RetentionRole', EnableCompaction: true, - CompactionRoleArn: 'arn:aws:iam::123456789012:role/CompactionRole', - CompactionStrategy: 'sort' + CompactionRoleArn: 'arn:aws:iam::123456789012:role/CompactionRole' }); template = cf.merge(db); From bd09b80f12d2a71d1a13ef765c66e1725cb92d52 Mon Sep 17 00:00:00 2001 From: jeffhiltz Date: Fri, 19 Dec 2025 17:11:47 -0500 Subject: [PATCH 05/14] update cfn-lint to get newer property types --- requirements.dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.dev.txt b/requirements.dev.txt index f76f9b7..7582496 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -1,2 +1,2 @@ aws-sam-cli==1.142.1 -cfn-lint==1.36.1 +cfn-lint==1.43.1 From 5f961e0b96a5b405fb4d07de10a53c5b0d3f1bc1 Mon Sep 17 00:00:00 2001 From: jeffhiltz Date: Fri, 19 Dec 2025 17:32:39 -0500 Subject: [PATCH 06/14] update sam-cli in order to update cfn-lint --- requirements.dev.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.dev.txt b/requirements.dev.txt index 7582496..3e6c219 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -1,2 +1,2 @@ -aws-sam-cli==1.142.1 -cfn-lint==1.43.1 +aws-sam-cli==1.149.0 +cfn-lint==1.41.0 From c6e5dcf1c752a47edbff3781e7b8596acd83f835 Mon Sep 17 00:00:00 2001 From: majacob Date: Wed, 21 Jan 2026 16:29:11 -0500 Subject: [PATCH 07/14] updates to use openTableFormatInput --- lib/shortcuts/api.md | 39 +- lib/shortcuts/glue-iceberg-table.js | 206 +- package-lock.json | 1890 +++++++++-------- package.json | 2 +- .../glue-iceberg-table-defaults.json | 41 +- .../glue-iceberg-table-no-defaults.json | 41 +- ...lue-iceberg-table-with-all-optimizers.json | 41 +- ...ue-iceberg-table-with-both-optimizers.json | 41 +- ...-iceberg-table-with-compaction-custom.json | 41 +- ...ceberg-table-with-compaction-defaults.json | 41 +- ...e-iceberg-table-with-optimizer-custom.json | 41 +- ...iceberg-table-with-optimizer-defaults.json | 41 +- ...erg-table-with-orphan-deletion-custom.json | 41 +- ...g-table-with-orphan-deletion-defaults.json | 41 +- test/shortcuts.test.js | 13 +- 15 files changed, 1259 insertions(+), 1301 deletions(-) diff --git a/lib/shortcuts/api.md b/lib/shortcuts/api.md index 4434da1..6ccf2a2 100644 --- a/lib/shortcuts/api.md +++ b/lib/shortcuts/api.md @@ -217,21 +217,30 @@ Create a Glue table backed by Apache Iceberg format on S3. | Param | Type | Default | Description | | --- | --- | --- | --- | -| options | Object | | Accepts the same options as cloudfriend's [`GlueTable`](https://github.com/mapbox/cloudfriend/blob/master/lib/shortcuts/glue-table.js), though the following additional attributes are either required or hard-wired: | -| options.Location | String | | The physical location of the table. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-storagedescriptor.html#cfn-glue-table-storagedescriptor-location). | -| [options.TableType] | String | 'EXTERNAL_TABLE' | Hard-wired by this shortcut. | -| [options.IcebergVersion] | String | '2' | The table version for the Iceberg table. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-iceberginput.html). | -| [options.EnableOptimizer] | Boolean | false | Whether to enable the snapshot retention optimizer for this Iceberg table. | -| [options.OptimizerRoleArn] | String | | The ARN of the IAM role for the retention optimizer to use. Required if EnableOptimizer is true. Can be the same role as CompactionRoleArn or OrphanFileDeletionRoleArn if multiple optimizers are enabled. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). | -| [options.SnapshotRetentionPeriodInDays] | Number | 5 | The number of days to retain snapshots. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). | -| [options.NumberOfSnapshotsToRetain] | Number | 1 | The minimum number of snapshots to retain. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). | -| [options.CleanExpiredFiles] | Boolean | true | Whether to delete expired data files after expiring snapshots. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). | -| [options.EnableCompaction] | Boolean | false | Whether to enable the compaction optimizer for this Iceberg table. Note: CloudFormation does not support configuring compaction strategy or thresholds; the optimizer will use AWS defaults (binpack strategy). Configuration must be done via AWS CLI/API. See [GitHub issue](https://github.com/aws-cloudformation/cloudformation-coverage-roadmap/issues/2257). | -| [options.CompactionRoleArn] | String | | The ARN of the IAM role for the compaction optimizer to use. Required if EnableCompaction is true. Can be the same role as OptimizerRoleArn or OrphanFileDeletionRoleArn if multiple optimizers are enabled. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). | -| [options.EnableOrphanFileDeletion] | Boolean | false | Whether to enable the orphan file deletion optimizer for this Iceberg table. | -| [options.OrphanFileDeletionRoleArn] | String | | The ARN of the IAM role for the orphan file deletion optimizer to use. Required if EnableOrphanFileDeletion is true. Can be the same role as OptimizerRoleArn or CompactionRoleArn if multiple optimizers are enabled. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). | -| [options.OrphanFileRetentionPeriodInDays] | Number | 3 | The number of days to retain orphan files before deleting them. See [AWS documentation](https://docs.aws.amazon.com/glue/latest/dg/enable-orphan-file-deletion.html). | -| [options.OrphanFileDeletionLocation] | String | | The S3 location to scan for orphan files. Defaults to the table location if not specified. See [AWS documentation](https://docs.aws.amazon.com/glue/latest/dg/enable-orphan-file-deletion.html). | +| options | Object | | Options for creating an Iceberg table. | +| options.LogicalName | String | | The logical name of the Glue Table within the CloudFormation template. | +| options.Name | String | | The name of the table. | +| options.DatabaseName | String | | The name of the database the table resides in. | +| options.Location | String | | The physical location of the table (S3 URI). Required. | +| [options.Columns] | Array.<Object> | | Simple column definitions as array of {Name, Type, Required}. If provided, will be auto-converted to Iceberg Schema format. Use options.Schema for full control. | +| [options.Schema] | Object | | Full Iceberg schema definition with Type: "struct" and Fields array. Either Columns or Schema must be provided. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-icebergtableinput.html). | +| [options.PartitionSpec] | Object | | Iceberg partition specification. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-partitionspec.html). | +| [options.WriteOrder] | Object | | Iceberg write order specification. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-writeorder.html). | +| [options.CatalogId] | String | AccountId | The AWS account ID for the account in which to create the table. | +| [options.IcebergVersion] | String | '2' | The table version for the Iceberg table. | +| [options.EnableOptimizer] | Boolean | false | Whether to enable the snapshot retention optimizer. | +| [options.OptimizerRoleArn] | String | | The ARN of the IAM role for the retention optimizer. Required if EnableOptimizer is true. | +| [options.SnapshotRetentionPeriodInDays] | Number | 5 | The number of days to retain snapshots. | +| [options.NumberOfSnapshotsToRetain] | Number | 1 | The minimum number of snapshots to retain. | +| [options.CleanExpiredFiles] | Boolean | true | Whether to delete expired data files after expiring snapshots. | +| [options.EnableCompaction] | Boolean | false | Whether to enable the compaction optimizer. | +| [options.CompactionRoleArn] | String | | The ARN of the IAM role for the compaction optimizer. Required if EnableCompaction is true. | +| [options.EnableOrphanFileDeletion] | Boolean | false | Whether to enable the orphan file deletion optimizer. | +| [options.OrphanFileDeletionRoleArn] | String | | The ARN of the IAM role for the orphan file deletion optimizer. Required if EnableOrphanFileDeletion is true. | +| [options.OrphanFileRetentionPeriodInDays] | Number | 3 | The number of days to retain orphan files before deleting them. | +| [options.OrphanFileDeletionLocation] | String | | The S3 location to scan for orphan files. | +| [options.Condition] | String | | CloudFormation condition name. | +| [options.DependsOn] | String | | CloudFormation resource dependency. | diff --git a/lib/shortcuts/glue-iceberg-table.js b/lib/shortcuts/glue-iceberg-table.js index 8b3c7bb..48066cf 100644 --- a/lib/shortcuts/glue-iceberg-table.js +++ b/lib/shortcuts/glue-iceberg-table.js @@ -1,67 +1,51 @@ 'use strict'; -const GlueTable = require('./glue-table'); - /** * Create a Glue table backed by Apache Iceberg format on S3. * - * @param {Object} options - Accepts the same options as cloudfriend's - * [`GlueTable`](https://github.com/mapbox/cloudfriend/blob/master/lib/shortcuts/glue-table.js), - * though the following additional attributes are either required or hard-wired: - * @param {String} options.Location - The physical location of the table. See - * [AWS - * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-storagedescriptor.html#cfn-glue-table-storagedescriptor-location). - * @param {String} [options.TableType='EXTERNAL_TABLE'] - Hard-wired by this - * shortcut. - * @param {String} [options.IcebergVersion='2'] - The table version for the - * Iceberg table. See [AWS - * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-iceberginput.html). - * @param {Boolean} [options.EnableOptimizer=false] - Whether to enable the - * snapshot retention optimizer for this Iceberg table. - * @param {String} [options.OptimizerRoleArn=undefined] - The ARN of the IAM - * role for the retention optimizer to use. Required if EnableOptimizer is - * true. Can be the same role as CompactionRoleArn or OrphanFileDeletionRoleArn - * if multiple optimizers are enabled. See [AWS - * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). - * @param {Number} [options.SnapshotRetentionPeriodInDays=5] - The number of - * days to retain snapshots. See [AWS - * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). - * @param {Number} [options.NumberOfSnapshotsToRetain=1] - The minimum number - * of snapshots to retain. See [AWS - * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). - * @param {Boolean} [options.CleanExpiredFiles=true] - Whether to delete - * expired data files after expiring snapshots. See [AWS - * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-glue-tableoptimizer-icebergretentionconfiguration.html). - * @param {Boolean} [options.EnableCompaction=false] - Whether to enable the - * compaction optimizer for this Iceberg table. Note: CloudFormation does not - * support configuring compaction strategy or thresholds; the optimizer will use - * AWS defaults (binpack strategy). Configuration must be done via AWS CLI/API. - * See [GitHub issue](https://github.com/aws-cloudformation/cloudformation-coverage-roadmap/issues/2257). - * @param {String} [options.CompactionRoleArn=undefined] - The ARN of the IAM - * role for the compaction optimizer to use. Required if EnableCompaction is - * true. Can be the same role as OptimizerRoleArn or OrphanFileDeletionRoleArn - * if multiple optimizers are enabled. See [AWS - * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). - * @param {Boolean} [options.EnableOrphanFileDeletion=false] - Whether to - * enable the orphan file deletion optimizer for this Iceberg table. - * @param {String} [options.OrphanFileDeletionRoleArn=undefined] - The ARN of - * the IAM role for the orphan file deletion optimizer to use. Required if - * EnableOrphanFileDeletion is true. Can be the same role as OptimizerRoleArn - * or CompactionRoleArn if multiple optimizers are enabled. See [AWS - * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-tableoptimizer-tableoptimizerconfiguration.html). - * @param {Number} [options.OrphanFileRetentionPeriodInDays=3] - The number of - * days to retain orphan files before deleting them. See [AWS - * documentation](https://docs.aws.amazon.com/glue/latest/dg/enable-orphan-file-deletion.html). - * @param {String} [options.OrphanFileDeletionLocation=undefined] - The S3 - * location to scan for orphan files. Defaults to the table location if not - * specified. See [AWS - * documentation](https://docs.aws.amazon.com/glue/latest/dg/enable-orphan-file-deletion.html). + * @param {Object} options - Options for creating an Iceberg table. + * @param {String} options.LogicalName - The logical name of the Glue Table within the CloudFormation template. + * @param {String} options.Name - The name of the table. + * @param {String} options.DatabaseName - The name of the database the table resides in. + * @param {String} options.Location - The physical location of the table (S3 URI). Required. + * @param {Array} [options.Columns] - Simple column definitions as array of {Name, Type, Required}. + * If provided, will be auto-converted to Iceberg Schema format. Use options.Schema for full control. + * @param {Object} [options.Schema] - Full Iceberg schema definition with Type: "struct" and Fields array. + * Either Columns or Schema must be provided. See [AWS + * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-icebergtableinput.html). + * @param {Object} [options.PartitionSpec] - Iceberg partition specification. See [AWS + * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-partitionspec.html). + * @param {Object} [options.WriteOrder] - Iceberg write order specification. See [AWS + * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-writeorder.html). + * @param {String} [options.CatalogId=AccountId] - The AWS account ID for the account in which to create the table. + * @param {String} [options.IcebergVersion='2'] - The table version for the Iceberg table. + * @param {Boolean} [options.EnableOptimizer=false] - Whether to enable the snapshot retention optimizer. + * @param {String} [options.OptimizerRoleArn=undefined] - The ARN of the IAM role for the retention optimizer. Required if EnableOptimizer is true. + * @param {Number} [options.SnapshotRetentionPeriodInDays=5] - The number of days to retain snapshots. + * @param {Number} [options.NumberOfSnapshotsToRetain=1] - The minimum number of snapshots to retain. + * @param {Boolean} [options.CleanExpiredFiles=true] - Whether to delete expired data files after expiring snapshots. + * @param {Boolean} [options.EnableCompaction=false] - Whether to enable the compaction optimizer. + * @param {String} [options.CompactionRoleArn=undefined] - The ARN of the IAM role for the compaction optimizer. Required if EnableCompaction is true. + * @param {Boolean} [options.EnableOrphanFileDeletion=false] - Whether to enable the orphan file deletion optimizer. + * @param {String} [options.OrphanFileDeletionRoleArn=undefined] - The ARN of the IAM role for the orphan file deletion optimizer. Required if EnableOrphanFileDeletion is true. + * @param {Number} [options.OrphanFileRetentionPeriodInDays=3] - The number of days to retain orphan files before deleting them. + * @param {String} [options.OrphanFileDeletionLocation=undefined] - The S3 location to scan for orphan files. + * @param {String} [options.Condition=undefined] - CloudFormation condition name. + * @param {String} [options.DependsOn=undefined] - CloudFormation resource dependency. */ -class GlueIcebergTable extends GlueTable { +class GlueIcebergTable { constructor(options) { if (!options) throw new Error('Options required'); const { + LogicalName, + Name, + DatabaseName, Location, + Columns, + Schema, + PartitionSpec, + WriteOrder, + CatalogId = { Ref: 'AWS::AccountId' }, IcebergVersion = '2', EnableOptimizer = false, OptimizerRoleArn, @@ -73,12 +57,18 @@ class GlueIcebergTable extends GlueTable { EnableOrphanFileDeletion = false, OrphanFileDeletionRoleArn, OrphanFileRetentionPeriodInDays = 3, - OrphanFileDeletionLocation + OrphanFileDeletionLocation, + Condition, + DependsOn } = options; - const required = [Location]; + // Validate required fields + const required = [LogicalName, Name, DatabaseName, Location]; if (required.some((variable) => !variable)) - throw new Error('You must provide a Location'); + throw new Error('You must provide a LogicalName, Name, DatabaseName, and Location'); + + if (!Columns && !Schema) + throw new Error('You must provide either Columns or Schema'); if (EnableOptimizer && !OptimizerRoleArn) throw new Error('You must provide an OptimizerRoleArn when EnableOptimizer is true'); @@ -89,34 +79,64 @@ class GlueIcebergTable extends GlueTable { if (EnableOrphanFileDeletion && !OrphanFileDeletionRoleArn) throw new Error('You must provide an OrphanFileDeletionRoleArn when EnableOrphanFileDeletion is true'); - super( - Object.assign( - { - TableType: 'EXTERNAL_TABLE', - Parameters: { EXTERNAL: 'TRUE' } - }, - options - ) - ); - - const logicalName = options.LogicalName; - this.Resources[logicalName].Properties.OpenTableFormatInput = { - IcebergInput: { - MetadataOperation: 'CREATE', - Version: IcebergVersion + // Convert simple Columns format to Iceberg Schema format if needed + let icebergSchema = Schema; + if (!Schema && Columns) { + icebergSchema = { + Type: 'struct', + Fields: Columns.map((col, index) => ({ + Name: col.Name, + Type: col.Type, + Id: index + 1, + Required: col.Required !== undefined ? col.Required : true + })) + }; + } + + // Build the Iceberg table resource (no TableInput!) + this.Resources = { + [LogicalName]: { + Type: 'AWS::Glue::Table', + Condition, + DependsOn, + Properties: { + CatalogId, + DatabaseName, + Name, + OpenTableFormatInput: { + IcebergInput: { + MetadataOperation: 'CREATE', + Version: IcebergVersion, + IcebergTableInput: { + Location, + Schema: icebergSchema + } + } + } + } } }; + // Add optional PartitionSpec if provided + if (PartitionSpec) { + this.Resources[LogicalName].Properties.OpenTableFormatInput.IcebergInput.IcebergTableInput.PartitionSpec = PartitionSpec; + } + + // Add optional WriteOrder if provided + if (WriteOrder) { + this.Resources[LogicalName].Properties.OpenTableFormatInput.IcebergInput.IcebergTableInput.WriteOrder = WriteOrder; + } + // Optionally add TableOptimizer for configuring snapshot retention if (EnableOptimizer) { - const optimizerLogicalName = `${logicalName}RetentionOptimizer`; + const optimizerLogicalName = `${LogicalName}RetentionOptimizer`; this.Resources[optimizerLogicalName] = { Type: 'AWS::Glue::TableOptimizer', - DependsOn: logicalName, + DependsOn: LogicalName, Properties: { - CatalogId: options.CatalogId || { Ref: 'AWS::AccountId' }, - DatabaseName: options.DatabaseName, - TableName: options.Name, + CatalogId, + DatabaseName, + TableName: Name, Type: 'retention', TableOptimizerConfiguration: { RoleArn: OptimizerRoleArn, @@ -133,8 +153,8 @@ class GlueIcebergTable extends GlueTable { }; // Apply Condition to optimizer if specified on the table - if (options.Condition) { - this.Resources[optimizerLogicalName].Condition = options.Condition; + if (Condition) { + this.Resources[optimizerLogicalName].Condition = Condition; } } @@ -144,14 +164,14 @@ class GlueIcebergTable extends GlueTable { // via AWS CLI/API after stack creation, or will use AWS defaults. // See: https://github.com/aws-cloudformation/cloudformation-coverage-roadmap/issues/2257 if (EnableCompaction) { - const compactionLogicalName = `${logicalName}CompactionOptimizer`; + const compactionLogicalName = `${LogicalName}CompactionOptimizer`; this.Resources[compactionLogicalName] = { Type: 'AWS::Glue::TableOptimizer', - DependsOn: logicalName, + DependsOn: LogicalName, Properties: { - CatalogId: options.CatalogId || { Ref: 'AWS::AccountId' }, - DatabaseName: options.DatabaseName, - TableName: options.Name, + CatalogId, + DatabaseName, + TableName: Name, Type: 'compaction', TableOptimizerConfiguration: { RoleArn: CompactionRoleArn, @@ -161,14 +181,14 @@ class GlueIcebergTable extends GlueTable { }; // Apply Condition to compaction optimizer if specified on the table - if (options.Condition) { - this.Resources[compactionLogicalName].Condition = options.Condition; + if (Condition) { + this.Resources[compactionLogicalName].Condition = Condition; } } // Optionally add TableOptimizer for orphan file deletion if (EnableOrphanFileDeletion) { - const orphanLogicalName = `${logicalName}OrphanFileDeletionOptimizer`; + const orphanLogicalName = `${LogicalName}OrphanFileDeletionOptimizer`; const icebergConfiguration = { OrphanFileRetentionPeriodInDays }; @@ -180,11 +200,11 @@ class GlueIcebergTable extends GlueTable { this.Resources[orphanLogicalName] = { Type: 'AWS::Glue::TableOptimizer', - DependsOn: logicalName, + DependsOn: LogicalName, Properties: { - CatalogId: options.CatalogId || { Ref: 'AWS::AccountId' }, - DatabaseName: options.DatabaseName, - TableName: options.Name, + CatalogId, + DatabaseName, + TableName: Name, Type: 'orphan_file_deletion', TableOptimizerConfiguration: { RoleArn: OrphanFileDeletionRoleArn, @@ -197,8 +217,8 @@ class GlueIcebergTable extends GlueTable { }; // Apply Condition to orphan file deletion optimizer if specified on the table - if (options.Condition) { - this.Resources[orphanLogicalName].Condition = options.Condition; + if (Condition) { + this.Resources[orphanLogicalName].Condition = Condition; } } } diff --git a/package-lock.json b/package-lock.json index 5a62f94..96910ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "@mapbox/eslint-config-mapbox": "^1.2.1", "eslint": "^4.18.2", "eslint-plugin-node": "^6.0.1", - "jsdoc-to-markdown": "^7.1.1", + "jsdoc-to-markdown": "^9.1.3", "nyc": "^14.1.0", "opener": "^1.4.1", "tap-spec": "^5.0.0", @@ -843,10 +843,11 @@ } }, "node_modules/@jsdoc/salty": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz", - "integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.9.tgz", + "integrity": "sha512-yYxMVH7Dqw6nO0d5NIV8OQWnitU8k6vXH8NtgqAfIa/IUqRMxRv/NUJJ08VEKbAakwxlgBl5PJdrU0dMPStsnw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "lodash": "^4.17.21" }, @@ -860,6 +861,44 @@ "integrity": "sha512-hhE2sN7L/RO+MhdHIhmpL0KLhA/sWxV1WWH998fmGimI40XDbBgG1Z4d8nTlfuf9jaRDJ88/uklSBvFRhvMeDA==", "dev": true }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@smithy/abort-controller": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", @@ -1455,26 +1494,29 @@ } }, "node_modules/@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", - "dev": true + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", "dev": true, + "license": "MIT", "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "@types/linkify-it": "^5", + "@types/mdurl": "^2" } }, "node_modules/@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" }, "node_modules/@types/uuid": { "version": "9.0.8", @@ -1533,27 +1575,6 @@ "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", "dev": true }, - "node_modules/ansi-escape-sequences": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz", - "integrity": "sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw==", - "dev": true, - "dependencies": { - "array-back": "^3.0.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/ansi-escape-sequences/node_modules/array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-escapes": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", @@ -1613,6 +1634,7 @@ "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.17" } @@ -1654,7 +1676,8 @@ "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/bowser": { "version": "2.11.0", @@ -1673,6 +1696,19 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/buffer-from": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", @@ -1686,26 +1722,24 @@ "dev": true }, "node_modules/cache-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-2.0.0.tgz", - "integrity": "sha512-4gkeHlFpSKgm3vm2gJN5sPqfmijYRFYCQ6tv5cLw0xVmT6r1z1vd4FNnpuOREco3cBs1G709sZ72LdgddKvL5w==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-3.0.1.tgz", + "integrity": "sha512-itTIMLEKbh6Dw5DruXbxAgcyLnh/oPGVLBfTPqBOftASxHe8bAeXy7JkO4F0LvHqht7XqP5O/09h5UcHS2w0FA==", "dev": true, + "license": "MIT", "dependencies": { - "array-back": "^4.0.1", - "fs-then-native": "^2.0.0", - "mkdirp2": "^1.0.4" + "array-back": "^6.2.2" }, "engines": { - "node": ">=8" - } - }, - "node_modules/cache-point/node_modules/array-back": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", - "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", - "dev": true, - "engines": { - "node": ">=8" + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } } }, "node_modules/caching-transform": { @@ -1771,6 +1805,7 @@ "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", "dev": true, + "license": "MIT", "dependencies": { "lodash": "^4.17.15" }, @@ -1792,6 +1827,98 @@ "node": ">=4" } }, + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/chalk-template/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/chalk-template/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk-template/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/chalk/node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -1919,19 +2046,6 @@ "node": ">=0.10.0" } }, - "node_modules/collect-all": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.4.tgz", - "integrity": "sha512-RKZhRwJtJEP5FWul+gkSMEnaK6H3AGPTTWOiRimCcs+rc/OmQE3Yhy1Q7A7KsdkG3ZXVdZq68Y6ONSdvkeEcKA==", - "dev": true, - "dependencies": { - "stream-connect": "^1.0.2", - "stream-via": "^1.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1948,100 +2062,53 @@ "dev": true }, "node_modules/command-line-args": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", - "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-6.0.1.tgz", + "integrity": "sha512-Jr3eByUjqyK0qd8W0SGFW1nZwqCaNCtbXjRo2cRJC1OYxWl3MZ5t1US3jq+cO4sPavqgw4l9BMGX0CBe+trepg==", "dev": true, + "license": "MIT", "dependencies": { - "array-back": "^3.1.0", - "find-replace": "^3.0.0", + "array-back": "^6.2.2", + "find-replace": "^5.0.2", "lodash.camelcase": "^4.3.0", - "typical": "^4.0.0" + "typical": "^7.2.0" }, "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/command-line-args/node_modules/array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/command-line-args/node_modules/typical": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", - "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/command-line-tool": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/command-line-tool/-/command-line-tool-0.8.0.tgz", - "integrity": "sha512-Xw18HVx/QzQV3Sc5k1vy3kgtOeGmsKIqwtFFoyjI4bbcpSgnw2CWVULvtakyw4s6fhyAdI6soQQhXc2OzJy62g==", - "dev": true, - "dependencies": { - "ansi-escape-sequences": "^4.0.0", - "array-back": "^2.0.0", - "command-line-args": "^5.0.0", - "command-line-usage": "^4.1.0", - "typical": "^2.6.1" + "node": ">=12.20" }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/command-line-tool/node_modules/array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", - "dev": true, - "dependencies": { - "typical": "^2.6.1" + "peerDependencies": { + "@75lb/nature": "latest" }, - "engines": { - "node": ">=4" + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } } }, "node_modules/command-line-usage": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-4.1.0.tgz", - "integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==", - "dev": true, - "dependencies": { - "ansi-escape-sequences": "^4.0.0", - "array-back": "^2.0.0", - "table-layout": "^0.4.2", - "typical": "^2.6.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/command-line-usage/node_modules/array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", + "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", "dev": true, + "license": "MIT", "dependencies": { - "typical": "^2.6.1" + "array-back": "^6.2.2", + "chalk-template": "^0.4.0", + "table-layout": "^4.1.0", + "typical": "^7.1.1" }, "engines": { - "node": ">=4" + "node": ">=12.20.0" } }, "node_modules/common-sequence": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-2.0.2.tgz", - "integrity": "sha512-jAg09gkdkrDO9EWTdXfv80WWH3yeZl5oT69fGfedBNS9pXUKYInVJ1bJ+/ht2+Moeei48TmSbQDYMc8EOx9G0g==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-3.0.0.tgz", + "integrity": "sha512-g/CgSYk93y+a1IKm50tKl7kaT/OjjTYVQlEbUlt/49ZLV1mcKpUU7iyDiqTAeLdb4QDtQfq3ako8y8v//fzrWQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12.17" } }, "node_modules/commondir": { @@ -2146,6 +2213,16 @@ "node": ">=4.8" } }, + "node_modules/current-module-paths": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/current-module-paths/-/current-module-paths-1.1.3.tgz", + "integrity": "sha512-7AH+ZTRKikdK4s1RmY0l6067UD/NZc7p3zZVZxvmnH80G31kr0y0W0E6ibYM4IS01MEm8DiC5FnTcgcgkbFHoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -2179,15 +2256,6 @@ "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", "dev": true }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -2229,26 +2297,30 @@ "dev": true }, "node_modules/dmd": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.1.0.tgz", - "integrity": "sha512-0zQIJ873gay1scCTFZvHPWM9mVJBnaylB2NQDI8O9u8O32m00Jb6uxDKexZm8hjTRM7RiWe0FJ32pExHoXdwoQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/dmd/-/dmd-7.1.1.tgz", + "integrity": "sha512-Ap2HP6iuOek7eShReDLr9jluNJm9RMZESlt29H/Xs1qrVMkcS9X6m5h1mBC56WMxNiSo0wvjGICmZlYUSFjwZQ==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^6.2.2", - "cache-point": "^2.0.0", - "common-sequence": "^2.0.2", - "file-set": "^4.0.2", - "handlebars": "^4.7.7", - "marked": "^4.0.12", - "object-get": "^2.1.1", - "reduce-flatten": "^3.0.1", - "reduce-unique": "^2.0.1", - "reduce-without": "^1.0.1", - "test-value": "^3.0.0", - "walk-back": "^5.1.0" + "cache-point": "^3.0.0", + "common-sequence": "^3.0.0", + "file-set": "^5.2.2", + "handlebars": "^4.7.8", + "marked": "^4.3.0", + "walk-back": "^5.1.1" }, "engines": { - "node": ">=12" + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } } }, "node_modules/doctrine": { @@ -2279,10 +2351,14 @@ } }, "node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } @@ -2613,6 +2689,23 @@ "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "dev": true }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", @@ -2643,6 +2736,16 @@ "fxparser": "src/cli/cli.js" } }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -2669,45 +2772,38 @@ } }, "node_modules/file-set": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/file-set/-/file-set-4.0.2.tgz", - "integrity": "sha512-fuxEgzk4L8waGXaAkd8cMr73Pm0FxOVkn8hztzUW7BAHhOGH90viQNXbiOsnecCWmfInqU6YmAMwxRMdKETceQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/file-set/-/file-set-5.3.0.tgz", + "integrity": "sha512-FKCxdjLX0J6zqTWdT0RXIxNF/n7MyXXnsSUp0syLEOCKdexvPZ02lNNv2a+gpK9E3hzUYF3+eFZe32ci7goNUg==", "dev": true, + "license": "MIT", "dependencies": { - "array-back": "^5.0.0", - "glob": "^7.1.6" + "array-back": "^6.2.2", + "fast-glob": "^3.3.2" }, "engines": { - "node": ">=10" - } - }, - "node_modules/file-set/node_modules/array-back": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", - "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", - "dev": true, - "engines": { - "node": ">=10" + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } } }, - "node_modules/file-set/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "to-regex-range": "^5.0.1" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8" } }, "node_modules/find-cache-dir": { @@ -2725,24 +2821,21 @@ } }, "node_modules/find-replace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", - "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", - "dev": true, - "dependencies": { - "array-back": "^3.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/find-replace/node_modules/array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz", + "integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=14" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } } }, "node_modules/find-up": { @@ -2803,15 +2896,6 @@ "which": "^1.2.9" } }, - "node_modules/fs-then-native": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fs-then-native/-/fs-then-native-2.0.0.tgz", - "integrity": "sha512-X712jAOaWXkemQCAmWeg5rOT2i+KOpWz1Z/txk/cW0qlOu2oQ9H61vc5w3X/iyuUEfq/OyaFJ78/cZAQD1/bgA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2935,6 +3019,19 @@ "node": "*" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -2954,13 +3051,14 @@ } }, "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, + "license": "MIT", "dependencies": { "minimist": "^1.2.5", - "neo-async": "^2.6.0", + "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, @@ -3291,6 +3389,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-finite": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", @@ -3312,6 +3420,19 @@ "node": ">=0.10.0" } }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -3324,6 +3445,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-number-object": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", @@ -3597,26 +3728,28 @@ "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "xmlcreate": "^2.0.4" } }, "node_modules/jsdoc": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", - "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.5.tgz", + "integrity": "sha512-P4C6MWP9yIlMiK8nwoZvxN84vb6MsnXcHuy7XzVOvQoCizWX5JFCBsWIIWKXBltpoRZXddUOVQmCTOZt9yDj9g==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@babel/parser": "^7.20.15", "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^12.2.3", + "@types/markdown-it": "^14.1.1", "bluebird": "^3.7.2", "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.2", "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", + "markdown-it": "^14.1.0", + "markdown-it-anchor": "^8.6.7", "marked": "^4.0.10", "mkdirp": "^1.0.4", "requizzle": "^0.2.3", @@ -3631,78 +3764,76 @@ } }, "node_modules/jsdoc-api": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-7.2.0.tgz", - "integrity": "sha512-93YDnlm/OYTlLOFeNs4qAv0RBCJ0kGj67xQaWy8wrbk97Rw1EySitoOTHsTHXPEs3uyx2IStPKGrbE7LTnZXbA==", + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-9.3.5.tgz", + "integrity": "sha512-TQwh1jA8xtCkIbVwm/XA3vDRAa5JjydyKx1cC413Sh3WohDFxcMdwKSvn4LOsq2xWyAmOU/VnSChTQf6EF0R8g==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^6.2.2", - "cache-point": "^2.0.0", - "collect-all": "^1.0.4", - "file-set": "^4.0.2", - "fs-then-native": "^2.0.0", - "jsdoc": "^4.0.0", + "cache-point": "^3.0.1", + "current-module-paths": "^1.1.2", + "file-set": "^5.3.0", + "jsdoc": "^4.0.4", "object-to-spawn-args": "^2.0.1", - "temp-path": "^1.0.0", - "walk-back": "^5.1.0" + "walk-back": "^5.1.1" }, "engines": { "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } } }, "node_modules/jsdoc-parse": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.2.4.tgz", - "integrity": "sha512-MQA+lCe3ioZd0uGbyB3nDCDZcKgKC7m/Ivt0LgKZdUoOlMJxUWJQ3WI6GeyHp9ouznKaCjlp7CU9sw5k46yZTw==", + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.2.5.tgz", + "integrity": "sha512-8JaSNjPLr2IuEY4Das1KM6Z4oLHZYUnjRrr27hKSa78Cj0i5Lur3DzNnCkz+DfrKBDoljGMoWOiBVQbtUZJBPw==", "dev": true, "license": "MIT", "dependencies": { "array-back": "^6.2.2", "find-replace": "^5.0.1", - "lodash.omit": "^4.5.0", "sort-array": "^5.0.0" }, "engines": { "node": ">=12" } }, - "node_modules/jsdoc-parse/node_modules/find-replace": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz", - "integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@75lb/nature": "latest" - }, - "peerDependenciesMeta": { - "@75lb/nature": { - "optional": true - } - } - }, "node_modules/jsdoc-to-markdown": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-7.1.1.tgz", - "integrity": "sha512-CI86d63xAVNO+ENumWwmJ034lYe5iGU5GwjtTA11EuphP9tpnoi4hrKgR/J8uME0D+o4KUpVfwX1fjZhc8dEtg==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-9.1.3.tgz", + "integrity": "sha512-i9wi+6WHX0WKziv0ar88T8h7OmxA0LWdQaV23nY6uQyKvdUPzVt0o6YAaOceFuKRF5Rvlju5w/KnZBfdpDAlnw==", "dev": true, + "license": "MIT", "dependencies": { "array-back": "^6.2.2", - "command-line-tool": "^0.8.0", + "command-line-args": "^6.0.1", + "command-line-usage": "^7.0.3", "config-master": "^3.1.0", - "dmd": "^6.1.0", - "jsdoc-api": "^7.1.1", - "jsdoc-parse": "^6.1.0", - "walk-back": "^5.1.0" + "dmd": "^7.1.1", + "jsdoc-api": "^9.3.5", + "jsdoc-parse": "^6.2.5", + "walk-back": "^5.1.1" }, "bin": { "jsdoc2md": "bin/cli.js" }, "engines": { "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } } }, "node_modules/jsdoc/node_modules/escape-string-regexp": { @@ -3710,6 +3841,7 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -3719,6 +3851,7 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, + "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" }, @@ -3731,6 +3864,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -3774,6 +3908,7 @@ "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.1.9" } @@ -3804,12 +3939,13 @@ } }, "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", "dev": true, + "license": "MIT", "dependencies": { - "uc.micro": "^1.0.1" + "uc.micro": "^2.0.0" } }, "node_modules/load-json-file": { @@ -3859,7 +3995,8 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.flattendeep": { "version": "4.4.0", @@ -3867,18 +4004,6 @@ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, - "node_modules/lodash.omit": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", - "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", - "dev": true - }, - "node_modules/lodash.padend": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", - "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", - "dev": true - }, "node_modules/lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", @@ -3924,19 +4049,21 @@ } }, "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, "bin": { - "markdown-it": "bin/markdown-it.js" + "markdown-it": "bin/markdown-it.mjs" } }, "node_modules/markdown-it-anchor": { @@ -3944,6 +4071,7 @@ "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", "dev": true, + "license": "Unlicense", "peerDependencies": { "@types/markdown-it": "*", "markdown-it": "*" @@ -3953,13 +4081,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/marked": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.1.tgz", - "integrity": "sha512-0cNMnTcUJPxbA6uWmCmjWz4NJRe/0Xfk2NhXCUHjew9qJzFN20krFnsUe7QynwqOwa5m1fZ4UDg0ycKFVC0ccw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, + "license": "MIT", "bin": { "marked": "bin/marked.js" }, @@ -3968,10 +4098,11 @@ } }, "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" }, "node_modules/mem": { "version": "4.3.0", @@ -3996,6 +4127,30 @@ "source-map": "^0.6.1" } }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -4034,12 +4189,6 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/mkdirp2": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/mkdirp2/-/mkdirp2-1.0.5.tgz", - "integrity": "sha512-xOE9xbICroUDmG1ye2h4bZ8WBie9EGmACaco8K8cx6RlkJJrxGIqjGqztAI+NMhexXBcdGbSEzI6N3EJPevxZw==", - "dev": true - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4063,7 +4212,8 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/nested-error-stacks": { "version": "2.1.0", @@ -4273,14 +4423,8 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-get": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object-get/-/object-get-2.1.1.tgz", - "integrity": "sha512-7n4IpLMzGGcLEMiQKsNR7vCe+N5E9LORFrtNUVy4sO3dj9a3HedZCxEL2T7QuLhcHN1NBuBsMOKaOsAYI9IIvg==", - "dev": true + "node": ">=0.10.0" + } }, "node_modules/object-inspect": { "version": "1.6.0", @@ -4302,6 +4446,7 @@ "resolved": "https://registry.npmjs.org/object-to-spawn-args/-/object-to-spawn-args-2.0.1.tgz", "integrity": "sha512-6FuKFQ39cOID+BMZ3QaphcC8Y4cw6LXBLyIgPU+OhIYwviJamPAn+4mITapnSBQrejB+NNp+FMskhD8Cq+Ys3w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.0.0" } @@ -4582,6 +4727,19 @@ "dev": true, "license": "ISC" }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/pkg-dir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", @@ -4656,6 +4814,37 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/re-emitter": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/re-emitter/-/re-emitter-1.1.3.tgz", @@ -4716,61 +4905,6 @@ "node": ">=4" } }, - "node_modules/reduce-flatten": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-3.0.1.tgz", - "integrity": "sha512-bYo+97BmUUOzg09XwfkwALt4PQH1M5L0wzKerBt6WLm3Fhdd43mMS89HiT1B9pJIqko/6lWx3OnV4J9f2Kqp5Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/reduce-unique": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/reduce-unique/-/reduce-unique-2.0.1.tgz", - "integrity": "sha512-x4jH/8L1eyZGR785WY+ePtyMNhycl1N2XOLxhCbzZFaqF4AXjLzqSxa2UHgJ2ZVR/HHyPOvl1L7xRnW8ye5MdA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/reduce-without": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-without/-/reduce-without-1.0.1.tgz", - "integrity": "sha512-zQv5y/cf85sxvdrKPlfcRzlDn/OqKFThNimYmsS3flmkioKvkUGn2Qg9cJVoQiEvdxFGLE0MQER/9fZ9sUqdxg==", - "dev": true, - "dependencies": { - "test-value": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/reduce-without/node_modules/array-back": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", - "dev": true, - "dependencies": { - "typical": "^2.6.0" - }, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/reduce-without/node_modules/test-value": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz", - "integrity": "sha512-+1epbAxtKeXttkGFMTX9H42oqzOTufR1ceCF+GYA5aOmvaPq9wd4PUS8329fn2RRLGNeUkgRLnVpycjx8DsO2w==", - "dev": true, - "dependencies": { - "array-back": "^1.0.3", - "typical": "^2.6.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -4851,6 +4985,7 @@ "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", "dev": true, + "license": "MIT", "dependencies": { "lodash": "^4.17.21" } @@ -4903,6 +5038,17 @@ "through": "~2.3.4" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", @@ -4927,6 +5073,30 @@ "node": ">=0.12.0" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/rx-lite": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", @@ -5076,16 +5246,6 @@ } } }, - "node_modules/sort-array/node_modules/typical": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", - "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.17" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -5147,39 +5307,6 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "node_modules/stream-connect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz", - "integrity": "sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==", - "dev": true, - "dependencies": { - "array-back": "^1.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stream-connect/node_modules/array-back": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", - "dev": true, - "dependencies": { - "typical": "^2.6.0" - }, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/stream-via": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/stream-via/-/stream-via-1.0.4.tgz", - "integrity": "sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -5340,31 +5467,17 @@ } }, "node_modules/table-layout": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", - "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", - "dev": true, - "dependencies": { - "array-back": "^2.0.0", - "deep-extend": "~0.6.0", - "lodash.padend": "^4.6.1", - "typical": "^2.6.1", - "wordwrapjs": "^3.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/table-layout/node_modules/array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", + "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", "dev": true, + "license": "MIT", "dependencies": { - "typical": "^2.6.1" + "array-back": "^6.2.2", + "wordwrapjs": "^5.1.0" }, "engines": { - "node": ">=4" + "node": ">=12.17" } }, "node_modules/table/node_modules/ansi-regex": { @@ -5549,12 +5662,6 @@ "path-parse": "^1.0.5" } }, - "node_modules/temp-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", - "integrity": "sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==", - "dev": true - }, "node_modules/test-exclude": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", @@ -5587,31 +5694,6 @@ "node": "*" } }, - "node_modules/test-value": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", - "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==", - "dev": true, - "dependencies": { - "array-back": "^2.0.0", - "typical": "^2.6.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/test-value/node_modules/array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", - "dev": true, - "dependencies": { - "typical": "^2.6.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -5646,6 +5728,19 @@ "node": ">=0.6.0" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/trim": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", @@ -5677,22 +5772,28 @@ "dev": true }, "node_modules/typical": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", - "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", - "dev": true + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", + "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } }, "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" }, "node_modules/uglify-js": { - "version": "3.17.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.3.tgz", - "integrity": "sha512-JmMFDME3iufZnBpyKL+uS78LRiC+mK55zWfM5f/pWBJfpOttXAqYfdDGRukYhJuyRinvPVAtUhvy7rlDybNtFg==", + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, + "license": "BSD-2-Clause", "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" @@ -5717,10 +5818,11 @@ } }, "node_modules/underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", - "dev": true + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "dev": true, + "license": "MIT" }, "node_modules/util-deprecate": { "version": "1.0.2", @@ -5748,10 +5850,11 @@ } }, "node_modules/walk-back": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.1.0.tgz", - "integrity": "sha512-Uhxps5yZcVNbLEAnb+xaEEMdgTXl9qAQDzKYejG2AZ7qPwRQ81lozY9ECDbjLPNWm7YsO1IK5rsP1KoQzXAcGA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.1.1.tgz", + "integrity": "sha512-e/FRLDVdZQWFrAzU6Hdvpm7D7m2ina833gIKLptQykRK49mmCYHLHq7UqjPDbxbKLZkTkW1rFqbengdE3sLfdw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.17" } @@ -5797,25 +5900,13 @@ "dev": true }, "node_modules/wordwrapjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", - "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", - "dev": true, - "dependencies": { - "reduce-flatten": "^1.0.1", - "typical": "^2.6.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/wordwrapjs/node_modules/reduce-flatten": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", - "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.1.tgz", + "integrity": "sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=12.17" } }, "node_modules/wrap-ansi": { @@ -5864,7 +5955,8 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/xtend": { "version": "4.0.2", @@ -6526,9 +6618,9 @@ } }, "@jsdoc/salty": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz", - "integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.9.tgz", + "integrity": "sha512-yYxMVH7Dqw6nO0d5NIV8OQWnitU8k6vXH8NtgqAfIa/IUqRMxRv/NUJJ08VEKbAakwxlgBl5PJdrU0dMPStsnw==", "dev": true, "requires": { "lodash": "^4.17.21" @@ -6540,6 +6632,32 @@ "integrity": "sha512-hhE2sN7L/RO+MhdHIhmpL0KLhA/sWxV1WWH998fmGimI40XDbBgG1Z4d8nTlfuf9jaRDJ88/uklSBvFRhvMeDA==", "dev": true }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, "@smithy/abort-controller": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", @@ -6969,25 +7087,25 @@ } }, "@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", "dev": true }, "@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", "dev": true, "requires": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "@types/linkify-it": "^5", + "@types/mdurl": "^2" } }, "@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", "dev": true }, "@types/uuid": { @@ -7036,23 +7154,6 @@ "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", "dev": true }, - "ansi-escape-sequences": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz", - "integrity": "sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw==", - "dev": true, - "requires": { - "array-back": "^3.0.1" - }, - "dependencies": { - "array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "dev": true - } - } - }, "ansi-escapes": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", @@ -7154,6 +7255,15 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "requires": { + "fill-range": "^7.1.1" + } + }, "buffer-from": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", @@ -7167,22 +7277,12 @@ "dev": true }, "cache-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-2.0.0.tgz", - "integrity": "sha512-4gkeHlFpSKgm3vm2gJN5sPqfmijYRFYCQ6tv5cLw0xVmT6r1z1vd4FNnpuOREco3cBs1G709sZ72LdgddKvL5w==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-3.0.1.tgz", + "integrity": "sha512-itTIMLEKbh6Dw5DruXbxAgcyLnh/oPGVLBfTPqBOftASxHe8bAeXy7JkO4F0LvHqht7XqP5O/09h5UcHS2w0FA==", "dev": true, "requires": { - "array-back": "^4.0.1", - "fs-then-native": "^2.0.0", - "mkdirp2": "^1.0.4" - }, - "dependencies": { - "array-back": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", - "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", - "dev": true - } + "array-back": "^6.2.2" } }, "caching-transform": { @@ -7254,16 +7354,76 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "^1.9.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", + "dev": true, + "requires": { + "chalk": "^4.1.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" } }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } } } @@ -7351,16 +7511,6 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, - "collect-all": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.4.tgz", - "integrity": "sha512-RKZhRwJtJEP5FWul+gkSMEnaK6H3AGPTTWOiRimCcs+rc/OmQE3Yhy1Q7A7KsdkG3ZXVdZq68Y6ONSdvkeEcKA==", - "dev": true, - "requires": { - "stream-connect": "^1.0.2", - "stream-via": "^1.0.4" - } - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -7377,82 +7527,33 @@ "dev": true }, "command-line-args": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", - "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-6.0.1.tgz", + "integrity": "sha512-Jr3eByUjqyK0qd8W0SGFW1nZwqCaNCtbXjRo2cRJC1OYxWl3MZ5t1US3jq+cO4sPavqgw4l9BMGX0CBe+trepg==", "dev": true, "requires": { - "array-back": "^3.1.0", - "find-replace": "^3.0.0", + "array-back": "^6.2.2", + "find-replace": "^5.0.2", "lodash.camelcase": "^4.3.0", - "typical": "^4.0.0" - }, - "dependencies": { - "array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "dev": true - }, - "typical": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", - "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", - "dev": true - } - } - }, - "command-line-tool": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/command-line-tool/-/command-line-tool-0.8.0.tgz", - "integrity": "sha512-Xw18HVx/QzQV3Sc5k1vy3kgtOeGmsKIqwtFFoyjI4bbcpSgnw2CWVULvtakyw4s6fhyAdI6soQQhXc2OzJy62g==", - "dev": true, - "requires": { - "ansi-escape-sequences": "^4.0.0", - "array-back": "^2.0.0", - "command-line-args": "^5.0.0", - "command-line-usage": "^4.1.0", - "typical": "^2.6.1" - }, - "dependencies": { - "array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", - "dev": true, - "requires": { - "typical": "^2.6.1" - } - } + "typical": "^7.2.0" } }, "command-line-usage": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-4.1.0.tgz", - "integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", + "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", "dev": true, "requires": { - "ansi-escape-sequences": "^4.0.0", - "array-back": "^2.0.0", - "table-layout": "^0.4.2", - "typical": "^2.6.1" - }, - "dependencies": { - "array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", - "dev": true, - "requires": { - "typical": "^2.6.1" - } - } + "array-back": "^6.2.2", + "chalk-template": "^0.4.0", + "table-layout": "^4.1.0", + "typical": "^7.1.1" } }, "common-sequence": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-2.0.2.tgz", - "integrity": "sha512-jAg09gkdkrDO9EWTdXfv80WWH3yeZl5oT69fGfedBNS9pXUKYInVJ1bJ+/ht2+Moeei48TmSbQDYMc8EOx9G0g==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-3.0.0.tgz", + "integrity": "sha512-g/CgSYk93y+a1IKm50tKl7kaT/OjjTYVQlEbUlt/49ZLV1mcKpUU7iyDiqTAeLdb4QDtQfq3ako8y8v//fzrWQ==", "dev": true }, "commondir": { @@ -7545,6 +7646,12 @@ "which": "^1.2.9" } }, + "current-module-paths": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/current-module-paths/-/current-module-paths-1.1.3.tgz", + "integrity": "sha512-7AH+ZTRKikdK4s1RmY0l6067UD/NZc7p3zZVZxvmnH80G31kr0y0W0E6ibYM4IS01MEm8DiC5FnTcgcgkbFHoA==", + "dev": true + }, "debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -7566,12 +7673,6 @@ "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", "dev": true }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -7604,23 +7705,18 @@ "dev": true }, "dmd": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.1.0.tgz", - "integrity": "sha512-0zQIJ873gay1scCTFZvHPWM9mVJBnaylB2NQDI8O9u8O32m00Jb6uxDKexZm8hjTRM7RiWe0FJ32pExHoXdwoQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/dmd/-/dmd-7.1.1.tgz", + "integrity": "sha512-Ap2HP6iuOek7eShReDLr9jluNJm9RMZESlt29H/Xs1qrVMkcS9X6m5h1mBC56WMxNiSo0wvjGICmZlYUSFjwZQ==", "dev": true, "requires": { "array-back": "^6.2.2", - "cache-point": "^2.0.0", - "common-sequence": "^2.0.2", - "file-set": "^4.0.2", - "handlebars": "^4.7.7", - "marked": "^4.0.12", - "object-get": "^2.1.1", - "reduce-flatten": "^3.0.1", - "reduce-unique": "^2.0.1", - "reduce-without": "^1.0.1", - "test-value": "^3.0.0", - "walk-back": "^5.1.0" + "cache-point": "^3.0.0", + "common-sequence": "^3.0.0", + "file-set": "^5.2.2", + "handlebars": "^4.7.8", + "marked": "^4.3.0", + "walk-back": "^5.1.1" } }, "doctrine": { @@ -7648,9 +7744,9 @@ } }, "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true }, "error-ex": { @@ -7914,6 +8010,19 @@ "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "dev": true }, + "fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + } + }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", @@ -7934,6 +8043,15 @@ "strnum": "^2.1.0" } }, + "fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -7954,35 +8072,22 @@ } }, "file-set": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/file-set/-/file-set-4.0.2.tgz", - "integrity": "sha512-fuxEgzk4L8waGXaAkd8cMr73Pm0FxOVkn8hztzUW7BAHhOGH90viQNXbiOsnecCWmfInqU6YmAMwxRMdKETceQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/file-set/-/file-set-5.3.0.tgz", + "integrity": "sha512-FKCxdjLX0J6zqTWdT0RXIxNF/n7MyXXnsSUp0syLEOCKdexvPZ02lNNv2a+gpK9E3hzUYF3+eFZe32ci7goNUg==", "dev": true, "requires": { - "array-back": "^5.0.0", - "glob": "^7.1.6" - }, - "dependencies": { - "array-back": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", - "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } + "array-back": "^6.2.2", + "fast-glob": "^3.3.2" + } + }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" } }, "find-cache-dir": { @@ -7997,21 +8102,11 @@ } }, "find-replace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", - "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz", + "integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==", "dev": true, - "requires": { - "array-back": "^3.0.1" - }, - "dependencies": { - "array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "dev": true - } - } + "requires": {} }, "find-up": { "version": "3.0.0", @@ -8065,12 +8160,6 @@ } } }, - "fs-then-native": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fs-then-native/-/fs-then-native-2.0.0.tgz", - "integrity": "sha512-X712jAOaWXkemQCAmWeg5rOT2i+KOpWz1Z/txk/cW0qlOu2oQ9H61vc5w3X/iyuUEfq/OyaFJ78/cZAQD1/bgA==", - "dev": true - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -8169,6 +8258,15 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -8182,13 +8280,13 @@ "dev": true }, "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, "requires": { "minimist": "^1.2.5", - "neo-async": "^2.6.0", + "neo-async": "^2.6.2", "source-map": "^0.6.1", "uglify-js": "^3.1.4", "wordwrap": "^1.0.0" @@ -8432,6 +8530,12 @@ "has-tostringtag": "^1.0.0" } }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, "is-finite": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", @@ -8447,12 +8551,27 @@ "number-is-nan": "^1.0.0" } }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, "is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, "is-number-object": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", @@ -8671,21 +8790,21 @@ } }, "jsdoc": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", - "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.5.tgz", + "integrity": "sha512-P4C6MWP9yIlMiK8nwoZvxN84vb6MsnXcHuy7XzVOvQoCizWX5JFCBsWIIWKXBltpoRZXddUOVQmCTOZt9yDj9g==", "dev": true, "requires": { "@babel/parser": "^7.20.15", "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^12.2.3", + "@types/markdown-it": "^14.1.1", "bluebird": "^3.7.2", "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.2", "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", + "markdown-it": "^14.1.0", + "markdown-it-anchor": "^8.6.7", "marked": "^4.0.10", "mkdirp": "^1.0.4", "requizzle": "^0.2.3", @@ -8714,56 +8833,45 @@ } }, "jsdoc-api": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-7.2.0.tgz", - "integrity": "sha512-93YDnlm/OYTlLOFeNs4qAv0RBCJ0kGj67xQaWy8wrbk97Rw1EySitoOTHsTHXPEs3uyx2IStPKGrbE7LTnZXbA==", + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-9.3.5.tgz", + "integrity": "sha512-TQwh1jA8xtCkIbVwm/XA3vDRAa5JjydyKx1cC413Sh3WohDFxcMdwKSvn4LOsq2xWyAmOU/VnSChTQf6EF0R8g==", "dev": true, "requires": { "array-back": "^6.2.2", - "cache-point": "^2.0.0", - "collect-all": "^1.0.4", - "file-set": "^4.0.2", - "fs-then-native": "^2.0.0", - "jsdoc": "^4.0.0", + "cache-point": "^3.0.1", + "current-module-paths": "^1.1.2", + "file-set": "^5.3.0", + "jsdoc": "^4.0.4", "object-to-spawn-args": "^2.0.1", - "temp-path": "^1.0.0", - "walk-back": "^5.1.0" + "walk-back": "^5.1.1" } }, "jsdoc-parse": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.2.4.tgz", - "integrity": "sha512-MQA+lCe3ioZd0uGbyB3nDCDZcKgKC7m/Ivt0LgKZdUoOlMJxUWJQ3WI6GeyHp9ouznKaCjlp7CU9sw5k46yZTw==", + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.2.5.tgz", + "integrity": "sha512-8JaSNjPLr2IuEY4Das1KM6Z4oLHZYUnjRrr27hKSa78Cj0i5Lur3DzNnCkz+DfrKBDoljGMoWOiBVQbtUZJBPw==", "dev": true, "requires": { "array-back": "^6.2.2", "find-replace": "^5.0.1", - "lodash.omit": "^4.5.0", "sort-array": "^5.0.0" - }, - "dependencies": { - "find-replace": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz", - "integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==", - "dev": true, - "requires": {} - } } }, "jsdoc-to-markdown": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-7.1.1.tgz", - "integrity": "sha512-CI86d63xAVNO+ENumWwmJ034lYe5iGU5GwjtTA11EuphP9tpnoi4hrKgR/J8uME0D+o4KUpVfwX1fjZhc8dEtg==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-9.1.3.tgz", + "integrity": "sha512-i9wi+6WHX0WKziv0ar88T8h7OmxA0LWdQaV23nY6uQyKvdUPzVt0o6YAaOceFuKRF5Rvlju5w/KnZBfdpDAlnw==", "dev": true, "requires": { "array-back": "^6.2.2", - "command-line-tool": "^0.8.0", + "command-line-args": "^6.0.1", + "command-line-usage": "^7.0.3", "config-master": "^3.1.0", - "dmd": "^6.1.0", - "jsdoc-api": "^7.1.1", - "jsdoc-parse": "^6.1.0", - "walk-back": "^5.1.0" + "dmd": "^7.1.1", + "jsdoc-api": "^9.3.5", + "jsdoc-parse": "^6.2.5", + "walk-back": "^5.1.1" } }, "jsesc": { @@ -8819,12 +8927,12 @@ } }, "linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", "dev": true, "requires": { - "uc.micro": "^1.0.1" + "uc.micro": "^2.0.0" } }, "load-json-file": { @@ -8875,18 +8983,6 @@ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, - "lodash.omit": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", - "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", - "dev": true - }, - "lodash.padend": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", - "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", - "dev": true - }, "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", @@ -8925,16 +9021,17 @@ } }, "markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, "requires": { "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, "dependencies": { "argparse": { @@ -8953,15 +9050,15 @@ "requires": {} }, "marked": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.1.tgz", - "integrity": "sha512-0cNMnTcUJPxbA6uWmCmjWz4NJRe/0Xfk2NhXCUHjew9qJzFN20krFnsUe7QynwqOwa5m1fZ4UDg0ycKFVC0ccw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true }, "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", "dev": true }, "mem": { @@ -8984,6 +9081,22 @@ "source-map": "^0.6.1" } }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "requires": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + } + }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -9013,12 +9126,6 @@ "minimist": "^1.2.5" } }, - "mkdirp2": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/mkdirp2/-/mkdirp2-1.0.5.tgz", - "integrity": "sha512-xOE9xbICroUDmG1ye2h4bZ8WBie9EGmACaco8K8cx6RlkJJrxGIqjGqztAI+NMhexXBcdGbSEzI6N3EJPevxZw==", - "dev": true - }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -9219,12 +9326,6 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, - "object-get": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object-get/-/object-get-2.1.1.tgz", - "integrity": "sha512-7n4IpLMzGGcLEMiQKsNR7vCe+N5E9LORFrtNUVy4sO3dj9a3HedZCxEL2T7QuLhcHN1NBuBsMOKaOsAYI9IIvg==", - "dev": true - }, "object-inspect": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", @@ -9455,6 +9556,12 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, "pkg-dir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", @@ -9511,6 +9618,18 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, + "punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, "re-emitter": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/re-emitter/-/re-emitter-1.1.3.tgz", @@ -9562,48 +9681,6 @@ "strip-indent": "^2.0.0" } }, - "reduce-flatten": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-3.0.1.tgz", - "integrity": "sha512-bYo+97BmUUOzg09XwfkwALt4PQH1M5L0wzKerBt6WLm3Fhdd43mMS89HiT1B9pJIqko/6lWx3OnV4J9f2Kqp5Q==", - "dev": true - }, - "reduce-unique": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/reduce-unique/-/reduce-unique-2.0.1.tgz", - "integrity": "sha512-x4jH/8L1eyZGR785WY+ePtyMNhycl1N2XOLxhCbzZFaqF4AXjLzqSxa2UHgJ2ZVR/HHyPOvl1L7xRnW8ye5MdA==", - "dev": true - }, - "reduce-without": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-without/-/reduce-without-1.0.1.tgz", - "integrity": "sha512-zQv5y/cf85sxvdrKPlfcRzlDn/OqKFThNimYmsS3flmkioKvkUGn2Qg9cJVoQiEvdxFGLE0MQER/9fZ9sUqdxg==", - "dev": true, - "requires": { - "test-value": "^2.0.0" - }, - "dependencies": { - "array-back": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", - "dev": true, - "requires": { - "typical": "^2.6.0" - } - }, - "test-value": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz", - "integrity": "sha512-+1epbAxtKeXttkGFMTX9H42oqzOTufR1ceCF+GYA5aOmvaPq9wd4PUS8329fn2RRLGNeUkgRLnVpycjx8DsO2w==", - "dev": true, - "requires": { - "array-back": "^1.0.3", - "typical": "^2.6.0" - } - } - } - }, "regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -9703,6 +9780,12 @@ "through": "~2.3.4" } }, + "reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true + }, "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", @@ -9721,6 +9804,15 @@ "is-promise": "^2.1.0" } }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "rx-lite": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", @@ -9836,14 +9928,6 @@ "requires": { "array-back": "^6.2.2", "typical": "^7.1.1" - }, - "dependencies": { - "typical": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", - "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", - "dev": true - } } }, "source-map": { @@ -9904,32 +9988,6 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "stream-connect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz", - "integrity": "sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==", - "dev": true, - "requires": { - "array-back": "^1.0.2" - }, - "dependencies": { - "array-back": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", - "dev": true, - "requires": { - "typical": "^2.6.0" - } - } - } - }, - "stream-via": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/stream-via/-/stream-via-1.0.4.tgz", - "integrity": "sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==", - "dev": true - }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -10080,27 +10138,13 @@ } }, "table-layout": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", - "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", + "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", "dev": true, "requires": { - "array-back": "^2.0.0", - "deep-extend": "~0.6.0", - "lodash.padend": "^4.6.1", - "typical": "^2.6.1", - "wordwrapjs": "^3.0.0" - }, - "dependencies": { - "array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", - "dev": true, - "requires": { - "typical": "^2.6.1" - } - } + "array-back": "^6.2.2", + "wordwrapjs": "^5.1.0" } }, "tap-out": { @@ -10229,12 +10273,6 @@ } } }, - "temp-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", - "integrity": "sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==", - "dev": true - }, "test-exclude": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", @@ -10263,27 +10301,6 @@ } } }, - "test-value": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", - "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==", - "dev": true, - "requires": { - "array-back": "^2.0.0", - "typical": "^2.6.1" - }, - "dependencies": { - "array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", - "dev": true, - "requires": { - "typical": "^2.6.1" - } - } - } - }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -10315,6 +10332,15 @@ "os-tmpdir": "~1.0.2" } }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "trim": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", @@ -10342,21 +10368,21 @@ "dev": true }, "typical": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", - "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", + "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", "dev": true }, "uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", "dev": true }, "uglify-js": { - "version": "3.17.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.3.tgz", - "integrity": "sha512-JmMFDME3iufZnBpyKL+uS78LRiC+mK55zWfM5f/pWBJfpOttXAqYfdDGRukYhJuyRinvPVAtUhvy7rlDybNtFg==", + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, "optional": true }, @@ -10373,9 +10399,9 @@ } }, "underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", "dev": true }, "util-deprecate": { @@ -10401,9 +10427,9 @@ } }, "walk-back": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.1.0.tgz", - "integrity": "sha512-Uhxps5yZcVNbLEAnb+xaEEMdgTXl9qAQDzKYejG2AZ7qPwRQ81lozY9ECDbjLPNWm7YsO1IK5rsP1KoQzXAcGA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.1.1.tgz", + "integrity": "sha512-e/FRLDVdZQWFrAzU6Hdvpm7D7m2ina833gIKLptQykRK49mmCYHLHq7UqjPDbxbKLZkTkW1rFqbengdE3sLfdw==", "dev": true }, "which": { @@ -10441,22 +10467,10 @@ "dev": true }, "wordwrapjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", - "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", - "dev": true, - "requires": { - "reduce-flatten": "^1.0.1", - "typical": "^2.6.1" - }, - "dependencies": { - "reduce-flatten": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", - "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ==", - "dev": true - } - } + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.1.tgz", + "integrity": "sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==", + "dev": true }, "wrap-ansi": { "version": "2.1.0", diff --git a/package.json b/package.json index 7550cc9..fab84dc 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@mapbox/eslint-config-mapbox": "^1.2.1", "eslint": "^4.18.2", "eslint-plugin-node": "^6.0.1", - "jsdoc-to-markdown": "^7.1.1", + "jsdoc-to-markdown": "^9.1.3", "nyc": "^14.1.0", "opener": "^1.4.1", "tap-spec": "^5.0.0", diff --git a/test/fixtures/shortcuts/glue-iceberg-table-defaults.json b/test/fixtures/shortcuts/glue-iceberg-table-defaults.json index 2d00ccf..a454e98 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-defaults.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-defaults.json @@ -13,34 +13,25 @@ "Ref": "AWS::AccountId" }, "DatabaseName": "my_database", - "TableInput": { - "Description": { - "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" - }, - "Name": "my_table", - "Parameters": { - "EXTERNAL": "TRUE" - }, - "PartitionKeys": [], - "TableType": "EXTERNAL_TABLE", - "StorageDescriptor": { - "Columns": [ - { - "Name": "column", - "Type": "string" - } - ], - "Compressed": false, - "Location": "s3://fake/location", - "NumberOfBuckets": 0, - "SerdeInfo": {}, - "StoredAsSubDirectories": true - } - }, + "Name": "my_table", "OpenTableFormatInput": { "IcebergInput": { "MetadataOperation": "CREATE", - "Version": "2" + "Version": "2", + "IcebergTableInput": { + "Location": "s3://fake/location", + "Schema": { + "Type": "struct", + "Fields": [ + { + "Name": "column", + "Type": "string", + "Id": 1, + "Required": true + } + ] + } + } } } } diff --git a/test/fixtures/shortcuts/glue-iceberg-table-no-defaults.json b/test/fixtures/shortcuts/glue-iceberg-table-no-defaults.json index 3c16713..7cb90a5 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-no-defaults.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-no-defaults.json @@ -23,34 +23,25 @@ "Properties": { "CatalogId": "1234", "DatabaseName": "my_database", - "TableInput": { - "Description": "my_table description", - "Name": "my_table", - "Owner": "Team", - "Parameters": { - "table": "params" - }, - "PartitionKeys": [], - "Retention": 12, - "TableType": "EXTERNAL_TABLE", - "StorageDescriptor": { - "Columns": [ - { - "Name": "column", - "Type": "string" - } - ], - "Compressed": false, - "Location": "s3://fake/location", - "NumberOfBuckets": 0, - "SerdeInfo": {}, - "StoredAsSubDirectories": true - } - }, + "Name": "my_table", "OpenTableFormatInput": { "IcebergInput": { "MetadataOperation": "CREATE", - "Version": "2" + "Version": "2", + "IcebergTableInput": { + "Location": "s3://fake/location", + "Schema": { + "Type": "struct", + "Fields": [ + { + "Name": "column", + "Type": "string", + "Id": 1, + "Required": true + } + ] + } + } } } } diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-all-optimizers.json b/test/fixtures/shortcuts/glue-iceberg-table-with-all-optimizers.json index f2d181a..cb6eb05 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-with-all-optimizers.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-all-optimizers.json @@ -13,34 +13,25 @@ "Ref": "AWS::AccountId" }, "DatabaseName": "my_database", - "TableInput": { - "Description": { - "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" - }, - "Name": "my_table", - "Parameters": { - "EXTERNAL": "TRUE" - }, - "PartitionKeys": [], - "TableType": "EXTERNAL_TABLE", - "StorageDescriptor": { - "Columns": [ - { - "Name": "column", - "Type": "string" - } - ], - "Compressed": false, - "Location": "s3://fake/location", - "NumberOfBuckets": 0, - "SerdeInfo": {}, - "StoredAsSubDirectories": true - } - }, + "Name": "my_table", "OpenTableFormatInput": { "IcebergInput": { "MetadataOperation": "CREATE", - "Version": "2" + "Version": "2", + "IcebergTableInput": { + "Location": "s3://fake/location", + "Schema": { + "Type": "struct", + "Fields": [ + { + "Name": "column", + "Type": "string", + "Id": 1, + "Required": true + } + ] + } + } } } } diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-both-optimizers.json b/test/fixtures/shortcuts/glue-iceberg-table-with-both-optimizers.json index a9fe1d6..b5c8af6 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-with-both-optimizers.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-both-optimizers.json @@ -13,34 +13,25 @@ "Ref": "AWS::AccountId" }, "DatabaseName": "my_database", - "TableInput": { - "Description": { - "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" - }, - "Name": "my_table", - "Parameters": { - "EXTERNAL": "TRUE" - }, - "PartitionKeys": [], - "TableType": "EXTERNAL_TABLE", - "StorageDescriptor": { - "Columns": [ - { - "Name": "column", - "Type": "string" - } - ], - "Compressed": false, - "Location": "s3://fake/location", - "NumberOfBuckets": 0, - "SerdeInfo": {}, - "StoredAsSubDirectories": true - } - }, + "Name": "my_table", "OpenTableFormatInput": { "IcebergInput": { "MetadataOperation": "CREATE", - "Version": "2" + "Version": "2", + "IcebergTableInput": { + "Location": "s3://fake/location", + "Schema": { + "Type": "struct", + "Fields": [ + { + "Name": "column", + "Type": "string", + "Id": 1, + "Required": true + } + ] + } + } } } } diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-custom.json b/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-custom.json index c4ffe91..183a0c3 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-custom.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-custom.json @@ -19,34 +19,25 @@ "Ref": "AWS::AccountId" }, "DatabaseName": "my_database", - "TableInput": { - "Description": { - "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" - }, - "Name": "my_table", - "Parameters": { - "EXTERNAL": "TRUE" - }, - "PartitionKeys": [], - "TableType": "EXTERNAL_TABLE", - "StorageDescriptor": { - "Columns": [ - { - "Name": "column", - "Type": "string" - } - ], - "Compressed": false, - "Location": "s3://fake/location", - "NumberOfBuckets": 0, - "SerdeInfo": {}, - "StoredAsSubDirectories": true - } - }, + "Name": "my_table", "OpenTableFormatInput": { "IcebergInput": { "MetadataOperation": "CREATE", - "Version": "2" + "Version": "2", + "IcebergTableInput": { + "Location": "s3://fake/location", + "Schema": { + "Type": "struct", + "Fields": [ + { + "Name": "column", + "Type": "string", + "Id": 1, + "Required": true + } + ] + } + } } } } diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-defaults.json b/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-defaults.json index 9948d48..eadabf9 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-defaults.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-defaults.json @@ -13,34 +13,25 @@ "Ref": "AWS::AccountId" }, "DatabaseName": "my_database", - "TableInput": { - "Description": { - "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" - }, - "Name": "my_table", - "Parameters": { - "EXTERNAL": "TRUE" - }, - "PartitionKeys": [], - "TableType": "EXTERNAL_TABLE", - "StorageDescriptor": { - "Columns": [ - { - "Name": "column", - "Type": "string" - } - ], - "Compressed": false, - "Location": "s3://fake/location", - "NumberOfBuckets": 0, - "SerdeInfo": {}, - "StoredAsSubDirectories": true - } - }, + "Name": "my_table", "OpenTableFormatInput": { "IcebergInput": { "MetadataOperation": "CREATE", - "Version": "2" + "Version": "2", + "IcebergTableInput": { + "Location": "s3://fake/location", + "Schema": { + "Type": "struct", + "Fields": [ + { + "Name": "column", + "Type": "string", + "Id": 1, + "Required": true + } + ] + } + } } } } diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json b/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json index cc3ed38..10415bf 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json @@ -27,34 +27,25 @@ "Ref": "AWS::AccountId" }, "DatabaseName": "my_database", - "TableInput": { - "Description": { - "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" - }, - "Name": "my_table", - "Parameters": { - "EXTERNAL": "TRUE" - }, - "PartitionKeys": [], - "TableType": "EXTERNAL_TABLE", - "StorageDescriptor": { - "Columns": [ - { - "Name": "column", - "Type": "string" - } - ], - "Compressed": false, - "Location": "s3://fake/location", - "NumberOfBuckets": 0, - "SerdeInfo": {}, - "StoredAsSubDirectories": true - } - }, + "Name": "my_table", "OpenTableFormatInput": { "IcebergInput": { "MetadataOperation": "CREATE", - "Version": "2" + "Version": "2", + "IcebergTableInput": { + "Location": "s3://fake/location", + "Schema": { + "Type": "struct", + "Fields": [ + { + "Name": "column", + "Type": "string", + "Id": 1, + "Required": true + } + ] + } + } } } } diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-defaults.json b/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-defaults.json index 742cb3f..5c0d669 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-defaults.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-defaults.json @@ -13,34 +13,25 @@ "Ref": "AWS::AccountId" }, "DatabaseName": "my_database", - "TableInput": { - "Description": { - "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" - }, - "Name": "my_table", - "Parameters": { - "EXTERNAL": "TRUE" - }, - "PartitionKeys": [], - "TableType": "EXTERNAL_TABLE", - "StorageDescriptor": { - "Columns": [ - { - "Name": "column", - "Type": "string" - } - ], - "Compressed": false, - "Location": "s3://fake/location", - "NumberOfBuckets": 0, - "SerdeInfo": {}, - "StoredAsSubDirectories": true - } - }, + "Name": "my_table", "OpenTableFormatInput": { "IcebergInput": { "MetadataOperation": "CREATE", - "Version": "2" + "Version": "2", + "IcebergTableInput": { + "Location": "s3://fake/location", + "Schema": { + "Type": "struct", + "Fields": [ + { + "Name": "column", + "Type": "string", + "Id": 1, + "Required": true + } + ] + } + } } } } diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-custom.json b/test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-custom.json index dd776ab..721859c 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-custom.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-custom.json @@ -19,34 +19,25 @@ "Ref": "AWS::AccountId" }, "DatabaseName": "my_database", - "TableInput": { - "Description": { - "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" - }, - "Name": "my_table", - "Parameters": { - "EXTERNAL": "TRUE" - }, - "PartitionKeys": [], - "TableType": "EXTERNAL_TABLE", - "StorageDescriptor": { - "Columns": [ - { - "Name": "column", - "Type": "string" - } - ], - "Compressed": false, - "Location": "s3://fake/location", - "NumberOfBuckets": 0, - "SerdeInfo": {}, - "StoredAsSubDirectories": true - } - }, + "Name": "my_table", "OpenTableFormatInput": { "IcebergInput": { "MetadataOperation": "CREATE", - "Version": "2" + "Version": "2", + "IcebergTableInput": { + "Location": "s3://fake/location", + "Schema": { + "Type": "struct", + "Fields": [ + { + "Name": "column", + "Type": "string", + "Id": 1, + "Required": true + } + ] + } + } } } } diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-defaults.json b/test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-defaults.json index e710c29..d02f089 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-defaults.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-defaults.json @@ -13,34 +13,25 @@ "Ref": "AWS::AccountId" }, "DatabaseName": "my_database", - "TableInput": { - "Description": { - "Fn::Sub": "Created by the ${AWS::StackName} CloudFormation stack" - }, - "Name": "my_table", - "Parameters": { - "EXTERNAL": "TRUE" - }, - "PartitionKeys": [], - "TableType": "EXTERNAL_TABLE", - "StorageDescriptor": { - "Columns": [ - { - "Name": "column", - "Type": "string" - } - ], - "Compressed": false, - "Location": "s3://fake/location", - "NumberOfBuckets": 0, - "SerdeInfo": {}, - "StoredAsSubDirectories": true - } - }, + "Name": "my_table", "OpenTableFormatInput": { "IcebergInput": { "MetadataOperation": "CREATE", - "Version": "2" + "Version": "2", + "IcebergTableInput": { + "Location": "s3://fake/location", + "Schema": { + "Type": "struct", + "Fields": [ + { + "Name": "column", + "Type": "string", + "Id": 1, + "Required": true + } + ] + } + } } } } diff --git a/test/shortcuts.test.js b/test/shortcuts.test.js index f3b3c2a..f3616f9 100644 --- a/test/shortcuts.test.js +++ b/test/shortcuts.test.js @@ -16,8 +16,13 @@ const noUndefined = (template) => JSON.parse(JSON.stringify(template)); test('[shortcuts] fixture validation', async (assert) => { // Runs cfn-lint, ignoring "warnings". Install via pip or Homebrew to run these // tests locally. - const cfnLint = (filepath) => new Promise((resolve, reject) => { - cp.exec(`cfn-lint ${filepath} --ignore-checks W`, (err, stdout) => { + const cfnLint = (filepath, filename) => new Promise((resolve, reject) => { + // Ignore E3003 (missing TableInput) and E3002 (unexpected properties) for Iceberg tables only + // cfn-lint doesn't yet support OpenTableFormatInput (Iceberg table format) + const isIcebergTable = filename.includes('glue-iceberg-table'); + const ignoreChecks = isIcebergTable ? 'W,E3003,E3002' : 'W'; + + cp.exec(`cfn-lint ${filepath} --ignore-checks ${ignoreChecks}`, (err, stdout) => { if (err) return reject(new Error(stdout)); return resolve(); }); @@ -30,7 +35,7 @@ test('[shortcuts] fixture validation', async (assert) => { while (toValidate.length) { const filename = toValidate.shift(); await Promise.all([ - cfnLint(path.join(__dirname, 'fixtures', 'shortcuts', filename)) + cfnLint(path.join(__dirname, 'fixtures', 'shortcuts', filename), filename) .then(() => assert.pass(`${filename} fixture passed validation`)) .catch((err) => { assert.fail(`${filename} fixture fails validation`); @@ -1442,7 +1447,7 @@ test('[shortcuts] glue iceberg table', (assert) => { ); assert.throws( () => new cf.shortcuts.GlueIcebergTable({}), - /You must provide a Location/, + /You must provide a LogicalName, Name, DatabaseName, and Location/, 'throws without required parameters' ); From bc552e348d58102e1b7205e6d340f3adfa791b9f Mon Sep 17 00:00:00 2001 From: majacob Date: Fri, 23 Jan 2026 10:40:32 -0500 Subject: [PATCH 08/14] update version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 96910ec..606198c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@mapbox/cloudfriend", - "version": "9.1.1", + "version": "9.1.1-0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@mapbox/cloudfriend", - "version": "9.1.1", + "version": "9.1.1-0", "license": "ISC", "dependencies": { "@aws-sdk/client-cloudformation": "^3.848.0", diff --git a/package.json b/package.json index fab84dc..e52269c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mapbox/cloudfriend", - "version": "9.1.1", + "version": "9.1.1-0", "description": "Helper functions for assembling CloudFormation templates in JavaScript", "main": "index.js", "engines": { From 515ffe23b45fb763e780c98460fd1bc143c84b63 Mon Sep 17 00:00:00 2001 From: majacob Date: Tue, 27 Jan 2026 16:52:19 -0500 Subject: [PATCH 09/14] remove columns, just preserve schema --- lib/shortcuts/api.md | 3 +- lib/shortcuts/glue-iceberg-table.js | 54 ++--------- package-lock.json | 4 +- package.json | 2 +- test/shortcuts.test.js | 133 +++++++++++++++++----------- 5 files changed, 93 insertions(+), 103 deletions(-) diff --git a/lib/shortcuts/api.md b/lib/shortcuts/api.md index 6ccf2a2..859fe18 100644 --- a/lib/shortcuts/api.md +++ b/lib/shortcuts/api.md @@ -222,8 +222,7 @@ Create a Glue table backed by Apache Iceberg format on S3. | options.Name | String | | The name of the table. | | options.DatabaseName | String | | The name of the database the table resides in. | | options.Location | String | | The physical location of the table (S3 URI). Required. | -| [options.Columns] | Array.<Object> | | Simple column definitions as array of {Name, Type, Required}. If provided, will be auto-converted to Iceberg Schema format. Use options.Schema for full control. | -| [options.Schema] | Object | | Full Iceberg schema definition with Type: "struct" and Fields array. Either Columns or Schema must be provided. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-icebergtableinput.html). | +| options.Schema | Object | | Full Iceberg schema definition with Type: "struct" and Fields array. Each field must have Id (integer), Name (string), Type (string or object for complex types), and Required (boolean). See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-icebergtableinput.html). | | [options.PartitionSpec] | Object | | Iceberg partition specification. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-partitionspec.html). | | [options.WriteOrder] | Object | | Iceberg write order specification. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-writeorder.html). | | [options.CatalogId] | String | AccountId | The AWS account ID for the account in which to create the table. | diff --git a/lib/shortcuts/glue-iceberg-table.js b/lib/shortcuts/glue-iceberg-table.js index 48066cf..4fd133e 100644 --- a/lib/shortcuts/glue-iceberg-table.js +++ b/lib/shortcuts/glue-iceberg-table.js @@ -8,10 +8,9 @@ * @param {String} options.Name - The name of the table. * @param {String} options.DatabaseName - The name of the database the table resides in. * @param {String} options.Location - The physical location of the table (S3 URI). Required. - * @param {Array} [options.Columns] - Simple column definitions as array of {Name, Type, Required}. - * If provided, will be auto-converted to Iceberg Schema format. Use options.Schema for full control. - * @param {Object} [options.Schema] - Full Iceberg schema definition with Type: "struct" and Fields array. - * Either Columns or Schema must be provided. See [AWS + * @param {Object} options.Schema - Full Iceberg schema definition with Type: "struct" and Fields array. + * Each field must have Id (integer), Name (string), Type (string or object for complex types), and Required (boolean). + * See [AWS * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-icebergtableinput.html). * @param {Object} [options.PartitionSpec] - Iceberg partition specification. See [AWS * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-partitionspec.html). @@ -30,8 +29,6 @@ * @param {String} [options.OrphanFileDeletionRoleArn=undefined] - The ARN of the IAM role for the orphan file deletion optimizer. Required if EnableOrphanFileDeletion is true. * @param {Number} [options.OrphanFileRetentionPeriodInDays=3] - The number of days to retain orphan files before deleting them. * @param {String} [options.OrphanFileDeletionLocation=undefined] - The S3 location to scan for orphan files. - * @param {String} [options.Condition=undefined] - CloudFormation condition name. - * @param {String} [options.DependsOn=undefined] - CloudFormation resource dependency. */ class GlueIcebergTable { constructor(options) { @@ -41,7 +38,6 @@ class GlueIcebergTable { Name, DatabaseName, Location, - Columns, Schema, PartitionSpec, WriteOrder, @@ -57,18 +53,13 @@ class GlueIcebergTable { EnableOrphanFileDeletion = false, OrphanFileDeletionRoleArn, OrphanFileRetentionPeriodInDays = 3, - OrphanFileDeletionLocation, - Condition, - DependsOn + OrphanFileDeletionLocation } = options; // Validate required fields - const required = [LogicalName, Name, DatabaseName, Location]; + const required = [LogicalName, Name, DatabaseName, Location, Schema]; if (required.some((variable) => !variable)) - throw new Error('You must provide a LogicalName, Name, DatabaseName, and Location'); - - if (!Columns && !Schema) - throw new Error('You must provide either Columns or Schema'); + throw new Error('You must provide a LogicalName, Name, DatabaseName, Location, and Schema'); if (EnableOptimizer && !OptimizerRoleArn) throw new Error('You must provide an OptimizerRoleArn when EnableOptimizer is true'); @@ -79,26 +70,10 @@ class GlueIcebergTable { if (EnableOrphanFileDeletion && !OrphanFileDeletionRoleArn) throw new Error('You must provide an OrphanFileDeletionRoleArn when EnableOrphanFileDeletion is true'); - // Convert simple Columns format to Iceberg Schema format if needed - let icebergSchema = Schema; - if (!Schema && Columns) { - icebergSchema = { - Type: 'struct', - Fields: Columns.map((col, index) => ({ - Name: col.Name, - Type: col.Type, - Id: index + 1, - Required: col.Required !== undefined ? col.Required : true - })) - }; - } - // Build the Iceberg table resource (no TableInput!) this.Resources = { [LogicalName]: { Type: 'AWS::Glue::Table', - Condition, - DependsOn, Properties: { CatalogId, DatabaseName, @@ -109,7 +84,7 @@ class GlueIcebergTable { Version: IcebergVersion, IcebergTableInput: { Location, - Schema: icebergSchema + Schema } } } @@ -151,11 +126,6 @@ class GlueIcebergTable { } } }; - - // Apply Condition to optimizer if specified on the table - if (Condition) { - this.Resources[optimizerLogicalName].Condition = Condition; - } } // Optionally add TableOptimizer for compaction @@ -179,11 +149,6 @@ class GlueIcebergTable { } } }; - - // Apply Condition to compaction optimizer if specified on the table - if (Condition) { - this.Resources[compactionLogicalName].Condition = Condition; - } } // Optionally add TableOptimizer for orphan file deletion @@ -215,11 +180,6 @@ class GlueIcebergTable { } } }; - - // Apply Condition to orphan file deletion optimizer if specified on the table - if (Condition) { - this.Resources[orphanLogicalName].Condition = Condition; - } } } } diff --git a/package-lock.json b/package-lock.json index 606198c..4e30c7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@mapbox/cloudfriend", - "version": "9.1.1-0", + "version": "9.1.1-1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@mapbox/cloudfriend", - "version": "9.1.1-0", + "version": "9.1.1-1", "license": "ISC", "dependencies": { "@aws-sdk/client-cloudformation": "^3.848.0", diff --git a/package.json b/package.json index e52269c..db00052 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mapbox/cloudfriend", - "version": "9.1.1-0", + "version": "9.1.1-1", "description": "Helper functions for assembling CloudFormation templates in JavaScript", "main": "index.js", "engines": { diff --git a/test/shortcuts.test.js b/test/shortcuts.test.js index f3616f9..b593fe3 100644 --- a/test/shortcuts.test.js +++ b/test/shortcuts.test.js @@ -1447,7 +1447,7 @@ test('[shortcuts] glue iceberg table', (assert) => { ); assert.throws( () => new cf.shortcuts.GlueIcebergTable({}), - /You must provide a LogicalName, Name, DatabaseName, and Location/, + /You must provide a LogicalName, Name, DatabaseName, Location, and Schema/, 'throws without required parameters' ); @@ -1455,9 +1455,12 @@ test('[shortcuts] glue iceberg table', (assert) => { LogicalName: 'MyTable', DatabaseName: 'my_database', Name: 'my_table', - Columns: [ - { Name: 'column', Type: 'string' } - ], + Schema: { + Type: 'struct', + Fields: [ + { Name: 'column', Type: 'string', Id: 1, Required: true } + ] + }, Location: 's3://fake/location' }); @@ -1473,25 +1476,22 @@ test('[shortcuts] glue iceberg table', (assert) => { LogicalName: 'MyTable', DatabaseName: 'my_database', Name: 'my_table', - Columns: [ - { Name: 'column', Type: 'string' } - ], + Schema: { + Type: 'struct', + Fields: [ + { Name: 'column', Type: 'string', Id: 1, Required: true } + ] + }, CatalogId: '1234', Owner: 'Team', Parameters: { table: 'params' }, Description: 'my_table description', Retention: 12, Location: 's3://fake/location', - IcebergVersion: '2', - Condition: 'Always', - DependsOn: 'AnotherThing' + IcebergVersion: '2' }); - template = cf.merge( - { Conditions: { Always: cf.equals('1', '1') } }, - { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } }, - db - ); + template = cf.merge(db); if (update) fixtures.update('glue-iceberg-table-no-defaults', template); assert.deepEqual( noUndefined(template), @@ -1504,9 +1504,12 @@ test('[shortcuts] glue iceberg table', (assert) => { LogicalName: 'MyTable', DatabaseName: 'my_database', Name: 'my_table', - Columns: [ - { Name: 'column', Type: 'string' } - ], + Schema: { + Type: 'struct', + Fields: [ + { Name: 'column', Type: 'string', Id: 1, Required: true } + ] + }, Location: 's3://fake/location', EnableOptimizer: true }), @@ -1518,9 +1521,12 @@ test('[shortcuts] glue iceberg table', (assert) => { LogicalName: 'MyTable', DatabaseName: 'my_database', Name: 'my_table', - Columns: [ - { Name: 'column', Type: 'string' } - ], + Schema: { + Type: 'struct', + Fields: [ + { Name: 'column', Type: 'string', Id: 1, Required: true } + ] + }, Location: 's3://fake/location', EnableOptimizer: true, OptimizerRoleArn: 'arn:aws:iam::123456789012:role/OptimizerRole' @@ -1538,20 +1544,21 @@ test('[shortcuts] glue iceberg table', (assert) => { LogicalName: 'MyTable', DatabaseName: 'my_database', Name: 'my_table', - Columns: [ - { Name: 'column', Type: 'string' } - ], + Schema: { + Type: 'struct', + Fields: [ + { Name: 'column', Type: 'string', Id: 1, Required: true } + ] + }, Location: 's3://fake/location', EnableOptimizer: true, OptimizerRoleArn: cf.getAtt('OptimizerRole', 'Arn'), SnapshotRetentionPeriodInDays: 7, NumberOfSnapshotsToRetain: 3, - CleanExpiredFiles: false, - Condition: 'Always' + CleanExpiredFiles: false }); template = cf.merge( - { Conditions: { Always: cf.equals('1', '1') } }, { Resources: { OptimizerRole: { Type: 'AWS::IAM::Role', Properties: { AssumeRolePolicyDocument: {} } } } }, db ); @@ -1567,9 +1574,12 @@ test('[shortcuts] glue iceberg table', (assert) => { LogicalName: 'MyTable', DatabaseName: 'my_database', Name: 'my_table', - Columns: [ - { Name: 'column', Type: 'string' } - ], + Schema: { + Type: 'struct', + Fields: [ + { Name: 'column', Type: 'string', Id: 1, Required: true } + ] + }, Location: 's3://fake/location', EnableCompaction: true }), @@ -1581,9 +1591,12 @@ test('[shortcuts] glue iceberg table', (assert) => { LogicalName: 'MyTable', DatabaseName: 'my_database', Name: 'my_table', - Columns: [ - { Name: 'column', Type: 'string' } - ], + Schema: { + Type: 'struct', + Fields: [ + { Name: 'column', Type: 'string', Id: 1, Required: true } + ] + }, Location: 's3://fake/location', EnableCompaction: true, CompactionRoleArn: 'arn:aws:iam::123456789012:role/CompactionRole' @@ -1601,9 +1614,12 @@ test('[shortcuts] glue iceberg table', (assert) => { LogicalName: 'MyTable', DatabaseName: 'my_database', Name: 'my_table', - Columns: [ - { Name: 'column', Type: 'string' } - ], + Schema: { + Type: 'struct', + Fields: [ + { Name: 'column', Type: 'string', Id: 1, Required: true } + ] + }, Location: 's3://fake/location', EnableCompaction: true, CompactionRoleArn: cf.getAtt('CompactionRole', 'Arn') @@ -1624,9 +1640,12 @@ test('[shortcuts] glue iceberg table', (assert) => { LogicalName: 'MyTable', DatabaseName: 'my_database', Name: 'my_table', - Columns: [ - { Name: 'column', Type: 'string' } - ], + Schema: { + Type: 'struct', + Fields: [ + { Name: 'column', Type: 'string', Id: 1, Required: true } + ] + }, Location: 's3://fake/location', EnableOptimizer: true, OptimizerRoleArn: 'arn:aws:iam::123456789012:role/RetentionRole', @@ -1647,9 +1666,12 @@ test('[shortcuts] glue iceberg table', (assert) => { LogicalName: 'MyTable', DatabaseName: 'my_database', Name: 'my_table', - Columns: [ - { Name: 'column', Type: 'string' } - ], + Schema: { + Type: 'struct', + Fields: [ + { Name: 'column', Type: 'string', Id: 1, Required: true } + ] + }, Location: 's3://fake/location', EnableOrphanFileDeletion: true }), @@ -1661,9 +1683,12 @@ test('[shortcuts] glue iceberg table', (assert) => { LogicalName: 'MyTable', DatabaseName: 'my_database', Name: 'my_table', - Columns: [ - { Name: 'column', Type: 'string' } - ], + Schema: { + Type: 'struct', + Fields: [ + { Name: 'column', Type: 'string', Id: 1, Required: true } + ] + }, Location: 's3://fake/location', EnableOrphanFileDeletion: true, OrphanFileDeletionRoleArn: 'arn:aws:iam::123456789012:role/OrphanFileDeletionRole' @@ -1681,9 +1706,12 @@ test('[shortcuts] glue iceberg table', (assert) => { LogicalName: 'MyTable', DatabaseName: 'my_database', Name: 'my_table', - Columns: [ - { Name: 'column', Type: 'string' } - ], + Schema: { + Type: 'struct', + Fields: [ + { Name: 'column', Type: 'string', Id: 1, Required: true } + ] + }, Location: 's3://fake/location', EnableOrphanFileDeletion: true, OrphanFileDeletionRoleArn: cf.getAtt('OrphanFileDeletionRole', 'Arn'), @@ -1706,9 +1734,12 @@ test('[shortcuts] glue iceberg table', (assert) => { LogicalName: 'MyTable', DatabaseName: 'my_database', Name: 'my_table', - Columns: [ - { Name: 'column', Type: 'string' } - ], + Schema: { + Type: 'struct', + Fields: [ + { Name: 'column', Type: 'string', Id: 1, Required: true } + ] + }, Location: 's3://fake/location', EnableOptimizer: true, OptimizerRoleArn: 'arn:aws:iam::123456789012:role/SharedRole', From 57a3e3667b3af890e8d68d137328268aa9a05372 Mon Sep 17 00:00:00 2001 From: majacob Date: Tue, 27 Jan 2026 18:00:47 -0500 Subject: [PATCH 10/14] update version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4e30c7e..454a309 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@mapbox/cloudfriend", - "version": "9.1.1-1", + "version": "9.1.1-2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@mapbox/cloudfriend", - "version": "9.1.1-1", + "version": "9.1.1-2", "license": "ISC", "dependencies": { "@aws-sdk/client-cloudformation": "^3.848.0", diff --git a/package.json b/package.json index db00052..0dc3320 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mapbox/cloudfriend", - "version": "9.1.1-1", + "version": "9.1.1-2", "description": "Helper functions for assembling CloudFormation templates in JavaScript", "main": "index.js", "engines": { From d6dd4e25798e1e9b0d277c876cd782f4d1e3fad2 Mon Sep 17 00:00:00 2001 From: majacob Date: Wed, 28 Jan 2026 09:52:58 -0500 Subject: [PATCH 11/14] test fixes --- lib/shortcuts/api.md | 2 -- .../shortcuts/glue-iceberg-table-no-defaults.json | 14 +------------- .../glue-iceberg-table-with-optimizer-custom.json | 13 ++----------- 3 files changed, 3 insertions(+), 26 deletions(-) diff --git a/lib/shortcuts/api.md b/lib/shortcuts/api.md index 859fe18..58a50e9 100644 --- a/lib/shortcuts/api.md +++ b/lib/shortcuts/api.md @@ -238,8 +238,6 @@ Create a Glue table backed by Apache Iceberg format on S3. | [options.OrphanFileDeletionRoleArn] | String | | The ARN of the IAM role for the orphan file deletion optimizer. Required if EnableOrphanFileDeletion is true. | | [options.OrphanFileRetentionPeriodInDays] | Number | 3 | The number of days to retain orphan files before deleting them. | | [options.OrphanFileDeletionLocation] | String | | The S3 location to scan for orphan files. | -| [options.Condition] | String | | CloudFormation condition name. | -| [options.DependsOn] | String | | CloudFormation resource dependency. | diff --git a/test/fixtures/shortcuts/glue-iceberg-table-no-defaults.json b/test/fixtures/shortcuts/glue-iceberg-table-no-defaults.json index 7cb90a5..ce6b1c7 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-no-defaults.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-no-defaults.json @@ -4,22 +4,10 @@ "Parameters": {}, "Rules": {}, "Mappings": {}, - "Conditions": { - "Always": { - "Fn::Equals": [ - "1", - "1" - ] - } - }, + "Conditions": {}, "Resources": { - "AnotherThing": { - "Type": "AWS::SNS::Topic" - }, "MyTable": { "Type": "AWS::Glue::Table", - "Condition": "Always", - "DependsOn": "AnotherThing", "Properties": { "CatalogId": "1234", "DatabaseName": "my_database", diff --git a/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json b/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json index 10415bf..66dc06b 100644 --- a/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json +++ b/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json @@ -4,14 +4,7 @@ "Parameters": {}, "Rules": {}, "Mappings": {}, - "Conditions": { - "Always": { - "Fn::Equals": [ - "1", - "1" - ] - } - }, + "Conditions": {}, "Resources": { "OptimizerRole": { "Type": "AWS::IAM::Role", @@ -21,7 +14,6 @@ }, "MyTable": { "Type": "AWS::Glue::Table", - "Condition": "Always", "Properties": { "CatalogId": { "Ref": "AWS::AccountId" @@ -76,8 +68,7 @@ } } } - }, - "Condition": "Always" + } } }, "Outputs": {} From 78903e4de7a67cbce649d54c4efd2639959e247a Mon Sep 17 00:00:00 2001 From: majacob Date: Wed, 28 Jan 2026 15:18:34 -0500 Subject: [PATCH 12/14] fix links --- lib/shortcuts/glue-iceberg-table.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/shortcuts/glue-iceberg-table.js b/lib/shortcuts/glue-iceberg-table.js index 4fd133e..bf80bb9 100644 --- a/lib/shortcuts/glue-iceberg-table.js +++ b/lib/shortcuts/glue-iceberg-table.js @@ -11,11 +11,11 @@ * @param {Object} options.Schema - Full Iceberg schema definition with Type: "struct" and Fields array. * Each field must have Id (integer), Name (string), Type (string or object for complex types), and Required (boolean). * See [AWS - * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-icebergtableinput.html). + * documentation](https://docs.aws.amazon.com/glue/latest/webapi/API_IcebergSchema.html). * @param {Object} [options.PartitionSpec] - Iceberg partition specification. See [AWS - * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-partitionspec.html). + * documentation](https://docs.aws.amazon.com/glue/latest/webapi/API_IcebergPartitionSpec.html). * @param {Object} [options.WriteOrder] - Iceberg write order specification. See [AWS - * documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-writeorder.html). + * documentation](https://docs.aws.amazon.com/glue/latest/webapi/API_IcebergSortOrder.html). * @param {String} [options.CatalogId=AccountId] - The AWS account ID for the account in which to create the table. * @param {String} [options.IcebergVersion='2'] - The table version for the Iceberg table. * @param {Boolean} [options.EnableOptimizer=false] - Whether to enable the snapshot retention optimizer. From 2ddc1655ce83f741677eabc442f31fc146cf0869 Mon Sep 17 00:00:00 2001 From: majacob Date: Wed, 28 Jan 2026 15:21:55 -0500 Subject: [PATCH 13/14] links --- lib/shortcuts/api.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/shortcuts/api.md b/lib/shortcuts/api.md index 58a50e9..967ba45 100644 --- a/lib/shortcuts/api.md +++ b/lib/shortcuts/api.md @@ -222,9 +222,9 @@ Create a Glue table backed by Apache Iceberg format on S3. | options.Name | String | | The name of the table. | | options.DatabaseName | String | | The name of the database the table resides in. | | options.Location | String | | The physical location of the table (S3 URI). Required. | -| options.Schema | Object | | Full Iceberg schema definition with Type: "struct" and Fields array. Each field must have Id (integer), Name (string), Type (string or object for complex types), and Required (boolean). See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-icebergtableinput.html). | -| [options.PartitionSpec] | Object | | Iceberg partition specification. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-partitionspec.html). | -| [options.WriteOrder] | Object | | Iceberg write order specification. See [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-writeorder.html). | +| options.Schema | Object | | Full Iceberg schema definition with Type: "struct" and Fields array. Each field must have Id (integer), Name (string), Type (string or object for complex types), and Required (boolean). See [AWS documentation](https://docs.aws.amazon.com/glue/latest/webapi/API_IcebergSchema.html). | +| [options.PartitionSpec] | Object | | Iceberg partition specification. See [AWS documentation](https://docs.aws.amazon.com/glue/latest/webapi/API_IcebergPartitionSpec.html). | +| [options.WriteOrder] | Object | | Iceberg write order specification. See [AWS documentation](https://docs.aws.amazon.com/glue/latest/webapi/API_IcebergSortOrder.html). | | [options.CatalogId] | String | AccountId | The AWS account ID for the account in which to create the table. | | [options.IcebergVersion] | String | '2' | The table version for the Iceberg table. | | [options.EnableOptimizer] | Boolean | false | Whether to enable the snapshot retention optimizer. | From 738358997fe3560da6a763f450975cb289b102db Mon Sep 17 00:00:00 2001 From: Marie Jacob Rajan Date: Thu, 29 Jan 2026 14:49:20 -0500 Subject: [PATCH 14/14] Potential fix for code scanning alert no. 4: Shell command built from environment values Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- test/shortcuts.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/shortcuts.test.js b/test/shortcuts.test.js index b593fe3..e0064d9 100644 --- a/test/shortcuts.test.js +++ b/test/shortcuts.test.js @@ -22,7 +22,7 @@ test('[shortcuts] fixture validation', async (assert) => { const isIcebergTable = filename.includes('glue-iceberg-table'); const ignoreChecks = isIcebergTable ? 'W,E3003,E3002' : 'W'; - cp.exec(`cfn-lint ${filepath} --ignore-checks ${ignoreChecks}`, (err, stdout) => { + cp.execFile('cfn-lint', [filepath, '--ignore-checks', ignoreChecks], (err, stdout) => { if (err) return reject(new Error(stdout)); return resolve(); });