@vulcn/engine 0.5.0 → 0.8.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/CHANGELOG.md +50 -0
- package/dist/index.cjs +425 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +273 -2
- package/dist/index.d.ts +273 -2
- package/dist/index.js +416 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -156,23 +156,17 @@ var DriverManager = class {
|
|
|
156
156
|
/**
|
|
157
157
|
* Execute a session
|
|
158
158
|
* Invokes plugin hooks (onRunStart, onRunEnd) around the driver runner.
|
|
159
|
+
* Plugin onRunStart is deferred until the driver signals the page is ready
|
|
160
|
+
* via the onPageReady callback, ensuring plugins get a real page object.
|
|
159
161
|
*/
|
|
160
162
|
async execute(session, pluginManager2, options = {}) {
|
|
161
163
|
const driver = this.getForSession(session);
|
|
162
164
|
const findings = [];
|
|
163
165
|
const logger = this.createLogger(driver.name);
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
findings,
|
|
169
|
-
addFinding: (finding) => {
|
|
170
|
-
findings.push(finding);
|
|
171
|
-
pluginManager2.addFinding(finding);
|
|
172
|
-
options.onFinding?.(finding);
|
|
173
|
-
},
|
|
174
|
-
logger,
|
|
175
|
-
options
|
|
166
|
+
const addFinding = (finding) => {
|
|
167
|
+
findings.push(finding);
|
|
168
|
+
pluginManager2.addFinding(finding);
|
|
169
|
+
options.onFinding?.(finding);
|
|
176
170
|
};
|
|
177
171
|
const pluginCtx = {
|
|
178
172
|
session,
|
|
@@ -182,21 +176,57 @@ var DriverManager = class {
|
|
|
182
176
|
engine: { version: "0.3.0", pluginApiVersion: 1 },
|
|
183
177
|
payloads: pluginManager2.getPayloads(),
|
|
184
178
|
findings,
|
|
179
|
+
addFinding,
|
|
185
180
|
logger,
|
|
186
181
|
fetch: globalThis.fetch
|
|
187
182
|
};
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
183
|
+
const ctx = {
|
|
184
|
+
session,
|
|
185
|
+
pluginManager: pluginManager2,
|
|
186
|
+
payloads: pluginManager2.getPayloads(),
|
|
187
|
+
findings,
|
|
188
|
+
addFinding,
|
|
189
|
+
logger,
|
|
190
|
+
options: {
|
|
191
|
+
...options,
|
|
192
|
+
// Provide onPageReady callback — fires plugin onRunStart hooks
|
|
193
|
+
// with the real page object once the driver has created it
|
|
194
|
+
onPageReady: async (page) => {
|
|
195
|
+
pluginCtx.page = page;
|
|
196
|
+
for (const loaded of pluginManager2.getPlugins()) {
|
|
197
|
+
if (loaded.enabled && loaded.plugin.hooks?.onRunStart) {
|
|
198
|
+
try {
|
|
199
|
+
await loaded.plugin.hooks.onRunStart({
|
|
200
|
+
...pluginCtx,
|
|
201
|
+
config: loaded.config
|
|
202
|
+
});
|
|
203
|
+
} catch (err) {
|
|
204
|
+
logger.warn(
|
|
205
|
+
`Plugin ${loaded.plugin.name} onRunStart failed: ${err}`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
// Fires before browser closes — lets plugins flush pending async work
|
|
212
|
+
onBeforeClose: async (_page) => {
|
|
213
|
+
for (const loaded of pluginManager2.getPlugins()) {
|
|
214
|
+
if (loaded.enabled && loaded.plugin.hooks?.onBeforeClose) {
|
|
215
|
+
try {
|
|
216
|
+
await loaded.plugin.hooks.onBeforeClose({
|
|
217
|
+
...pluginCtx,
|
|
218
|
+
config: loaded.config
|
|
219
|
+
});
|
|
220
|
+
} catch (err) {
|
|
221
|
+
logger.warn(
|
|
222
|
+
`Plugin ${loaded.plugin.name} onBeforeClose failed: ${err}`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
197
227
|
}
|
|
198
228
|
}
|
|
199
|
-
}
|
|
229
|
+
};
|
|
200
230
|
let result = await driver.runner.execute(session, ctx);
|
|
201
231
|
for (const loaded of pluginManager2.getPlugins()) {
|
|
202
232
|
if (loaded.enabled && loaded.plugin.hooks?.onRunEnd) {
|
|
@@ -213,6 +243,109 @@ var DriverManager = class {
|
|
|
213
243
|
}
|
|
214
244
|
return result;
|
|
215
245
|
}
|
|
246
|
+
/**
|
|
247
|
+
* Execute multiple sessions with a shared browser (scan-level orchestration).
|
|
248
|
+
*
|
|
249
|
+
* This is the preferred entry point for running a full scan. It:
|
|
250
|
+
* 1. Launches ONE browser for the entire scan
|
|
251
|
+
* 2. Passes the browser to each session's runner via options.browser
|
|
252
|
+
* 3. Each session creates its own context (lightweight, isolated cookies)
|
|
253
|
+
* 4. Aggregates results across all sessions
|
|
254
|
+
* 5. Closes the browser once at the end
|
|
255
|
+
*
|
|
256
|
+
* This is 5-10x faster than calling execute() per session because
|
|
257
|
+
* launching a browser takes 2-3 seconds.
|
|
258
|
+
*/
|
|
259
|
+
async executeScan(sessions, pluginManager2, options = {}) {
|
|
260
|
+
if (sessions.length === 0) {
|
|
261
|
+
const empty = {
|
|
262
|
+
findings: [],
|
|
263
|
+
stepsExecuted: 0,
|
|
264
|
+
payloadsTested: 0,
|
|
265
|
+
duration: 0,
|
|
266
|
+
errors: ["No sessions to execute"]
|
|
267
|
+
};
|
|
268
|
+
return { results: [], aggregate: empty };
|
|
269
|
+
}
|
|
270
|
+
const startTime = Date.now();
|
|
271
|
+
const results = [];
|
|
272
|
+
const allFindings = [];
|
|
273
|
+
let totalSteps = 0;
|
|
274
|
+
let totalPayloads = 0;
|
|
275
|
+
const allErrors = [];
|
|
276
|
+
const firstDriver = this.getForSession(sessions[0]);
|
|
277
|
+
let sharedBrowser = null;
|
|
278
|
+
if (firstDriver.name === "browser") {
|
|
279
|
+
try {
|
|
280
|
+
const driverPkg = "@vulcn/driver-browser";
|
|
281
|
+
const { launchBrowser } = await import(
|
|
282
|
+
/* @vite-ignore */
|
|
283
|
+
driverPkg
|
|
284
|
+
);
|
|
285
|
+
const browserType = sessions[0].driverConfig.browser ?? "chromium";
|
|
286
|
+
const headless = options.headless ?? true;
|
|
287
|
+
const result = await launchBrowser({
|
|
288
|
+
browser: browserType,
|
|
289
|
+
headless
|
|
290
|
+
});
|
|
291
|
+
sharedBrowser = result.browser;
|
|
292
|
+
} catch {
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
await pluginManager2.callHook("onScanStart", async (hook, ctx) => {
|
|
297
|
+
const scanCtx = {
|
|
298
|
+
...ctx,
|
|
299
|
+
sessions,
|
|
300
|
+
headless: options.headless ?? true,
|
|
301
|
+
sessionCount: sessions.length
|
|
302
|
+
};
|
|
303
|
+
await hook(scanCtx);
|
|
304
|
+
});
|
|
305
|
+
for (const session of sessions) {
|
|
306
|
+
const sessionOptions = {
|
|
307
|
+
...options,
|
|
308
|
+
...sharedBrowser ? { browser: sharedBrowser } : {}
|
|
309
|
+
};
|
|
310
|
+
const result = await this.execute(
|
|
311
|
+
session,
|
|
312
|
+
pluginManager2,
|
|
313
|
+
sessionOptions
|
|
314
|
+
);
|
|
315
|
+
results.push(result);
|
|
316
|
+
allFindings.push(...result.findings);
|
|
317
|
+
totalSteps += result.stepsExecuted;
|
|
318
|
+
totalPayloads += result.payloadsTested;
|
|
319
|
+
allErrors.push(...result.errors);
|
|
320
|
+
}
|
|
321
|
+
} finally {
|
|
322
|
+
if (sharedBrowser && typeof sharedBrowser.close === "function") {
|
|
323
|
+
await sharedBrowser.close();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const aggregate = {
|
|
327
|
+
findings: allFindings,
|
|
328
|
+
stepsExecuted: totalSteps,
|
|
329
|
+
payloadsTested: totalPayloads,
|
|
330
|
+
duration: Date.now() - startTime,
|
|
331
|
+
errors: allErrors
|
|
332
|
+
};
|
|
333
|
+
let finalAggregate = aggregate;
|
|
334
|
+
finalAggregate = await pluginManager2.callHookPipe(
|
|
335
|
+
"onScanEnd",
|
|
336
|
+
finalAggregate,
|
|
337
|
+
async (hook, value, ctx) => {
|
|
338
|
+
const scanCtx = {
|
|
339
|
+
...ctx,
|
|
340
|
+
sessions,
|
|
341
|
+
headless: options.headless ?? true,
|
|
342
|
+
sessionCount: sessions.length
|
|
343
|
+
};
|
|
344
|
+
return await hook(value, scanCtx);
|
|
345
|
+
}
|
|
346
|
+
);
|
|
347
|
+
return { results, aggregate: finalAggregate };
|
|
348
|
+
}
|
|
216
349
|
/**
|
|
217
350
|
* Validate driver structure
|
|
218
351
|
*/
|
|
@@ -487,6 +620,9 @@ var PluginManager = class {
|
|
|
487
620
|
engine: engineInfo,
|
|
488
621
|
payloads: this.sharedPayloads,
|
|
489
622
|
findings: this.sharedFindings,
|
|
623
|
+
addFinding: (finding) => {
|
|
624
|
+
this.sharedFindings.push(finding);
|
|
625
|
+
},
|
|
490
626
|
logger: this.createLogger("plugin"),
|
|
491
627
|
fetch: globalThis.fetch
|
|
492
628
|
};
|
|
@@ -583,12 +719,269 @@ var PluginManager = class {
|
|
|
583
719
|
}
|
|
584
720
|
};
|
|
585
721
|
var pluginManager = new PluginManager();
|
|
722
|
+
|
|
723
|
+
// src/auth.ts
|
|
724
|
+
import {
|
|
725
|
+
randomBytes,
|
|
726
|
+
createCipheriv,
|
|
727
|
+
createDecipheriv,
|
|
728
|
+
pbkdf2Sync
|
|
729
|
+
} from "crypto";
|
|
730
|
+
var ALGORITHM = "aes-256-gcm";
|
|
731
|
+
var KEY_LENGTH = 32;
|
|
732
|
+
var IV_LENGTH = 16;
|
|
733
|
+
var SALT_LENGTH = 32;
|
|
734
|
+
var PBKDF2_ITERATIONS = 1e5;
|
|
735
|
+
var PBKDF2_DIGEST = "sha512";
|
|
736
|
+
function deriveKey(passphrase, salt) {
|
|
737
|
+
return pbkdf2Sync(
|
|
738
|
+
passphrase,
|
|
739
|
+
salt,
|
|
740
|
+
PBKDF2_ITERATIONS,
|
|
741
|
+
KEY_LENGTH,
|
|
742
|
+
PBKDF2_DIGEST
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
function encrypt(data, passphrase) {
|
|
746
|
+
const salt = randomBytes(SALT_LENGTH);
|
|
747
|
+
const iv = randomBytes(IV_LENGTH);
|
|
748
|
+
const key = deriveKey(passphrase, salt);
|
|
749
|
+
const cipher = createCipheriv(ALGORITHM, key, iv);
|
|
750
|
+
let encrypted = cipher.update(data, "utf8", "hex");
|
|
751
|
+
encrypted += cipher.final("hex");
|
|
752
|
+
const tag = cipher.getAuthTag();
|
|
753
|
+
const payload = {
|
|
754
|
+
version: 1,
|
|
755
|
+
salt: salt.toString("hex"),
|
|
756
|
+
iv: iv.toString("hex"),
|
|
757
|
+
tag: tag.toString("hex"),
|
|
758
|
+
data: encrypted,
|
|
759
|
+
iterations: PBKDF2_ITERATIONS
|
|
760
|
+
};
|
|
761
|
+
return JSON.stringify(payload);
|
|
762
|
+
}
|
|
763
|
+
function decrypt(encrypted, passphrase) {
|
|
764
|
+
const payload = JSON.parse(encrypted);
|
|
765
|
+
if (payload.version !== 1) {
|
|
766
|
+
throw new Error(`Unsupported encryption version: ${payload.version}`);
|
|
767
|
+
}
|
|
768
|
+
const salt = Buffer.from(payload.salt, "hex");
|
|
769
|
+
const iv = Buffer.from(payload.iv, "hex");
|
|
770
|
+
const tag = Buffer.from(payload.tag, "hex");
|
|
771
|
+
const key = deriveKey(passphrase, salt);
|
|
772
|
+
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
|
773
|
+
decipher.setAuthTag(tag);
|
|
774
|
+
let decrypted = decipher.update(payload.data, "hex", "utf8");
|
|
775
|
+
decrypted += decipher.final("utf8");
|
|
776
|
+
return decrypted;
|
|
777
|
+
}
|
|
778
|
+
function encryptCredentials(credentials, passphrase) {
|
|
779
|
+
return encrypt(JSON.stringify(credentials), passphrase);
|
|
780
|
+
}
|
|
781
|
+
function decryptCredentials(encrypted, passphrase) {
|
|
782
|
+
const json = decrypt(encrypted, passphrase);
|
|
783
|
+
return JSON.parse(json);
|
|
784
|
+
}
|
|
785
|
+
function encryptStorageState(storageState, passphrase) {
|
|
786
|
+
return encrypt(storageState, passphrase);
|
|
787
|
+
}
|
|
788
|
+
function decryptStorageState(encrypted, passphrase) {
|
|
789
|
+
return decrypt(encrypted, passphrase);
|
|
790
|
+
}
|
|
791
|
+
function getPassphrase(interactive) {
|
|
792
|
+
if (interactive) return interactive;
|
|
793
|
+
const envKey = process.env.VULCN_KEY;
|
|
794
|
+
if (envKey) return envKey;
|
|
795
|
+
throw new Error(
|
|
796
|
+
"No passphrase provided. Set VULCN_KEY environment variable or pass --passphrase."
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// src/session.ts
|
|
801
|
+
import { readFile as readFile2, writeFile, mkdir, readdir } from "fs/promises";
|
|
802
|
+
import { existsSync as existsSync2 } from "fs";
|
|
803
|
+
import { join, basename } from "path";
|
|
804
|
+
import { parse as parse2, stringify as stringify2 } from "yaml";
|
|
805
|
+
async function loadSessionDir(dirPath) {
|
|
806
|
+
const manifestPath = join(dirPath, "manifest.yml");
|
|
807
|
+
if (!existsSync2(manifestPath)) {
|
|
808
|
+
throw new Error(
|
|
809
|
+
`No manifest.yml found in ${dirPath}. Is this a v2 session directory?`
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
const manifestYaml = await readFile2(manifestPath, "utf-8");
|
|
813
|
+
const manifest = parse2(manifestYaml);
|
|
814
|
+
if (manifest.version !== "2") {
|
|
815
|
+
throw new Error(
|
|
816
|
+
`Unsupported session format version: ${manifest.version}. Expected "2".`
|
|
817
|
+
);
|
|
818
|
+
}
|
|
819
|
+
let authConfig;
|
|
820
|
+
if (manifest.auth?.configFile) {
|
|
821
|
+
const authPath = join(dirPath, manifest.auth.configFile);
|
|
822
|
+
if (existsSync2(authPath)) {
|
|
823
|
+
const authYaml = await readFile2(authPath, "utf-8");
|
|
824
|
+
authConfig = parse2(authYaml);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
const sessions = [];
|
|
828
|
+
for (const ref of manifest.sessions) {
|
|
829
|
+
if (ref.injectable === false) continue;
|
|
830
|
+
const sessionPath = join(dirPath, ref.file);
|
|
831
|
+
if (!existsSync2(sessionPath)) {
|
|
832
|
+
console.warn(`Session file not found: ${sessionPath}, skipping`);
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
const sessionYaml = await readFile2(sessionPath, "utf-8");
|
|
836
|
+
const sessionData = parse2(sessionYaml);
|
|
837
|
+
const session = {
|
|
838
|
+
name: sessionData.name ?? basename(ref.file, ".yml"),
|
|
839
|
+
driver: manifest.driver,
|
|
840
|
+
driverConfig: {
|
|
841
|
+
...manifest.driverConfig,
|
|
842
|
+
startUrl: resolveUrl(
|
|
843
|
+
manifest.target,
|
|
844
|
+
sessionData.page
|
|
845
|
+
)
|
|
846
|
+
},
|
|
847
|
+
steps: sessionData.steps ?? [],
|
|
848
|
+
metadata: {
|
|
849
|
+
recordedAt: manifest.recordedAt,
|
|
850
|
+
version: "2",
|
|
851
|
+
manifestDir: dirPath
|
|
852
|
+
}
|
|
853
|
+
};
|
|
854
|
+
sessions.push(session);
|
|
855
|
+
}
|
|
856
|
+
return { manifest, sessions, authConfig };
|
|
857
|
+
}
|
|
858
|
+
function isSessionDir(path) {
|
|
859
|
+
return existsSync2(join(path, "manifest.yml"));
|
|
860
|
+
}
|
|
861
|
+
function looksLikeSessionDir(path) {
|
|
862
|
+
return path.endsWith(".vulcn") || path.endsWith(".vulcn/");
|
|
863
|
+
}
|
|
864
|
+
async function saveSessionDir(dirPath, options) {
|
|
865
|
+
await mkdir(join(dirPath, "sessions"), { recursive: true });
|
|
866
|
+
const sessionRefs = [];
|
|
867
|
+
for (const session of options.sessions) {
|
|
868
|
+
const safeName = slugify(session.name);
|
|
869
|
+
const fileName = `sessions/${safeName}.yml`;
|
|
870
|
+
const sessionPath = join(dirPath, fileName);
|
|
871
|
+
const startUrl = session.driverConfig.startUrl;
|
|
872
|
+
const page = startUrl ? startUrl.replace(options.target, "").replace(/^\//, "/") : void 0;
|
|
873
|
+
const sessionData = {
|
|
874
|
+
name: session.name,
|
|
875
|
+
...page ? { page } : {},
|
|
876
|
+
steps: session.steps
|
|
877
|
+
};
|
|
878
|
+
await writeFile(sessionPath, stringify2(sessionData), "utf-8");
|
|
879
|
+
const hasInjectable = session.steps.some(
|
|
880
|
+
(s) => s.type === "browser.input" && s.injectable !== false
|
|
881
|
+
);
|
|
882
|
+
sessionRefs.push({
|
|
883
|
+
file: fileName,
|
|
884
|
+
injectable: hasInjectable
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
if (options.authConfig) {
|
|
888
|
+
await mkdir(join(dirPath, "auth"), { recursive: true });
|
|
889
|
+
await writeFile(
|
|
890
|
+
join(dirPath, "auth", "config.yml"),
|
|
891
|
+
stringify2(options.authConfig),
|
|
892
|
+
"utf-8"
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
if (options.encryptedState) {
|
|
896
|
+
await mkdir(join(dirPath, "auth"), { recursive: true });
|
|
897
|
+
await writeFile(
|
|
898
|
+
join(dirPath, "auth", "state.enc"),
|
|
899
|
+
options.encryptedState,
|
|
900
|
+
"utf-8"
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
if (options.requests && options.requests.length > 0) {
|
|
904
|
+
await mkdir(join(dirPath, "requests"), { recursive: true });
|
|
905
|
+
for (const req of options.requests) {
|
|
906
|
+
const safeName = slugify(req.sessionName);
|
|
907
|
+
await writeFile(
|
|
908
|
+
join(dirPath, "requests", `${safeName}.json`),
|
|
909
|
+
JSON.stringify(req, null, 2),
|
|
910
|
+
"utf-8"
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
const manifest = {
|
|
915
|
+
version: "2",
|
|
916
|
+
name: options.name,
|
|
917
|
+
target: options.target,
|
|
918
|
+
recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
919
|
+
driver: options.driver,
|
|
920
|
+
driverConfig: options.driverConfig,
|
|
921
|
+
...options.authConfig ? {
|
|
922
|
+
auth: {
|
|
923
|
+
strategy: options.authConfig.strategy,
|
|
924
|
+
configFile: "auth/config.yml",
|
|
925
|
+
stateFile: options.encryptedState ? "auth/state.enc" : void 0,
|
|
926
|
+
loggedInIndicator: options.authConfig.loggedInIndicator,
|
|
927
|
+
loggedOutIndicator: options.authConfig.loggedOutIndicator
|
|
928
|
+
}
|
|
929
|
+
} : {},
|
|
930
|
+
sessions: sessionRefs,
|
|
931
|
+
scan: {
|
|
932
|
+
tier: "auto",
|
|
933
|
+
parallel: 1,
|
|
934
|
+
timeout: 12e4
|
|
935
|
+
}
|
|
936
|
+
};
|
|
937
|
+
await writeFile(join(dirPath, "manifest.yml"), stringify2(manifest), "utf-8");
|
|
938
|
+
}
|
|
939
|
+
async function readAuthState(dirPath) {
|
|
940
|
+
const statePath = join(dirPath, "auth", "state.enc");
|
|
941
|
+
if (!existsSync2(statePath)) return null;
|
|
942
|
+
return readFile2(statePath, "utf-8");
|
|
943
|
+
}
|
|
944
|
+
async function readCapturedRequests(dirPath) {
|
|
945
|
+
const requestsDir = join(dirPath, "requests");
|
|
946
|
+
if (!existsSync2(requestsDir)) return [];
|
|
947
|
+
const files = await readdir(requestsDir);
|
|
948
|
+
const requests = [];
|
|
949
|
+
for (const file of files) {
|
|
950
|
+
if (!file.endsWith(".json")) continue;
|
|
951
|
+
const content = await readFile2(join(requestsDir, file), "utf-8");
|
|
952
|
+
requests.push(JSON.parse(content));
|
|
953
|
+
}
|
|
954
|
+
return requests;
|
|
955
|
+
}
|
|
956
|
+
function resolveUrl(target, page) {
|
|
957
|
+
if (!page) return target;
|
|
958
|
+
if (page.startsWith("http")) return page;
|
|
959
|
+
const base = target.replace(/\/$/, "");
|
|
960
|
+
const path = page.startsWith("/") ? page : `/${page}`;
|
|
961
|
+
return `${base}${path}`;
|
|
962
|
+
}
|
|
963
|
+
function slugify(text) {
|
|
964
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
|
|
965
|
+
}
|
|
586
966
|
export {
|
|
587
967
|
DRIVER_API_VERSION,
|
|
588
968
|
DriverManager,
|
|
589
969
|
PLUGIN_API_VERSION,
|
|
590
970
|
PluginManager,
|
|
971
|
+
decrypt,
|
|
972
|
+
decryptCredentials,
|
|
973
|
+
decryptStorageState,
|
|
591
974
|
driverManager,
|
|
592
|
-
|
|
975
|
+
encrypt,
|
|
976
|
+
encryptCredentials,
|
|
977
|
+
encryptStorageState,
|
|
978
|
+
getPassphrase,
|
|
979
|
+
isSessionDir,
|
|
980
|
+
loadSessionDir,
|
|
981
|
+
looksLikeSessionDir,
|
|
982
|
+
pluginManager,
|
|
983
|
+
readAuthState,
|
|
984
|
+
readCapturedRequests,
|
|
985
|
+
saveSessionDir
|
|
593
986
|
};
|
|
594
987
|
//# sourceMappingURL=index.js.map
|