@adobe/helix-config-storage 2.12.1 → 2.12.2

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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npx mocha:*)"
5
+ ]
6
+ }
7
+ }
package/.env ADDED
File without changes
package/CLAUDE.md ADDED
@@ -0,0 +1,47 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Commands
6
+
7
+ ```bash
8
+ # Run all tests with coverage
9
+ npm test
10
+
11
+ # Run a single test file
12
+ npx mocha --spec 'test/config-store.test.js'
13
+
14
+ # Lint (ESLint + JSON schema validation)
15
+ npm run lint
16
+
17
+ # Validate JSON schemas only
18
+ ./validate-json-schemas.sh
19
+ ```
20
+
21
+ ## Architecture
22
+
23
+ This is a Node.js library for managing configuration storage for Adobe Helix (Edge Delivery Services) projects. It provides CRUD operations for org, site, and profile configuration objects stored in a content bus via `@adobe/helix-shared-storage`.
24
+
25
+ ### Core Concepts
26
+
27
+ - **Config types**: `org`, `sites`, `profiles` — stored at `/orgs/{org}/{type}/{name}.json`
28
+ - **Fragments**: Support for updating nested subpaths (e.g., `/cdn/prod`, `/secrets/{id}`) via parameterized deep-path operations
29
+ - **Profile merging**: Site configs can inherit from profiles; `config-merge.js` handles atomic/object/array union merge strategies
30
+ - **Versioning**: Auto-incrementing version history stored alongside each config
31
+ - **Access control gates**: `withAllowAdmin(bool)` and `withAllowOps(bool)` on the store control what callers may modify
32
+
33
+ ### Key Files
34
+
35
+ - `src/config-store.js` — Main `ConfigStore` class with create/read/update/remove/validate methods
36
+ - `src/fragment.js` — Fragment path parsing and application for nested updates
37
+ - `src/config-merge.js` — Profile/site merge logic (atomic vs. deep-merge vs. array-union per property)
38
+ - `src/config-validator.js` — AJV-based schema validation across 40+ JSON schemas in `src/schemas/`
39
+ - `src/config-versioning.js` — Version history management
40
+ - `src/utils.js` — Helpers for users, tokens, secrets, content/code source detection
41
+
42
+ ### Patterns
43
+
44
+ - All operations accept a `ctx` object providing storage access (`HelixStorage.fromContext(ctx).configBus()`) and logging (`ctx.log`)
45
+ - Sensitive fields (secrets, tokens) are redacted on read — only metadata is returned
46
+ - Schema validation uses AJV with coercion and defaults enabled
47
+ - The `Fragment` class handles parameterized sub-paths; arrays and objects within configs use different merge strategies
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/helix-config-storage",
3
- "version": "2.12.1",
3
+ "version": "2.12.2",
4
4
  "description": "Helix Config Storage",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -36,18 +36,18 @@
36
36
  "reporter-options": "configFile=.mocha-multi.json"
37
37
  },
38
38
  "devDependencies": {
39
- "@adobe/eslint-config-helix": "3.0.21",
39
+ "@adobe/eslint-config-helix": "3.0.22",
40
40
  "@eslint/config-helpers": "0.5.2",
41
41
  "@semantic-release/changelog": "6.0.3",
42
42
  "@semantic-release/git": "10.0.1",
43
- "@semantic-release/npm": "13.1.4",
43
+ "@semantic-release/npm": "13.1.5",
44
44
  "ajv-cli": "5.0.0",
45
45
  "c8": "11.0.0",
46
46
  "eslint": "9.4.0",
47
47
  "husky": "9.1.7",
48
48
  "json-schema-to-typescript": "15.0.4",
49
49
  "junit-report-builder": "5.1.1",
50
- "lint-staged": "16.2.7",
50
+ "lint-staged": "16.3.1",
51
51
  "mocha": "11.7.5",
52
52
  "mocha-multi-reporters": "1.5.1",
53
53
  "mocha-suppress-logs": "0.6.0",
@@ -68,6 +68,7 @@
68
68
  "@adobe/helix-shared-string": "^2.1.0",
69
69
  "@adobe/helix-shared-utils": "^3.0.2",
70
70
  "ajv": "8.18.0",
71
+ "ajv-errors": "^3.0.0",
71
72
  "ajv-formats": "3.0.1",
72
73
  "jose": "6.1.3"
73
74
  }
@@ -10,6 +10,7 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
  import Ajv2019 from 'ajv/dist/2019.js';
13
+ import ajvErrors from 'ajv-errors';
13
14
  import ajvFormats from 'ajv-formats';
14
15
  import { ValidationError } from './ValidationError.js';
15
16
 
@@ -114,6 +115,7 @@ export async function validate(config, type) {
114
115
  strict: false,
115
116
  });
116
117
  ajvFormats(ajv);
118
+ ajvErrors(ajv);
117
119
 
118
120
  ajv.addSchema(SCHEMAS);
119
121
  const res = ajv.validate(schema, config);
@@ -12,10 +12,12 @@
12
12
  "host": {
13
13
  "description": "production host",
14
14
  "type": "string",
15
- "pattern": "^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.){1,3}[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$",
15
+ "pattern": "^$|^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.){1,3}[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$",
16
16
  "not": {
17
- "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$",
18
- "description": "AEM endpoints not allowed"
17
+ "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$"
18
+ },
19
+ "errorMessage": {
20
+ "not": "AEM endpoints are not allowed as /cdn/prod/host"
19
21
  }
20
22
  },
21
23
  "route": {
@@ -12,10 +12,12 @@
12
12
  "host": {
13
13
  "description": "production host",
14
14
  "type": "string",
15
- "pattern": "^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.){1,3}[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$",
15
+ "pattern": "^$|^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.){1,3}[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$",
16
16
  "not": {
17
- "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$",
18
- "description": "AEM endpoints not allowed"
17
+ "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$"
18
+ },
19
+ "errorMessage": {
20
+ "not": "AEM endpoints are not allowed as /cdn/prod/host"
19
21
  }
20
22
  },
21
23
  "route": {
@@ -12,10 +12,12 @@
12
12
  "host": {
13
13
  "description": "production host",
14
14
  "type": "string",
15
- "pattern": "^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.){1,3}[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$",
15
+ "pattern": "^$|^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.){1,3}[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$",
16
16
  "not": {
17
- "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$",
18
- "description": "AEM endpoints not allowed"
17
+ "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$"
18
+ },
19
+ "errorMessage": {
20
+ "not": "AEM endpoints are not allowed as /cdn/prod/host"
19
21
  }
20
22
  },
21
23
  "route": {
@@ -13,10 +13,12 @@
13
13
  "host": {
14
14
  "description": "production host",
15
15
  "type": "string",
16
- "pattern": "^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.){1,3}[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$",
16
+ "pattern": "^$|^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.){1,3}[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$",
17
17
  "not": {
18
- "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$",
19
- "description": "AEM endpoints not allowed"
18
+ "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$"
19
+ },
20
+ "errorMessage": {
21
+ "not": "AEM endpoints are not allowed as /cdn/prod/host"
20
22
  }
21
23
  },
22
24
  "route": {
@@ -12,10 +12,12 @@
12
12
  "host": {
13
13
  "description": "production host",
14
14
  "type": "string",
15
- "pattern": "^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.){1,3}[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$",
15
+ "pattern": "^$|^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.){1,3}[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$",
16
16
  "not": {
17
- "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$",
18
- "description": "AEM endpoints not allowed"
17
+ "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$"
18
+ },
19
+ "errorMessage": {
20
+ "not": "AEM endpoints are not allowed as /cdn/prod/host"
19
21
  }
20
22
  },
21
23
  "envId": {
@@ -31,8 +31,10 @@
31
31
  "type": "string",
32
32
  "pattern": "^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.){1,3}[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$",
33
33
  "not": {
34
- "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$",
35
- "description": "AEM endpoints not allowed"
34
+ "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$"
35
+ },
36
+ "errorMessage": {
37
+ "not": "AEM endpoints are not allowed as /cdn/prod/host"
36
38
  }
37
39
  }
38
40
  },
@@ -53,8 +55,10 @@
53
55
  ],
54
56
  "type": "string",
55
57
  "not": {
56
- "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$",
57
- "description": "AEM endpoints not allowed"
58
+ "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$"
59
+ },
60
+ "errorMessage": {
61
+ "not": "AEM endpoints are not allowed as /cdn/live/host"
58
62
  }
59
63
  }
60
64
  },
@@ -73,8 +77,10 @@
73
77
  ],
74
78
  "type": "string",
75
79
  "not": {
76
- "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$",
77
- "description": "AEM endpoints not allowed"
80
+ "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$"
81
+ },
82
+ "errorMessage": {
83
+ "not": "AEM endpoints are not allowed as /cdn/preview/host"
78
84
  }
79
85
  }
80
86
  },
@@ -93,8 +99,10 @@
93
99
  ],
94
100
  "type": "string",
95
101
  "not": {
96
- "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$",
97
- "description": "AEM endpoints not allowed"
102
+ "pattern": ".*--.*--.*\\.aem\\.(live|page|reviews|network)$"
103
+ },
104
+ "errorMessage": {
105
+ "not": "AEM endpoints are not allowed as /cdn/review/host"
98
106
  }
99
107
  }
100
108
  },