@emesoft_company/playwright-core 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -0
- package/dist/api/index.cjs +248 -0
- package/dist/api/index.cjs.map +1 -0
- package/dist/api/index.d.cts +12 -0
- package/dist/api/index.d.ts +12 -0
- package/dist/api/index.js +245 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api-client-C_StjpPX.d.ts +58 -0
- package/dist/api-client-CpXo_NOU.d.cts +58 -0
- package/dist/auth/index.cjs +310 -0
- package/dist/auth/index.cjs.map +1 -0
- package/dist/auth/index.d.cts +98 -0
- package/dist/auth/index.d.ts +98 -0
- package/dist/auth/index.js +304 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/config/index.cjs +268 -0
- package/dist/config/index.cjs.map +1 -0
- package/dist/config/index.d.cts +105 -0
- package/dist/config/index.d.ts +105 -0
- package/dist/config/index.js +254 -0
- package/dist/config/index.js.map +1 -0
- package/dist/data/index.cjs +59 -0
- package/dist/data/index.cjs.map +1 -0
- package/dist/data/index.d.cts +25 -0
- package/dist/data/index.d.ts +25 -0
- package/dist/data/index.js +50 -0
- package/dist/data/index.js.map +1 -0
- package/dist/di/index.cjs +47 -0
- package/dist/di/index.cjs.map +1 -0
- package/dist/di/index.d.cts +19 -0
- package/dist/di/index.d.ts +19 -0
- package/dist/di/index.js +45 -0
- package/dist/di/index.js.map +1 -0
- package/dist/errors/index.cjs +90 -0
- package/dist/errors/index.cjs.map +1 -0
- package/dist/errors/index.d.cts +38 -0
- package/dist/errors/index.d.ts +38 -0
- package/dist/errors/index.js +83 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/expect/index.cjs +36 -0
- package/dist/expect/index.cjs.map +1 -0
- package/dist/expect/index.d.cts +30 -0
- package/dist/expect/index.d.ts +30 -0
- package/dist/expect/index.js +34 -0
- package/dist/expect/index.js.map +1 -0
- package/dist/fixtures/index.cjs +660 -0
- package/dist/fixtures/index.cjs.map +1 -0
- package/dist/fixtures/index.d.cts +68 -0
- package/dist/fixtures/index.d.ts +68 -0
- package/dist/fixtures/index.js +651 -0
- package/dist/fixtures/index.js.map +1 -0
- package/dist/index.cjs +1322 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +1248 -0
- package/dist/index.js.map +1 -0
- package/dist/logging/index.cjs +32 -0
- package/dist/logging/index.cjs.map +1 -0
- package/dist/logging/index.d.cts +18 -0
- package/dist/logging/index.d.ts +18 -0
- package/dist/logging/index.js +25 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/reporting/index.cjs +59 -0
- package/dist/reporting/index.cjs.map +1 -0
- package/dist/reporting/index.d.cts +63 -0
- package/dist/reporting/index.d.ts +63 -0
- package/dist/reporting/index.js +52 -0
- package/dist/reporting/index.js.map +1 -0
- package/dist/retry-XqnSC8YL.d.cts +27 -0
- package/dist/retry-XqnSC8YL.d.ts +27 -0
- package/dist/schema-y5w2hbT-.d.cts +292 -0
- package/dist/schema-y5w2hbT-.d.ts +292 -0
- package/dist/types-CbWnSYEK.d.cts +5 -0
- package/dist/types-CbWnSYEK.d.ts +5 -0
- package/dist/types-CopwwpSW.d.cts +8 -0
- package/dist/types-CopwwpSW.d.ts +8 -0
- package/dist/types-hInbkPXf.d.cts +59 -0
- package/dist/types-hInbkPXf.d.ts +59 -0
- package/dist/ui/index.cjs +366 -0
- package/dist/ui/index.cjs.map +1 -0
- package/dist/ui/index.d.cts +190 -0
- package/dist/ui/index.d.ts +190 -0
- package/dist/ui/index.js +348 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/utils/index.cjs +72 -0
- package/dist/utils/index.cjs.map +1 -0
- package/dist/utils/index.d.cts +27 -0
- package/dist/utils/index.d.ts +27 -0
- package/dist/utils/index.js +66 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +133 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var zod = require('zod');
|
|
4
|
+
var fs = require('fs');
|
|
5
|
+
var dotenv = require('dotenv');
|
|
6
|
+
var test = require('@playwright/test');
|
|
7
|
+
|
|
8
|
+
// src/errors/app-error.ts
|
|
9
|
+
var AppError = class extends Error {
|
|
10
|
+
code;
|
|
11
|
+
context;
|
|
12
|
+
constructor(message, options) {
|
|
13
|
+
super(message, { cause: options.cause });
|
|
14
|
+
this.name = new.target.name;
|
|
15
|
+
this.code = options.code;
|
|
16
|
+
this.context = options.context;
|
|
17
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var ConfigValidationError = class extends AppError {
|
|
21
|
+
constructor(message, issues, context) {
|
|
22
|
+
super(message, { code: "CONFIG_VALIDATION_ERROR", context: { issues, ...context } });
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var ConfigMissingError = class extends AppError {
|
|
26
|
+
constructor(name) {
|
|
27
|
+
super(`Required environment variable "${name}" is not set`, {
|
|
28
|
+
code: "ENV_VAR_MISSING",
|
|
29
|
+
context: { name }
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// src/utils/deep-merge.ts
|
|
35
|
+
function isPlainObject(value) {
|
|
36
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
37
|
+
}
|
|
38
|
+
function deepMerge(base, override) {
|
|
39
|
+
if (override === void 0) return base;
|
|
40
|
+
if (!isPlainObject(base) || !isPlainObject(override)) {
|
|
41
|
+
return override;
|
|
42
|
+
}
|
|
43
|
+
const result = { ...base };
|
|
44
|
+
for (const [key, value] of Object.entries(override)) {
|
|
45
|
+
if (value === void 0) continue;
|
|
46
|
+
const baseValue = base[key];
|
|
47
|
+
result[key] = isPlainObject(baseValue) && isPlainObject(value) ? deepMerge(baseValue, value) : value;
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
function pruneUndefined(value) {
|
|
52
|
+
if (Array.isArray(value)) return value.map(pruneUndefined);
|
|
53
|
+
if (isPlainObject(value)) {
|
|
54
|
+
const result = {};
|
|
55
|
+
for (const [key, val] of Object.entries(value)) {
|
|
56
|
+
if (val === void 0) continue;
|
|
57
|
+
const pruned = pruneUndefined(val);
|
|
58
|
+
if (isPlainObject(pruned) && Object.keys(pruned).length === 0) continue;
|
|
59
|
+
result[key] = pruned;
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
var KNOWN_ENVIRONMENTS = ["local", "dev", "qa", "uat", "staging", "prod"];
|
|
66
|
+
var EnvironmentSchema = zod.z.string().min(1);
|
|
67
|
+
var TimeoutsSchema = zod.z.object({
|
|
68
|
+
actionMs: zod.z.number().int().positive().default(15e3),
|
|
69
|
+
navigationMs: zod.z.number().int().positive().default(3e4),
|
|
70
|
+
testMs: zod.z.number().int().positive().default(6e4),
|
|
71
|
+
/** Default budget for `waitForCondition` and other non-locator polling. */
|
|
72
|
+
waitMs: zod.z.number().int().positive().default(3e4)
|
|
73
|
+
});
|
|
74
|
+
var AuthConfigSchema = zod.z.object({
|
|
75
|
+
tokenUrl: zod.z.string().url().optional(),
|
|
76
|
+
clientId: zod.z.string().optional(),
|
|
77
|
+
clientSecret: zod.z.string().optional(),
|
|
78
|
+
username: zod.z.string().optional(),
|
|
79
|
+
password: zod.z.string().optional(),
|
|
80
|
+
/** Single-session storage state file (see `buildGlobalSetup`). */
|
|
81
|
+
storageStatePath: zod.z.string().optional(),
|
|
82
|
+
/** Directory used for per-role storage state files in multi-role setups. */
|
|
83
|
+
storageStateDir: zod.z.string().default(".auth")
|
|
84
|
+
}).default({});
|
|
85
|
+
var ReportingConfigSchema = zod.z.object({
|
|
86
|
+
allureEnabled: zod.z.boolean().default(false),
|
|
87
|
+
resultsDir: zod.z.string().default("allure-results"),
|
|
88
|
+
/** `{id}` is replaced with a test-case id — see `meta()` in `@emesoft_company/playwright-core/reporting`. */
|
|
89
|
+
testCaseUrlTemplate: zod.z.string().optional()
|
|
90
|
+
}).default({});
|
|
91
|
+
var RetryConfigSchema = zod.z.object({
|
|
92
|
+
retries: zod.z.number().int().min(0).default(2),
|
|
93
|
+
minDelayMs: zod.z.number().int().positive().default(200),
|
|
94
|
+
maxDelayMs: zod.z.number().int().positive().default(5e3)
|
|
95
|
+
}).default({});
|
|
96
|
+
var DiagnosticsConfigSchema = zod.z.object({
|
|
97
|
+
/** Collect browser console errors + failed requests and attach them to the report. */
|
|
98
|
+
enabled: zod.z.boolean().default(true),
|
|
99
|
+
/** Fail the test if the page logged a console error. Off by default — many apps are noisy. */
|
|
100
|
+
failOnConsoleError: zod.z.boolean().default(false),
|
|
101
|
+
/** Fail the test on an uncaught page exception. */
|
|
102
|
+
failOnPageError: zod.z.boolean().default(false),
|
|
103
|
+
/** Console messages matching any of these substrings are ignored entirely. */
|
|
104
|
+
ignoreConsolePatterns: zod.z.array(zod.z.string()).default([])
|
|
105
|
+
}).default({});
|
|
106
|
+
var AppConfigSchema = zod.z.object({
|
|
107
|
+
environment: EnvironmentSchema,
|
|
108
|
+
baseUrl: zod.z.string().url(),
|
|
109
|
+
apiBaseUrl: zod.z.string().url().optional(),
|
|
110
|
+
tenant: zod.z.string().optional(),
|
|
111
|
+
timeouts: TimeoutsSchema.default({}),
|
|
112
|
+
auth: AuthConfigSchema,
|
|
113
|
+
reporting: ReportingConfigSchema,
|
|
114
|
+
retry: RetryConfigSchema,
|
|
115
|
+
diagnostics: DiagnosticsConfigSchema,
|
|
116
|
+
logLevel: zod.z.enum(["fatal", "error", "warn", "info", "debug", "trace", "silent"]).default("info"),
|
|
117
|
+
custom: zod.z.record(zod.z.string(), zod.z.unknown()).default({})
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// src/config/loader.ts
|
|
121
|
+
function readEnvConfig(env) {
|
|
122
|
+
return {
|
|
123
|
+
environment: env.TEST_ENV ?? "dev",
|
|
124
|
+
baseUrl: env.BASE_URL,
|
|
125
|
+
apiBaseUrl: env.API_BASE_URL,
|
|
126
|
+
tenant: env.TENANT,
|
|
127
|
+
logLevel: env.LOG_LEVEL,
|
|
128
|
+
timeouts: {
|
|
129
|
+
actionMs: env.ACTION_TIMEOUT_MS ? Number(env.ACTION_TIMEOUT_MS) : void 0,
|
|
130
|
+
navigationMs: env.NAVIGATION_TIMEOUT_MS ? Number(env.NAVIGATION_TIMEOUT_MS) : void 0,
|
|
131
|
+
testMs: env.TEST_TIMEOUT_MS ? Number(env.TEST_TIMEOUT_MS) : void 0,
|
|
132
|
+
waitMs: env.WAIT_TIMEOUT_MS ? Number(env.WAIT_TIMEOUT_MS) : void 0
|
|
133
|
+
},
|
|
134
|
+
auth: {
|
|
135
|
+
tokenUrl: env.AUTH_TOKEN_URL,
|
|
136
|
+
clientId: env.AUTH_CLIENT_ID,
|
|
137
|
+
clientSecret: env.AUTH_CLIENT_SECRET,
|
|
138
|
+
username: env.AUTH_USERNAME,
|
|
139
|
+
password: env.AUTH_PASSWORD,
|
|
140
|
+
storageStatePath: env.AUTH_STORAGE_STATE_PATH,
|
|
141
|
+
storageStateDir: env.AUTH_STORAGE_STATE_DIR
|
|
142
|
+
},
|
|
143
|
+
reporting: {
|
|
144
|
+
allureEnabled: env.REPORT_ALLURE === "true",
|
|
145
|
+
resultsDir: env.ALLURE_RESULTS_DIR,
|
|
146
|
+
testCaseUrlTemplate: env.TEST_CASE_URL_TEMPLATE
|
|
147
|
+
},
|
|
148
|
+
retry: {
|
|
149
|
+
retries: env.RETRY_COUNT ? Number(env.RETRY_COUNT) : void 0,
|
|
150
|
+
minDelayMs: env.RETRY_MIN_DELAY_MS ? Number(env.RETRY_MIN_DELAY_MS) : void 0,
|
|
151
|
+
maxDelayMs: env.RETRY_MAX_DELAY_MS ? Number(env.RETRY_MAX_DELAY_MS) : void 0
|
|
152
|
+
},
|
|
153
|
+
diagnostics: {
|
|
154
|
+
enabled: env.DIAGNOSTICS === void 0 ? void 0 : env.DIAGNOSTICS !== "false",
|
|
155
|
+
failOnConsoleError: env.FAIL_ON_CONSOLE_ERROR === void 0 ? void 0 : env.FAIL_ON_CONSOLE_ERROR === "true",
|
|
156
|
+
failOnPageError: env.FAIL_ON_PAGE_ERROR === void 0 ? void 0 : env.FAIL_ON_PAGE_ERROR === "true"
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function requireEnv(name, env = process.env) {
|
|
161
|
+
const value = env[name];
|
|
162
|
+
if (value === void 0 || value === "") {
|
|
163
|
+
throw new ConfigMissingError(name);
|
|
164
|
+
}
|
|
165
|
+
return value;
|
|
166
|
+
}
|
|
167
|
+
function loadConfig(options = {}) {
|
|
168
|
+
const { env = process.env, overrides } = options;
|
|
169
|
+
const fromEnv = pruneUndefined(readEnvConfig(env));
|
|
170
|
+
const merged = deepMerge(fromEnv, overrides);
|
|
171
|
+
const result = AppConfigSchema.safeParse(merged);
|
|
172
|
+
if (!result.success) {
|
|
173
|
+
throw new ConfigValidationError(
|
|
174
|
+
`Invalid application config: ${result.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ")}`,
|
|
175
|
+
result.error.issues
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
return result.data;
|
|
179
|
+
}
|
|
180
|
+
function loadEnvFiles(files, env = process.env) {
|
|
181
|
+
const merged = {};
|
|
182
|
+
const loaded = [];
|
|
183
|
+
for (const file of files) {
|
|
184
|
+
if (!fs.existsSync(file)) continue;
|
|
185
|
+
Object.assign(merged, dotenv.parse(fs.readFileSync(file)));
|
|
186
|
+
loaded.push(file);
|
|
187
|
+
}
|
|
188
|
+
const applied = [];
|
|
189
|
+
for (const [key, value] of Object.entries(merged)) {
|
|
190
|
+
if (env[key] === void 0) {
|
|
191
|
+
env[key] = value;
|
|
192
|
+
applied.push(key);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return { loaded, applied };
|
|
196
|
+
}
|
|
197
|
+
function defaultEnvFiles(environment) {
|
|
198
|
+
return [".env", ".env.local", `.env.${environment}`, `.env.${environment}.local`];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// src/reporting/allure-setup.ts
|
|
202
|
+
function buildReporterConfig(config) {
|
|
203
|
+
const reporters = [["html", { open: "never" }]];
|
|
204
|
+
if (config.reporting.allureEnabled) {
|
|
205
|
+
reporters.push(["allure-playwright", { resultsDir: config.reporting.resultsDir, detail: true }]);
|
|
206
|
+
}
|
|
207
|
+
return reporters;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/config/define-config.ts
|
|
211
|
+
function resolveEnvFiles(options, environment) {
|
|
212
|
+
if (options.envFile === false) return [];
|
|
213
|
+
if (typeof options.envFile === "string") return [options.envFile];
|
|
214
|
+
if (Array.isArray(options.envFile)) return options.envFile;
|
|
215
|
+
return defaultEnvFiles(environment);
|
|
216
|
+
}
|
|
217
|
+
function defineE2EConfig(options = {}) {
|
|
218
|
+
const environment = process.env.TEST_ENV ?? options.defaultEnvironment ?? "dev";
|
|
219
|
+
loadEnvFiles(resolveEnvFiles(options, environment));
|
|
220
|
+
const overrides = typeof options.configOverrides === "function" ? options.configOverrides() : options.configOverrides;
|
|
221
|
+
const appConfig = loadConfig({ overrides: deepMerge({ environment }, overrides) });
|
|
222
|
+
const browsers = options.browsers ?? ["chromium"];
|
|
223
|
+
const deviceByBrowser = {
|
|
224
|
+
chromium: "Desktop Chrome",
|
|
225
|
+
firefox: "Desktop Firefox",
|
|
226
|
+
webkit: "Desktop Safari"
|
|
227
|
+
};
|
|
228
|
+
const generated = {
|
|
229
|
+
testDir: options.testDir ?? "./tests",
|
|
230
|
+
fullyParallel: true,
|
|
231
|
+
retries: appConfig.retry.retries,
|
|
232
|
+
timeout: appConfig.timeouts.testMs,
|
|
233
|
+
reporter: buildReporterConfig(appConfig),
|
|
234
|
+
// `globalSetup` is only wired when a path is given AND a storage state path is configured —
|
|
235
|
+
// otherwise a project that hasn't set up login yet would fail on every run.
|
|
236
|
+
...options.globalSetup && appConfig.auth.storageStatePath ? { globalSetup: options.globalSetup } : {},
|
|
237
|
+
...options.globalTeardown ? { globalTeardown: options.globalTeardown } : {},
|
|
238
|
+
use: {
|
|
239
|
+
baseURL: appConfig.baseUrl,
|
|
240
|
+
actionTimeout: appConfig.timeouts.actionMs,
|
|
241
|
+
navigationTimeout: appConfig.timeouts.navigationMs,
|
|
242
|
+
trace: "on-first-retry",
|
|
243
|
+
screenshot: "only-on-failure",
|
|
244
|
+
...options.globalSetup && appConfig.auth.storageStatePath ? { storageState: appConfig.auth.storageStatePath } : {}
|
|
245
|
+
},
|
|
246
|
+
projects: browsers.map((browser) => ({
|
|
247
|
+
name: browser,
|
|
248
|
+
use: { ...test.devices[deviceByBrowser[browser]] }
|
|
249
|
+
}))
|
|
250
|
+
};
|
|
251
|
+
return deepMerge(generated, options.playwright);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
exports.AppConfigSchema = AppConfigSchema;
|
|
255
|
+
exports.AuthConfigSchema = AuthConfigSchema;
|
|
256
|
+
exports.DiagnosticsConfigSchema = DiagnosticsConfigSchema;
|
|
257
|
+
exports.EnvironmentSchema = EnvironmentSchema;
|
|
258
|
+
exports.KNOWN_ENVIRONMENTS = KNOWN_ENVIRONMENTS;
|
|
259
|
+
exports.ReportingConfigSchema = ReportingConfigSchema;
|
|
260
|
+
exports.RetryConfigSchema = RetryConfigSchema;
|
|
261
|
+
exports.TimeoutsSchema = TimeoutsSchema;
|
|
262
|
+
exports.defaultEnvFiles = defaultEnvFiles;
|
|
263
|
+
exports.defineE2EConfig = defineE2EConfig;
|
|
264
|
+
exports.loadConfig = loadConfig;
|
|
265
|
+
exports.loadEnvFiles = loadEnvFiles;
|
|
266
|
+
exports.requireEnv = requireEnv;
|
|
267
|
+
//# sourceMappingURL=index.cjs.map
|
|
268
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/errors/app-error.ts","../../src/utils/deep-merge.ts","../../src/config/schema.ts","../../src/config/loader.ts","../../src/config/env.ts","../../src/reporting/allure-setup.ts","../../src/config/define-config.ts"],"names":["z","existsSync","parse","readFileSync","devices"],"mappings":";;;;;;;;AAWO,IAAM,QAAA,GAAN,cAAuB,KAAA,CAAM;AAAA,EACzB,IAAA;AAAA,EACA,OAAA;AAAA,EAET,WAAA,CAAY,SAAiB,OAAA,EAA0B;AACrD,IAAA,KAAA,CAAM,OAAA,EAAS,EAAE,KAAA,EAAO,OAAA,CAAQ,OAAO,CAAA;AACvC,IAAA,IAAA,CAAK,OAAO,GAAA,CAAA,MAAA,CAAW,IAAA;AACvB,IAAA,IAAA,CAAK,OAAO,OAAA,CAAQ,IAAA;AACpB,IAAA,IAAA,CAAK,UAAU,OAAA,CAAQ,OAAA;AACvB,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAW,SAAS,CAAA;AAAA,EAClD;AACF,CAAA;AAEO,IAAM,qBAAA,GAAN,cAAoC,QAAA,CAAS;AAAA,EAClD,WAAA,CAAY,OAAA,EAAiB,MAAA,EAAiB,OAAA,EAAmC;AAC/E,IAAA,KAAA,CAAM,OAAA,EAAS,EAAE,IAAA,EAAM,yBAAA,EAA2B,OAAA,EAAS,EAAE,MAAA,EAAQ,GAAG,OAAA,EAAQ,EAAG,CAAA;AAAA,EACrF;AACF,CAAA;AAwBO,IAAM,kBAAA,GAAN,cAAiC,QAAA,CAAS;AAAA,EAC/C,YAAY,IAAA,EAAc;AACxB,IAAA,KAAA,CAAM,CAAA,+BAAA,EAAkC,IAAI,CAAA,YAAA,CAAA,EAAgB;AAAA,MAC1D,IAAA,EAAM,iBAAA;AAAA,MACN,OAAA,EAAS,EAAE,IAAA;AAAK,KACjB,CAAA;AAAA,EACH;AACF,CAAA;;;AC3DO,SAAS,cAAc,KAAA,EAAkD;AAC9E,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,CAAC,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5E;AAOO,SAAS,SAAA,CAAa,MAAS,QAAA,EAAsB;AAC1D,EAAA,IAAI,QAAA,KAAa,QAAW,OAAO,IAAA;AACnC,EAAA,IAAI,CAAC,aAAA,CAAc,IAAI,KAAK,CAAC,aAAA,CAAc,QAAQ,CAAA,EAAG;AACpD,IAAA,OAAO,QAAA;AAAA,EACT;AACA,EAAA,MAAM,MAAA,GAAkC,EAAE,GAAG,IAAA,EAAK;AAClD,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,QAAQ,CAAA,EAAG;AACnD,IAAA,IAAI,UAAU,MAAA,EAAW;AACzB,IAAA,MAAM,SAAA,GAAa,KAAiC,GAAG,CAAA;AACvD,IAAA,MAAA,CAAO,GAAG,CAAA,GAAI,aAAA,CAAc,SAAS,CAAA,IAAK,aAAA,CAAc,KAAK,CAAA,GAAI,SAAA,CAAU,SAAA,EAAW,KAAK,CAAA,GAAI,KAAA;AAAA,EACjG;AACA,EAAA,OAAO,MAAA;AACT;AAGO,SAAS,eAAe,KAAA,EAAyB;AACtD,EAAA,IAAI,MAAM,OAAA,CAAQ,KAAK,GAAG,OAAO,KAAA,CAAM,IAAI,cAAc,CAAA;AACzD,EAAA,IAAI,aAAA,CAAc,KAAK,CAAA,EAAG;AACxB,IAAA,MAAM,SAAkC,EAAC;AACzC,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,GAAG,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAC9C,MAAA,IAAI,QAAQ,MAAA,EAAW;AACvB,MAAA,MAAM,MAAA,GAAS,eAAe,GAAG,CAAA;AACjC,MAAA,IAAI,aAAA,CAAc,MAAM,CAAA,IAAK,MAAA,CAAO,KAAK,MAAM,CAAA,CAAE,WAAW,CAAA,EAAG;AAC/D,MAAA,MAAA,CAAO,GAAG,CAAA,GAAI,MAAA;AAAA,IAChB;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAA;AACT;AClCO,IAAM,qBAAqB,CAAC,OAAA,EAAS,OAAO,IAAA,EAAM,KAAA,EAAO,WAAW,MAAM;AAS1E,IAAM,iBAAA,GAAoBA,KAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC;AAE1C,IAAM,cAAA,GAAiBA,MAAE,MAAA,CAAO;AAAA,EACrC,QAAA,EAAUA,MAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,IAAM,CAAA;AAAA,EACpD,YAAA,EAAcA,MAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,GAAM,CAAA;AAAA,EACxD,MAAA,EAAQA,MAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,GAAM,CAAA;AAAA;AAAA,EAElD,MAAA,EAAQA,MAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,GAAM;AACpD,CAAC;AAEM,IAAM,gBAAA,GAAmBA,MAC7B,MAAA,CAAO;AAAA,EACN,UAAUA,KAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,QAAA,EAAS;AAAA,EACpC,QAAA,EAAUA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC9B,YAAA,EAAcA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAClC,QAAA,EAAUA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC9B,QAAA,EAAUA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA;AAAA,EAE9B,gBAAA,EAAkBA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA;AAAA,EAEtC,eAAA,EAAiBA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,OAAO;AAC7C,CAAC,CAAA,CACA,OAAA,CAAQ,EAAE;AAEN,IAAM,qBAAA,GAAwBA,MAClC,MAAA,CAAO;AAAA,EACN,aAAA,EAAeA,KAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,KAAK,CAAA;AAAA,EACxC,UAAA,EAAYA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,gBAAgB,CAAA;AAAA;AAAA,EAE/C,mBAAA,EAAqBA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AAClC,CAAC,CAAA,CACA,OAAA,CAAQ,EAAE;AAEN,IAAM,iBAAA,GAAoBA,MAC9B,MAAA,CAAO;AAAA,EACN,OAAA,EAASA,KAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,GAAA,CAAI,CAAC,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAA;AAAA,EAC1C,UAAA,EAAYA,MAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,GAAG,CAAA;AAAA,EACnD,UAAA,EAAYA,MAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,GAAK;AACvD,CAAC,CAAA,CACA,OAAA,CAAQ,EAAE;AAEN,IAAM,uBAAA,GAA0BA,MACpC,MAAA,CAAO;AAAA;AAAA,EAEN,OAAA,EAASA,KAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,IAAI,CAAA;AAAA;AAAA,EAEjC,kBAAA,EAAoBA,KAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,KAAK,CAAA;AAAA;AAAA,EAE7C,eAAA,EAAiBA,KAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,KAAK,CAAA;AAAA;AAAA,EAE1C,qBAAA,EAAuBA,MAAE,KAAA,CAAMA,KAAA,CAAE,QAAQ,CAAA,CAAE,OAAA,CAAQ,EAAE;AACvD,CAAC,CAAA,CACA,OAAA,CAAQ,EAAE;AAON,IAAM,eAAA,GAAkBA,MAAE,MAAA,CAAO;AAAA,EACtC,WAAA,EAAa,iBAAA;AAAA,EACb,OAAA,EAASA,KAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI;AAAA,EACxB,YAAYA,KAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,QAAA,EAAS;AAAA,EACtC,MAAA,EAAQA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC5B,QAAA,EAAU,cAAA,CAAe,OAAA,CAAQ,EAAE,CAAA;AAAA,EACnC,IAAA,EAAM,gBAAA;AAAA,EACN,SAAA,EAAW,qBAAA;AAAA,EACX,KAAA,EAAO,iBAAA;AAAA,EACP,WAAA,EAAa,uBAAA;AAAA,EACb,QAAA,EAAUA,KAAA,CAAE,IAAA,CAAK,CAAC,SAAS,OAAA,EAAS,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,OAAA,EAAS,QAAQ,CAAC,CAAA,CAAE,QAAQ,MAAM,CAAA;AAAA,EAC/F,MAAA,EAAQA,KAAA,CAAE,MAAA,CAAOA,KAAA,CAAE,MAAA,EAAO,EAAGA,KAAA,CAAE,OAAA,EAAS,CAAA,CAAE,OAAA,CAAQ,EAAE;AACtD,CAAC;;;AC9ED,SAAS,cAAc,GAAA,EAAiD;AACtE,EAAA,OAAO;AAAA,IACL,WAAA,EAAa,IAAI,QAAA,IAAY,KAAA;AAAA,IAC7B,SAAS,GAAA,CAAI,QAAA;AAAA,IACb,YAAY,GAAA,CAAI,YAAA;AAAA,IAChB,QAAQ,GAAA,CAAI,MAAA;AAAA,IACZ,UAAU,GAAA,CAAI,SAAA;AAAA,IACd,QAAA,EAAU;AAAA,MACR,UAAU,GAAA,CAAI,iBAAA,GAAoB,MAAA,CAAO,GAAA,CAAI,iBAAiB,CAAA,GAAI,MAAA;AAAA,MAClE,cAAc,GAAA,CAAI,qBAAA,GAAwB,MAAA,CAAO,GAAA,CAAI,qBAAqB,CAAA,GAAI,MAAA;AAAA,MAC9E,QAAQ,GAAA,CAAI,eAAA,GAAkB,MAAA,CAAO,GAAA,CAAI,eAAe,CAAA,GAAI,MAAA;AAAA,MAC5D,QAAQ,GAAA,CAAI,eAAA,GAAkB,MAAA,CAAO,GAAA,CAAI,eAAe,CAAA,GAAI;AAAA,KAC9D;AAAA,IACA,IAAA,EAAM;AAAA,MACJ,UAAU,GAAA,CAAI,cAAA;AAAA,MACd,UAAU,GAAA,CAAI,cAAA;AAAA,MACd,cAAc,GAAA,CAAI,kBAAA;AAAA,MAClB,UAAU,GAAA,CAAI,aAAA;AAAA,MACd,UAAU,GAAA,CAAI,aAAA;AAAA,MACd,kBAAkB,GAAA,CAAI,uBAAA;AAAA,MACtB,iBAAiB,GAAA,CAAI;AAAA,KACvB;AAAA,IACA,SAAA,EAAW;AAAA,MACT,aAAA,EAAe,IAAI,aAAA,KAAkB,MAAA;AAAA,MACrC,YAAY,GAAA,CAAI,kBAAA;AAAA,MAChB,qBAAqB,GAAA,CAAI;AAAA,KAC3B;AAAA,IACA,KAAA,EAAO;AAAA,MACL,SAAS,GAAA,CAAI,WAAA,GAAc,MAAA,CAAO,GAAA,CAAI,WAAW,CAAA,GAAI,MAAA;AAAA,MACrD,YAAY,GAAA,CAAI,kBAAA,GAAqB,MAAA,CAAO,GAAA,CAAI,kBAAkB,CAAA,GAAI,MAAA;AAAA,MACtE,YAAY,GAAA,CAAI,kBAAA,GAAqB,MAAA,CAAO,GAAA,CAAI,kBAAkB,CAAA,GAAI;AAAA,KACxE;AAAA,IACA,WAAA,EAAa;AAAA,MACX,SAAS,GAAA,CAAI,WAAA,KAAgB,MAAA,GAAY,MAAA,GAAY,IAAI,WAAA,KAAgB,OAAA;AAAA,MACzE,oBAAoB,GAAA,CAAI,qBAAA,KAA0B,MAAA,GAAY,MAAA,GAAY,IAAI,qBAAA,KAA0B,MAAA;AAAA,MACxG,iBAAiB,GAAA,CAAI,kBAAA,KAAuB,MAAA,GAAY,MAAA,GAAY,IAAI,kBAAA,KAAuB;AAAA;AACjG,GACF;AACF;AAMO,SAAS,UAAA,CAAW,IAAA,EAAc,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAAa;AACrF,EAAA,MAAM,KAAA,GAAQ,IAAI,IAAI,CAAA;AACtB,EAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,EAAA,EAAI;AACvC,IAAA,MAAM,IAAI,mBAAmB,IAAI,CAAA;AAAA,EACnC;AACA,EAAA,OAAO,KAAA;AACT;AAeO,SAAS,UAAA,CAAW,OAAA,GAA6B,EAAC,EAAc;AACrE,EAAA,MAAM,EAAE,GAAA,GAAM,OAAA,CAAQ,GAAA,EAAK,WAAU,GAAI,OAAA;AAEzC,EAAA,MAAM,OAAA,GAAU,cAAA,CAAe,aAAA,CAAc,GAAG,CAAC,CAAA;AACjD,EAAA,MAAM,MAAA,GAAS,SAAA,CAAU,OAAA,EAAsB,SAAS,CAAA;AAExD,EAAA,MAAM,MAAA,GAAS,eAAA,CAAgB,SAAA,CAAU,MAAM,CAAA;AAC/C,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,IAAA,MAAM,IAAI,qBAAA;AAAA,MACR,CAAA,4BAAA,EAA+B,OAAO,KAAA,CAAM,MAAA,CACzC,IAAI,CAAC,KAAA,KAAU,GAAG,KAAA,CAAM,IAAA,CAAK,KAAK,GAAG,CAAA,IAAK,QAAQ,CAAA,EAAA,EAAK,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA,CACtE,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,MACb,OAAO,KAAA,CAAM;AAAA,KACf;AAAA,EACF;AACA,EAAA,OAAO,MAAA,CAAO,IAAA;AAChB;AC7DO,SAAS,YAAA,CAAa,KAAA,EAAiB,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAAyB;AACtG,EAAA,MAAM,SAAiC,EAAC;AACxC,EAAA,MAAM,SAAmB,EAAC;AAE1B,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,IAAI,CAACC,aAAA,CAAW,IAAI,CAAA,EAAG;AACvB,IAAA,MAAA,CAAO,OAAO,MAAA,EAAQC,YAAA,CAAMC,eAAA,CAAa,IAAI,CAAC,CAAC,CAAA;AAC/C,IAAA,MAAA,CAAO,KAAK,IAAI,CAAA;AAAA,EAClB;AAEA,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AACjD,IAAA,IAAI,GAAA,CAAI,GAAG,CAAA,KAAM,MAAA,EAAW;AAC1B,MAAA,GAAA,CAAI,GAAG,CAAA,GAAI,KAAA;AACX,MAAA,OAAA,CAAQ,KAAK,GAAG,CAAA;AAAA,IAClB;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,QAAQ,OAAA,EAAQ;AAC3B;AAOO,SAAS,gBAAgB,WAAA,EAA+B;AAC7D,EAAA,OAAO,CAAC,QAAQ,YAAA,EAAc,CAAA,KAAA,EAAQ,WAAW,CAAA,CAAA,EAAI,CAAA,KAAA,EAAQ,WAAW,CAAA,MAAA,CAAQ,CAAA;AAClF;;;ACxCO,SAAS,oBAAoB,MAAA,EAAiE;AACnG,EAAA,MAAM,SAAA,GAAuC,CAAC,CAAC,MAAA,EAAQ,EAAE,IAAA,EAAM,OAAA,EAAS,CAAC,CAAA;AACzE,EAAA,IAAI,MAAA,CAAO,UAAU,aAAA,EAAe;AAClC,IAAA,SAAA,CAAU,IAAA,CAAK,CAAC,mBAAA,EAAqB,EAAE,UAAA,EAAY,MAAA,CAAO,SAAA,CAAU,UAAA,EAAY,MAAA,EAAQ,IAAA,EAAM,CAAC,CAAA;AAAA,EACjG;AACA,EAAA,OAAO,SAAA;AACT;;;ACyBA,SAAS,eAAA,CAAgB,SAAiC,WAAA,EAA+B;AACvF,EAAA,IAAI,OAAA,CAAQ,OAAA,KAAY,KAAA,EAAO,OAAO,EAAC;AACvC,EAAA,IAAI,OAAO,OAAA,CAAQ,OAAA,KAAY,UAAU,OAAO,CAAC,QAAQ,OAAO,CAAA;AAChE,EAAA,IAAI,MAAM,OAAA,CAAQ,OAAA,CAAQ,OAAO,CAAA,SAAU,OAAA,CAAQ,OAAA;AACnD,EAAA,OAAO,gBAAgB,WAAW,CAAA;AACpC;AAgBO,SAAS,eAAA,CAAgB,OAAA,GAAkC,EAAC,EAAyB;AAC1F,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,GAAA,CAAI,QAAA,IAAY,QAAQ,kBAAA,IAAsB,KAAA;AAC1E,EAAA,YAAA,CAAa,eAAA,CAAgB,OAAA,EAAS,WAAW,CAAC,CAAA;AAElD,EAAA,MAAM,SAAA,GACJ,OAAO,OAAA,CAAQ,eAAA,KAAoB,aAAa,OAAA,CAAQ,eAAA,KAAoB,OAAA,CAAQ,eAAA;AAGtF,EAAA,MAAM,SAAA,GAAY,UAAA,CAAW,EAAE,SAAA,EAAW,SAAA,CAAU,EAAE,WAAA,EAAY,EAAG,SAAS,CAAA,EAAG,CAAA;AAEjF,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,QAAA,IAAY,CAAC,UAAU,CAAA;AAChD,EAAA,MAAM,eAAA,GAA+C;AAAA,IACnD,QAAA,EAAU,gBAAA;AAAA,IACV,OAAA,EAAS,iBAAA;AAAA,IACT,MAAA,EAAQ;AAAA,GACV;AAEA,EAAA,MAAM,SAAA,GAAkC;AAAA,IACtC,OAAA,EAAS,QAAQ,OAAA,IAAW,SAAA;AAAA,IAC5B,aAAA,EAAe,IAAA;AAAA,IACf,OAAA,EAAS,UAAU,KAAA,CAAM,OAAA;AAAA,IACzB,OAAA,EAAS,UAAU,QAAA,CAAS,MAAA;AAAA,IAC5B,QAAA,EAAU,oBAAoB,SAAS,CAAA;AAAA;AAAA;AAAA,IAGvC,GAAI,OAAA,CAAQ,WAAA,IAAe,SAAA,CAAU,IAAA,CAAK,gBAAA,GACtC,EAAE,WAAA,EAAa,OAAA,CAAQ,WAAA,EAAY,GACnC,EAAC;AAAA,IACL,GAAI,QAAQ,cAAA,GAAiB,EAAE,gBAAgB,OAAA,CAAQ,cAAA,KAAmB,EAAC;AAAA,IAC3E,GAAA,EAAK;AAAA,MACH,SAAS,SAAA,CAAU,OAAA;AAAA,MACnB,aAAA,EAAe,UAAU,QAAA,CAAS,QAAA;AAAA,MAClC,iBAAA,EAAmB,UAAU,QAAA,CAAS,YAAA;AAAA,MACtC,KAAA,EAAO,gBAAA;AAAA,MACP,UAAA,EAAY,iBAAA;AAAA,MACZ,GAAI,OAAA,CAAQ,WAAA,IAAe,SAAA,CAAU,IAAA,CAAK,gBAAA,GACtC,EAAE,YAAA,EAAc,SAAA,CAAU,IAAA,CAAK,gBAAA,EAAiB,GAChD;AAAC,KACP;AAAA,IACA,QAAA,EAAU,QAAA,CAAS,GAAA,CAAI,CAAC,OAAA,MAAa;AAAA,MACnC,IAAA,EAAM,OAAA;AAAA,MACN,KAAK,EAAE,GAAGC,aAAQ,eAAA,CAAgB,OAAO,CAAC,CAAA;AAAE,KAC9C,CAAE;AAAA,GACJ;AAEA,EAAA,OAAO,SAAA,CAAU,SAAA,EAAW,OAAA,CAAQ,UAAU,CAAA;AAChD","file":"index.cjs","sourcesContent":["export interface AppErrorOptions {\n code: string;\n cause?: unknown;\n context?: Record<string, unknown>;\n}\n\n/**\n * Base error class for the framework. Every error thrown by core carries a\n * machine-readable `code` and optional `context`, so callers can branch on\n * `error.code` instead of parsing messages.\n */\nexport class AppError extends Error {\n readonly code: string;\n readonly context?: Record<string, unknown>;\n\n constructor(message: string, options: AppErrorOptions) {\n super(message, { cause: options.cause });\n this.name = new.target.name;\n this.code = options.code;\n this.context = options.context;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class ConfigValidationError extends AppError {\n constructor(message: string, issues: unknown, context?: Record<string, unknown>) {\n super(message, { code: \"CONFIG_VALIDATION_ERROR\", context: { issues, ...context } });\n }\n}\n\nexport class ApiError extends AppError {\n readonly status?: number;\n readonly method?: string;\n readonly url?: string;\n\n constructor(\n message: string,\n options: AppErrorOptions & { status?: number; method?: string; url?: string },\n ) {\n super(message, options);\n this.status = options.status;\n this.method = options.method;\n this.url = options.url;\n }\n}\n\nexport class AuthError extends AppError {\n constructor(message: string, options: Omit<AppErrorOptions, \"code\"> = {}) {\n super(message, { code: \"AUTH_ERROR\", ...options });\n }\n}\n\nexport class ConfigMissingError extends AppError {\n constructor(name: string) {\n super(`Required environment variable \"${name}\" is not set`, {\n code: \"ENV_VAR_MISSING\",\n context: { name },\n });\n }\n}\n","export function isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Recursively merges `override` onto `base`. Arrays and primitives replace wholesale;\n * only plain objects are merged key by key. `undefined` values in `override` are ignored,\n * so a partial object never accidentally erases a configured value.\n */\nexport function deepMerge<T>(base: T, override: unknown): T {\n if (override === undefined) return base;\n if (!isPlainObject(base) || !isPlainObject(override)) {\n return override as T;\n }\n const result: Record<string, unknown> = { ...base };\n for (const [key, value] of Object.entries(override)) {\n if (value === undefined) continue;\n const baseValue = (base as Record<string, unknown>)[key];\n result[key] = isPlainObject(baseValue) && isPlainObject(value) ? deepMerge(baseValue, value) : value;\n }\n return result as T;\n}\n\n/** Strips `undefined` values and empty nested objects, so zod defaults apply instead. */\nexport function pruneUndefined(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(pruneUndefined);\n if (isPlainObject(value)) {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value)) {\n if (val === undefined) continue;\n const pruned = pruneUndefined(val);\n if (isPlainObject(pruned) && Object.keys(pruned).length === 0) continue;\n result[key] = pruned;\n }\n return result;\n }\n return value;\n}\n","import { z } from \"zod\";\n\n/** Environments seen most often across projects — autocompleted, but any string is accepted. */\nexport const KNOWN_ENVIRONMENTS = [\"local\", \"dev\", \"qa\", \"uat\", \"staging\", \"prod\"] as const;\n\n/**\n * Free-form on purpose: teams run `uat`, `qa`, `sit`, per-tenant envs and so on. The union\n * below keeps editor autocomplete for the common ones without rejecting anything else\n * (`(string & {})` is the TS idiom that preserves suggestions on an open string type).\n */\nexport type Environment = (typeof KNOWN_ENVIRONMENTS)[number] | (string & {});\n\nexport const EnvironmentSchema = z.string().min(1);\n\nexport const TimeoutsSchema = z.object({\n actionMs: z.number().int().positive().default(15_000),\n navigationMs: z.number().int().positive().default(30_000),\n testMs: z.number().int().positive().default(60_000),\n /** Default budget for `waitForCondition` and other non-locator polling. */\n waitMs: z.number().int().positive().default(30_000),\n});\n\nexport const AuthConfigSchema = z\n .object({\n tokenUrl: z.string().url().optional(),\n clientId: z.string().optional(),\n clientSecret: z.string().optional(),\n username: z.string().optional(),\n password: z.string().optional(),\n /** Single-session storage state file (see `buildGlobalSetup`). */\n storageStatePath: z.string().optional(),\n /** Directory used for per-role storage state files in multi-role setups. */\n storageStateDir: z.string().default(\".auth\"),\n })\n .default({});\n\nexport const ReportingConfigSchema = z\n .object({\n allureEnabled: z.boolean().default(false),\n resultsDir: z.string().default(\"allure-results\"),\n /** `{id}` is replaced with a test-case id — see `meta()` in `@emesoft_company/playwright-core/reporting`. */\n testCaseUrlTemplate: z.string().optional(),\n })\n .default({});\n\nexport const RetryConfigSchema = z\n .object({\n retries: z.number().int().min(0).default(2),\n minDelayMs: z.number().int().positive().default(200),\n maxDelayMs: z.number().int().positive().default(5_000),\n })\n .default({});\n\nexport const DiagnosticsConfigSchema = z\n .object({\n /** Collect browser console errors + failed requests and attach them to the report. */\n enabled: z.boolean().default(true),\n /** Fail the test if the page logged a console error. Off by default — many apps are noisy. */\n failOnConsoleError: z.boolean().default(false),\n /** Fail the test on an uncaught page exception. */\n failOnPageError: z.boolean().default(false),\n /** Console messages matching any of these substrings are ignored entirely. */\n ignoreConsolePatterns: z.array(z.string()).default([]),\n })\n .default({});\n\n/**\n * Base config schema shared by every project. Projects extend this via\n * `AppConfigSchema.extend({ ... })` or by nesting project-specific fields\n * under `custom` — core never hardcodes project business fields here.\n */\nexport const AppConfigSchema = z.object({\n environment: EnvironmentSchema,\n baseUrl: z.string().url(),\n apiBaseUrl: z.string().url().optional(),\n tenant: z.string().optional(),\n timeouts: TimeoutsSchema.default({}),\n auth: AuthConfigSchema,\n reporting: ReportingConfigSchema,\n retry: RetryConfigSchema,\n diagnostics: DiagnosticsConfigSchema,\n logLevel: z.enum([\"fatal\", \"error\", \"warn\", \"info\", \"debug\", \"trace\", \"silent\"]).default(\"info\"),\n custom: z.record(z.string(), z.unknown()).default({}),\n});\n\nexport type AppConfig = Omit<z.infer<typeof AppConfigSchema>, \"environment\"> & {\n environment: Environment;\n};\nexport type TimeoutsConfig = z.infer<typeof TimeoutsSchema>;\nexport type AuthConfig = z.infer<typeof AuthConfigSchema>;\nexport type ReportingConfig = z.infer<typeof ReportingConfigSchema>;\nexport type RetryConfig = z.infer<typeof RetryConfigSchema>;\nexport type DiagnosticsConfig = z.infer<typeof DiagnosticsConfigSchema>;\n","import { ConfigMissingError, ConfigValidationError } from \"../errors/app-error.js\";\nimport { deepMerge, pruneUndefined } from \"../utils/deep-merge.js\";\nimport { AppConfigSchema, type AppConfig } from \"./schema.js\";\nimport type { DeepPartial } from \"./types.js\";\n\nfunction readEnvConfig(env: NodeJS.ProcessEnv): Record<string, unknown> {\n return {\n environment: env.TEST_ENV ?? \"dev\",\n baseUrl: env.BASE_URL,\n apiBaseUrl: env.API_BASE_URL,\n tenant: env.TENANT,\n logLevel: env.LOG_LEVEL,\n timeouts: {\n actionMs: env.ACTION_TIMEOUT_MS ? Number(env.ACTION_TIMEOUT_MS) : undefined,\n navigationMs: env.NAVIGATION_TIMEOUT_MS ? Number(env.NAVIGATION_TIMEOUT_MS) : undefined,\n testMs: env.TEST_TIMEOUT_MS ? Number(env.TEST_TIMEOUT_MS) : undefined,\n waitMs: env.WAIT_TIMEOUT_MS ? Number(env.WAIT_TIMEOUT_MS) : undefined,\n },\n auth: {\n tokenUrl: env.AUTH_TOKEN_URL,\n clientId: env.AUTH_CLIENT_ID,\n clientSecret: env.AUTH_CLIENT_SECRET,\n username: env.AUTH_USERNAME,\n password: env.AUTH_PASSWORD,\n storageStatePath: env.AUTH_STORAGE_STATE_PATH,\n storageStateDir: env.AUTH_STORAGE_STATE_DIR,\n },\n reporting: {\n allureEnabled: env.REPORT_ALLURE === \"true\",\n resultsDir: env.ALLURE_RESULTS_DIR,\n testCaseUrlTemplate: env.TEST_CASE_URL_TEMPLATE,\n },\n retry: {\n retries: env.RETRY_COUNT ? Number(env.RETRY_COUNT) : undefined,\n minDelayMs: env.RETRY_MIN_DELAY_MS ? Number(env.RETRY_MIN_DELAY_MS) : undefined,\n maxDelayMs: env.RETRY_MAX_DELAY_MS ? Number(env.RETRY_MAX_DELAY_MS) : undefined,\n },\n diagnostics: {\n enabled: env.DIAGNOSTICS === undefined ? undefined : env.DIAGNOSTICS !== \"false\",\n failOnConsoleError: env.FAIL_ON_CONSOLE_ERROR === undefined ? undefined : env.FAIL_ON_CONSOLE_ERROR === \"true\",\n failOnPageError: env.FAIL_ON_PAGE_ERROR === undefined ? undefined : env.FAIL_ON_PAGE_ERROR === \"true\",\n },\n };\n}\n\n/**\n * Reads a required environment variable, failing immediately with a clear message instead of\n * letting an `undefined` surface as a confusing failure deep inside a test.\n */\nexport function requireEnv(name: string, env: NodeJS.ProcessEnv = process.env): string {\n const value = env[name];\n if (value === undefined || value === \"\") {\n throw new ConfigMissingError(name);\n }\n return value;\n}\n\nexport interface LoadConfigOptions {\n /** Defaults to `process.env`. */\n env?: NodeJS.ProcessEnv;\n /** Project-specific overrides (e.g. from a project's `config.override.ts`), merged on top of env-derived values. */\n overrides?: DeepPartial<AppConfig>;\n}\n\n/**\n * Loads and validates the application config for the current environment.\n * Resolution order: env-derived defaults -> project `overrides` -> zod validation.\n * Fails fast with `ConfigValidationError` when required fields (e.g. `baseUrl`) are missing/invalid,\n * instead of letting tests fail later with a confusing `undefined` somewhere downstream.\n */\nexport function loadConfig(options: LoadConfigOptions = {}): AppConfig {\n const { env = process.env, overrides } = options;\n\n const fromEnv = pruneUndefined(readEnvConfig(env)) as DeepPartial<AppConfig>;\n const merged = deepMerge(fromEnv as AppConfig, overrides);\n\n const result = AppConfigSchema.safeParse(merged);\n if (!result.success) {\n throw new ConfigValidationError(\n `Invalid application config: ${result.error.issues\n .map((issue) => `${issue.path.join(\".\") || \"(root)\"}: ${issue.message}`)\n .join(\"; \")}`,\n result.error.issues,\n );\n }\n return result.data;\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { parse } from \"dotenv\";\n\nexport interface LoadEnvFilesResult {\n /** Files that existed and were read, in the order they were applied. */\n loaded: string[];\n /** Variable names actually written to the environment. */\n applied: string[];\n}\n\n/**\n * Loads several env files with predictable precedence:\n *\n * 1. **Later files beat earlier ones** — so `.env.staging.local` overrides `.env.staging`.\n * 2. **Real environment variables beat every file** — a `BASE_URL` exported by CI or the shell\n * is never clobbered by a committed `.env`, which is what makes the same config work locally\n * and in a pipeline.\n *\n * Missing files are skipped silently: the `.local` files are gitignored and simply won't exist\n * on most machines.\n *\n * The two rules together are why this can't just be repeated `dotenv.config()` calls — dotenv\n * either never overrides (so later files lose) or always overrides (so the shell loses). Files\n * are merged first, then applied only where the environment has nothing already.\n */\nexport function loadEnvFiles(files: string[], env: NodeJS.ProcessEnv = process.env): LoadEnvFilesResult {\n const merged: Record<string, string> = {};\n const loaded: string[] = [];\n\n for (const file of files) {\n if (!existsSync(file)) continue;\n Object.assign(merged, parse(readFileSync(file)));\n loaded.push(file);\n }\n\n const applied: string[] = [];\n for (const [key, value] of Object.entries(merged)) {\n if (env[key] === undefined) {\n env[key] = value;\n applied.push(key);\n }\n }\n\n return { loaded, applied };\n}\n\n/**\n * The standard cascade for an environment, following the convention used by Vite/CRA/Next:\n * a committed base, a gitignored base override, then the same pair for the specific environment.\n * Keep secrets in the `.local` files — they are gitignored; `.env` / `.env.<environment>` are not.\n */\nexport function defaultEnvFiles(environment: string): string[] {\n return [\".env\", \".env.local\", `.env.${environment}`, `.env.${environment}.local`];\n}\n","import type { AppConfig } from \"../config/schema.js\";\n\nexport type PlaywrightReporterEntry = [string] | [string, Record<string, unknown>];\n\n/**\n * Builds the `reporter` array for a project's `playwright.config.ts`. Always includes\n * the built-in HTML reporter; adds Allure when `reporting.allureEnabled` is true.\n *\n * Allure is optional and NOT bundled transitively: because Playwright resolves reporter\n * packages relative to the project that runs the tests, a project enabling Allure must add\n * `allure-playwright` (and `allure-js-commons` if using manual annotations) to its own\n * `devDependencies`. See `docs/EXTENDING.md`.\n */\nexport function buildReporterConfig(config: Pick<AppConfig, \"reporting\">): PlaywrightReporterEntry[] {\n const reporters: PlaywrightReporterEntry[] = [[\"html\", { open: \"never\" }]];\n if (config.reporting.allureEnabled) {\n reporters.push([\"allure-playwright\", { resultsDir: config.reporting.resultsDir, detail: true }]);\n }\n return reporters;\n}\n","import { devices, type PlaywrightTestConfig } from \"@playwright/test\";\nimport { buildReporterConfig } from \"../reporting/allure-setup.js\";\nimport { deepMerge } from \"../utils/deep-merge.js\";\nimport { defaultEnvFiles, loadEnvFiles } from \"./env.js\";\nimport { loadConfig } from \"./loader.js\";\nimport type { AppConfig } from \"./schema.js\";\nimport type { DeepPartial } from \"./types.js\";\n\nexport type BrowserName = \"chromium\" | \"firefox\" | \"webkit\";\n\nexport interface DefineE2EConfigOptions {\n /**\n * Project-specific config overrides merged on top of the env-derived values.\n * Pass a FUNCTION if the overrides read `process.env` — it is called after the env files are\n * loaded, whereas an object literal in a `playwright.config.ts` import would be evaluated\n * before them.\n */\n configOverrides?: DeepPartial<AppConfig> | (() => DeepPartial<AppConfig>);\n /**\n * Environment to use when `TEST_ENV` isn't set. Defaults to `\"dev\"`. Set it for apps that\n * only have one environment (e.g. `\"staging\"`) so nobody has to export `TEST_ENV` by hand.\n */\n defaultEnvironment?: string;\n /**\n * Env files to load. Defaults to the standard cascade for the resolved environment:\n * `.env`, `.env.local`, `.env.<environment>`, `.env.<environment>.local` — later files win,\n * but real environment variables always win over files. Pass an explicit list to take full\n * control, or `false` to load none.\n */\n envFile?: string | string[] | false;\n /** Defaults to `./tests`. */\n testDir?: string;\n /** Browser projects to generate. Defaults to `[\"chromium\"]`. */\n browsers?: BrowserName[];\n /** Path to a global setup module (see `buildGlobalSetup`). */\n globalSetup?: string;\n globalTeardown?: string;\n /**\n * Deep-merged over everything generated here — the escape hatch for any Playwright option\n * core doesn't model. Anything set here wins.\n */\n playwright?: PlaywrightTestConfig;\n}\n\nfunction resolveEnvFiles(options: DefineE2EConfigOptions, environment: string): string[] {\n if (options.envFile === false) return [];\n if (typeof options.envFile === \"string\") return [options.envFile];\n if (Array.isArray(options.envFile)) return options.envFile;\n return defaultEnvFiles(environment);\n}\n\n/**\n * Builds a project's `playwright.config.ts` from its `AppConfig`, so each project doesn't\n * re-implement the same env-loading + config-plumbing boilerplate.\n *\n * Order matters and is handled here: env files are loaded FIRST, then `configOverrides` is\n * evaluated (hence the function form), then the config is validated, then the Playwright config\n * is assembled from it.\n *\n * ```ts\n * // playwright.config.ts\n * import { defineE2EConfig } from \"@emesoft_company/playwright-core/config\";\n * export default defineE2EConfig();\n * ```\n */\nexport function defineE2EConfig(options: DefineE2EConfigOptions = {}): PlaywrightTestConfig {\n const environment = process.env.TEST_ENV ?? options.defaultEnvironment ?? \"dev\";\n loadEnvFiles(resolveEnvFiles(options, environment));\n\n const overrides =\n typeof options.configOverrides === \"function\" ? options.configOverrides() : options.configOverrides;\n // The resolved environment is a base, not an override: an explicit `environment` in the\n // project's own overrides still wins.\n const appConfig = loadConfig({ overrides: deepMerge({ environment }, overrides) });\n\n const browsers = options.browsers ?? [\"chromium\"];\n const deviceByBrowser: Record<BrowserName, string> = {\n chromium: \"Desktop Chrome\",\n firefox: \"Desktop Firefox\",\n webkit: \"Desktop Safari\",\n };\n\n const generated: PlaywrightTestConfig = {\n testDir: options.testDir ?? \"./tests\",\n fullyParallel: true,\n retries: appConfig.retry.retries,\n timeout: appConfig.timeouts.testMs,\n reporter: buildReporterConfig(appConfig),\n // `globalSetup` is only wired when a path is given AND a storage state path is configured —\n // otherwise a project that hasn't set up login yet would fail on every run.\n ...(options.globalSetup && appConfig.auth.storageStatePath\n ? { globalSetup: options.globalSetup }\n : {}),\n ...(options.globalTeardown ? { globalTeardown: options.globalTeardown } : {}),\n use: {\n baseURL: appConfig.baseUrl,\n actionTimeout: appConfig.timeouts.actionMs,\n navigationTimeout: appConfig.timeouts.navigationMs,\n trace: \"on-first-retry\",\n screenshot: \"only-on-failure\",\n ...(options.globalSetup && appConfig.auth.storageStatePath\n ? { storageState: appConfig.auth.storageStatePath }\n : {}),\n },\n projects: browsers.map((browser) => ({\n name: browser,\n use: { ...devices[deviceByBrowser[browser]] },\n })),\n };\n\n return deepMerge(generated, options.playwright);\n}\n"]}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { A as AppConfig } from '../schema-y5w2hbT-.cjs';
|
|
2
|
+
export { a as AppConfigSchema, b as AuthConfig, c as AuthConfigSchema, D as DiagnosticsConfig, d as DiagnosticsConfigSchema, E as Environment, e as EnvironmentSchema, K as KNOWN_ENVIRONMENTS, R as ReportingConfig, f as ReportingConfigSchema, g as RetryConfig, h as RetryConfigSchema, T as TimeoutsConfig, i as TimeoutsSchema } from '../schema-y5w2hbT-.cjs';
|
|
3
|
+
import { D as DeepPartial } from '../types-CbWnSYEK.cjs';
|
|
4
|
+
import { PlaywrightTestConfig } from '@playwright/test';
|
|
5
|
+
import 'zod';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Reads a required environment variable, failing immediately with a clear message instead of
|
|
9
|
+
* letting an `undefined` surface as a confusing failure deep inside a test.
|
|
10
|
+
*/
|
|
11
|
+
declare function requireEnv(name: string, env?: NodeJS.ProcessEnv): string;
|
|
12
|
+
interface LoadConfigOptions {
|
|
13
|
+
/** Defaults to `process.env`. */
|
|
14
|
+
env?: NodeJS.ProcessEnv;
|
|
15
|
+
/** Project-specific overrides (e.g. from a project's `config.override.ts`), merged on top of env-derived values. */
|
|
16
|
+
overrides?: DeepPartial<AppConfig>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Loads and validates the application config for the current environment.
|
|
20
|
+
* Resolution order: env-derived defaults -> project `overrides` -> zod validation.
|
|
21
|
+
* Fails fast with `ConfigValidationError` when required fields (e.g. `baseUrl`) are missing/invalid,
|
|
22
|
+
* instead of letting tests fail later with a confusing `undefined` somewhere downstream.
|
|
23
|
+
*/
|
|
24
|
+
declare function loadConfig(options?: LoadConfigOptions): AppConfig;
|
|
25
|
+
|
|
26
|
+
interface LoadEnvFilesResult {
|
|
27
|
+
/** Files that existed and were read, in the order they were applied. */
|
|
28
|
+
loaded: string[];
|
|
29
|
+
/** Variable names actually written to the environment. */
|
|
30
|
+
applied: string[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Loads several env files with predictable precedence:
|
|
34
|
+
*
|
|
35
|
+
* 1. **Later files beat earlier ones** — so `.env.staging.local` overrides `.env.staging`.
|
|
36
|
+
* 2. **Real environment variables beat every file** — a `BASE_URL` exported by CI or the shell
|
|
37
|
+
* is never clobbered by a committed `.env`, which is what makes the same config work locally
|
|
38
|
+
* and in a pipeline.
|
|
39
|
+
*
|
|
40
|
+
* Missing files are skipped silently: the `.local` files are gitignored and simply won't exist
|
|
41
|
+
* on most machines.
|
|
42
|
+
*
|
|
43
|
+
* The two rules together are why this can't just be repeated `dotenv.config()` calls — dotenv
|
|
44
|
+
* either never overrides (so later files lose) or always overrides (so the shell loses). Files
|
|
45
|
+
* are merged first, then applied only where the environment has nothing already.
|
|
46
|
+
*/
|
|
47
|
+
declare function loadEnvFiles(files: string[], env?: NodeJS.ProcessEnv): LoadEnvFilesResult;
|
|
48
|
+
/**
|
|
49
|
+
* The standard cascade for an environment, following the convention used by Vite/CRA/Next:
|
|
50
|
+
* a committed base, a gitignored base override, then the same pair for the specific environment.
|
|
51
|
+
* Keep secrets in the `.local` files — they are gitignored; `.env` / `.env.<environment>` are not.
|
|
52
|
+
*/
|
|
53
|
+
declare function defaultEnvFiles(environment: string): string[];
|
|
54
|
+
|
|
55
|
+
type BrowserName = "chromium" | "firefox" | "webkit";
|
|
56
|
+
interface DefineE2EConfigOptions {
|
|
57
|
+
/**
|
|
58
|
+
* Project-specific config overrides merged on top of the env-derived values.
|
|
59
|
+
* Pass a FUNCTION if the overrides read `process.env` — it is called after the env files are
|
|
60
|
+
* loaded, whereas an object literal in a `playwright.config.ts` import would be evaluated
|
|
61
|
+
* before them.
|
|
62
|
+
*/
|
|
63
|
+
configOverrides?: DeepPartial<AppConfig> | (() => DeepPartial<AppConfig>);
|
|
64
|
+
/**
|
|
65
|
+
* Environment to use when `TEST_ENV` isn't set. Defaults to `"dev"`. Set it for apps that
|
|
66
|
+
* only have one environment (e.g. `"staging"`) so nobody has to export `TEST_ENV` by hand.
|
|
67
|
+
*/
|
|
68
|
+
defaultEnvironment?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Env files to load. Defaults to the standard cascade for the resolved environment:
|
|
71
|
+
* `.env`, `.env.local`, `.env.<environment>`, `.env.<environment>.local` — later files win,
|
|
72
|
+
* but real environment variables always win over files. Pass an explicit list to take full
|
|
73
|
+
* control, or `false` to load none.
|
|
74
|
+
*/
|
|
75
|
+
envFile?: string | string[] | false;
|
|
76
|
+
/** Defaults to `./tests`. */
|
|
77
|
+
testDir?: string;
|
|
78
|
+
/** Browser projects to generate. Defaults to `["chromium"]`. */
|
|
79
|
+
browsers?: BrowserName[];
|
|
80
|
+
/** Path to a global setup module (see `buildGlobalSetup`). */
|
|
81
|
+
globalSetup?: string;
|
|
82
|
+
globalTeardown?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Deep-merged over everything generated here — the escape hatch for any Playwright option
|
|
85
|
+
* core doesn't model. Anything set here wins.
|
|
86
|
+
*/
|
|
87
|
+
playwright?: PlaywrightTestConfig;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Builds a project's `playwright.config.ts` from its `AppConfig`, so each project doesn't
|
|
91
|
+
* re-implement the same env-loading + config-plumbing boilerplate.
|
|
92
|
+
*
|
|
93
|
+
* Order matters and is handled here: env files are loaded FIRST, then `configOverrides` is
|
|
94
|
+
* evaluated (hence the function form), then the config is validated, then the Playwright config
|
|
95
|
+
* is assembled from it.
|
|
96
|
+
*
|
|
97
|
+
* ```ts
|
|
98
|
+
* // playwright.config.ts
|
|
99
|
+
* import { defineE2EConfig } from "@emesoft_company/playwright-core/config";
|
|
100
|
+
* export default defineE2EConfig();
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
declare function defineE2EConfig(options?: DefineE2EConfigOptions): PlaywrightTestConfig;
|
|
104
|
+
|
|
105
|
+
export { AppConfig, type BrowserName, DeepPartial, type DefineE2EConfigOptions, type LoadConfigOptions, type LoadEnvFilesResult, defaultEnvFiles, defineE2EConfig, loadConfig, loadEnvFiles, requireEnv };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { A as AppConfig } from '../schema-y5w2hbT-.js';
|
|
2
|
+
export { a as AppConfigSchema, b as AuthConfig, c as AuthConfigSchema, D as DiagnosticsConfig, d as DiagnosticsConfigSchema, E as Environment, e as EnvironmentSchema, K as KNOWN_ENVIRONMENTS, R as ReportingConfig, f as ReportingConfigSchema, g as RetryConfig, h as RetryConfigSchema, T as TimeoutsConfig, i as TimeoutsSchema } from '../schema-y5w2hbT-.js';
|
|
3
|
+
import { D as DeepPartial } from '../types-CbWnSYEK.js';
|
|
4
|
+
import { PlaywrightTestConfig } from '@playwright/test';
|
|
5
|
+
import 'zod';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Reads a required environment variable, failing immediately with a clear message instead of
|
|
9
|
+
* letting an `undefined` surface as a confusing failure deep inside a test.
|
|
10
|
+
*/
|
|
11
|
+
declare function requireEnv(name: string, env?: NodeJS.ProcessEnv): string;
|
|
12
|
+
interface LoadConfigOptions {
|
|
13
|
+
/** Defaults to `process.env`. */
|
|
14
|
+
env?: NodeJS.ProcessEnv;
|
|
15
|
+
/** Project-specific overrides (e.g. from a project's `config.override.ts`), merged on top of env-derived values. */
|
|
16
|
+
overrides?: DeepPartial<AppConfig>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Loads and validates the application config for the current environment.
|
|
20
|
+
* Resolution order: env-derived defaults -> project `overrides` -> zod validation.
|
|
21
|
+
* Fails fast with `ConfigValidationError` when required fields (e.g. `baseUrl`) are missing/invalid,
|
|
22
|
+
* instead of letting tests fail later with a confusing `undefined` somewhere downstream.
|
|
23
|
+
*/
|
|
24
|
+
declare function loadConfig(options?: LoadConfigOptions): AppConfig;
|
|
25
|
+
|
|
26
|
+
interface LoadEnvFilesResult {
|
|
27
|
+
/** Files that existed and were read, in the order they were applied. */
|
|
28
|
+
loaded: string[];
|
|
29
|
+
/** Variable names actually written to the environment. */
|
|
30
|
+
applied: string[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Loads several env files with predictable precedence:
|
|
34
|
+
*
|
|
35
|
+
* 1. **Later files beat earlier ones** — so `.env.staging.local` overrides `.env.staging`.
|
|
36
|
+
* 2. **Real environment variables beat every file** — a `BASE_URL` exported by CI or the shell
|
|
37
|
+
* is never clobbered by a committed `.env`, which is what makes the same config work locally
|
|
38
|
+
* and in a pipeline.
|
|
39
|
+
*
|
|
40
|
+
* Missing files are skipped silently: the `.local` files are gitignored and simply won't exist
|
|
41
|
+
* on most machines.
|
|
42
|
+
*
|
|
43
|
+
* The two rules together are why this can't just be repeated `dotenv.config()` calls — dotenv
|
|
44
|
+
* either never overrides (so later files lose) or always overrides (so the shell loses). Files
|
|
45
|
+
* are merged first, then applied only where the environment has nothing already.
|
|
46
|
+
*/
|
|
47
|
+
declare function loadEnvFiles(files: string[], env?: NodeJS.ProcessEnv): LoadEnvFilesResult;
|
|
48
|
+
/**
|
|
49
|
+
* The standard cascade for an environment, following the convention used by Vite/CRA/Next:
|
|
50
|
+
* a committed base, a gitignored base override, then the same pair for the specific environment.
|
|
51
|
+
* Keep secrets in the `.local` files — they are gitignored; `.env` / `.env.<environment>` are not.
|
|
52
|
+
*/
|
|
53
|
+
declare function defaultEnvFiles(environment: string): string[];
|
|
54
|
+
|
|
55
|
+
type BrowserName = "chromium" | "firefox" | "webkit";
|
|
56
|
+
interface DefineE2EConfigOptions {
|
|
57
|
+
/**
|
|
58
|
+
* Project-specific config overrides merged on top of the env-derived values.
|
|
59
|
+
* Pass a FUNCTION if the overrides read `process.env` — it is called after the env files are
|
|
60
|
+
* loaded, whereas an object literal in a `playwright.config.ts` import would be evaluated
|
|
61
|
+
* before them.
|
|
62
|
+
*/
|
|
63
|
+
configOverrides?: DeepPartial<AppConfig> | (() => DeepPartial<AppConfig>);
|
|
64
|
+
/**
|
|
65
|
+
* Environment to use when `TEST_ENV` isn't set. Defaults to `"dev"`. Set it for apps that
|
|
66
|
+
* only have one environment (e.g. `"staging"`) so nobody has to export `TEST_ENV` by hand.
|
|
67
|
+
*/
|
|
68
|
+
defaultEnvironment?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Env files to load. Defaults to the standard cascade for the resolved environment:
|
|
71
|
+
* `.env`, `.env.local`, `.env.<environment>`, `.env.<environment>.local` — later files win,
|
|
72
|
+
* but real environment variables always win over files. Pass an explicit list to take full
|
|
73
|
+
* control, or `false` to load none.
|
|
74
|
+
*/
|
|
75
|
+
envFile?: string | string[] | false;
|
|
76
|
+
/** Defaults to `./tests`. */
|
|
77
|
+
testDir?: string;
|
|
78
|
+
/** Browser projects to generate. Defaults to `["chromium"]`. */
|
|
79
|
+
browsers?: BrowserName[];
|
|
80
|
+
/** Path to a global setup module (see `buildGlobalSetup`). */
|
|
81
|
+
globalSetup?: string;
|
|
82
|
+
globalTeardown?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Deep-merged over everything generated here — the escape hatch for any Playwright option
|
|
85
|
+
* core doesn't model. Anything set here wins.
|
|
86
|
+
*/
|
|
87
|
+
playwright?: PlaywrightTestConfig;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Builds a project's `playwright.config.ts` from its `AppConfig`, so each project doesn't
|
|
91
|
+
* re-implement the same env-loading + config-plumbing boilerplate.
|
|
92
|
+
*
|
|
93
|
+
* Order matters and is handled here: env files are loaded FIRST, then `configOverrides` is
|
|
94
|
+
* evaluated (hence the function form), then the config is validated, then the Playwright config
|
|
95
|
+
* is assembled from it.
|
|
96
|
+
*
|
|
97
|
+
* ```ts
|
|
98
|
+
* // playwright.config.ts
|
|
99
|
+
* import { defineE2EConfig } from "@emesoft_company/playwright-core/config";
|
|
100
|
+
* export default defineE2EConfig();
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
declare function defineE2EConfig(options?: DefineE2EConfigOptions): PlaywrightTestConfig;
|
|
104
|
+
|
|
105
|
+
export { AppConfig, type BrowserName, DeepPartial, type DefineE2EConfigOptions, type LoadConfigOptions, type LoadEnvFilesResult, defaultEnvFiles, defineE2EConfig, loadConfig, loadEnvFiles, requireEnv };
|