@microsoft/sp-module-interfaces 1.22.1 → 1.23.0-beta.0

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 (59) hide show
  1. package/dist/index-internal-beta.d.ts +20 -2
  2. package/dist/index-internal-public.d.ts +20 -2
  3. package/dist/index-internal.d.ts +728 -16
  4. package/lib-commonjs/manifestSchemaValidator.js +125 -25
  5. package/lib-commonjs/manifestSchemas/examples/commandSet_2.manifest.js +59 -0
  6. package/lib-commonjs/manifestSchemas/examples/commandSet_3.manifest.js +66 -0
  7. package/lib-commonjs/manifestSchemas/examples/commandSet_4.manifest.js +73 -0
  8. package/lib-commonjs/manifestSchemas/examples/commandSet_5.manifest.js +51 -0
  9. package/lib-commonjs/manifestSchemas/jsonSchemas/adaptive-card-extension-manifest.schema.json +1 -1
  10. package/lib-commonjs/manifestSchemas/jsonSchemas/any-value.schema.json +1 -1
  11. package/lib-commonjs/manifestSchemas/jsonSchemas/client-side-application-manifest.schema.json +1 -1
  12. package/lib-commonjs/manifestSchemas/jsonSchemas/client-side-assembly-manifest.schema.json +1 -1
  13. package/lib-commonjs/manifestSchemas/jsonSchemas/client-side-component-loader-configuration.schema.json +1 -2
  14. package/lib-commonjs/manifestSchemas/jsonSchemas/client-side-component-manifest.schema.json +1 -1
  15. package/lib-commonjs/manifestSchemas/jsonSchemas/client-side-extension-manifest.schema.json +1 -1
  16. package/lib-commonjs/manifestSchemas/jsonSchemas/client-side-library-manifest.schema.json +1 -1
  17. package/lib-commonjs/manifestSchemas/jsonSchemas/client-side-manifest-base.schema.json +1 -1
  18. package/lib-commonjs/manifestSchemas/jsonSchemas/client-side-multi-version-manifest.schema.json +1 -1
  19. package/lib-commonjs/manifestSchemas/jsonSchemas/client-side-web-part-manifest.schema.json +1 -1
  20. package/lib-commonjs/manifestSchemas/jsonSchemas/command-set-extension-manifest.schema.json +9 -3
  21. package/lib-commonjs/manifestSchemas/jsonSchemas/component-fallback-version.schema.json +1 -1
  22. package/lib-commonjs/manifestSchemas/jsonSchemas/guid.schema.json +1 -1
  23. package/lib-commonjs/manifestSchemas/jsonSchemas/localized-string.schema.json +1 -1
  24. package/lib-commonjs/manifestSchemas/jsonSchemas/prefab-app-manifest.schema.json +2 -2
  25. package/lib-commonjs/manifestSchemas/jsonSchemas/semver.schema.json +1 -1
  26. package/lib-commonjs/manifestSchemas/remote/site-design-script-actions.schema.json +4 -6
  27. package/lib-dts/manifestSchemaValidator.d.ts +16 -4
  28. package/lib-dts/manifestSchemas/ICommandSetExtensionManifest.d.ts +20 -2
  29. package/lib-dts/manifestSchemas/examples/commandSet_2.manifest.d.ts +8 -0
  30. package/lib-dts/manifestSchemas/examples/commandSet_3.manifest.d.ts +8 -0
  31. package/lib-dts/manifestSchemas/examples/commandSet_4.manifest.d.ts +8 -0
  32. package/lib-dts/manifestSchemas/examples/commandSet_5.manifest.d.ts +8 -0
  33. package/lib-dts/tsdoc-metadata.json +1 -1
  34. package/lib-esm/manifestSchemaValidator.js +126 -27
  35. package/lib-esm/manifestSchemas/examples/commandSet_2.manifest.js +49 -0
  36. package/lib-esm/manifestSchemas/examples/commandSet_3.manifest.js +56 -0
  37. package/lib-esm/manifestSchemas/examples/commandSet_4.manifest.js +63 -0
  38. package/lib-esm/manifestSchemas/examples/commandSet_5.manifest.js +41 -0
  39. package/lib-esm/manifestSchemas/jsonSchemas/adaptive-card-extension-manifest.schema.json +1 -1
  40. package/lib-esm/manifestSchemas/jsonSchemas/any-value.schema.json +1 -1
  41. package/lib-esm/manifestSchemas/jsonSchemas/client-side-application-manifest.schema.json +1 -1
  42. package/lib-esm/manifestSchemas/jsonSchemas/client-side-assembly-manifest.schema.json +1 -1
  43. package/lib-esm/manifestSchemas/jsonSchemas/client-side-component-loader-configuration.schema.json +1 -2
  44. package/lib-esm/manifestSchemas/jsonSchemas/client-side-component-manifest.schema.json +1 -1
  45. package/lib-esm/manifestSchemas/jsonSchemas/client-side-extension-manifest.schema.json +1 -1
  46. package/lib-esm/manifestSchemas/jsonSchemas/client-side-library-manifest.schema.json +1 -1
  47. package/lib-esm/manifestSchemas/jsonSchemas/client-side-manifest-base.schema.json +1 -1
  48. package/lib-esm/manifestSchemas/jsonSchemas/client-side-multi-version-manifest.schema.json +1 -1
  49. package/lib-esm/manifestSchemas/jsonSchemas/client-side-web-part-manifest.schema.json +1 -1
  50. package/lib-esm/manifestSchemas/jsonSchemas/command-set-extension-manifest.schema.json +9 -3
  51. package/lib-esm/manifestSchemas/jsonSchemas/component-fallback-version.schema.json +1 -1
  52. package/lib-esm/manifestSchemas/jsonSchemas/guid.schema.json +1 -1
  53. package/lib-esm/manifestSchemas/jsonSchemas/localized-string.schema.json +1 -1
  54. package/lib-esm/manifestSchemas/jsonSchemas/prefab-app-manifest.schema.json +2 -2
  55. package/lib-esm/manifestSchemas/jsonSchemas/semver.schema.json +1 -1
  56. package/lib-esm/manifestSchemas/remote/site-design-script-actions.schema.json +4 -6
  57. package/package.json +12 -14
  58. package/lib-commonjs/manifestSchemas/remote/draft-04.schema.json +0 -137
  59. package/lib-esm/manifestSchemas/remote/draft-04.schema.json +0 -137
@@ -26,36 +26,22 @@ function _interop_require_default(obj) {
26
26
  };
27
27
  }
28
28
  // Discover the schema files
29
+ // use internal site-design-script-actions schema for validation instead of remote URL
30
+ // as this schema is only used for Prefab, which is currently only internal. When we enable Prefab
31
+ // for external use, we can switch back to the remote URL with appropriate changes.
29
32
  const schemasDirectory = `${__dirname}/manifestSchemas/jsonSchemas`;
30
33
  const schemaFiles = _nodecorelibrary.FileSystem.readFolderItemNames(schemasDirectory);
31
34
  const schemas = schemaFiles.map((schemaFile)=>require(`${schemasDirectory}/${schemaFile}`));
32
- const SITE_SCRIPTS_SCHEMA_URL = 'https://developer.microsoft.com/json-schemas/sp/site-design-script-actions.schema.json';
33
- const siteScriptsSchemaPromise = fetch(SITE_SCRIPTS_SCHEMA_URL).then((response)=>{
34
- return response.text();
35
- }).catch((error)=>{
36
- // eslint-disable-next-line no-console
37
- console.error(`Failed to fetch remote schema: ${error}`);
38
- // fallback to local copy
39
- return JSON.stringify(_sitedesignscriptactionsschemajson.default);
40
- });
41
35
  const schemaValidator = new _ajv.default();
42
- siteScriptsSchemaPromise.then((remoteSchema)=>{
43
- schemaValidator.addSchema(JSON.parse(remoteSchema), SITE_SCRIPTS_SCHEMA_URL);
44
- schemas.forEach((schema, index)=>{
45
- // Use the schema's id property as the key to avoid "schema id ignored" warnings
46
- const schemaId = schema.id || `schema-${index}`;
47
- schemaValidator.addSchema(schema, schemaId);
48
- });
49
- }).catch((error)=>{
50
- // swallowing the error here
36
+ schemaValidator.addSchema(_sitedesignscriptactionsschemajson.default, _sitedesignscriptactionsschemajson.default.$id);
37
+ schemas.forEach((schema, index)=>{
38
+ // Use the schema's $id property as the key
39
+ const schemaId = schema.$id || `schema-${index}`;
40
+ schemaValidator.addSchema(schema, schemaId);
51
41
  });
52
42
  class ManifestValidator {
53
43
  static registerRemoteSchemaReferences() {
54
- return siteScriptsSchemaPromise.then(()=>{
55
- // no-op;
56
- }).catch(()=>{
57
- // no-op;
58
- });
44
+ return Promise.resolve();
59
45
  }
60
46
  static validateApplicationManifest(manifest) {
61
47
  return ManifestValidator._validateManifest(manifest, _clientsideapplicationmanifestschemajson.default);
@@ -64,7 +50,26 @@ class ManifestValidator {
64
50
  return ManifestValidator._validateManifest(manifest, _prefabappmanifestschemajson.default);
65
51
  }
66
52
  static validateCommandSetManifest(manifest) {
67
- return ManifestValidator._validateManifest(manifest, _commandsetextensionmanifestschemajson.default);
53
+ // First, run JSON Schema validation
54
+ const schemaValidationResult = ManifestValidator._validateManifest(manifest, _commandsetextensionmanifestschemajson.default);
55
+ // If schema validation failed, return early
56
+ if (!schemaValidationResult.result) {
57
+ return schemaValidationResult;
58
+ }
59
+ // Parse manifest if it's a string
60
+ const manifestObject = typeof manifest === 'string' ? JSON.parse(manifest) : manifest;
61
+ // Perform custom validation for command set specific rules
62
+ const customErrors = ManifestValidator._validateCommandSetHierarchy(manifestObject);
63
+ if (customErrors.length > 0) {
64
+ return {
65
+ result: false,
66
+ errors: [
67
+ ...schemaValidationResult.errors,
68
+ ...customErrors
69
+ ]
70
+ };
71
+ }
72
+ return schemaValidationResult;
68
73
  }
69
74
  static validateExtensionManifest(manifest) {
70
75
  return ManifestValidator._validateManifest(manifest, _clientsideextensionmanifestschemajson.default);
@@ -90,7 +95,7 @@ class ManifestValidator {
90
95
  const printAjvError = (error)=>{
91
96
  // AJV errors don't have nested inner errors
92
97
  // Each error is flat and represents a single validation failure
93
- const path = error.dataPath || error.schemaPath || 'root';
98
+ const path = error.instancePath || error.schemaPath || 'root';
94
99
  const message = error.message || 'Validation error';
95
100
  return [
96
101
  `(${path}) ${message}`
@@ -123,6 +128,101 @@ class ManifestValidator {
123
128
  errors: schemaValidator.errors || []
124
129
  };
125
130
  }
131
+ /**
132
+ * Validates command set hierarchy rules that cannot be expressed in JSON Schema:
133
+ * - Group references must point to existing items with type 'group'
134
+ * - Maximum nesting depth is 2 levels
135
+ * - No circular references in group hierarchy
136
+ */ static _validateCommandSetHierarchy(manifest) {
137
+ const errors = [];
138
+ const items = manifest.items;
139
+ if (!items) {
140
+ return errors;
141
+ }
142
+ const itemIds = Object.keys(items);
143
+ // Validate each item
144
+ for (const itemId of itemIds){
145
+ const item = items[itemId];
146
+ // Check if parent group exists and is a group
147
+ if (item.group) {
148
+ if (!items[item.group]) {
149
+ errors.push({
150
+ keyword: 'group-reference',
151
+ instancePath: `/items/${itemId}/group`,
152
+ schemaPath: '#/properties/items/patternProperties/group',
153
+ params: {
154
+ groupId: item.group
155
+ },
156
+ message: `Parent group '${item.group}' does not exist in manifest`
157
+ });
158
+ } else if (items[item.group].type !== 'group') {
159
+ errors.push({
160
+ keyword: 'group-type',
161
+ instancePath: `/items/${itemId}/group`,
162
+ schemaPath: '#/properties/items/patternProperties/group',
163
+ params: {
164
+ groupId: item.group
165
+ },
166
+ message: `Parent '${item.group}' is not a group (type must be 'group')`
167
+ });
168
+ }
169
+ }
170
+ // Check nesting depth and circular references for groups only
171
+ if (item.type === 'group') {
172
+ const visited = new Set();
173
+ const depth = ManifestValidator._getGroupDepth(itemId, items, visited);
174
+ if (depth === Infinity) {
175
+ errors.push({
176
+ keyword: 'circular-reference',
177
+ instancePath: `/items/${itemId}`,
178
+ schemaPath: '#/properties/items',
179
+ params: {
180
+ itemId
181
+ },
182
+ message: `Circular reference detected in group hierarchy for '${itemId}'`
183
+ });
184
+ } else if (depth > 2) {
185
+ errors.push({
186
+ keyword: 'max-depth',
187
+ instancePath: `/items/${itemId}`,
188
+ schemaPath: '#/properties/items',
189
+ params: {
190
+ depth,
191
+ itemId
192
+ },
193
+ message: `Group '${itemId}' has nesting depth ${depth}, which exceeds maximum of 2`
194
+ });
195
+ }
196
+ }
197
+ }
198
+ return errors;
199
+ }
200
+ /**
201
+ * Calculates the depth of an item in the group hierarchy.
202
+ * Returns Infinity if a circular reference is detected.
203
+ */ static _getGroupDepth(itemId, items, visited) {
204
+ // Circular reference detection
205
+ if (visited.has(itemId)) {
206
+ return Infinity;
207
+ }
208
+ const item = items[itemId];
209
+ if (!item) {
210
+ return 0;
211
+ }
212
+ // If no parent group, depth is 1
213
+ if (!item.group) {
214
+ return 1;
215
+ }
216
+ // If parent doesn't exist, treat as depth 1
217
+ if (!items[item.group]) {
218
+ return 1;
219
+ }
220
+ // Mark as visited before recursing
221
+ visited.add(itemId);
222
+ // Recursively calculate parent's depth and add 1
223
+ const parentDepth = ManifestValidator._getGroupDepth(item.group, items, visited);
224
+ return parentDepth + 1;
225
+ }
126
226
  }
127
227
 
128
228
  //# sourceMappingURL=./manifestSchemaValidator.js.map
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "default", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return _default;
9
+ }
10
+ });
11
+ /**
12
+ * Example manifest with single-level groups.
13
+ * Commands are organized under a parent group.
14
+ */ const extensionManifest = {
15
+ manifestVersion: 2,
16
+ componentType: 'Extension',
17
+ id: '00000000-0000-0000-0000-000000000001',
18
+ alias: 'singleLevelGroupExtension',
19
+ version: '0.0.1',
20
+ extensionType: 'ListViewCommandSet',
21
+ items: {
22
+ EXPORT_GROUP: {
23
+ title: {
24
+ default: 'Export'
25
+ },
26
+ type: 'group',
27
+ iconImageUrl: 'icons/export.png'
28
+ },
29
+ EXPORT_PDF: {
30
+ title: {
31
+ default: 'Export to PDF'
32
+ },
33
+ type: 'command',
34
+ group: 'EXPORT_GROUP'
35
+ },
36
+ EXPORT_EXCEL: {
37
+ title: {
38
+ default: 'Export to Excel'
39
+ },
40
+ type: 'command',
41
+ group: 'EXPORT_GROUP'
42
+ }
43
+ },
44
+ loaderConfig: {
45
+ internalModuleBaseUrls: [
46
+ 'https://cdn.net/'
47
+ ],
48
+ entryModuleId: 'main.bundle',
49
+ scriptResources: {
50
+ 'main.bundle': {
51
+ type: 'path',
52
+ path: 'bundle.script.js'
53
+ }
54
+ }
55
+ }
56
+ };
57
+ const _default = extensionManifest;
58
+
59
+ //# sourceMappingURL=./commandSet_2.manifest.js.map
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "default", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return _default;
9
+ }
10
+ });
11
+ /**
12
+ * Example manifest with nested groups (2 levels).
13
+ * Demonstrates a parent group containing a child group with commands.
14
+ */ const extensionManifest = {
15
+ manifestVersion: 2,
16
+ componentType: 'Extension',
17
+ id: '00000000-0000-0000-0000-000000000002',
18
+ alias: 'nestedGroupExtension',
19
+ version: '0.0.1',
20
+ extensionType: 'ListViewCommandSet',
21
+ items: {
22
+ TOOLS_GROUP: {
23
+ title: {
24
+ default: 'Tools'
25
+ },
26
+ type: 'group',
27
+ iconImageUrl: 'icons/tools.png'
28
+ },
29
+ ANALYSIS_GROUP: {
30
+ title: {
31
+ default: 'Analysis'
32
+ },
33
+ type: 'group',
34
+ group: 'TOOLS_GROUP'
35
+ },
36
+ RUN_ANALYSIS: {
37
+ title: {
38
+ default: 'Run Analysis'
39
+ },
40
+ type: 'command',
41
+ group: 'ANALYSIS_GROUP'
42
+ },
43
+ VIEW_REPORT: {
44
+ title: {
45
+ default: 'View Report'
46
+ },
47
+ type: 'command',
48
+ group: 'ANALYSIS_GROUP'
49
+ }
50
+ },
51
+ loaderConfig: {
52
+ internalModuleBaseUrls: [
53
+ 'https://cdn.net/'
54
+ ],
55
+ entryModuleId: 'main.bundle',
56
+ scriptResources: {
57
+ 'main.bundle': {
58
+ type: 'path',
59
+ path: 'bundle.script.js'
60
+ }
61
+ }
62
+ }
63
+ };
64
+ const _default = extensionManifest;
65
+
66
+ //# sourceMappingURL=./commandSet_3.manifest.js.map
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "default", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return _default;
9
+ }
10
+ });
11
+ /**
12
+ * Example manifest with mixed structure.
13
+ * Demonstrates top-level commands alongside grouped commands.
14
+ */ const extensionManifest = {
15
+ manifestVersion: 2,
16
+ componentType: 'Extension',
17
+ id: '00000000-0000-0000-0000-000000000003',
18
+ alias: 'mixedStructureExtension',
19
+ version: '0.0.1',
20
+ extensionType: 'ListViewCommandSet',
21
+ items: {
22
+ QUICK_VIEW: {
23
+ title: {
24
+ default: 'Quick View'
25
+ },
26
+ type: 'command',
27
+ iconImageUrl: 'icons/quickview.png'
28
+ },
29
+ SHARE_GROUP: {
30
+ title: {
31
+ default: 'Share'
32
+ },
33
+ type: 'group',
34
+ iconImageUrl: 'icons/share.png'
35
+ },
36
+ SHARE_EMAIL: {
37
+ title: {
38
+ default: 'Share via Email'
39
+ },
40
+ type: 'command',
41
+ group: 'SHARE_GROUP'
42
+ },
43
+ SHARE_LINK: {
44
+ title: {
45
+ default: 'Copy Link'
46
+ },
47
+ type: 'command',
48
+ group: 'SHARE_GROUP'
49
+ },
50
+ SETTINGS: {
51
+ title: {
52
+ default: 'Settings'
53
+ },
54
+ type: 'command',
55
+ iconImageUrl: 'icons/settings.png'
56
+ }
57
+ },
58
+ loaderConfig: {
59
+ internalModuleBaseUrls: [
60
+ 'https://cdn.net/'
61
+ ],
62
+ entryModuleId: 'main.bundle',
63
+ scriptResources: {
64
+ 'main.bundle': {
65
+ type: 'path',
66
+ path: 'bundle.script.js'
67
+ }
68
+ }
69
+ }
70
+ };
71
+ const _default = extensionManifest;
72
+
73
+ //# sourceMappingURL=./commandSet_4.manifest.js.map
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "default", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return _default;
9
+ }
10
+ });
11
+ /**
12
+ * Example manifest with an empty group (no children).
13
+ * Empty groups are valid at schema level - they simply won't render at runtime.
14
+ */ const extensionManifest = {
15
+ manifestVersion: 2,
16
+ componentType: 'Extension',
17
+ id: '00000000-0000-0000-0000-000000000004',
18
+ alias: 'emptyGroupExtension',
19
+ version: '0.0.1',
20
+ extensionType: 'ListViewCommandSet',
21
+ items: {
22
+ EMPTY_GROUP: {
23
+ title: {
24
+ default: 'Empty Group'
25
+ },
26
+ type: 'group'
27
+ },
28
+ STANDALONE_COMMAND: {
29
+ title: {
30
+ default: 'Standalone Command'
31
+ },
32
+ type: 'command',
33
+ iconImageUrl: 'icons/standalone.png'
34
+ }
35
+ },
36
+ loaderConfig: {
37
+ internalModuleBaseUrls: [
38
+ 'https://cdn.net/'
39
+ ],
40
+ entryModuleId: 'main.bundle',
41
+ scriptResources: {
42
+ 'main.bundle': {
43
+ type: 'path',
44
+ path: 'bundle.script.js'
45
+ }
46
+ }
47
+ }
48
+ };
49
+ const _default = extensionManifest;
50
+
51
+ //# sourceMappingURL=./commandSet_5.manifest.js.map
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "adaptive-card-extension-manifest.schema.json",
2
+ "$id": "adaptive-card-extension-manifest.schema.json",
3
3
  "title": "Adaptive Card Extension manifest",
4
4
  "description": "All Adaptive Card Extension built on the SharePoint framework need a valid component manifest. This interface represents properties that are required by all types of BaseAdaptiveCardExtension. Adaptive Card Extension specific manifests will extend this interface to add properties required by that component type.",
5
5
  "definitions": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "any-value.schema.json",
2
+ "$id": "any-value.schema.json",
3
3
 
4
4
  "type": ["array", "boolean", "integer", "number", "object", "string"],
5
5
  "items": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "client-side-application-manifest.schema.json",
2
+ "$id": "client-side-application-manifest.schema.json",
3
3
  "title": "Client-side application manifest",
4
4
  "description": "A client-side application is the architectural component that manages the appearance and behavior of an entire web page.",
5
5
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "client-side-assembly-manifest.schema.json",
2
+ "$id": "client-side-assembly-manifest.schema.json",
3
3
  "title": "Client-side assembly manifest",
4
4
  "description": "A client-side assembly is a standalone-executable bundle containing a component, the loader, and all of their dependencies.",
5
5
 
@@ -1,11 +1,10 @@
1
1
  {
2
- "id": "client-side-component-loader-configuration.schema.json",
2
+ "$id": "client-side-component-loader-configuration.schema.json",
3
3
  "title": "Client-side component manifest",
4
4
  "description": "All client side components built on the SharePoint framework need a valid component manifest. This interface represents properties that are required by all types of client side components like Applications and Web Parts. Component specific manifests will extend this interface to add properties required by that component type.",
5
5
 
6
6
  "definitions": {
7
7
  "moduleConfiguration": {
8
- "id": "moduleConfiguration",
9
8
  "type": "object",
10
9
  "title": "Module Configuration",
11
10
  "description": "This is the base interface for a script module's definition.",
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "client-side-component-manifest.schema.json",
2
+ "$id": "client-side-component-manifest.schema.json",
3
3
  "title": "Client-side component manifest",
4
4
  "description": "All client side components built on the SharePoint framework need a valid component manifest. This interface represents properties that are required by all types of client side components like Applications and Web Parts. Component specific manifests will extend this interface to add properties required by that component type.",
5
5
  "definitions": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "client-side-extension-manifest.schema.json",
2
+ "$id": "client-side-extension-manifest.schema.json",
3
3
  "title": "Client-side extension manifest",
4
4
  "description": "A client-side extension is a plug-in component that customizes or augments the functionality of a client-side application.",
5
5
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "client-side-library-manifest.schema.json",
2
+ "$id": "client-side-library-manifest.schema.json",
3
3
  "title": "Client-side library manifest",
4
4
  "description": "A client-side library is a library containing reusable JavaScript code and/or resources.",
5
5
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "client-side-manifest-base.schema.json",
2
+ "$id": "client-side-manifest-base.schema.json",
3
3
  "title": "Client-side component manifest base",
4
4
  "description": "Properties common to all deployable manifests.",
5
5
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "client-side-multi-version-manifest.schema.json",
2
+ "$id": "client-side-multi-version-manifest.schema.json",
3
3
  "title": "Client-side multi-version manifest",
4
4
  "description": "Multi-version manifests are defined by this schema.",
5
5
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "client-side-web-part-manifest.schema.json",
2
+ "$id": "client-side-web-part-manifest.schema.json",
3
3
  "title": "Client-side webpart manifest",
4
4
  "description": "A client-side webpart is a drop-in control that is part of the end user page authoring experience.",
5
5
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "command-set-extension-manifest.schema.json",
2
+ "$id": "command-set-extension-manifest.schema.json",
3
3
  "title": "Command set extension manifest",
4
4
  "description": "A client-side extension that defines a set of custom commands that can be shown in a menu, tool bar, etc.",
5
5
 
@@ -58,9 +58,9 @@
58
58
  },
59
59
  "type": {
60
60
  "type": "string",
61
- "enum": ["command"],
61
+ "enum": ["command", "group"],
62
62
  "title": "Type",
63
- "description": "Type of the item. Currently only \"command\" is allowed."
63
+ "description": "Type of the item. Use 'command' for executable commands, or 'group' for containers that organize commands into submenus."
64
64
  },
65
65
  "ariaLabel": {
66
66
  "title": "ARIA Label",
@@ -72,6 +72,12 @@
72
72
  "minLength": 1,
73
73
  "title": "Icon Image URL",
74
74
  "description": "An optional URL for an image to be displayed next to the item. The requirements for this image are defined by the type of extension; some extension types may not display the image at all."
75
+ },
76
+ "group": {
77
+ "type": "string",
78
+ "pattern": "^[A-Z0-9_]+$",
79
+ "title": "Group",
80
+ "description": "Optional ID of a parent group item this command/group belongs to. The parent must be defined in the same manifest with type 'group'. Maximum nesting depth is 2 levels."
75
81
  }
76
82
  },
77
83
  "additionalProperties": false
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "component-fallback-version.schema.json",
2
+ "$id": "component-fallback-version.schema.json",
3
3
  "title": "Component Fallback Version",
4
4
  "description": "Represents a fallback version of a component that can be used under certain logical conditions, including flight and killswitch checks.",
5
5
  "type": "object",
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "guid.schema.json",
2
+ "$id": "guid.schema.json",
3
3
 
4
4
  "type": "string",
5
5
  "minLength": 1,
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "localized-string.schema.json",
2
+ "$id": "localized-string.schema.json",
3
3
 
4
4
  "type": "object",
5
5
  "oneOf": [
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "prefab-app-manifest.schema.json",
2
+ "$id": "prefab-app-manifest.schema.json",
3
3
  "title": "PREFAB application manifest",
4
4
  "description": "A PREFAB application is the architectural component that allows developers to declaratively specify behavior of a full page application.",
5
5
 
@@ -17,7 +17,7 @@
17
17
  "enum": ["onInstall", "onDemand"]
18
18
  },
19
19
  "actions": {
20
- "$ref": "https://developer.microsoft.com/json-schemas/sp/site-design-script-actions.schema.json#/properties/actions"
20
+ "$ref": "remote/site-design-script-actions.schema.json#/properties/actions"
21
21
  }
22
22
  },
23
23
  "required": ["type", "actions"],
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "semver.schema.json",
2
+ "$id": "semver.schema.json",
3
3
 
4
4
  "type": "string",
5
5
  "minLength": 5,
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "remote/site-design-script-actions.schema.json",
3
4
  "title": "JSON Schema for SiteScript Extensions",
4
5
  "definitions": {
5
6
  "activateSPFeature": {
@@ -197,8 +198,7 @@
197
198
  "$ref": "#/definitions/SPListSubactions/associateListViewCommandSet"
198
199
  }
199
200
  ]
200
- },
201
- "additionalItems": false
201
+ }
202
202
  }
203
203
  },
204
204
  "required": ["listName", "templateType", "verb"],
@@ -884,8 +884,7 @@
884
884
  "$ref": "#/definitions/SPContentTypeSubactions/removeSiteColumn"
885
885
  }
886
886
  ]
887
- },
888
- "additionalItems": false
887
+ }
889
888
  }
890
889
  },
891
890
  "required": ["verb", "name", "hidden"],
@@ -1147,8 +1146,7 @@
1147
1146
  "$ref": "#/definitions/setFooterLogo"
1148
1147
  }
1149
1148
  ]
1150
- },
1151
- "additionalItems": false
1149
+ }
1152
1150
  },
1153
1151
  "$schema": {
1154
1152
  "type": "string"