Best JavaScript code snippet using playwright-internal
cli.ts
Source:cli.ts
1#!/usr/bin/env node2// tslint:disable no-console3/*4 * IMPORTANT: the './postgraphilerc' import MUST come first!5 *6 * Reason: enables user to apply modifications to their Node.js environment7 * (e.g. sourcing modules that affect global state, like dotenv) before any of8 * our other require()s occur.9 */10import config from './postgraphilerc';11import * as os from 'os';12import { createServer } from 'http';13import chalk from 'chalk';14import program = require('commander');15import jwt = require('jsonwebtoken');16import { parse as parsePgConnectionString } from 'pg-connection-string';17import postgraphile, { getPostgraphileSchemaBuilder } from './postgraphile';18import { makePgSmartTagsFromFilePlugin } from '../plugins';19import { Pool, PoolConfig } from 'pg';20import cluster = require('cluster');21import { makePluginHook, PostGraphilePlugin } from './pluginHook';22import debugFactory = require('debug');23import { mixed } from '../interfaces';24// @ts-ignore Allow importing JSON25import * as manifest from '../../package.json';26// @ts-ignore Allow importing JSON27import sponsors = require('../../sponsors.json');28import { enhanceHttpServerWithWebSockets } from './http/subscriptions';29import { existsSync } from 'fs';30const tagsFile = process.cwd() + '/postgraphile.tags.json5';31/*32 * Watch mode on the tags file is non-trivial, so only load the plugin if the33 * file exists when PostGraphile starts.34 */35const smartTagsPlugin = existsSync(tagsFile) ? makePgSmartTagsFromFilePlugin() : null;36const isDev = process.env.POSTGRAPHILE_ENV === 'development';37function isString(str: unknown): str is string {38 return typeof str === 'string';39}40const sponsor = sponsors[Math.floor(sponsors.length * Math.random())];41const debugCli = debugFactory('postgraphile:cli');42// TODO: Demo Postgres database43const DEMO_PG_URL = null;44function extractPlugins(45 rawArgv: Array<string>,46): {47 argv: Array<string>;48 plugins: Array<PostGraphilePlugin>;49} {50 let argv;51 let pluginStrings = [];52 if (rawArgv[2] === '--plugins') {53 pluginStrings = rawArgv[3].split(',');54 argv = [...rawArgv.slice(0, 2), ...rawArgv.slice(4)];55 } else {56 pluginStrings = (config && config['options'] && config['options']['plugins']) || [];57 argv = rawArgv;58 }59 const plugins = pluginStrings.map((pluginString: string) => {60 debugCli('Loading plugin %s', pluginString);61 const rawPlugin = require(pluginString); // tslint:disable-lin no-var-requires62 if (rawPlugin['default'] && typeof rawPlugin['default'] === 'object') {63 return rawPlugin['default'];64 } else {65 return rawPlugin;66 }67 });68 return { argv, plugins };69}70const { argv: argvSansPlugins, plugins: extractedPlugins } = extractPlugins(process.argv);71const pluginHook = makePluginHook(extractedPlugins);72program.version(manifest.version).usage('[options...]').description(manifest.description);73// .option('-d, --demo', 'run PostGraphile using the demo database connection')74export type AddFlagFn = (75 optionString: string,76 description: string,77 parse?: (option: string) => mixed,78) => AddFlagFn;79function addFlag(80 optionString: string,81 description: string,82 parse?: (option: string) => mixed,83): AddFlagFn {84 program.option(optionString, description, parse);85 return addFlag;86}87// Standard options88program89 .option(90 '--plugins <string>',91 'a list of PostGraphile server plugins (not Graphile Engine schema plugins) to load; if present, must be the _first_ option',92 )93 .option(94 '-c, --connection <string>',95 "the PostgreSQL database name or connection string. If omitted, inferred from environmental variables (see https://www.postgresql.org/docs/current/static/libpq-envars.html). Examples: 'db', 'postgres:///db', 'postgres://user:password@domain:port/db?ssl=true'",96 )97 .option(98 '-C, --owner-connection <string>',99 'as `--connection`, but for a privileged user (e.g. for setting up watch fixtures, logical decoding, etc); defaults to the value from `--connection`',100 )101 .option(102 '-s, --schema <string>',103 'a Postgres schema to be introspected. Use commas to define multiple schemas',104 (option: string) => option.split(','),105 )106 .option(107 '-S, --subscriptions',108 'Enable GraphQL support for subscriptions (you still need a subscriptions plugin currently)',109 )110 .option(111 '--websockets <string>',112 "Choose which websocket transport libraries to use. Use commas to define multiple. Defaults to 'v0,v1' if `--subscriptions` or `--live` were passed, '[]' otherwise",113 (option: string) => option.split(','),114 )115 .option(116 '--websocket-operations <operations>',117 "Toggle which GraphQL websocket transport operations are supported: 'subscriptions' or 'all'. Defaults to 'subscriptions'",118 )119 .option(120 '-L, --live',121 '[EXPERIMENTAL] Enables live-query support via GraphQL subscriptions (sends updated payload any time nested collections/records change). Implies --subscriptions',122 )123 .option(124 '-w, --watch',125 'automatically updates your GraphQL schema when your database schema changes (NOTE: requires DB superuser to install `postgraphile_watch` schema)',126 )127 .option('-n, --host <string>', 'the hostname to be used. Defaults to `localhost`')128 .option('-p, --port <number>', 'the port to be used. Defaults to 5000', parseFloat)129 .option(130 '-m, --max-pool-size <number>',131 'the maximum number of clients to keep in the Postgres pool. defaults to 10',132 parseFloat,133 )134 .option(135 '-r, --default-role <string>',136 'the default Postgres role to use when a request is made. supercedes the role used to connect to the database',137 )138 .option(139 '--retry-on-init-fail',140 'if an error occurs building the initial schema, this flag will cause PostGraphile to keep trying to build the schema with exponential backoff rather than exiting',141 );142pluginHook('cli:flags:add:standard', addFlag);143// Schema configuration144program145 .option(146 '-j, --dynamic-json',147 '[RECOMMENDED] enable dynamic JSON in GraphQL inputs and outputs. PostGraphile uses stringified JSON by default',148 )149 .option(150 '-N, --no-setof-functions-contain-nulls',151 '[RECOMMENDED] if none of your `RETURNS SETOF compound_type` functions mix NULLs with the results then you may enable this to reduce the nullables in the GraphQL schema',152 )153 .option('-a, --classic-ids', 'use classic global id field name. required to support Relay 1')154 .option(155 '-M, --disable-default-mutations',156 'disable default mutations, mutation will only be possible through Postgres functions',157 )158 .option(159 '--simple-collections <omit|both|only>',160 '"omit" (default) - relay connections only, "only" - simple collections only (no Relay connections), "both" - both',161 )162 .option(163 '--no-ignore-rbac',164 '[RECOMMENDED] set this to exclude fields, queries and mutations that are not available to any possible user (determined from the user in connection string and any role they can become); this will be enabled by default in v5',165 )166 .option(167 '--no-ignore-indexes',168 '[RECOMMENDED] set this to exclude filters, orderBy, and relations that would be expensive to access due to missing indexes',169 )170 .option(171 '--include-extension-resources',172 'by default, tables and functions that come from extensions are excluded; use this flag to include them (not recommended)',173 );174pluginHook('cli:flags:add:schema', addFlag);175// Error enhancements176program177 .option(178 '--show-error-stack [json|string]',179 'show JavaScript error stacks in the GraphQL result errors (recommended in development)',180 )181 .option(182 '--extended-errors <string>',183 "a comma separated list of extended Postgres error fields to display in the GraphQL result. Recommended in development: 'hint,detail,errcode'. Default: none",184 (option: string) => option.split(',').filter(_ => _),185 );186pluginHook('cli:flags:add:errorHandling', addFlag);187// Plugin-related options188program189 .option(190 '--append-plugins <string>',191 'a comma-separated list of plugins to append to the list of Graphile Engine schema plugins',192 )193 .option(194 '--prepend-plugins <string>',195 'a comma-separated list of plugins to prepend to the list of Graphile Engine schema plugins',196 )197 .option(198 '--skip-plugins <string>',199 'a comma-separated list of Graphile Engine schema plugins to skip',200 );201pluginHook('cli:flags:add:plugins', addFlag);202// Things that relate to -X203program204 .option(205 '--read-cache <path>',206 '[experimental] reads cached values from local cache file to improve startup time (you may want to do this in production)',207 )208 .option(209 '--write-cache <path>',210 '[experimental] writes computed values to local cache file so startup can be faster (do this during the build phase)',211 )212 .option(213 '--export-schema-json <path>',214 'enables exporting the detected schema, in JSON format, to the given location. The directories must exist already, if the file exists it will be overwritten.',215 )216 .option(217 '--export-schema-graphql <path>',218 'enables exporting the detected schema, in GraphQL schema format, to the given location. The directories must exist already, if the file exists it will be overwritten.',219 )220 .option(221 '--sort-export',222 'lexicographically (alphabetically) sort exported schema for more stable diffing.',223 )224 .option(225 '-X, --no-server',226 '[experimental] for when you just want to use --write-cache or --export-schema-* and not actually run a server (e.g. CI)',227 );228pluginHook('cli:flags:add:noServer', addFlag);229// Webserver configuration230program231 .option(232 '-q, --graphql <path>',233 'the route to mount the GraphQL server on. defaults to `/graphql`',234 )235 .option(236 '-i, --graphiql <path>',237 'the route to mount the GraphiQL interface on. defaults to `/graphiql`',238 )239 .option(240 '--enhance-graphiql',241 '[DEVELOPMENT] opt in to additional GraphiQL functionality (this may change over time - only intended for use in development; automatically enables with `subscriptions` and `live`)',242 )243 .option(244 '-b, --disable-graphiql',245 'disables the GraphiQL interface. overrides the GraphiQL route option',246 )247 .option(248 '-o, --cors',249 'enable generous CORS settings; disabled by default, if possible use a proxy instead',250 )251 .option(252 '-l, --body-size-limit <string>',253 "set the maximum size of the HTTP request body that can be parsed (default 100kB). The size can be given as a human-readable string, such as '200kB' or '5MB' (case insensitive).",254 )255 .option('--timeout <number>', 'set the timeout value in milliseconds for sockets', parseFloat)256 .option(257 '--cluster-workers <count>',258 '[experimental] spawn <count> workers to increase throughput',259 parseFloat,260 )261 .option(262 '--enable-query-batching',263 '[experimental] enable the server to process multiple GraphQL queries in one request',264 )265 .option('--disable-query-log', 'disable logging queries to console (recommended in production)')266 .option(267 '--allow-explain',268 '[EXPERIMENTAL] allows users to use the Explain button in GraphiQL to view the plan for the SQL that is executed (DO NOT USE IN PRODUCTION)',269 );270pluginHook('cli:flags:add:webserver', addFlag);271// JWT-related options272program273 .option(274 '-e, --jwt-secret <string>',275 'the secret to be used when creating and verifying JWTs. if none is provided auth will be disabled',276 )277 .option(278 '--jwt-verify-algorithms <string>',279 'a comma separated list of the names of the allowed jwt token algorithms',280 (option: string) => option.split(','),281 )282 .option(283 '-A, --jwt-verify-audience <string>',284 "a comma separated list of JWT audiences that will be accepted; defaults to 'postgraphile'. To disable audience verification, set to ''.",285 (option: string) => option.split(',').filter(_ => _),286 )287 .option(288 '--jwt-verify-clock-tolerance <number>',289 'number of seconds to tolerate when checking the nbf and exp claims, to deal with small clock differences among different servers',290 parseFloat,291 )292 .option('--jwt-verify-id <string>', 'the name of the allowed jwt token id')293 .option(294 '--jwt-verify-ignore-expiration',295 'if `true` do not validate the expiration of the token defaults to `false`',296 )297 .option(298 '--jwt-verify-ignore-not-before',299 'if `true` do not validate the notBefore of the token defaults to `false`',300 )301 .option(302 '--jwt-verify-issuer <string>',303 'a comma separated list of the names of the allowed jwt token issuer',304 (option: string) => option.split(','),305 )306 .option('--jwt-verify-subject <string>', 'the name of the allowed jwt token subject')307 .option(308 '--jwt-role <string>',309 'a comma seperated list of strings that create a path in the jwt from which to extract the postgres role. if none is provided it will use the key `role` on the root of the jwt.',310 (option: string) => option.split(','),311 )312 .option(313 '-t, --jwt-token-identifier <identifier>',314 'the Postgres identifier for a composite type that will be used to create JWT tokens',315 );316pluginHook('cli:flags:add:jwt', addFlag);317// Any other options318pluginHook('cli:flags:add', addFlag);319// Deprecated320program321 .option(322 '--token <identifier>',323 '[DEPRECATED] Use --jwt-token-identifier instead. This option will be removed in v5.',324 )325 .option(326 '--secret <string>',327 '[DEPRECATED] Use --jwt-secret instead. This option will be removed in v5.',328 )329 .option(330 '--jwt-audiences <string>',331 '[DEPRECATED] Use --jwt-verify-audience instead. This option will be removed in v5.',332 (option: string) => option.split(','),333 )334 .option(335 '--legacy-functions-only',336 '[DEPRECATED] PostGraphile 4.1.0 introduced support for PostgreSQL functions than declare parameters with IN/OUT/INOUT or declare RETURNS TABLE(...); enable this flag to ignore these types of functions. This option will be removed in v5.',337 );338pluginHook('cli:flags:add:deprecated', addFlag);339// Awkward application workarounds / legacy support340program341 .option(342 '--legacy-relations <omit|deprecated|only>',343 "some one-to-one relations were previously detected as one-to-many - should we export 'only' the old relation shapes, both new and old but mark the old ones as 'deprecated', or 'omit' the old relation shapes entirely",344 )345 .option(346 '--legacy-json-uuid',347 `ONLY use this option if you require the v3 typenames 'Json' and 'Uuid' over 'JSON' and 'UUID'`,348 );349pluginHook('cli:flags:add:workarounds', addFlag);350program.on('--help', () => {351 console.log(`352Get started:353 $ postgraphile354 $ postgraphile -c postgres://localhost/my_db355 $ postgraphile --connection postgres://user:pass@localhost/my_db --schema my_schema --watch --dynamic-json356`);357 process.exit(0);358});359program.parse(argvSansPlugins);360function exitWithErrorMessage(message: string): never {361 console.error(message);362 console.error();363 console.error('For help, run `postgraphile --help`');364 process.exit(1);365}366if (program.args.length) {367 exitWithErrorMessage(368 `ERROR: some of the parameters you passed could not be processed: '${program.args.join(369 "', '",370 )}'`,371 );372}373if (program['plugins']) {374 exitWithErrorMessage(`--plugins must be the first argument to postgraphile if specified`);375}376// Kill server on exit.377process.on('SIGINT', () => {378 process.exit(1);379});380// For `--no-*` options, `program` automatically contains the default,381// overriding our options. We typically want the CLI to "win", but not382// with defaults! So this code extracts those `--no-*` values and383// re-overwrites the values if necessary.384const configOptions = config['options'] || {};385const overridesFromOptions = {};386['ignoreIndexes', 'ignoreRbac', 'setofFunctionsContainNulls'].forEach(option => {387 if (option in configOptions) {388 overridesFromOptions[option] = configOptions[option];389 }390});391// Destruct our configuration file and command line arguments, use defaults, and rename options to392// something appropriate for JavaScript.393const {394 demo: isDemo = false,395 connection: pgConnectionString,396 ownerConnection,397 subscriptions,398 live,399 websockets = subscriptions || live ? ['v0', 'v1'] : [],400 websocketOperations = 'subscriptions',401 watch: watchPg,402 schema: dbSchema,403 host: hostname = 'localhost',404 port = 5000,405 timeout: serverTimeout,406 maxPoolSize,407 defaultRole: pgDefaultRole,408 retryOnInitFail,409 graphql: graphqlRoute = '/graphql',410 graphiql: graphiqlRoute = '/graphiql',411 enhanceGraphiql = false,412 disableGraphiql = false,413 secret: deprecatedJwtSecret,414 jwtSecret,415 jwtPublicKey,416 jwtAudiences,417 jwtVerifyAlgorithms,418 jwtVerifyAudience,419 jwtVerifyClockTolerance,420 jwtVerifyId,421 jwtVerifyIgnoreExpiration,422 jwtVerifyIgnoreNotBefore,423 jwtVerifyIssuer,424 jwtVerifySubject,425 jwtSignOptions = {},426 jwtVerifyOptions: rawJwtVerifyOptions,427 jwtRole = ['role'],428 token: deprecatedJwtPgTypeIdentifier,429 jwtTokenIdentifier: jwtPgTypeIdentifier,430 cors: enableCors = false,431 classicIds = false,432 dynamicJson = false,433 disableDefaultMutations = false,434 ignoreRbac = true,435 includeExtensionResources = false,436 exportSchemaJson: exportJsonSchemaPath,437 exportSchemaGraphql: exportGqlSchemaPath,438 sortExport = false,439 showErrorStack: rawShowErrorStack,440 extendedErrors = [],441 bodySizeLimit,442 appendPlugins: appendPluginNames,443 prependPlugins: prependPluginNames,444 // replaceAllPlugins is NOT exposed via the CLI445 skipPlugins: skipPluginNames,446 readCache,447 writeCache,448 legacyRelations: rawLegacyRelations = 'deprecated',449 server: yesServer,450 clusterWorkers,451 enableQueryBatching,452 setofFunctionsContainNulls = true,453 legacyJsonUuid,454 disableQueryLog,455 allowExplain,456 simpleCollections,457 legacyFunctionsOnly,458 ignoreIndexes,459 // tslint:disable-next-line no-any460} = { ...config['options'], ...program, ...overridesFromOptions } as typeof program;461const showErrorStack = (val => {462 switch (val) {463 case 'string':464 case true:465 return true;466 case null:467 case undefined:468 return undefined;469 case 'json':470 return 'json';471 default: {472 exitWithErrorMessage(473 `Invalid argument for '--show-error-stack' - expected no argument, or 'string' or 'json'`,474 );475 }476 }477})(rawShowErrorStack);478if (allowExplain && !disableGraphiql && !enhanceGraphiql) {479 exitWithErrorMessage('`--allow-explain` requires `--enhance-graphiql` or `--disable-graphiql`');480}481let legacyRelations: 'omit' | 'deprecated' | 'only';482if (!['omit', 'only', 'deprecated'].includes(rawLegacyRelations)) {483 exitWithErrorMessage(484 `Invalid argument to '--legacy-relations' - expected on of 'omit', 'deprecated', 'only'; but received '${rawLegacyRelations}'`,485 );486} else {487 legacyRelations = rawLegacyRelations;488}489// Validate websockets argument490if (491 // must be array492 !Array.isArray(websockets) ||493 // empty array = 'none'494 (websockets.length &&495 // array can only hold the versions496 websockets.some(ver => !['v0', 'v1'].includes(ver)))497) {498 exitWithErrorMessage(499 `Invalid argument to '--websockets' - expected 'v0' and/or 'v1' (separated by comma); but received '${websockets}'`,500 );501}502if (websocketOperations !== 'subscriptions' && websocketOperations !== 'all') {503 exitWithErrorMessage(504 `Invalid argument to '--websocket-operations' - expected 'subscriptions' or 'all' but received '${websocketOperations}'`,505 );506}507const noServer = !yesServer;508// Add custom logic for getting the schemas from our CLI. If we are in demo509// mode, we want to use the `forum_example` schema. Otherwise the `public`510// schema is what we want.511const schemas: Array<string> = dbSchema || (isDemo ? ['forum_example'] : ['public']);512const ownerConnectionString = ownerConnection || pgConnectionString || process.env.DATABASE_URL;513// Work around type mismatches between parsePgConnectionString and PoolConfig514const coerce = (o: ReturnType<typeof parsePgConnectionString>): PoolConfig => {515 const port =516 typeof o.port === 'number'517 ? o.port518 : typeof o.port === 'string'519 ? parseInt(o.port, 10)520 : undefined;521 return {522 ...o,523 application_name: o['application_name'] || undefined,524 ssl:525 o.ssl == null526 ? undefined527 : (o.ssl as any).rejectUnauthorized == null528 ? !!o.ssl529 : (o.ssl as any),530 user: typeof o.user === 'string' ? o.user : undefined,531 database: typeof o.database === 'string' ? o.database : undefined,532 password: typeof o.password === 'string' ? o.password : undefined,533 port: Number.isFinite(port) ? port : undefined,534 host: typeof o.host === 'string' ? o.host : undefined,535 };536};537// Create our Postgres config.538const pgConfig: PoolConfig = {539 // If we have a Postgres connection string, parse it and use that as our540 // config. If we donât have a connection string use some environment541 // variables or final defaults. Other environment variables should be542 // detected and used by `pg`.543 ...(pgConnectionString || process.env.DATABASE_URL || isDemo544 ? coerce(parsePgConnectionString(pgConnectionString || process.env.DATABASE_URL || DEMO_PG_URL))545 : {546 host: process.env.PGHOST || process.env.PGHOSTADDR || 'localhost',547 port: (process.env.PGPORT ? parseInt(process.env.PGPORT, 10) : null) || 5432,548 database: process.env.PGDATABASE,549 user: process.env.PGUSER,550 password: process.env.PGPASSWORD,551 }),552 // Add the max pool size to our config.553 max: maxPoolSize,554};555const loadPlugins = (rawNames: mixed) => {556 if (!rawNames) {557 return undefined;558 }559 const names = Array.isArray(rawNames) ? rawNames : String(rawNames).split(',');560 return names.map(rawName => {561 if (typeof rawName === 'function') {562 return rawName;563 }564 const name = String(rawName);565 const parts = name.split(':');566 if (567 process.platform === 'win32' &&568 parts[0].length === 1 &&569 /^[a-z]$/i.test(parts[0]) &&570 ['\\', '/'].includes(name[2])571 ) {572 // Assume this is a windows path `C:/path/to/module.js` or `C:\path\to\module.js`573 const driveLetter = parts.shift();574 // Add the drive part back onto the path575 parts[0] = `${driveLetter}:${parts[0]}`;576 }577 let root;578 try {579 root = require(String(parts.shift()));580 } catch (e) {581 // tslint:disable-next-line no-console582 console.error(`Failed to load plugin '${name}'`);583 throw e;584 }585 let plugin = root;586 let part: string | void;587 while ((part = parts.shift())) {588 plugin = plugin[part];589 if (plugin == null) {590 throw new Error(`No plugin found matching spec '${name}' - failed at '${part}'`);591 }592 }593 if (typeof plugin === 'function') {594 return plugin;595 } else if (plugin === root && typeof plugin.default === 'function') {596 return plugin.default; // ES6 workaround597 } else {598 throw new Error(599 `No plugin found matching spec '${name}' - expected function, found '${typeof plugin}'`,600 );601 }602 });603};604if (jwtAudiences != null && jwtVerifyAudience != null) {605 exitWithErrorMessage(606 `Provide either '--jwt-audiences' or '-A, --jwt-verify-audience' but not both`,607 );608}609function trimNulls(obj: Record<string, any>): Record<string, any> {610 return Object.keys(obj).reduce((memo, key) => {611 if (obj[key] != null) {612 memo[key] = obj[key];613 }614 return memo;615 }, {});616}617if (618 rawJwtVerifyOptions &&619 (jwtVerifyAlgorithms ||620 jwtVerifyAudience ||621 jwtVerifyClockTolerance ||622 jwtVerifyId ||623 jwtVerifyIgnoreExpiration ||624 jwtVerifyIgnoreNotBefore ||625 jwtVerifyIssuer ||626 jwtVerifySubject)627) {628 exitWithErrorMessage(629 'You may not mix `jwtVerifyOptions` with the legacy `jwtVerify*` settings; please only provide `jwtVerifyOptions`.',630 );631}632const jwtVerifyOptions: jwt.VerifyOptions = rawJwtVerifyOptions633 ? rawJwtVerifyOptions634 : trimNulls({635 algorithms: jwtVerifyAlgorithms,636 audience: jwtVerifyAudience,637 clockTolerance: jwtVerifyClockTolerance,638 jwtId: jwtVerifyId,639 ignoreExpiration: jwtVerifyIgnoreExpiration,640 ignoreNotBefore: jwtVerifyIgnoreNotBefore,641 issuer: jwtVerifyIssuer,642 subject: jwtVerifySubject,643 });644const appendPlugins = loadPlugins(appendPluginNames);645const prependPlugins = loadPlugins(prependPluginNames);646const skipPlugins = loadPlugins(skipPluginNames);647// The options to pass through to the schema builder, or the middleware648const postgraphileOptions = pluginHook(649 'cli:library:options',650 {651 ...config['options'],652 classicIds,653 dynamicJson,654 disableDefaultMutations,655 ignoreRBAC: ignoreRbac,656 includeExtensionResources,657 graphqlRoute,658 graphiqlRoute,659 graphiql: !disableGraphiql,660 enhanceGraphiql: enhanceGraphiql ? true : undefined,661 jwtPgTypeIdentifier: jwtPgTypeIdentifier || deprecatedJwtPgTypeIdentifier,662 jwtSecret: jwtSecret || deprecatedJwtSecret || process.env.JWT_SECRET,663 jwtPublicKey,664 jwtAudiences,665 jwtSignOptions,666 jwtRole,667 jwtVerifyOptions,668 retryOnInitFail,669 pgDefaultRole,670 subscriptions: subscriptions || live,671 websockets,672 websocketOperations,673 live,674 watchPg,675 showErrorStack,676 extendedErrors,677 disableQueryLog,678 allowExplain: allowExplain ? true : undefined,679 enableCors,680 exportJsonSchemaPath,681 exportGqlSchemaPath,682 sortExport,683 bodySizeLimit,684 appendPlugins: smartTagsPlugin ? [smartTagsPlugin, ...(appendPlugins || [])] : appendPlugins,685 prependPlugins,686 skipPlugins,687 readCache,688 writeCache,689 legacyRelations,690 setofFunctionsContainNulls,691 legacyJsonUuid,692 enableQueryBatching,693 pluginHook,694 simpleCollections,695 legacyFunctionsOnly,696 ignoreIndexes,697 ownerConnectionString,698 },699 { config, cliOptions: program },700);701function killAllWorkers(signal = 'SIGTERM'): void {702 for (const id in cluster.workers) {703 const worker = cluster.workers[id];704 if (Object.prototype.hasOwnProperty.call(cluster.workers, id) && worker) {705 worker.kill(signal);706 }707 }708}709if (noServer) {710 // No need for a server, let's just spin up the schema builder711 (async (): Promise<void> => {712 const pgPool = new Pool(pgConfig);713 pgPool.on('error', err => {714 // tslint:disable-next-line no-console715 console.error('PostgreSQL client generated error: ', err.message);716 });717 const { getGraphQLSchema } = getPostgraphileSchemaBuilder(pgPool, schemas, postgraphileOptions);718 await getGraphQLSchema();719 if (!watchPg) {720 await pgPool.end();721 }722 })().then(null, e => {723 console.error('Error occurred!');724 console.error(e);725 process.exit(1);726 });727} else {728 if (clusterWorkers >= 2 && cluster.isMaster) {729 let shuttingDown = false;730 const shutdown = () => {731 if (!shuttingDown) {732 shuttingDown = true;733 process.exitCode = 1;734 const fallbackTimeout = setTimeout(() => {735 const remainingCount = Object.keys(cluster.workers).length;736 if (remainingCount > 0) {737 console.log(738 ` [cluster] ${remainingCount} workers did not die fast enough, sending SIGKILL`,739 );740 killAllWorkers('SIGKILL');741 const ultraFallbackTimeout = setTimeout(() => {742 console.log(743 ` [cluster] really should have exited automatically, but haven't - exiting`,744 );745 process.exit(3);746 }, 5000);747 ultraFallbackTimeout.unref();748 } else {749 console.log(` [cluster] should have exited automatically, but haven't - exiting`);750 process.exit(2);751 }752 }, 5000);753 fallbackTimeout.unref();754 console.log(` [cluster] killing other workers with SIGTERM`);755 killAllWorkers('SIGTERM');756 }757 };758 cluster.on('exit', (worker, code, signal) => {759 console.log(760 ` [cluster] worker pid=${worker.process.pid} exited (code=${code}, signal=${signal})`,761 );762 shutdown();763 });764 for (let i = 0; i < clusterWorkers; i++) {765 const worker = cluster.fork({766 POSTGRAPHILE_WORKER_NUMBER: String(i + 1),767 });768 console.log(` [cluster] started worker ${i + 1} (pid=${worker.process.pid})`);769 }770 } else {771 // Createâs our PostGraphile server772 const rawMiddleware = postgraphile(pgConfig, schemas, postgraphileOptions);773 // You probably don't want this hook; likely you want774 // `postgraphile:middleware` instead. This hook will likely be removed in775 // future without warning.776 const middleware = pluginHook(777 /* DO NOT USE -> */ 'cli:server:middleware' /* <- DO NOT USE */,778 rawMiddleware,779 {780 options: postgraphileOptions,781 },782 );783 const server = createServer(middleware);784 if (serverTimeout) {785 server.timeout = serverTimeout;786 }787 if (websockets.length) {788 enhanceHttpServerWithWebSockets(server, middleware);789 }790 pluginHook('cli:server:created', server, {791 options: postgraphileOptions,792 middleware,793 });794 // Start our server by listening to a specific port and host name. Also log795 // some instructions and other interesting information.796 server.listen(port, hostname, () => {797 const address = server.address();798 const actualPort = typeof address === 'string' ? port : address.port;799 const self = cluster.isMaster800 ? isDev801 ? `server (pid=${process.pid})`802 : 'server'803 : `worker ${process.env.POSTGRAPHILE_WORKER_NUMBER} (pid=${process.pid})`;804 const versionString = `v${manifest.version}`;805 if (cluster.isMaster || process.env.POSTGRAPHILE_WORKER_NUMBER === '1') {806 console.log('');807 console.log(808 `PostGraphile ${versionString} ${self} listening on port ${chalk.underline(809 actualPort.toString(),810 )} ð`,811 );812 console.log('');813 const {814 host: rawPgHost,815 port: rawPgPort,816 database: pgDatabase,817 user: pgUser,818 password: pgPassword,819 } = pgConfig;820 // Not using default because want to handle the empty string also.821 const pgHost = rawPgHost || 'localhost';822 const pgPort = (rawPgPort && parseInt(String(rawPgPort), 10)) || 5432;823 const safeConnectionString = isDemo824 ? 'postgraphile_demo'825 : `postgres://${pgUser ? pgUser : ''}${pgPassword ? ':[SECRET]' : ''}${826 pgUser || pgPassword ? '@' : ''827 }${pgUser || pgPassword || pgHost !== 'localhost' || pgPort !== 5432 ? pgHost : ''}${828 pgPort !== 5432 ? `:${pgConfig.port || 5432}` : ''829 }${pgDatabase ? `/${pgDatabase}` : ''}`;830 const information: Array<string> = pluginHook(831 'cli:greeting',832 [833 `GraphQL API: ${chalk.underline.bold.blue(834 `http://${hostname}:${actualPort}${graphqlRoute}`,835 )}` +836 (postgraphileOptions.subscriptions837 ? ` (${postgraphileOptions.live ? 'live ' : ''}subscriptions enabled)`838 : ''),839 !disableGraphiql &&840 `GraphiQL GUI/IDE: ${chalk.underline.bold.blue(841 `http://${hostname}:${actualPort}${graphiqlRoute}`,842 )}` +843 (postgraphileOptions.enhanceGraphiql ||844 postgraphileOptions.live ||845 postgraphileOptions.subscriptions846 ? ''847 : ` (${chalk.bold('RECOMMENDATION')}: add '--enhance-graphiql')`),848 `Postgres connection: ${chalk.underline.magenta(safeConnectionString)}${849 postgraphileOptions.watchPg ? ' (watching)' : ''850 }`,851 `Postgres schema(s): ${schemas.map(schema => chalk.magenta(schema)).join(', ')}`,852 `Documentation: ${chalk.underline(853 `https://graphile.org/postgraphile/introduction/`,854 )}`,855 `Node.js version: ${process.version} on ${os.platform()} ${os.arch()}`,856 extractedPlugins.length === 0857 ? `Join ${chalk.bold(858 sponsor,859 )} in supporting PostGraphile development: ${chalk.underline.bold.blue(860 `https://graphile.org/sponsor/`,861 )}`862 : null,863 ],864 {865 options: postgraphileOptions,866 middleware,867 port: actualPort,868 chalk,869 },870 ).filter(isString);871 console.log(information.map(msg => ` ⣠${msg}`).join('\n'));872 console.log('');873 console.log(chalk.gray('* * *'));874 } else {875 console.log(876 `PostGraphile ${versionString} ${self} listening on port ${chalk.underline(877 actualPort.toString(),878 )} ð`,879 );880 }881 console.log('');882 });883 }884}...
cli.js
Source:cli.js
...97 }98 };99 });100 }101 const overrides = overridesFromOptions(opts);102 if (opts.headed) overrides.use = {103 headless: false104 };105 const runner = new _runner.Runner(defaultConfig, overrides);106 async function loadConfig(configFile) {107 if (fs.existsSync(configFile)) {108 if (process.stdout.isTTY) console.log(`Using config at ` + configFile);109 const loadedConfig = await runner.loadConfigFile(configFile);110 if ('projects' in loadedConfig && opts.browser) throw new Error(`Cannot use --browser option when configuration file defines projects. Specify browserName in the projects instead.`);111 return true;112 }113 return false;114 }115 async function loadConfigFromDirectory(directory) {116 const configNames = [tsConfig, jsConfig, mjsConfig];117 for (const configName of configNames) {118 if (await loadConfig(path.resolve(directory, configName))) return true;119 }120 return false;121 }122 if (opts.config) {123 const configFile = path.resolve(process.cwd(), opts.config);124 if (!fs.existsSync(configFile)) throw new Error(`${opts.config} does not exist`);125 if (fs.statSync(configFile).isDirectory()) {126 // When passed a directory, look for a config file inside.127 if (!(await loadConfigFromDirectory(configFile))) {128 // If there is no config, assume this as a root testing directory.129 runner.loadEmptyConfig(configFile);130 }131 } else {132 // When passed a file, it must be a config file.133 await loadConfig(configFile);134 }135 } else if (!(await loadConfigFromDirectory(process.cwd()))) {136 // No --config option, let's look for the config file in the current directory.137 // If not, scan the world.138 runner.loadEmptyConfig(process.cwd());139 }140 const filePatternFilters = args.map(arg => {141 const match = /^(.*):(\d+)$/.exec(arg);142 return {143 re: forceRegExp(match ? match[1] : arg),144 line: match ? parseInt(match[2], 10) : null145 };146 });147 const result = await runner.run(!!opts.list, filePatternFilters, opts.project || undefined);148 await (0, _profiler.stopProfiling)(undefined);149 if (result === 'sigint') process.exit(130);150 process.exit(result === 'passed' ? 0 : 1);151}152function forceRegExp(pattern) {153 const match = pattern.match(/^\/(.*)\/([gi]*)$/);154 if (match) return new RegExp(match[1], match[2]);155 return new RegExp(pattern, 'gi');156}157function overridesFromOptions(options) {158 const isDebuggerAttached = !!require('inspector').url();159 const shardPair = options.shard ? options.shard.split('/').map(t => parseInt(t, 10)) : undefined;160 return {161 forbidOnly: options.forbidOnly ? true : undefined,162 globalTimeout: isDebuggerAttached ? 0 : options.globalTimeout ? parseInt(options.globalTimeout, 10) : undefined,163 grep: options.grep ? forceRegExp(options.grep) : undefined,164 grepInvert: options.grepInvert ? forceRegExp(options.grepInvert) : undefined,165 maxFailures: options.x ? 1 : options.maxFailures ? parseInt(options.maxFailures, 10) : undefined,166 outputDir: options.output ? path.resolve(process.cwd(), options.output) : undefined,167 quiet: options.quiet ? options.quiet : undefined,168 repeatEach: options.repeatEach ? parseInt(options.repeatEach, 10) : undefined,169 retries: options.retries ? parseInt(options.retries, 10) : undefined,170 reporter: options.reporter && options.reporter.length ? options.reporter.split(',').map(r => [resolveReporter(r)]) : undefined,171 shard: shardPair ? {...
Using AI Code Generation
1const { Playwright } = require('playwright');2const { chromium } = require('playwright-chromium');3const { firefox } = require('playwright-firefox');4const { webkit } = require('playwright-webkit');5const options = {6 viewport: {7 },8 userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36',9 httpCredentials: {10 },11 geolocation: {
Using AI Code Generation
1const { overridesFromOptions } = require('playwright/lib/utils/options');2const { chromium } = require('playwright');3(async () => {4 const browser = await chromium.launch({5 });6 const context = await browser.newContext(overridesFromOptions({7 userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36',8 extraHTTPHeaders: {}9 }));10 const page = await context.newPage();11})();
Using AI Code Generation
1const { overridesFromOptions } = require('playwright/lib/utils/overrides');2const options = {3 viewport: {4 },5};6const overrides = overridesFromOptions(options);7console.log(overrides);8const { createBrowserContext } = require('playwright/lib/server/browserContext');9const browser = await chromium.launch();10const context = await createBrowserContext(browser, overrides);11console.log(context);12const { createPageInContext } = require('playwright/lib/server/browserContext');13const page = await createPageInContext(context);14console.log(page);15const { createPage } = require('playwright/lib/server/browser');16const page = await createPage(browser, overrides);17console.log(page);18{ ignoreHTTPSErrors: true,19 viewport: { width: 1920, height: 1080 } }20BrowserContext {21 _browser: Browser {22 _connection: Connection {23 _callbacks: Map(0) {},24 _promises: Map(0) {},25 _sessions: Map(0) {},26 _eventListeners: Map(0) {},27 _browser: Browser {28 }29 },30 _browserType: Chromium {31 _options: {}32 },33 _options: { headless: false }34 },35 _options: { ignoreHTTPSErrors: true, viewport: { width: 1920, height: 1080 } },
Using AI Code Generation
1const { Playwright } = require('@playwright/test');2const options = {3 viewport: { width: 1280, height: 720 },4 recordVideo: { dir: 'videos' },5 recordTrace: { dir: 'traces' },6 recordHar: { omitContent: true, path: 'trace.har' },7 proxy: {8 },9};10const overrides = Playwright._overridesFromOptions(options);11console.log(overrides);12"scripts": {13 }14{15 viewport: { width: 1280, height: 720 },16 recordVideo: { dir: 'videos' },17 recordTrace: { dir: 'traces' },18 recordHar: { omitContent: true, path: 'trace.har' },19 proxy: {20 },21}22const { Playwright } = require('@playwright/test');23const options = {24 viewport: { width: 1280, height: 720 },25 recordVideo: { dir: 'videos' },26 recordTrace: { dir: 'traces' },27 recordHar: { omitContent: true, path: 'trace.har' },28 proxy: {29 },
Using AI Code Generation
1const { chromium } = require('playwright');2const { Internal } = require('playwright/lib/server/browserType');3const browser = await chromium.launch();4const context = await browser.newContext();5const internal = new Internal(browser);6const overrides = internal.overridesFromOptions(context._options);7const { chromium } = require('playwright');8const { Internal } = require('playwright/lib/server/browserType');9const browser = await chromium.launch();10const context = await browser.newContext();11const internal = new Internal(browser);12const overrides = internal.overridesFromOptions(context._options);13const { chromium } = require('playwright');14const { Internal } = require('playwright/lib/server/browserType');15const browser = await chromium.launch();16const context = await browser.newContext();17const internal = new Internal(browser);18const overrides = internal.overridesFromOptions(context._options);
Using AI Code Generation
1const { Playwright } = require("playwright");2const playwright = new Playwright({3 {4 },5});6const browser = playwright.chromium.launch({7});8const page = await browser.newPage();9await page.screenshot({ path: "google.png" });10await browser.close();11const { Playwright } = require("playwright");12const playwright = new Playwright({13 {14 },15});16const browser = playwright.chromium.launch({17});18const page = await browser.newPage();19await page.screenshot({ path: "google.png" });20await browser.close();21const { Playwright } = require("playwright");22const playwright = new Playwright({23 {24 },25});26const browser = playwright.chromium.launch({27});28const page = await browser.newPage();29await page.screenshot({ path: "google.png" });30await browser.close();31const { Playwright } = require("playwright");32const playwright = new Playwright({33 {
LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!