@adobe/aio-cli-plugin-app-storage 1.2.0 → 1.3.0-pre.2026-02-12.sha-6f4504ab

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 +688 -1
  2. package/package.json +25 -5
  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 -316
@@ -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 = {}
@@ -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 chalk from 'chalk'
15
+ import { prettyJson } from '../../../utils/output.js'
16
+
17
+ export class Stats extends DBBaseCommand {
18
+ async run () {
19
+ try {
20
+ this.log(chalk.blue('Fetching database statistics...'))
21
+
22
+ const client = await this.db.connect()
23
+ const stats = await client.dbStats()
24
+
25
+ this.debugLogger?.info?.('Database statistics retrieved:', stats)
26
+
27
+ const result = {
28
+ ...stats,
29
+ namespace: this.rtNamespace,
30
+ timestamp: new Date().toISOString()
31
+ }
32
+
33
+ this.displayStats(stats)
34
+
35
+ return result
36
+ } catch (error) {
37
+ this.debugLogger?.error?.('Stats command error:', error)
38
+
39
+ this.log(chalk.red('Failed to retrieve database statistics'))
40
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
41
+ this.log(chalk.dim(` Error: ${error.message}`))
42
+
43
+ this.error(`Failed to fetch database statistics: ${error.message}`)
44
+ }
45
+ }
46
+
47
+ displayStats (stats) {
48
+ this.log(chalk.green('Database Statistics:'))
49
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
50
+
51
+ if (stats && typeof stats === 'object') {
52
+ // Format and display stats in a readable way
53
+ Object.entries(stats).forEach(([key, value]) => {
54
+ this.log(chalk.dim(` ${key}: ${this.formatValue(value)}`))
55
+ })
56
+ } else {
57
+ this.log(chalk.dim(` Raw Stats: ${this.formatValue(stats)}`))
58
+ }
59
+
60
+ this.log('')
61
+ this.log(chalk.dim(` Retrieved: ${new Date().toLocaleString()}`))
62
+ }
63
+
64
+ formatValue (value) {
65
+ if (typeof value === 'number') {
66
+ // Format large numbers with commas
67
+ return value.toLocaleString()
68
+ }
69
+ if (typeof value === 'object' && value !== null) {
70
+ return `\n${prettyJson(value)}`
71
+ }
72
+ return String(value)
73
+ }
74
+ }
75
+
76
+ Stats.description = 'Get statistics about your App Builder database'
77
+
78
+ Stats.examples = [
79
+ '$ aio app db stats',
80
+ '$ aio app db stats --json'
81
+ ]
82
+
83
+ Stats.flags = {
84
+ ...DBBaseCommand.flags
85
+ }
86
+
87
+ Stats.args = {}
@@ -0,0 +1,159 @@
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 { Flags } from '@oclif/core'
15
+ import chalk from 'chalk'
16
+ import { DB_STATUS } from '../../../constants/db.js'
17
+
18
+ export class Status extends DBBaseCommand {
19
+ async run () {
20
+ const { watch } = this.flags
21
+
22
+ this.debugLogger?.info?.('Checking database provisioning status')
23
+
24
+ if (watch) {
25
+ return this.watchStatus()
26
+ } else {
27
+ return this.checkStatus()
28
+ }
29
+ }
30
+
31
+ async checkStatus () {
32
+ try {
33
+ this.log(chalk.blue('Checking database provisioning status...'))
34
+
35
+ const provisionStatusResponse = await this.db.provisionStatus()
36
+ this.debugLogger?.info?.('Status result:', provisionStatusResponse)
37
+
38
+ this.displayStatus(provisionStatusResponse)
39
+
40
+ return {
41
+ ...provisionStatusResponse,
42
+ namespace: this.rtNamespace,
43
+ timestamp: new Date().toISOString()
44
+ }
45
+ } catch (error) {
46
+ this.debugLogger?.error?.('Status command error:', error)
47
+
48
+ if (error.httpStatusCode === 404) {
49
+ this.log(chalk.yellow('No database has been provisioned for this workspace'))
50
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
51
+ this.log(chalk.dim(` Status: ${DB_STATUS.NOT_PROVISIONED}`))
52
+
53
+ return {
54
+ status: DB_STATUS.NOT_PROVISIONED,
55
+ namespace: this.rtNamespace,
56
+ timestamp: new Date().toISOString()
57
+ }
58
+ }
59
+
60
+ this.error(`Failed to check database status: ${error.message}`)
61
+ }
62
+ }
63
+
64
+ async watchStatus () {
65
+ this.log(chalk.blue('Watching database provisioning status (press Ctrl+C to stop)...'))
66
+
67
+ let previousStatus = null
68
+ const checkInterval = 3000 // 3 seconds
69
+
70
+ const watchLoop = async () => {
71
+ try {
72
+ const provisionStatusResponse = await this.db.provisionStatus()
73
+
74
+ // Only display if status changed
75
+ if (previousStatus?.status !== provisionStatusResponse?.status) {
76
+ this.log(chalk.dim(`\n[${new Date().toLocaleTimeString()}]`))
77
+ this.displayStatus(provisionStatusResponse, false) // Don't show timestamp in watch mode
78
+ previousStatus = provisionStatusResponse
79
+
80
+ // Stop watching if provisioning is complete or failed
81
+ const currentStatus = provisionStatusResponse.status?.toUpperCase()
82
+ if (currentStatus !== DB_STATUS.REQUESTED && currentStatus !== DB_STATUS.PROCESSING) {
83
+ this.log(chalk.dim('\nStopping watch mode.'))
84
+ return provisionStatusResponse
85
+ }
86
+ }
87
+
88
+ // Schedule next check
89
+ setTimeout(watchLoop, checkInterval)
90
+ } catch (error) {
91
+ this.debugLogger?.error?.('Watch status error:', error)
92
+ this.log(chalk.red(`\n[${new Date().toLocaleTimeString()}] Error: ${error.message}`))
93
+
94
+ // Continue watching despite errors
95
+ setTimeout(watchLoop, checkInterval)
96
+ }
97
+ }
98
+
99
+ // Start watching
100
+ return watchLoop()
101
+ }
102
+
103
+ displayStatus (provisionStatusResponse, showTimestamp = true) {
104
+ const currentStatus = provisionStatusResponse.status.toUpperCase()
105
+ const statusColor = this.getStatusColor(currentStatus)
106
+
107
+ this.log(statusColor(`Database Status: ${currentStatus}`))
108
+ this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
109
+
110
+ if (provisionStatusResponse.message) {
111
+ this.log(chalk.dim(` Message: ${provisionStatusResponse.message}`))
112
+ }
113
+
114
+ if (provisionStatusResponse.submitted) {
115
+ this.log(chalk.dim(` Submitted: ${new Date(provisionStatusResponse.submitted).toLocaleString()}`))
116
+ }
117
+
118
+ if (showTimestamp) {
119
+ this.log(chalk.dim(` Checked: ${new Date().toLocaleString()}`))
120
+ }
121
+ }
122
+
123
+ /* istanbul ignore next */
124
+ getStatusColor (statusValue) {
125
+ const status = statusValue.toUpperCase()
126
+ switch (status) {
127
+ case DB_STATUS.PROVISIONED:
128
+ return chalk.green
129
+ case DB_STATUS.REQUESTED:
130
+ case DB_STATUS.PROCESSING:
131
+ return chalk.yellow
132
+ case DB_STATUS.FAILED:
133
+ case DB_STATUS.REJECTED:
134
+ return chalk.red
135
+ case DB_STATUS.NOT_PROVISIONED:
136
+ return chalk.blue
137
+ default:
138
+ return chalk.gray
139
+ }
140
+ }
141
+ }
142
+
143
+ Status.description = 'Check the provisioning status of your App Builder database'
144
+
145
+ Status.examples = [
146
+ '$ aio app db status',
147
+ '$ aio app db status --watch',
148
+ '$ aio app db status --json'
149
+ ]
150
+
151
+ Status.flags = {
152
+ ...DBBaseCommand.flags,
153
+ watch: Flags.boolean({
154
+ description: 'Watch for status changes (press Ctrl+C to stop)',
155
+ default: false
156
+ })
157
+ }
158
+
159
+ Status.args = {}
@@ -10,12 +10,12 @@ OF ANY KIND, either express or implied. See the License for the specific languag
10
10
  governing permissions and limitations under the License.
11
11
  */
12
12
  import chalk from 'chalk'
13
- import { BaseCommand } from '../../../BaseCommand.js'
13
+ import { StateBaseCommand } from '../../../StateBaseCommand.js'
14
14
  import { Args, Flags } from '@oclif/core'
15
15
 
16
16
  const MAX_ARGV_NO_CONFIRM = 5
17
17
 
18
- export class Delete extends BaseCommand {
18
+ export class Delete extends StateBaseCommand {
19
19
  async run () {
20
20
  const { match, force } = this.flags
21
21
  const { argv: keysToDelete } = await this.parse(Delete)
@@ -100,7 +100,7 @@ Delete.args = {
100
100
  }
101
101
 
102
102
  Delete.flags = {
103
- ...BaseCommand.flags,
103
+ ...StateBaseCommand.flags,
104
104
  match: Flags.string({
105
105
  description: '[use with caution!] deletes ALL key-values matching the provided glob-like pattern',
106
106
  required: false
@@ -10,10 +10,10 @@ OF ANY KIND, either express or implied. See the License for the specific languag
10
10
  governing permissions and limitations under the License.
11
11
  */
12
12
  import chalk from 'chalk'
13
- import { BaseCommand } from '../../../BaseCommand.js'
13
+ import { StateBaseCommand } from '../../../StateBaseCommand.js'
14
14
  import { Args } from '@oclif/core'
15
15
 
16
- export class Get extends BaseCommand {
16
+ export class Get extends StateBaseCommand {
17
17
  async run () {
18
18
  const ret = await this.state.get(this.args.key)
19
19
 
@@ -9,14 +9,14 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA
9
9
  OF ANY KIND, either express or implied. See the License for the specific language
10
10
  governing permissions and limitations under the License.
11
11
  */
12
- import { BaseCommand } from '../../../BaseCommand.js'
12
+ import { StateBaseCommand } from '../../../StateBaseCommand.js'
13
13
  import { Flags } from '@oclif/core'
14
14
  import chalk from 'chalk'
15
15
 
16
16
  const MAX_KEYS = 5000
17
17
  const COUNT_HINT = 500 // per iteration
18
18
 
19
- export class List extends BaseCommand {
19
+ export class List extends StateBaseCommand {
20
20
  async run () {
21
21
  const allKeys = []
22
22
 
@@ -55,7 +55,7 @@ List.examples = [
55
55
  ]
56
56
 
57
57
  List.flags = {
58
- ...BaseCommand.flags,
58
+ ...StateBaseCommand.flags,
59
59
  match: Flags.string({
60
60
  name: 'match',
61
61
  char: 'm',
@@ -9,12 +9,12 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA
9
9
  OF ANY KIND, either express or implied. See the License for the specific language
10
10
  governing permissions and limitations under the License.
11
11
  */
12
- import { BaseCommand } from '../../../BaseCommand.js'
12
+ import { StateBaseCommand } from '../../../StateBaseCommand.js'
13
13
  import { Args, Flags } from '@oclif/core'
14
- import { DEFAULT_TTL_SECONDS } from '../../../constants.js'
14
+ import { DEFAULT_TTL_SECONDS } from '../../../constants/state.js'
15
15
  import chalk from 'chalk'
16
16
 
17
- export class Put extends BaseCommand {
17
+ export class Put extends StateBaseCommand {
18
18
  async run () {
19
19
  const { key, value } = this.args
20
20
  const { json, ttl } = this.flags
@@ -55,7 +55,7 @@ Put.args = {
55
55
  }
56
56
 
57
57
  Put.flags = {
58
- ...BaseCommand.flags,
58
+ ...StateBaseCommand.flags,
59
59
  ttl: Flags.integer({
60
60
  char: 't',
61
61
  description: 'Time to live in seconds. Default is 86400 (24 hours), max is 31536000 (1 year).',