@adobe/aio-cli-plugin-app-storage 1.2.0 → 1.3.0-pre.2026-02-20.sha-df98265c

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 (37) hide show
  1. package/README.md +722 -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/org/stats.js +111 -0
  23. package/src/commands/app/db/ping.js +77 -0
  24. package/src/commands/app/db/provision.js +190 -0
  25. package/src/commands/app/db/stats.js +101 -0
  26. package/src/commands/app/db/status.js +159 -0
  27. package/src/commands/app/state/delete.js +3 -3
  28. package/src/commands/app/state/get.js +2 -2
  29. package/src/commands/app/state/list.js +3 -3
  30. package/src/commands/app/state/put.js +4 -4
  31. package/src/commands/app/state/stats.js +2 -2
  32. package/src/constants/db.js +32 -0
  33. package/src/constants/global.js +14 -0
  34. package/src/{constants.js → constants/state.js} +3 -0
  35. package/src/utils/inputValidation.js +74 -0
  36. package/src/utils/output.js +35 -0
  37. package/oclif.manifest.json +0 -316
@@ -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).',
@@ -9,9 +9,9 @@ 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
 
14
- export class Stats extends BaseCommand {
14
+ export class Stats extends StateBaseCommand {
15
15
  async run () {
16
16
  const ret = await this.state.stats()
17
17
  this.log(
@@ -0,0 +1,32 @@
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
+ export const DB_STATUS = {
14
+ PROVISIONED: 'PROVISIONED',
15
+ REQUESTED: 'REQUESTED',
16
+ PROCESSING: 'PROCESSING',
17
+ FAILED: 'FAILED',
18
+ REJECTED: 'REJECTED',
19
+ NOT_PROVISIONED: 'NOT_PROVISIONED',
20
+ DELETED: 'DELETED',
21
+ UNKNOWN: 'UNKNOWN'
22
+ }
23
+
24
+ // Region constants for db are separate from state in case they diverge in the future
25
+ export const CONFIG_DB_REGION = 'db.region'
26
+ export const DEFAULT_REGION = 'amer'
27
+ export const AVAILABLE_REGIONS = {
28
+ prod: ['amer', 'emea', 'apac', 'aus'],
29
+ stage: ['amer', 'amer2']
30
+ }
31
+
32
+ export const CONFIG_DB_ENDPOINT = 'db.endpoint'
@@ -0,0 +1,14 @@
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
+ export const CONFIG_RUNTIME_NAMESPACE = 'runtime.namespace'
14
+ export const CONFIG_RUNTIME_AUTH = 'runtime.auth'
@@ -11,6 +11,9 @@ governing permissions and limitations under the License.
11
11
  */
12
12
 
13
13
  // state.region is a new configuration (env=AIO_STATE_REGION)
14
+ // Region constants for state are separate from db in case they diverge in the future
14
15
  export const CONFIG_STATE_REGION = 'state.region'
16
+ export const DEFAULT_REGION = 'amer'
17
+ export const AVAILABLE_REGIONS = ['amer', 'emea', 'apac', 'aus']
15
18
 
16
19
  export const DEFAULT_TTL_SECONDS = 60 * 60 * 24 // 24 hours
@@ -0,0 +1,74 @@
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
+ /**
14
+ * Helper to verify the argument/option input is a javascript or json object
15
+ * Returns the object if valid, or throws an error if invalid
16
+ *
17
+ * @param {object|string} input - The input to validate
18
+ * @param {string=} label - Optional label for an error message, such as the argument/option name
19
+ * @returns {object} - The validated object
20
+ */
21
+ export function asObject (input, label = undefined) {
22
+ label = label ? `${label}: ` : ''
23
+ if (typeof input === 'object' && input !== null && !Array.isArray(input)) {
24
+ return input
25
+ }
26
+ if (typeof input !== 'string' || input.trim().length === 0) {
27
+ throw new Error(`${label}Value '${input}' is not a JSON object`)
28
+ }
29
+
30
+ let result
31
+ try {
32
+ result = JSON.parse(input)
33
+ } catch (e) {
34
+ e.message = `${label}JSON parse error: ${e.message}`
35
+ throw e
36
+ }
37
+ if (typeof result !== 'object' || result === null || Array.isArray(result)) {
38
+ throw new Error(`${label}Value '${input}' is not a JSON object`)
39
+ }
40
+
41
+ return result
42
+ }
43
+
44
+ /**
45
+ * Helper to verify the argument/option input is a non-empty string
46
+ * Throws an error if the input is not a string with length > 0
47
+ * Does not check for whitespace-only strings, call `trim()` when passing the input if needed
48
+ *
49
+ * @param {string} input - The input to validate
50
+ * @param {string=} label - Optional label for an error message, such as the argument/option name
51
+ * @returns {string} - The validated non-empty string
52
+ */
53
+ export function isNonEmptyString (input, label = undefined) {
54
+ label = label ? `${label}: ` : ''
55
+ if (typeof input !== 'string' || input.length === 0) {
56
+ throw new Error(`${label}Must be a non-empty string`)
57
+ }
58
+ return input
59
+ }
60
+
61
+ /**
62
+ * Determine if a runtime namespace corresponds to a production workspace.
63
+ * production if optional prefix<development-> + orgId + projectName with no trailing workspace suffix.
64
+ *
65
+ * @param {string} namespace - The runtime namespace to check
66
+ * @returns {boolean} - True if the namespace is a production workspace, false otherwise
67
+ */
68
+ export function isProductionNamespace (namespace) {
69
+ if (typeof namespace !== 'string' || !namespace.trim()) {
70
+ throw new Error('Invalid runtime namespace')
71
+ }
72
+ const PROD_NS_REGEX = /^(?:development-)?\d+-[a-z0-9]+$/i
73
+ return PROD_NS_REGEX.test(namespace)
74
+ }
@@ -0,0 +1,35 @@
1
+ /*
2
+ Copyright 2024 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
+ /**
14
+ * Prettifies json for readable log output
15
+ *
16
+ * @param {*} val - The value to pretty print as JSON.
17
+ * @param {number} indent - The number of spaces to indent the entire output.
18
+ * @returns {string} - The pretty printed JSON string.
19
+ */
20
+ export function prettyJson (val, indent = 5) {
21
+ let out
22
+ if (typeof val === 'string') {
23
+ try {
24
+ out = JSON.stringify(JSON.parse(val), null, 2)
25
+ } catch (e) {
26
+ out = val // If parsing fails, return the original string
27
+ }
28
+ } else {
29
+ out = JSON.stringify(val, null, 2)
30
+ }
31
+ if (indent) {
32
+ out = out.replace(/^/gm, ' '.repeat(indent))
33
+ }
34
+ return out
35
+ }