@adobe/helix-config-storage 2.5.2 → 2.8.1

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,73 @@
1
+ ## [2.8.1](https://github.com/adobe/helix-config-storage/compare/v2.8.0...v2.8.1) (2025-08-22)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * back to adobe npm org ([#170](https://github.com/adobe/helix-config-storage/issues/170)) ([092be53](https://github.com/adobe/helix-config-storage/commit/092be5397d19ad2e7151f5def3412ffebd4d5e1f))
7
+
8
+ # [2.8.0](https://github.com/adobe/helix-config-storage/compare/v2.7.6...v2.8.0) (2025-08-21)
9
+
10
+
11
+ ### Features
12
+
13
+ * use POST for restore version ([#169](https://github.com/adobe/helix-config-storage/issues/169)) ([0723da3](https://github.com/adobe/helix-config-storage/commit/0723da321920eb1b4ebd86fc7dc27049e302e30c))
14
+
15
+ ## [2.7.6](https://github.com/adobe/helix-config-storage/compare/v2.7.5...v2.7.6) (2025-08-19)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * add status code when initial validation fails ([#167](https://github.com/adobe/helix-config-storage/issues/167)) ([64705e9](https://github.com/adobe/helix-config-storage/commit/64705e9cf90b1fbde3ee3b1ff7faf55da1d715dc))
21
+
22
+ ## [2.7.5](https://github.com/adobe/helix-config-storage/compare/v2.7.4...v2.7.5) (2025-08-14)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * **deps:** update dependency @adobe/helix-shared-storage to v1.4.1 ([#166](https://github.com/adobe/helix-config-storage/issues/166)) ([9d3f510](https://github.com/adobe/helix-config-storage/commit/9d3f510a9d8e702582e843b9f4be30ff920375bc))
28
+
29
+ ## [2.7.4](https://github.com/adobe/helix-config-storage/compare/v2.7.3...v2.7.4) (2025-08-13)
30
+
31
+
32
+ ### Bug Fixes
33
+
34
+ * proper list sites and profiles ([#165](https://github.com/adobe/helix-config-storage/issues/165)) ([1017a91](https://github.com/adobe/helix-config-storage/commit/1017a91773fd29bcc498f2f20c9b63e06c142dd9))
35
+
36
+ ## [2.7.3](https://github.com/adobe/helix-config-storage/compare/v2.7.2...v2.7.3) (2025-08-12)
37
+
38
+
39
+ ### Bug Fixes
40
+
41
+ * **deps:** update adobe fixes ([#164](https://github.com/adobe/helix-config-storage/issues/164)) ([c688e5d](https://github.com/adobe/helix-config-storage/commit/c688e5dc6375f14be529967c4650ab7d60e2e63e))
42
+
43
+ ## [2.7.2](https://github.com/adobe/helix-config-storage/compare/v2.7.1...v2.7.2) (2025-08-12)
44
+
45
+
46
+ ### Bug Fixes
47
+
48
+ * update npm package ([77e5607](https://github.com/adobe/helix-config-storage/commit/77e56079a3842ac39cbf71dfcb9eb0531d0086b2))
49
+
50
+ ## [2.7.1](https://github.com/adobe/helix-config-storage/compare/v2.7.0...v2.7.1) (2025-08-12)
51
+
52
+
53
+ ### Bug Fixes
54
+
55
+ * **deps:** update adobe fixes ([#163](https://github.com/adobe/helix-config-storage/issues/163)) ([0af5fc4](https://github.com/adobe/helix-config-storage/commit/0af5fc4953a7d106c6f16ac7877dde3dc2d9c090))
56
+
57
+ # [2.7.0](https://github.com/adobe/helix-config-storage/compare/v2.6.0...v2.7.0) (2025-08-11)
58
+
59
+
60
+ ### Features
61
+
62
+ * add versioning apis ([#160](https://github.com/adobe/helix-config-storage/issues/160)) ([1eca459](https://github.com/adobe/helix-config-storage/commit/1eca459d781c3a9f1ed03c539022d55f1146e7c5))
63
+
64
+ # [2.6.0](https://github.com/adobe/helix-config-storage/compare/v2.5.2...v2.6.0) (2025-08-05)
65
+
66
+
67
+ ### Features
68
+
69
+ * include wordSaveDelay in sidekick schema ([0ad3d04](https://github.com/adobe/helix-config-storage/commit/0ad3d048a5b3ee8ee5dbe621b3638ce352d0fc6c))
70
+
1
71
  ## [2.5.2](https://github.com/adobe/helix-config-storage/compare/v2.5.1...v2.5.2) (2025-07-23)
2
72
 
3
73
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/helix-config-storage",
3
- "version": "2.5.2",
3
+ "version": "2.8.1",
4
4
  "description": "Helix Config Storage",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -36,8 +36,8 @@
36
36
  "reporter-options": "configFile=.mocha-multi.json"
37
37
  },
38
38
  "devDependencies": {
39
- "@adobe/eslint-config-helix": "3.0.8",
40
- "@eslint/config-helpers": "0.3.0",
39
+ "@adobe/eslint-config-helix": "3.0.9",
40
+ "@eslint/config-helpers": "0.3.1",
41
41
  "@semantic-release/changelog": "6.0.3",
42
42
  "@semantic-release/git": "10.0.1",
43
43
  "@semantic-release/npm": "12.0.2",
@@ -47,7 +47,7 @@
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.1.2",
50
+ "lint-staged": "16.1.5",
51
51
  "mocha": "11.7.1",
52
52
  "mocha-multi-reporters": "1.5.1",
53
53
  "mocha-suppress-logs": "0.6.0",
@@ -64,7 +64,7 @@
64
64
  "@adobe/helix-shared-config": "^11.1.4",
65
65
  "@adobe/helix-shared-git": "^3.0.18",
66
66
  "@adobe/helix-shared-process-queue": "3.1.3",
67
- "@adobe/helix-shared-storage": "^1.3.0",
67
+ "@adobe/helix-shared-storage": "^1.4.0",
68
68
  "@adobe/helix-shared-string": "^2.1.0",
69
69
  "@adobe/helix-shared-utils": "^3.0.2",
70
70
  "ajv": "8.17.1",
@@ -25,6 +25,7 @@ import {
25
25
  import { validate as validateSchema } from './config-validator.js';
26
26
  import { getMergedConfig } from './config-merge.js';
27
27
  import { ValidationError } from './ValidationError.js';
28
+ import { ConfigVersioning } from './config-versioning.js';
28
29
 
29
30
  const FRAGMENTS_COMMON = {
30
31
  content: 'object',
@@ -324,6 +325,11 @@ export class ConfigStore {
324
325
  */
325
326
  #profiles = {};
326
327
 
328
+ /**
329
+ * @member {ConfigVersioning} versioning the versioning manager
330
+ */
331
+ #versioning;
332
+
327
333
  /**
328
334
  * @param {string} org the org id
329
335
  * @param {string} type store type (org, sites, profiles, secrets, users)
@@ -331,27 +337,28 @@ export class ConfigStore {
331
337
  */
332
338
  constructor(org, type = 'org', name = '') {
333
339
  if (!org) {
334
- throw new Error('org required');
340
+ throw new StatusCodeError(400, 'org required');
335
341
  }
336
342
  if (org.includes('/') || type.includes('/') || name.includes('/')) {
337
- throw new Error('orgId, type and name must not contain slashes');
343
+ throw new StatusCodeError(400, 'orgId, type and name must not contain slashes');
338
344
  }
339
345
  if (org.toLowerCase() !== org) {
340
- throw new Error('orgId must be lowercase');
346
+ throw new StatusCodeError(400, 'orgId must be lowercase');
341
347
  }
342
348
  if (type === 'sites' && name.toLowerCase() !== name) {
343
- throw new Error('site must be lowercase');
349
+ throw new StatusCodeError(400, 'site must be lowercase');
344
350
  }
345
351
  this.org = org;
346
352
  this.type = type;
347
353
  this.name = name;
348
354
  this.key = type === 'org'
349
- ? `/orgs/${org}/${name || 'config'}.json`
355
+ ? `/orgs/${org}/config.json`
350
356
  : `/orgs/${org}/${type}/${name}.json`;
351
357
  this.now = new Date();
352
358
  this.isAdmin = false;
353
359
  this.isOps = false;
354
360
  this.listDetails = false;
361
+ this.#versioning = new ConfigVersioning(org, type, name);
355
362
  }
356
363
 
357
364
  /**
@@ -399,7 +406,7 @@ export class ConfigStore {
399
406
  async #list(ctx) {
400
407
  const storage = HelixStorage.fromContext(ctx).configBus();
401
408
  const key = `orgs/${this.org}/${this.type}/`;
402
- const list = await storage.list(key);
409
+ const list = await storage.list(key, true);
403
410
  let entries = list.map((entry) => {
404
411
  const siteKey = entry.key;
405
412
  if (siteKey.endsWith('.json')) {
@@ -523,6 +530,7 @@ export class ConfigStore {
523
530
  throw new StatusCodeError(409, 'create not supported on substructures.');
524
531
  }
525
532
  const storage = HelixStorage.fromContext(ctx).configBus();
533
+
526
534
  if (await storage.head(this.key)) {
527
535
  throw new StatusCodeError(409, 'config already exists.');
528
536
  }
@@ -544,13 +552,14 @@ export class ConfigStore {
544
552
  }
545
553
  data.users = users;
546
554
  }
547
-
548
555
  if (this.type !== 'org') {
549
556
  updateContentSource(ctx, data.content);
550
557
  updateCodeSource(ctx, data.code);
551
558
  }
559
+
552
560
  // we don't allow to define created
553
561
  delete data.created;
562
+
554
563
  this.#updateTimeStamps(data);
555
564
  let oldConfig = {};
556
565
  if (this.type === 'sites') {
@@ -561,6 +570,9 @@ export class ConfigStore {
561
570
  const config = await this.getAggregatedConfig(ctx, data);
562
571
  await this.validate(ctx, config);
563
572
  await this.validatePermissions(ctx, oldConfig, config);
573
+
574
+ // Create a new version before saving
575
+ await this.#versioning.createVersion(ctx, data, 'initial');
564
576
  await storage.put(this.key, JSON.stringify(data), 'application/json');
565
577
  await this.purge(ctx, null, config);
566
578
  }
@@ -569,6 +581,12 @@ export class ConfigStore {
569
581
  if (this.name === '' && (this.type === 'sites' || this.type === 'profiles')) {
570
582
  return this.#list(ctx);
571
583
  }
584
+ if (relPath === 'versions') {
585
+ return this.listVersions(ctx);
586
+ }
587
+ if (relPath.startsWith('versions/')) {
588
+ return this.getVersion(ctx, Number.parseInt(relPath.substring('versions/'.length), 10));
589
+ }
572
590
 
573
591
  const storage = HelixStorage.fromContext(ctx).configBus();
574
592
  const buf = await storage.get(this.key);
@@ -584,10 +602,25 @@ export class ConfigStore {
584
602
  }
585
603
 
586
604
  async update(ctx, data, relPath = '') {
605
+ if (relPath.startsWith('versions/')) {
606
+ return this.#versioning.updateVersion(ctx, Number.parseInt(relPath.substring('versions/'.length), 10), data.name);
607
+ }
608
+ const frag = getFragmentInfo(this.type, relPath);
609
+ const { restoreVersion } = data ?? {};
610
+ if (restoreVersion && frag) {
611
+ throw new StatusCodeError(400, 'restoreVersion not allowed with object path.');
612
+ }
587
613
  const storage = HelixStorage.fromContext(ctx).configBus();
588
614
  const buf = await storage.get(this.key);
589
615
  let old = buf ? JSON.parse(buf) : null;
590
- const frag = getFragmentInfo(this.type, relPath);
616
+ let { versionName } = data ?? {};
617
+ delete data?.versionName;
618
+ if (restoreVersion) {
619
+ ctx.log.info(`restoring ${this.org}/${this.name} config from version ${restoreVersion}...`);
620
+ data = await this.#versioning.getVersion(ctx, restoreVersion, true);
621
+ versionName = `restored from version ${restoreVersion}`;
622
+ }
623
+
591
624
  let config = data;
592
625
  // set config to null if empty object
593
626
  if (isDeepEqual(config, {})) {
@@ -761,8 +794,21 @@ export class ConfigStore {
761
794
  updateCodeSource(ctx, config.code);
762
795
  }
763
796
 
764
- let oldConfig = buf ? JSON.parse(buf) : null;
765
- config.created = oldConfig?.created;
797
+ let oldConfig = null;
798
+ let modified = true;
799
+ if (buf) {
800
+ oldConfig = JSON.parse(buf);
801
+ modified = !isDeepEqual({
802
+ ...oldConfig,
803
+ lastModified: 'ignore',
804
+ }, {
805
+ ...config,
806
+ lastModified: 'ignore',
807
+ });
808
+ config.created = oldConfig.created;
809
+ } else if (!restoreVersion) {
810
+ delete config.created; // don't allow to set created on new config
811
+ }
766
812
  this.#updateTimeStamps(config);
767
813
  let newConfig = config;
768
814
  let purgeConfig = oldConfig;
@@ -776,12 +822,22 @@ export class ConfigStore {
776
822
 
777
823
  await this.validate(ctx, newConfig);
778
824
  await this.validatePermissions(ctx, oldConfig, newConfig);
825
+
826
+ if (modified) {
827
+ // Create a new version before saving only when modified
828
+ await this.#versioning.createVersion(ctx, config, versionName);
829
+ }
779
830
  await storage.put(this.key, JSON.stringify(config), 'application/json');
780
831
  await this.purge(ctx, purgeConfig, newConfig);
781
832
  return ret ?? redact(data, frag);
782
833
  }
783
834
 
784
835
  async remove(ctx, relPath) {
836
+ if (relPath.startsWith('versions/')) {
837
+ await this.deleteVersion(ctx, Number.parseInt(relPath.substring('versions/'.length), 10));
838
+ return;
839
+ }
840
+
785
841
  const storage = HelixStorage.fromContext(ctx).configBus();
786
842
  const buf = await storage.get(this.key);
787
843
  if (!buf) {
@@ -808,6 +864,35 @@ export class ConfigStore {
808
864
  await this.purge(ctx, oldConfig, null);
809
865
  }
810
866
 
867
+ /**
868
+ * List all versions for this config
869
+ * @param {Object} ctx - Context object
870
+ * @returns {Promise<Array>} Array of version information
871
+ */
872
+ async listVersions(ctx) {
873
+ return this.#versioning.listVersions(ctx);
874
+ }
875
+
876
+ /**
877
+ * Get a specific version of the config
878
+ * @param {Object} ctx - Context object
879
+ * @param {number} version - Version number to retrieve
880
+ * @returns {Promise<Object>} The versioned config data
881
+ */
882
+ async getVersion(ctx, version) {
883
+ return this.#versioning.getVersion(ctx, version);
884
+ }
885
+
886
+ /**
887
+ * Delete a specific version
888
+ * @param {Object} ctx - Context object
889
+ * @param {number} version - Version number to delete
890
+ * @returns {Promise<void>}
891
+ */
892
+ async deleteVersion(ctx, version) {
893
+ return this.#versioning.deleteVersion(ctx, version);
894
+ }
895
+
811
896
  // eslint-disable-next-line class-methods-use-this,no-unused-vars
812
897
  async purge(ctx, oldConfig, newConfig) {
813
898
  // override in subclass
@@ -0,0 +1,188 @@
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
+ import { HelixStorage } from '@adobe/helix-shared-storage';
14
+ import { StatusCodeError } from './status-code-error.js';
15
+
16
+ /**
17
+ * Version metadata structure
18
+ * @typedef {Object} VersionMetadata
19
+ * @property {number} version - Version number (auto-incrementing)
20
+ * @property {string} timestamp - ISO timestamp when version was created
21
+ * @property {string} [name] - Optional name/comment for the version
22
+ */
23
+
24
+ /**
25
+ * Version information returned by listVersions
26
+ * @typedef {Object} VersionInfo
27
+ * @property {number} version - Version number
28
+ * @property {string} timestamp - ISO timestamp when version was created
29
+ * @property {string} [name] - Optional name/comment for the version
30
+ * @property {boolean} isCurrent - Whether this is the current version
31
+ */
32
+
33
+ /**
34
+ * Config versioning manager
35
+ */
36
+ export class ConfigVersioning {
37
+ /**
38
+ * Create a new ConfigVersioning instance
39
+ * @param {string} org - Organization name
40
+ * @param {string} type - Config type (sites, profiles, org)
41
+ * @param {string} name - Config name
42
+ */
43
+ constructor(org, type = 'org', name = '') {
44
+ this.org = org;
45
+ this.type = type;
46
+ this.name = name;
47
+ this.prefix = type === 'org'
48
+ ? `orgs/${org}/versions`
49
+ : `orgs/${org}/${type}/${name}/versions`;
50
+ }
51
+
52
+ /**
53
+ * Create a new version of the config
54
+ * @param {Object} ctx - Context object
55
+ * @param {Object} config - The config data to version
56
+ * @param {string} [name] - Optional name for the version
57
+ * @returns {Promise<VersionMetadata>} Version metadata
58
+ */
59
+ async createVersion(ctx, config, name) {
60
+ const storage = HelixStorage.fromContext(ctx).configBus();
61
+
62
+ // Get current version number
63
+ const metadata = await this.getVersionMetadata(ctx);
64
+ const nextVersion = metadata.versions
65
+ .reduce((acc, current) => Math.max(acc, current.version), 0) + 1;
66
+
67
+ // eslint-disable-next-line no-param-reassign
68
+ config.version = nextVersion;
69
+
70
+ // Create version metadata
71
+ const versionMetadata = {
72
+ version: nextVersion,
73
+ created: new Date().toISOString(),
74
+ };
75
+ const meta = {
76
+ 'x-version': String(nextVersion),
77
+ };
78
+ if (name) {
79
+ versionMetadata.name = name;
80
+ meta['x-version-name'] = name;
81
+ }
82
+
83
+ // Store the versioned config
84
+ await storage.put(`${this.prefix}/${nextVersion}.json`, JSON.stringify(config, null, 2), 'application/json', meta, true);
85
+
86
+ // Update metadata
87
+ metadata.current = nextVersion;
88
+ metadata.versions.push(versionMetadata);
89
+
90
+ // Keep only the last 100 versions to prevent storage bloat
91
+ if (metadata.versions.length > 100) {
92
+ metadata.versions = metadata.versions.slice(-100);
93
+ }
94
+ await storage.put(`${this.prefix}.json`, JSON.stringify(metadata, null, 2), 'application/json');
95
+ return versionMetadata;
96
+ }
97
+
98
+ /**
99
+ * Update the name of a specific version
100
+ * @param ctx
101
+ * @param version
102
+ * @param name
103
+ * @returns {Promise<*>}
104
+ */
105
+ async updateVersion(ctx, version, name) {
106
+ const storage = HelixStorage.fromContext(ctx).configBus();
107
+ const metadata = await this.getVersionMetadata(ctx);
108
+ const versionInfo = metadata.versions.find((v) => v.version === version);
109
+ if (!versionInfo) {
110
+ throw new StatusCodeError(404, `Version ${version} not found.`);
111
+ }
112
+ versionInfo.name = name;
113
+ await storage.put(`${this.prefix}.json`, JSON.stringify(metadata, null, 2), 'application/json');
114
+ return versionInfo;
115
+ }
116
+
117
+ /**
118
+ * List all versions for this config
119
+ * @param {Object} ctx - Context object
120
+ * @returns {Promise<VersionInfo[]>} Array of version information
121
+ */
122
+ async listVersions(ctx) {
123
+ return this.getVersionMetadata(ctx);
124
+ }
125
+
126
+ /**
127
+ * Get a specific version of the config
128
+ * @param {Object} ctx - Context object
129
+ * @param {number} version - Version number to retrieve
130
+ * @param {boolean} [direct=false] - If true, fetch directly from storage without metadata
131
+ * @returns {Promise<Object>} The versioned config data
132
+ */
133
+ async getVersion(ctx, version, direct) {
134
+ const storage = HelixStorage.fromContext(ctx).configBus();
135
+ const versionKey = `${this.prefix}/${version}.json`;
136
+ const buf = await storage.get(versionKey);
137
+ if (!buf) {
138
+ throw new StatusCodeError(404, `Version ${version} not found.`);
139
+ }
140
+ if (direct) {
141
+ return JSON.parse(buf);
142
+ }
143
+
144
+ const metadata = await this.getVersionMetadata(ctx);
145
+ const versionInfo = metadata.versions.find((v) => v.version === version) || {};
146
+ return {
147
+ ...versionInfo,
148
+ data: JSON.parse(buf),
149
+ };
150
+ }
151
+
152
+ /**
153
+ * Delete a specific version
154
+ * @param {Object} ctx - Context object
155
+ * @param {number} version - Version number to delete
156
+ * @returns {Promise<void>}
157
+ */
158
+ async deleteVersion(ctx, version) {
159
+ const storage = HelixStorage.fromContext(ctx).configBus();
160
+
161
+ // Get metadata to check if this is the current version
162
+ const metadata = await this.getVersionMetadata(ctx);
163
+ if (version === metadata.current) {
164
+ throw new StatusCodeError(400, 'Cannot delete the current version.');
165
+ }
166
+
167
+ // Remove version from metadata
168
+ metadata.versions = metadata.versions.filter((v) => v.version !== version);
169
+ await storage.put(`${this.prefix}.json`, JSON.stringify(metadata, null, 2), 'application/json');
170
+
171
+ // Delete the versioned config
172
+ await storage.remove(`${this.prefix}/${version}.json`);
173
+ }
174
+
175
+ /**
176
+ * Get version metadata
177
+ * @param {Object} ctx - Context object
178
+ * @returns {Promise<Object>} Version metadata
179
+ */
180
+ async getVersionMetadata(ctx) {
181
+ const storage = HelixStorage.fromContext(ctx).configBus();
182
+ const buf = await storage.get(`${this.prefix}.json`);
183
+ if (!buf) {
184
+ return { current: 0, versions: [] };
185
+ }
186
+ return JSON.parse(buf);
187
+ }
188
+ }
package/src/index.js CHANGED
@@ -10,6 +10,7 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
  export { ConfigStore } from './config-store.js';
13
+ export { ConfigVersioning } from './config-versioning.js';
13
14
  export { SCHEMAS } from './config-validator.js';
14
15
  export * from './ValidationError.js';
15
16
  export * from './config-merge.js';
@@ -7,7 +7,6 @@
7
7
  "properties": {
8
8
  "version": {
9
9
  "type": "integer",
10
- "enum": [1],
11
10
  "default": 1
12
11
  },
13
12
  "created": { "$ref": "https://ns.adobe.com/helix/config/common#/definitions/created" },
@@ -7,7 +7,6 @@
7
7
  "properties": {
8
8
  "version": {
9
9
  "type": "integer",
10
- "enum": [1],
11
10
  "default": 1
12
11
  },
13
12
  "title": { "$ref": "https://ns.adobe.com/helix/config/common#/definitions/title" },
@@ -250,6 +250,10 @@
250
250
  "type": "string"
251
251
  },
252
252
  "description": "Additional hosts that are trusted to use the sidekick authentication"
253
+ },
254
+ "wordSaveDelay": {
255
+ "type": "number",
256
+ "description": "The delay to wait for Word to save the document before previewing (defaults to 1500)"
253
257
  }
254
258
  },
255
259
  "additionalProperties": false
@@ -7,7 +7,6 @@
7
7
  "properties": {
8
8
  "version": {
9
9
  "type": "integer",
10
- "enum": [1],
11
10
  "default": 1
12
11
  },
13
12
  "name": {