@syncbridge/common 0.4.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 (87) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +1 -0
  3. package/classes/base-element.d.ts +46 -0
  4. package/classes/base-element.js +80 -0
  5. package/classes/component-base.d.ts +26 -0
  6. package/classes/component-base.js +12 -0
  7. package/classes/frame-stream.d.ts +34 -0
  8. package/classes/frame-stream.js +140 -0
  9. package/classes/processor-base.d.ts +15 -0
  10. package/classes/processor-base.js +8 -0
  11. package/classes/runnable.d.ts +86 -0
  12. package/classes/runnable.js +228 -0
  13. package/classes/sb-error.d.ts +11 -0
  14. package/classes/sb-error.js +54 -0
  15. package/classes/stack-executor.d.ts +10 -0
  16. package/classes/stack-executor.js +37 -0
  17. package/constants.d.ts +6 -0
  18. package/constants.js +17 -0
  19. package/decorators/component.decorator.d.ts +15 -0
  20. package/decorators/component.decorator.js +28 -0
  21. package/decorators/decorator-helpers.d.ts +2 -0
  22. package/decorators/decorator-helpers.js +13 -0
  23. package/decorators/define-variable.decorator.d.ts +14 -0
  24. package/decorators/define-variable.decorator.js +57 -0
  25. package/decorators/finalize-element-metadata.d.ts +4 -0
  26. package/decorators/finalize-element-metadata.js +30 -0
  27. package/decorators/processor-events.decorator.d.ts +5 -0
  28. package/decorators/processor-events.decorator.js +21 -0
  29. package/decorators/processor.decorator.d.ts +31 -0
  30. package/decorators/processor.decorator.js +42 -0
  31. package/decorators/use-component.decorator.d.ts +9 -0
  32. package/decorators/use-component.decorator.js +24 -0
  33. package/decorators/use-variables.decorator.d.ts +5 -0
  34. package/decorators/use-variables.decorator.js +20 -0
  35. package/index.d.ts +20 -0
  36. package/index.js +20 -0
  37. package/interfaces/extension-package.interface.d.ts +6 -0
  38. package/interfaces/extension-package.interface.js +1 -0
  39. package/interfaces/index.d.ts +2 -0
  40. package/interfaces/index.js +2 -0
  41. package/interfaces/logger.interface.d.ts +17 -0
  42. package/interfaces/logger.interface.js +1 -0
  43. package/models/enums/log-level.d.ts +9 -0
  44. package/models/enums/log-level.js +15 -0
  45. package/models/enums/service-status.d.ts +8 -0
  46. package/models/enums/service-status.js +14 -0
  47. package/models/enums/variable-format.enum.d.ts +9 -0
  48. package/models/enums/variable-format.enum.js +15 -0
  49. package/models/enums/variable-type.enum.d.ts +8 -0
  50. package/models/enums/variable-type.enum.js +14 -0
  51. package/models/index.d.ts +13 -0
  52. package/models/index.js +13 -0
  53. package/models/metadata/author-metadata.d.ts +8 -0
  54. package/models/metadata/author-metadata.js +48 -0
  55. package/models/metadata/component-metadata.d.ts +8 -0
  56. package/models/metadata/component-metadata.js +25 -0
  57. package/models/metadata/element-component-metadata.d.ts +19 -0
  58. package/models/metadata/element-component-metadata.js +70 -0
  59. package/models/metadata/element-metadata.d.ts +18 -0
  60. package/models/metadata/element-metadata.js +81 -0
  61. package/models/metadata/package-metadata.d.ts +7 -0
  62. package/models/metadata/package-metadata.js +27 -0
  63. package/models/metadata/processor-metadata.d.ts +5 -0
  64. package/models/metadata/processor-metadata.js +14 -0
  65. package/models/metadata/variable-metadata.d.ts +31 -0
  66. package/models/metadata/variable-metadata.js +138 -0
  67. package/models/profile/log-options.d.ts +16 -0
  68. package/models/profile/log-options.js +56 -0
  69. package/models/profile/profile-component.d.ts +23 -0
  70. package/models/profile/profile-component.js +77 -0
  71. package/models/profile/profile.d.ts +25 -0
  72. package/models/profile/profile.js +115 -0
  73. package/models-document.d.ts +3 -0
  74. package/models-document.js +15 -0
  75. package/package.json +33 -0
  76. package/processor-factory.d.ts +13 -0
  77. package/processor-factory.js +106 -0
  78. package/registry.d.ts +32 -0
  79. package/registry.js +205 -0
  80. package/utils/encrypt-helpers.d.ts +2 -0
  81. package/utils/encrypt-helpers.js +43 -0
  82. package/utils/make-extension-package.d.ts +3 -0
  83. package/utils/make-extension-package.js +13 -0
  84. package/utils/metadata-utils.d.ts +7 -0
  85. package/utils/metadata-utils.js +99 -0
  86. package/utils/profile-utils.d.ts +21 -0
  87. package/utils/profile-utils.js +222 -0
package/registry.js ADDED
@@ -0,0 +1,205 @@
1
+ import * as fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import process from 'node:process';
4
+ import { fileURLToPath, pathToFileURL } from 'node:url';
5
+ import { deepClone, isPlainObject } from '@jsopen/objects';
6
+ import { COMPONENT_OPTIONS, PROCESSOR_OPTIONS } from './constants.js';
7
+ import { isExtensionPackage } from './utils/make-extension-package.js';
8
+ export var ExtensionRegistry;
9
+ (function (ExtensionRegistry) {
10
+ let _resolver = import.meta.resolve;
11
+ ExtensionRegistry.packages = new Map();
12
+ ExtensionRegistry.components = new Map();
13
+ ExtensionRegistry.processors = new Map();
14
+ function setResolver(resolver) {
15
+ _resolver = resolver;
16
+ }
17
+ ExtensionRegistry.setResolver = setResolver;
18
+ function getProcessor(className) {
19
+ const rec = ExtensionRegistry.processors.get(className);
20
+ if (!rec) {
21
+ throw new Error(`Processor "${className}" not found in registry`);
22
+ }
23
+ return rec;
24
+ }
25
+ ExtensionRegistry.getProcessor = getProcessor;
26
+ function getComponent(className) {
27
+ const rec = ExtensionRegistry.components.get(className);
28
+ if (!rec) {
29
+ throw new Error(`Component "${className}" not found in registry`);
30
+ }
31
+ return rec;
32
+ }
33
+ ExtensionRegistry.getComponent = getComponent;
34
+ async function registerPackage(...specifiers) {
35
+ const errors = [];
36
+ await Promise.all(specifiers.map(specifier => _registerPackage(specifier).catch(err => {
37
+ errors.push(`\n ${specifier}: ${err.message}`);
38
+ })));
39
+ if (errors.length) {
40
+ console.error(`Failed to register extensions:${errors}`);
41
+ }
42
+ }
43
+ ExtensionRegistry.registerPackage = registerPackage;
44
+ async function _registerPackage(specifier, options) {
45
+ let packagePath = fileURLToPath(_resolver(specifier));
46
+ const json = locatePkgJson(path.dirname(packagePath));
47
+ if (!json) {
48
+ if (options?.ignoreInvalid)
49
+ return;
50
+ throw new TypeError(`Can't locate package.json file for "${specifier}"`);
51
+ }
52
+ if (path.isAbsolute(packagePath) && process.platform === 'win32') {
53
+ packagePath = pathToFileURL(packagePath).href;
54
+ }
55
+ const pkg = (await import(packagePath)).default;
56
+ if (!isExtensionPackage(pkg)) {
57
+ if (options?.ignoreInvalid)
58
+ return;
59
+ throw new TypeError(`"${specifier}" do not export ExtensionPackage interface`);
60
+ }
61
+ // ignore if already registered
62
+ if (ExtensionRegistry.packages.get(json.name))
63
+ return;
64
+ // Register dependencies
65
+ if (pkg.dependencies) {
66
+ for (const dep of pkg.dependencies)
67
+ await _registerPackage(dep, { ignoreInvalid: true });
68
+ }
69
+ const packageRec = Object.freeze({
70
+ name: json.name,
71
+ version: json.version,
72
+ description: json.description,
73
+ });
74
+ ExtensionRegistry.packages.set(packageRec.name, packageRec);
75
+ if (Array.isArray(pkg.components)) {
76
+ for (const ctor of pkg.components) {
77
+ await registerComponent(ctor, packageRec.name);
78
+ }
79
+ }
80
+ if (Array.isArray(pkg.processors)) {
81
+ for (const ctor of pkg.processors) {
82
+ await registerProcessor(ctor, packageRec.name);
83
+ }
84
+ }
85
+ }
86
+ async function registerComponent(ctor, packageName) {
87
+ const _metadata = Reflect.getMetadata(COMPONENT_OPTIONS, ctor);
88
+ if (!_metadata)
89
+ throw new TypeError(`Class "${ctor.name}" has no component metadata.`);
90
+ if (_metadata.abstract)
91
+ return;
92
+ let componentRec = ExtensionRegistry.components.get(_metadata.className);
93
+ if (componentRec)
94
+ throw new TypeError(`Component "${_metadata.className}" already registered by "${componentRec.package}" package`);
95
+ let metadata = await resolvePromisesDeep(_metadata);
96
+ metadata = deepClone({
97
+ className: metadata.className,
98
+ displayName: metadata.displayName,
99
+ description: metadata.description,
100
+ iconUrl: metadata.iconUrl,
101
+ author: metadata.author,
102
+ interfaces: metadata.interfaces,
103
+ tags: metadata.tags,
104
+ abstract: metadata.abstract,
105
+ variables: metadata.variables,
106
+ components: metadata.components,
107
+ ...metadata,
108
+ });
109
+ if (metadata.variables) {
110
+ /** Convert object enumValues to arrays */
111
+ for (const v of Object.values(metadata.variables)) {
112
+ if (v.enumValues &&
113
+ typeof v.enumValues === 'object' &&
114
+ !Array.isArray(v.enumValues)) {
115
+ let values = v.enumValues;
116
+ const keys = Object.keys(values).filter(k => !/^\d+$/.test(k));
117
+ values = keys.reduce((a, k) => {
118
+ if (values[k] != null)
119
+ a.push(values[k]);
120
+ return a;
121
+ // v => !(typeof v === 'number' && !keys.includes(String(v))),
122
+ }, []);
123
+ v.enumValues = values;
124
+ }
125
+ }
126
+ }
127
+ componentRec = {
128
+ package: packageName,
129
+ ctor,
130
+ metadata,
131
+ };
132
+ ExtensionRegistry.components.set(metadata.className, componentRec);
133
+ }
134
+ ExtensionRegistry.registerComponent = registerComponent;
135
+ async function registerProcessor(ctor, packageName) {
136
+ const _metadata = Reflect.getMetadata(PROCESSOR_OPTIONS, ctor);
137
+ if (!_metadata)
138
+ throw new TypeError(`Class "${ctor.name}" has no processor metadata.`);
139
+ if (_metadata.abstract)
140
+ return;
141
+ let processorRec = ExtensionRegistry.processors.get(_metadata.className);
142
+ if (processorRec)
143
+ throw new TypeError(`Processor "${_metadata.className}" already registered by "${processorRec.package}" package`);
144
+ let metadata = await resolvePromisesDeep(_metadata);
145
+ metadata = deepClone({
146
+ className: metadata.className,
147
+ displayName: metadata.displayName,
148
+ description: metadata.description,
149
+ iconUrl: metadata.iconUrl,
150
+ author: metadata.author,
151
+ tags: metadata.tags,
152
+ abstract: metadata.abstract,
153
+ variables: metadata.variables,
154
+ components: metadata.components,
155
+ ...metadata,
156
+ });
157
+ if (metadata.variables) {
158
+ /** Convert object enumValues to arrays */
159
+ for (const v of Object.values(metadata.variables)) {
160
+ if (v.enumValues &&
161
+ typeof v.enumValues === 'object' &&
162
+ !Array.isArray(v.enumValues)) {
163
+ let values = v.enumValues;
164
+ const keys = Object.keys(values).filter(k => !/^\d+$/.test(k));
165
+ values = keys.reduce((a, k) => {
166
+ if (values[k] != null)
167
+ a.push(values[k]);
168
+ return a;
169
+ // v => !(typeof v === 'number' && !keys.includes(String(v))),
170
+ }, []);
171
+ v.enumValues = values;
172
+ }
173
+ }
174
+ }
175
+ processorRec = {
176
+ package: packageName,
177
+ ctor,
178
+ metadata,
179
+ };
180
+ ExtensionRegistry.processors.set(metadata.className, processorRec);
181
+ }
182
+ ExtensionRegistry.registerProcessor = registerProcessor;
183
+ })(ExtensionRegistry || (ExtensionRegistry = {}));
184
+ function locatePkgJson(directory) {
185
+ if (directory.startsWith('file:'))
186
+ directory = fileURLToPath(directory);
187
+ for (let i = 0; i < 3; i++) {
188
+ const f = path.resolve(directory, 'package.json');
189
+ if (fs.existsSync(f)) {
190
+ const json = JSON.parse(fs.readFileSync(f, 'utf8'));
191
+ if (json.name && json.version)
192
+ return json;
193
+ }
194
+ directory = path.dirname(directory);
195
+ }
196
+ }
197
+ async function resolvePromisesDeep(obj) {
198
+ obj = await obj;
199
+ if (obj && isPlainObject(obj)) {
200
+ for (const k of Object.keys(obj)) {
201
+ obj[k] = await resolvePromisesDeep(obj[k]);
202
+ }
203
+ }
204
+ return obj;
205
+ }
@@ -0,0 +1,2 @@
1
+ export declare const secretEncryptor: (secretKey: string) => import("valgen").Validator<string, string, import("valgen").ExecutionOptions>;
2
+ export declare const secretDecryptor: (secretKey: string) => import("valgen").Validator<string, string, import("valgen").ExecutionOptions>;
@@ -0,0 +1,43 @@
1
+ import crypto from 'node:crypto';
2
+ import { validator } from 'valgen';
3
+ export const secretEncryptor = (secretKey) => {
4
+ return validator((input) => {
5
+ if (input.startsWith('sbscrt::')) {
6
+ input = decryptText(input, secretKey);
7
+ }
8
+ return 'sbscrt::' + encryptText(input, secretKey);
9
+ });
10
+ };
11
+ export const secretDecryptor = (secretKey) => {
12
+ return validator((input) => {
13
+ if (input.startsWith('sbscrt::')) {
14
+ return decryptText(input.substring(6), secretKey);
15
+ }
16
+ return input;
17
+ });
18
+ };
19
+ function encryptText(input, secretKey) {
20
+ const salt = crypto.randomBytes(16);
21
+ const key = crypto.scryptSync(secretKey, salt, 32);
22
+ const iv = crypto.randomBytes(16);
23
+ const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
24
+ const encrypted = Buffer.concat([
25
+ cipher.update(input, 'utf8'),
26
+ cipher.final(),
27
+ ]);
28
+ // Store salt + IV + EncryptedData
29
+ return Buffer.concat([salt, iv, encrypted]).toString('base64');
30
+ }
31
+ function decryptText(input, secretKey) {
32
+ const data = Buffer.from(input, 'base64');
33
+ const salt = data.subarray(0, 16);
34
+ const iv = data.subarray(16, 32);
35
+ const encrypted = data.subarray(32);
36
+ const key = crypto.scryptSync(secretKey, salt, 32);
37
+ const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
38
+ const decrypted = Buffer.concat([
39
+ decipher.update(encrypted),
40
+ decipher.final(),
41
+ ]);
42
+ return decrypted.toString('utf8');
43
+ }
@@ -0,0 +1,3 @@
1
+ import { IExtensionPackage } from '../interfaces/index.js';
2
+ export declare function makeExtensionPackage(pkg: IExtensionPackage): IExtensionPackage;
3
+ export declare function isExtensionPackage(pkg: IExtensionPackage): pkg is IExtensionPackage;
@@ -0,0 +1,13 @@
1
+ const marker = Symbol('marker');
2
+ export function makeExtensionPackage(pkg) {
3
+ Object.defineProperty(pkg, marker, {
4
+ configurable: false,
5
+ writable: false,
6
+ enumerable: false,
7
+ value: 1,
8
+ });
9
+ return pkg;
10
+ }
11
+ export function isExtensionPackage(pkg) {
12
+ return pkg && typeof pkg === 'object' && pkg[marker] === 1;
13
+ }
@@ -0,0 +1,7 @@
1
+ import { StackExecutor } from '../classes/stack-executor.js';
2
+ import { ProcessorMetadata, Profile } from '../models/index.js';
3
+ export declare function materializeMetadata(profile: Profile): ProcessorMetadata;
4
+ export declare function materializeMetadataSilent(profile: Profile): {
5
+ metadata: ProcessorMetadata;
6
+ issues?: StackExecutor.Issue[];
7
+ };
@@ -0,0 +1,99 @@
1
+ import { clone, merge, omitUndefined } from '@jsopen/objects';
2
+ import { SbError } from '../classes/sb-error.js';
3
+ import { StackExecutor } from '../classes/stack-executor.js';
4
+ import { ExtensionRegistry } from '../registry.js';
5
+ export function materializeMetadata(profile) {
6
+ const { metadata, issues } = materializeMetadataSilent(profile);
7
+ if (issues?.length) {
8
+ const msg = 'Worker profile validation error.\n ' +
9
+ issues
10
+ .map(issue => `- ${issue.message}. Location: /${issue.location}`)
11
+ .join('\n ');
12
+ throw new SbError(msg, {
13
+ workerId: profile.id,
14
+ issues,
15
+ });
16
+ }
17
+ return metadata;
18
+ }
19
+ export function materializeMetadataSilent(profile) {
20
+ const stackExecutor = new StackExecutor();
21
+ const processorRec = ExtensionRegistry.getProcessor(profile.processorClass);
22
+ const metadata = omitUndefined(clone({
23
+ className: processorRec.metadata.className,
24
+ displayName: processorRec.metadata.displayName,
25
+ description: processorRec.metadata.description,
26
+ iconUrl: processorRec.metadata.iconUrl,
27
+ author: processorRec.metadata.author,
28
+ tags: processorRec.metadata.tags,
29
+ variables: processorRec.metadata.variables,
30
+ components: processorRec.metadata.components,
31
+ ...processorRec.metadata,
32
+ }, { deep: 'full' }));
33
+ delete metadata.components;
34
+ if (processorRec.metadata.components) {
35
+ stackExecutor.execute('components', () => {
36
+ metadata.components = _materializeComponents(stackExecutor, processorRec.metadata, profile);
37
+ });
38
+ }
39
+ if (stackExecutor.issues?.length)
40
+ return { metadata, issues: stackExecutor.issues };
41
+ return { metadata };
42
+ }
43
+ /**
44
+ *
45
+ * @private
46
+ */
47
+ function _materializeComponents(stackExecutor, metadata, profile) {
48
+ const out = {};
49
+ for (const [componentName, metadataComponent,] of Object.entries(metadata.components)) {
50
+ stackExecutor.execute(componentName, () => {
51
+ const materializedComponent = {
52
+ interfaces: metadataComponent.interfaces
53
+ ? [...metadataComponent.interfaces]
54
+ : undefined,
55
+ required: metadataComponent.required,
56
+ ...metadataComponent,
57
+ };
58
+ out[componentName] = materializedComponent;
59
+ /** Check if component defined in profile */
60
+ const profileComponent = profile?.components?.[componentName];
61
+ if (!profileComponent) {
62
+ if (metadataComponent.required)
63
+ throw new Error(`Component "${componentName}" should be configured`);
64
+ return;
65
+ }
66
+ /** istanbul ignore next */
67
+ if (!profileComponent.className)
68
+ throw new Error(`"className" property required`);
69
+ const reg = ExtensionRegistry.getComponent(profileComponent.className);
70
+ materializedComponent.className = profileComponent.className;
71
+ materializedComponent.variables = merge({}, [reg.metadata.variables || {}, metadataComponent.variables || {}], { deep: 'full' });
72
+ if (metadataComponent.className) {
73
+ if (materializedComponent.className &&
74
+ materializedComponent.className !== metadataComponent.className)
75
+ throw new Error(`Selected component class "${profileComponent.className}" do not match with definition component class (${metadataComponent.className}).`);
76
+ }
77
+ else {
78
+ const requiredInterfaces = metadataComponent.interfaces || [];
79
+ const implementations = reg.metadata.interfaces || [];
80
+ const filtered = requiredInterfaces.filter(x => !implementations.includes(x));
81
+ if (filtered.length) {
82
+ throw new Error(`Selected component "${profileComponent.className}" do not implements some of required interfaces (${filtered.join(',')}).`);
83
+ }
84
+ }
85
+ if (materializedComponent.variables) {
86
+ for (const [k, variable] of Object.entries(materializedComponent.variables)) {
87
+ if (variable.ignored)
88
+ delete materializedComponent.variables[k];
89
+ }
90
+ }
91
+ if (reg.metadata.components) {
92
+ stackExecutor.execute('components', () => {
93
+ materializedComponent.components = _materializeComponents(stackExecutor, reg.metadata, profileComponent);
94
+ });
95
+ }
96
+ });
97
+ }
98
+ return out;
99
+ }
@@ -0,0 +1,21 @@
1
+ import { StackExecutor } from '../classes/stack-executor.js';
2
+ import { ProcessorMetadata, Profile } from '../models/index.js';
3
+ export interface CodecOptions {
4
+ setDefaultValues?: boolean;
5
+ cryptoSecretKey?: string;
6
+ }
7
+ /**
8
+ * Decodes profile
9
+ */
10
+ export declare function decodeProfile(materializedMetadata: ProcessorMetadata, rawProfile: Profile, options?: CodecOptions): Profile;
11
+ export declare function decodeProfileSilent(materializedMetadata: ProcessorMetadata, rawProfile: Profile, options?: CodecOptions): {
12
+ profile: Profile;
13
+ issues?: StackExecutor.Issue[];
14
+ };
15
+ /**
16
+ * Encodes profile
17
+ */
18
+ export declare function encodeProfileSilent(metadata: ProcessorMetadata, rawProfile: Profile, options?: CodecOptions): {
19
+ profile: Profile;
20
+ issues?: StackExecutor.Issue[];
21
+ };
@@ -0,0 +1,222 @@
1
+ import { isAny, isBase64, isBoolean, isDateString, isEmail, isHex, isNumber, isString, isTime, isURL, validator, vg, } from 'valgen';
2
+ import { SbError } from '../classes/sb-error.js';
3
+ import { StackExecutor } from '../classes/stack-executor.js';
4
+ import { Profile, VariableType, } from '../models/index.js';
5
+ import { getModelsDocument } from '../models-document.js';
6
+ import { secretDecryptor, secretEncryptor } from './encrypt-helpers.js';
7
+ /**
8
+ * Decodes profile
9
+ */
10
+ export function decodeProfile(materializedMetadata, rawProfile, options) {
11
+ const { profile: out, issues } = decodeProfileSilent(materializedMetadata, rawProfile, options);
12
+ if (issues?.length) {
13
+ const msg = 'Worker profile validation error.\n ' +
14
+ issues
15
+ .map(issue => `- ${issue.message}. Location: /${issue.location}`)
16
+ .join('\n ');
17
+ throw new SbError(msg, {
18
+ workerId: rawProfile.id,
19
+ issues,
20
+ });
21
+ }
22
+ return out;
23
+ }
24
+ export function decodeProfileSilent(materializedMetadata, rawProfile, options) {
25
+ const modelsDocument = getModelsDocument();
26
+ const t = modelsDocument.node.getComplexType(Profile);
27
+ const fn = t.generateCodec('decode');
28
+ const out = fn(rawProfile);
29
+ const stackExecutor = new StackExecutor();
30
+ _codecProcessElement(stackExecutor, 'decode', materializedMetadata, out, options);
31
+ return { profile: out, issues: stackExecutor.issues };
32
+ }
33
+ /**
34
+ * Encodes profile
35
+ */
36
+ export function encodeProfileSilent(metadata, rawProfile, options) {
37
+ const modelsDocument = getModelsDocument();
38
+ const t = modelsDocument.node.getComplexType(Profile);
39
+ const fn = t.generateCodec('encode', { coerce: true });
40
+ const out = fn({
41
+ id: rawProfile.id,
42
+ processorClass: rawProfile.processorClass,
43
+ displayName: rawProfile.displayName,
44
+ group: rawProfile.group,
45
+ description: rawProfile.description,
46
+ iconUrl: rawProfile.iconUrl,
47
+ settings: {
48
+ autostart: rawProfile.settings?.autostart ?? false,
49
+ logs: rawProfile.settings?.logs ?? {},
50
+ ...rawProfile.settings,
51
+ },
52
+ values: rawProfile.values,
53
+ ...rawProfile,
54
+ });
55
+ const stackExecutor = new StackExecutor();
56
+ _codecProcessElement(stackExecutor, 'encode', metadata, out, options);
57
+ return { profile: out, issues: stackExecutor.issues };
58
+ }
59
+ /**
60
+ *
61
+ * @private
62
+ */
63
+ function _codecProcessElement(stackExecutor, codec, metadata, profile, options) {
64
+ profile.values = profile.values || {};
65
+ if (metadata.variables) {
66
+ stackExecutor.execute('values', () => {
67
+ _decodeVariables(stackExecutor, codec, metadata, profile, options);
68
+ });
69
+ }
70
+ else
71
+ delete profile.values;
72
+ if (metadata.components) {
73
+ stackExecutor.execute('components', () => {
74
+ profile.components = profile.components || {};
75
+ for (const [k, defComponent] of Object.entries(metadata.components)) {
76
+ if (!profile.components[k]) {
77
+ if (defComponent.required) {
78
+ stackExecutor.issues.push({
79
+ message: `Component "${k}" is required`,
80
+ severity: 'error',
81
+ location: `/components/${k}`,
82
+ });
83
+ }
84
+ continue;
85
+ }
86
+ stackExecutor.execute(k, () => {
87
+ _codecProcessElement(stackExecutor, codec, defComponent, profile.components[k], options);
88
+ });
89
+ }
90
+ });
91
+ }
92
+ else
93
+ delete profile?.components;
94
+ }
95
+ /**
96
+ *
97
+ * @protected
98
+ */
99
+ function _decodeVariables(stackExecutor, codec, metadata, profile, options) {
100
+ if (!metadata.variables) {
101
+ delete profile.values;
102
+ return;
103
+ }
104
+ const oldValues = profile.values;
105
+ profile.values = {};
106
+ const process = (variables, target, oldVals) => {
107
+ for (const [k, defVariable] of Object.entries(variables)) {
108
+ stackExecutor.execute(k, () => {
109
+ if (defVariable == null)
110
+ return;
111
+ const fns = [];
112
+ switch (defVariable.type) {
113
+ case VariableType.String: {
114
+ switch (defVariable.format) {
115
+ case 'hex':
116
+ fns.push(isHex);
117
+ break;
118
+ case 'base64':
119
+ fns.push(isBase64);
120
+ break;
121
+ case 'url':
122
+ fns.push(isURL);
123
+ break;
124
+ case 'email':
125
+ fns.push(isEmail);
126
+ break;
127
+ case 'date':
128
+ fns.push(vg.isDateString({
129
+ precisionMin: 'day',
130
+ precisionMax: 'day',
131
+ trim: true,
132
+ }));
133
+ break;
134
+ case 'date-time':
135
+ fns.push(isDateString);
136
+ break;
137
+ case 'time':
138
+ fns.push(isTime);
139
+ break;
140
+ default:
141
+ fns.push(isString);
142
+ }
143
+ if (defVariable.pattern)
144
+ fns.push(vg.matches(defVariable.pattern));
145
+ break;
146
+ }
147
+ case VariableType.Secret: {
148
+ fns.push(isString);
149
+ if (options?.cryptoSecretKey) {
150
+ if (codec === 'decode')
151
+ fns.push(secretDecryptor(options.cryptoSecretKey));
152
+ else
153
+ fns.push(secretEncryptor(options.cryptoSecretKey));
154
+ }
155
+ break;
156
+ }
157
+ case VariableType.Number: {
158
+ fns.push(isNumber);
159
+ break;
160
+ }
161
+ case VariableType.Boolean: {
162
+ fns.push(isBoolean);
163
+ break;
164
+ }
165
+ case VariableType.Enum: {
166
+ fns.push(vg.isEnum(defVariable.enumValues));
167
+ break;
168
+ }
169
+ case VariableType.Nested: {
170
+ if (!defVariable.variables) {
171
+ delete target[k];
172
+ }
173
+ else {
174
+ fns.push(validator(old => {
175
+ if (!old)
176
+ return;
177
+ const v = {};
178
+ process(defVariable.variables, v, old);
179
+ return v;
180
+ }));
181
+ // fns.push(isAny);
182
+ }
183
+ break;
184
+ }
185
+ default:
186
+ fns.push(isAny);
187
+ }
188
+ if (codec === 'decode') {
189
+ if (defVariable.minValue)
190
+ fns.push(vg.isGte(defVariable.minValue));
191
+ if (defVariable.maxValue)
192
+ fns.push(vg.isLt(defVariable.maxValue));
193
+ }
194
+ let fn = fns.length > 1 ? vg.pipe(fns) : fns[0];
195
+ if (defVariable.isArray) {
196
+ fn = vg.isArray(fn);
197
+ if (defVariable.minOccurs !== null || defVariable.maxOccurs) {
198
+ const p = [fn];
199
+ if (defVariable.minOccurs != null)
200
+ p.push(vg.lengthMin(1));
201
+ if (defVariable.maxOccurs != null)
202
+ p.push(vg.lengthMax(defVariable.maxOccurs));
203
+ fn = vg.pipe(p, { returnIndex: 0 });
204
+ }
205
+ }
206
+ if (options?.setDefaultValues ?? true) {
207
+ fn =
208
+ defVariable.required || defVariable.default != null
209
+ ? vg.required(fn, { default: defVariable.default })
210
+ : vg.optional(fn);
211
+ }
212
+ else
213
+ fn =
214
+ !defVariable.required || defVariable.default != null
215
+ ? vg.optional(fn)
216
+ : vg.required(fn);
217
+ target[k] = fn(oldVals?.[k], { coerce: true });
218
+ });
219
+ }
220
+ };
221
+ process(metadata.variables, profile.values, oldValues);
222
+ }