@adobe/aio-cli-plugin-app-storage 1.0.3 → 1.2.0-pre.2025-11-18.sha-9c4079ac

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +676 -12
  2. package/package.json +33 -6
  3. package/src/BaseCommand.js +11 -58
  4. package/src/DBBaseCommand.js +112 -0
  5. package/src/StateBaseCommand.js +87 -0
  6. package/src/commands/app/add/db.js +20 -0
  7. package/src/commands/app/db/collection/create.js +119 -0
  8. package/src/commands/app/db/collection/drop.js +90 -0
  9. package/src/commands/app/db/collection/list.js +100 -0
  10. package/src/commands/app/db/collection/rename.js +98 -0
  11. package/src/commands/app/db/collection/stats.js +94 -0
  12. package/src/commands/app/db/delete.js +89 -0
  13. package/src/commands/app/db/document/count.js +96 -0
  14. package/src/commands/app/db/document/delete.js +95 -0
  15. package/src/commands/app/db/document/find.js +133 -0
  16. package/src/commands/app/db/document/insert.js +147 -0
  17. package/src/commands/app/db/document/replace.js +122 -0
  18. package/src/commands/app/db/document/update.js +144 -0
  19. package/src/commands/app/db/index/create.js +170 -0
  20. package/src/commands/app/db/index/drop.js +87 -0
  21. package/src/commands/app/db/index/list.js +82 -0
  22. package/src/commands/app/db/ping.js +77 -0
  23. package/src/commands/app/db/provision.js +190 -0
  24. package/src/commands/app/db/stats.js +87 -0
  25. package/src/commands/app/db/status.js +159 -0
  26. package/src/commands/app/state/delete.js +3 -3
  27. package/src/commands/app/state/get.js +2 -2
  28. package/src/commands/app/state/list.js +3 -3
  29. package/src/commands/app/state/put.js +4 -4
  30. package/src/commands/app/state/stats.js +2 -2
  31. package/src/constants/db.js +32 -0
  32. package/src/constants/global.js +14 -0
  33. package/src/{constants.js → constants/state.js} +3 -0
  34. package/src/utils/inputValidation.js +74 -0
  35. package/src/utils/output.js +35 -0
  36. package/oclif.manifest.json +0 -306
@@ -0,0 +1,170 @@
1
+ /*
2
+ Copyright 2025 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { DBBaseCommand } from '../../../../DBBaseCommand.js'
14
+ import { Args, Flags } from '@oclif/core'
15
+ import chalk from 'chalk'
16
+ import { asObject, isNonEmptyString } from '../../../../utils/inputValidation.js'
17
+ import { prettyJson } from '../../../../utils/output.js'
18
+
19
+ // Regular expression to match the -s/--spec or -k/--key flags in <flag>=<value> format
20
+ // if match.groups.spec is defined, it means the flag was -s or --spec
21
+ // if match.groups.key is defined, it means the flag was -k or --key
22
+ const specFlagMatch = /^((?<spec>-s|--spec)|(?<key>-k|--key))=(?<val>.+)/
23
+
24
+ export class Create extends DBBaseCommand {
25
+ getOrderedSpecs () {
26
+ // Key/spec order matters when creating an index and both key and spec can be specified,
27
+ // so we need to parse argv to obtain the proper order since using this.flags loses the order between the two
28
+ const args = this.argv.slice(1) // Ignore the first element (collection name)
29
+ const fullSpec = []
30
+ let specOption = false
31
+ args.forEach((arg) => {
32
+ if (specOption === 'key') {
33
+ // Previous arg was -k or --key
34
+ fullSpec.push(arg)
35
+ specOption = false
36
+ } else if (specOption === 'spec') {
37
+ // Previous arg was -s or --spec
38
+ fullSpec.push(asObject(arg))
39
+ specOption = false
40
+ } else if (arg === '-k' || arg === '--key') {
41
+ // Next arg is a key
42
+ specOption = 'key'
43
+ } else if (arg === '-s' || arg === '--spec') {
44
+ // Next arg is a spec
45
+ specOption = 'spec'
46
+ } else {
47
+ // Check if the arg is in the form of -s=<val>/--spec=<val> or -k=<val>/--key=<val>
48
+ const specMatch = arg.match(specFlagMatch)
49
+ if (specMatch?.groups?.spec) {
50
+ // Spec is a JSON object
51
+ fullSpec.push(asObject(specMatch.groups.val))
52
+ } else if (specMatch?.groups?.key) {
53
+ // Key is a string
54
+ fullSpec.push(specMatch.groups.val)
55
+ }
56
+ specOption = false
57
+ }
58
+ })
59
+
60
+ return fullSpec
61
+ }
62
+
63
+ async run () {
64
+ const { collection } = this.args
65
+ const { name, unique } = this.flags
66
+
67
+ try {
68
+ const fullSpec = this.getOrderedSpecs()
69
+ const prettySpec = prettyJson(fullSpec)
70
+
71
+ this.log(chalk.blue(`Creating index ${name ? `'${name}' ` : ''}on collection '${collection}'...`))
72
+ this.log(chalk.dim(` Specification:\n${prettySpec}`))
73
+ if (unique) this.log(chalk.dim(` Unique index: ${unique}`))
74
+
75
+ const client = await this.db.connect()
76
+ const coll = await client.collection(collection)
77
+
78
+ // Build options
79
+ const options = {}
80
+ if (name) options.name = name
81
+ if (unique) options.unique = unique
82
+
83
+ const result = await coll.createIndex(fullSpec, options)
84
+
85
+ this.debugLogger?.info?.('Index created successfully:', result)
86
+
87
+ const response = {
88
+ collection,
89
+ indexName: result,
90
+ specification: fullSpec,
91
+ status: 'created',
92
+ namespace: this.rtNamespace,
93
+ timestamp: new Date().toISOString()
94
+ }
95
+
96
+ if (Object.keys(options).length > 0) response.options = options
97
+
98
+ this.log(chalk.green(`Index '${result}' created successfully in the '${collection}' collection`))
99
+ this.log(chalk.dim(` Specification:\n${prettySpec}`))
100
+ if (unique) this.log(chalk.dim(` Unique: ${unique}`))
101
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
102
+ this.log(chalk.dim(` Created: ${new Date().toLocaleString()}`))
103
+
104
+ return response
105
+ } catch (error) {
106
+ this.debugLogger?.error?.('Error creating index:', error)
107
+
108
+ this.log(chalk.red('Failed to create index'))
109
+ this.log(chalk.dim(` Collection: ${collection}`))
110
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
111
+
112
+ this.error(`Failed to create index on collection '${collection}': ${error.message}`)
113
+ }
114
+ }
115
+ }
116
+
117
+ Create.description = 'Create a new index on a collection in the database'
118
+
119
+ Create.examples = [
120
+ '$ aio app db index create users --spec \'{"name":1, "age":-1}\'',
121
+ '$ aio app db index create users -s \'{"name":1, "age":-1}\' --name "name_age_index"',
122
+ '$ aio app db index create students -s \'{"name":1}\' --key grade --unique',
123
+ '$ aio app db index create reviews -k sku -k rating',
124
+ '$ aio app db index create products -s \'{"name":"text", "category":"text"}\' --json',
125
+ '$ aio app db index create books -s \'{"author":1}\' -k year',
126
+ '$ aio app db idx create orders --spec \'{"customerId":1}\' --spec \'{"orderDate":-1}\' --name "customer_order_index" --unique'
127
+ ]
128
+
129
+ Create.args = {
130
+ collection: Args.string({
131
+ name: 'collection',
132
+ description: 'The name of the collection to create the index on',
133
+ required: true,
134
+ parse: input => isNonEmptyString(input, 'Collection name')
135
+ })
136
+ }
137
+
138
+ Create.flags = {
139
+ ...DBBaseCommand.flags,
140
+ spec: Flags.string({
141
+ char: 's',
142
+ helpGroup: 'Requires at least one of the index definition',
143
+ description: 'Index specification as a JSON object (e.g., \'{"name":1, "age":-1}\')',
144
+ multiple: true,
145
+ atLeastOne: ['key', 'spec'],
146
+ parse: input => asObject(input, 'Index specification')
147
+ }),
148
+ key: Flags.string({
149
+ char: 'k',
150
+ helpGroup: 'Requires at least one of the index definition',
151
+ description: 'Index key to use with default specification',
152
+ multiple: true,
153
+ atLeastOne: ['key', 'spec'],
154
+ // Note: untrimmed whitespace input is allowed for index keys
155
+ parse: input => isNonEmptyString(input, 'Index key')
156
+ }),
157
+ name: Flags.string({
158
+ char: 'n',
159
+ description: 'A name that uniquely identifies the index',
160
+ // Note: untrimmed whitespace input is allowed for index names
161
+ parse: input => isNonEmptyString(input, 'Index name')
162
+ }),
163
+ unique: Flags.boolean({
164
+ char: 'u',
165
+ description: 'Creates a unique index so that the collection will not accept insertion or update of documents where the index key value matches an existing value in the index',
166
+ default: false
167
+ })
168
+ }
169
+
170
+ Create.aliases = ['app:db:idx:create']
@@ -0,0 +1,87 @@
1
+ /*
2
+ Copyright 2025 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { DBBaseCommand } from '../../../../DBBaseCommand.js'
14
+ import { Args } from '@oclif/core'
15
+ import chalk from 'chalk'
16
+ import { isNonEmptyString } from '../../../../utils/inputValidation.js'
17
+ import { prettyJson } from '../../../../utils/output.js'
18
+
19
+ export class Drop extends DBBaseCommand {
20
+ async run () {
21
+ const { collection, indexName } = this.args
22
+
23
+ try {
24
+ this.log(chalk.blue(`Dropping index '${indexName}' from collection '${collection}'...`))
25
+
26
+ const client = await this.db.connect()
27
+ const coll = await client.collection(collection)
28
+
29
+ const result = await coll.dropIndex(indexName)
30
+
31
+ this.debugLogger?.info?.('Index dropped successfully:', result)
32
+
33
+ const response = {
34
+ collection,
35
+ indexName,
36
+ status: 'dropped',
37
+ namespace: this.rtNamespace,
38
+ timestamp: new Date().toISOString(),
39
+ result
40
+ }
41
+
42
+ this.log(chalk.green(`Index '${indexName}' dropped successfully`))
43
+ this.log(chalk.dim(` Details:\n${prettyJson(result)}`))
44
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
45
+ this.log(chalk.dim(` Dropped: ${new Date().toLocaleString()}`))
46
+
47
+ return response
48
+ } catch (error) {
49
+ this.debugLogger?.error?.('Error dropping index:', error)
50
+
51
+ this.log(chalk.red('Failed to drop index'))
52
+ this.log(chalk.dim(` Collection: ${collection}`))
53
+ this.log(chalk.dim(` Index: ${indexName}`))
54
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
55
+ this.log(chalk.dim(` Error: ${error.message}`))
56
+
57
+ this.error(`Failed to drop index '${indexName}' from collection '${collection}': ${error.message}`)
58
+ }
59
+ }
60
+ }
61
+
62
+ Drop.description = 'Drop an index from a collection in the database'
63
+
64
+ Drop.examples = [
65
+ '$ aio app db index drop users name_age_index',
66
+ '$ aio app db index drop products category_1 --json',
67
+ '$ aio app db idx drop orders orderDate_index'
68
+ ]
69
+
70
+ Drop.args = {
71
+ collection: Args.string({
72
+ name: 'collection',
73
+ description: 'The name of the collection to drop the index from',
74
+ required: true,
75
+ parse: input => isNonEmptyString(input, 'Collection name')
76
+ }),
77
+ indexName: Args.string({
78
+ name: 'indexName',
79
+ description: 'The name of the index to drop',
80
+ required: true,
81
+ parse: input => isNonEmptyString(input, 'Index name')
82
+ })
83
+ }
84
+
85
+ Drop.flags = DBBaseCommand.flags
86
+
87
+ Drop.aliases = ['app:db:idx:drop']
@@ -0,0 +1,82 @@
1
+ /*
2
+ Copyright 2025 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { DBBaseCommand } from '../../../../DBBaseCommand.js'
14
+ import { Args } from '@oclif/core'
15
+ import chalk from 'chalk'
16
+ import { isNonEmptyString } from '../../../../utils/inputValidation.js'
17
+ import { prettyJson } from '../../../../utils/output.js'
18
+
19
+ export class List extends DBBaseCommand {
20
+ async run () {
21
+ const { collection } = this.args
22
+
23
+ try {
24
+ this.log(chalk.blue(`Getting indexes from collection '${collection}'...`))
25
+
26
+ const client = await this.db.connect()
27
+ const coll = await client.collection(collection)
28
+
29
+ const result = await coll.getIndexes()
30
+
31
+ this.debugLogger?.info?.('Indexes retrieved successfully:', result)
32
+
33
+ const response = {
34
+ collection,
35
+ namespace: this.rtNamespace,
36
+ timestamp: new Date().toISOString(),
37
+ indexes: result
38
+ }
39
+
40
+ this.log(chalk.green('Indexes retrieved successfully'))
41
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
42
+ this.log(chalk.dim(` Retrieved: ${new Date().toLocaleString()}`))
43
+ if (result && Array.isArray(result) && result.length > 0) {
44
+ this.log(chalk.dim(` Indexes:\n${prettyJson(result)}`))
45
+ } else {
46
+ this.log(chalk.dim(' No indexes found for this collection'))
47
+ }
48
+
49
+ return response
50
+ } catch (error) {
51
+ this.debugLogger?.error?.('Error getting indexes:', error)
52
+
53
+ this.log(chalk.red('Failed to retrieve indexes'))
54
+ this.log(chalk.dim(` Collection: ${collection}`))
55
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
56
+ this.log(chalk.dim(` Error: ${error.message}`))
57
+
58
+ this.error(`Failed to retrieve indexes from collection '${collection}': ${error.message}`)
59
+ }
60
+ }
61
+ }
62
+
63
+ List.description = 'Get the list of indexes from a collection in the database'
64
+
65
+ List.examples = [
66
+ '$ aio app db index list users',
67
+ '$ aio app db index list products --json',
68
+ '$ aio app db idx list orders'
69
+ ]
70
+
71
+ List.args = {
72
+ collection: Args.string({
73
+ name: 'collection',
74
+ description: 'The name of the collection to retrieve indexes from',
75
+ required: true,
76
+ parse: input => isNonEmptyString(input, 'Collection name')
77
+ })
78
+ }
79
+
80
+ List.flags = DBBaseCommand.flags
81
+
82
+ List.aliases = ['app:db:idx:list']
@@ -0,0 +1,77 @@
1
+ /*
2
+ Copyright 2025 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { DBBaseCommand } from '../../../DBBaseCommand.js'
14
+ import chalk from 'chalk'
15
+
16
+ export class Ping extends DBBaseCommand {
17
+ async run () {
18
+ try {
19
+ this.log(chalk.blue('Testing database connectivity...'))
20
+
21
+ const startTime = Date.now()
22
+ const pingResult = await this.db.ping()
23
+ const endTime = Date.now()
24
+ const responseTime = endTime - startTime
25
+
26
+ this.debugLogger?.info?.(`Database ping completed in ${responseTime}ms:`, pingResult)
27
+
28
+ this.log(chalk.green('Database connection successful'))
29
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
30
+ this.log(chalk.dim(` Response time: ${responseTime}ms`))
31
+
32
+ if (typeof pingResult === 'string') {
33
+ this.log(chalk.dim(` Response: ${pingResult}`))
34
+ }
35
+
36
+ const result = {
37
+ status: 'success',
38
+ namespace: this.rtNamespace,
39
+ responseTime,
40
+ response: pingResult,
41
+ timestamp: new Date().toISOString()
42
+ }
43
+
44
+ this.log(chalk.dim('Database is ready for operations'))
45
+
46
+ return result
47
+ } catch (error) {
48
+ this.debugLogger?.error?.('Ping command error:', error)
49
+
50
+ this.log(chalk.red('Database connection failed'))
51
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
52
+ this.log(chalk.dim(` Error: ${error.message}`))
53
+
54
+ const result = {
55
+ status: 'failed',
56
+ namespace: this.rtNamespace,
57
+ error: error.message,
58
+ timestamp: new Date().toISOString()
59
+ }
60
+
61
+ return result
62
+ }
63
+ }
64
+ }
65
+
66
+ Ping.description = 'Test connectivity to your App Builder database'
67
+
68
+ Ping.examples = [
69
+ '$ aio app db ping',
70
+ '$ aio app db ping --json'
71
+ ]
72
+
73
+ Ping.flags = {
74
+ ...DBBaseCommand.flags
75
+ }
76
+
77
+ Ping.args = {}
@@ -0,0 +1,190 @@
1
+ /*
2
+ Copyright 2025 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { DBBaseCommand } from '../../../DBBaseCommand.js'
14
+ import chalk from 'chalk'
15
+ import { DB_STATUS } from '../../../constants/db.js'
16
+ import { Flags } from '@oclif/core'
17
+
18
+ export class Provision extends DBBaseCommand {
19
+ async run () {
20
+ const region = this.db.region
21
+
22
+ try {
23
+ // First check if database is already provisioned
24
+ this.log(chalk.blue('Checking current provisioning status...'))
25
+
26
+ let provisionStatusResponse
27
+ try {
28
+ provisionStatusResponse = await this.db.provisionStatus()
29
+ this.debugLogger?.info?.('Provision status:', provisionStatusResponse)
30
+ } catch (error) {
31
+ this.debugLogger?.info?.('No existing provisioning status found:', error.message)
32
+ provisionStatusResponse = null
33
+ }
34
+
35
+ if (provisionStatusResponse) {
36
+ const currentStatus = provisionStatusResponse.status.toUpperCase()
37
+ const statusRegion = provisionStatusResponse.region
38
+
39
+ if (currentStatus === DB_STATUS.PROVISIONED) {
40
+ this.log(chalk.green('Database is already provisioned and ready for use'))
41
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
42
+ this.log(chalk.dim(` Region: ${statusRegion}`))
43
+ this.log(chalk.dim(` Status: ${currentStatus}`))
44
+
45
+ return {
46
+ status: 'already_provisioned',
47
+ namespace: this.rtNamespace,
48
+ details: provisionStatusResponse
49
+ }
50
+ } else if (currentStatus === DB_STATUS.REQUESTED) {
51
+ this.log(chalk.yellow('Database provisioning request has been submitted and is pending'))
52
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
53
+ this.log(chalk.dim(` Region: ${statusRegion}`))
54
+ this.log(chalk.dim(` Status: ${currentStatus}`))
55
+ this.log(chalk.dim('\nUse "aio app db status --watch" to monitor progress'))
56
+
57
+ return {
58
+ status: 'in_progress',
59
+ namespace: this.rtNamespace,
60
+ details: provisionStatusResponse
61
+ }
62
+ } else if (currentStatus === DB_STATUS.PROCESSING) {
63
+ this.log(chalk.yellow('Database is currently being provisioned'))
64
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
65
+ this.log(chalk.dim(` Region: ${statusRegion}`))
66
+ this.log(chalk.dim(` Status: ${currentStatus}`))
67
+ this.log(chalk.dim('\nUse "aio app db status --watch" to monitor progress'))
68
+ this.log(chalk.red('If provisioning takes unusually long, please contact the App Builder team'))
69
+
70
+ return {
71
+ status: 'in_progress',
72
+ namespace: this.rtNamespace,
73
+ details: provisionStatusResponse
74
+ }
75
+ } else if (currentStatus === DB_STATUS.FAILED) {
76
+ this.log(chalk.red('Previous database provisioning failed'))
77
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
78
+ this.log(chalk.dim(` Status: ${currentStatus}`))
79
+ this.log(chalk.yellow('\nAttempting to provision again...'))
80
+ this.log(chalk.red('If the problem persists, please contact the App Builder team'))
81
+ } else if (currentStatus === DB_STATUS.REJECTED) {
82
+ this.log(chalk.red('Previous database provisioning request was rejected'))
83
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
84
+ this.log(chalk.dim(` Status: ${currentStatus}`))
85
+ this.log(chalk.yellow('\nAttempting to provision again...'))
86
+ this.log(chalk.red('If the problem persists, please contact the App Builder team for assistance'))
87
+ } else if (currentStatus !== DB_STATUS.NOT_PROVISIONED) {
88
+ this.log(chalk.yellow(`Database status is '${currentStatus}' - attempting to provision...`))
89
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
90
+ this.log(chalk.dim(` Status: ${currentStatus}`))
91
+ this.log(chalk.red('If you encounter issues, please contact the App Builder team'))
92
+ }
93
+ }
94
+
95
+ // Create a new database if not yet provisioned
96
+ this.warn('Database provisioning will create new database resources')
97
+
98
+ // Skip confirmation prompt if --yes flag is used
99
+ if (!this.flags.yes) {
100
+ // eslint-disable-next-line node/no-unsupported-features/es-syntax
101
+ const { confirm } = await import('@inquirer/prompts')
102
+
103
+ const confirmed = await confirm({
104
+ message: `Provision database for namespace '${this.rtNamespace}'?`,
105
+ default: false
106
+ })
107
+
108
+ if (!confirmed) {
109
+ this.log('Database provisioning cancelled')
110
+ return { status: 'cancelled' }
111
+ }
112
+ }
113
+
114
+ // Start provisioning
115
+ this.log(chalk.blue(`Submitting a request for a database to be provisioned for the '${this.rtNamespace}' namespace...`))
116
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
117
+ this.log(chalk.dim(` Region: ${region}`))
118
+
119
+ const provisionResult = await this.db.provisionRequest()
120
+ this.debugLogger?.info?.('Provision request result:', provisionResult)
121
+
122
+ // Handle different provision result statuses
123
+ const resultStatus = provisionResult?.status?.toUpperCase() || DB_STATUS.UNKNOWN
124
+
125
+ if (resultStatus === DB_STATUS.PROVISIONED) {
126
+ this.log(chalk.green('Database provisioned successfully and ready for use!'))
127
+ } else if (resultStatus === DB_STATUS.REQUESTED) {
128
+ this.log(chalk.blue('Database provisioning request submitted successfully'))
129
+ this.log(chalk.dim('Provisioning is now pending...'))
130
+ } else if (resultStatus === DB_STATUS.PROCESSING) {
131
+ this.log(chalk.yellow('Database is being provisioned...'))
132
+ } else if (resultStatus === DB_STATUS.FAILED) {
133
+ this.error(`Database provisioning failed: ${provisionResult.message || 'Unknown error'}`)
134
+ } else if (resultStatus === DB_STATUS.REJECTED) {
135
+ this.error(`Database provisioning request was rejected: ${provisionResult.message || 'Unknown reason'}`)
136
+ } else if (resultStatus === DB_STATUS.UNKNOWN) {
137
+ this.warn(`Database provisioning request returned unrecognized status '${provisionResult.status || 'undefined'}', an update to the aio cli tool may be necessary.`)
138
+ this.warn('If the issue persists, please contact the App Builder team.')
139
+ } else {
140
+ this.warn(`Database provisioning request returned unexpected status '${resultStatus}', an update to the aio cli tool may be necessary.`)
141
+ this.warn('If the issue persists, please contact the App Builder team.')
142
+ }
143
+
144
+ const result = {
145
+ status: resultStatus.toLowerCase(),
146
+ namespace: this.rtNamespace,
147
+ timestamp: new Date().toISOString(),
148
+ details: provisionResult
149
+ }
150
+
151
+ // If region was specified as a CLI flag, include it in the output for next steps
152
+ const { region: regionFlag } = this.flags
153
+ const regionFlagString = regionFlag ? ` --region ${regionFlag}` : ''
154
+
155
+ if (resultStatus !== DB_STATUS.PROVISIONED) {
156
+ this.log(chalk.dim('\nNext steps:'))
157
+ this.log(chalk.dim(` - Monitor progress: aio app db status${regionFlagString} --watch`))
158
+ this.log(chalk.dim(` - Check status: aio app db status${regionFlagString}`))
159
+ } else {
160
+ this.log(chalk.dim('\nNext steps:'))
161
+ this.log(chalk.dim(` - Test connection: aio app db ping${regionFlagString}`))
162
+ }
163
+
164
+ return result
165
+ } catch (error) {
166
+ this.debugLogger?.error?.('Provision command error:', error)
167
+ this.error(`Database provisioning failed: ${error.message}`)
168
+ }
169
+ }
170
+ }
171
+
172
+ Provision.description = 'Provision a new database for your App Builder application'
173
+
174
+ Provision.examples = [
175
+ '$ aio app db provision',
176
+ '$ aio app db provision --region amer',
177
+ '$ aio app db provision --json',
178
+ '$ aio app db provision --yes'
179
+ ]
180
+
181
+ Provision.flags = {
182
+ ...DBBaseCommand.flags,
183
+ yes: Flags.boolean({
184
+ char: 'y',
185
+ description: 'Skip confirmation prompt and provision automatically',
186
+ default: false
187
+ })
188
+ }
189
+
190
+ Provision.args = {}