@hubspot/project-parsing-lib 0.17.0-beta.0 → 0.18.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/project-parsing-lib",
3
- "version": "0.17.0-beta.0",
3
+ "version": "0.18.0-beta.0",
4
4
  "description": "Parsing library for converting projects directory structures to their intermediate representation",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,4 +1,4 @@
1
1
  export { getProjectMetadata } from '../lib/project.js';
2
2
  export { projectContainsHsMetaFiles } from '../lib/files.js';
3
3
  export type { ProjectMetadata } from '../lib/project.js';
4
- export { LATEST_SUPPORTED_PLATFORM_VERSION, isSupportedPlatformVersion, isLegacyProject, } from '../lib/platformVersion.js';
4
+ export { LATEST_SUPPORTED_PLATFORM_VERSION, isSupportedPlatformVersion, isLegacyProject, supportsComponentPermissions, } from '../lib/platformVersion.js';
@@ -1,3 +1,3 @@
1
1
  export { getProjectMetadata } from '../lib/project.js';
2
2
  export { projectContainsHsMetaFiles } from '../lib/files.js';
3
- export { LATEST_SUPPORTED_PLATFORM_VERSION, isSupportedPlatformVersion, isLegacyProject, } from '../lib/platformVersion.js';
3
+ export { LATEST_SUPPORTED_PLATFORM_VERSION, isSupportedPlatformVersion, isLegacyProject, supportsComponentPermissions, } from '../lib/platformVersion.js';
@@ -1,3 +1,3 @@
1
1
  export { translate, translateForLocalDev } from '../lib/translate.js';
2
2
  export { isTranslationError, type TranslationError } from '../lib/errors.js';
3
- export type { IntermediateRepresentation, IntermediateRepresentationLocalDev, IntermediateRepresentationNodeLocalDev, IntermediateRepresentationNode, TranslationContext, TranslationOptions, TranslationOptionsLocalDev, ComponentMeta, } from '../lib/types.js';
3
+ export type { IntermediateRepresentation, IntermediateRepresentationLocalDev, IntermediateRepresentationNodeLocalDev, IntermediateRepresentationNode, TranslationContext, TranslationOptions, TranslationOptionsLocalDev, ComponentMeta, ComponentPermissions, } from '../lib/types.js';
@@ -33,6 +33,7 @@ export declare const errorMessages: {
33
33
  unsupportedType: (type: string) => string;
34
34
  errorWithField: (field: string, error: string | undefined) => string;
35
35
  invalidJson: string;
36
+ permissionsNotSupported: (platformVersion: string) => string;
36
37
  unexpectedToplevelFields: (unexpectedFields: string) => string;
37
38
  wrongDirectoryForComponent: (directory: string, componentType: string, componentMetadata: ComponentMetadata, correctDir: string) => string;
38
39
  };
package/src/lang/copy.js CHANGED
@@ -34,6 +34,7 @@ export const errorMessages = {
34
34
  unsupportedType: (type) => `Unsupported type: ${mapToUserFacingType(type)}`,
35
35
  errorWithField: (field, error) => `${field}: ${error || 'Unknown error'}`,
36
36
  invalidJson: 'Invalid JSON',
37
+ permissionsNotSupported: (platformVersion) => `The "permissions" field is not supported on platform version "${platformVersion}". Upgrade to "2026.09" or later to use component-level permissions.`,
37
38
  unexpectedToplevelFields: (unexpectedFields) => `Unexpected top-level properties found: ${unexpectedFields}`,
38
39
  wrongDirectoryForComponent: (directory, componentType, componentMetadata, correctDir) => `The directory '${directory}' is incorrect for type '${componentType}'. ${componentMetadata.userFriendlyName} ${componentMetadata.userFriendlyTypePlural} should only be placed in the '${correctDir}' directory`,
39
40
  },
@@ -231,4 +231,5 @@ export const ALLOWED_TOP_LEVEL_FIELDS = new Set([
231
231
  'type',
232
232
  'config',
233
233
  'dependencies',
234
+ 'permissions',
234
235
  ]);
@@ -1,3 +1,4 @@
1
1
  export declare const LATEST_SUPPORTED_PLATFORM_VERSION: string;
2
2
  export declare function isSupportedPlatformVersion(platformVersion?: string | null): boolean;
3
3
  export declare function isLegacyProject(platformVersion?: string | null): boolean;
4
+ export declare function supportsComponentPermissions(platformVersion: string): boolean;
@@ -18,3 +18,13 @@ export function isLegacyProject(platformVersion) {
18
18
  return false;
19
19
  return [PLATFORM_VERSIONS.v2023_2, PLATFORM_VERSIONS.v2025_1].includes(platformVersion);
20
20
  }
21
+ const PRE_PERMISSIONS_VERSIONS = new Set([
22
+ PLATFORM_VERSIONS.v2023_2,
23
+ PLATFORM_VERSIONS.v2025_1,
24
+ PLATFORM_VERSIONS.v2025_2,
25
+ PLATFORM_VERSIONS.v2026_03_BETA,
26
+ PLATFORM_VERSIONS.v2026_03,
27
+ ]);
28
+ export function supportsComponentPermissions(platformVersion) {
29
+ return !PRE_PERMISSIONS_VERSIONS.has(platformVersion);
30
+ }
@@ -7,11 +7,13 @@ import { errorMessages } from '../lang/copy.js';
7
7
  import { getJavaNumberType } from './utils.js';
8
8
  import { convertPathToPosixPath } from './files.js';
9
9
  function calculateComponentDeps(fileValidationResult, parentComponents, appObjects, appFunctionsPackageUid) {
10
- let dependencies = {};
11
- // If there are dependencies in the config file, pass them through
12
- if (!fileValidationResult.content?.dependencies) {
13
- dependencies = { ...fileValidationResult.content?.dependencies };
14
- }
10
+ // Pass through any developer-authored dependencies declared in the hsmeta
11
+ // file. Framework-injected structural dependencies (app, allAppObjects,
12
+ // serverlessPackage) are layered on top below and take precedence over any
13
+ // authored value for those reserved keys.
14
+ const dependencies = {
15
+ ...fileValidationResult.content?.dependencies,
16
+ };
15
17
  const { type } = fileValidationResult.content;
16
18
  // If the component is a child of the App component, add the app and appObjects as dependencies
17
19
  if (Components[type]?.parentComponent === APP_KEY) {
@@ -95,7 +97,7 @@ export function transform(fileParseResults, translationContext, hsProfileContent
95
97
  fileParseResult: currentFile,
96
98
  };
97
99
  }
98
- const { config, uid, type } = currentFile.content;
100
+ const { config, uid, type, permissions } = currentFile.content;
99
101
  const { dependencies, errors } = calculateComponentDeps(currentFile, parentComponents, allAppObjects, serverlessPackageUid);
100
102
  if (errors) {
101
103
  currentFile.errors?.push(...errors);
@@ -108,6 +110,7 @@ export function transform(fileParseResults, translationContext, hsProfileContent
108
110
  componentDeps: dependencies,
109
111
  metaFilePath: convertPathToPosixPath(currentFile.file),
110
112
  files: {},
113
+ ...(permissions ? { permissions } : {}),
111
114
  },
112
115
  fileParseResult: currentFile,
113
116
  };
@@ -1,10 +1,16 @@
1
1
  import type { ErrorObject } from 'ajv';
2
2
  export type Dependencies = Record<string, string | string[]>;
3
+ export interface ComponentPermissions {
4
+ requiredScopes?: string[];
5
+ optionalScopes?: string[];
6
+ conditionallyRequiredScopes?: string[];
7
+ }
3
8
  export interface ComponentMeta {
4
9
  uid: string;
5
10
  type: string;
6
11
  config: unknown;
7
12
  dependencies?: Dependencies;
13
+ permissions?: ComponentPermissions;
8
14
  }
9
15
  export interface FileActionResult {
10
16
  file: string;
@@ -24,6 +30,7 @@ export interface IntermediateRepresentationNode {
24
30
  uid: string;
25
31
  config: unknown;
26
32
  files: unknown;
33
+ permissions?: ComponentPermissions;
27
34
  }
28
35
  export type BEProfileVariableType = 'PROFILE_INT' | 'PROFILE_LONG' | 'PROFILE_STRING' | 'PROFILE_BOOLEAN';
29
36
  export interface BEProfileVariables {
@@ -5,6 +5,7 @@ import { errorMessages } from '../lang/copy.js';
5
5
  import { AUTO_GENERATED_COMPONENT_TYPES, Components } from './constants.js';
6
6
  import path from 'path';
7
7
  import { findTransformationByUid, mapToUserFacingType } from './transform.js';
8
+ import { supportsComponentPermissions } from './platformVersion.js';
8
9
  import { validateUid } from './uid.js';
9
10
  // ajv-formats does not play nicely with ESM and TS
10
11
  import addFormatsModule from 'ajv-formats';
@@ -47,6 +48,10 @@ function validateIntermediateRepresentationNode(schema, transformation, irNode,
47
48
  // If there is no config block, there is nothing to validation
48
49
  shouldSkipValidation = true;
49
50
  }
51
+ if (irNode.permissions &&
52
+ !supportsComponentPermissions(translationContext.platformVersion)) {
53
+ transformation.fileParseResult.errors.push(errorMessages.validation.permissionsNotSupported(translationContext.platformVersion));
54
+ }
50
55
  if (!schema[irNode.componentType]) {
51
56
  if (!irNode.componentType) {
52
57
  transformation.fileParseResult.errors.push(errorMessages.validation.missingType);