env => {
        const title = BROWSER_TITLES[browser]

        const buildDir = path.resolve(process.cwd(), `${BUILDS_DIR}/${browser}`)

        writeManifest(env, browser, buildDir)
        writeSchema(env, browser, buildDir)

        return () => {
            // Allow only building for specific browser targets. Useful in local dev for faster
            // builds.
            if (process.env.TARGETS && !process.env.TARGETS.includes(browser)) {
                return
            }

            signale.await(`Building the ${title} ${env} bundle`)

            copyDist(buildDir)

            const zipDest = path.resolve(process.cwd(), `${BUILDS_DIR}/bundles/${BROWSER_BUNDLE_ZIPS[browser]}`)
            if (zipDest) {
                shelljs.mkdir('-p', `./${BUILDS_DIR}/bundles`)
                shelljs.exec(`cd ${buildDir} && zip -q -r ${zipDest} *`)
            }

            signale.success(`Done building the ${title} ${env} bundle`)
        }
    }


() => {
            // Allow only building for specific browser targets. Useful in local dev for faster
            // builds.
            if (process.env.TARGETS && !process.env.TARGETS.includes(browser)) {
                return
            }

            signale.await(`Building the ${title} ${env} bundle`)

            copyDist(buildDir)

            const zipDest = path.resolve(process.cwd(), `${BUILDS_DIR}/bundles/${BROWSER_BUNDLE_ZIPS[browser]}`)
            if (zipDest) {
                shelljs.mkdir('-p', `./${BUILDS_DIR}/bundles`)
                shelljs.exec(`cd ${buildDir} && zip -q -r ${zipDest} *`)
            }

            signale.success(`Done building the ${title} ${env} bundle`)
        }


export type BuildEnv = 'dev' | 'prod'


type Browser = 'firefox' | 'chrome'


export type FilteredInfiniteScrollGridTestsQueryVariables = {};


export type FilteredInfiniteScrollGridTestsQueryResponse = {
    readonly show: ({
        readonly filteredArtworks: ({
            readonly " $fragmentRefs": FilteredInfiniteScrollGrid_filteredArtworks$ref;
        }) | null;
    }) | null;
};


export type FilteredInfiniteScrollGridTestsQuery = {
    readonly response: FilteredInfiniteScrollGridTestsQueryResponse;
    readonly variables: FilteredInfiniteScrollGridTestsQueryVariables;
};


@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    StoreModule.forRoot(reducers, {
      metaReducers,
      runtimeChecks: {
        strictStateImmutability: true,
        strictActionImmutability: true
      }
    }),
    StoreDevtoolsModule.instrument({ maxAge: 25, logOnly: environment.production }),
    EffectsModule.forRoot([AppEffects]),
    StoreRouterConnectingModule.forRoot(),
    EntityDataModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }


export class ClimateChart {
    private options = {
        chart: {
            // backgroundColor: '#EFEFFB',
            plotBorderWidth: 0,
            animation: false,
            plotShadow: false,
            height: 350
        },
        title: {
            text: '',
        },
        exporting: {
            enabled: false
        },
        legend: {
            itemStyle: {
                width: 40
            },
            title: {
                text: 'click to
show/hide',
                style: {
                    fontWeight: 'normal',
                    fontSize: '10px',
                    fontStyle: 'italic'
                }
            },
            align: 'right',
            verticalAlign: 'top',
            layout: 'vertical'

        },
        tooltip: {
            useHTML: true,
            formatter: function() {
                return this.x + ' ' + this.series.name + '
' + this.y + this.series.options.tooltip.valueSuffix;
            }
        },
        credits: {
            enabled: false
        },
        xAxis: {
            crosshair: {
                snap: false
            },
            categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
        },
        yAxis: {
            title: {
                text: ''
            },
            gridLineWidth: 1
        },
        plotOptions: {
            series: {
                states: {
                    hover: {
                        lineWidthPlus: 0
                    }
                },
                marker: {
                    enabled: false
                },
                lineWidth: 2,
            },

        },
        loading: false,
    };

    constructor(config: Object) {
        this.setOptions(config);
    }

    /**
     * Get the highchart plot options.
     * @return {Object} highchart options
     */
    get plotOptions(): Object {
        return this.options;
    }

    /**
     * Set and Add the given Graph options
     * @param  {Object} config object of highcharts options
     */
    private setOptions(config: Object): void {
        for (let option in config) {
            if (config.hasOwnProperty(option)) {
                this.options[option] = config[option];
            }
        }
    }
}


/**
     * Set and Add the given Graph options
     * @param  {Object} config object of highcharts options
     */
private setOptions(config: Object): void {
        for (let option in config) {
            if (config.hasOwnProperty(option)) {
                this.options[option] = config[option];
            }
        }
    }


(name: string, required: boolean): string | undefined =>
  required ? requiredEnvString(name) : optionalEnvString(name)


(name: string): string | undefined => {
  const envVariable = process.env[name];

  if (typeof envVariable === 'string' && envVariable.length !== 0) {
    return envVariable;
  }

  return undefined;
}


(name: string, defaultValue?: string): string => {
  const envVariable = process.env[name];

  if (typeof envVariable === 'string' && envVariable.length !== 0) {
    return envVariable;
  }

  if (typeof defaultValue === 'string' && defaultValue.length !== 0) {
    return defaultValue;
  }

  console.error(`Missing required environment variable '${name}'.`);
  process.exit(1);
}


// Basic checks to determine if we can consider a and b to be mergable
function areEntriesEqual(a: CacheEntry, b: CacheEntry): boolean {
	if (a.version !== b.version) {
		// Outdated cache file
		return false;
	}

	if (a.configHash !== b.configHash) {
		// Project config has been changed since this was last updated
		return false;
	}

	if (a.mtime !== b.mtime) {
		// File has been changed
		return false;
	}

	return true;
}


(filename) => {
			return this.server.cache.handleDeleted(filename);
		}


async () => {
			// Wait on possible running writePending
			await this.runningWritePromise;

			// Write any remaining
			await this.writePending("end");
		}


() => {
				this.runningWritePromise = this.writePending("queue").catch((err) => {
					this.server.onFatalError(err);
				}).finally(() => {
					// Finished running
					this.runningWritePromise = undefined;
				});
			}


(err) => {
					this.server.onFatalError(err);
				}


() => {
					// Finished running
					this.runningWritePromise = undefined;
				}


export default class Cache {
	constructor(server: Server) {
		this.server = server;
		this.loadedEntries = new AbsoluteFilePathMap();
		this.disabled =
			!server.options.forceCacheEnabled && process.env.ROME_CACHE === "0";
		this.cachePath = server.userConfig.cachePath;

		this.runningWritePromise = undefined;
		this.pendingWriteTimer = undefined;
		this.pendingWrites = new AbsoluteFilePathMap();
	}

	disabled: boolean;
	loadedEntries: AbsoluteFilePathMap<CacheEntry>;
	server: Server;
	cachePath: AbsoluteFilePath;

	runningWritePromise: undefined | Promise<void>;
	pendingWrites: AbsoluteFilePathMap<CacheEntry>;
	pendingWriteTimer: undefined | NodeJS.Timeout;

	async init() {
		this.server.memoryFs.deletedFileEvent.subscribe((filename) => {
			return this.server.cache.handleDeleted(filename);
		});

		const {memoryFs} = this.server;
		await createDirectory(this.cachePath);
		await memoryFs.watch(this.cachePath);

		this.server.endEvent.subscribe(async () => {
			// Wait on possible running writePending
			await this.runningWritePromise;

			// Write any remaining
			await this.writePending("end");
		});
	}

	async writePending(reason: "queue" | "end") {
		// Clear timer since we're now running
		const {pendingWriteTimer} = this;
		if (pendingWriteTimer !== undefined) {
			clearTimeout(pendingWriteTimer);
		}

		const {pendingWrites} = this;
		this.pendingWrites = new AbsoluteFilePathMap();

		// Write pending files
		const filelinks: Array<string> = [];
		for (const [path, entry] of pendingWrites) {
			filelinks.push(path.toMarkup());
			await writeFile(path, stringifyJSON(entry));
		}

		// Log
		const {logger} = this.server;
		if (filelinks.length > 0) {
			logger.info(`[Cache] Wrote entries due to ${reason}`);
			logger.list(filelinks);
		}
	}

	addPendingWrite(path: AbsoluteFilePath, entry: CacheEntry) {
		this.pendingWrites.set(path, entry);

		// Set a write timer
		const {pendingWriteTimer} = this;
		if (pendingWriteTimer !== undefined) {
			return;
		}

		this.pendingWriteTimer = setTimeout(
			() => {
				this.runningWritePromise = this.writePending("queue").catch((err) => {
					this.server.onFatalError(err);
				}).finally(() => {
					// Finished running
					this.runningWritePromise = undefined;
				});
			},
			BATCH_WRITES_MS,
		);
	}

	async createEmptyEntry(path: AbsoluteFilePath): Promise<CacheEntry> {
		const {projectManager, memoryFs} = this.server;

		const project: ProjectDefinition = await projectManager.assertProject(path);

		const configHashes = [...project.meta.configHashes];
		const pkg = this.server.memoryFs.getOwnedManifest(path);
		if (pkg !== undefined) {
			configHashes.push(pkg.hash);
		}

		const entry: CacheEntry = {
			version: VERSION,
			projectDir: project.folder.join(),
			configHash: configHashes.join(";"),
			mtime: memoryFs.getMtime(path),
			compile: {},
			analyzeDependencies: undefined,
			moduleSignature: undefined,
			lint: {},
		};

		return entry;
	}

	getCacheFilename(path: AbsoluteFilePath): AbsoluteFilePath {
		const uid = this.server.projectManager.getUid(path, true);
		return this.cachePath.append(uid);
	}

	async handleDeleted(path: AbsoluteFilePath) {
		// Handle the file not existing
		const cacheFilename = this.getCacheFilename(path);
		await removeFile(cacheFilename);
		this.loadedEntries.delete(path);
	}

	async get(path: AbsoluteFilePath): Promise<CacheEntry> {
		const emptyEntry = await this.createEmptyEntry(path);

		// If we have a loaded memory entry, make sure it's valid compared to the default entry (file changes etc)
		let loaded = this.loadedEntries.get(path);
		if (loaded !== undefined && areEntriesEqual(loaded, emptyEntry)) {
			return loaded;
		}

		if (this.disabled) {
			return emptyEntry;
		}

		const cacheFilename = this.getCacheFilename(path);
		const entry = await this.checkPossibleDiskCacheEntry(
			cacheFilename,
			emptyEntry,
		);
		this.loadedEntries.set(path, entry);
		return entry;
	}

	async checkPossibleDiskCacheEntry(
		cacheFilename: AbsoluteFilePath,
		emptyEntry: CacheEntry,
	): Promise<CacheEntry> {
		const {memoryFs} = this.server;

		if (!memoryFs.exists(cacheFilename)) {
			return emptyEntry;
		}

		try {
			const json = await readFileText(cacheFilename);
			const obj = JSON.parse(json);

			if (areEntriesEqual(emptyEntry, obj)) {
				return {...emptyEntry, ...obj};
			} else {
				// If the entries aren't equal then there's something wrong with the cache entry
				await this.handleDeleted(cacheFilename);
				return emptyEntry;
			}
		} catch (err) {
			// TODO add some heuristic to only catch json and cache permission errors
			return emptyEntry;
		}
	}

	async update(
		path: AbsoluteFilePath,
		partialEntryCallback:
			| Partial<CacheEntry>
			| ((entry: CacheEntry) => Partial<CacheEntry>),
	) {
		const currEntry = await this.get(path);
		const partialEntry: Partial<CacheEntry> =
			typeof partialEntryCallback === "function"
				? partialEntryCallback(currEntry)
				: partialEntryCallback;

		const entry: CacheEntry = {
			...currEntry,
			...partialEntry,
		};

		// TODO should batch these and write during idle time
		const cacheFilename = this.getCacheFilename(path);
		this.loadedEntries.set(path, entry);

		if (this.disabled) {
			return;
		}

		await createDirectory(cacheFilename.getParent());
		this.addPendingWrite(cacheFilename, entry);
	}
}


export type CacheEntry = {
	version: string;
	configHash: string;
	projectDir: string;
	mtime: number;
	compile: {
		[key: string]: WorkerCompileResult;
	};
	lint: {
		[key: string]: WorkerLintResult;
	};
	analyzeDependencies: undefined | WorkerAnalyzeDependencyResult;
	moduleSignature: undefined | ModuleSignature;
};


async init() {
		this.server.memoryFs.deletedFileEvent.subscribe((filename) => {
			return this.server.cache.handleDeleted(filename);
		});

		const {memoryFs} = this.server;
		await createDirectory(this.cachePath);
		await memoryFs.watch(this.cachePath);

		this.server.endEvent.subscribe(async () => {
			// Wait on possible running writePending
			await this.runningWritePromise;

			// Write any remaining
			await this.writePending("end");
		});
	}


async writePending(reason: "queue" | "end") {
		// Clear timer since we're now running
		const {pendingWriteTimer} = this;
		if (pendingWriteTimer !== undefined) {
			clearTimeout(pendingWriteTimer);
		}

		const {pendingWrites} = this;
		this.pendingWrites = new AbsoluteFilePathMap();

		// Write pending files
		const filelinks: Array<string> = [];
		for (const [path, entry] of pendingWrites) {
			filelinks.push(path.toMarkup());
			await writeFile(path, stringifyJSON(entry));
		}

		// Log
		const {logger} = this.server;
		if (filelinks.length > 0) {
			logger.info(`[Cache] Wrote entries due to ${reason}`);
			logger.list(filelinks);
		}
	}


addPendingWrite(path: AbsoluteFilePath, entry: CacheEntry) {
		this.pendingWrites.set(path, entry);

		// Set a write timer
		const {pendingWriteTimer} = this;
		if (pendingWriteTimer !== undefined) {
			return;
		}

		this.pendingWriteTimer = setTimeout(
			() => {
				this.runningWritePromise = this.writePending("queue").catch((err) => {
					this.server.onFatalError(err);
				}).finally(() => {
					// Finished running
					this.runningWritePromise = undefined;
				});
			},
			BATCH_WRITES_MS,
		);
	}


async createEmptyEntry(path: AbsoluteFilePath): Promise<CacheEntry> {
		const {projectManager, memoryFs} = this.server;

		const project: ProjectDefinition = await projectManager.assertProject(path);

		const configHashes = [...project.meta.configHashes];
		const pkg = this.server.memoryFs.getOwnedManifest(path);
		if (pkg !== undefined) {
			configHashes.push(pkg.hash);
		}

		const entry: CacheEntry = {
			version: VERSION,
			projectDir: project.folder.join(),
			configHash: configHashes.join(";"),
			mtime: memoryFs.getMtime(path),
			compile: {},
			analyzeDependencies: undefined,
			moduleSignature: undefined,
			lint: {},
		};

		return entry;
	}


getCacheFilename(path: AbsoluteFilePath): AbsoluteFilePath {
		const uid = this.server.projectManager.getUid(path, true);
		return this.cachePath.append(uid);
	}


async handleDeleted(path: AbsoluteFilePath) {
		// Handle the file not existing
		const cacheFilename = this.getCacheFilename(path);
		await removeFile(cacheFilename);
		this.loadedEntries.delete(path);
	}


async get(path: AbsoluteFilePath): Promise<CacheEntry> {
		const emptyEntry = await this.createEmptyEntry(path);

		// If we have a loaded memory entry, make sure it's valid compared to the default entry (file changes etc)
		let loaded = this.loadedEntries.get(path);
		if (loaded !== undefined && areEntriesEqual(loaded, emptyEntry)) {
			return loaded;
		}

		if (this.disabled) {
			return emptyEntry;
		}

		const cacheFilename = this.getCacheFilename(path);
		const entry = await this.checkPossibleDiskCacheEntry(
			cacheFilename,
			emptyEntry,
		);
		this.loadedEntries.set(path, entry);
		return entry;
	}


async checkPossibleDiskCacheEntry(
		cacheFilename: AbsoluteFilePath,
		emptyEntry: CacheEntry,
	): Promise<CacheEntry> {
		const {memoryFs} = this.server;

		if (!memoryFs.exists(cacheFilename)) {
			return emptyEntry;
		}

		try {
			const json = await readFileText(cacheFilename);
			const obj = JSON.parse(json);

			if (areEntriesEqual(emptyEntry, obj)) {
				return {...emptyEntry, ...obj};
			} else {
				// If the entries aren't equal then there's something wrong with the cache entry
				await this.handleDeleted(cacheFilename);
				return emptyEntry;
			}
		} catch (err) {
			// TODO add some heuristic to only catch json and cache permission errors
			return emptyEntry;
		}
	}


async update(
		path: AbsoluteFilePath,
		partialEntryCallback:
			| Partial<CacheEntry>
			| ((entry: CacheEntry) => Partial<CacheEntry>),
	) {
		const currEntry = await this.get(path);
		const partialEntry: Partial<CacheEntry> =
			typeof partialEntryCallback === "function"
				? partialEntryCallback(currEntry)
				: partialEntryCallback;

		const entry: CacheEntry = {
			...currEntry,
			...partialEntry,
		};

		// TODO should batch these and write during idle time
		const cacheFilename = this.getCacheFilename(path);
		this.loadedEntries.set(path, entry);

		if (this.disabled) {
			return;
		}

		await createDirectory(cacheFilename.getParent());
		this.addPendingWrite(cacheFilename, entry);
	}


(isQualityGatesOnly) => {
      this.isQualityGatesOnly = isQualityGatesOnly;
    }


(service) => this.filteredServices.includes(service.serviceName)


@Component({
  selector: 'ktb-stage-details',
  templateUrl: './ktb-stage-details.component.html',
  styleUrls: ['./ktb-stage-details.component.scss'],
})
export class KtbStageDetailsComponent implements OnInit, OnDestroy {
  public _project?: Project;
  public selectedStage?: Stage;
  public filterEventType: ServiceFilterType;
  public overlayConfig: DtOverlayConfig = {
    pinnable: true,
  };
  public isQualityGatesOnly = false;
  private _filteredServices: string[] = [];
  private readonly unsubscribe$ = new Subject<void>();

  @ViewChild('problemFilterEventButton') public problemFilterEventButton?: DtToggleButtonItem<string>;
  @ViewChild('evaluationFilterEventButton') public evaluationFilterEventButton?: DtToggleButtonItem<string>;
  @ViewChild('approvalFilterEventButton') public approvalFilterEventButton?: DtToggleButtonItem<string>;

  @Input()
  get project(): Project | undefined {
    return this._project;
  }

  set project(project: Project | undefined) {
    if (this._project !== project) {
      this._project = project;
      this.selectedStage = undefined;
    }
  }

  get filteredServices(): string[] {
    return this._filteredServices;
  }

  set filteredServices(services: string[]) {
    this._filteredServices = services;
    this.resetFilter(undefined);
  }

  constructor(private dataService: DataService) {}

  ngOnInit(): void {
    this.dataService.isQualityGatesOnly.pipe(takeUntil(this.unsubscribe$)).subscribe((isQualityGatesOnly) => {
      this.isQualityGatesOnly = isQualityGatesOnly;
    });
  }

  selectStage($event: { stage: Stage; filterType: ServiceFilterType }): void {
    this.selectedStage = $event.stage;
    if (this.filterEventType !== $event.filterType) {
      this.resetFilter($event.filterType);
    }
  }

  private resetFilter(eventType: ServiceFilterType): void {
    this.problemFilterEventButton?.deselect();
    this.evaluationFilterEventButton?.deselect();
    this.approvalFilterEventButton?.deselect();
    this.filterEventType = eventType;
  }

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  selectFilterEvent($event: DtToggleButtonChange<any>): void {
    if ($event.isUserInput) {
      this.filterEventType = $event.source.selected ? $event.value : null;
    }
  }

  getServiceLink(service: Service): string[] {
    return ['service', service.serviceName, 'context', service.deploymentContext ?? '', 'stage', service.stage];
  }

  public filterServices(services: Service[], type: ServiceFilterType): Service[] {
    const filteredServices =
      this.filteredServices.length === 0
        ? services
        : services.filter((service) => this.filteredServices.includes(service.serviceName));
    if (this.filterEventType && filteredServices.length === 0 && this.filterEventType === type) {
      this.resetFilter(undefined);
    }
    return filteredServices;
  }

  ngOnDestroy(): void {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
  }
}


export type ServiceFilterType = 'evaluation' | 'problem' | 'approval' | undefined;


ngOnInit(): void {
    this.dataService.isQualityGatesOnly.pipe(takeUntil(this.unsubscribe$)).subscribe((isQualityGatesOnly) => {
      this.isQualityGatesOnly = isQualityGatesOnly;
    });
  }


selectStage($event: { stage: Stage; filterType: ServiceFilterType }): void {
    this.selectedStage = $event.stage;
    if (this.filterEventType !== $event.filterType) {
      this.resetFilter($event.filterType);
    }
  }


private resetFilter(eventType: ServiceFilterType): void {
    this.problemFilterEventButton?.deselect();
    this.evaluationFilterEventButton?.deselect();
    this.approvalFilterEventButton?.deselect();
    this.filterEventType = eventType;
  }


// eslint-disable-next-line @typescript-eslint/no-explicit-any
selectFilterEvent($event: DtToggleButtonChange<any>): void {
    if ($event.isUserInput) {
      this.filterEventType = $event.source.selected ? $event.value : null;
    }
  }


getServiceLink(service: Service): string[] {
    return ['service', service.serviceName, 'context', service.deploymentContext ?? '', 'stage', service.stage];
  }


public filterServices(services: Service[], type: ServiceFilterType): Service[] {
    const filteredServices =
      this.filteredServices.length === 0
        ? services
        : services.filter((service) => this.filteredServices.includes(service.serviceName));
    if (this.filterEventType && filteredServices.length === 0 && this.filterEventType === type) {
      this.resetFilter(undefined);
    }
    return filteredServices;
  }


ngOnDestroy(): void {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
  }


(domyślnie: systemowy)


(domyślnie: 8333 lub testnet: 18333)


(domyślnie: 8332 or testnet: 18332)


@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: process.env.JWTSECRETKEY,
    });
  }

  async validate(payload: any) {
    return {
      id: payload.id,
    };
  }
}


async validate(payload: any) {
    return {
      id: payload.id,
    };
  }


(props) => {
  const { className, ...rest } = props;
  useDeprecationWarning({
    shouldWarn: true,
    message: `Built-in icons are being deprecated in the next major release. You can use the react-icons (https://react-icons.github.io/react-icons) package with the Material Icon set in place of these icons or any other React Icon library.
import { IconCall } from '@aws-amplify/ui-react'; → import { MdCall } from 'react-icons/md';`,
  });
  return (
    <View
      as="span"
      width="1em"
      height="1em"
      className={classNames(ComponentClassNames.Icon, className)}
      {...rest}
    >
      <svg
        width="24"
        height="24"
        viewBox="0 0 24 24"
        fill="none"
        xmlns="http://www.w3.org/2000/svg"
      >
        <path
          d="M6.54 5C6.6 5.89 6.75 6.76 6.99 7.59L5.79 8.79C5.38 7.59 5.12 6.32 5.03 5H6.54V5ZM16.4 17.02C17.25 17.26 18.12 17.41 19 17.47V18.96C17.68 18.87 16.41 18.61 15.2 18.21L16.4 17.02V17.02ZM7.5 3H4C3.45 3 3 3.45 3 4C3 13.39 10.61 21 20 21C20.55 21 21 20.55 21 20V16.51C21 15.96 20.55 15.51 20 15.51C18.76 15.51 17.55 15.31 16.43 14.94C16.33 14.9 16.22 14.89 16.12 14.89C15.86 14.89 15.61 14.99 15.41 15.18L13.21 17.38C10.38 15.93 8.06 13.62 6.62 10.79L8.82 8.59C9.1 8.31 9.18 7.92 9.07 7.57C8.7 6.45 8.5 5.25 8.5 4C8.5 3.45 8.05 3 7.5 3Z"
          fill="currentColor"
        />
      </svg>
    </View>


classNames(ComponentClassNames


@Injectable()
export class AppService {
  async getOGP(urlBase: string): Promise<object> {
    const url = encodeURI(urlBase);
    const { result } = await ogs({ url });
    return result;
  }
}


async getOGP(urlBase: string): Promise<object> {
    const url = encodeURI(urlBase);
    const { result } = await ogs({ url });
    return result;
  }


export class Database {
  fs: FirestoreHandler;
  cache: CacheHandler;
  constructor(public db: DbT) {
    this.fs = new FirestoreHandler({
      serviceAccKeyFilePath: db.serviceAccKeyFilePath
    });
    this.cache = new CacheHandler(db.cache_max_age, db.cache_allocated_memory);
  }

  async write<T>(writeObj: docCollection, document: docData) {
    const randomId = uuidv1();
    const preparedData = {
      collection: writeObj.collection.toLowerCase(),
      id: writeObj.id ? writeObj.id.toLowerCase() : null,
      uuid: randomId
    };

    const { id, collection, uuid } = preparedData;

    const getCollectionData = this.cache.get(collection);

    const documentIdData = getCollectionData ? getCollectionData[id] : null;

    const prepId = id ? id : uuid;
    const preparedCacheObjDoc = {
      [prepId]: document
    };

    this.cache.set(collection, preparedCacheObjDoc);
    let response;
    try {
      if (!documentIdData) {
        response = await this.fs.write(preparedData, document);
        // console.log('Write to firebase', response);
      }
    } catch (error) {
      console.log(error);
    }
    return response;
  }

  async readOne<T>(readObj: docCollection) {
    let response;
    try {
      const { id, collection } = readObj;

      const getCollectionFromCache = this.cache.get(collection[id]);

      if (getCollectionFromCache && getCollectionFromCache !== undefined) {
        response = getCollectionFromCache;
      } else {
        response = await this.fs.readOne(readObj);

        this.cache.set(collection[id], response);

        // console.log('From Firebase', response);
      }
    } catch (error) {
      throw error;
    }
    return response;
  }
  async readMany<T>(readManyObj: readManyType) {
    let response;
    try {
      if (
        this.cache.get(readManyObj.collection) &&
        this.cache.get(readManyObj.collection) !== undefined
      ) {
        // console.log('from cache', this.cache.get(readManyObj.collection));
        response = this.cache.get(readManyObj.collection);
      } else {
        response = await this.fs.readMany(readManyObj);
        this.cache.set(readManyObj.collection, response);
        // console.log('from service', response);
      }
    } catch (error) {
      throw error;
    }
    return response;
  }

  async delete<T>(readObj: docCollection) {
    let response;
    try {
      const { id, collection } = readObj;

      response = await this.fs.delete(readObj);

      this.cache.delete(collection[id]);
    } catch (error) {
      throw error;
    }
    return response;
  }
}


async write<T>(writeObj: docCollection, document: docData) {
    const randomId = uuidv1();
    const preparedData = {
      collection: writeObj.collection.toLowerCase(),
      id: writeObj.id ? writeObj.id.toLowerCase() : null,
      uuid: randomId
    };

    const { id, collection, uuid } = preparedData;

    const getCollectionData = this.cache.get(collection);

    const documentIdData = getCollectionData ? getCollectionData[id] : null;

    const prepId = id ? id : uuid;
    const preparedCacheObjDoc = {
      [prepId]: document
    };

    this.cache.set(collection, preparedCacheObjDoc);
    let response;
    try {
      if (!documentIdData) {
        response = await this.fs.write(preparedData, document);
        // console.log('Write to firebase', response);
      }
    } catch (error) {
      console.log(error);
    }
    return response;
  }


async readOne<T>(readObj: docCollection) {
    let response;
    try {
      const { id, collection } = readObj;

      const getCollectionFromCache = this.cache.get(collection[id]);

      if (getCollectionFromCache && getCollectionFromCache !== undefined) {
        response = getCollectionFromCache;
      } else {
        response = await this.fs.readOne(readObj);

        this.cache.set(collection[id], response);

        // console.log('From Firebase', response);
      }
    } catch (error) {
      throw error;
    }
    return response;
  }


async readMany<T>(readManyObj: readManyType) {
    let response;
    try {
      if (
        this.cache.get(readManyObj.collection) &&
        this.cache.get(readManyObj.collection) !== undefined
      ) {
        // console.log('from cache', this.cache.get(readManyObj.collection));
        response = this.cache.get(readManyObj.collection);
      } else {
        response = await this.fs.readMany(readManyObj);
        this.cache.set(readManyObj.collection, response);
        // console.log('from service', response);
      }
    } catch (error) {
      throw error;
    }
    return response;
  }


async delete<T>(readObj: docCollection) {
    let response;
    try {
      const { id, collection } = readObj;

      response = await this.fs.delete(readObj);

      this.cache.delete(collection[id]);
    } catch (error) {
      throw error;
    }
    return response;
  }


({ props }: IProps): JSX.Element => {
    const dispatch = useDispatch();

    useEffect(() => {
        dispatch(getCart());
    }, [dispatch]);

    const { totalQty } = useSelector(selectCartState());
    const isLoggedIn = useSelector(checkIfIsLoggedIn());

    const { openSideDrawer } = useSelector<RootState, StateProps>((state) => {
        return { openSideDrawer: state.ui.openSideDrawer };
    });

    // Classes for styling Navigation component
    const navProps = {
        ul: '',
        li: 'navigation__item--box',
        a: 'navigation__link--box',
    };

    // My Account box handlers
    const onMyAccountItemClick = (id: number) => {
        switch (id) {
            case 1:
                break;
            case 2:
                break;
            case 3:
                break;
            case 4:
                break;
            case 5:
                break;
            case 6:
                dispatch(logout());
                break;
            default:
                break;
        }
    };

    return (

            {/* -------- Hamburger button box -------- */}

                <HamburgerButton
                    handleHamburger={() => dispatch(handleSideDrawer())}
                    open={openSideDrawer}
                />

            {/* -------- Left box -------- */}

Trillo

                        {headerLinks.map((item) => (
                            <NavigationItem
                                link={item}
                                onClick={()


() => {
        dispatch(getCart());
    }


(state) => {
        return { openSideDrawer: state.ui.openSideDrawer };
    }


(id: number) => {
        switch (id) {
            case 1:
                break;
            case 2:
                break;
            case 3:
                break;
            case 4:
                break;
            case 5:
                break;
            case 6:
                dispatch(logout());
                break;
            default:
                break;
        }
    }


() => dispatch(handleSideDrawer())


(item) => (
                            <NavigationItem
                                link={item}


() =>
                                dispatch(
                                    handleModal(
                                        <SideModal
                                            title="Sign In"
                                            content={<LoginForm />


(item) => (
                                    <NavigationItem
                                        link={item}


interface StateProps {
    openSideDrawer: boolean;
}


interface IProps {
    props?: string;
}


async (
  exception: unknown
): Promise<updateRegistrationFormAnswer.ErrorType> => {
  if (exception instanceof HTTPError) {
    const body = await exception.response.json()
    switch (body.error?.info?.type) {
      case "INVALID_FORM_ANSWER":
        return { errorCode: "invalidFormAnswer", error: body }
      case "OUT_OF_PROJECT_CREATION_PERIOD":
        return { errorCode: "outOfProjectCreationPeriod", error: body }
      default:
        return { errorCode: "unknown", error: body }
    }
  } else if (exception instanceof TimeoutError) {
    return { errorCode: "timeout", error: exception }
  } else {
    return { errorCode: "unknown", error: exception }
  }
}


async (
  props: updateRegistrationFormAnswer.Props
): Promise<RegistrationFormAnswer | updateRegistrationFormAnswer.ErrorType> => {
  if (props.projectId) {
    try {
      const { answer } = await client({ idToken: props.idToken })
        .post("project/registration-form/answer/update", {
          json: {
            project_id: props.projectId,
            registration_form_id: props.registrationFormId,
            items: props.items,
          },
        })
        .json()
      return answer
    } catch (err) {
      return await handleException(err)
    }
  }

  if (props.pendingProjectId) {
    try {
      const { answer } = await client({ idToken: props.idToken })
        .post("pending-project/registration-form/answer/update", {
          json: {
            pending_project_id: props.pendingProjectId,
            registration_form_id: props.registrationFormId,
            items: props.items,
          },
        })
        .json()
      return answer
    } catch (err) {
      return await handleException(err)
    }
  }

  throw new Error("Either of projectId or pendingProjectId needed.")
}


type Props = (
    | Readonly<{
        projectId: string
        pendingProjectId?: undefined
      }>
    | Readonly<{
        projectId?: undefined
        pendingProjectId: string
      }>
  ) &
    Readonly<{
      idToken: string
      registrationFormId: string
      items: FormAnswerItem[]
    }>


type ErrorType = {
    errorCode:
      | "invalidFormAnswer"
      | "outOfProjectCreationPeriod"
      | "timeout"
      | "unknown"
    error?: any
  }


export async function* paginateListWorkflows(
  config: GluePaginationConfiguration,
  input: ListWorkflowsCommandInput,
  ...additionalArguments: any
): Paginator<ListWorkflowsCommandOutput> {
  // ToDo: replace with actual type instead of typeof input.NextToken
  let token: typeof input.NextToken | undefined = config.startingToken || undefined;
  let hasNext = true;
  let page: ListWorkflowsCommandOutput;
  while (hasNext) {
    input.NextToken = token;
    input["MaxResults"] = config.pageSize;
    if (config.client instanceof Glue) {
      page = await makePagedRequest(config.client, input, ...additionalArguments);
    } else if (config.client instanceof GlueClient) {
      page = await makePagedClientRequest(config.client, input, ...additionalArguments);
    } else {
      throw new Error("Invalid client, expected Glue | GlueClient");
    }
    yield page;
    token = page.NextToken;
    hasNext = !!token;
  }
  // @ts-ignore
  return undefined;
}


async (
  client: GlueClient,
  input: ListWorkflowsCommandInput,
  ...args: any
): Promise<ListWorkflowsCommandOutput> => {
  // @ts-ignore
  return await client.send(new ListWorkflowsCommand(input), ...args);
}


async (
  client: Glue,
  input: ListWorkflowsCommandInput,
  ...args: any
): Promise<ListWorkflowsCommandOutput> => {
  // @ts-ignore
  return await client.listWorkflows(input, ...args);
}


() => {
  let app: INestApplication;
  let repository: UserRepository;

  const authGuardMock = { canActivate: (): any => true };
  const rolesGuardMock = { canActivate: (): any => true };

  const testUser: User = {
    login: 'userTestLogin',
    password: '',
    firstName: 'UserTest',
    lastName: 'UserTest',
    email: 'usertest@localhost.it',
    imageUrl: '',
    activated: true,
    langKey: 'en',
    createdBy: 'testadmin',
    lastModifiedBy: 'testadmin'
  };

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule]
    })
      .overrideGuard(AuthGuard)
      .useValue(authGuardMock)
      .overrideGuard(RolesGuard)
      .useValue(rolesGuardMock)
      .compile();

    app = moduleFixture.createNestApplication();
    await app.init();
    repository = moduleFixture.get<UserRepository>(UserRepository);
  });

  it('/POST create user', async () => {
    const createdUser: User = (
      await request(app.getHttpServer())
        .post('/api/users')
        .send(testUser)
        .expect(201)
    ).body;

    expect(createdUser.login).toEqual(testUser.login);
    await repository.remove(createdUser);
  });

  it('/GET all users', () => {
    request(app.getHttpServer())
      .get('/api/users')
      .expect(200);
  });

  it('/PUT update user', async () => {
    const savedUser: User = await repository.save(testUser);
    savedUser.firstName = 'Updated Name';

    const updatedUser: User = (
      await request(app.getHttpServer())
        .put('/api/users')
        .send(savedUser)
        .expect(201)
    ).body;

    expect(updatedUser).toEqual(savedUser);

    await repository.remove(savedUser);
  });

  it('/GET user with a login name', async () => {
    const savedUser: User = await repository.save(testUser);

    const getUser: User = (
      await request(app.getHttpServer())
        .get('/api/users/' + savedUser.login)
        .expect(200)
    ).body;

    expect(getUser).toEqual(savedUser);

    await repository.remove(savedUser);
  });

  it('/DELETE user', async () => {
    const savedUser: User = await repository.save(testUser);

    await request(app.getHttpServer())
      .delete('/api/users/' + savedUser.login)
      .expect(204);

    const userUndefined = await repository.findOne(savedUser.id);
    expect(userUndefined).toBeUndefined();
  });

  afterEach(async () => {
    await app.close();
  });
}


(): any => true


async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule]
    })
      .overrideGuard(AuthGuard)
      .useValue(authGuardMock)
      .overrideGuard(RolesGuard)
      .useValue(rolesGuardMock)
      .compile();

    app = moduleFixture.createNestApplication();
    await app.init();
    repository = moduleFixture.get<UserRepository>(UserRepository);
  }


async () => {
    const createdUser: User = (
      await request(app.getHttpServer())
        .post('/api/users')
        .send(testUser)
        .expect(201)
    ).body;

    expect(createdUser.login).toEqual(testUser.login);
    await repository.remove(createdUser);
  }


() => {
    request(app.getHttpServer())
      .get('/api/users')
      .expect(200);
  }


async () => {
    const savedUser: User = await repository.save(testUser);
    savedUser.firstName = 'Updated Name';

    const updatedUser: User = (
      await request(app.getHttpServer())
        .put('/api/users')
        .send(savedUser)
        .expect(201)
    ).body;

    expect(updatedUser).toEqual(savedUser);

    await repository.remove(savedUser);
  }


async () => {
    const savedUser: User = await repository.save(testUser);

    const getUser: User = (
      await request(app.getHttpServer())
        .get('/api/users/' + savedUser.login)
        .expect(200)
    ).body;

    expect(getUser).toEqual(savedUser);

    await repository.remove(savedUser);
  }


async () => {
    const savedUser: User = await repository.save(testUser);

    await request(app.getHttpServer())
      .delete('/api/users/' + savedUser.login)
      .expect(204);

    const userUndefined = await repository.findOne(savedUser.id);
    expect(userUndefined).toBeUndefined();
  }


export function createRoute<Params = {}>(routeConfig: {
  parent?: Route;
  path?: string;
  exact?: boolean;
  entryPoint: EntryPoint<Params, {}>;
}): Route<Params> {
  return routeConfig;
}


type Route<Params = {}> = Readonly<{
  parent?: Route;
  path?: string;
  exact?: boolean;
  entryPoint: EntryPoint<Params, {}>;
}>;


args => (
  <Grid>
    <Row style={


() => {
        this.product.review = this.reviewText.value;
      }


e => console.log(`Error: ${e}`)


@Component({
  selector: 'app-customer-review',
  templateUrl: './customer-review.component.html',
  providers: [CustomerProductService]
})

export class CustomerReviewComponent {
  form: FormGroup;
  @Input() product: ProductViewModel;
  @Input() customerEmail: string; // User's e-mail
  reviewText = new FormControl('', Validators.required);

  constructor(private productService: CustomerProductService, fb: FormBuilder) {
    this.form = fb.group({
      'reviewText': this.reviewText
    });
  }

  onSubmit() {
    this.productService.makeReview(this.reviewText.value, this.product.id, this.customerEmail)
      .subscribe(() => {
        this.product.review = this.reviewText.value;
      },
        e => console.log(`Error: ${e}`)
      );
  }
}


onSubmit() {
    this.productService.makeReview(this.reviewText.value, this.product.id, this.customerEmail)
      .subscribe(() => {
        this.product.review = this.reviewText.value;
      },
        e => console.log(`Error: ${e}`)
      );
  }


async (request: NextApiRequest, response: NextApiResponse) => {
  if (request.method === 'POST') {

    const session = await getSession({ req: request })

    const user = await fauna.query<User>(
      q.Get(
        q.Match(
          q.Index('user_by_email'),
          q.Casefold(session.user.email)
        )
      )
    );

    let customerId = user.data.stripe_customer_id;

    if (!customerId) {
      const stripeCustomer = await stripe.customers.create({
        email: session.user.email
      });

      await fauna.query(
        q.Update(
          q.Ref(q.Collection('users'), user.ref.id),
          {
            data: {
              stripe_customer_id: stripeCustomer.id
            }
          }
        )
      );

      customerId = stripeCustomer.id;
    };

    const stripeCheckoutSession = await stripe.checkout.sessions.create({
      customer: customerId,
      payment_method_types: ['card'],
      billing_address_collection: 'required',
      line_items: [
        {
          price: 'price_1InXCaAVfBP6TE9gFV26VivK', quantity: 1
        },
      ],
      mode: 'subscription',
      allow_promotion_codes: true,
      success_url: process.env.STRIPE_SUCCESS_URL,
      cancel_url: process.env.STRIPE_CANCEL_URL
    })

    return response.status(200).json({ sessionId: stripeCheckoutSession.id });
  } else {
    response.setHeader('Allow', 'POST');
    response.status(405).end('Method not allowed');
  }
}


type User = {
  ref: {
    id: string;
  },
  data: {
    stripe_customer_id: string;
  }
}


function TableHead({
  columns,
  onSelectAll,
  orderBy = '',
  orderDirection = 'asc',
  selection = false,
  allSelected = false,
  onRequestSort,
  size = 'medium',
  hasActions = false,
}: TableHeadProps) {
  const createSortHandler = (property: string) => () => {
    if (onRequestSort) {
      if (property === orderBy) {
        onRequestSort(property, orderDirection === 'asc' ? 'desc' : 'asc');
      } else {
        onRequestSort(property, 'asc');
      }
    }
  };

  const handleSelectAll = () => {
    if (onSelectAll) {
      onSelectAll(!allSelected);
    }
  };

  return (
    <MUITableHead>
      <TableRow>
        {selection && (
          <TableCell size={size} padding="checkbox">
            <Checkbox checked={allSelected} onClick={handleSelectAll} />
          </TableCell>
        )}


(property: string) => () => {
    if (onRequestSort) {
      if (property === orderBy) {
        onRequestSort(property, orderDirection === 'asc' ? 'desc' : 'asc');
      } else {
        onRequestSort(property, 'asc');
      }
    }
  }


() => {
    if (onRequestSort) {
      if (property === orderBy) {
        onRequestSort(property, orderDirection === 'asc' ? 'desc' : 'asc');
      } else {
        onRequestSort(property, 'asc');
      }
    }
  }


() => {
    if (onSelectAll) {
      onSelectAll(!allSelected);
    }
  }


col => {
          return (
            <TableCell
              size={size}
              key={col.field}
              padding={col.disablePadding ? 'none' : 'default'}
              align={col.align ? col.align : 'left'}
              sortDirection={
                (orderBy === col.field
                  ? orderDirection
                  : false) as SortDirection
              }


export interface TableHeadProps {
  columns: Column[];
  selection: boolean;
  onSelectAll?: (checked: boolean) => void;
  allSelected: boolean;
  orderDirection: OrderDirection;
  orderBy: string;
  size?: 'small' | 'medium';
  onRequestSort?: (
    property: string,
    orderDirection: OrderDirection
  ) => void | Promise<void>;
  hasActions: boolean;
}


createSortHandler(col


(u: PendingUser) => {
            return <PendingUserView key={u.name} user={u} onReview={this.onShowReview.bind(this, u)}/>
        }


export class PendingUserList extends React.Component<props, state> {

    constructor(props: props){
        super(props);
        this.state = {
            showReview: false,
            selectedUser: null
        }
    }

    shouldComponentUpdate(nextProps: props, nextState: state) {
        return !shallowequal(this.props, nextProps) || !shallowequal(this.state, nextState);
    }

    onShowReview(u: PendingUser) {
        this.setState({
            showReview: true,
            selectedUser: u
        });
    }
    onDismissReview() {
        this.setState({
            showReview: false
        });
    }

    render() {
        let users = this.props.users.toList().map((u: PendingUser) => {
            return <PendingUserView key={u.name} user={u} onReview={this.onShowReview.bind(this, u)}/>
        })

        return (
            <Grid>
                <Row>
                    <Col md={3}>Name</Col>
                    <Col md={3}>Email</Col>
                    <Col md={3}>Registered</Col>
                    <Col md={3}></Col>
                </Row>
                {users}
                <ReviewPendingModal
                    show={this.state.showReview}
                    cancel={this.onDismissReview.bind(this)}
                    store={this.props.store}
                    user={this.state.selectedUser} />
            </Grid>
        )
    }
}


interface props {
    store: ReduxStore,
    users: Map<string, PendingUser>
}


interface state {
    showReview?: boolean
    selectedUser?: PendingUser
}


shouldComponentUpdate(nextProps: props, nextState: state) {
        return !shallowequal(this.props, nextProps) || !shallowequal(this.state, nextState);
    }


onShowReview(u: PendingUser) {
        this.setState({
            showReview: true,
            selectedUser: u
        });
    }


onDismissReview() {
        this.setState({
            showReview: false
        });
    }


render() {
        let users = this.props.users.toList().map((u: PendingUser) => {
            return <PendingUserView key={u.name} user={u} onReview={this.onShowReview.bind(this, u)}/>
        })

        return (
            <Grid>
                <Row>
                    <Col md={3}>Name</Col>
                    <Col md={3}>Email</Col>
                    <Col md={3}>Registered</Col>
                    <Col md={3}></Col>
                </Row>
                {users}
                <ReviewPendingModal
                    show={this.state.showReview}
                    cancel={this.onDismissReview.bind(this)}
                    store={this.props.store}
                    user={this.state.selectedUser} />
            </Grid>
        )
    }


(observer) => {
            const httpParams = new AgentHttpParams();
            httpParams.url = '/admin/product/list';
            httpParams.data = {
                pageNum: params.pageNum,
                pageSize: params.pageSize
            };
            if (params.productName) {
                httpParams.data["productName"] = params.productName;
            }
            if (params.productType) {
                httpParams.data["productType"] = params.productType;
            }
            if (params.gameName) {
                httpParams.data["gameName"] = params.gameName;
            }
            if (params.gameId) {
                httpParams.data["gameId"] = params.gameId;
            }
            httpParams.callback = ((response: any) => {
                observer.next(response);
                observer.complete();
            });
            this.httpBaseService.getData(httpParams);
        }


(response: any) => {
                observer.next(response);
                observer.complete();
            }


@Injectable()
export class CloudProductServer {
    constructor(private httpBaseService: HttpBaseService) {

    }
    //产品查询 admin/product/list
    getProductList(params: any) {
        return new Observable((observer) => {
            const httpParams = new AgentHttpParams();
            httpParams.url = '/admin/product/list';
            httpParams.data = {
                pageNum: params.pageNum,
                pageSize: params.pageSize
            };
            if (params.productName) {
                httpParams.data["productName"] = params.productName;
            }
            if (params.productType) {
                httpParams.data["productType"] = params.productType;
            }
            if (params.gameName) {
                httpParams.data["gameName"] = params.gameName;
            }
            if (params.gameId) {
                httpParams.data["gameId"] = params.gameId;
            }
            httpParams.callback = ((response: any) => {
                observer.next(response);
                observer.complete();
            });
            this.httpBaseService.getData(httpParams);
        });
    }
}


//产品查询 admin/product/list
getProductList(params: any) {
        return new Observable((observer) => {
            const httpParams = new AgentHttpParams();
            httpParams.url = '/admin/product/list';
            httpParams.data = {
                pageNum: params.pageNum,
                pageSize: params.pageSize
            };
            if (params.productName) {
                httpParams.data["productName"] = params.productName;
            }
            if (params.productType) {
                httpParams.data["productType"] = params.productType;
            }
            if (params.gameName) {
                httpParams.data["gameName"] = params.gameName;
            }
            if (params.gameId) {
                httpParams.data["gameId"] = params.gameId;
            }
            httpParams.callback = ((response: any) => {
                observer.next(response);
                observer.complete();
            });
            this.httpBaseService.getData(httpParams);
        });
    }


@NgModule({
  imports: [
    CommonModule,
    Routing,
    EffectsModule.forFeature([MoviesEffects]),
    StoreModule.forFeature('movies', reducer),
    LazyLoadImagesModule
  ],
  declarations: [LandingComponent]
})
export class MoviesModule {}


@NgModule({
  declarations: [SimTimepickerDirective, SimTimepickerComponent, SimTimepickerContentComponent, SimTimeViewComponent, SimTimeListDirective],
  imports: [CommonModule, SimDateModule, A11yModule, OverlayModule, PortalModule, SimButtonModule],
  exports: [SimInputModule, SimTimepickerDirective, SimTimepickerComponent, SimTimeViewComponent],
  providers: [SIM_TIMEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER]
})
export class SimTimepickerModule {}


export interface AuthServiceResponse {
    [key: string]: unknown;
    message: string;
    success: boolean;
}


/**
 * 重试高阶函数
 * @example
 * const fn = Retry(() => {
 *  if(Math.random()>0.5){
 *    throw New Error()
 *  }else{
 *    return 1
 *  }
 * });
 * (await fn()) === 1;
 * @param cb 需要重试的函数
 * @param options 重试配置
 * @returns 包装后的重试函数，只要重试成功，就不会报错
 */
export declare function Retry(cb: (...arg: any) => any, options?: IOptions): (...arg: any[]) => Promise<any>;


/**
 * 重试配置
 * 更多需要透传给retry的配置
 * @see https://www.npmjs.com/package/retry
 */
interface IOptions {
    /**
     * 是否开启调试日志
     * @default false
     */
    debug?: boolean;
    /**
     * 重试次数
     * @default 2 俗话说，事不过三
     */
    retries?: number;
    /**
     * 更多需要透传给retry的配置
     * @see https://www.npmjs.com/package/retry
     */
    [key: string]: any;
}


export function useChartWidth(): [RefObject<HTMLDivElement>, number] {
  const containerRef = useRef<HTMLDivElement>(null);
  const [width, setWidth] = useState<number>(0);

  const handleResize = () =>
    containerRef.current && setWidth(containerRef.current.clientWidth);

  useLayoutEffect(() => {
    handleResize();
  }, []);

  useEffect(() => {
    window.addEventListener("resize", handleResize);
    return () => window.removeEventListener("resize", handleResize);
  }, []);
  return [containerRef, width];
}


() =>
    containerRef.current && setWidth(containerRef.current.clientWidth)


() => {
    window.addEventListener("resize", handleResize);
    return () => window.removeEventListener("resize", handleResize);
  }


() => window.removeEventListener("resize", handleResize)


() => {
  it("should call ParamFilter.useParam method with the correct parameters", () => {
    class Ctrl {
      test(@Context("expression") test: any) {}
    }

    const param = ParamMetadata.get(Ctrl, "test", 0);
    expect(param.expression).to.eq("expression");
    expect(param.paramType).to.eq(ParamTypes.$CTX);
  });
}


() => {
    class Ctrl {
      test(@Context("expression") test: any) {}
    }

    const param = ParamMetadata.get(Ctrl, "test", 0);
    expect(param.expression).to.eq("expression");
    expect(param.paramType).to.eq(ParamTypes.$CTX);
  }


class Ctrl {
      test(@Context("expression") test: any) {}
    }


test(@Context("expression") test: any) {}


({
  moderator,
  createdAt,
  body,
  onDelete,
  id,
}) => {
  const deleteNote = useCallback(() => {
    if (onDelete) {
      onDelete(id);
    }
  }, [id]);
  return (

        <Typography variant="bodyCopy" className={styles.bodyType}>
          {body}
        </Typography>

      <Flex justifyContent="space-between">
        <Flex alignItems="center">
          <Timestamp>{createdAt}</Timestamp>
          {moderator && (
            <>
              <Localized id="moderatorNote-left-by">
                <Typography variant="timestamp" className={styles.leftBy}>
                  Left by:
                </Typography>
              </Localized>
              <Typography className={styles.username} variant="timestamp">
                {moderator}
              </Typography>
            </>


() => {
    if (onDelete) {
      onDelete(id);
    }
  }


interface Props {
  body: string;
  moderator: string | null;
  createdAt: string;
  onDelete: ((id: string) => Promise<any>) | null;
  id: string;
}


//
export default interface Fill {

}


function isWelcomePageEnabled(configurationService: IConfigurationService, contextService: IWorkspaceContextService) {
	const startupEditor = configurationService.inspect(configurationKey);
	if (!startupEditor.userValue && !startupEditor.workspaceValue) {
		const welcomeEnabled = configurationService.inspect(oldConfigurationKey);
		if (welcomeEnabled.value !== undefined && welcomeEnabled.value !== null) {
			return welcomeEnabled.value;
		}
	}
	return startupEditor.value === 'welcomePage' || startupEditor.value === 'gettingStarted' || startupEditor.value === 'readme' || startupEditor.value === 'welcomePageInEmptyWorkbench' && contextService.getWorkbenchState() === WorkbenchState.EMPTY;
}


hasBackups => {
				// Open the welcome even if we opened a set of default editors
				if ((!editorService.activeEditor || layoutService.openedDefaultEditors) && !hasBackups) {
					const startupEditorSetting = configurationService.getValue(configurationKey) as string;
					const openWithReadme = startupEditorSetting === 'readme';
					if (openWithReadme) {
						return Promise.all(contextService.getWorkspace().folders.map(folder => {
							const folderUri = folder.uri;
							return fileService.resolve(folderUri)
								.then(folder => {
									const files = folder.children ? folder.children.map(child => child.name).sort() : [];

									const file = files.find(file => file.toLowerCase() === 'readme.md') || files.find(file => file.toLowerCase().startsWith('readme'));

									if (file) {
										return joinPath(folderUri, file);
									}
									return undefined;
								}, onUnexpectedError);
						})).then(arrays.coalesce)
							.then<any>(readmes => {
								if (!editorService.activeEditor) {
									if (readmes.length) {
										const isMarkDown = (readme: URI) => readme.path.toLowerCase().endsWith('.md');
										return Promise.all([
											this.commandService.executeCommand('markdown.showPreview', null, readmes.filter(isMarkDown), { locked: true }),
											editorService.openEditors(readmes.filter(readme => !isMarkDown(readme))
												.map(readme => ({ resource: readme }))),
										]);
									} else {
										return instantiationService.createInstance(WelcomePage).openEditor();
									}
								}
								return undefined;
							});
					} else {
						const startupEditorTypeID = startupEditorSetting === 'gettingStarted' ? gettingStartedInputTypeId : welcomeInputTypeId;
						const startupEditorCtor = startupEditorSetting === 'gettingStarted' ? GettingStartedPage : WelcomePage;

						let options: IEditorOptions;
						let editor = editorService.activeEditor;
						if (editor) {
							// Ensure that the welcome editor won't get opened more than once
							if (editor.getTypeId() === startupEditorTypeID || editorService.editors.some(e => e.getTypeId() === startupEditorTypeID)) {
								return undefined;
							}
							options = { pinned: false, index: 0 };
						} else {
							options = { pinned: false };
						}
						return instantiationService.createInstance(startupEditorCtor).openEditor(options);
					}
				}
				return undefined;
			}


folder => {
							const folderUri = folder.uri;
							return fileService.resolve(folderUri)
								.then(folder => {
									const files = folder.children ? folder.children.map(child => child.name).sort() : [];

									const file = files.find(file => file.toLowerCase() === 'readme.md') || files.find(file => file.toLowerCase().startsWith('readme'));

									if (file) {
										return joinPath(folderUri, file);
									}
									return undefined;
								}, onUnexpectedError);
						}


folder => {
									const files = folder.children ? folder.children.map(child => child.name).sort() : [];

									const file = files.find(file => file.toLowerCase() === 'readme.md') || files.find(file => file.toLowerCase().startsWith('readme'));

									if (file) {
										return joinPath(folderUri, file);
									}
									return undefined;
								}


child => child.name


file => file.toLowerCase() === 'readme.md'


file => file.toLowerCase().startsWith('readme')


readmes => {
								if (!editorService.activeEditor) {
									if (readmes.length) {
										const isMarkDown = (readme: URI) => readme.path.toLowerCase().endsWith('.md');
										return Promise.all([
											this.commandService.executeCommand('markdown.showPreview', null, readmes.filter(isMarkDown), { locked: true }),
											editorService.openEditors(readmes.filter(readme => !isMarkDown(readme))
												.map(readme => ({ resource: readme }))),
										]);
									} else {
										return instantiationService.createInstance(WelcomePage).openEditor();
									}
								}
								return undefined;
							}


(readme: URI) => readme.path.toLowerCase().endsWith('.md')


readme => !isMarkDown(readme)


readme => ({ resource: readme })


e => e.getTypeId() === startupEditorTypeID


accessor => {
	const viewletService = accessor.get(IViewletService);
	return viewletService.openViewlet(VIEWLET_ID, true)
		.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
		.then(viewlet => {
			viewlet.search('@sort:installs azure ');
			viewlet.focus();
		});
}


viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer


viewlet => {
			viewlet.search('@sort:installs azure ');
			viewlet.focus();
		}


() => this.dispose()


(container: HTMLElement) => this.onReady(container, recentlyOpened, installedExtensions)


e => {
			this.configurationService.updateValue(configurationKey, showOnStartup.checked ? 'welcomePage' : 'newUntitledFile');
		}


({ workspaces }) => {
			// Filter out the current workspace
			workspaces = workspaces.filter(recent => !this.contextService.isCurrentWorkspace(isRecentWorkspace(recent) ? recent.workspace : recent.folderUri));
			if (!workspaces.length) {
				pageElement.classList.add('emptyRecent');
				return;
			}
			const ul = container.querySelector('.recent ul');
			if (!ul) {
				return;
			}
			const moreRecent = ul.querySelector('.moreRecent')!;
			const workspacesToShow = workspaces.slice(0, 5);
			const updateEntries = () => {
				const listEntries = this.createListEntries(workspacesToShow);
				while (ul.firstChild) {
					ul.removeChild(ul.firstChild);
				}
				ul.append(...listEntries, moreRecent);
			};
			updateEntries();
			this._register(this.labelService.onDidChangeFormatters(updateEntries));
		}


recent => !this.contextService.isCurrentWorkspace(isRecentWorkspace(recent) ? recent.workspace : recent.folderUri)


() => {
				const listEntries = this.createListEntries(workspacesToShow);
				while (ul.firstChild) {
					ul.removeChild(ul.firstChild);
				}
				ul.append(...listEntries, moreRecent);
			}


ids => {
			for (const id of ids) {
				if (container.querySelector(`.installExtension[data-extension="${id.id}"], .enabledExtension[data-extension="${id.id}"]`)) {
					const installedExtensions = this.instantiationService.invokeFunction(getInstalledExtensions);
					this.updateInstalledExtensions(container, installedExtensions);
					break;
				}
			}
		}


recent => {
			let fullPath: string;
			let windowOpenable: IWindowOpenable;
			if (isRecentFolder(recent)) {
				windowOpenable = { folderUri: recent.folderUri };
				fullPath = recent.label || this.labelService.getWorkspaceLabel(recent.folderUri, { verbose: true });
			} else {
				fullPath = recent.label || this.labelService.getWorkspaceLabel(recent.workspace, { verbose: true });
				windowOpenable = { workspaceUri: recent.workspace.configPath };
			}

			const { name, parentPath } = splitName(fullPath);

			const li = document.createElement('li');
			const a = document.createElement('a');

			a.innerText = name;
			a.title = fullPath;
			a.setAttribute('aria-label', localize('welcomePage.openFolderWithPath', "Open folder {0} with path {1}", name, parentPath));
			a.href = 'javascript:void(0)';
			a.addEventListener('click', e => {
				this.telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', {
					id: 'openRecentFolder',
					from: telemetryFrom
				});
				this.hostService.openWindow([windowOpenable], { forceNewWindow: e.ctrlKey || e.metaKey });
				e.preventDefault();
				e.stopPropagation();
			});
			li.appendChild(a);

			const span = document.createElement('span');
			span.classList.add('path');
			span.classList.add('detail');
			span.innerText = parentPath;
			span.title = fullPath;
			li.appendChild(span);

			return li;
		}


e => {
				this.telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', {
					id: 'openRecentFolder',
					from: telemetryFrom
				});
				this.hostService.openWindow([windowOpenable], { forceNewWindow: e.ctrlKey || e.metaKey });
				e.preventDefault();
				e.stopPropagation();
			}


(extension, i) => {
				if (i) {
					list.appendChild(document.createTextNode(localize('welcomePage.extensionListSeparator', ", ")));
				}

				const a = document.createElement('a');
				a.innerText = extension.name;
				a.title = extension.title || (extension.isKeymap ? localize('welcomePage.installKeymap', "Install {0} keymap", extension.name) : localize('welcomePage.installExtensionPack', "Install additional support for {0}", extension.name));
				if (extension.isCommand) {
					a.href = `command:${extension.id}`;
					list.appendChild(a);
				} else {
					a.classList.add('installExtension');
					a.setAttribute('data-extension', extension.id);
					a.href = 'javascript:void(0)';
					a.addEventListener('click', e => {
						this.installExtension(extension, strings);
						e.preventDefault();
						e.stopPropagation();
					});
					list.appendChild(a);

					const span = document.createElement('span');
					span.innerText = extension.name;
					span.title = extension.isKeymap ? localize('welcomePage.installedKeymap', "{0} keymap is already installed", extension.name) : localize('welcomePage.installedExtensionPack', "{0} support is already installed", extension.name);
					span.classList.add('enabledExtension');
					span.setAttribute('data-extension', extension.id);
					list.appendChild(span);
				}
			}


e => {
						this.installExtension(extension, strings);
						e.preventDefault();
						e.stopPropagation();
					}


extensions => {
			const installedExtension = extensions.find(extension => areSameExtensions(extension.identifier, { id: extensionSuggestion.id }));
			if (installedExtension && installedExtension.globallyEnabled) {
				/* __GDPR__FRAGMENT__
					"WelcomePageInstalled-1" : {
						"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
						"extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
						"outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
					}
				*/
				this.telemetryService.publicLog(strings.installedEvent, {
					from: telemetryFrom,
					extensionId: extensionSuggestion.id,
					outcome: 'already_enabled',
				});
				this.notificationService.info(strings.alreadyInstalled.replace('{0}', extensionSuggestion.name));
				return;
			}
			const foundAndInstalled = installedExtension ? Promise.resolve(installedExtension.local) : this.extensionGalleryService.query({ names: [extensionSuggestion.id], source: telemetryFrom }, CancellationToken.None)
				.then((result): null | Promise<ILocalExtension | null> => {
					const [extension] = result.firstPage;
					if (!extension) {
						return null;
					}
					return this.extensionManagementService.installFromGallery(extension)
						.then(() => this.extensionManagementService.getInstalled())
						.then(installed => {
							const local = installed.filter(i => areSameExtensions(extension.identifier, i.identifier))[0];
							// TODO: Do this as part of the install to avoid multiple events.
							return this.extensionEnablementService.setEnablement([local], EnablementState.DisabledGlobally).then(() => local);
						});
				});

			this.notificationService.prompt(
				Severity.Info,
				strings.reloadAfterInstall.replace('{0}', extensionSuggestion.name),
				[{
					label: localize('ok', "OK"),
					run: () => {
						const messageDelay = new TimeoutTimer();
						messageDelay.cancelAndSet(() => {
							this.notificationService.info(strings.installing.replace('{0}', extensionSuggestion.name));
						}, 300);
						const extensionsToDisable = extensions.filter(extension => isKeymapExtension(this.tipsService, extension) && extension.globallyEnabled).map(extension => extension.local);
						extensionsToDisable.length ? this.extensionEnablementService.setEnablement(extensionsToDisable, EnablementState.DisabledGlobally) : Promise.resolve()
							.then(() => {
								return foundAndInstalled.then(foundExtension => {
									messageDelay.cancel();
									if (foundExtension) {
										return this.extensionEnablementService.setEnablement([foundExtension], EnablementState.EnabledGlobally)
											.then(() => {
												/* __GDPR__FRAGMENT__
													"WelcomePageInstalled-2" : {
														"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
														"extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
														"outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
													}
												*/
												this.telemetryService.publicLog(strings.installedEvent, {
													from: telemetryFrom,
													extensionId: extensionSuggestion.id,
													outcome: installedExtension ? 'enabled' : 'installed',
												});
												return this.hostService.reload();
											});
									} else {
										/* __GDPR__FRAGMENT__
											"WelcomePageInstalled-3" : {
												"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
												"extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
												"outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
											}
										*/
										this.telemetryService.publicLog(strings.installedEvent, {
											from: telemetryFrom,
											extensionId: extensionSuggestion.id,
											outcome: 'not_found',
										});
										this.notificationService.error(strings.extensionNotFound.replace('{0}', extensionSuggestion.name).replace('{1}', extensionSuggestion.id));
										return undefined;
									}
								});
							}).then(undefined, err => {
								/* __GDPR__FRAGMENT__
									"WelcomePageInstalled-4" : {
										"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
										"extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
										"outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
									}
								*/
								this.telemetryService.publicLog(strings.installedEvent, {
									from: telemetryFrom,
									extensionId: extensionSuggestion.id,
									outcome: isPromiseCanceledError(err) ? 'canceled' : 'error',
								});
								this.notificationService.error(err);
							});
					}
				}, {
					label: localize('details', "Details"),
					run: () => {
						/* __GDPR__FRAGMENT__
							"WelcomePageDetails-1" : {
								"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
								"extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
							}
						*/
						this.telemetryService.publicLog(strings.detailsEvent, {
							from: telemetryFrom,
							extensionId: extensionSuggestion.id,
						});
						this.extensionsWorkbenchService.queryGallery({ names: [extensionSuggestion.id] }, CancellationToken.None)
							.then(result => this.extensionsWorkbenchService.open(result.firstPage[0]))
							.then(undefined, onUnexpectedError);
					}
				}]
			);
		}


extension => areSameExtensions(extension.identifier, { id: extensionSuggestion.id })


(result): null | Promise<ILocalExtension | null> => {
					const [extension] = result.firstPage;
					if (!extension) {
						return null;
					}
					return this.extensionManagementService.installFromGallery(extension)
						.then(() => this.extensionManagementService.getInstalled())
						.then(installed => {
							const local = installed.filter(i => areSameExtensions(extension.identifier, i.identifier))[0];
							// TODO: Do this as part of the install to avoid multiple events.
							return this.extensionEnablementService.setEnablement([local], EnablementState.DisabledGlobally).then(() => local);
						});
				}


() => this.extensionManagementService.getInstalled()


installed => {
							const local = installed.filter(i => areSameExtensions(extension.identifier, i.identifier))[0];
							// TODO: Do this as part of the install to avoid multiple events.
							return this.extensionEnablementService.setEnablement([local], EnablementState.DisabledGlobally).then(() => local);
						}


i => areSameExtensions(extension.identifier, i.identifier)


() => local


() => {
						const messageDelay = new TimeoutTimer();
						messageDelay.cancelAndSet(() => {
							this.notificationService.info(strings.installing.replace('{0}', extensionSuggestion.name));
						}, 300);
						const extensionsToDisable = extensions.filter(extension => isKeymapExtension(this.tipsService, extension) && extension.globallyEnabled).map(extension => extension.local);
						extensionsToDisable.length ? this.extensionEnablementService.setEnablement(extensionsToDisable, EnablementState.DisabledGlobally) : Promise.resolve()
							.then(() => {
								return foundAndInstalled.then(foundExtension => {
									messageDelay.cancel();
									if (foundExtension) {
										return this.extensionEnablementService.setEnablement([foundExtension], EnablementState.EnabledGlobally)
											.then(() => {
												/* __GDPR__FRAGMENT__
													"WelcomePageInstalled-2" : {
														"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
														"extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
														"outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
													}
												*/
												this.telemetryService.publicLog(strings.installedEvent, {
													from: telemetryFrom,
													extensionId: extensionSuggestion.id,
													outcome: installedExtension ? 'enabled' : 'installed',
												});
												return this.hostService.reload();
											});
									} else {
										/* __GDPR__FRAGMENT__
											"WelcomePageInstalled-3" : {
												"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
												"extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
												"outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
											}
										*/
										this.telemetryService.publicLog(strings.installedEvent, {
											from: telemetryFrom,
											extensionId: extensionSuggestion.id,
											outcome: 'not_found',
										});
										this.notificationService.error(strings.extensionNotFound.replace('{0}', extensionSuggestion.name).replace('{1}', extensionSuggestion.id));
										return undefined;
									}
								});
							}).then(undefined, err => {
								/* __GDPR__FRAGMENT__
									"WelcomePageInstalled-4" : {
										"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
										"extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
										"outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
									}
								*/
								this.telemetryService.publicLog(strings.installedEvent, {
									from: telemetryFrom,
									extensionId: extensionSuggestion.id,
									outcome: isPromiseCanceledError(err) ? 'canceled' : 'error',
								});
								this.notificationService.error(err);
							});
					}


() => {
							this.notificationService.info(strings.installing.replace('{0}', extensionSuggestion.name));
						}


extension => isKeymapExtension(this.tipsService, extension) && extension.globallyEnabled


extension => extension.local


() => {
								return foundAndInstalled.then(foundExtension => {
									messageDelay.cancel();
									if (foundExtension) {
										return this.extensionEnablementService.setEnablement([foundExtension], EnablementState.EnabledGlobally)
											.then(() => {
												/* __GDPR__FRAGMENT__
													"WelcomePageInstalled-2" : {
														"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
														"extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
														"outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
													}
												*/
												this.telemetryService.publicLog(strings.installedEvent, {
													from: telemetryFrom,
													extensionId: extensionSuggestion.id,
													outcome: installedExtension ? 'enabled' : 'installed',
												});
												return this.hostService.reload();
											});
									} else {
										/* __GDPR__FRAGMENT__
											"WelcomePageInstalled-3" : {
												"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
												"extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
												"outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
											}
										*/
										this.telemetryService.publicLog(strings.installedEvent, {
											from: telemetryFrom,
											extensionId: extensionSuggestion.id,
											outcome: 'not_found',
										});
										this.notificationService.error(strings.extensionNotFound.replace('{0}', extensionSuggestion.name).replace('{1}', extensionSuggestion.id));
										return undefined;
									}
								});
							}


foundExtension => {
									messageDelay.cancel();
									if (foundExtension) {
										return this.extensionEnablementService.setEnablement([foundExtension], EnablementState.EnabledGlobally)
											.then(() => {
												/* __GDPR__FRAGMENT__
													"WelcomePageInstalled-2" : {
														"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
														"extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
														"outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
													}
												*/
												this.telemetryService.publicLog(strings.installedEvent, {
													from: telemetryFrom,
													extensionId: extensionSuggestion.id,
													outcome: installedExtension ? 'enabled' : 'installed',
												});
												return this.hostService.reload();
											});
									} else {
										/* __GDPR__FRAGMENT__
											"WelcomePageInstalled-3" : {
												"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
												"extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
												"outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
											}
										*/
										this.telemetryService.publicLog(strings.installedEvent, {
											from: telemetryFrom,
											extensionId: extensionSuggestion.id,
											outcome: 'not_found',
										});
										this.notificationService.error(strings.extensionNotFound.replace('{0}', extensionSuggestion.name).replace('{1}', extensionSuggestion.id));
										return undefined;
									}
								}


() => {
												/* __GDPR__FRAGMENT__
													"WelcomePageInstalled-2" : {
														"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
														"extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
														"outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
													}
												*/
												this.telemetryService.publicLog(strings.installedEvent, {
													from: telemetryFrom,
													extensionId: extensionSuggestion.id,
													outcome: installedExtension ? 'enabled' : 'installed',
												});
												return this.hostService.reload();
											}


err => {
								/* __GDPR__FRAGMENT__
									"WelcomePageInstalled-4" : {
										"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
										"extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
										"outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
									}
								*/
								this.telemetryService.publicLog(strings.installedEvent, {
									from: telemetryFrom,
									extensionId: extensionSuggestion.id,
									outcome: isPromiseCanceledError(err) ? 'canceled' : 'error',
								});
								this.notificationService.error(err);
							}


() => {
						/* __GDPR__FRAGMENT__
							"WelcomePageDetails-1" : {
								"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
								"extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
							}
						*/
						this.telemetryService.publicLog(strings.detailsEvent, {
							from: telemetryFrom,
							extensionId: extensionSuggestion.id,
						});
						this.extensionsWorkbenchService.queryGallery({ names: [extensionSuggestion.id] }, CancellationToken.None)
							.then(result => this.extensionsWorkbenchService.open(result.firstPage[0]))
							.then(undefined, onUnexpectedError);
					}


result => this.extensionsWorkbenchService.open(result.firstPage[0])


err => {
			/* __GDPR__FRAGMENT__
				"WelcomePageInstalled-6" : {
					"from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
					"extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
					"outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
				}
			*/
			this.telemetryService.publicLog(strings.installedEvent, {
				from: telemetryFrom,
				extensionId: extensionSuggestion.id,
				outcome: isPromiseCanceledError(err) ? 'canceled' : 'error',
			});
			this.notificationService.error(err);
		}


extensions => {
			const elements = container.querySelectorAll('.installExtension, .enabledExtension');
			for (let i = 0; i < elements.length; i++) {
				elements[i].classList.remove('installed');
			}
			extensions.filter(ext => ext.globallyEnabled)
				.map(ext => ext.identifier.id)
				.forEach(id => {
					const install = container.querySelectorAll(`.installExtension[data-extension="${id}"]`);
					for (let i = 0; i < install.length; i++) {
						install[i].classList.add('installed');
					}
					const enabled = container.querySelectorAll(`.enabledExtension[data-extension="${id}"]`);
					for (let i = 0; i < enabled.length; i++) {
						enabled[i].classList.add('installed');
					}
				});
		}


ext => ext.globallyEnabled


ext => ext.identifier.id


id => {
					const install = container.querySelectorAll(`.installExtension[data-extension="${id}"]`);
					for (let i = 0; i < install.length; i++) {
						install[i].classList.add('installed');
					}
					const enabled = container.querySelectorAll(`.enabledExtension[data-extension="${id}"]`);
					for (let i = 0; i < enabled.length; i++) {
						enabled[i].classList.add('installed');
					}
				}


(theme, collector) => {
	const backgroundColor = theme.getColor(welcomePageBackground);
	if (backgroundColor) {
		collector.addRule(`.monaco-workbench .part.editor > .content .welcomePageContainer { background-color: ${backgroundColor}; }`);
	}
	const foregroundColor = theme.getColor(foreground);
	if (foregroundColor) {
		collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage .caption { color: ${foregroundColor}; }`);
	}
	const descriptionColor = theme.getColor(descriptionForeground);
	if (descriptionColor) {
		collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage .detail { color: ${descriptionColor}; }`);
	}
	const buttonColor = getExtraColor(theme, buttonBackground, { dark: 'rgba(0, 0, 0, .2)', extra_dark: 'rgba(200, 235, 255, .042)', light: 'rgba(0,0,0,.04)', hc: 'black' });
	if (buttonColor) {
		collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage .commands .item button { background: ${buttonColor}; }`);
	}
	const buttonHoverColor = getExtraColor(theme, buttonHoverBackground, { dark: 'rgba(200, 235, 255, .072)', extra_dark: 'rgba(200, 235, 255, .072)', light: 'rgba(0,0,0,.10)', hc: null });
	if (buttonHoverColor) {
		collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage .commands .item button:hover { background: ${buttonHoverColor}; }`);
	}
	const link = theme.getColor(textLinkForeground);
	if (link) {
		collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage a { color: ${link}; }`);
	}
	const activeLink = theme.getColor(textLinkActiveForeground);
	if (activeLink) {
		collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage a:hover,
			.monaco-workbench .part.editor > .content .welcomePage a:active { color: ${activeLink}; }`);
	}
	const focusColor = theme.getColor(focusBorder);
	if (focusColor) {
		collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage a:focus { outline-color: ${focusColor}; }`);
	}
	const border = theme.getColor(contrastBorder);
	if (border) {
		collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage .commands .item button { border-color: ${border}; }`);
	}
	const activeBorder = theme.getColor(activeContrastBorder);
	if (activeBorder) {
		collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage .commands .item button:hover { outline-color: ${activeBorder}; }`);
	}
}


export class WelcomePageContribution implements IWorkbenchContribution {

	constructor(
		@IInstantiationService instantiationService: IInstantiationService,
		@IConfigurationService configurationService: IConfigurationService,
		@IEditorService editorService: IEditorService,
		@IBackupFileService backupFileService: IBackupFileService,
		@IFileService fileService: IFileService,
		@IWorkspaceContextService contextService: IWorkspaceContextService,
		@ILifecycleService lifecycleService: ILifecycleService,
		@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
		@ICommandService private readonly commandService: ICommandService,
	) {
		const enabled = isWelcomePageEnabled(configurationService, contextService);
		if (enabled && lifecycleService.startupKind !== StartupKind.ReloadedWindow) {
			backupFileService.hasBackups().then(hasBackups => {
				// Open the welcome even if we opened a set of default editors
				if ((!editorService.activeEditor || layoutService.openedDefaultEditors) && !hasBackups) {
					const startupEditorSetting = configurationService.getValue(configurationKey) as string;
					const openWithReadme = startupEditorSetting === 'readme';
					if (openWithReadme) {
						return Promise.all(contextService.getWorkspace().folders.map(folder => {
							const folderUri = folder.uri;
							return fileService.resolve(folderUri)
								.then(folder => {
									const files = folder.children ? folder.children.map(child => child.name).sort() : [];

									const file = files.find(file => file.toLowerCase() === 'readme.md') || files.find(file => file.toLowerCase().startsWith('readme'));

									if (file) {
										return joinPath(folderUri, file);
									}
									return undefined;
								}, onUnexpectedError);
						})).then(arrays.coalesce)
							.then<any>(readmes => {
								if (!editorService.activeEditor) {
									if (readmes.length) {
										const isMarkDown = (readme: URI) => readme.path.toLowerCase().endsWith('.md');
										return Promise.all([
											this.commandService.executeCommand('markdown.showPreview', null, readmes.filter(isMarkDown), { locked: true }),
											editorService.openEditors(readmes.filter(readme => !isMarkDown(readme))
												.map(readme => ({ resource: readme }))),
										]);
									} else {
										return instantiationService.createInstance(WelcomePage).openEditor();
									}
								}
								return undefined;
							});
					} else {
						const startupEditorTypeID = startupEditorSetting === 'gettingStarted' ? gettingStartedInputTypeId : welcomeInputTypeId;
						const startupEditorCtor = startupEditorSetting === 'gettingStarted' ? GettingStartedPage : WelcomePage;

						let options: IEditorOptions;
						let editor = editorService.activeEditor;
						if (editor) {
							// Ensure that the welcome editor won't get opened more than once
							if (editor.getTypeId() === startupEditorTypeID || editorService.editors.some(e => e.getTypeId() === startupEditorTypeID)) {
								return undefined;
							}
							options = { pinned: false, index: 0 };
						} else {
							options = { pinned: false };
						}
						return instantiationService.createInstance(startupEditorCtor).openEditor(options);
					}
				}
				return undefined;
			}).then(undefined, onUnexpectedError);
		}
	}
}


export class WelcomePageAction extends Action {

	public static readonly ID = 'workbench.action.showWelcomePage';
	public static readonly LABEL = localize('welcomePage', "Welcome");

	constructor(
		id: string,
		label: string,
		@IInstantiationService private readonly instantiationService: IInstantiationService
	) {
		super(id, label);
	}

	public run(): Promise<void> {
		return this.instantiationService.createInstance(WelcomePage)
			.openEditor()
			.then(() => undefined);
	}
}


export class WelcomeInputFactory implements IEditorInputFactory {

	static readonly ID = welcomeInputTypeId;

	public canSerialize(editorInput: EditorInput): boolean {
		return true;
	}

	public serialize(editorInput: EditorInput): string {
		return '{}';
	}

	public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): WalkThroughInput {
		return instantiationService.createInstance(WelcomePage)
			.editorInput;
	}
}


interface ExtensionSuggestion {
	name: string;
	title?: string;
	id: string;
	isKeymap?: boolean;
	isCommand?: boolean;
}


interface Strings {
	installEvent: string;
	installedEvent: string;
	detailsEvent: string;

	alreadyInstalled: string;
	reloadAfterInstall: string;
	installing: string;
	extensionNotFound: string;
}


public run(): Promise<void> {
		return this.instantiationService.createInstance(WelcomePage)
			.openEditor()
			.then(() => undefined);
	}


public openEditor(options: IEditorOptions = { pinned: false }) {
		return this.editorService.openEditor(this.editorInput, options);
	}


private onReady(container: HTMLElement, recentlyOpened: Promise<IRecentlyOpened>, installedExtensions: Promise<IExtensionStatus[]>): void {
		const enabled = isWelcomePageEnabled(this.configurationService, this.contextService);
		const showOnStartup = <HTMLInputElement>container.querySelector('#showOnStartup');
		if (enabled) {
			showOnStartup.setAttribute('checked', 'checked');
		}
		showOnStartup.addEventListener('click', e => {
			this.configurationService.updateValue(configurationKey, showOnStartup.checked ? 'welcomePage' : 'newUntitledFile');
		});

		const prodName = container.querySelector('.welcomePage .title .caption') as HTMLElement;
		if (prodName) {
			prodName.textContent = this.productService.nameLong;
		}

		const pageElement = container.querySelector('.welcomePage') as HTMLElement;
		pageElement.classList.add(this.contextService.getWorkbenchState() === WorkbenchState.EMPTY ? 'empty-window' : 'none-empty-window');

		recentlyOpened.then(({ workspaces }) => {
			// Filter out the current workspace
			workspaces = workspaces.filter(recent => !this.contextService.isCurrentWorkspace(isRecentWorkspace(recent) ? recent.workspace : recent.folderUri));
			if (!workspaces.length) {
				pageElement.classList.add('emptyRecent');
				return;
			}
			const ul = container.querySelector('.recent ul');
			if (!ul) {
				return;
			}
			const moreRecent = ul.querySelector('.moreRecent')!;
			const workspacesToShow = workspaces.slice(0, 5);
			const updateEntries = () => {
				const listEntries = this.createListEntries(workspacesToShow);
				while (ul.firstChild) {
					ul.removeChild(ul.firstChild);
				}
				ul.append(...listEntries, moreRecent);
			};
			updateEntries();
			this._register(this.labelService.onDidChangeFormatters(updateEntries));
		}).then(undefined, onUnexpectedError);

		this.addExtensionList(container, '.extensionPackList', extensionPacks, extensionPackStrings);
		this.addExtensionList(container, '.keymapList', keymapExtensions, keymapStrings);

		this.updateInstalledExtensions(container, installedExtensions);
		this._register(this.instantiationService.invokeFunction(onExtensionChanged)(ids => {
			for (const id of ids) {
				if (container.querySelector(`.installExtension[data-extension="${id.id}"], .enabledExtension[data-extension="${id.id}"]`)) {
					const installedExtensions = this.instantiationService.invokeFunction(getInstalledExtensions);
					this.updateInstalledExtensions(container, installedExtensions);
					break;
				}
			}
		}));
	}


private createListEntries(recents: (IRecentWorkspace | IRecentFolder)[]) {
		return recents.map(recent => {
			let fullPath: string;
			let windowOpenable: IWindowOpenable;
			if (isRecentFolder(recent)) {
				windowOpenable = { folderUri: recent.folderUri };
				fullPath = recent.label || this.labelService.getWorkspaceLabel(recent.folderUri, { verbose: true });
			} else {
				fullPath = recent.label || this.labelService.getWorkspaceLabel(recent.workspace, { verbose: true });
				windowOpenable = { workspaceUri: recent.workspace.configPath };
			}

			const { name, parentPath } = splitName(fullPath);

			const li = document.createElement('li');
			const a = document.createElement('a');

			a.innerText = name;
			a.title = fullPath;
			a.setAttribute('aria-label', localize('welcomePage.openFolderWithPath', "Open folder {0} with path {1}", name, parentPath));
			a.href = 'javascript:void(0)';
			a.addEventListener('click', e => {
				this.telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', {
					id: 'openRecentFolder',
					from: telemetryFrom
				});
				this.hostService.openWindow([windowOpenable], { forceNewWindow: e.ctrlKey || e.metaKey });
				e.preventDefault();
				e.stopPropagation();
			});
			li.appendChild(a);

			const span = document.createElement('span');
			span.classList.add('path');
			span.classList.add('detail');
			span.innerText = parentPath;
			span.title = fullPath;
			li.appendChild(span);

			return li;
		});
	}


private addExtensionList(container: HTMLElement, listSelector: string, suggestions: ExtensionSuggestion[], strings: Strings) {
		const list = container.querySelector(listSelector);
		if (list) {
			suggestions.forEach((extension, i) => {
				if (i) {
					list.appendChild(document.createTextNode(localize('welcomePage.extensionListSeparator', ", ")));
				}

				const a = document.createElement('a');
				a.innerText = extension.name;
				a.title = extension.title || (extension.isKeymap ? localize('welcomePage.installKeymap', "Install {0} keymap", extension.name) : localize('welcomePage.installExtensionPack', "Install additional support for {0}", extension.name));
				if (extension.isCommand) {
					a.href = `command:${extension.id}`;
					list.appendChild(a);
				} else {
					a.classList.add('installExtension');
					a.setAttribute('data-extension', extension.id);
					a.href = 'javascript:void(0)';
					a.addEventListener('click', e => {
						this.installExtension(extension, strings);
						e.preventDefault();
						e.stopPropagation();
					});
					list.appendChild(a);

					const span = document.createElement('span');
					span.innerText = extension.name;
					span.title = extension.isKeymap ? localize('welcomePage.installedKeymap', "{0} keymap is already installed", extension.name) : localize('welcomePage.installedExtensionPack', "{0} support is already installed", extension.name);
					span.classList.add('enabledExtension');
					span.setAttribute('data-extension', extension.id);
					list.appendChild(span);
				}
			});
		}
	}