CVE-2023-28444 (published 2023-03-24T20:15Z)

angular-server-side-configuration helps configure an angular application at runtime on the server or in a docker container via environment variables. angular-server-side-configuration detects used environment variables in TypeScript (.ts) files during build time of an Angular CLI project. The detected environment variables are written to a ngssc.json file in the output directory.\nDuring deployment of an Angular based app, the environment variables based on the variables from ngssc.json are inserted into the apps index.html (or defined index file). With version 15.0.0 the environment variable detection was widened to the entire project, relative to the angular.json file from the Angular CLI. In a monorepo setup, this could lead to environment variables intended for a backend/service to be detected and written to the ngssc.json, which would then be populated and exposed via index.html. This has NO IMPACT, in a plain Angular project that has no backend component. This vulnerability has been mitigated in version 15.1.0, by adding an option `searchPattern` which restricts the detection file range by default. As a workaround, manually edit or create ngssc.json or run script after ngssc.json generation.\n

CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)

Weakness information:
cwe_id: CWE-200
cwe_name: Exposure of Sensitive Information to an Unauthorized Actor
description: The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
extended_description: 
	   There are many different kinds of mistakes that introduce information exposures. The severity of the error can range widely, depending on the context in which the product operates, the type of sensitive information that is revealed, and the benefits it may provide to an attacker.  Some kinds of sensitive information include:
       
         private, personal information, such as personal messages, financial data, health records, geographic location, or contact details
         system status and environment, such as the operating system and installed packages
		 business secrets and intellectual property
		 network status and configuration
		 the product's own code or internal state
         metadata, e.g. logging of connections or message headers
		 indirect information, such as a discrepancy between two internal operations that can be observed by an outsider
       
	    Information might be sensitive to different parties, each of which may have their own expectations for whether the information should be protected.  These parties include:
               
                  the product's own users
                  people or organizations whose information is created or used by the product, even if they are not direct product users
		  the product's administrators, including the admins of the system(s) and/or networks on which the product operates
                  the developer
               
	    Information exposures can occur in different ways:
               
                  the code explicitly inserts sensitive information into resources or messages that are intentionally made accessible to unauthorized actors, but should not contain the information - i.e., the information should have been "scrubbed" or "sanitized"
                  a different weakness or mistake indirectly inserts the sensitive information into resources, such as a web script error revealing the full system path of the program.
                  the code manages resources that intentionally contain sensitive information, but the resources are unintentionally made accessible to unauthorized actors. In this case, the information exposure is resultant - i.e., a different weakness enabled the access to the information in the first place.
               
	    It is common practice to describe any loss of confidentiality as an "information exposure," but this can lead to overuse of CWE-200 in CWE mapping. From the CWE perspective, loss of confidentiality is a technical impact that can arise from dozens of different weaknesses, such as insecure file permissions or out-of-bounds read.  CWE-200 and its lower-level descendants are intended to cover the mistakes that occur in behaviors that explicitly manage, store, transfer, or cleanse sensitive information.
         
         

Fix information:
repo_url: https://github.com/kyubisation/angular-server-side-configuration
commit_details: repo_url: https://github.com/kyubisation/angular-server-side-configuration
author: Lukas Spirig
author_date: 2023-03-22 17:36:12+01:00
msg: feat: add searchPattern as an option and restrict default search pattern (#75)

With version 15, the behavior of the environment variable lookup changed to try to find occurrences in the whole workspace. This could lead to a potential information disclosure vulnerability, by exposing environment variables meant for a backend/Node.js application.
This PR mitigates this possibility, by restricting the lookup path of environment variables to `sourceRoot` of the related angular.json project and excluding files having `server` in their name (a common pattern for universal apps).
repository: repo_url: https://github.com/kyubisation/angular-server-side-configuration
repo_name: kyubisation/angular-server-side-configuration
description: Configure an angular application at runtime on the server
repo_language: TypeScript
owner: kyubisation
file_changes: filename: .gitignore
change_type: ModificationType.MODIFY
diff: @@ -10,6 +10,7 @@ pids
 *.pid
 *.seed
 *.pid.lock
+test/test-project-*
 
 # Directory for instrumented libs generated by jscoverage/JSCover
 lib-cov

programming_language: Markdown
filename: .nvmrc
change_type: ModificationType.ADD
diff: @@ -0,0 +1 @@
+18
\ No newline at end of file

programming_language: SQL
filename: README.md
change_type: ModificationType.MODIFY
diff: @@ -20,13 +20,33 @@ Starting with version 13, there are experimental builders for the `build` and `s
 To install the experimental builders run `ng add angular-server-side-configuration` and answer
 `Would you like to use the experimental builders for build and serve?` with `y(es)`.
 
+## Changes with version 15.1
+
+With the change in version 15 to search all files for environment variables, a potential
+information disclosure vulnerability was introduced, as unintended environment variables might
+be exposed.
+
+You might be affected, if you use environment variables in your angular.json workspace that expose
+application internals (like used services/tools) or secrets that are also available on the
+deployed environment (e.g. a monorepo that also contains a Node.js backend, which is deployed in
+the same system/container/pod as the frontend bundle).
+
+This should have little to no impact, if you only have a simple angular.json workspace without
+a backend component.
+
+With 15.1 this risk is mitigated, as the lookup path is restricted by adding an option
+`searchPattern` which defaults to `{sourceRoot}/**/!(*server*).ts` (only search `sourceRoot`
+of the related angular.json project and exclude files with `server` in the name).
+
+
 ## Changes with version 15
 
 With version 15 of the Angular CLI, environment files are no longer generated by default. In
 order to adapt, using `ng add angular-server-side-configuration` will try to add the import in
-either `app.module.ts`, `app.components.ts` or `main.ts` depending on availability. The ngssc
-builder will also now search all files for environment variable usages instead of just the
-defined environment file. Due to this, the `ngsscEnvironmentFile` option can be removed.
+either `environment.prod.ts`, `environment.ts`, `app.module.ts`, `app.components.ts` or `main.ts`
+depending on availability. The ngssc builder will also now search all files for environment
+variable usages instead of just the defined environment file. Due to this, the
+`ngsscEnvironmentFile` option can be removed.
 
 ## Getting Started
 
@@ -74,7 +94,9 @@ used environment variables and generate an [ngssc.json](#ngsscjson) in the defin
             "browserTarget": "your-project-name:build",
             // Optional
             // (Defaults to the basename of the index option of the browser target)
-            "filePattern": "index.html"
+            "filePattern": "index.html",
+            // (Defaults to {sourceRoot}/**/!(*server*).ts)
+            "searchPattern": "src/environments/environment*.ts"
           },
           "configurations": {
             "production": {
@@ -93,7 +115,7 @@ used environment variables and generate an [ngssc.json](#ngsscjson) in the defin
 
 To run the ngssc build, run the command `ng run your-project-name:ngsscbuild:production`.
 
-### environment.prod.ts
+### TypeScript files
 
 angular-server-side-configuration supports two variants for using environment variables:
 process.env._ or NG_ENV._

programming_language: Markdown
filename: package.json
change_type: ModificationType.MODIFY
diff: @@ -11,6 +11,7 @@
     "build:demo-i18n": "ng build ngssc-builders-app -c i18n",
     "build": "run-s build:*",
     "format": "prettier --write **/*.{js,ts,css,scss,json,md,html}",
+    "pretest:lib": "yarn -s build:lib",
     "test:lib": "node test/jasmine.js",
     "test:app": "ng test ngssc-app --configuration=ci",
     "test:cli": "cd cli && go test",
@@ -78,6 +79,7 @@
     "cross-env": "^7.0.3",
     "eslint": "^8.28.0",
     "eslint-plugin-import": "2.26.0",
+    "glob": "^9.3.1",
     "jasmine": "~4.5.0",
     "jasmine-core": "~4.5.0",
     "karma": "~6.4.0",

programming_language: TypeScript
filename: schema.json
change_type: ModificationType.MODIFY
diff: @@ -64,7 +64,9 @@
               }
             },
             "additionalProperties": false,
-            "required": ["input"]
+            "required": [
+              "input"
+            ]
           },
           {
             "type": "string",
@@ -100,7 +102,9 @@
               }
             },
             "additionalProperties": false,
-            "required": ["input"]
+            "required": [
+              "input"
+            ]
           },
           {
             "type": "string",
@@ -114,7 +118,12 @@
       "description": "The stylesheet language to use for the application's inline component styles.",
       "type": "string",
       "default": "css",
-      "enum": ["css", "less", "sass", "scss"]
+      "enum": [
+        "css",
+        "less",
+        "sass",
+        "scss"
+      ]
     },
     "stylePreprocessorOptions": {
       "description": "Options to pass to style preprocessors.",
@@ -286,13 +295,21 @@
     "i18nMissingTranslation": {
       "type": "string",
       "description": "How to handle missing translations for i18n.",
-      "enum": ["warning", "error", "ignore"],
+      "enum": [
+        "warning",
+        "error",
+        "ignore"
+      ],
       "default": "warning"
     },
     "i18nDuplicateTranslation": {
       "type": "string",
       "description": "How to handle duplicate translations for i18n.",
-      "enum": ["warning", "error", "ignore"],
+      "enum": [
+        "warning",
+        "error",
+        "ignore"
+      ],
       "default": "warning"
     },
     "localize": {
@@ -322,7 +339,12 @@
       "type": "string",
       "description": "Define the output filename cache-busting hashing mode.",
       "default": "none",
-      "enum": ["none", "all", "media", "bundles"]
+      "enum": [
+        "none",
+        "all",
+        "media",
+        "bundles"
+      ]
     },
     "poll": {
       "type": "number",
@@ -389,7 +411,9 @@
               "description": "The output path of the application's generated HTML index file. The full provided path will be used and will be considered relative to the application's configured output path."
             }
           },
-          "required": ["input"]
+          "required": [
+            "input"
+          ]
         }
       ]
     },
@@ -414,7 +438,11 @@
       "type": "string",
       "description": "Define the crossorigin attribute setting of elements that provide CORS support.",
       "default": "none",
-      "enum": ["none", "anonymous", "use-credentials"]
+      "enum": [
+        "none",
+        "anonymous",
+        "use-credentials"
+      ]
     },
     "allowedCommonJsDependencies": {
       "description": "A list of CommonJS packages that are allowed to be used without a build time warning.",
@@ -432,10 +460,20 @@
       "type": "string",
       "description": "The file pattern, into which the environment variables should be inserted during ngssc insert (Defaults to index.html)",
       "default": ""
+    },
+    "searchPattern": {
+      "type": "string",
+      "description": "The search pattern to use when searching for environment variable occurrences (Defaults to {sourceRoot}/**/!(*server*).ts)",
+      "default": ""
     }
   },
   "additionalProperties": false,
-  "required": ["outputPath", "index", "main", "tsConfig"],
+  "required": [
+    "outputPath",
+    "index",
+    "main",
+    "tsConfig"
+  ],
   "definitions": {
     "assetPattern": {
       "oneOf": [
@@ -468,7 +506,11 @@
             }
           },
           "additionalProperties": false,
-          "required": ["glob", "input", "output"]
+          "required": [
+            "glob",
+            "input",
+            "output"
+          ]
         },
         {
           "type": "string"
@@ -490,7 +532,10 @@
             }
           },
           "additionalProperties": false,
-          "required": ["src", "replaceWith"]
+          "required": [
+            "src",
+            "replaceWith"
+          ]
         },
         {
           "type": "object",
@@ -505,7 +550,10 @@
             }
           },
           "additionalProperties": false,
-          "required": ["replace", "with"]
+          "required": [
+            "replace",
+            "with"
+          ]
         }
       ]
     },
@@ -515,7 +563,15 @@
         "type": {
           "type": "string",
           "description": "The type of budget.",
-          "enum": ["all", "allScript", "any", "anyScript", "anyComponentStyle", "bundle", "initial"]
+          "enum": [
+            "all",
+            "allScript",
+            "any",
+            "anyScript",
+            "anyComponentStyle",
+            "bundle",
+            "initial"
+          ]
         },
         "name": {
           "type": "string",
@@ -551,7 +607,9 @@
         }
       },
       "additionalProperties": false,
-      "required": ["type"]
+      "required": [
+        "type"
+      ]
     }
   }
-}
+}
\ No newline at end of file

programming_language: JavaScript
filename: index.ts
change_type: ModificationType.MODIFY
diff: @@ -33,7 +33,7 @@ export function ngsscServeWebpackBrowser(
   } = {}
 ): Observable<DevServerBuilderOutput> {
   transforms.indexHtml = async (content: string) => {
-    const ngsscContext = await detectVariables(context);
+    const ngsscContext = await detectVariables(context, options.searchPattern);
     const ngssc = buildNgssc(ngsscContext, options);
     const populatedVariables = populateVariables(ngssc.environmentVariables);
     context.logger.info(

programming_language: TypeScript
filename: schema.json
change_type: ModificationType.MODIFY
diff: @@ -104,8 +104,15 @@
       "type": "string",
       "description": "The file pattern, into which the environment variables should be inserted during ngssc insert (Defaults to index.html)",
       "default": ""
+    },
+    "searchPattern": {
+      "type": "string",
+      "description": "The search pattern to use when searching for environment variable occurrences (Defaults to {sourceRoot}/**/!(*server*).ts)",
+      "default": ""
     }
   },
   "additionalProperties": false,
-  "required": ["browserTarget"]
-}
+  "required": [
+    "browserTarget"
+  ]
+}
\ No newline at end of file

programming_language: JavaScript
filename: index.spec.ts
change_type: ModificationType.MODIFY
diff: @@ -1,67 +1,25 @@
-import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'fs';
-import { tmpdir } from 'os';
-import { join } from 'path';
 import { Architect } from '@angular-devkit/architect';
-import { TestingArchitectHost } from '@angular-devkit/architect/testing';
-import { JsonObject, schema } from '@angular-devkit/core';
-import { Logger } from '@angular-devkit/core/src/logger';
+import { normalize, virtualFs } from '@angular-devkit/core';
 
 import { Ngssc } from 'angular-server-side-configuration';
 
-import { Schema } from './schema';
+import { createArchitect, host } from '../../../../test/test-utils';
 
 describe('Ngssc Builder', () => {
-  let tmpDir: string;
-  let distDir: string;
-  let architect: Architect;
-  let architectHost: TestingArchitectHost;
-  let logger: Logger;
-  let logs: string[];
-  const buildConfig = {
-    fileReplacements: [{ replace: 'dummy', with: 'environment.prod.ts' }],
-    index: 'src/index.html',
-    outputPath: 'dist',
-  };
+  const targetSpec = { project: 'app', target: 'ngsscbuild' };
+  let architect: Architect | undefined;
 
   beforeEach(async () => {
-    const registry = new schema.CoreSchemaRegistry();
-    registry.addPostTransform(schema.transforms.addUndefinedDefaults);
-
-    tmpDir = mkdtempSync(join(tmpdir(), 'ngssc-'));
-    writeFileSync(join(tmpDir, 'environment.prod.ts'), envContent, 'utf8');
-    distDir = join(tmpDir, 'dist');
-    mkdirSync(distDir);
-    writeFileSync(join(distDir, 'main.js'), '', 'utf8');
-    architectHost = new TestingArchitectHost(tmpDir, tmpDir);
-    architect = new Architect(architectHost, registry);
-
-    // This will either take a Node package name, or a path to the directory
-    // for the package.json file.
-    await architectHost.addBuilderFromPackage(
-      '../../../../projects/angular-server-side-configuration'
-    );
-    await architectHost.addBuilderFromPackage('..');
-
-    logs = [];
-    logger = new Logger('ngssc');
-    logger.subscribe((m) => logs.push(m.message));
+    architect = undefined;
+    await host.initialize().toPromise();
   });
+  afterEach(async () => host.restore().toPromise());
 
-  function addDummyBuildTarget(config: any = buildConfig) {
-    architectHost.addTarget(
-      { project: 'dummy', target: 'build' },
-      '@angular-devkit/architect:true',
-      config
-    );
-  }
+  async function runNgsscbuild() {
+    architect = (await createArchitect(host.root())).architect;
 
-  async function runNgsscbuild(options: Schema & JsonObject) {
     // A "run" can have multiple outputs, and contains progress information.
-    const run = await architect.scheduleBuilder(
-      'angular-server-side-configuration:ngsscbuild',
-      options,
-      { logger }
-    );
+    const run = await architect.scheduleTarget(targetSpec);
 
     // The "result" member (of type BuilderOutput) is the next output.
     const output = await run.result;
@@ -73,65 +31,36 @@ describe('Ngssc Builder', () => {
     return output;
   }
 
+  function readNgsscJson(): Ngssc {
+    const content = virtualFs.fileBufferToString(
+      host.scopedSync().read(normalize('dist/ngssc.json'))
+    );
+
+    return JSON.parse(content);
+  }
+
   it('should build with process variant', async () => {
-    addDummyBuildTarget();
-    const output = await runNgsscbuild({
-      additionalEnvironmentVariables: [],
-      aotSupport: false,
-      browserTarget: 'dummy:build',
-      filePattern: '',
-    });
+    const output = await runNgsscbuild();
 
     expect(output.success).toBe(true);
-    expect(logs.some((l) => l.includes('ngssc'))).toBeTruthy();
-    const ngssc: Ngssc = JSON.parse(readFileSync(join(distDir, 'ngssc.json'), 'utf8'));
+
+    const ngssc = readNgsscJson();
     expect(ngssc.variant).toEqual('process');
     expect(ngssc.filePattern).toEqual('index.html');
   });
 
   it('should aggregate environment variables', async () => {
     const expected = 'OTHER_VARIABLE';
-    addDummyBuildTarget();
-    const output = await runNgsscbuild({
-      additionalEnvironmentVariables: [expected],
-      aotSupport: false,
-      browserTarget: 'dummy:build',
-      filePattern: '',
-    });
+    host.replaceInFile(
+      'angular.json',
+      '"additionalEnvironmentVariables": [],',
+      `"additionalEnvironmentVariables": ["${expected}"],`
+    );
+    const output = await runNgsscbuild();
 
     expect(output.success).toBe(true);
-    expect(logs.some((l) => l.includes('ngssc'))).toBeTruthy();
-    const ngssc: Ngssc = JSON.parse(readFileSync(join(distDir, 'ngssc.json'), 'utf8'));
+
+    const ngssc = readNgsscJson();
     expect(ngssc.environmentVariables).toContain(expected);
   });
 });
-
-const envContent = `
-import 'angular-server-side-configuration/process';
-
-/**
- * How to use angular-server-side-configuration:
- *
- * Use process.env.NAME_OF_YOUR_ENVIRONMENT_VARIABLE
- *
- * export const environment = {
- *   stringValue: process.env.STRING_VALUE,
- *   stringValueWithDefault: process.env.STRING_VALUE || 'defaultValue',
- *   numberValue: Number(process.env.NUMBER_VALUE),
- *   numberValueWithDefault: Number(process.env.NUMBER_VALUE || 10),
- *   booleanValue: Boolean(process.env.BOOLEAN_VALUE),
- *   booleanValueInverted: process.env.BOOLEAN_VALUE_INVERTED !== 'false',
- * };
- */
-
-export const environment = {
-  production: process.env.PROD !== 'false',
-  apiBackend: process.env.API_BACKEND || 'http://example.com',
-  ternary: process.env.TERNARY ? 'asdf' : 'qwer',
-  simpleValue: process.env.SIMPLE_VALUE,
-  something: {
-    asdf: process.env.OMG || 'omg',
-    qwer: parseInt(process.env.NUMBER || ''),
-  }
-};
-`;

programming_language: TypeScript
filename: index.ts
change_type: ModificationType.MODIFY
diff: @@ -1,9 +1,10 @@
-import { lstatSync, promises, readdirSync } from 'fs';
+import { promises } from 'fs';
 import { basename, join } from 'path';
 import { BuilderContext, createBuilder, targetFromTargetString } from '@angular-devkit/architect';
+import { BrowserBuilderOptions } from '@angular-devkit/build-angular';
 import { json, JsonObject } from '@angular-devkit/core';
 import { Ngssc } from 'angular-server-side-configuration';
-import { BrowserBuilderOptions } from '@angular-devkit/build-angular';
+import { glob } from 'glob';
 import { Schema } from './schema';
 import { VariableDetector } from './variable-detector';
 import { NgsscContext } from './ngssc-context';
@@ -33,15 +34,31 @@ export async function detectVariablesAndBuildNgsscJson(
   context: BuilderContext,
   multiple: boolean = false
 ) {
-  const ngsscContext = await detectVariables(context);
+  const ngsscContext = await detectVariables(context, options.searchPattern);
   const outputPath = join(context.workspaceRoot, browserOptions.outputPath);
   const ngssc = buildNgssc(ngsscContext, options, browserOptions, multiple);
   await writeFileAsync(join(outputPath, 'ngssc.json'), JSON.stringify(ngssc, null, 2), 'utf8');
 }
 
-export async function detectVariables(context: BuilderContext): Promise<NgsscContext> {
+export async function detectVariables(
+  context: BuilderContext,
+  searchPattern?: string | null
+): Promise<NgsscContext> {
+  const projectName = context.target && context.target.project;
+  if (!projectName) {
+    throw new Error('The builder requires a target.');
+  }
+
+  const projectMetadata = await context.getProjectMetadata(projectName);
+  const sourceRoot = projectMetadata.sourceRoot as string | undefined;
+  const defaultSearchPattern = sourceRoot ? `${sourceRoot}/**/!(*server*).ts` : '**/!(*server*).ts';
+
   const detector = new VariableDetector(context.logger);
-  const typeScriptFiles = findTypeScriptFiles(context.workspaceRoot);
+  const typeScriptFiles = await glob(searchPattern || defaultSearchPattern, {
+    absolute: true,
+    cwd: context.workspaceRoot,
+    ignore: ['**/node_modules/**', '**/*.spec.ts', '**/*.d.ts'],
+  });
   let ngsscContext: NgsscContext | null = null;
   for (const file of typeScriptFiles) {
     const fileContent = await readFileAsync(file, 'utf8');
@@ -73,23 +90,6 @@ export async function detectVariables(context: BuilderContext): Promise<NgsscCon
   return ngsscContext;
 }
 
-function findTypeScriptFiles(root: string): string[] {
-  const directory = root.replace(/\\/g, '/');
-  return readdirSync(directory)
-    .map((f) => `${directory}/${f}`)
-    .map((f) => {
-      const stat = lstatSync(f);
-      if (stat.isDirectory()) {
-        return findTypeScriptFiles(f);
-      } else if (stat.isFile() && f.endsWith('.ts') && !f.endsWith('.spec.ts')) {
-        return [f];
-      } else {
-        return [];
-      }
-    })
-    .reduce((current, next) => current.concat(next), []);
-}
-
 export function buildNgssc(
   ngsscContext: NgsscContext,
   options: NgsscBuildSchema,

programming_language: TypeScript
filename: schema.json
change_type: ModificationType.MODIFY
diff: @@ -14,6 +14,11 @@
       "type": "string",
       "description": "The file pattern, into which the environment variables should be inserted during ngssc insert (Defaults to index.html)",
       "default": ""
+    },
+    "searchPattern": {
+      "type": "string",
+      "description": "The search pattern to use when searching for environment variable occurrences (Defaults to {sourceRoot}/**/!(*server*).ts)",
+      "default": ""
     }
   }
 }

programming_language: JavaScript
filename: schema.ts
change_type: ModificationType.MODIFY
diff: @@ -2,4 +2,5 @@ export interface Schema {
   additionalEnvironmentVariables: string[];
   browserTarget: string;
   filePattern: string | null;
+  searchPattern?: string | null;
 }

programming_language: TypeScript
filename: ng-package.json
change_type: ModificationType.MODIFY
diff: @@ -3,5 +3,6 @@
   "dest": "../../dist/angular-server-side-configuration",
   "lib": {
     "entryFile": "src/public-api.ts"
-  }
+  },
+  "allowedNonPeerDependencies": ["glob"]
 }

programming_language: JavaScript
filename: package.json
change_type: ModificationType.MODIFY
diff: @@ -31,6 +31,7 @@
     "@angular/core": "^15.0.0"
   },
   "dependencies": {
-    "tslib": "^2.3.0"
+    "tslib": "^2.3.0",
+    "glob": "^9.3.1"
   }
 }

programming_language: JavaScript
filename: index.ts
change_type: ModificationType.MODIFY
diff: @@ -87,6 +87,8 @@ function addDescriptionToMainFile(options: Schema): Rule {
     }
 
     const insertFile = [
+      join(dirname(mainFile), 'environment.prod.ts'),
+      join(dirname(mainFile), 'environment.ts'),
       join(dirname(mainFile), 'app/app.module.ts'),
       join(dirname(mainFile), 'app/app.component.ts'),
       mainFile,

programming_language: TypeScript
filename: .browserslistrc
change_type: ModificationType.ADD
diff: @@ -0,0 +1,4 @@
+# We want to run tests large with ever green browser so that 
+# we never trigger differential loading as this will slow down the tests.
+
+last 2 Chrome versions
\ No newline at end of file

programming_language: Markdown
filename: .editorconfig
change_type: ModificationType.ADD
diff: @@ -0,0 +1,13 @@
+# Editor configuration, see https://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false

programming_language: Haskell
filename: .gitignore
change_type: ModificationType.ADD
diff: @@ -0,0 +1,3 @@
+# Don't ignore node_modules, this project is not meant to be installed.
+# Also, ~ import path in styles does only looks in the first node_modules found.
+# /node_modules

programming_language: Markdown
filename: README.md
change_type: ModificationType.ADD
diff: @@ -0,0 +1,27 @@
+# HelloWorldApp
+
+This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.7.0-beta.1.
+
+## Development server
+
+Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
+
+## Code scaffolding
+
+Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
+
+## Build
+
+Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
+
+## Running unit tests
+
+Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
+
+## Running end-to-end tests
+
+Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
+
+## Further help
+
+To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/main/README.md).

programming_language: Markdown
filename: angular.json
change_type: ModificationType.ADD
diff: @@ -0,0 +1,162 @@
+{
+  "$schema": "../../../../packages/angular_devkit/core/src/workspace/workspace-schema.json",
+  "version": 1,
+  "newProjectRoot": "./projects",
+  "cli": {
+    "cache": {
+      "enabled": false
+    }
+  },
+  "schematics": {},
+  "projects": {
+    "app": {
+      "root": "src",
+      "sourceRoot": "src",
+      "projectType": "application",
+      "prefix": "app",
+      "schematics": {},
+      "targets": {
+        "build": {
+          "builder": "@angular-devkit/build-angular:browser",
+          "options": {
+            "outputPath": "dist",
+            "index": "src/index.html",
+            "main": "src/main.ts",
+            "polyfills": "src/polyfills.ts",
+            "tsConfig": "src/tsconfig.app.json",
+            "progress": false,
+            "sourceMap": false,
+            "aot": false,
+            "vendorChunk": true,
+            "buildOptimizer": false,
+            "optimization": false,
+            "extractLicenses": false,
+            "namedChunks": true,
+            "assets": [
+              "src/favicon.ico",
+              "src/assets"
+            ],
+            "styles": [
+              "src/styles.css"
+            ],
+            "scripts": []
+          },
+          "configurations": {
+            "production": {
+              "optimization": true,
+              "outputHashing": "all",
+              "sourceMap": false,
+              "namedChunks": false,
+              "aot": true,
+              "extractLicenses": true,
+              "vendorChunk": false,
+              "buildOptimizer": true
+            },
+            "inline-critical-css": {
+              "optimization": {
+                "styles": {
+                  "minify": true,
+                  "inlineCritical": true
+                },
+                "scripts": true,
+                "fonts": true
+              }
+            }
+          }
+        },
+        "server": {
+          "builder": "@angular-devkit/build-angular:server",
+          "options": {
+            "outputPath": "dist-server",
+            "main": "src/main.server.ts",
+            "tsConfig": "src/tsconfig.server.json",
+            "progress": false,
+            "sourceMap": true,
+            "optimization": false,
+            "extractLicenses": false
+          }
+        },
+        "app-shell": {
+          "builder": "@angular-devkit/build-angular:app-shell",
+          "options": {
+            "browserTarget": "app:build",
+            "serverTarget": "app:server"
+          },
+          "configurations": {
+            "production": {
+              "browserTarget": "app:build:production"
+            }
+          }
+        },
+        "serve": {
+          "builder": "@angular-devkit/build-angular:dev-server",
+          "options": {
+            "browserTarget": "app:build",
+            "watch": false
+          },
+          "configurations": {
+            "production": {
+              "browserTarget": "app:build:production"
+            }
+          }
+        },
+        "extract-i18n": {
+          "builder": "@angular-devkit/build-angular:extract-i18n",
+          "options": {
+            "browserTarget": "app:build",
+            "progress": false,
+            "outputPath": "src"
+          }
+        },
+        "test": {
+          "builder": "@angular-devkit/build-angular:karma",
+          "options": {
+            "polyfills": ["zone.js", "zone.js/testing"],
+
+            "tsConfig": "src/tsconfig.spec.json",
+            "karmaConfig": "karma.conf.js",
+            "browsers": "ChromeHeadlessCI",
+            "progress": false,
+            "watch": false,
+            "styles": [
+              {
+                "input": "src/styles.css"
+              }
+            ],
+            "scripts": [],
+            "assets": [
+              "src/favicon.ico",
+              "src/assets"
+            ]
+          }
+        },
+        "ngsscbuild": {
+          "builder": "angular-server-side-configuration:ngsscbuild",
+          "options": {
+            "additionalEnvironmentVariables": [],
+            "browserTarget": "app:build"
+          },
+          "configurations": {
+            "production": {
+              "browserTarget": "app:build:production"
+            }
+          }
+        }
+      }
+    },
+    "app-e2e": {
+      "root": "e2e",
+      "projectType": "application",
+      "targets": {
+        "e2e": {
+          "builder": "@angular-devkit/build-angular:protractor",
+          "options": {
+            "protractorConfig": "protractor.conf.js",
+            "devServerTarget": "app:serve",
+            "webdriverUpdate": false
+          }
+        }
+      }
+    }
+  }
+}

programming_language: TypeScript
filename: karma.conf.js
change_type: ModificationType.ADD
diff: @@ -0,0 +1,53 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+// Karma configuration file, see link for more information
+// https://karma-runner.github.io/1.0/config/configuration-file.html
+
+const path = require('path');
+process.env.CHROME_BIN = require('puppeteer').executablePath();
+
+module.exports = function(config) {
+  config.set({
+    basePath: '',
+    frameworks: ['jasmine', '@angular-devkit/build-angular'],
+    plugins: [
+      require('karma-jasmine'),
+      require('karma-chrome-launcher'),
+      require('karma-jasmine-html-reporter'),
+      require('karma-coverage'),
+      require('@angular-devkit/build-angular/plugins/karma'),
+    ],
+    client: {
+      clearContext: false, // leave Jasmine Spec Runner output visible in browser
+    },
+    jasmineHtmlReporter: {
+      suppressAll: true // removes the duplicated traces
+    },
+    coverageReporter: {
+      dir: path.join(__dirname, './coverage'),
+      subdir: '.',
+      reporters: [
+        {type: 'lcov'},
+      ],
+    },
+    reporters: ['progress', 'kjhtml'],
+    port: 9876,
+    colors: true,
+    logLevel: config.LOG_INFO,
+    autoWatch: true,
+    browsers: ['Chrome'],
+    customLaunchers: {
+      ChromeHeadlessCI: {
+        base: 'ChromeHeadless',
+        flags: ['--disable-gpu'],
+      },
+    },
+    singleRun: false,
+  });
+};

programming_language: JavaScript
filename: protractor.conf.js
change_type: ModificationType.ADD
diff: @@ -0,0 +1,43 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+// Protractor configuration file, see link for more information
+// https://github.com/angular/protractor/blob/master/lib/config.ts
+
+const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
+const { resolve } = require('path');
+
+exports.config = {
+  allScriptsTimeout: 11000,
+  specs: ['./e2e/**/*.e2e-spec.ts'],
+  capabilities: {
+    browserName: 'chrome',
+    chromeOptions: {
+      args: ['--headless', '--disable-gpu', '--window-size=800,600'],
+      binary: require('puppeteer').executablePath(),
+    },
+  },
+  directConnect: true,
+  baseUrl: 'http://localhost:4200/',
+  framework: 'jasmine',
+  jasmineNodeOpts: {
+    showColors: true,
+    defaultTimeoutInterval: 30000,
+    print: function() {},
+  },
+  onPrepare() {
+    require('ts-node').register({
+      project: resolve(__dirname, './e2e/tsconfig.e2e.json'),
+    });
+    jasmine.getEnv().addReporter(new SpecReporter({
+      spec: {
+        displayStacktrace: StacktraceOption.PRETTY
+      }
+    }));
+  },
+};

programming_language: TypeScript
filename: app.component.html
change_type: ModificationType.ADD
diff: @@ -0,0 +1,22 @@
+<!--The content below is only a placeholder and can be replaced.-->
+<div style="text-align:center">
+  <h1>
+    Welcome to {{ title }}!
+  </h1>
+  <img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
+</div>
+<h2>Here are some links to help you start: </h2>
+<ul>
+  <li>
+    <h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
+  </li>
+  <li>
+    <h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
+  </li>
+  <li>
+    <h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
+  </li>
+</ul>
+
+<p i18n>i18n test</p>
+

programming_language: HTML
filename: app.component.spec.ts
change_type: ModificationType.ADD
diff: @@ -0,0 +1,33 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import { TestBed } from '@angular/core/testing';
+import { AppComponent } from './app.component';
+describe('AppComponent', () => {
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [AppComponent],
+    }).compileComponents();
+  });
+  it('should create the app', () => {
+    const fixture = TestBed.createComponent(AppComponent);
+    const app = fixture.debugElement.componentInstance;
+    expect(app).toBeTruthy();
+  });
+  it(`should have as title 'app'`, () => {
+    const fixture = TestBed.createComponent(AppComponent);
+    const app = fixture.debugElement.componentInstance;
+    expect(app.title).toEqual('app');
+  });
+  it('should render title in a h1 tag', () => {
+    const fixture = TestBed.createComponent(AppComponent);
+    fixture.detectChanges();
+    const compiled = fixture.debugElement.nativeElement;
+    expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
+  });
+});

programming_language: TypeScript
filename: app.component.ts
change_type: ModificationType.ADD
diff: @@ -0,0 +1,18 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import { Component } from '@angular/core';
+
+@Component({
+  selector: 'app-root',
+  templateUrl: './app.component.html',
+  styleUrls: ['./app.component.css'],
+})
+export class AppComponent {
+  title = process.env.TEST;
+}

programming_language: TypeScript
filename: app.module.ts
change_type: ModificationType.ADD
diff: @@ -0,0 +1,26 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+
+
+import { AppComponent } from './app.component';
+
+
+@NgModule({
+  declarations: [
+    AppComponent
+  ],
+  imports: [
+    BrowserModule
+  ],
+  providers: [],
+  bootstrap: [AppComponent]
+})
+export class AppModule { }

programming_language: TypeScript
filename: app.server.module.ts
change_type: ModificationType.ADD
diff: @@ -0,0 +1,22 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import { NgModule } from '@angular/core';
+import { ServerModule } from '@angular/platform-server';
+
+import { AppModule } from './app.module';
+import { AppComponent } from './app.component';
+
+@NgModule({
+  imports: [
+    AppModule,
+    ServerModule,
+  ],
+  bootstrap: [AppComponent],
+})
+export class AppServerModule {}

programming_language: TypeScript
filename: index.html
change_type: ModificationType.ADD
diff: @@ -0,0 +1,14 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <title>HelloWorldApp</title>
+  <base href="/">
+
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <link rel="icon" type="image/x-icon" href="favicon.ico">
+</head>
+<body>
+  <app-root></app-root>
+</body>
+</html>

programming_language: HTML
filename: main.server.ts
change_type: ModificationType.ADD
diff: @@ -0,0 +1,9 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+export { AppServerModule } from './app/app.server.module';

programming_language: TypeScript
filename: main.ts
change_type: ModificationType.ADD
diff: @@ -0,0 +1,15 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic()
+  .bootstrapModule(AppModule)
+  .catch(err => console.log(err));

programming_language: TypeScript
filename: polyfills.ts
change_type: ModificationType.ADD
diff: @@ -0,0 +1,70 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ *      file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/guide/browser-support
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/** IE10 and IE11 requires the following for NgClass support on SVG elements */
+// import 'classlist.js';  // Run `npm install --save classlist.js`.
+
+/**
+ * Web Animations `@angular/platform-browser/animations`
+ * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
+ * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
+ */
+// import 'web-animations-js';  // Run `npm install --save web-animations-js`.
+
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following flags
+ * because those flags need to be set before `zone.js` being loaded, and webpack
+ * will put import in the top of bundle, so user need to create a separate file
+ * in this directory (for example: zone-flags.ts), and put the following flags
+ * into that file, and then add the following code before importing zone.js.
+ * import './zone-flags.ts';
+ *
+ * The flags allowed in zone-flags.ts are listed here.
+ *
+ * The following flags will work for all browsers.
+ *
+ * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
+ * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
+ * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
+ *
+ *  in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
+ *  with the following flag, it will bypass `zone.js` patch for IE/Edge
+ *
+ *  (window as any).__Zone_enable_cross_context_check = true;
+ *
+ */
+
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js'; // Included with Angular CLI.
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */

programming_language: TypeScript
filename: messages.xlf
change_type: ModificationType.ADD
diff: @@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+  <file source-language="en" datatype="plaintext" original="ng2.template">
+    <body>
+      <trans-unit id="8261100fbc1830397116d0a032e7c8f54af93c68" datatype="html">
+        <source>i18n test</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">app/app.component.ts</context>
+          <context context-type="linenumber">21</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">app/app.component.ts</context>
+          <context context-type="linenumber">23</context>
+        </context-group>
+      </trans-unit>
+    </body>
+  </file>
+</xliff>

programming_language: HTML
filename: styles.css
change_type: ModificationType.ADD
diff: @@ -0,0 +1 @@
+/* You can add global styles to this file, and also import other style files */

programming_language: CSS
filename: tsconfig.app.json
change_type: ModificationType.ADD
diff: @@ -0,0 +1,14 @@
+{
+  "extends": "../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../out-tsc/app",
+    "types": ["@angular/localize", "node"]
+  },
+  "files": [
+    "main.ts",
+    "polyfills.ts"
+  ],
+  "include": [
+    "**/*.d.ts"
+  ]
+}

programming_language: CoffeeScript
filename: tsconfig.server.json
change_type: ModificationType.ADD
diff: @@ -0,0 +1,11 @@
+{
+  "extends": "./tsconfig.app.json",
+  "compilerOptions": {
+    "outDir": "../dist-server",
+    "baseUrl": "./",
+    "types": ["@angular/localize", "node"]
+  },
+  "files": [
+    "main.server.ts"
+  ]
+}

programming_language: TypeScript
filename: tsconfig.spec.json
change_type: ModificationType.ADD
diff: @@ -0,0 +1,17 @@
+{
+  "extends": "../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../out-tsc/spec",
+    "types": [
+      "jasmine",
+      "@angular/localize"
+    ]
+  },
+  "files": [
+    "polyfills.ts"
+  ],
+  "include": [
+    "**/*.spec.ts",
+    "**/*.d.ts"
+  ]
+}

programming_language: CoffeeScript
filename: typings.d.ts
change_type: ModificationType.ADD
diff: @@ -0,0 +1,13 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+/* SystemJS module definition */
+declare var module: NodeModule;
+interface NodeModule {
+  id: string;
+}

programming_language: TypeScript
filename: tsconfig.json
change_type: ModificationType.ADD
diff: @@ -0,0 +1,27 @@
+{
+  "compileOnSave": false,
+  "compilerOptions": {
+    "baseUrl": "./",
+    "outDir": "./dist/out-tsc",
+    "sourceMap": true,
+    "declaration": false,
+    "moduleResolution": "node",
+    "emitDecoratorMetadata": true,
+    "experimentalDecorators": true,
+    "target": "es2022",
+    "module": "es2022",
+    "useDefineForClassFields": false,
+    "typeRoots": [
+      "node_modules/@types"
+    ],
+    "lib": [
+      "es2022",
+      "dom"
+    ]
+  },
+  "angularCompilerOptions": {
+    "enableIvy": true,
+    "disableTypeScriptVersionCheck": true,
+    "strictTemplates": true
+  }
+}

programming_language: TypeScript
filename: jasmine.js
change_type: ModificationType.MODIFY
diff: @@ -9,5 +9,12 @@ if (module === require.main) {
   const jasmine = new Jasmine({ projectBaseDir: path.resolve() });
   jasmine.exitOnCompletion = true;
   jasmine.addMatchingSpecFiles(['projects/angular-server-side-configuration/**/*.spec.ts']);
-  jasmine.execute();
+  jasmine
+    .execute()
+    .then((result) => {
+      if (result.failedExpectations.length) {
+        console.error(result.failedExpectations);
+      }
+    })
+    .catch((e) => console.error(e));
 }

programming_language: TypeScript
filename: test-utils.ts
change_type: ModificationType.ADD
diff: @@ -0,0 +1,59 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import { Architect, BuilderOutput } from '@angular-devkit/architect';
+import { WorkspaceNodeModulesArchitectHost } from '@angular-devkit/architect/node';
+import { TestProjectHost, TestingArchitectHost } from '@angular-devkit/architect/testing';
+import { Path, getSystemPath, join, normalize, schema, workspaces } from '@angular-devkit/core';
+
+// Default timeout for large specs is 2.5 minutes.
+jasmine.DEFAULT_TIMEOUT_INTERVAL = 150000;
+
+export const workspaceRoot = join(normalize(__dirname), `hello-world-app/`);
+export const host = new TestProjectHost(workspaceRoot);
+export const outputPath: Path = normalize('dist');
+
+export const browserTargetSpec = { project: 'app', target: 'build' };
+export const devServerTargetSpec = { project: 'app', target: 'serve' };
+export const extractI18nTargetSpec = { project: 'app', target: 'extract-i18n' };
+export const karmaTargetSpec = { project: 'app', target: 'test' };
+export const tslintTargetSpec = { project: 'app', target: 'lint' };
+export const protractorTargetSpec = { project: 'app-e2e', target: 'e2e' };
+
+export async function createArchitect(workspaceRoot: Path) {
+  const registry = new schema.CoreSchemaRegistry();
+  registry.addPostTransform(schema.transforms.addUndefinedDefaults);
+  const workspaceSysPath = getSystemPath(workspaceRoot);
+
+  const { workspace } = await workspaces.readWorkspace(
+    workspaceSysPath,
+    workspaces.createWorkspaceHost(host)
+  );
+  const architectHost = new TestingArchitectHost(
+    workspaceSysPath,
+    workspaceSysPath,
+    new WorkspaceNodeModulesArchitectHost(workspace, workspaceSysPath)
+  );
+  await architectHost.addBuilderFromPackage('..');
+  //require('ts-node').register(require('../projects/angular-server-side-configuration/builders/tsconfig.json'));
+  await architectHost.addBuilderFromPackage(
+    '../../../../dist/angular-server-side-configuration'
+  );
+  const architect = new Architect(architectHost, registry);
+
+  return {
+    workspace,
+    architectHost,
+    architect,
+  };
+}
+
+export interface BrowserBuildOutput {
+  output: BuilderOutput;
+  files: { [file: string]: Promise<string> };
+}

programming_language: TypeScript
filename: yarn.lock
change_type: ModificationType.MODIFY
diff: @@ -4363,6 +4363,16 @@ glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7:
     once "^1.3.0"
     path-is-absolute "^1.0.0"
 
+glob@^9.3.1:
+  version "9.3.1"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.1.tgz#4a15cf72479a440f77e97805b21e1b5b6e4fb18a"
+  integrity sha512-qERvJb7IGsnkx6YYmaaGvDpf77c951hICMdWaFXyH3PlVob8sbPJJyJX0kWkiCWyXUzoy9UOTNjGg0RbD8bYIw==
+  dependencies:
+    fs.realpath "^1.0.0"
+    minimatch "^7.4.1"
+    minipass "^4.2.4"
+    path-scurry "^1.6.1"
+
 globals@^11.1.0:
   version "11.12.0"
   resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
@@ -5433,6 +5443,11 @@ lru-cache@^6.0.0:
   dependencies:
     yallist "^4.0.0"
 
+lru-cache@^7.14.1:
+  version "7.18.3"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89"
+  integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==
+
 lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1:
   version "7.14.1"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.14.1.tgz#8da8d2f5f59827edb388e63e459ac23d6d408fea"
@@ -5644,6 +5659,13 @@ minimatch@^5.0.1:
   dependencies:
     brace-expansion "^2.0.1"
 
+minimatch@^7.4.1:
+  version "7.4.2"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.2.tgz#157e847d79ca671054253b840656720cb733f10f"
+  integrity sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA==
+  dependencies:
+    brace-expansion "^2.0.1"
+
 minimatch@~3.0.4:
   version "3.0.8"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1"
@@ -5730,6 +5752,11 @@ minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6:
   dependencies:
     yallist "^4.0.0"
 
+minipass@^4.0.2, minipass@^4.2.4:
+  version "4.2.5"
+  resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.5.tgz#9e0e5256f1e3513f8c34691dd68549e85b2c8ceb"
+  integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==
+
 minizlib@^2.1.1, minizlib@^2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
@@ -6367,6 +6394,14 @@ path-parse@^1.0.7:
   resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
   integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
 
+path-scurry@^1.6.1:
+  version "1.6.2"
+  resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.2.tgz#72783838113a4a0cec155323b2c01eb1b193396f"
+  integrity sha512-J6MQNh56h6eHFY3vsQ+Lq+zKPwn71POieutmVt2leU8W+zz8HVIdJyn3I3Zs6IKbIQtuKXirVjTBFNBcbFO44Q==
+  dependencies:
+    lru-cache "^7.14.1"
+    minipass "^4.0.2"
+
 path-to-regexp@0.1.7:
   version "0.1.7"
   resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"

programming_language: PowerShell