@platforma-sdk/tengo-builder 1.16.1 → 1.17.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.
Files changed (40) hide show
  1. package/dist/commands/build.d.ts.map +1 -1
  2. package/dist/compiler/compiler.d.ts.map +1 -1
  3. package/dist/compiler/compileroptions.d.ts +6 -0
  4. package/dist/compiler/compileroptions.d.ts.map +1 -0
  5. package/dist/compiler/main.d.ts +0 -1
  6. package/dist/compiler/main.d.ts.map +1 -1
  7. package/dist/compiler/package.d.ts +6 -0
  8. package/dist/compiler/package.d.ts.map +1 -1
  9. package/dist/compiler/source.d.ts +9 -4
  10. package/dist/compiler/source.d.ts.map +1 -1
  11. package/dist/compiler/template.d.ts +5 -0
  12. package/dist/compiler/template.d.ts.map +1 -1
  13. package/dist/compiler/test.artifacts.d.ts +9 -0
  14. package/dist/compiler/test.artifacts.d.ts.map +1 -1
  15. package/dist/compiler/util.d.ts +3 -0
  16. package/dist/compiler/util.d.ts.map +1 -1
  17. package/dist/index.js +28 -28
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.mjs +575 -513
  20. package/dist/index.mjs.map +1 -1
  21. package/package.json +4 -4
  22. package/src/commands/build.ts +2 -1
  23. package/src/commands/check.ts +1 -1
  24. package/src/commands/dump/all.ts +1 -1
  25. package/src/commands/dump/assets.ts +1 -1
  26. package/src/commands/dump/libs.ts +1 -1
  27. package/src/commands/dump/software.ts +1 -1
  28. package/src/commands/dump/templates.ts +1 -1
  29. package/src/commands/dump/tests.ts +1 -1
  30. package/src/commands/test.ts +1 -1
  31. package/src/compiler/compiler.test.ts +51 -20
  32. package/src/compiler/compiler.ts +10 -4
  33. package/src/compiler/compileroptions.ts +51 -0
  34. package/src/compiler/main.ts +4 -19
  35. package/src/compiler/package.ts +21 -13
  36. package/src/compiler/source.test.ts +18 -4
  37. package/src/compiler/source.ts +94 -20
  38. package/src/compiler/template.ts +6 -0
  39. package/src/compiler/test.artifacts.ts +66 -8
  40. package/src/compiler/util.ts +21 -0
@@ -1,5 +1,5 @@
1
1
  import { Command, Flags } from '@oclif/core'
2
- import { createLogger } from '../../compiler/main'
2
+ import { createLogger } from '../../compiler/util'
3
3
  import { dumpLibs } from '../../shared/dump'
4
4
  import { stdout } from 'process'
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core'
2
- import { createLogger } from '../../compiler/main'
2
+ import { createLogger } from '../../compiler/util'
3
3
  import { dumpSoftware } from '../../shared/dump'
4
4
  import { stdout } from 'process'
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core'
2
- import { createLogger } from '../../compiler/main'
2
+ import { createLogger } from '../../compiler/util'
3
3
  import { dumpTemplates } from '../../shared/dump'
4
4
  import { stdout } from 'process'
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core'
2
- import { createLogger } from '../../compiler/main'
2
+ import { createLogger } from '../../compiler/util'
3
3
  import { dumpTests } from '../../shared/dump'
4
4
  import { stdout } from 'process'
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core';
2
- import { createLogger } from '../compiler/main';
2
+ import { createLogger } from '../compiler/util';
3
3
  import { dumpAll } from '../shared/dump';
4
4
  import { GlobalFlags } from '../shared/basecmd';
5
5
  import { spawnEmbed, waitFor } from '../shared/proc';
@@ -1,48 +1,79 @@
1
1
  import { TengoTemplateCompiler } from './compiler';
2
2
  import { ArtifactSource, parseSource } from './source';
3
- import {
4
- TestArtifactSource, testLocalPackage,
5
- testPackage1,
6
- testPackage1Lib1Name,
7
- testPackage1Lib1Src, testPackage1Lib2Name, testPackage1Lib2Src,
8
- testPackage1Soft1Name,
9
- testPackage1Soft1Src,
10
- testPackage1Tpl3CompiledBase64, testPackage1Tpl3Name
11
- } from './test.artifacts';
3
+ import * as winston from 'winston';
4
+ import * as ta from './test.artifacts';
12
5
  import { artifactNameToString } from './package';
13
6
  import { Template } from './template';
7
+ import { createLogger } from './util';
14
8
 
15
- function parseSrc(src: TestArtifactSource[]): ArtifactSource[] {
16
- return src.map(tp => {
17
- const aSrc = parseSource('dist', tp.src, tp.fullName, true);
9
+ function parseSrc(logger: winston.Logger, src: ta.TestArtifactSource[]): ArtifactSource[] {
10
+ return src.map((tp) => {
11
+ const aSrc = parseSource(logger, 'dist', tp.src, tp.fullName, true);
18
12
  return aSrc;
19
13
  });
20
14
  }
21
15
 
22
16
  test('compile package 1', () => {
17
+ const logger = createLogger('error');
18
+
23
19
  const compiler = new TengoTemplateCompiler('dist');
24
- const compiled = compiler.compileAndAdd(parseSrc(testPackage1));
25
- expect(compiled.templates[0].data.libs).toHaveProperty(artifactNameToString(testPackage1Lib1Name));
20
+ const compiled = compiler.compileAndAdd(parseSrc(logger, ta.testPackage1));
21
+ expect(compiled.templates[0].data.libs).toHaveProperty(
22
+ artifactNameToString(ta.testPackage1Lib1Name)
23
+ );
26
24
  console.log(Buffer.from(compiled.templates[0].content).toString('base64'));
27
25
  });
28
26
 
29
27
  test('compile main source set', () => {
30
28
  const compiler = new TengoTemplateCompiler('dist');
29
+ const logger = createLogger('error');
31
30
 
32
31
  // emulate adding compiled artifacts
33
- compiler.addLib(parseSource('dist', testPackage1Lib1Src, testPackage1Lib1Name, true));
34
- compiler.addLib(parseSource('dist', testPackage1Lib2Src, testPackage1Lib2Name, true));
35
- compiler.addSoftware(parseSource('dist', testPackage1Soft1Src, testPackage1Soft1Name, true));
36
- compiler.addTemplate(new Template('dist', testPackage1Tpl3Name, { content: Buffer.from(testPackage1Tpl3CompiledBase64, 'base64') }));
32
+ compiler.addLib(
33
+ parseSource(logger, 'dist', ta.testPackage1Lib1Src, ta.testPackage1Lib1Name, true)
34
+ );
35
+ compiler.addLib(
36
+ parseSource(logger, 'dist', ta.testPackage1Lib2Src, ta.testPackage1Lib2Name, true)
37
+ );
38
+ compiler.addSoftware(
39
+ parseSource(logger, 'dist', ta.testPackage1Soft1Src, ta.testPackage1Soft1Name, true)
40
+ );
41
+ compiler.addTemplate(
42
+ new Template('dist', ta.testPackage1Tpl3Name, {
43
+ content: Buffer.from(ta.testPackage1Tpl3CompiledBase64, 'base64')
44
+ })
45
+ );
37
46
 
38
47
  // all elements in the context must have all their dependencies met
39
48
  compiler.checkLibs();
40
49
 
41
50
  // main package compilation
42
- const compiled = compiler.compileAndAdd(parseSrc(testLocalPackage));
43
- const tpl1 = compiled.templates.find(t => t.fullName.id === 'local-template-1')!;
51
+ const compiled = compiler.compileAndAdd(parseSrc(logger, ta.testLocalPackage));
52
+ const tpl1 = compiled.templates.find((t) => t.fullName.id === 'local-template-1')!;
44
53
  expect(tpl1).toBeDefined();
45
54
 
46
55
  // checking that transient library dependency was resolved
47
56
  expect(tpl1.data.templates).toHaveProperty('package1:template-3');
48
57
  });
58
+
59
+ test('compile template with hash override', () => {
60
+ const compiler = new TengoTemplateCompiler('dist');
61
+ const logger = createLogger('error');
62
+
63
+ // main package compilation
64
+ const compiled = compiler.compileAndAdd(parseSrc(logger, ta.testPackage2));
65
+ const tpl3 = compiled.templates.find((t) => t.fullName.id === 'local-template-3')!;
66
+ console.log('Tpl3: ' + JSON.stringify(tpl3));
67
+ expect(tpl3).toBeDefined();
68
+
69
+ // checking that transient library dependency was resolved
70
+ expect(tpl3.data.hashOverride).toEqual('CE0F6EDF-D97C-44E7-B16B-D661D4C799C1'.toLowerCase());
71
+ expect(tpl3.data.libs[artifactNameToString(ta.testLocalLib3.fullName)]).toBeDefined();
72
+ });
73
+
74
+ test('wrong hash override value throws error', () => {
75
+ const compiler = new TengoTemplateCompiler('dist');
76
+ const logger = createLogger('error');
77
+
78
+ expect(() => compiler.compileAndAdd(parseSrc(logger, ta.testPackage2BrokenHash))).toThrow('must contain valid UUID as an override');
79
+ });
@@ -2,7 +2,6 @@ import { ArtifactSource } from './source';
2
2
  import { Template, TemplateData } from './template';
3
3
  import {
4
4
  TypedArtifactName,
5
- artifactKey,
6
5
  fullNameToString,
7
6
  typedArtifactNameToString,
8
7
  artifactNameToString,
@@ -11,6 +10,7 @@ import {
11
10
  } from './package';
12
11
  import { ArtifactStore } from './artifactset';
13
12
  import { assertNever } from './util';
13
+ import { applyLibraryCompilerOptions, applyTemplateCompilerOptions } from './compileroptions';
14
14
 
15
15
  export interface TemplatesAndLibs {
16
16
  templates: Template[],
@@ -35,7 +35,7 @@ export class TengoTemplateCompiler {
35
35
  trace: string[]) {
36
36
  for (const dep of deps) {
37
37
  switch (dep.type) {
38
- case 'library':
38
+ case 'library': {
39
39
  const lib = this.getLibOrError(dep);
40
40
 
41
41
  const recursionStart = trace.indexOf(artifactNameToString(dep))
@@ -44,15 +44,19 @@ export class TengoTemplateCompiler {
44
44
  throw new Error(errorMessage)
45
45
  }
46
46
 
47
- data.libs[artifactNameToString(dep)] = {
47
+ const tplLib = {
48
48
  ...formatArtefactNameAndVersion(lib.fullName),
49
49
  src: lib.src
50
- };
50
+ }
51
+
52
+ applyLibraryCompilerOptions(lib.compilerOptions, tplLib)
53
+ data.libs[artifactNameToString(dep)] = tplLib;
51
54
 
52
55
  // populate with transient library dependencies
53
56
  this.populateTemplateDataFromDependencies(fullName, data, lib.dependencies, [...trace, artifactNameToString(dep)]);
54
57
 
55
58
  break;
59
+ }
56
60
  case 'software':
57
61
  const software = this.getSoftwareOrError(dep);
58
62
  data.software[artifactNameToString(dep)] = {
@@ -105,6 +109,8 @@ export class TengoTemplateCompiler {
105
109
  src: tplSrc.src
106
110
  };
107
111
 
112
+ applyTemplateCompilerOptions(tplSrc.compilerOptions, tplData);
113
+
108
114
  // collecting dependencies in output format
109
115
  this.populateTemplateDataFromDependencies(tplSrc.fullName, tplData, tplSrc.dependencies, []);
110
116
 
@@ -0,0 +1,51 @@
1
+ import { CompilerOption } from './package';
2
+ import { TemplateData, TemplateLibData } from './template';
3
+ import * as util from './util';
4
+
5
+ export function applyTemplateCompilerOptions(opts: CompilerOption[], tpl: TemplateData) {
6
+ for (const opt of opts) {
7
+ switch (opt.name) {
8
+ case 'hash_override': {
9
+ tpl.hashOverride = hashOverride(opt.args);
10
+ break;
11
+ }
12
+ }
13
+ }
14
+ }
15
+
16
+ export function applyLibraryCompilerOptions(opts: CompilerOption[], lib: TemplateLibData) {
17
+ for (const opt of opts) {
18
+ switch (opt.name) {
19
+ case 'hash_override': {
20
+ throw new Error(
21
+ `hash_override compiler option can be used ONLY on template level. Even in templates it is already dangerous enough` +
22
+ ` to potentially break everything in Platforma Backend. In libraries with the transitive dependencies`+
23
+ ` resolution and flattening of libraries list on template level, it becomes so unpredictibally disasterous, that`+
24
+ ` we are doomed to never find the ends of a knot if anything goes wrong.`
25
+ );
26
+ }
27
+ }
28
+ }
29
+ }
30
+
31
+ export function hashOverride(args: string[]): string {
32
+ if (args.length != 1) {
33
+ throw new Error(
34
+ 'hash_override compiler option expects exactly one argument: hash_override <some string>. Note, you can use only UUID as a value.'
35
+ );
36
+ }
37
+
38
+ const override = args[0].toLowerCase();
39
+
40
+ if (!util.isUUID(override)) {
41
+ throw new Error(
42
+ 'hash_override must contain valid UUID as an override. As hash_override affects deduplication,' +
43
+ " it becomes completely not possible to distinguish several different templates from each other on backend's side." +
44
+ ' This means, if you set hash_override to a simple value (say, letter "a") on two completely different templates,' +
45
+ " they will be marked as interchangeable on backend's side with unpredictable consequences." +
46
+ ' UUID looks like a safe enough tradeoff between the feature usage simplicity and duplication safety'
47
+ );
48
+ }
49
+
50
+ return override;
51
+ }
@@ -37,21 +37,6 @@ const srcSoftwareSuffix = '.sw.json';
37
37
  const srcAssetSuffix = '.as.json';
38
38
  const compilableSuffixes = [srcLibSuffix, srcTplSuffix, srcSoftwareSuffix, srcAssetSuffix];
39
39
 
40
- export function createLogger(level: string = 'debug'): winston.Logger {
41
- return winston.createLogger({
42
- level: level,
43
- format: winston.format.printf(({ level, message }) => {
44
- return `${level.padStart(6, ' ')}: ${message}`;
45
- }),
46
- transports: [
47
- new winston.transports.Console({
48
- stderrLevels: ['error', 'warn', 'info', 'debug'],
49
- handleExceptions: true
50
- })
51
- ]
52
- });
53
- }
54
-
55
40
  export function getPackageInfo(): PackageJson {
56
41
  const packageInfo: PackageJson = JSON.parse(fs.readFileSync('package.json').toString());
57
42
  return packageInfo;
@@ -156,7 +141,7 @@ function loadLibsFromDir(
156
141
  id: f.slice(0, f.length - compiledLibSuffix.length),
157
142
  version: packageJson.version
158
143
  };
159
- const src = parseSourceFile(mode, file, fullName, true);
144
+ const src = parseSourceFile(logger, mode, file, fullName, true);
160
145
  compiler.addLib(src);
161
146
  logger.debug(`Adding dependency ${fullNameToString(fullName)} from ${file}`);
162
147
  if (src.dependencies.length > 0) {
@@ -207,7 +192,7 @@ function loadSoftwareFromDir(
207
192
  version: packageJson.version
208
193
  };
209
194
 
210
- const software = new ArtifactSource(mode, fullName, fs.readFileSync(file).toString(), file, []);
195
+ const software = new ArtifactSource(mode, fullName, fs.readFileSync(file).toString(), file, [], []);
211
196
 
212
197
  logger.debug(`Adding dependency ${fullNameToString(fullName)} from ${file}`);
213
198
  compiler.addSoftware(software);
@@ -232,7 +217,7 @@ function loadAssetsFromDir(
232
217
  version: packageJson.version
233
218
  };
234
219
 
235
- const asset = new ArtifactSource(mode, fullName, fs.readFileSync(file).toString(), file, []);
220
+ const asset = new ArtifactSource(mode, fullName, fs.readFileSync(file).toString(), file, [], []);
236
221
 
237
222
  logger.debug(`Adding dependency ${fullNameToString(fullName)} from ${file}`);
238
223
  compiler.addAsset(asset);
@@ -275,7 +260,7 @@ export function parseSources(
275
260
 
276
261
  const file = path.resolve(root, inRootPath);
277
262
  logger.debug(`Parsing ${fullNameToString(fullName)} from ${file}`);
278
- const newSrc = parseSourceFile(mode, file, fullName, true);
263
+ const newSrc = parseSourceFile(logger, mode, file, fullName, true);
279
264
  if (newSrc.dependencies.length > 0) {
280
265
  logger.debug('Detected dependencies:');
281
266
  for (const dep of newSrc.dependencies) logger.debug(` - ${typedArtifactNameToString(dep)}`);
@@ -1,6 +1,3 @@
1
- import { gunzipSync, gzipSync } from 'node:zlib';
2
- import canonicalize from 'canonicalize';
3
-
4
1
  /*
5
2
  Package "@milaboratory/current-tengo-package".
6
3
 
@@ -20,9 +17,17 @@ import canonicalize from 'canonicalize';
20
17
 
21
18
  */
22
19
 
23
- export type CompileMode = 'dist'
20
+ export type CompileMode = 'dist';
21
+
22
+ export type CompilerOption = {
23
+ /** Option name, like 'hash_override' */
24
+ name: string; // we can't use strict literals typing here as we will break compatibility of the code through the lifetime of the compiler.
24
25
 
25
- export type ArtifactType = 'library' | 'template' | 'test' | 'software' | 'asset'
26
+ /** Arguments of the option. Everything that follows option name */
27
+ args: string[];
28
+ };
29
+
30
+ export type ArtifactType = 'library' | 'template' | 'test' | 'software' | 'asset';
26
31
 
27
32
  /** Artifact Name including package version */
28
33
  export interface FullArtifactName {
@@ -60,33 +65,36 @@ export function typedArtifactNameToString(name: TypedArtifactName): string {
60
65
  return `${name.type}:${name.pkg}:${name.id}`;
61
66
  }
62
67
 
63
- export function typedArtifactNamesEquals(name1: TypedArtifactName, name2: TypedArtifactName): boolean {
68
+ export function typedArtifactNamesEquals(
69
+ name1: TypedArtifactName,
70
+ name2: TypedArtifactName
71
+ ): boolean {
64
72
  return name1.type == name2.type && name1.pkg == name2.pkg && name1.id == name2.id;
65
73
  }
66
74
 
67
-
68
75
  /** used to format artefact name while generating output files */
69
76
  export function artifactNameToString(name: ArtifactName): string {
70
77
  return `${name.pkg}:${name.id}`;
71
78
  }
72
79
 
73
80
  /** used to format artefact name and version while generating output files */
74
- export function formatArtefactNameAndVersion(name: FullArtifactName): { name: string, version: string } {
81
+ export function formatArtefactNameAndVersion(name: FullArtifactName): {
82
+ name: string;
83
+ version: string;
84
+ } {
75
85
  return { name: artifactNameToString(name), version: name.version };
76
86
  }
77
87
 
78
88
  /** used to format artefact name and version while generating output files */
79
89
  export function parseArtefactNameAndVersion(nameAndVersion: {
80
- name: string,
81
- version: string
90
+ name: string;
91
+ version: string;
82
92
  }): FullArtifactNameWithoutType {
83
93
  const match = nameAndVersion.name.match(/^(?<pkg>[^:]*):(?<id>[^:]*)$/);
84
- if (!match)
85
- throw new Error(`malformed artifact name: ${nameAndVersion.name}`);
94
+ if (!match) throw new Error(`malformed artifact name: ${nameAndVersion.name}`);
86
95
  return { pkg: match.groups!['pkg'], id: match.groups!['id'], version: nameAndVersion.version };
87
96
  }
88
97
 
89
-
90
98
  export function fullNameWithoutTypeToString(name: FullArtifactNameWithoutType): string {
91
99
  return `${name.pkg}:${name.id}:${name.version}`;
92
100
  }
@@ -1,14 +1,19 @@
1
1
  import { parseSource } from './source';
2
+ import { createLogger } from './util';
2
3
  import {
3
4
  testLocalLib1Name,
4
5
  testLocalLib1Src,
5
6
  testLocalLib2Name,
6
7
  testLocalLib2Src,
7
- testLocalLib1SrcNormalized
8
+ testLocalLib1SrcNormalized,
9
+ testLocalTpl3Src,
10
+ testLocalTpl3Name
8
11
  } from './test.artifacts';
9
12
 
10
13
  test('test lib 1 parsing', () => {
11
- const libSrc = parseSource('dist', testLocalLib1Src, testLocalLib1Name, true);
14
+ const logger = createLogger('error');
15
+
16
+ const libSrc = parseSource(logger, 'dist', testLocalLib1Src, testLocalLib1Name, true);
12
17
  expect(libSrc.src).toEqual(testLocalLib1SrcNormalized);
13
18
  expect(libSrc.dependencies).toEqual([
14
19
  { type: 'library', pkg: 'package1', id: 'other-lib-2' },
@@ -17,13 +22,15 @@ test('test lib 1 parsing', () => {
17
22
  { type: 'template', pkg: 'package1', id: 'template-3' }
18
23
  ]);
19
24
 
20
- expect(parseSource('dist', testLocalLib1Src, testLocalLib1Name, false).src).toEqual(
25
+ expect(parseSource(logger, 'dist', testLocalLib1Src, testLocalLib1Name, false).src).toEqual(
21
26
  testLocalLib1Src
22
27
  );
23
28
  });
24
29
 
25
30
  test('test lib 2 parsing', () => {
26
- const libSrc = parseSource('dist', testLocalLib2Src, testLocalLib2Name, true);
31
+ const logger = createLogger('error');
32
+
33
+ const libSrc = parseSource(logger, 'dist', testLocalLib2Src, testLocalLib2Name, true);
27
34
  expect(libSrc.dependencies).toEqual([
28
35
  { type: 'library', pkg: 'package1', id: 'someid' },
29
36
  { type: 'library', pkg: '@platforma-sdk/workflow-tengo', id: 'assets' },
@@ -33,3 +40,10 @@ test('test lib 2 parsing', () => {
33
40
  { type: 'template', pkg: 'current-package', id: 'local-template-2' }
34
41
  ]);
35
42
  });
43
+
44
+ test('test tpl 3 parsing', () => {
45
+ const logger = createLogger('error');
46
+
47
+ const tplSrc = parseSource(logger, 'dist', testLocalTpl3Src, testLocalTpl3Name, true);
48
+ expect(tplSrc.compilerOptions[0].name).toEqual('hash_override');
49
+ });