@wenathlan/extension 1.1.51 → 1.1.53
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 +7 -5
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1908 -200
- package/dist/index.js.map +4 -4
- package/dist/memory.d.ts +85 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/policy.d.ts +37 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/protocol.d.ts +139 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/trigger.d.ts +151 -0
- package/dist/trigger.d.ts.map +1 -0
- package/dist/types.d.ts +289 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/workflow.d.ts +19 -3
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workfloweditor.d.ts +108 -0
- package/dist/workfloweditor.d.ts.map +1 -0
- package/extension/dist/background.js +2306 -20
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/pagebridge.js +1 -1
- package/extension/dist/pagebridge.js.map +2 -2
- package/extension/dist/popup.html +1 -1
- package/extension/dist/popup.js +24 -1
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sidepanel.html +1 -1
- package/extension/dist/sidepanel.js +1442 -1
- package/extension/dist/sidepanel.js.map +4 -4
- package/extension/dist/style.css +2 -0
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -335,6 +335,14 @@ function teardowncdpsession(input) {
|
|
|
335
335
|
|
|
336
336
|
// workflow.ts
|
|
337
337
|
var workflowkinds = ["composeworkflow", "savetemplate", "runworkflow", "dryrun", "delay", "waitelement", "compute", "extractvars"];
|
|
338
|
+
function nestedparamof(value) {
|
|
339
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
340
|
+
const candidate = value;
|
|
341
|
+
if (typeof candidate.name !== "string" || !/^[a-z][a-z0-9]*$/.test(candidate.name)) return void 0;
|
|
342
|
+
if (!variablekinds.includes(candidate.kind)) return void 0;
|
|
343
|
+
if (candidate.default !== void 0 && !["string", "number", "boolean"].includes(typeof candidate.default) && !Array.isArray(candidate.default)) return void 0;
|
|
344
|
+
return { name: candidate.name, kind: candidate.kind, ...candidate.default !== void 0 ? { default: candidate.default } : {} };
|
|
345
|
+
}
|
|
338
346
|
function workflowstepof(value) {
|
|
339
347
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
340
348
|
const candidate = value;
|
|
@@ -344,6 +352,7 @@ function workflowstepof(value) {
|
|
|
344
352
|
if (candidate.target !== void 0 && (typeof candidate.target !== "string" || !candidate.target)) return void 0;
|
|
345
353
|
if (candidate.value !== void 0 && typeof candidate.value !== "string") return void 0;
|
|
346
354
|
if (candidate.options !== void 0 && typeof candidate.options !== "string") return void 0;
|
|
355
|
+
if (candidate.breakpoint !== void 0 && typeof candidate.breakpoint !== "boolean") return void 0;
|
|
347
356
|
const bindings = Array.isArray(candidate.bindings) ? candidate.bindings.flatMap((binding) => bindingof(binding) !== void 0 ? [bindingof(binding)] : []) : void 0;
|
|
348
357
|
if (candidate.bindings !== void 0 && bindings === void 0) return void 0;
|
|
349
358
|
if (Array.isArray(candidate.bindings) && bindings !== void 0 && bindings.length !== candidate.bindings.length) return void 0;
|
|
@@ -351,14 +360,20 @@ function workflowstepof(value) {
|
|
|
351
360
|
if (candidate.expression !== void 0 && expression === void 0) return void 0;
|
|
352
361
|
const extract = candidate.extract === void 0 ? void 0 : regexruleof(candidate.extract);
|
|
353
362
|
if (candidate.extract !== void 0 && extract === void 0) return void 0;
|
|
354
|
-
|
|
363
|
+
const params = Array.isArray(candidate.params) ? candidate.params.flatMap((param) => nestedparamof(param) !== void 0 ? [nestedparamof(param)] : []) : void 0;
|
|
364
|
+
if (candidate.params !== void 0 && params === void 0) return void 0;
|
|
365
|
+
if (Array.isArray(candidate.params) && params !== void 0 && params.length !== candidate.params.length) return void 0;
|
|
366
|
+
return { id: candidate.id, kind: candidate.kind, label: candidate.label, ...candidate.target !== void 0 ? { target: candidate.target } : {}, ...candidate.value !== void 0 ? { value: candidate.value } : {}, ...candidate.options !== void 0 ? { options: candidate.options } : {}, ...bindings !== void 0 && bindings.length > 0 ? { bindings } : {}, ...expression !== void 0 ? { expression } : {}, ...extract !== void 0 ? { extract } : {}, ...candidate.breakpoint === true ? { breakpoint: true } : {}, ...params !== void 0 && params.length > 0 ? { params } : {} };
|
|
355
367
|
}
|
|
356
368
|
function blockinvocationof(value) {
|
|
357
369
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
358
370
|
const candidate = value;
|
|
359
371
|
if (typeof candidate.block !== "string" || !candidate.block.trim()) return void 0;
|
|
360
372
|
if (typeof candidate.label !== "string" || !candidate.label.trim()) return void 0;
|
|
361
|
-
|
|
373
|
+
const params = Array.isArray(candidate.params) ? candidate.params.flatMap((param) => nestedparamof(param) !== void 0 ? [nestedparamof(param)] : []) : void 0;
|
|
374
|
+
if (candidate.params !== void 0 && params === void 0) return void 0;
|
|
375
|
+
if (Array.isArray(candidate.params) && params !== void 0 && params.length !== candidate.params.length) return void 0;
|
|
376
|
+
return { block: candidate.block, label: candidate.label, ...params !== void 0 && params.length > 0 ? { params } : {} };
|
|
362
377
|
}
|
|
363
378
|
function workflowblockof(value) {
|
|
364
379
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
@@ -437,10 +452,15 @@ function regexruleof(value) {
|
|
|
437
452
|
function expandblocks(steps, blocks) {
|
|
438
453
|
const byname = new Map(blocks.map((block) => [block.name, block]));
|
|
439
454
|
const expanded = [];
|
|
440
|
-
const visit = (entries, path, inside) => {
|
|
455
|
+
const visit = (entries, path, inside, params) => {
|
|
456
|
+
let stamped = params === void 0;
|
|
441
457
|
for (const entry of entries) {
|
|
442
458
|
if ("kind" in entry && "label" in entry && !("block" in entry)) {
|
|
443
|
-
|
|
459
|
+
const marked = inside === void 0 ? entry : { ...entry, block: inside };
|
|
460
|
+
if (!stamped && params !== void 0) {
|
|
461
|
+
expanded.push({ ...marked, params });
|
|
462
|
+
stamped = true;
|
|
463
|
+
} else expanded.push(marked);
|
|
444
464
|
continue;
|
|
445
465
|
}
|
|
446
466
|
const invocation = blockinvocationof(entry);
|
|
@@ -448,7 +468,7 @@ function expandblocks(steps, blocks) {
|
|
|
448
468
|
if (path.includes(invocation.block)) throw new Error(`The block ${invocation.block} recurs inside itself and cannot expand.`);
|
|
449
469
|
const block = byname.get(invocation.block);
|
|
450
470
|
if (!block) throw new Error(`The block ${invocation.block} is not defined in the workflow.`);
|
|
451
|
-
visit(block.steps, [...path, invocation.block], invocation.block);
|
|
471
|
+
visit(block.steps, [...path, invocation.block], invocation.block, invocation.params ?? params);
|
|
452
472
|
}
|
|
453
473
|
};
|
|
454
474
|
visit(steps, [], void 0);
|
|
@@ -816,6 +836,17 @@ async function runworkflow(input) {
|
|
|
816
836
|
if (step.block !== void 0 && step.block !== activeblock) {
|
|
817
837
|
scopes = pushscope(scopes, step.block, scopes[scopes.length - 1].name);
|
|
818
838
|
activeblock = step.block;
|
|
839
|
+
if (step.params) {
|
|
840
|
+
try {
|
|
841
|
+
for (const param of step.params) {
|
|
842
|
+
if (param.default === void 0) continue;
|
|
843
|
+
scopes = setvariable(scopes, param.name, param.kind, coercevariable(param.default, param.kind), input.now);
|
|
844
|
+
}
|
|
845
|
+
} catch (error) {
|
|
846
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
847
|
+
return { run: { ...run, state: "failed", endedat: Date.now(), failreason: `The nested parameter of block ${step.block} failed: ${reason}` }, scopes, log, outputs };
|
|
848
|
+
}
|
|
849
|
+
}
|
|
819
850
|
} else if (step.block === void 0 && activeblock !== void 0) {
|
|
820
851
|
while (scopes.length > 1) scopes = popscope(scopes);
|
|
821
852
|
activeblock = void 0;
|
|
@@ -848,6 +879,27 @@ function dryrunworkflow(input) {
|
|
|
848
879
|
}
|
|
849
880
|
return { run: { ...run, state: "done", cursor: input.record.steps.length, endedat: input.now }, scopes, log };
|
|
850
881
|
}
|
|
882
|
+
function watchdogpass(input) {
|
|
883
|
+
const verdicts = [];
|
|
884
|
+
for (const run of input.runs) {
|
|
885
|
+
if (run.state !== "running") continue;
|
|
886
|
+
const lastcompletedat = input.lastcompletedat[run.id] ?? run.startedat;
|
|
887
|
+
const live = input.liveexecutors.includes(run.id);
|
|
888
|
+
const silence = input.now - lastcompletedat;
|
|
889
|
+
if (!live && input.config.zombiewindow !== void 0 && silence >= input.config.zombiewindow) {
|
|
890
|
+
verdicts.push({ runid: run.id, verdict: "zombie", action: "reap", reason: `The run ${run.id} lost its executor ${silence} ms ago and reaps as a zombie of a browser shutdown at its last checkpoint ${run.cursor}.`, ...lastcompletedat !== run.startedat ? { lastcompletedat } : {} });
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
if (!live) continue;
|
|
894
|
+
if (silence >= input.config.stallthreshold) {
|
|
895
|
+
const action = input.config.action;
|
|
896
|
+
verdicts.push({ runid: run.id, verdict: "stalled", action, reason: `The run ${run.id} completed no step for ${silence} ms past the reviewed threshold and the watchdog recovers it with ${action} at cursor ${run.cursor}.`, ...lastcompletedat !== run.startedat ? { lastcompletedat } : {} });
|
|
897
|
+
continue;
|
|
898
|
+
}
|
|
899
|
+
verdicts.push({ runid: run.id, verdict: "healthy", action: "none", reason: `The run ${run.id} completed its last step ${silence} ms ago and stays healthy.`, ...lastcompletedat !== run.startedat ? { lastcompletedat } : {} });
|
|
900
|
+
}
|
|
901
|
+
return verdicts;
|
|
902
|
+
}
|
|
851
903
|
|
|
852
904
|
// controlflow.ts
|
|
853
905
|
var controlflowkinds = ["condition", "branch", "loop", "repeatuntil", "whileloop", "foreach", "parallel", "trycatch"];
|
|
@@ -4363,6 +4415,178 @@ var sessionmemory = class {
|
|
|
4363
4415
|
}
|
|
4364
4416
|
return history;
|
|
4365
4417
|
}
|
|
4418
|
+
/** Stores one armed trigger rule with its workflow reference; re-arming the same id replaces the rule while the fire history survives. */
|
|
4419
|
+
async addtriggerule(rule) {
|
|
4420
|
+
const rules = (await this.gettriggerules()).filter((entry) => entry.id !== rule.id);
|
|
4421
|
+
await this.adapter.set("triggerules", [rule, ...rules]);
|
|
4422
|
+
}
|
|
4423
|
+
/** Returns every armed trigger rule, newest first. */
|
|
4424
|
+
async gettriggerules() {
|
|
4425
|
+
return await this.adapter.get("triggerules") ?? [];
|
|
4426
|
+
}
|
|
4427
|
+
/** Returns one armed trigger rule by its id. */
|
|
4428
|
+
async gettriggerule(id) {
|
|
4429
|
+
return (await this.gettriggerules()).find((rule) => rule.id === id);
|
|
4430
|
+
}
|
|
4431
|
+
/** Replaces one stored rule after an enable, disable, pause, resume, cooldown or fire bookkeeping change. */
|
|
4432
|
+
async settriggerule(rule) {
|
|
4433
|
+
const rules = await this.gettriggerules();
|
|
4434
|
+
await this.adapter.set("triggerules", rules.map((entry) => entry.id === rule.id ? rule : entry));
|
|
4435
|
+
}
|
|
4436
|
+
/** Replaces every stored rule at once so the session pause and resume suspend and release the whole rule set atomically. */
|
|
4437
|
+
async settriggerules(rules) {
|
|
4438
|
+
return this.adapter.set("triggerules", rules);
|
|
4439
|
+
}
|
|
4440
|
+
/** Removes one armed rule when the user disarms it; the fire history survives for the audit trail. */
|
|
4441
|
+
async removetriggerule(id) {
|
|
4442
|
+
await this.adapter.set("triggerules", (await this.gettriggerules()).filter((rule) => rule.id !== id));
|
|
4443
|
+
}
|
|
4444
|
+
/** Lists every armed rule joined with the name of its composed workflow so the trigger list shows what each rule launches. */
|
|
4445
|
+
async listtriggers() {
|
|
4446
|
+
const rules = await this.gettriggerules();
|
|
4447
|
+
const names = new Map((await this.listworkflows()).map((record2) => [record2.id, record2.name]));
|
|
4448
|
+
return rules.map((rule) => ({ rule, ...names.has(rule.workflowid) ? { workflowname: names.get(rule.workflowid) } : {} }));
|
|
4449
|
+
}
|
|
4450
|
+
/** Records one trigger fire with the reviewed retention window; an absent window keeps every fire record while the rule counters always survive. */
|
|
4451
|
+
async addtriggerfire(fire) {
|
|
4452
|
+
const fires = await this.listtriggerfires();
|
|
4453
|
+
const combined = [fire, ...fires];
|
|
4454
|
+
const retention = (await this.getsettings())?.triggerretention;
|
|
4455
|
+
await this.adapter.set("triggerfires", retention === void 0 ? combined : combined.slice(0, retention));
|
|
4456
|
+
}
|
|
4457
|
+
/** Returns every stored trigger fire record, newest first, optionally filtered to one rule. */
|
|
4458
|
+
async listtriggerfires(ruleid) {
|
|
4459
|
+
const fires = await this.adapter.get("triggerfires") ?? [];
|
|
4460
|
+
return ruleid === void 0 ? fires : fires.filter((fire) => fire.ruleid === ruleid);
|
|
4461
|
+
}
|
|
4462
|
+
/** Stores the pending trigger queue: fires that arrived while the target run was busy or the session paused; the resume drains them through the same gates. */
|
|
4463
|
+
async settriggerqueue(queue) {
|
|
4464
|
+
return this.adapter.set("triggerqueue", queue);
|
|
4465
|
+
}
|
|
4466
|
+
/** Returns the pending trigger queue, oldest first. */
|
|
4467
|
+
async gettriggerqueue() {
|
|
4468
|
+
return await this.adapter.get("triggerqueue") ?? [];
|
|
4469
|
+
}
|
|
4470
|
+
/** Stores one verified webhook payload of a rule; the executor verifies the shared secret and the schema before anything persists. */
|
|
4471
|
+
async addwebhookpayload(ruleid, payload, at) {
|
|
4472
|
+
const stored = await this.adapter.get("webhookpayloads") ?? [];
|
|
4473
|
+
await this.adapter.set("webhookpayloads", [{ ruleid, payload, at }, ...stored]);
|
|
4474
|
+
}
|
|
4475
|
+
/** Returns the stored webhook payloads of one rule, newest first; only secret verified deliveries ever reach this store. */
|
|
4476
|
+
async listwebhookpayloads(ruleid) {
|
|
4477
|
+
const stored = await this.adapter.get("webhookpayloads") ?? [];
|
|
4478
|
+
return stored.filter((entry) => entry.ruleid === ruleid);
|
|
4479
|
+
}
|
|
4480
|
+
/** Stores one manual run preview with its step list so the panel renders it before confirmation. */
|
|
4481
|
+
async addmanualrun(preview) {
|
|
4482
|
+
const runs = (await this.listmanualruns()).filter((entry) => entry.id !== preview.id);
|
|
4483
|
+
await this.adapter.set("manualruns", [preview, ...runs]);
|
|
4484
|
+
}
|
|
4485
|
+
/** Returns every stored manual run preview with its confirmation outcome, newest first. */
|
|
4486
|
+
async listmanualruns() {
|
|
4487
|
+
return await this.adapter.get("manualruns") ?? [];
|
|
4488
|
+
}
|
|
4489
|
+
/** Stores one workflow version record with its change note; saving the same version again replaces its note while older versions survive for the timeline. */
|
|
4490
|
+
async addworkflowversion(version) {
|
|
4491
|
+
const versions = (await this.listworkflowversions()).filter((entry) => !(entry.workflowid === version.workflowid && entry.version === version.version));
|
|
4492
|
+
await this.adapter.set("workflowversions", [version, ...versions]);
|
|
4493
|
+
}
|
|
4494
|
+
/** Returns every stored workflow version record, newest first, optionally filtered to one workflow. */
|
|
4495
|
+
async listworkflowversions(workflowid) {
|
|
4496
|
+
const versions = await this.adapter.get("workflowversions") ?? [];
|
|
4497
|
+
return workflowid === void 0 ? versions : versions.filter((entry) => entry.workflowid === workflowid);
|
|
4498
|
+
}
|
|
4499
|
+
/** Stores one version diff result for the history view. */
|
|
4500
|
+
async addversiondiff(diff) {
|
|
4501
|
+
const diffs = (await this.listversiondiffs()).filter((entry) => !(entry.workflowid === diff.workflowid && entry.from === diff.from && entry.to === diff.to));
|
|
4502
|
+
await this.adapter.set("versiondiffs", [diff, ...diffs]);
|
|
4503
|
+
}
|
|
4504
|
+
/** Returns every stored version diff result, newest first, optionally filtered to one workflow. */
|
|
4505
|
+
async listversiondiffs(workflowid) {
|
|
4506
|
+
const diffs = await this.adapter.get("versiondiffs") ?? [];
|
|
4507
|
+
return workflowid === void 0 ? diffs : diffs.filter((entry) => entry.workflowid === workflowid);
|
|
4508
|
+
}
|
|
4509
|
+
/** Records one run history entry — the outcome, duration and trigger cause of one execution — under the user configured retention window with no code ceiling. */
|
|
4510
|
+
async addrunhistory(entry) {
|
|
4511
|
+
const entries = await this.gethistory();
|
|
4512
|
+
const combined = [entry, ...entries];
|
|
4513
|
+
const retention = (await this.getsettings())?.runhistoryretention;
|
|
4514
|
+
await this.adapter.set("runhistory", retention === void 0 ? combined : combined.slice(0, retention));
|
|
4515
|
+
}
|
|
4516
|
+
/** Returns the stored run history, newest first, filtered by workflow, outcome and time floor; the filters stay user choices. */
|
|
4517
|
+
async gethistory(filter) {
|
|
4518
|
+
const entries = await this.adapter.get("runhistory") ?? [];
|
|
4519
|
+
let filtered = entries;
|
|
4520
|
+
if (filter?.workflowid !== void 0) filtered = filtered.filter((entry) => entry.workflowid === filter.workflowid);
|
|
4521
|
+
if (filter?.outcome !== void 0) filtered = filtered.filter((entry) => entry.outcome === filter.outcome);
|
|
4522
|
+
if (filter?.since !== void 0) filtered = filtered.filter((entry) => entry.endedat >= filter.since);
|
|
4523
|
+
if (filter?.limit !== void 0) filtered = filtered.slice(0, filter.limit);
|
|
4524
|
+
return filtered;
|
|
4525
|
+
}
|
|
4526
|
+
/** Stores the editor layout of one workflow so the canvas reopens exactly as left. */
|
|
4527
|
+
async seteditorlayout(workflowid, layout) {
|
|
4528
|
+
return this.adapter.set(`editorlayout${workflowid}`, layout);
|
|
4529
|
+
}
|
|
4530
|
+
/** Returns the stored editor layout of one workflow. */
|
|
4531
|
+
async geteditorlayout(workflowid) {
|
|
4532
|
+
return await this.adapter.get(`editorlayout${workflowid}`) ?? void 0;
|
|
4533
|
+
}
|
|
4534
|
+
/** Stores the breakpoint step ids of one workflow. */
|
|
4535
|
+
async setworkflowbreakpoints(workflowid, stepids) {
|
|
4536
|
+
return this.adapter.set(`workflowbreakpoints${workflowid}`, stepids);
|
|
4537
|
+
}
|
|
4538
|
+
/** Returns the stored breakpoint step ids of one workflow, oldest first. */
|
|
4539
|
+
async getworkflowbreakpoints(workflowid) {
|
|
4540
|
+
return await this.adapter.get(`workflowbreakpoints${workflowid}`) ?? [];
|
|
4541
|
+
}
|
|
4542
|
+
/** Stores one per site policy override; re-adding the same id replaces its deltas. */
|
|
4543
|
+
async addsiteoverride(override) {
|
|
4544
|
+
const overrides = (await this.listsiteoverrides()).filter((entry) => entry.id !== override.id);
|
|
4545
|
+
await this.adapter.set("siteoverrides", [override, ...overrides]);
|
|
4546
|
+
}
|
|
4547
|
+
/** Returns every stored per site override, newest first, optionally filtered to one workflow. */
|
|
4548
|
+
async listsiteoverrides(workflowid) {
|
|
4549
|
+
const overrides = await this.adapter.get("siteoverrides") ?? [];
|
|
4550
|
+
return workflowid === void 0 ? overrides : overrides.filter((entry) => entry.workflowid === workflowid);
|
|
4551
|
+
}
|
|
4552
|
+
/** Removes one per site override when the user deletes it. */
|
|
4553
|
+
async removesiteoverride(id) {
|
|
4554
|
+
await this.adapter.set("siteoverrides", (await this.listsiteoverrides()).filter((entry) => entry.id !== id));
|
|
4555
|
+
}
|
|
4556
|
+
/** Stores one watchdog event with its recovery outcome; the event history keeps the audit trail of every scan. */
|
|
4557
|
+
async addwatchdogevent(event) {
|
|
4558
|
+
const events = (await this.listwatchdogevents()).filter((entry) => entry.id !== event.id);
|
|
4559
|
+
await this.adapter.set("watchdogevents", [event, ...events]);
|
|
4560
|
+
}
|
|
4561
|
+
/** Returns every stored watchdog event, newest first. */
|
|
4562
|
+
async listwatchdogevents() {
|
|
4563
|
+
return await this.adapter.get("watchdogevents") ?? [];
|
|
4564
|
+
}
|
|
4565
|
+
/** Stores one pending workflow import held for review; approving it later stores the record as runnable. */
|
|
4566
|
+
async addworkflowimport(entry) {
|
|
4567
|
+
const imports = (await this.listworkflowimports()).filter((candidate) => candidate.id !== entry.id);
|
|
4568
|
+
await this.adapter.set("workflowimports", [entry, ...imports]);
|
|
4569
|
+
}
|
|
4570
|
+
/** Returns every pending workflow import, newest first. */
|
|
4571
|
+
async listworkflowimports() {
|
|
4572
|
+
return await this.adapter.get("workflowimports") ?? [];
|
|
4573
|
+
}
|
|
4574
|
+
/** Removes one pending import when the user approves or rejects it. */
|
|
4575
|
+
async removeworkflowimport(id) {
|
|
4576
|
+
await this.adapter.set("workflowimports", (await this.listworkflowimports()).filter((entry) => entry.id !== id));
|
|
4577
|
+
}
|
|
4578
|
+
/** Stores the per workflow background run flags so a workflow keeps running with the panel closed. */
|
|
4579
|
+
async setbackgroundruns(flags) {
|
|
4580
|
+
return this.adapter.set("backgroundruns", flags);
|
|
4581
|
+
}
|
|
4582
|
+
/** Returns the per workflow background run flags. */
|
|
4583
|
+
async getbackgroundruns() {
|
|
4584
|
+
return await this.adapter.get("backgroundruns") ?? {};
|
|
4585
|
+
}
|
|
4586
|
+
/** Removes one stored workflow record version; a rejected import or rollback disappears from the library while every other version survives. */
|
|
4587
|
+
async removeworkflowversion(id, version) {
|
|
4588
|
+
await this.adapter.set("workflowrecords", (await this.getworkflowrecordversions()).filter((entry) => !(entry.id === id && entry.version === version)));
|
|
4589
|
+
}
|
|
4366
4590
|
};
|
|
4367
4591
|
function mediakindof(record2) {
|
|
4368
4592
|
if ("pages" in record2) return "pdf";
|
|
@@ -4899,193 +5123,399 @@ function extractvalues(body, paths) {
|
|
|
4899
5123
|
return fields.map((field) => ({ path: field.path, ...field.value !== void 0 ? { value: field.value } : {}, ...field.missing ? { missing: true } : {} }));
|
|
4900
5124
|
}
|
|
4901
5125
|
|
|
4902
|
-
//
|
|
4903
|
-
var
|
|
4904
|
-
var
|
|
4905
|
-
var
|
|
4906
|
-
|
|
4907
|
-
|
|
5126
|
+
// trigger.ts
|
|
5127
|
+
var triggerkinds = ["visitrule", "urlrule", "menurule", "keyrule", "buttonrule", "cronrule", "intervalrule", "urllistrule", "webhookrule", "eventrule"];
|
|
5128
|
+
var triggerfamilies = ["visit", "url", "menu", "key", "button", "cron", "interval", "urllist", "webhook", "event"];
|
|
5129
|
+
var triggereventcatalog = ["mutate", "focus", "banner", "console", "error", "navigate"];
|
|
5130
|
+
var defaulttriggercooldown = 1e4;
|
|
5131
|
+
function istriggerkind(kind) {
|
|
5132
|
+
return triggerkinds.includes(kind);
|
|
5133
|
+
}
|
|
5134
|
+
function triggerfamilyof(kind) {
|
|
5135
|
+
const index = triggerkinds.indexOf(kind);
|
|
5136
|
+
return index >= 0 ? triggerfamilies[index] : void 0;
|
|
5137
|
+
}
|
|
5138
|
+
function triggerlabel(value) {
|
|
5139
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
5140
|
+
}
|
|
5141
|
+
function positivewindow(value) {
|
|
5142
|
+
if (value === void 0) return void 0;
|
|
5143
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
4908
5144
|
}
|
|
4909
|
-
function
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
5145
|
+
function jitterwindow(value) {
|
|
5146
|
+
if (value === void 0) return void 0;
|
|
5147
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
5148
|
+
}
|
|
5149
|
+
function httpsorigin(value) {
|
|
5150
|
+
if (typeof value !== "string" || !value.trim()) return void 0;
|
|
5151
|
+
try {
|
|
5152
|
+
const parsed = new URL(value.trim());
|
|
5153
|
+
if (parsed.protocol !== "https:") return void 0;
|
|
5154
|
+
return parsed.origin;
|
|
5155
|
+
} catch {
|
|
5156
|
+
return void 0;
|
|
4914
5157
|
}
|
|
4915
|
-
return redacted;
|
|
4916
5158
|
}
|
|
4917
|
-
function
|
|
4918
|
-
if (value
|
|
4919
|
-
|
|
4920
|
-
if (
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
5159
|
+
function webhookfieldof(value) {
|
|
5160
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
5161
|
+
const candidate = value;
|
|
5162
|
+
if (typeof candidate.name !== "string" || !/^[a-z][a-z0-9]*$/i.test(candidate.name)) return void 0;
|
|
5163
|
+
if (candidate.kind !== "string" && candidate.kind !== "number" && candidate.kind !== "boolean") return void 0;
|
|
5164
|
+
if (candidate.required !== void 0 && typeof candidate.required !== "boolean") return void 0;
|
|
5165
|
+
return { name: candidate.name, kind: candidate.kind, ...candidate.required === true ? { required: true } : {} };
|
|
5166
|
+
}
|
|
5167
|
+
function triggerpayloadof(family, value) {
|
|
5168
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
5169
|
+
const candidate = value;
|
|
5170
|
+
if (family === "visit") {
|
|
5171
|
+
if (!Array.isArray(candidate.origins) || candidate.origins.length === 0) return void 0;
|
|
5172
|
+
const origins = candidate.origins.map((origin) => httpsorigin(origin));
|
|
5173
|
+
if (origins.some((origin) => origin === void 0)) return void 0;
|
|
5174
|
+
return { origins: [...new Set(origins)] };
|
|
5175
|
+
}
|
|
5176
|
+
if (family === "url") {
|
|
5177
|
+
if (typeof candidate.pattern !== "string" || !candidate.pattern.trim()) return void 0;
|
|
5178
|
+
if (httpsorigin(candidate.pattern) === void 0) return void 0;
|
|
5179
|
+
return { pattern: candidate.pattern.trim() };
|
|
5180
|
+
}
|
|
5181
|
+
if (family === "menu") {
|
|
5182
|
+
const title = triggerlabel(candidate.title);
|
|
5183
|
+
if (!title) return void 0;
|
|
5184
|
+
return { title };
|
|
5185
|
+
}
|
|
5186
|
+
if (family === "key") {
|
|
5187
|
+
if (typeof candidate.command !== "string" || !/^[a-z][a-z0-9-]*$/.test(candidate.command)) return void 0;
|
|
5188
|
+
if (candidate.key !== void 0 && (typeof candidate.key !== "string" || !candidate.key.trim())) return void 0;
|
|
5189
|
+
return { command: candidate.command, ...candidate.key !== void 0 ? { key: candidate.key } : {} };
|
|
5190
|
+
}
|
|
5191
|
+
if (family === "button") return {};
|
|
5192
|
+
if (family === "cron") {
|
|
5193
|
+
if (typeof candidate.cron !== "string" || !candidate.cron.trim()) return void 0;
|
|
5194
|
+
if (cronparse(candidate.cron) === void 0) return void 0;
|
|
5195
|
+
if (candidate.timezone !== void 0 && (typeof candidate.timezone !== "string" || !timezonevalid(candidate.timezone))) return void 0;
|
|
5196
|
+
return { cron: candidate.cron.trim(), ...candidate.timezone !== void 0 ? { timezone: candidate.timezone } : {} };
|
|
5197
|
+
}
|
|
5198
|
+
if (family === "interval") {
|
|
5199
|
+
const period = positivewindow(candidate.period);
|
|
5200
|
+
if (period === void 0) return void 0;
|
|
5201
|
+
const jitter = jitterwindow(candidate.jitter);
|
|
5202
|
+
if (candidate.jitter !== void 0 && jitter === void 0) return void 0;
|
|
5203
|
+
return { period, ...jitter !== void 0 ? { jitter } : {} };
|
|
5204
|
+
}
|
|
5205
|
+
if (family === "urllist") {
|
|
5206
|
+
if (!Array.isArray(candidate.urls) || candidate.urls.length === 0) return void 0;
|
|
5207
|
+
const urls = candidate.urls.map((url) => httpsorigin(url) === void 0 ? void 0 : url.trim());
|
|
5208
|
+
if (urls.some((url) => url === void 0)) return void 0;
|
|
5209
|
+
return { urls };
|
|
5210
|
+
}
|
|
5211
|
+
if (family === "webhook") {
|
|
5212
|
+
if (typeof candidate.secret !== "string" || !webhooksecretok(candidate.secret)) return void 0;
|
|
5213
|
+
if (!Array.isArray(candidate.schema) || candidate.schema.length === 0) return void 0;
|
|
5214
|
+
const schema = candidate.schema.map((field) => webhookfieldof(field));
|
|
5215
|
+
if (schema.some((field) => field === void 0)) return void 0;
|
|
5216
|
+
const names = schema.map((field) => field.name);
|
|
5217
|
+
if (new Set(names).size !== names.length) return void 0;
|
|
5218
|
+
return { secret: candidate.secret, schema };
|
|
5219
|
+
}
|
|
5220
|
+
const events = candidate.events;
|
|
5221
|
+
if (!Array.isArray(events) || events.length === 0) return void 0;
|
|
5222
|
+
if (!events.every((name) => typeof name === "string" && triggereventcatalog.includes(name))) return void 0;
|
|
5223
|
+
return { events: [...new Set(events)] };
|
|
5224
|
+
}
|
|
5225
|
+
function webhooksecretok(secret) {
|
|
5226
|
+
if (secret.length < 24) return false;
|
|
5227
|
+
if (/^(.)\1+$/.test(secret)) return false;
|
|
5228
|
+
return /[a-z]/i.test(secret) && /\d/.test(secret);
|
|
5229
|
+
}
|
|
5230
|
+
function timezonevalid(timezone) {
|
|
5231
|
+
try {
|
|
5232
|
+
new Intl.DateTimeFormat("en-US", { timeZone: timezone });
|
|
5233
|
+
return true;
|
|
5234
|
+
} catch {
|
|
5235
|
+
return false;
|
|
4938
5236
|
}
|
|
4939
5237
|
}
|
|
4940
|
-
function
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
if (remaining <= 0) {
|
|
4949
|
-
const tag = Array.isArray(item) ? "Array" : item.constructor?.name ?? "Object";
|
|
4950
|
-
return `[${tag}]`;
|
|
4951
|
-
}
|
|
4952
|
-
if (Array.isArray(item)) return `[${item.map((entry) => render(entry, remaining - 1)).join(", ")}]`;
|
|
4953
|
-
const record2 = item;
|
|
4954
|
-
return `{${Object.keys(record2).map((key) => `${key}: ${render(record2[key], remaining - 1)}`).join(", ")}}`;
|
|
4955
|
-
};
|
|
4956
|
-
return render(value, Math.max(0, depth));
|
|
5238
|
+
function armrule(input) {
|
|
5239
|
+
if (typeof input.workflowid !== "string" || !input.workflowid.trim()) return void 0;
|
|
5240
|
+
const payload = triggerpayloadof(input.family, input.payload);
|
|
5241
|
+
if (!payload) return void 0;
|
|
5242
|
+
if (input.cooldown !== void 0 && (typeof input.cooldown !== "number" || !Number.isFinite(input.cooldown) || input.cooldown <= 0)) return void 0;
|
|
5243
|
+
const cooldown = input.cooldown ?? (input.family === "webhook" || input.family === "event" ? defaulttriggercooldown : 0);
|
|
5244
|
+
const label = input.label ?? `The ${input.family} rule of ${input.workflowid}`;
|
|
5245
|
+
return { id: input.id ?? crypto.randomUUID(), kind: input.family, workflowid: input.workflowid, label, ...payload, cooldown, state: { enabled: true, cooldown }, stats: { fires: 0, launches: 0, suppressions: 0 }, createdat: input.now };
|
|
4957
5246
|
}
|
|
4958
|
-
function
|
|
4959
|
-
|
|
4960
|
-
return { level: input.level, text: redactconsoletext(parts.join(" "), input.redact), argkinds: input.args.map((arg) => argkind(arg)), repeat: 1 };
|
|
5247
|
+
function updaterule(rule, patch) {
|
|
5248
|
+
return { ...rule, ...patch.state !== void 0 ? { state: { ...rule.state, ...patch.state } } : {}, ...patch.stats !== void 0 ? { stats: { ...rule.stats, ...patch.stats } } : {} };
|
|
4961
5249
|
}
|
|
4962
|
-
function
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
if (!located) continue;
|
|
4971
|
-
const segments = located.split(":");
|
|
4972
|
-
const column = Number.parseInt(segments.pop() ?? "", 10);
|
|
4973
|
-
const lineno = Number.parseInt(segments.pop() ?? "", 10);
|
|
4974
|
-
const url = segments.join(":");
|
|
4975
|
-
if (!Number.isFinite(lineno) || lineno < 0) continue;
|
|
4976
|
-
const name = body.endsWith(`(${located})`) ? body.slice(0, body.length - located.length - 2).trim() : "";
|
|
4977
|
-
frames.push({ ...name ? { functionname: name } : {}, url, line: lineno, ...Number.isFinite(column) ? { column } : {} });
|
|
5250
|
+
function matchurl(pattern, url) {
|
|
5251
|
+
let parsedpattern;
|
|
5252
|
+
let parsedurl;
|
|
5253
|
+
try {
|
|
5254
|
+
parsedpattern = new URL(pattern);
|
|
5255
|
+
parsedurl = new URL(url);
|
|
5256
|
+
} catch {
|
|
5257
|
+
return false;
|
|
4978
5258
|
}
|
|
4979
|
-
return
|
|
5259
|
+
if (parsedpattern.protocol !== parsedurl.protocol) return false;
|
|
5260
|
+
if (parsedpattern.hostname !== parsedurl.hostname) return false;
|
|
5261
|
+
if (parsedpattern.port !== "" && parsedpattern.port !== parsedurl.port) return false;
|
|
5262
|
+
return globmatch(`${parsedpattern.pathname}${parsedpattern.search}`, `${parsedurl.pathname}${parsedurl.search}`);
|
|
4980
5263
|
}
|
|
4981
|
-
function
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
return { reason: redactconsoletext(input.reason, input.redact), frames: input.stacktext !== void 0 ? stackframes(input.stacktext) : [] };
|
|
5264
|
+
function globmatch(pattern, text2) {
|
|
5265
|
+
const escaped = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5266
|
+
const expression = new RegExp(["^", pattern.split("**").map((segment) => segment.split("*").map(escaped).join("[^/]*")).join(".*"), "$"].join(""));
|
|
5267
|
+
return expression.test(text2);
|
|
4986
5268
|
}
|
|
4987
|
-
function
|
|
4988
|
-
|
|
5269
|
+
function visitmatch(origins, url) {
|
|
5270
|
+
let origin = "";
|
|
5271
|
+
try {
|
|
5272
|
+
origin = new URL(url).origin;
|
|
5273
|
+
} catch {
|
|
5274
|
+
return false;
|
|
5275
|
+
}
|
|
5276
|
+
return origins.includes(origin);
|
|
5277
|
+
}
|
|
5278
|
+
function cronparse(expression) {
|
|
5279
|
+
const fields = expression.trim().split(/\s+/);
|
|
5280
|
+
if (fields.length !== 5) return void 0;
|
|
5281
|
+
const minutes = cronfield(fields[0] ?? "", 0, 59);
|
|
5282
|
+
const hours = cronfield(fields[1] ?? "", 0, 23);
|
|
5283
|
+
const daysofmonth = cronfield(fields[2] ?? "", 1, 31);
|
|
5284
|
+
const months = cronfield(fields[3] ?? "", 1, 12, monthnames);
|
|
5285
|
+
const daysofweek = cronfield(fields[4] ?? "", 0, 7, weekdaynames, true);
|
|
5286
|
+
if (!minutes || !hours || !daysofmonth || !months || !daysofweek) return void 0;
|
|
5287
|
+
return { minutes, hours, daysofmonth, months, daysofweek: [...new Set(daysofweek.map((day) => day % 7))].sort((left, right) => left - right) };
|
|
5288
|
+
}
|
|
5289
|
+
var weekdaynames = { sun: 0, mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6 };
|
|
5290
|
+
var monthnames = { jan: 1, feb: 2, mar: 3, apr: 4, may: 5, jun: 6, jul: 7, aug: 8, sep: 9, oct: 10, nov: 11, dec: 12 };
|
|
5291
|
+
function cronfield(field, min, max, names, sundayseven = false) {
|
|
5292
|
+
const values = /* @__PURE__ */ new Set();
|
|
5293
|
+
for (const part of field.split(",")) {
|
|
5294
|
+
if (!part) return void 0;
|
|
5295
|
+
const [range, stepstring] = part.split("/");
|
|
5296
|
+
const step = stepstring === void 0 ? 1 : Number(stepstring);
|
|
5297
|
+
if (!Number.isInteger(step) || step < 1) return void 0;
|
|
5298
|
+
let low = min;
|
|
5299
|
+
let high = max;
|
|
5300
|
+
if (range !== void 0 && range !== "*") {
|
|
5301
|
+
const bounds = range.split("-");
|
|
5302
|
+
if (bounds.length > 2) return void 0;
|
|
5303
|
+
const lowvalue = cronvalue(bounds[0] ?? "", min, max, names);
|
|
5304
|
+
if (lowvalue === void 0) return void 0;
|
|
5305
|
+
low = lowvalue;
|
|
5306
|
+
high = lowvalue;
|
|
5307
|
+
if (bounds.length === 2) {
|
|
5308
|
+
const highvalue = cronvalue(bounds[1] ?? "", min, max, names);
|
|
5309
|
+
if (highvalue === void 0 || highvalue < lowvalue) return void 0;
|
|
5310
|
+
high = highvalue;
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5313
|
+
for (let value = low; value <= high; value += step) values.add(value);
|
|
5314
|
+
}
|
|
5315
|
+
const list = [...values];
|
|
5316
|
+
if (list.some((value) => value < min || value > max)) return void 0;
|
|
5317
|
+
if (sundayseven && values.has(7)) {
|
|
5318
|
+
values.delete(7);
|
|
5319
|
+
values.add(0);
|
|
5320
|
+
}
|
|
5321
|
+
return [...values].sort((left, right) => left - right);
|
|
4989
5322
|
}
|
|
4990
|
-
function
|
|
4991
|
-
|
|
5323
|
+
function cronvalue(value, min, max, names) {
|
|
5324
|
+
const candidate = names?.[value.toLowerCase()];
|
|
5325
|
+
if (candidate !== void 0) return candidate;
|
|
5326
|
+
if (!/^\d+$/.test(value)) return void 0;
|
|
5327
|
+
const parsed = Number(value);
|
|
5328
|
+
if (parsed < min || parsed > max) return void 0;
|
|
5329
|
+
return parsed;
|
|
4992
5330
|
}
|
|
4993
|
-
function
|
|
4994
|
-
|
|
4995
|
-
const
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5331
|
+
function calendarparts(at, timezone) {
|
|
5332
|
+
if (timezone === void 0) {
|
|
5333
|
+
const date = new Date(at);
|
|
5334
|
+
return { minute: date.getUTCMinutes(), hour: date.getUTCHours(), day: date.getUTCDate(), month: date.getUTCMonth() + 1, weekday: date.getUTCDay() };
|
|
5335
|
+
}
|
|
5336
|
+
const parts = new Intl.DateTimeFormat("en-US", { timeZone: timezone, hourCycle: "h23", minute: "numeric", hour: "numeric", day: "numeric", month: "short", weekday: "short" }).formatToParts(new Date(at));
|
|
5337
|
+
const pick = (type) => parts.find((part) => part.type === type)?.value ?? "";
|
|
5338
|
+
const weekday = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].indexOf(pick("weekday"));
|
|
5339
|
+
const month = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].indexOf(pick("month")) + 1;
|
|
5340
|
+
return { minute: Number(pick("minute")), hour: Number(pick("hour")), day: Number(pick("day")), month, weekday };
|
|
5341
|
+
}
|
|
5342
|
+
function crondaymatch(schedule, parts) {
|
|
5343
|
+
if (!schedule.months.includes(parts.month)) return false;
|
|
5344
|
+
const domfull = schedule.daysofmonth.length === 31;
|
|
5345
|
+
const dowfull = schedule.daysofweek.length === 7;
|
|
5346
|
+
const dommatch = schedule.daysofmonth.includes(parts.day);
|
|
5347
|
+
const dowmatch = schedule.daysofweek.includes(parts.weekday);
|
|
5348
|
+
if (!domfull && !dowfull) return dommatch || dowmatch;
|
|
5349
|
+
if (!domfull) return dommatch;
|
|
5350
|
+
if (!dowfull) return dowmatch;
|
|
5351
|
+
return true;
|
|
5000
5352
|
}
|
|
5001
|
-
function
|
|
5002
|
-
const
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5353
|
+
function cronnext(expression, from, timezone) {
|
|
5354
|
+
const schedule = cronparse(expression);
|
|
5355
|
+
if (!schedule) return void 0;
|
|
5356
|
+
const minute = 6e4;
|
|
5357
|
+
const hour = 60 * minute;
|
|
5358
|
+
const day = 24 * hour;
|
|
5359
|
+
let candidate = Math.floor(from / minute) * minute + minute;
|
|
5360
|
+
const horizon = from + 4 * 366 * day;
|
|
5361
|
+
while (candidate <= horizon) {
|
|
5362
|
+
const parts = calendarparts(candidate, timezone);
|
|
5363
|
+
if (!crondaymatch(schedule, parts)) {
|
|
5364
|
+
candidate += day - parts.hour * hour - parts.minute * minute;
|
|
5007
5365
|
continue;
|
|
5008
5366
|
}
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
previous.repeat += 1;
|
|
5367
|
+
if (!schedule.hours.includes(parts.hour)) {
|
|
5368
|
+
const later = schedule.hours.find((value) => value > parts.hour);
|
|
5369
|
+
candidate += later === void 0 ? day - parts.hour * hour - parts.minute * minute : (later - parts.hour) * hour - parts.minute * minute;
|
|
5013
5370
|
continue;
|
|
5014
5371
|
}
|
|
5015
|
-
|
|
5372
|
+
if (!schedule.minutes.includes(parts.minute)) {
|
|
5373
|
+
const later = schedule.minutes.find((value) => value > parts.minute);
|
|
5374
|
+
candidate += later === void 0 ? (60 - parts.minute) * minute : (later - parts.minute) * minute;
|
|
5375
|
+
continue;
|
|
5376
|
+
}
|
|
5377
|
+
return candidate;
|
|
5016
5378
|
}
|
|
5017
|
-
|
|
5018
|
-
|
|
5379
|
+
return void 0;
|
|
5380
|
+
}
|
|
5381
|
+
function schedulecron(rule, from) {
|
|
5382
|
+
return cronnext(rule.cron, from, rule.timezone);
|
|
5383
|
+
}
|
|
5384
|
+
function scheduleinterval(rule, lastfire, armedat, seed) {
|
|
5385
|
+
const base = (lastfire ?? armedat) + rule.period;
|
|
5386
|
+
const jitter = rule.jitter ?? 0;
|
|
5387
|
+
if (jitter <= 0) return base;
|
|
5388
|
+
return Math.max(0, Math.round(base - jitter / 2 + seededrandom(seed) * jitter));
|
|
5389
|
+
}
|
|
5390
|
+
function listdue(rules, now) {
|
|
5391
|
+
return rules.flatMap((rule) => {
|
|
5392
|
+
if (rule.state.nextfireat === void 0 || rule.state.nextfireat > now) return [];
|
|
5393
|
+
if (!rule.state.enabled || rule.state.pausedat !== void 0) return [];
|
|
5394
|
+
return [{ rule, overdueby: now - rule.state.nextfireat }];
|
|
5395
|
+
});
|
|
5396
|
+
}
|
|
5397
|
+
function applycooldown(rule, now) {
|
|
5398
|
+
const lastfireat = rule.state.lastfireat;
|
|
5399
|
+
if (lastfireat === void 0 || rule.state.cooldown <= 0) return { suppressed: false, remaining: 0 };
|
|
5400
|
+
const remaining = lastfireat + rule.state.cooldown - now;
|
|
5401
|
+
return { suppressed: remaining > 0, remaining: Math.max(0, remaining) };
|
|
5402
|
+
}
|
|
5403
|
+
function evaluatetrigger(input) {
|
|
5404
|
+
const rule = input.rule;
|
|
5405
|
+
if (!rule.state.enabled) return { fired: false, suppressed: "disabled" };
|
|
5406
|
+
if (rule.state.pausedat !== void 0) return { fired: false, suppressed: "paused" };
|
|
5407
|
+
if (!input.workflowreviewed) return { fired: false, suppressed: "unreviewed" };
|
|
5408
|
+
if (input.runactive) return { fired: false, suppressed: "dedupe" };
|
|
5409
|
+
const cooldown = applycooldown(rule, input.now);
|
|
5410
|
+
if (cooldown.suppressed) return { fired: false, suppressed: "cooldown", remaining: cooldown.remaining };
|
|
5411
|
+
const fire = { id: crypto.randomUUID(), ruleid: rule.id, at: input.now, cause: input.cause, ...input.url !== void 0 ? { url: input.url } : {}, ...input.title !== void 0 ? { title: input.title } : {}, ...input.payload !== void 0 ? { payload: input.payload } : {} };
|
|
5412
|
+
return { fired: true, fire };
|
|
5413
|
+
}
|
|
5414
|
+
function queuefire(queue, fire) {
|
|
5415
|
+
if (queue.some((pending) => pending.ruleid === fire.ruleid)) return { queue, queued: false, deduped: true };
|
|
5416
|
+
return { queue: [...queue, fire], queued: true, deduped: false };
|
|
5417
|
+
}
|
|
5418
|
+
async function drainqueue(queue, launch) {
|
|
5419
|
+
let remaining = [...queue];
|
|
5420
|
+
let launched = 0;
|
|
5421
|
+
while (remaining.length > 0) {
|
|
5422
|
+
const fire = remaining[0];
|
|
5423
|
+
try {
|
|
5424
|
+
await launch(fire);
|
|
5425
|
+
} catch {
|
|
5426
|
+
return { launched, remaining };
|
|
5427
|
+
}
|
|
5428
|
+
remaining = remaining.slice(1);
|
|
5429
|
+
launched += 1;
|
|
5430
|
+
}
|
|
5431
|
+
return { launched, remaining };
|
|
5432
|
+
}
|
|
5433
|
+
function runurllist(rule, now) {
|
|
5434
|
+
const urls = rule.urls ?? [];
|
|
5435
|
+
return urls.map((url) => ({ id: crypto.randomUUID(), ruleid: rule.id, at: now, cause: "urllist", url }));
|
|
5436
|
+
}
|
|
5437
|
+
function verifywebhook(input) {
|
|
5438
|
+
if (typeof input.rule.secret !== "string" || !input.rule.secret) return { verified: false, reason: "The webhook rule carries no reviewed secret." };
|
|
5439
|
+
if (!secrectsmatch(input.secret, input.rule.secret)) return { verified: false, reason: "The webhook secret does not match the reviewed secret of the rule." };
|
|
5440
|
+
const payload = input.payload;
|
|
5441
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return { verified: false, reason: "The webhook payload must be a JSON object." };
|
|
5442
|
+
const candidate = payload;
|
|
5443
|
+
for (const field of input.rule.schema ?? []) {
|
|
5444
|
+
const value = candidate[field.name];
|
|
5445
|
+
if (value === void 0) {
|
|
5446
|
+
if (field.required === true) return { verified: false, reason: `The required webhook field ${field.name} is missing.` };
|
|
5447
|
+
continue;
|
|
5448
|
+
}
|
|
5449
|
+
if (typeof value !== field.kind) return { verified: false, reason: `The webhook field ${field.name} is not a ${field.kind}.` };
|
|
5019
5450
|
}
|
|
5020
|
-
|
|
5021
|
-
return { entries: collapsed, flagged };
|
|
5451
|
+
return { verified: true };
|
|
5022
5452
|
}
|
|
5023
|
-
function
|
|
5024
|
-
if (
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
return
|
|
5453
|
+
function secrectsmatch(left, right) {
|
|
5454
|
+
if (left.length !== right.length) return false;
|
|
5455
|
+
let same = true;
|
|
5456
|
+
for (let index = 0; index < left.length; index += 1) if (left.charCodeAt(index) !== right.charCodeAt(index)) same = false;
|
|
5457
|
+
return same;
|
|
5028
5458
|
}
|
|
5029
|
-
function
|
|
5030
|
-
|
|
5031
|
-
for (const level of loglevels) counts[level] = 0;
|
|
5032
|
-
for (const entry of entries) counts[entry.level] = (counts[entry.level] ?? 0) + 1;
|
|
5033
|
-
return counts;
|
|
5459
|
+
function eventrulematches(rule, event) {
|
|
5460
|
+
return (rule.events ?? []).includes(event);
|
|
5034
5461
|
}
|
|
5035
|
-
function
|
|
5036
|
-
|
|
5037
|
-
return
|
|
5462
|
+
function observeevents(rules, event) {
|
|
5463
|
+
if (!triggereventcatalog.includes(event)) return [];
|
|
5464
|
+
return rules.filter((rule) => rule.kind === "event" && rule.state.enabled && rule.state.pausedat === void 0 && eventrulematches(rule, event));
|
|
5038
5465
|
}
|
|
5039
|
-
function
|
|
5040
|
-
|
|
5041
|
-
if (exchange.errorclass === void 0 && exchange.status < 400) return null;
|
|
5042
|
-
return { id: input.id, runid: exchange.runid, stepid: exchange.stepid, url: exchange.url, status: exchange.status, errorclass: exchange.errorclass ?? "httperror", correlationid: exchange.correlationid, at: input.at };
|
|
5466
|
+
function pauseall(rules, now) {
|
|
5467
|
+
return rules.map((rule) => rule.state.enabled && rule.state.pausedat === void 0 ? updaterule(rule, { state: { pausedat: now } }) : rule);
|
|
5043
5468
|
}
|
|
5044
|
-
function
|
|
5045
|
-
|
|
5046
|
-
if (
|
|
5047
|
-
|
|
5048
|
-
|
|
5469
|
+
function resumeall(rules) {
|
|
5470
|
+
return rules.map((rule) => {
|
|
5471
|
+
if (rule.state.pausedat === void 0) return rule;
|
|
5472
|
+
const state = { ...rule.state };
|
|
5473
|
+
delete state.pausedat;
|
|
5474
|
+
return { ...rule, state };
|
|
5475
|
+
});
|
|
5049
5476
|
}
|
|
5050
|
-
function
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
}
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5477
|
+
function manualpreview(record2, now) {
|
|
5478
|
+
return { id: crypto.randomUUID(), workflowid: record2.id, preview: record2.steps.map((step) => ({ stepid: step.id, kind: step.kind, label: step.label, ...step.block !== void 0 ? { block: step.block } : {}, ...controlsummary(step) !== void 0 ? { control: controlsummary(step) } : {} })), at: now };
|
|
5479
|
+
}
|
|
5480
|
+
function confirmmanualrun(preview, confirmed, now) {
|
|
5481
|
+
return { ...preview, confirmed, decidedat: now };
|
|
5482
|
+
}
|
|
5483
|
+
function triggersummary(rule) {
|
|
5484
|
+
return {
|
|
5485
|
+
kind: rule.kind,
|
|
5486
|
+
workflowid: rule.workflowid,
|
|
5487
|
+
label: rule.label,
|
|
5488
|
+
...rule.origins !== void 0 ? { origins: rule.origins } : {},
|
|
5489
|
+
...rule.pattern !== void 0 ? { pattern: rule.pattern } : {},
|
|
5490
|
+
...rule.title !== void 0 ? { title: rule.title } : {},
|
|
5491
|
+
...rule.command !== void 0 ? { command: rule.command } : {},
|
|
5492
|
+
...rule.key !== void 0 ? { key: rule.key } : {},
|
|
5493
|
+
...rule.cron !== void 0 ? { cron: rule.cron, ...rule.timezone !== void 0 ? { timezone: rule.timezone } : {} } : {},
|
|
5494
|
+
...rule.period !== void 0 ? { period: rule.period, ...rule.jitter !== void 0 ? { jitter: rule.jitter } : {} } : {},
|
|
5495
|
+
...rule.urls !== void 0 ? { urls: rule.urls } : {},
|
|
5496
|
+
...rule.events !== void 0 ? { events: rule.events } : {},
|
|
5497
|
+
...rule.schema !== void 0 ? { fields: rule.schema.length } : {},
|
|
5498
|
+
cooldown: rule.state.cooldown,
|
|
5499
|
+
enabled: rule.state.enabled,
|
|
5500
|
+
...rule.state.nextfireat !== void 0 ? { nextfireat: rule.state.nextfireat } : {}
|
|
5501
|
+
};
|
|
5502
|
+
}
|
|
5503
|
+
function ruleorigins(rule) {
|
|
5504
|
+
const origins = /* @__PURE__ */ new Set();
|
|
5505
|
+
for (const origin of rule.origins ?? []) origins.add(origin);
|
|
5506
|
+
if (rule.pattern !== void 0) {
|
|
5507
|
+
const origin = httpsorigin(rule.pattern);
|
|
5508
|
+
if (origin !== void 0) origins.add(origin);
|
|
5079
5509
|
}
|
|
5080
|
-
for (const
|
|
5081
|
-
const
|
|
5082
|
-
|
|
5083
|
-
for (let index = 0; index < missing; index += 1) {
|
|
5084
|
-
lines.push({ kind: "removed", text: line });
|
|
5085
|
-
removed.push(line);
|
|
5086
|
-
}
|
|
5510
|
+
for (const url of rule.urls ?? []) {
|
|
5511
|
+
const origin = httpsorigin(url);
|
|
5512
|
+
if (origin !== void 0) origins.add(origin);
|
|
5087
5513
|
}
|
|
5088
|
-
return
|
|
5514
|
+
return [...origins];
|
|
5515
|
+
}
|
|
5516
|
+
function ruleoriginsgranted(rule, workfloworigins) {
|
|
5517
|
+
const granted = new Set(workfloworigins);
|
|
5518
|
+
return ruleorigins(rule).every((origin) => granted.has(origin));
|
|
5089
5519
|
}
|
|
5090
5520
|
|
|
5091
5521
|
// socketbus.ts
|
|
@@ -5308,38 +5738,228 @@ function polldecision(input) {
|
|
|
5308
5738
|
return { continue: true, reason: "The long poll loop continues.", ...value !== void 0 ? { cursor: value } : {}, next: { ...next, wait: input.cursor.interval } };
|
|
5309
5739
|
}
|
|
5310
5740
|
|
|
5311
|
-
//
|
|
5312
|
-
var
|
|
5313
|
-
var
|
|
5314
|
-
var
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5741
|
+
// runtimeline.ts
|
|
5742
|
+
var timelinekinds = ["watchconsole", "watcherrors", "watchtasks"];
|
|
5743
|
+
var loglevels = ["error", "warn", "info", "log", "debug", "trace"];
|
|
5744
|
+
var timelinesources = ["console", "error", "rejection", "resource", "longtask", "network", "cdp"];
|
|
5745
|
+
function levelrank(level) {
|
|
5746
|
+
return loglevels.indexOf(level);
|
|
5747
|
+
}
|
|
5748
|
+
function redactconsoletext(text2, patterns) {
|
|
5749
|
+
let redacted = text2;
|
|
5750
|
+
for (const pattern of patterns) {
|
|
5751
|
+
if (!pattern) continue;
|
|
5752
|
+
while (redacted.includes(pattern)) redacted = redacted.replace(pattern, "[redacted]");
|
|
5753
|
+
}
|
|
5754
|
+
return redacted;
|
|
5755
|
+
}
|
|
5756
|
+
function argkind(value) {
|
|
5757
|
+
if (value === null) return "null";
|
|
5758
|
+
if (Array.isArray(value)) return "array";
|
|
5759
|
+
if (value instanceof Error) return "error";
|
|
5760
|
+
switch (typeof value) {
|
|
5761
|
+
case "string":
|
|
5762
|
+
return "string";
|
|
5763
|
+
case "number":
|
|
5764
|
+
return "number";
|
|
5765
|
+
case "boolean":
|
|
5766
|
+
return "boolean";
|
|
5767
|
+
case "bigint":
|
|
5768
|
+
return "bigint";
|
|
5769
|
+
case "symbol":
|
|
5770
|
+
return "symbol";
|
|
5771
|
+
case "function":
|
|
5772
|
+
return "function";
|
|
5773
|
+
case "undefined":
|
|
5774
|
+
return "undefined";
|
|
5775
|
+
default:
|
|
5776
|
+
return "object";
|
|
5777
|
+
}
|
|
5778
|
+
}
|
|
5779
|
+
function serializearg(value, depth) {
|
|
5780
|
+
const render = (item, remaining) => {
|
|
5781
|
+
if (item instanceof Error) return `${item.name}: ${item.message}`;
|
|
5782
|
+
if (typeof item === "string") return item;
|
|
5783
|
+
if (typeof item === "function") return `[function ${item.name || "anonymous"}]`;
|
|
5784
|
+
if (typeof item === "bigint") return `${item}n`;
|
|
5785
|
+
if (typeof item === "symbol") return item.toString();
|
|
5786
|
+
if (item === null || item === void 0 || typeof item !== "object") return String(item);
|
|
5787
|
+
if (remaining <= 0) {
|
|
5788
|
+
const tag = Array.isArray(item) ? "Array" : item.constructor?.name ?? "Object";
|
|
5789
|
+
return `[${tag}]`;
|
|
5790
|
+
}
|
|
5791
|
+
if (Array.isArray(item)) return `[${item.map((entry) => render(entry, remaining - 1)).join(", ")}]`;
|
|
5792
|
+
const record2 = item;
|
|
5793
|
+
return `{${Object.keys(record2).map((key) => `${key}: ${render(record2[key], remaining - 1)}`).join(", ")}}`;
|
|
5794
|
+
};
|
|
5795
|
+
return render(value, Math.max(0, depth));
|
|
5796
|
+
}
|
|
5797
|
+
function consolecapture(input) {
|
|
5798
|
+
const parts = input.args.map((arg) => serializearg(arg, input.depth));
|
|
5799
|
+
return { level: input.level, text: redactconsoletext(parts.join(" "), input.redact), argkinds: input.args.map((arg) => argkind(arg)), repeat: 1 };
|
|
5800
|
+
}
|
|
5801
|
+
function stackframes(stacktext) {
|
|
5802
|
+
const frames = [];
|
|
5803
|
+
for (const row of stacktext.split("\n")) {
|
|
5804
|
+
const trimmed = row.trim();
|
|
5805
|
+
if (!trimmed.startsWith("at ")) continue;
|
|
5806
|
+
const body = trimmed.slice(3).trim();
|
|
5807
|
+
const location = body.match(/\(([^()]*:\d+:\d+)\)$/) ?? body.match(/^(.*:\d+:\d+)$/);
|
|
5808
|
+
const located = location?.[1];
|
|
5809
|
+
if (!located) continue;
|
|
5810
|
+
const segments = located.split(":");
|
|
5811
|
+
const column = Number.parseInt(segments.pop() ?? "", 10);
|
|
5812
|
+
const lineno = Number.parseInt(segments.pop() ?? "", 10);
|
|
5813
|
+
const url = segments.join(":");
|
|
5814
|
+
if (!Number.isFinite(lineno) || lineno < 0) continue;
|
|
5815
|
+
const name = body.endsWith(`(${located})`) ? body.slice(0, body.length - located.length - 2).trim() : "";
|
|
5816
|
+
frames.push({ ...name ? { functionname: name } : {}, url, line: lineno, ...Number.isFinite(column) ? { column } : {} });
|
|
5817
|
+
}
|
|
5818
|
+
return frames;
|
|
5819
|
+
}
|
|
5820
|
+
function errorcapture(input) {
|
|
5821
|
+
return { message: redactconsoletext(input.message, input.redact), frames: input.stacktext !== void 0 ? stackframes(input.stacktext) : [], sourceurl: input.sourceurl, line: input.line };
|
|
5822
|
+
}
|
|
5823
|
+
function rejectioncapture(input) {
|
|
5824
|
+
return { reason: redactconsoletext(input.reason, input.redact), frames: input.stacktext !== void 0 ? stackframes(input.stacktext) : [] };
|
|
5825
|
+
}
|
|
5826
|
+
function longtaskcapture(input) {
|
|
5827
|
+
return input.entries.filter((entry) => entry.duration >= input.threshold).map((entry) => ({ duration: Math.round(entry.duration), starttime: Math.round(entry.starttime), attributions: [...entry.attributions] }));
|
|
5828
|
+
}
|
|
5829
|
+
function attachtimeline(input) {
|
|
5830
|
+
return { runid: input.runid, origin: input.origin, stepids: [...input.stepids], attachedat: input.now, entries: [] };
|
|
5831
|
+
}
|
|
5832
|
+
function filterentries(entries, levelset) {
|
|
5833
|
+
return entries.filter((entry) => {
|
|
5834
|
+
const floor = levelset.floors?.[entry.stepid] ?? levelset.floors?.["*"];
|
|
5835
|
+
if (floor !== void 0 && levelrank(entry.level) > levelrank(floor)) return false;
|
|
5836
|
+
if (levelset.sources !== void 0 && levelset.sources.length > 0 && !levelset.sources.includes(entry.source)) return false;
|
|
5837
|
+
return true;
|
|
5838
|
+
});
|
|
5839
|
+
}
|
|
5840
|
+
function spamdetect(entries, rule) {
|
|
5841
|
+
const collapsed = [];
|
|
5842
|
+
const counts = /* @__PURE__ */ new Map();
|
|
5843
|
+
for (const entry of entries) {
|
|
5844
|
+
if (rule.pattern !== "" && !entry.message.includes(rule.pattern)) {
|
|
5845
|
+
collapsed.push({ ...entry, repeat: 1 });
|
|
5846
|
+
continue;
|
|
5847
|
+
}
|
|
5848
|
+
const key = `${entry.level}|${entry.source}|${entry.message}`;
|
|
5849
|
+
const previous = collapsed[collapsed.length - 1];
|
|
5850
|
+
if (previous && previous.repeat !== void 0 && `${previous.level}|${previous.source}|${previous.message}` === key && entry.time - previous.time <= rule.windowsize) {
|
|
5851
|
+
previous.repeat += 1;
|
|
5852
|
+
continue;
|
|
5853
|
+
}
|
|
5854
|
+
collapsed.push({ ...entry, repeat: 1 });
|
|
5855
|
+
}
|
|
5856
|
+
for (const entry of collapsed) {
|
|
5857
|
+
if (entry.repeat > 1) counts.set(`${entry.level}|${entry.source}|${entry.message}`, entry.repeat);
|
|
5858
|
+
}
|
|
5859
|
+
const flagged = [...counts.entries()].filter(([, count]) => count > rule.collapse).map(([key, count]) => ({ message: key.split("|").slice(2).join("|"), count }));
|
|
5860
|
+
return { entries: collapsed, flagged };
|
|
5861
|
+
}
|
|
5862
|
+
function rotatelogs(entries, rule) {
|
|
5863
|
+
if (entries.length <= rule.maxentries) return { kept: [...entries], overflow: [] };
|
|
5864
|
+
const kept = entries.slice(entries.length - rule.maxentries);
|
|
5865
|
+
const overflow = entries.slice(0, entries.length - rule.maxentries);
|
|
5866
|
+
return { kept, overflow };
|
|
5867
|
+
}
|
|
5868
|
+
function timelinecounts(entries) {
|
|
5869
|
+
const counts = {};
|
|
5870
|
+
for (const level of loglevels) counts[level] = 0;
|
|
5871
|
+
for (const entry of entries) counts[entry.level] = (counts[entry.level] ?? 0) + 1;
|
|
5872
|
+
return counts;
|
|
5873
|
+
}
|
|
5874
|
+
function blockingduration(tasks, stepid, window) {
|
|
5875
|
+
const inside = tasks.filter((task) => task.starttime >= window.startedat && task.starttime <= window.endedat);
|
|
5876
|
+
return { stepid, blocking: inside.reduce((total, task) => total + task.duration, 0), tasks: inside.length };
|
|
5877
|
+
}
|
|
5878
|
+
function netfailureentryof(input) {
|
|
5879
|
+
const exchange = input.exchange;
|
|
5880
|
+
if (exchange.errorclass === void 0 && exchange.status < 400) return null;
|
|
5881
|
+
return { id: input.id, runid: exchange.runid, stepid: exchange.stepid, url: exchange.url, status: exchange.status, errorclass: exchange.errorclass ?? "httperror", correlationid: exchange.correlationid, at: input.at };
|
|
5882
|
+
}
|
|
5883
|
+
function watcherdetached(input) {
|
|
5884
|
+
for (const navigation of input.navigations) {
|
|
5885
|
+
if (navigation >= input.startedat && navigation <= input.startedat + input.lifetime) return { detached: true, at: navigation };
|
|
5886
|
+
}
|
|
5887
|
+
return { detached: false };
|
|
5888
|
+
}
|
|
5889
|
+
function consolediff(input) {
|
|
5890
|
+
const base = input.baselines;
|
|
5891
|
+
const target = input.targetlines;
|
|
5892
|
+
const basemap = /* @__PURE__ */ new Map();
|
|
5893
|
+
for (const line of base) basemap.set(line, (basemap.get(line) ?? 0) + 1);
|
|
5894
|
+
const targetmap = /* @__PURE__ */ new Map();
|
|
5895
|
+
for (const line of target) targetmap.set(line, (targetmap.get(line) ?? 0) + 1);
|
|
5896
|
+
const lines = [];
|
|
5897
|
+
const added = [];
|
|
5898
|
+
const removed = [];
|
|
5899
|
+
const repeated = [];
|
|
5900
|
+
for (const [line, count] of targetmap) {
|
|
5901
|
+
const basecount = basemap.get(line) ?? 0;
|
|
5902
|
+
if (basecount === 0) {
|
|
5903
|
+
for (let index = 0; index < count; index += 1) {
|
|
5904
|
+
lines.push({ kind: "added", text: line });
|
|
5905
|
+
added.push(line);
|
|
5906
|
+
}
|
|
5907
|
+
continue;
|
|
5908
|
+
}
|
|
5909
|
+
const share = Math.min(basecount, count);
|
|
5910
|
+
for (let index = 0; index < share; index += 1) {
|
|
5911
|
+
lines.push({ kind: "repeated", text: line, count: share });
|
|
5912
|
+
repeated.push(line);
|
|
5913
|
+
}
|
|
5914
|
+
for (let index = share; index < count; index += 1) {
|
|
5915
|
+
lines.push({ kind: "added", text: line });
|
|
5916
|
+
added.push(line);
|
|
5917
|
+
}
|
|
5918
|
+
}
|
|
5919
|
+
for (const [line, count] of basemap) {
|
|
5920
|
+
const targetcount = targetmap.get(line) ?? 0;
|
|
5921
|
+
const missing = Math.max(0, count - targetcount);
|
|
5922
|
+
for (let index = 0; index < missing; index += 1) {
|
|
5923
|
+
lines.push({ kind: "removed", text: line });
|
|
5924
|
+
removed.push(line);
|
|
5925
|
+
}
|
|
5926
|
+
}
|
|
5927
|
+
return { base: input.baseid, target: input.targetid, lines, added: added.length, removed: removed.length, repeated: repeated.length, at: input.now };
|
|
5928
|
+
}
|
|
5929
|
+
|
|
5930
|
+
// policy.ts
|
|
5931
|
+
var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk", "paginateextract", "resumeextract", "batchdownload", "pausedownload", "resumedownload", "interceptmime", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "cleanupartifacts", "recordscreen", "captureaudio", "downloadimages", "callrest", "callgraphql", "sendmessage", "blockrequest", "mockresponse", "rewriteheaders", "setcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles", "attachcdp", "detachcdp", "cdpcmd", "overridescript", "heapshot", "profilecpu", "capturesourcemaps", "emulatedevice", "emulatenetwork", "emulatelocate", "setuseragent", "overridepermission", "restoresession", "exportsessions", "importsessions", "runworkflow", "visitrule", "urlrule", "menurule", "keyrule", "buttonrule", "cronrule", "intervalrule", "urllistrule", "webhookrule", "eventrule"]);
|
|
5932
|
+
var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction", "capturebodies", "setbreakpoint", "stepcode", "watchexpr", "loop", "repeatuntil", "whileloop", "foreach", "parallel", "trycatch"]);
|
|
5933
|
+
var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures", "shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet", "capturepdf", "captureframe", "readmedia", "readassets", "probestream", "timelapse", "shotcanvas", "convertimage", "makethumbs", "fetchurl", "parsejson", "parsehtml", "opensocket", "waitmessage", "watchrequests", "readheaders", "mapapi", "subscribesse", "longpoll", "extractapi", "readcookies", "watchconsole", "watcherrors", "watchtasks", "watchcdp", "measureflow", "trackmemory", "watchshifts", "traceload", "annotatetrace", "replaytrace", "blackboxscripts", "persiststate", "capturesession", "namedsessions", "diffsessions", "searchsessions", "composeworkflow", "savetemplate", "dryrun", "delay", "waitelement", "compute", "extractvars", "condition", "branch"]);
|
|
5934
|
+
var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
|
|
5935
|
+
var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
|
|
5936
|
+
var targetactions = /* @__PURE__ */ new Set(["inspect", "focus", "click", "type", "scroll", "select", "hover", "clickdeep", "rightclick", "doubleclick", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "highlight", "setattribute", "removeattribute", "waitfor", "shiftclick", "typetime", "appendtext", "setvalue", "typeedit", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "expanddetails", "verifyvisible", "verifyenabled", "pierceshadow", "deriveselector", "fingerprintsection", "submitform", "retryform", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcode", "consentpassword", "scrapetable", "paginateextract", "shotelement", "captureframe", "shotcanvas"]);
|
|
5937
|
+
var valueactions = /* @__PURE__ */ new Set(["presskey", "drag", "drop", "upload", "readattribute", "removeattribute", "waittext", "evaluate", "zoomset", "tabactivate", "tabclose", "tabreload", "windowclose", "windowresize", "tabcreate", "windowcreate", "downloadfile", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "chooseradio", "setslider", "setdate", "setcolor", "followlink", "setfragment", "handleauth", "navintent", "openclipboard", "checksafe", "reopentab", "spanav", "duplicatetab", "pintab", "mutetab", "movetab", "movetabwindow", "searchtabs", "badgetab", "attachmeta", "focuswindow", "maximizewindow", "minimizewindow", "restorewindow", "incognitowindow", "asksubmit", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcode", "consentpassword", "pausedownload", "resumedownload", "verifydownload", "writeclipboard", "quarantinedownload", "scanvirus"]);
|
|
5938
|
+
var tabscommandactions = /* @__PURE__ */ new Set(["querytabs", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "watchtab", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "savelayout", "restorelayout", "findclones", "searchtabs", "badgetab", "attachmeta", "listaudio", "reopenrun", "snapshotsession"]);
|
|
5939
|
+
var formactions = /* @__PURE__ */ new Set(["fillform", "filllabel", "fillplaceholder", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "submitform", "readerrors", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "handoffcaptcha", "fillcard", "fillcode", "consentpassword", "skiphoneypot", "detectlogin", "detecttemplate"]);
|
|
5940
|
+
var datasetactions = /* @__PURE__ */ new Set(["scrapetable", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "importcsv", "looprows", "transformvalues", "deduperows", "paginateextract", "mergepages", "stamplerows", "previewgrid", "streamdisk", "resumeextract", "logprovenance"]);
|
|
5941
|
+
var exportactions = /* @__PURE__ */ new Set(["exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk"]);
|
|
5942
|
+
var filesactions = /* @__PURE__ */ new Set(["batchdownload", "pausedownload", "resumedownload", "verifydownload", "interceptmime", "exportnetlog", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "namecaptures", "cleanupartifacts"]);
|
|
5943
|
+
var captureactions = /* @__PURE__ */ new Set(["shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet"]);
|
|
5944
|
+
var mediaactions = /* @__PURE__ */ new Set(["capturepdf", "recordscreen", "captureaudio", "captureframe", "downloadimages", "shotcanvas", "probestream", "readmedia", "readassets", "timelapse", "convertimage", "makethumbs"]);
|
|
5945
|
+
var httpactions = /* @__PURE__ */ new Set(["fetchurl", "parsejson", "parsehtml", "callrest", "callgraphql"]);
|
|
5946
|
+
var socketactions = /* @__PURE__ */ new Set(["opensocket", "sendmessage", "waitmessage", "subscribesse", "longpoll"]);
|
|
5947
|
+
var netwatchactions = /* @__PURE__ */ new Set(["watchrequests", "readheaders", "capturebodies", "mapapi", "extractapi"]);
|
|
5948
|
+
var controlactions = /* @__PURE__ */ new Set(["blockrequest", "mockresponse", "rewriteheaders", "setcookies", "readcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles"]);
|
|
5949
|
+
var debugactions = /* @__PURE__ */ new Set(["watchconsole", "watcherrors", "watchtasks"]);
|
|
5950
|
+
var cdpactions = /* @__PURE__ */ new Set(["attachcdp", "detachcdp", "cdpcmd", "watchcdp", "setbreakpoint", "stepcode", "watchexpr", "overridescript"]);
|
|
5951
|
+
var profileractions = /* @__PURE__ */ new Set(["measureflow", "heapshot", "trackmemory", "profilecpu", "watchshifts", "traceload", "annotatetrace", "replaytrace", "capturesourcemaps"]);
|
|
5952
|
+
var emulationactions = /* @__PURE__ */ new Set(["emulatedevice", "emulatenetwork", "emulatelocate", "setuseragent", "overridepermission", "blackboxscripts"]);
|
|
5953
|
+
var sessionactions = /* @__PURE__ */ new Set(["persiststate", "capturesession", "restoresession", "namedsessions", "diffsessions", "searchsessions", "exportsessions", "importsessions"]);
|
|
5954
|
+
var workflowactions = /* @__PURE__ */ new Set(["composeworkflow", "savetemplate", "runworkflow", "dryrun", "delay", "waitelement", "compute", "extractvars", "condition", "branch", "loop", "repeatuntil", "whileloop", "foreach", "parallel", "trycatch"]);
|
|
5955
|
+
var triggeractions = /* @__PURE__ */ new Set(["visitrule", "urlrule", "menurule", "keyrule", "buttonrule", "cronrule", "intervalrule", "urllistrule", "webhookrule", "eventrule"]);
|
|
5956
|
+
var credentialheaders = /* @__PURE__ */ new Set(["authorization", "proxy-authorization", "cookie", "cookie2", "set-cookie", "api-key", "x-api-key", "x-auth-token", "x-session-token", "proxy-authorization"]);
|
|
5957
|
+
var fieldkinds = ["text", "email", "phone", "date", "number", "select", "check", "radio", "file", "password", "card", "code"];
|
|
5958
|
+
var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
|
|
5959
|
+
var groupcolors = ["grey", "blue", "red", "yellow", "green", "pink", "purple", "cyan", "orange"];
|
|
5960
|
+
function normalizeendpoint(value) {
|
|
5961
|
+
const endpoint = new URL(value.trim());
|
|
5962
|
+
if (endpoint.protocol !== "https:") throw new Error("Devthink accepts HTTPS endpoints only.");
|
|
5343
5963
|
if (endpoint.username || endpoint.password) throw new Error("Endpoint credentials are not allowed in the URL.");
|
|
5344
5964
|
return { endpoint: endpoint.toString(), origin: endpoint.origin, configuredat: Date.now() };
|
|
5345
5965
|
}
|
|
@@ -5354,6 +5974,9 @@ function issessionkind(kind) {
|
|
|
5354
5974
|
function isworkflowkind(kind) {
|
|
5355
5975
|
return workflowactions.has(kind);
|
|
5356
5976
|
}
|
|
5977
|
+
function istriggeraction(kind) {
|
|
5978
|
+
return triggeractions.has(kind);
|
|
5979
|
+
}
|
|
5357
5980
|
function iswatchkind(kind) {
|
|
5358
5981
|
return watchactions.has(kind);
|
|
5359
5982
|
}
|
|
@@ -7028,6 +7651,79 @@ function workflowgate(input) {
|
|
|
7028
7651
|
}
|
|
7029
7652
|
return { allowed: true };
|
|
7030
7653
|
}
|
|
7654
|
+
function validatetriggergrammar(step, options) {
|
|
7655
|
+
const family = triggerfamilyof(step.kind);
|
|
7656
|
+
if (family === void 0) return { allowed: false, reason: "The trigger step is not a reviewed trigger kind." };
|
|
7657
|
+
if (typeof options.workflowid !== "string" || !options.workflowid.trim()) return { allowed: false, reason: "Every trigger rule needs the reviewed id of the composed workflow it launches." };
|
|
7658
|
+
if (options.reviewed !== true) return { allowed: false, reason: "Every trigger rule needs the explicit arm review with its match fields and bound workflow shown before it arms." };
|
|
7659
|
+
if (options.label !== void 0 && (typeof options.label !== "string" || !options.label.trim())) return { allowed: false, reason: "The reviewed trigger label must be a non-empty string." };
|
|
7660
|
+
if (options.cooldown !== void 0 && (typeof options.cooldown !== "number" || !Number.isFinite(options.cooldown) || options.cooldown <= 0)) return { allowed: false, reason: "The reviewed cooldown window must be a positive number of milliseconds with no code ceiling; the webhook and event families keep the documented default when the review configures none." };
|
|
7661
|
+
const payload = options.rule;
|
|
7662
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return { allowed: false, reason: `The ${step.kind} step needs its reviewed rule payload in options.` };
|
|
7663
|
+
if (triggerpayloadof(family, payload) === void 0) {
|
|
7664
|
+
if (family === "visit") return { allowed: false, reason: "The visit rule needs a non-empty reviewed list of HTTPS origins it fires on." };
|
|
7665
|
+
if (family === "url") return { allowed: false, reason: "The url rule needs a reviewed HTTPS glob url pattern; `*` spans one path segment and `**` spans across segments." };
|
|
7666
|
+
if (family === "menu") return { allowed: false, reason: "The menu rule needs a reviewed non-empty context menu entry title." };
|
|
7667
|
+
if (family === "key") return { allowed: false, reason: "The keyboard shortcut rule needs a reviewed lowercase command name and an optional suggested key binding." };
|
|
7668
|
+
if (family === "cron") return { allowed: false, reason: "The cron rule needs a reviewed five field cron expression of minutes, hours, days, months and weekdays with named weekdays and months and an optional resolvable timezone; unparseable schedules are refused." };
|
|
7669
|
+
if (family === "interval") return { allowed: false, reason: "The interval rule needs a reviewed positive period in milliseconds with an optional zero or positive jitter window." };
|
|
7670
|
+
if (family === "urllist") return { allowed: false, reason: "The url list rule needs a reviewed non-empty list of HTTPS urls its workflow runs across." };
|
|
7671
|
+
if (family === "webhook") return { allowed: false, reason: `The webhook rule needs a reviewed shared secret of at least twenty four characters mixing letters and digits and a non-empty payload schema of named string, number or boolean fields.` };
|
|
7672
|
+
if (family === "event") return { allowed: false, reason: `The page event rule needs a reviewed non-empty list of event names of the observed event catalog: ${triggereventcatalog.join(", ")}.` };
|
|
7673
|
+
return { allowed: false, reason: "The trigger rule payload does not follow its family grammar." };
|
|
7674
|
+
}
|
|
7675
|
+
if (family === "cron") {
|
|
7676
|
+
const candidate = payload;
|
|
7677
|
+
if (typeof candidate.cron === "string" && cronparse(candidate.cron) === void 0) return { allowed: false, reason: "The cron expression does not parse as a five field schedule and is refused." };
|
|
7678
|
+
}
|
|
7679
|
+
if (family === "webhook") {
|
|
7680
|
+
const candidate = payload;
|
|
7681
|
+
if (typeof candidate.secret === "string" && !webhooksecretok(candidate.secret)) return { allowed: false, reason: "The webhook shared secret must hold at least twenty four characters mixing letters and digits; the entropy floor is a floor, never a cap." };
|
|
7682
|
+
}
|
|
7683
|
+
const armed = armrule({ family, workflowid: options.workflowid, ...typeof options.label === "string" && options.label.trim() ? { label: options.label } : {}, payload, ...typeof options.cooldown === "number" ? { cooldown: options.cooldown } : {}, now: 0 });
|
|
7684
|
+
if (armed === void 0) return { allowed: false, reason: "The trigger rule payload does not arm as a reviewed rule." };
|
|
7685
|
+
return { allowed: true };
|
|
7686
|
+
}
|
|
7687
|
+
function triggergate(input) {
|
|
7688
|
+
const gate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now: input.now, action: "arm the trigger rule" });
|
|
7689
|
+
if (!gate.allowed) return gate;
|
|
7690
|
+
if (!input.plan || input.plan.state !== "approved") return { allowed: false, reason: "Trigger rules need the approved plan review before they arm." };
|
|
7691
|
+
let triggeroptions = {};
|
|
7692
|
+
try {
|
|
7693
|
+
triggeroptions = parseoptions(input.step);
|
|
7694
|
+
} catch {
|
|
7695
|
+
triggeroptions = {};
|
|
7696
|
+
}
|
|
7697
|
+
if (triggeroptions.reviewed !== true) return { allowed: false, reason: "Every trigger rule needs the explicit arm review with its match fields and bound workflow shown before it arms." };
|
|
7698
|
+
return { allowed: true };
|
|
7699
|
+
}
|
|
7700
|
+
function triggerorigins(step) {
|
|
7701
|
+
let triggeroptions = {};
|
|
7702
|
+
try {
|
|
7703
|
+
triggeroptions = parseoptions(step);
|
|
7704
|
+
} catch {
|
|
7705
|
+
return [];
|
|
7706
|
+
}
|
|
7707
|
+
const family = triggerfamilyof(step.kind);
|
|
7708
|
+
if (family === void 0) return [];
|
|
7709
|
+
const armed = armrule({ family, workflowid: typeof triggeroptions.workflowid === "string" ? triggeroptions.workflowid : "", payload: triggeroptions.rule, ...typeof triggeroptions.cooldown === "number" ? { cooldown: triggeroptions.cooldown } : {}, now: 0 });
|
|
7710
|
+
if (armed === void 0) return [];
|
|
7711
|
+
const origins = [];
|
|
7712
|
+
for (const origin of armed.origins ?? []) origins.push(origin);
|
|
7713
|
+
if (armed.pattern !== void 0) {
|
|
7714
|
+
try {
|
|
7715
|
+
origins.push(new URL(armed.pattern).origin);
|
|
7716
|
+
} catch {
|
|
7717
|
+
}
|
|
7718
|
+
}
|
|
7719
|
+
for (const url of armed.urls ?? []) {
|
|
7720
|
+
try {
|
|
7721
|
+
origins.push(new URL(url).origin);
|
|
7722
|
+
} catch {
|
|
7723
|
+
}
|
|
7724
|
+
}
|
|
7725
|
+
return [...new Set(origins)];
|
|
7726
|
+
}
|
|
7031
7727
|
function dryrunprojection(step) {
|
|
7032
7728
|
if (iscontrolflowkind(step.kind)) {
|
|
7033
7729
|
for (const child of controlsteps(step)) {
|
|
@@ -7624,6 +8320,10 @@ function validatestep(step, origin) {
|
|
|
7624
8320
|
const workflowcheck = validateworkflowgrammar(step, options);
|
|
7625
8321
|
if (!workflowcheck.allowed) return workflowcheck;
|
|
7626
8322
|
}
|
|
8323
|
+
if (istriggeraction(step.kind)) {
|
|
8324
|
+
const triggercheck = validatetriggergrammar(step, options);
|
|
8325
|
+
if (!triggercheck.allowed) return triggercheck;
|
|
8326
|
+
}
|
|
7627
8327
|
if (step.kind === "tabcreate") {
|
|
7628
8328
|
if (options.background !== void 0 && typeof options.background !== "boolean") return { allowed: false, reason: "The reviewed background flag must be a boolean." };
|
|
7629
8329
|
if (options.window !== void 0 && (typeof options.window !== "number" || !Number.isInteger(options.window) || options.window < 0)) return { allowed: false, reason: "The reviewed target window id must be a non-negative integer." };
|
|
@@ -7818,6 +8518,10 @@ function canexecute(input) {
|
|
|
7818
8518
|
const workflowgatecheck = workflowgate({ session: input.session, plan: input.plan, step: input.step, tabid: input.tabid, origin: input.origin, now });
|
|
7819
8519
|
if (!workflowgatecheck.allowed) return workflowgatecheck;
|
|
7820
8520
|
}
|
|
8521
|
+
if (istriggeraction(input.step.kind)) {
|
|
8522
|
+
const triggergatecheck = triggergate({ session: input.session, plan: input.plan, step: input.step, tabid: input.tabid, origin: input.origin, now });
|
|
8523
|
+
if (!triggergatecheck.allowed) return triggergatecheck;
|
|
8524
|
+
}
|
|
7821
8525
|
if (iscontrolkind(input.step.kind)) {
|
|
7822
8526
|
const controlgate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: "control the network" });
|
|
7823
8527
|
if (!controlgate.allowed) return controlgate;
|
|
@@ -7899,9 +8603,137 @@ function canexecute(input) {
|
|
|
7899
8603
|
}
|
|
7900
8604
|
return validatestep(input.step, input.origin);
|
|
7901
8605
|
}
|
|
8606
|
+
function reviewedkinds() {
|
|
8607
|
+
return [...allowedactions].sort();
|
|
8608
|
+
}
|
|
8609
|
+
function editorsavegate(input) {
|
|
8610
|
+
const gate = sessiongate({ session: input.session, tabid: input.session?.tabid ?? 0, origin: input.session?.origin ?? "https://example.com", now: input.now, action: "save the workflow editor canvas" });
|
|
8611
|
+
if (!gate.allowed) return gate;
|
|
8612
|
+
if (!input.plan || input.plan.state !== "approved") return { allowed: false, reason: "Editor saves need the approved plan review before a new workflow version composes." };
|
|
8613
|
+
const model = input.model;
|
|
8614
|
+
if (typeof model.name !== "string" || !model.name.trim()) return { allowed: false, reason: "The workflow name of the canvas must be a non-empty string." };
|
|
8615
|
+
if (typeof model.version !== "number" || !Number.isInteger(model.version) || model.version < 1) return { allowed: false, reason: "The workflow version of the canvas must be a positive integer." };
|
|
8616
|
+
if (!Array.isArray(model.origins) || model.origins.length === 0) return { allowed: false, reason: "The canvas needs at least one granted HTTPS origin." };
|
|
8617
|
+
const ids = /* @__PURE__ */ new Set();
|
|
8618
|
+
for (const node of model.nodes) {
|
|
8619
|
+
if (node.step === void 0 === (node.invocation === void 0)) return { allowed: false, reason: "Every canvas node must be exactly one workflow step or one block invocation." };
|
|
8620
|
+
const id = node.id ?? (node.step !== void 0 ? node.step.id : node.invocation.block);
|
|
8621
|
+
if (!id || ids.has(id)) return { allowed: false, reason: `The canvas node id ${id || "(empty)"} must be unique.` };
|
|
8622
|
+
ids.add(id);
|
|
8623
|
+
}
|
|
8624
|
+
const reachable = /* @__PURE__ */ new Set();
|
|
8625
|
+
for (const node of model.nodes) {
|
|
8626
|
+
if (node.step !== void 0) {
|
|
8627
|
+
reachable.add(node.step.id);
|
|
8628
|
+
continue;
|
|
8629
|
+
}
|
|
8630
|
+
const walk = (entries) => {
|
|
8631
|
+
for (const entry of entries) {
|
|
8632
|
+
if (typeof entry.id === "string" && typeof entry.kind === "string") {
|
|
8633
|
+
reachable.add(entry.id);
|
|
8634
|
+
continue;
|
|
8635
|
+
}
|
|
8636
|
+
if (typeof entry.block === "string") {
|
|
8637
|
+
const nested = model.blocks.find((candidate) => candidate.name === entry.block);
|
|
8638
|
+
if (nested) walk(nested.steps);
|
|
8639
|
+
}
|
|
8640
|
+
}
|
|
8641
|
+
};
|
|
8642
|
+
const block = model.blocks.find((candidate) => candidate.name === node.invocation.block);
|
|
8643
|
+
if (!block) return { allowed: false, reason: `The block ${node.invocation.block} of the canvas has no definition.` };
|
|
8644
|
+
walk(block.steps);
|
|
8645
|
+
}
|
|
8646
|
+
let order = 0;
|
|
8647
|
+
const positionof = /* @__PURE__ */ new Map();
|
|
8648
|
+
for (const node of model.nodes) {
|
|
8649
|
+
if (node.step !== void 0) {
|
|
8650
|
+
positionof.set(node.step.id, order);
|
|
8651
|
+
order += 1;
|
|
8652
|
+
continue;
|
|
8653
|
+
}
|
|
8654
|
+
const walk = (entries) => {
|
|
8655
|
+
for (const entry of entries) {
|
|
8656
|
+
if (typeof entry.id === "string" && typeof entry.kind === "string") {
|
|
8657
|
+
positionof.set(entry.id, order);
|
|
8658
|
+
order += 1;
|
|
8659
|
+
continue;
|
|
8660
|
+
}
|
|
8661
|
+
if (typeof entry.block === "string") {
|
|
8662
|
+
const nested = model.blocks.find((candidate) => candidate.name === entry.block);
|
|
8663
|
+
if (nested) walk(nested.steps);
|
|
8664
|
+
}
|
|
8665
|
+
}
|
|
8666
|
+
};
|
|
8667
|
+
walk(model.blocks.find((candidate) => candidate.name === node.invocation.block).steps);
|
|
8668
|
+
}
|
|
8669
|
+
for (const edge of model.edges) {
|
|
8670
|
+
if (!reachable.has(edge.from)) return { allowed: false, reason: `The canvas edge of ${edge.variable} references the unknown source step ${edge.from}.` };
|
|
8671
|
+
if (!reachable.has(edge.to)) return { allowed: false, reason: `The canvas edge of ${edge.variable} references the unknown target step ${edge.to}.` };
|
|
8672
|
+
if ((positionof.get(edge.from) ?? -1) >= (positionof.get(edge.to) ?? -1)) return { allowed: false, reason: `The canvas edge of ${edge.variable} runs backwards and would form a cycle.` };
|
|
8673
|
+
}
|
|
8674
|
+
return { allowed: true };
|
|
8675
|
+
}
|
|
8676
|
+
function runreviewgranted(record2) {
|
|
8677
|
+
if (record2.reviewstate === "pending") return { allowed: false, reason: "The workflow stays unreviewed: the import or rollback review must approve its expanded step list before any run." };
|
|
8678
|
+
return { allowed: true };
|
|
8679
|
+
}
|
|
8680
|
+
var overrideknobs = ["loopbound", "stepms", "runms", "waitms", "delaybase"];
|
|
8681
|
+
function validatesiteoverride(override) {
|
|
8682
|
+
if (typeof override.pattern !== "string" || !override.pattern.startsWith("https://") || !/[a-z0-9.-]+/i.test(override.pattern.slice(8))) return { allowed: false, reason: "The override pattern must be an https origin or a `*` subdomain glob of one." };
|
|
8683
|
+
if (!override.pattern.includes("*")) {
|
|
8684
|
+
try {
|
|
8685
|
+
if (new URL(override.pattern).origin !== override.pattern) return { allowed: false, reason: "The override pattern must be a bare https origin or a `*` subdomain glob, never a path." };
|
|
8686
|
+
} catch {
|
|
8687
|
+
return { allowed: false, reason: "The override pattern must parse as an https origin or a `*` subdomain glob of one." };
|
|
8688
|
+
}
|
|
8689
|
+
}
|
|
8690
|
+
for (const [knob, delta] of Object.entries(override.deltas)) {
|
|
8691
|
+
if (!overrideknobs.includes(knob)) return { allowed: false, reason: `The override knob ${knob} is not one of the reviewed knobs: ${overrideknobs.join(", ")}.` };
|
|
8692
|
+
if (typeof delta !== "number" || !Number.isFinite(delta) || delta <= 0) return { allowed: false, reason: `The override delta of ${knob} must be a positive user value with no code ceiling.` };
|
|
8693
|
+
}
|
|
8694
|
+
return { allowed: true };
|
|
8695
|
+
}
|
|
8696
|
+
function exportcontentreview(file) {
|
|
8697
|
+
const secretkeys = /(secret|token|apikey|api_key|password|authorization|credential)/i;
|
|
8698
|
+
const scan = (label, options) => {
|
|
8699
|
+
if (options === void 0) return void 0;
|
|
8700
|
+
let payload;
|
|
8701
|
+
try {
|
|
8702
|
+
payload = JSON.parse(options);
|
|
8703
|
+
} catch {
|
|
8704
|
+
return void 0;
|
|
8705
|
+
}
|
|
8706
|
+
const walk = (value, path) => {
|
|
8707
|
+
if (!value || typeof value !== "object") return void 0;
|
|
8708
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
8709
|
+
if (secretkeys.test(key)) return { allowed: false, reason: `The export of ${label} carries the secret field ${path}${key} and secrets never leave the browser.` };
|
|
8710
|
+
const nested = walk(entry, `${path}${key}.`);
|
|
8711
|
+
if (nested !== void 0) return nested;
|
|
8712
|
+
}
|
|
8713
|
+
return void 0;
|
|
8714
|
+
};
|
|
8715
|
+
return walk(payload, "");
|
|
8716
|
+
};
|
|
8717
|
+
for (const step of file.workflow.steps) {
|
|
8718
|
+
const refusal = scan(`the step ${step.id}`, step.options);
|
|
8719
|
+
if (refusal !== void 0) return refusal;
|
|
8720
|
+
}
|
|
8721
|
+
for (const template of file.templates) {
|
|
8722
|
+
const refusal = scan(`the template ${template.name}`, template.step.options);
|
|
8723
|
+
if (refusal !== void 0) return refusal;
|
|
8724
|
+
}
|
|
8725
|
+
return { allowed: true };
|
|
8726
|
+
}
|
|
8727
|
+
function watchdogconfigvalid(config) {
|
|
8728
|
+
if (typeof config.enabled !== "boolean") return { allowed: false, reason: "The watchdog enabled flag must be a boolean." };
|
|
8729
|
+
if (typeof config.stallthreshold !== "number" || !Number.isFinite(config.stallthreshold) || config.stallthreshold <= 0) return { allowed: false, reason: "The watchdog stall threshold must be a positive number of milliseconds with no code ceiling." };
|
|
8730
|
+
if (!["retry", "pause", "cancel"].includes(config.action)) return { allowed: false, reason: "The watchdog recovery action must be retry, pause or cancel." };
|
|
8731
|
+
if (config.zombiewindow !== void 0 && (typeof config.zombiewindow !== "number" || !Number.isFinite(config.zombiewindow) || config.zombiewindow <= 0)) return { allowed: false, reason: "The watchdog zombie window, when configured, must be a positive number of milliseconds with no code ceiling." };
|
|
8732
|
+
return { allowed: true };
|
|
8733
|
+
}
|
|
7902
8734
|
|
|
7903
8735
|
// version.ts
|
|
7904
|
-
var packageversion = "1.1.
|
|
8736
|
+
var packageversion = "1.1.53";
|
|
7905
8737
|
|
|
7906
8738
|
// types.ts
|
|
7907
8739
|
var protocolversion = packageversion;
|
|
@@ -8140,6 +8972,25 @@ function parseproposal(value, origin, grants) {
|
|
|
8140
8972
|
}
|
|
8141
8973
|
if (step.kind === "runworkflow" && workflowoptions.reviewed !== true) throw new Error("Workflow runs without the explicit run review of the expanded step list are refused.");
|
|
8142
8974
|
}
|
|
8975
|
+
if (istriggeraction(step.kind)) {
|
|
8976
|
+
let triggeroptions = {};
|
|
8977
|
+
try {
|
|
8978
|
+
triggeroptions = parseoptions(step);
|
|
8979
|
+
} catch {
|
|
8980
|
+
triggeroptions = {};
|
|
8981
|
+
}
|
|
8982
|
+
if (triggeroptions.reviewed !== true) throw new Error("Trigger rules without the explicit arm review of their match fields and bound workflow are refused.");
|
|
8983
|
+
for (const ruleorigin of triggerorigins(step)) {
|
|
8984
|
+
const granted = covered.some((pattern) => {
|
|
8985
|
+
try {
|
|
8986
|
+
return new URL(ruleorigin).origin === new URL(pattern).origin;
|
|
8987
|
+
} catch {
|
|
8988
|
+
return false;
|
|
8989
|
+
}
|
|
8990
|
+
});
|
|
8991
|
+
if (!granted) throw new Error(`The trigger on ${ruleorigin} stays outside the grants.`);
|
|
8992
|
+
}
|
|
8993
|
+
}
|
|
8143
8994
|
const evaluation = validatestep(step, origin);
|
|
8144
8995
|
if (!evaluation.allowed) throw new Error(evaluation.reason);
|
|
8145
8996
|
const target = outboundtarget(step);
|
|
@@ -8282,7 +9133,7 @@ function requestbody(input) {
|
|
|
8282
9133
|
return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation, capabilities: input.capabilities });
|
|
8283
9134
|
}
|
|
8284
9135
|
function outcomeresponse(input) {
|
|
8285
|
-
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {}, ...input.network ? { network: input.network } : {}, ...input.control ? { control: input.control } : {}, ...input.timeline ? { timeline: input.timeline } : {}, ...input.cdp ? { cdp: input.cdp } : {}, ...input.profile ? { profile: input.profile } : {}, ...input.emulation ? { emulation: input.emulation } : {}, ...input.session ? { session: input.session } : {}, ...input.workflow ? { workflow: { runid: input.workflow.runid, state: input.workflow.state, ...input.workflow.dryrun === true ? { dryrun: true } : {}, produced: input.workflow.produced, consumed: input.workflow.consumed, ...input.workflow.timeout !== void 0 ? { timeout: input.workflow.timeout } : {}, ...input.workflow.retry !== void 0 ? { retry: input.workflow.retry } : {} } } : {} });
|
|
9136
|
+
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {}, ...input.network ? { network: input.network } : {}, ...input.control ? { control: input.control } : {}, ...input.timeline ? { timeline: input.timeline } : {}, ...input.cdp ? { cdp: input.cdp } : {}, ...input.profile ? { profile: input.profile } : {}, ...input.emulation ? { emulation: input.emulation } : {}, ...input.session ? { session: input.session } : {}, ...input.workflow ? { workflow: { runid: input.workflow.runid, state: input.workflow.state, ...input.workflow.dryrun === true ? { dryrun: true } : {}, produced: input.workflow.produced, consumed: input.workflow.consumed, ...input.workflow.timeout !== void 0 ? { timeout: input.workflow.timeout } : {}, ...input.workflow.retry !== void 0 ? { retry: input.workflow.retry } : {} } } : {}, ...input.trigger ? { trigger: { ruleid: input.trigger.ruleid, kind: input.trigger.kind, enabled: input.trigger.enabled, ...input.trigger.nextfireat !== void 0 ? { nextfireat: input.trigger.nextfireat } : {} } } : {} });
|
|
8286
9137
|
}
|
|
8287
9138
|
function mapresponse(input) {
|
|
8288
9139
|
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, map: input.map });
|
|
@@ -8434,8 +9285,789 @@ function sessionreport(input) {
|
|
|
8434
9285
|
function workflowreport(input) {
|
|
8435
9286
|
return { version: protocolversion, workflows: input.workflows, runs: input.runs, templates: input.templates, log: input.log ?? [], scopes: input.scopes ?? [], provenance: input.provenance ?? [], control: input.control ?? [] };
|
|
8436
9287
|
}
|
|
9288
|
+
function triggerlist(input) {
|
|
9289
|
+
const names = new Map(input.workflows.map((record2) => [record2.id, record2.name]));
|
|
9290
|
+
const rules = input.rules.map((rule) => {
|
|
9291
|
+
const workflowname = names.get(rule.workflowid);
|
|
9292
|
+
return {
|
|
9293
|
+
id: rule.id,
|
|
9294
|
+
kind: rule.kind,
|
|
9295
|
+
workflowid: rule.workflowid,
|
|
9296
|
+
...workflowname !== void 0 ? { workflowname } : {},
|
|
9297
|
+
label: rule.label,
|
|
9298
|
+
enabled: rule.state.enabled,
|
|
9299
|
+
...rule.state.pausedat !== void 0 ? { paused: true } : {},
|
|
9300
|
+
cooldown: rule.state.cooldown,
|
|
9301
|
+
...rule.state.lastfireat !== void 0 ? { lastfireat: rule.state.lastfireat } : {},
|
|
9302
|
+
...rule.state.nextfireat !== void 0 ? { nextfireat: rule.state.nextfireat } : {},
|
|
9303
|
+
fires: rule.stats.fires,
|
|
9304
|
+
launches: rule.stats.launches,
|
|
9305
|
+
suppressions: rule.stats.suppressions,
|
|
9306
|
+
summary: triggersummaryof(rule)
|
|
9307
|
+
};
|
|
9308
|
+
});
|
|
9309
|
+
return { version: protocolversion, rules, queued: (input.queue ?? []).length };
|
|
9310
|
+
}
|
|
9311
|
+
function triggersummaryof(rule) {
|
|
9312
|
+
const summary = { kind: rule.kind, workflowid: rule.workflowid };
|
|
9313
|
+
if (rule.origins !== void 0) summary.origins = rule.origins;
|
|
9314
|
+
if (rule.pattern !== void 0) summary.pattern = rule.pattern;
|
|
9315
|
+
if (rule.title !== void 0) summary.title = rule.title;
|
|
9316
|
+
if (rule.command !== void 0) summary.command = rule.command;
|
|
9317
|
+
if (rule.key !== void 0) summary.key = rule.key;
|
|
9318
|
+
if (rule.cron !== void 0) summary.cron = rule.cron;
|
|
9319
|
+
if (rule.timezone !== void 0) summary.timezone = rule.timezone;
|
|
9320
|
+
if (rule.period !== void 0) summary.period = rule.period;
|
|
9321
|
+
if (rule.jitter !== void 0) summary.jitter = rule.jitter;
|
|
9322
|
+
if (rule.urls !== void 0) summary.urls = rule.urls;
|
|
9323
|
+
if (rule.events !== void 0) summary.events = rule.events;
|
|
9324
|
+
if (rule.schema !== void 0) summary.fields = rule.schema.length;
|
|
9325
|
+
return summary;
|
|
9326
|
+
}
|
|
9327
|
+
function triggerfired(input) {
|
|
9328
|
+
return { version: protocolversion, triggerfired: { fireid: input.fire.id, ruleid: input.fire.ruleid, workflowid: input.workflowid, at: input.fire.at, cause: input.fire.cause, ...input.fire.url !== void 0 ? { url: input.fire.url } : {}, ...input.fire.title !== void 0 ? { title: input.fire.title } : {}, ...input.runid !== void 0 ? { runid: input.runid } : {} } };
|
|
9329
|
+
}
|
|
9330
|
+
function manualrunpreview(input) {
|
|
9331
|
+
return { version: protocolversion, manualrun: input.preview, ...input.workflowname !== void 0 ? { workflowname: input.workflowname } : {} };
|
|
9332
|
+
}
|
|
9333
|
+
var workflowfileversion = 1;
|
|
9334
|
+
function editorstate(input) {
|
|
9335
|
+
const editor = { versions: input.versions, diffs: input.diffs ?? [], history: input.history, breakpoints: input.breakpoints ?? [], overrides: input.overrides, imports: input.imports, backgroundruns: input.backgroundruns ?? {}, watchdog: { ...input.watchdog.config !== void 0 ? { config: input.watchdog.config } : {}, events: input.watchdog.events } };
|
|
9336
|
+
return { version: protocolversion, editor, ...input.model !== void 0 ? { model: input.model } : {} };
|
|
9337
|
+
}
|
|
9338
|
+
function runhistoryquery(value) {
|
|
9339
|
+
if (value === void 0 || value === null) return {};
|
|
9340
|
+
const candidate = record(value);
|
|
9341
|
+
const query = {};
|
|
9342
|
+
if (candidate.workflowid !== void 0) {
|
|
9343
|
+
if (typeof candidate.workflowid !== "string" || !candidate.workflowid.trim()) throw new Error("The run history workflow filter must be a non-empty string.");
|
|
9344
|
+
query.workflowid = candidate.workflowid;
|
|
9345
|
+
}
|
|
9346
|
+
if (candidate.outcome !== void 0) {
|
|
9347
|
+
if (typeof candidate.outcome !== "string" || !candidate.outcome.trim()) throw new Error("The run history outcome filter must be a non-empty string.");
|
|
9348
|
+
query.outcome = candidate.outcome;
|
|
9349
|
+
}
|
|
9350
|
+
if (candidate.since !== void 0) {
|
|
9351
|
+
if (typeof candidate.since !== "number" || !Number.isFinite(candidate.since)) throw new Error("The run history time floor must be a finite timestamp.");
|
|
9352
|
+
query.since = candidate.since;
|
|
9353
|
+
}
|
|
9354
|
+
if (candidate.limit !== void 0) {
|
|
9355
|
+
if (typeof candidate.limit !== "number" || !Number.isInteger(candidate.limit) || candidate.limit < 1) throw new Error("The run history entry count must be a positive integer with no code ceiling.");
|
|
9356
|
+
query.limit = candidate.limit;
|
|
9357
|
+
}
|
|
9358
|
+
return query;
|
|
9359
|
+
}
|
|
9360
|
+
function runhistoryreport(input) {
|
|
9361
|
+
return { version: protocolversion, entries: input.entries, query: input.query ?? {} };
|
|
9362
|
+
}
|
|
9363
|
+
|
|
9364
|
+
// workfloweditor.ts
|
|
9365
|
+
var palettecategories = ["actions", "controlflow", "waits", "variables", "triggers"];
|
|
9366
|
+
var palettenodes = [
|
|
9367
|
+
{ kind: "click", label: "Click an element", category: "actions", description: "Clicks the reviewed selector target." },
|
|
9368
|
+
{ kind: "type", label: "Type text", category: "actions", description: "Types the reviewed text into the target field." },
|
|
9369
|
+
{ kind: "navigate", label: "Navigate", category: "actions", description: "Navigates the tab to the reviewed url." },
|
|
9370
|
+
{ kind: "readtext", label: "Read text", category: "actions", description: "Reads the text of the target element." },
|
|
9371
|
+
{ kind: "scrapetable", label: "Scrape a table", category: "actions", description: "Extracts the reviewed table into a dataset." },
|
|
9372
|
+
{ kind: "fillform", label: "Fill a form", category: "actions", description: "Fills the reviewed form fields from a saved profile." },
|
|
9373
|
+
{ kind: "querytabs", label: "Query tabs", category: "actions", description: "Lists the tabs matching the reviewed query." },
|
|
9374
|
+
{ kind: "fetchurl", label: "Fetch a url", category: "actions", description: "Fetches the reviewed endpoint behind the call consent." },
|
|
9375
|
+
{ kind: "condition", label: "Condition", category: "controlflow", description: "Evaluates one reviewed boolean expression with no page side effect." },
|
|
9376
|
+
{ kind: "branch", label: "Branch", category: "controlflow", description: "Chooses one reviewed path by page state with a mandatory else path." },
|
|
9377
|
+
{ kind: "loop", label: "Loop a list", category: "controlflow", description: "Iterates a list variable binding the item and index per pass." },
|
|
9378
|
+
{ kind: "repeatuntil", label: "Repeat until", category: "controlflow", description: "Reruns the body until the convergence expression holds." },
|
|
9379
|
+
{ kind: "whileloop", label: "While loop", category: "controlflow", description: "Loops while the condition holds inside the reviewed bound." },
|
|
9380
|
+
{ kind: "foreach", label: "For each element", category: "controlflow", description: "Iterates the elements of the reviewed selector." },
|
|
9381
|
+
{ kind: "parallel", label: "Parallel branches", category: "controlflow", description: "Runs branches concurrently and joins them under the reviewed strategy." },
|
|
9382
|
+
{ kind: "trycatch", label: "Try catch", category: "controlflow", description: "Wraps fragile steps with a catch handler, retries and timeouts." },
|
|
9383
|
+
{ kind: "delay", label: "Delay", category: "waits", description: "Sleeps the reviewed base inside the jitter window." },
|
|
9384
|
+
{ kind: "waitelement", label: "Wait for element", category: "waits", description: "Polls the reviewed selector until appearance or timeout." },
|
|
9385
|
+
{ kind: "wait", label: "Wait", category: "waits", description: "Waits the reviewed duration." },
|
|
9386
|
+
{ kind: "waitfor", label: "Wait for target", category: "waits", description: "Waits until the reviewed target exists." },
|
|
9387
|
+
{ kind: "waittext", label: "Wait for text", category: "waits", description: "Waits until the reviewed text appears." },
|
|
9388
|
+
{ kind: "waitquiet", label: "Wait for quiet", category: "waits", description: "Waits until the page stops mutating." },
|
|
9389
|
+
{ kind: "waitload", label: "Wait for load", category: "waits", description: "Waits until the navigation settles." },
|
|
9390
|
+
{ kind: "compute", label: "Compute", category: "variables", description: "Evaluates one reviewed expression into the result variable." },
|
|
9391
|
+
{ kind: "extractvars", label: "Extract variables", category: "variables", description: "Applies the reviewed regex and stores the named captures." },
|
|
9392
|
+
{ kind: "savetemplate", label: "Save template", category: "variables", description: "Shares the reviewed step as a reusable template." },
|
|
9393
|
+
{ kind: "visitrule", label: "Visit rule", category: "triggers", description: "Fires on navigations to the reviewed origins." },
|
|
9394
|
+
{ kind: "urlrule", label: "Url rule", category: "triggers", description: "Fires when the url matches the reviewed glob pattern." },
|
|
9395
|
+
{ kind: "cronrule", label: "Cron rule", category: "triggers", description: "Fires on the reviewed five field cron schedule." },
|
|
9396
|
+
{ kind: "intervalrule", label: "Interval rule", category: "triggers", description: "Fires every reviewed period with the jitter spread." },
|
|
9397
|
+
{ kind: "webhookrule", label: "Webhook rule", category: "triggers", description: "Fires on a secret verified webhook delivery." },
|
|
9398
|
+
{ kind: "eventrule", label: "Event rule", category: "triggers", description: "Fires on the observed page events of the catalog." }
|
|
9399
|
+
];
|
|
9400
|
+
var optionschemas = {
|
|
9401
|
+
delay: [{ name: "base", kind: "number", required: true }, { name: "jitter", kind: "number" }],
|
|
9402
|
+
waitelement: [{ name: "timeout", kind: "number" }, { name: "poll", kind: "number" }],
|
|
9403
|
+
compute: [{ name: "expression", kind: "string", required: true }],
|
|
9404
|
+
extractvars: [{ name: "rule", kind: "string", required: true }],
|
|
9405
|
+
composeworkflow: [{ name: "name", kind: "string", required: true }, { name: "version", kind: "number" }],
|
|
9406
|
+
runworkflow: [{ name: "workflowid", kind: "string", required: true }, { name: "reviewed", kind: "boolean", required: true }, { name: "variables", kind: "string" }, { name: "background", kind: "boolean" }],
|
|
9407
|
+
dryrun: [{ name: "workflowid", kind: "string", required: true }],
|
|
9408
|
+
loop: [{ name: "loop", kind: "string", required: true }],
|
|
9409
|
+
repeatuntil: [{ name: "repeatuntil", kind: "string", required: true }],
|
|
9410
|
+
whileloop: [{ name: "whileloop", kind: "string", required: true }],
|
|
9411
|
+
foreach: [{ name: "foreach", kind: "string", required: true }],
|
|
9412
|
+
parallel: [{ name: "parallel", kind: "string", required: true }],
|
|
9413
|
+
trycatch: [{ name: "trycatch", kind: "string", required: true }]
|
|
9414
|
+
};
|
|
9415
|
+
function stepcategory(kind) {
|
|
9416
|
+
if (triggerkinds.includes(kind)) return "triggers";
|
|
9417
|
+
if (controlflowkinds.includes(kind)) return "controlflow";
|
|
9418
|
+
if (kind.startsWith("wait") || kind === "spawait" || kind === "delay") return "waits";
|
|
9419
|
+
if (kind === "compute" || kind === "extractvars" || kind === "savetemplate") return "variables";
|
|
9420
|
+
return "actions";
|
|
9421
|
+
}
|
|
9422
|
+
function buildsteplibrary(kinds) {
|
|
9423
|
+
return [...new Set(kinds)].sort().map((kind) => ({ kind, category: stepcategory(kind), optionschema: optionschemas[kind] ?? [] }));
|
|
9424
|
+
}
|
|
9425
|
+
var noderowheight = 96;
|
|
9426
|
+
var blockcolumnwidth = 280;
|
|
9427
|
+
var canvasoriginx = 40;
|
|
9428
|
+
function snapshotof(model) {
|
|
9429
|
+
const { undo, redo, dirty, ...rest } = model;
|
|
9430
|
+
void undo;
|
|
9431
|
+
void redo;
|
|
9432
|
+
void dirty;
|
|
9433
|
+
return { ...rest, dirty: true };
|
|
9434
|
+
}
|
|
9435
|
+
function withundo(model, next) {
|
|
9436
|
+
const undo = [...model.undo ?? [], snapshotof(model)];
|
|
9437
|
+
const { redo, ...rest } = next;
|
|
9438
|
+
void redo;
|
|
9439
|
+
return { ...rest, dirty: true, undo };
|
|
9440
|
+
}
|
|
9441
|
+
function nodeidof(node) {
|
|
9442
|
+
return node.id ?? (node.step !== void 0 ? node.step.id : node.invocation !== void 0 ? node.invocation.block : "");
|
|
9443
|
+
}
|
|
9444
|
+
function layoutsizeof(nodes) {
|
|
9445
|
+
const width = Math.max(640, ...nodes.map((node) => node.x + blockcolumnwidth)) + 40;
|
|
9446
|
+
const height = Math.max(480, ...nodes.map((node) => node.y + noderowheight)) + 40;
|
|
9447
|
+
return { width, height };
|
|
9448
|
+
}
|
|
9449
|
+
function loadworkflow(record2, layout) {
|
|
9450
|
+
const blocks = record2.blocks.map((block) => ({ ...block, steps: block.steps.map((entry) => ({ ...entry })) }));
|
|
9451
|
+
const blockcolumn = (blockname) => {
|
|
9452
|
+
const index2 = blocks.findIndex((block) => block.name === blockname);
|
|
9453
|
+
return index2 < 0 ? canvasoriginx : canvasoriginx + (index2 + 1) * blockcolumnwidth;
|
|
9454
|
+
};
|
|
9455
|
+
const invocationcount = /* @__PURE__ */ new Map();
|
|
9456
|
+
const nodes = [];
|
|
9457
|
+
const edges = [];
|
|
9458
|
+
let index = 0;
|
|
9459
|
+
while (index < record2.steps.length) {
|
|
9460
|
+
const step = record2.steps[index];
|
|
9461
|
+
for (const binding of step.bindings ?? []) edges.push({ from: binding.stepid, to: step.id, variable: binding.variable, kind: binding.kind, ...binding.path !== void 0 ? { path: binding.path } : {} });
|
|
9462
|
+
if (step.block === void 0) {
|
|
9463
|
+
const { bindings, block, params: params2, ...rest } = step;
|
|
9464
|
+
void bindings;
|
|
9465
|
+
void block;
|
|
9466
|
+
void params2;
|
|
9467
|
+
nodes.push({ step: { ...rest }, x: canvasoriginx, y: 60 + nodes.length * noderowheight });
|
|
9468
|
+
index += 1;
|
|
9469
|
+
continue;
|
|
9470
|
+
}
|
|
9471
|
+
const blockname = step.block;
|
|
9472
|
+
let end = index;
|
|
9473
|
+
while (end < record2.steps.length && record2.steps[end].block === blockname) end += 1;
|
|
9474
|
+
const region = record2.steps.slice(index, end);
|
|
9475
|
+
const count = (invocationcount.get(blockname) ?? 0) + 1;
|
|
9476
|
+
invocationcount.set(blockname, count);
|
|
9477
|
+
const params = region.flatMap((entry) => entry.params ?? []);
|
|
9478
|
+
nodes.push({ id: count === 1 ? blockname : `${blockname}${count}`, invocation: { block: blockname, label: blockname, ...params.length > 0 ? { params: params.map((param) => ({ ...param })) } : {} }, x: blockcolumn(blockname), y: 60 + nodes.length * noderowheight });
|
|
9479
|
+
index = end;
|
|
9480
|
+
}
|
|
9481
|
+
const size = layouttypeof(nodes, layout);
|
|
9482
|
+
const model = { workflowid: record2.id, name: record2.name, version: record2.version, origins: [...record2.origins], nodes, edges, blocks, layout: size, minimap: emptyminimap(), dirty: false };
|
|
9483
|
+
return { ...model, minimap: renderminimap(model).minimap };
|
|
9484
|
+
}
|
|
9485
|
+
function layouttypeof(nodes, layout) {
|
|
9486
|
+
const size = layoutsizeof(nodes);
|
|
9487
|
+
if (!layout) return { width: size.width, height: size.height, viewportx: 0, viewporty: 0, zoom: 1 };
|
|
9488
|
+
return { width: Math.max(size.width, layout.width), height: Math.max(size.height, layout.height), viewportx: layout.viewportx, viewporty: layout.viewporty, zoom: layout.zoom };
|
|
9489
|
+
}
|
|
9490
|
+
function emptyminimap() {
|
|
9491
|
+
return { width: 160, height: 100, scale: 0, zoom: 1, viewport: { x: 0, y: 0, width: 0, height: 0 } };
|
|
9492
|
+
}
|
|
9493
|
+
function saveworkflow(model, input) {
|
|
9494
|
+
if (typeof model.name !== "string" || !model.name.trim()) throw new Error("The workflow name must be a non-empty string.");
|
|
9495
|
+
if (typeof model.version !== "number" || !Number.isInteger(model.version) || model.version < 1) throw new Error("The workflow version must be a positive integer.");
|
|
9496
|
+
if (!Array.isArray(model.origins) || model.origins.length === 0) throw new Error("A workflow needs at least one granted HTTPS origin.");
|
|
9497
|
+
const ids = /* @__PURE__ */ new Set();
|
|
9498
|
+
for (const node of model.nodes) {
|
|
9499
|
+
if (node.step === void 0 === (node.invocation === void 0)) throw new Error("Every canvas node must be exactly one workflow step or one block invocation.");
|
|
9500
|
+
const id = nodeidof(node);
|
|
9501
|
+
if (!id || ids.has(id)) throw new Error(`The canvas node id ${id || "(empty)"} must be unique.`);
|
|
9502
|
+
ids.add(id);
|
|
9503
|
+
}
|
|
9504
|
+
const positionof = /* @__PURE__ */ new Map();
|
|
9505
|
+
let position = 0;
|
|
9506
|
+
for (const node of model.nodes) {
|
|
9507
|
+
if (node.step !== void 0) {
|
|
9508
|
+
positionof.set(node.step.id, position);
|
|
9509
|
+
position += 1;
|
|
9510
|
+
continue;
|
|
9511
|
+
}
|
|
9512
|
+
const block = model.blocks.find((entry) => entry.name === node.invocation?.block);
|
|
9513
|
+
if (!block) throw new Error(`The block ${node.invocation?.block ?? ""} of the canvas has no definition.`);
|
|
9514
|
+
const walk = (entries2) => {
|
|
9515
|
+
for (const entry of entries2) {
|
|
9516
|
+
if ("kind" in entry && "label" in entry && !("block" in entry)) {
|
|
9517
|
+
positionof.set(entry.id, position);
|
|
9518
|
+
position += 1;
|
|
9519
|
+
continue;
|
|
9520
|
+
}
|
|
9521
|
+
const nested = model.blocks.find((candidate) => candidate.name === entry.block);
|
|
9522
|
+
if (!nested) throw new Error(`The block ${entry.block} of the canvas has no definition.`);
|
|
9523
|
+
walk(nested.steps);
|
|
9524
|
+
}
|
|
9525
|
+
};
|
|
9526
|
+
walk(block.steps);
|
|
9527
|
+
}
|
|
9528
|
+
for (const edge of model.edges) {
|
|
9529
|
+
if (!positionof.has(edge.from)) throw new Error(`The edge of ${edge.variable} references the unknown source step ${edge.from}.`);
|
|
9530
|
+
if (!positionof.has(edge.to)) throw new Error(`The edge of ${edge.variable} references the unknown target step ${edge.to}.`);
|
|
9531
|
+
if (positionof.get(edge.from) >= positionof.get(edge.to)) throw new Error(`The edge of ${edge.variable} runs backwards from ${edge.from} into ${edge.to} and would form a cycle.`);
|
|
9532
|
+
}
|
|
9533
|
+
const bindingsof = (stepid) => model.edges.filter((edge) => edge.to === stepid).map((edge) => ({ variable: edge.variable, kind: edge.kind, stepid: edge.from, ...edge.path !== void 0 ? { path: edge.path } : {} }));
|
|
9534
|
+
const entries = [];
|
|
9535
|
+
const attached = /* @__PURE__ */ new Map();
|
|
9536
|
+
for (const node of model.nodes) {
|
|
9537
|
+
if (node.invocation !== void 0) {
|
|
9538
|
+
entries.push({ ...node.invocation });
|
|
9539
|
+
continue;
|
|
9540
|
+
}
|
|
9541
|
+
const step = node.step;
|
|
9542
|
+
const bindings = bindingsof(step.id);
|
|
9543
|
+
const { block, params, ...rest } = { ...step, ...bindings.length > 0 ? { bindings } : {} };
|
|
9544
|
+
void params;
|
|
9545
|
+
const carried = rest;
|
|
9546
|
+
if (block !== void 0) {
|
|
9547
|
+
if (!model.blocks.some((candidate) => candidate.name === block)) throw new Error(`The step ${step.id} attaches to the unknown block ${block}.`);
|
|
9548
|
+
const list = attached.get(block) ?? [];
|
|
9549
|
+
list.push(carried);
|
|
9550
|
+
attached.set(block, list);
|
|
9551
|
+
continue;
|
|
9552
|
+
}
|
|
9553
|
+
entries.push(carried);
|
|
9554
|
+
}
|
|
9555
|
+
const blocks = model.blocks.map((block) => {
|
|
9556
|
+
const snapped = attached.get(block.name) ?? [];
|
|
9557
|
+
const snappedids = new Set(snapped.map((step) => step.id));
|
|
9558
|
+
const carried = [];
|
|
9559
|
+
for (const entry of block.steps) {
|
|
9560
|
+
if ("kind" in entry && "label" in entry && !("block" in entry) && snappedids.has(entry.id)) continue;
|
|
9561
|
+
carried.push(entry);
|
|
9562
|
+
}
|
|
9563
|
+
const steps = [...carried, ...snapped];
|
|
9564
|
+
const withbindings = [];
|
|
9565
|
+
for (const entry of steps) {
|
|
9566
|
+
if (!("kind" in entry && "label" in entry && !("block" in entry))) {
|
|
9567
|
+
withbindings.push(entry);
|
|
9568
|
+
continue;
|
|
9569
|
+
}
|
|
9570
|
+
const bindings = bindingsof(entry.id);
|
|
9571
|
+
const { block: inner, params, ...rest } = { ...entry, ...bindings.length > 0 ? { bindings } : {} };
|
|
9572
|
+
void inner;
|
|
9573
|
+
void params;
|
|
9574
|
+
withbindings.push(rest);
|
|
9575
|
+
}
|
|
9576
|
+
return { ...block, steps: withbindings };
|
|
9577
|
+
});
|
|
9578
|
+
const composed = composeworkflow({ id: model.workflowid, name: model.name, version: model.version, origins: [...model.origins], steps: entries, blocks: blocks.map((block) => ({ ...block })), now: input.now, ...input.kindallowed !== void 0 ? { kindallowed: input.kindallowed } : {}, ...input.riskof !== void 0 ? { riskof: input.riskof } : {} });
|
|
9579
|
+
const checked = validateworkflow(composed, input.kindallowed !== void 0 ? { kindallowed: input.kindallowed } : {});
|
|
9580
|
+
if (!checked.allowed) throw new Error(checked.reason ?? "The canvas model failed the workflow grammar.");
|
|
9581
|
+
return composed;
|
|
9582
|
+
}
|
|
9583
|
+
function snapnode(model, nodeid, x, y, grid = 20) {
|
|
9584
|
+
if (!Number.isFinite(grid) || grid <= 0) throw new Error("The snap grid must be a positive number.");
|
|
9585
|
+
const index = model.nodes.findIndex((node2) => nodeidof(node2) === nodeid);
|
|
9586
|
+
if (index < 0) throw new Error(`No canvas node matches ${nodeid}.`);
|
|
9587
|
+
const node = model.nodes[index];
|
|
9588
|
+
if (node.step === void 0) throw new Error("A block invocation node attaches through its own definition, not through snapping.");
|
|
9589
|
+
const snappedx = Math.round(x / grid) * grid;
|
|
9590
|
+
const snappedy = Math.round(y / grid) * grid;
|
|
9591
|
+
let attached;
|
|
9592
|
+
for (const [blockindex, block] of model.blocks.entries()) {
|
|
9593
|
+
const columnx = canvasoriginx + (blockindex + 1) * blockcolumnwidth;
|
|
9594
|
+
if (Math.abs(snappedx - columnx) <= blockcolumnwidth / 2) attached = block.name;
|
|
9595
|
+
}
|
|
9596
|
+
const { block: priorblock, ...rest } = node.step;
|
|
9597
|
+
void priorblock;
|
|
9598
|
+
const step = { ...rest, ...attached !== void 0 ? { block: attached } : {} };
|
|
9599
|
+
const nodes = model.nodes.map((candidate, position) => position === index ? { step, x: snappedx, y: snappedy } : candidate);
|
|
9600
|
+
const size = layouttypeof(nodes, model.layout);
|
|
9601
|
+
const next = { ...model, nodes, layout: size };
|
|
9602
|
+
return withundo(model, { ...next, minimap: renderminimap(next).minimap });
|
|
9603
|
+
}
|
|
9604
|
+
function reordersteps(model, nodeid, index) {
|
|
9605
|
+
const current = model.nodes.findIndex((node) => nodeidof(node) === nodeid);
|
|
9606
|
+
if (current < 0) throw new Error(`No canvas node matches ${nodeid}.`);
|
|
9607
|
+
if (!Number.isInteger(index) || index < 0 || index > model.nodes.length - 1) throw new Error("The reorder index must address an existing position of the canvas list.");
|
|
9608
|
+
const nodes = [...model.nodes];
|
|
9609
|
+
const [moved] = nodes.splice(current, 1);
|
|
9610
|
+
if (!moved) throw new Error("The reordered canvas node vanished.");
|
|
9611
|
+
nodes.splice(index, 0, moved);
|
|
9612
|
+
const next = { ...model, nodes };
|
|
9613
|
+
return withundo(model, { ...next, minimap: renderminimap(next).minimap });
|
|
9614
|
+
}
|
|
9615
|
+
function groupselect(model, nodeids, blockname) {
|
|
9616
|
+
if (!/^[a-z][a-z0-9]*$/.test(blockname)) throw new Error("The block name must be a unique lowercase word.");
|
|
9617
|
+
if (model.blocks.some((block) => block.name === blockname)) throw new Error(`The block name ${blockname} already exists on the canvas.`);
|
|
9618
|
+
const selected = nodeids.map((id) => {
|
|
9619
|
+
const node = model.nodes.find((candidate) => nodeidof(candidate) === id);
|
|
9620
|
+
if (!node || node.step === void 0) throw new Error(`The grouping selection must address step nodes; ${id} is not one.`);
|
|
9621
|
+
return node;
|
|
9622
|
+
});
|
|
9623
|
+
if (selected.length === 0) throw new Error("The grouping selection needs at least one step node.");
|
|
9624
|
+
const steps = selected.map((node) => node.step);
|
|
9625
|
+
const blocks = [...model.blocks, { name: blockname, label: blockname, steps: steps.map((step) => ({ ...step })) }];
|
|
9626
|
+
const firstindex = model.nodes.findIndex((node) => nodeidof(node) === nodeids[0]);
|
|
9627
|
+
const invocationnode = { id: blockname, invocation: { block: blockname, label: blockname }, x: selected[0].x, y: selected[0].y };
|
|
9628
|
+
const nodes = [];
|
|
9629
|
+
model.nodes.forEach((node, index) => {
|
|
9630
|
+
if (nodeids.includes(nodeidof(node))) {
|
|
9631
|
+
if (index === firstindex) nodes.push(invocationnode);
|
|
9632
|
+
return;
|
|
9633
|
+
}
|
|
9634
|
+
nodes.push(node);
|
|
9635
|
+
});
|
|
9636
|
+
const next = { ...model, nodes, blocks };
|
|
9637
|
+
return withundo(model, { ...next, minimap: renderminimap(next).minimap });
|
|
9638
|
+
}
|
|
9639
|
+
function expandtemplate(model, template, params = [], index) {
|
|
9640
|
+
const parsed = steptemplateof(template);
|
|
9641
|
+
if (!parsed) throw new Error("The template does not carry one reviewed workflow step.");
|
|
9642
|
+
let id = parsed.step.id;
|
|
9643
|
+
let suffix = 2;
|
|
9644
|
+
const taken = new Set(model.nodes.map((node) => nodeidof(node)));
|
|
9645
|
+
while (taken.has(id)) {
|
|
9646
|
+
id = `${parsed.step.id}${suffix}`;
|
|
9647
|
+
suffix += 1;
|
|
9648
|
+
}
|
|
9649
|
+
const step = { ...parsed.step, id, ...params.length > 0 ? { params: params.map((param) => ({ ...param })) } : {} };
|
|
9650
|
+
const position = index !== void 0 && Number.isInteger(index) && index >= 0 && index <= model.nodes.length ? index : model.nodes.length;
|
|
9651
|
+
const nodes = [...model.nodes.slice(0, position), { step, x: canvasoriginx, y: 60 + position * noderowheight }, ...model.nodes.slice(position)];
|
|
9652
|
+
const next = { ...model, nodes };
|
|
9653
|
+
return withundo(model, { ...next, minimap: renderminimap(next).minimap });
|
|
9654
|
+
}
|
|
9655
|
+
function addnode(model, step, index) {
|
|
9656
|
+
const normalized = workflowstepof(step);
|
|
9657
|
+
if (!normalized) throw new Error("The canvas insertion needs one reviewed workflow step.");
|
|
9658
|
+
let id = normalized.id;
|
|
9659
|
+
let suffix = 2;
|
|
9660
|
+
const taken = new Set(model.nodes.map((node) => nodeidof(node)));
|
|
9661
|
+
while (taken.has(id)) {
|
|
9662
|
+
id = `${normalized.id}${suffix}`;
|
|
9663
|
+
suffix += 1;
|
|
9664
|
+
}
|
|
9665
|
+
const position = index !== void 0 && Number.isInteger(index) && index >= 0 && index <= model.nodes.length ? index : model.nodes.length;
|
|
9666
|
+
const nodes = [...model.nodes.slice(0, position), { step: { ...normalized, id }, x: canvasoriginx, y: 60 + position * noderowheight }, ...model.nodes.slice(position)];
|
|
9667
|
+
const next = { ...model, nodes };
|
|
9668
|
+
return withundo(model, { ...next, minimap: renderminimap(next).minimap });
|
|
9669
|
+
}
|
|
9670
|
+
function editstep(model, step) {
|
|
9671
|
+
const normalized = workflowstepof(step);
|
|
9672
|
+
if (!normalized) throw new Error("The step inspector edit needs one reviewed workflow step.");
|
|
9673
|
+
const index = model.nodes.findIndex((node2) => node2.step?.id === normalized.id);
|
|
9674
|
+
if (index < 0) throw new Error(`No canvas step matches ${normalized.id}.`);
|
|
9675
|
+
const node = model.nodes[index];
|
|
9676
|
+
const nodes = model.nodes.map((candidate, position) => position === index ? { step: { ...normalized, ...node.step?.block !== void 0 ? { block: node.step.block } : {}, ...node.step?.breakpoint === true ? { breakpoint: true } : {} }, x: node.x, y: node.y } : candidate);
|
|
9677
|
+
const next = { ...model, nodes };
|
|
9678
|
+
return withundo(model, { ...next, minimap: renderminimap(next).minimap });
|
|
9679
|
+
}
|
|
9680
|
+
function renderminimap(model, width = 160, height = 100) {
|
|
9681
|
+
if (!Number.isFinite(width) || width <= 0 || !Number.isFinite(height) || height <= 0) throw new Error("The mini map size must be positive.");
|
|
9682
|
+
const canvaswidth = Math.max(1, model.layout.width);
|
|
9683
|
+
const canvasheight = Math.max(1, model.layout.height);
|
|
9684
|
+
const scale = Math.min(width / canvaswidth, height / canvasheight);
|
|
9685
|
+
const zoom = model.layout.zoom > 0 ? model.layout.zoom : 1;
|
|
9686
|
+
const visiblewidth = canvaswidth / zoom;
|
|
9687
|
+
const visibleheight = canvasheight / zoom;
|
|
9688
|
+
const viewport = {
|
|
9689
|
+
x: Math.max(0, Math.min(model.layout.viewportx, canvaswidth)) * scale,
|
|
9690
|
+
y: Math.max(0, Math.min(model.layout.viewporty, canvasheight)) * scale,
|
|
9691
|
+
width: visiblewidth * scale,
|
|
9692
|
+
height: visibleheight * scale
|
|
9693
|
+
};
|
|
9694
|
+
const nodes = model.nodes.map((node) => ({ id: nodeidof(node), x: node.x * scale, y: node.y * scale }));
|
|
9695
|
+
return { minimap: { width, height, scale, zoom, viewport }, nodes };
|
|
9696
|
+
}
|
|
9697
|
+
function minimapfocus(model, x, y, width = 160, height = 100) {
|
|
9698
|
+
const projection = renderminimap(model, width, height);
|
|
9699
|
+
if (projection.minimap.scale <= 0) return model;
|
|
9700
|
+
const canvasx = x / projection.minimap.scale;
|
|
9701
|
+
const canvasy = y / projection.minimap.scale;
|
|
9702
|
+
const zoom = model.layout.zoom > 0 ? model.layout.zoom : 1;
|
|
9703
|
+
const visiblewidth = model.layout.width / zoom;
|
|
9704
|
+
const visibleheight = model.layout.height / zoom;
|
|
9705
|
+
const viewportx = Math.max(0, Math.min(canvasx - visiblewidth / 2, Math.max(0, model.layout.width - visiblewidth)));
|
|
9706
|
+
const viewporty = Math.max(0, Math.min(canvasy - visibleheight / 2, Math.max(0, model.layout.height - visibleheight)));
|
|
9707
|
+
const next = { ...model, layout: { ...model.layout, viewportx, viewporty } };
|
|
9708
|
+
return { ...next, minimap: renderminimap(next).minimap };
|
|
9709
|
+
}
|
|
9710
|
+
function zoomcanvas(model, zoom) {
|
|
9711
|
+
if (!Number.isFinite(zoom) || zoom <= 0) throw new Error("The canvas zoom must be a positive number with no code ceiling.");
|
|
9712
|
+
const next = { ...model, layout: { ...model.layout, zoom } };
|
|
9713
|
+
const labelscale = zoom < 1 ? 1 / zoom : 1;
|
|
9714
|
+
return { model: { ...next, minimap: renderminimap(next).minimap }, labelscale };
|
|
9715
|
+
}
|
|
9716
|
+
function searchsteps(model, query) {
|
|
9717
|
+
const needle = query.trim().toLowerCase();
|
|
9718
|
+
if (!needle) return [];
|
|
9719
|
+
const results = [];
|
|
9720
|
+
for (const node of model.nodes) {
|
|
9721
|
+
if (node.step === void 0) continue;
|
|
9722
|
+
const matched = [];
|
|
9723
|
+
if (node.step.label.toLowerCase().includes(needle)) matched.push("label");
|
|
9724
|
+
if (node.step.kind.toLowerCase().includes(needle)) matched.push("kind");
|
|
9725
|
+
const variables = [
|
|
9726
|
+
...model.edges.filter((edge) => edge.to === node.step?.id || edge.from === node.step?.id).map((edge) => edge.variable),
|
|
9727
|
+
...node.step.expression !== void 0 ? [node.step.expression.result] : [],
|
|
9728
|
+
...node.step.extract !== void 0 ? node.step.extract.groups : []
|
|
9729
|
+
];
|
|
9730
|
+
if (variables.some((name) => name.toLowerCase().includes(needle))) matched.push("variable");
|
|
9731
|
+
if (matched.length > 0) results.push({ id: node.step.id, label: node.step.label, kind: node.step.kind, matched });
|
|
9732
|
+
}
|
|
9733
|
+
return results;
|
|
9734
|
+
}
|
|
9735
|
+
function markbreakpoint(model, stepid) {
|
|
9736
|
+
const toggle = (step) => {
|
|
9737
|
+
const { breakpoint, ...rest } = step;
|
|
9738
|
+
void breakpoint;
|
|
9739
|
+
return breakpoint === true ? rest : { ...rest, breakpoint: true };
|
|
9740
|
+
};
|
|
9741
|
+
const index = model.nodes.findIndex((node) => node.step?.id === stepid);
|
|
9742
|
+
if (index >= 0) {
|
|
9743
|
+
const node = model.nodes[index];
|
|
9744
|
+
const step = node.step;
|
|
9745
|
+
const nodes = model.nodes.map((candidate, position) => position === index ? { step: toggle(step), x: candidate.x, y: candidate.y } : candidate);
|
|
9746
|
+
const next2 = { ...model, nodes };
|
|
9747
|
+
return withundo(model, { ...next2, minimap: renderminimap(next2).minimap });
|
|
9748
|
+
}
|
|
9749
|
+
const blocks = model.blocks.map((block) => {
|
|
9750
|
+
const stepindex = block.steps.findIndex((entry) => "kind" in entry && "label" in entry && !("block" in entry) && entry.id === stepid);
|
|
9751
|
+
if (stepindex < 0) return block;
|
|
9752
|
+
const steps = block.steps.map((entry, position) => position === stepindex ? toggle(entry) : entry);
|
|
9753
|
+
return { ...block, steps };
|
|
9754
|
+
});
|
|
9755
|
+
if (blocks.every((block, position) => block === model.blocks[position])) throw new Error(`No canvas step matches ${stepid}.`);
|
|
9756
|
+
const next = { ...model, blocks };
|
|
9757
|
+
return withundo(model, next);
|
|
9758
|
+
}
|
|
9759
|
+
function runtobreakpoint(input) {
|
|
9760
|
+
const cursor = input.cursor !== void 0 && Number.isInteger(input.cursor) && input.cursor >= 0 ? input.cursor : 0;
|
|
9761
|
+
const marked = new Set(input.breakpoints);
|
|
9762
|
+
for (let index = cursor; index < input.record.steps.length; index += 1) {
|
|
9763
|
+
const step = input.record.steps[index];
|
|
9764
|
+
if (step.breakpoint === true || marked.has(step.id)) {
|
|
9765
|
+
return { until: index, pausat: step.id, remaining: input.record.steps.length - index };
|
|
9766
|
+
}
|
|
9767
|
+
}
|
|
9768
|
+
return { until: input.record.steps.length, pausat: void 0, remaining: 0 };
|
|
9769
|
+
}
|
|
9770
|
+
function diffversions(from, to, now) {
|
|
9771
|
+
const fromsteps = new Map(from.steps.map((step) => [step.id, step]));
|
|
9772
|
+
const tosteps = new Map(to.steps.map((step) => [step.id, step]));
|
|
9773
|
+
const added = [];
|
|
9774
|
+
const removed = [];
|
|
9775
|
+
const changed = [];
|
|
9776
|
+
for (const step of to.steps) {
|
|
9777
|
+
const prior = fromsteps.get(step.id);
|
|
9778
|
+
if (!prior) {
|
|
9779
|
+
added.push({ stepid: step.id, kind: step.kind, label: step.label });
|
|
9780
|
+
continue;
|
|
9781
|
+
}
|
|
9782
|
+
const changes = [];
|
|
9783
|
+
if (prior.label !== step.label) changes.push("label");
|
|
9784
|
+
if (prior.kind !== step.kind) changes.push("kind");
|
|
9785
|
+
if (prior.target !== step.target) changes.push("target");
|
|
9786
|
+
if (prior.value !== step.value) changes.push("value");
|
|
9787
|
+
if (prior.options !== step.options) changes.push("options");
|
|
9788
|
+
if (JSON.stringify(prior.expression) !== JSON.stringify(step.expression)) changes.push("expression");
|
|
9789
|
+
if (JSON.stringify(prior.extract) !== JSON.stringify(step.extract)) changes.push("extract");
|
|
9790
|
+
if (JSON.stringify(prior.bindings) !== JSON.stringify(step.bindings)) changes.push("bindings");
|
|
9791
|
+
if (changes.length > 0) changed.push({ stepid: step.id, kind: step.kind, label: step.label, changes });
|
|
9792
|
+
}
|
|
9793
|
+
for (const step of from.steps) {
|
|
9794
|
+
if (!tosteps.has(step.id)) removed.push({ stepid: step.id, kind: step.kind, label: step.label });
|
|
9795
|
+
}
|
|
9796
|
+
return { workflowid: to.id, from: from.version, to: to.version, added, removed, changed, at: now };
|
|
9797
|
+
}
|
|
9798
|
+
function exportworkflow(record2, format, note, now) {
|
|
9799
|
+
const file = { format: workflowfileversion, exportedat: now ?? Date.now(), workflow: record2, ...note !== void 0 && note.trim() !== "" ? { note } : {}, templates: [] };
|
|
9800
|
+
return { format, contents: serializefile(file, format), file };
|
|
9801
|
+
}
|
|
9802
|
+
function shareworkflow(record2, templates, format, note, now) {
|
|
9803
|
+
const file = { format: workflowfileversion, exportedat: now ?? Date.now(), workflow: record2, ...note !== void 0 && note.trim() !== "" ? { note } : {}, templates: templates.map((template) => ({ ...template })) };
|
|
9804
|
+
return { format, contents: serializefile(file, format), file };
|
|
9805
|
+
}
|
|
9806
|
+
function importworkflow(input) {
|
|
9807
|
+
const format = input.format ?? (input.contents.trimStart().startsWith("{") ? "json" : "yaml");
|
|
9808
|
+
const parsed = parsefile(input.contents, format);
|
|
9809
|
+
if (parsed.format !== workflowfileversion) throw new Error(`The workflow file format ${String(parsed.format)} is not the reviewed format ${workflowfileversion}.`);
|
|
9810
|
+
const candidate = parsed.workflow;
|
|
9811
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) throw new Error("The workflow file carries no workflow record.");
|
|
9812
|
+
const fields = candidate;
|
|
9813
|
+
const stepsvalue = fields.steps;
|
|
9814
|
+
if (!Array.isArray(stepsvalue) || stepsvalue.length === 0) throw new Error("An imported workflow needs at least one step.");
|
|
9815
|
+
const steps = [];
|
|
9816
|
+
for (const entry of stepsvalue) {
|
|
9817
|
+
const step = workflowstepof(entry);
|
|
9818
|
+
if (step) {
|
|
9819
|
+
steps.push(step);
|
|
9820
|
+
continue;
|
|
9821
|
+
}
|
|
9822
|
+
throw new Error("Every imported workflow entry must be a reviewed step.");
|
|
9823
|
+
}
|
|
9824
|
+
const composed = composeworkflow({
|
|
9825
|
+
id: typeof fields.id === "string" && fields.id.trim() !== "" ? fields.id : crypto.randomUUID(),
|
|
9826
|
+
name: typeof fields.name === "string" ? fields.name : "",
|
|
9827
|
+
version: typeof fields.version === "number" ? fields.version : 1,
|
|
9828
|
+
origins: Array.isArray(fields.origins) ? fields.origins.filter((origin) => typeof origin === "string") : [],
|
|
9829
|
+
steps,
|
|
9830
|
+
now: input.now ?? Date.now(),
|
|
9831
|
+
...input.kindallowed !== void 0 ? { kindallowed: input.kindallowed } : {},
|
|
9832
|
+
...input.riskof !== void 0 ? { riskof: input.riskof } : {}
|
|
9833
|
+
});
|
|
9834
|
+
const templatesvalue = parsed.templates;
|
|
9835
|
+
if (templatesvalue !== void 0 && !Array.isArray(templatesvalue)) throw new Error("The packed templates of the workflow file must be a list.");
|
|
9836
|
+
const templates = [];
|
|
9837
|
+
for (const entry of templatesvalue ?? []) {
|
|
9838
|
+
const template = steptemplateof(entry);
|
|
9839
|
+
if (!template) throw new Error("A packed template of the workflow file does not carry one reviewed step.");
|
|
9840
|
+
templates.push(template);
|
|
9841
|
+
}
|
|
9842
|
+
const record2 = { ...composed, reviewstate: "pending" };
|
|
9843
|
+
return { record: record2, templates, file: { ...parsed, workflow: record2 } };
|
|
9844
|
+
}
|
|
9845
|
+
function bindparam(model, blockname, param) {
|
|
9846
|
+
if (!/^[a-z][a-z0-9]*$/.test(param.name)) throw new Error("The nested parameter name must be a lowercase word.");
|
|
9847
|
+
const index = model.nodes.findIndex((node2) => node2.invocation?.block === blockname);
|
|
9848
|
+
if (index < 0) throw new Error(`No block invocation of ${blockname} sits on the canvas.`);
|
|
9849
|
+
const node = model.nodes[index];
|
|
9850
|
+
const invocation = node.invocation;
|
|
9851
|
+
const params = [...(invocation.params ?? []).filter((existing) => existing.name !== param.name), { ...param }];
|
|
9852
|
+
const nodes = model.nodes.map((candidate, position) => position === index ? { invocation: { ...invocation, params }, x: candidate.x, y: candidate.y } : candidate);
|
|
9853
|
+
const next = { ...model, nodes };
|
|
9854
|
+
return withundo(model, { ...next, minimap: renderminimap(next).minimap });
|
|
9855
|
+
}
|
|
9856
|
+
function originmatches(pattern, origin) {
|
|
9857
|
+
if (pattern === origin) return true;
|
|
9858
|
+
const glob = pattern.replace(/\./g, "\\.").replace(/\*/g, "[^.]+");
|
|
9859
|
+
if (!glob.startsWith("https://")) return false;
|
|
9860
|
+
return new RegExp(`^${glob}$`).test(origin);
|
|
9861
|
+
}
|
|
9862
|
+
function applyoverride(record2, override) {
|
|
9863
|
+
const matching = record2.origins.filter((origin) => originmatches(override.pattern, origin));
|
|
9864
|
+
if (matching.length === 0) throw new Error(`The override pattern ${override.pattern} matches none of the workflow origins ${record2.origins.join(", ")}.`);
|
|
9865
|
+
const knobs = /* @__PURE__ */ new Set(["loopbound", "stepms", "runms", "waitms", "delaybase"]);
|
|
9866
|
+
for (const knob of Object.keys(override.deltas)) {
|
|
9867
|
+
if (!knobs.has(knob)) throw new Error(`The override knob ${knob} is not one of the reviewed knobs: ${[...knobs].join(", ")}.`);
|
|
9868
|
+
if (typeof override.deltas[knob] !== "number" || !Number.isFinite(override.deltas[knob]) || override.deltas[knob] <= 0) throw new Error(`The override delta of ${knob} must be a positive number with no code ceiling.`);
|
|
9869
|
+
}
|
|
9870
|
+
const apply = (step) => {
|
|
9871
|
+
if (Object.keys(override.deltas).length === 0) return step;
|
|
9872
|
+
let payload = {};
|
|
9873
|
+
try {
|
|
9874
|
+
payload = step.options !== void 0 ? JSON.parse(step.options) : {};
|
|
9875
|
+
} catch {
|
|
9876
|
+
payload = {};
|
|
9877
|
+
}
|
|
9878
|
+
const bodyof = (key) => payload[key] !== void 0 && typeof payload[key] === "object" && !Array.isArray(payload[key]) ? payload[key] : {};
|
|
9879
|
+
if (override.deltas.loopbound !== void 0 && ["loop", "repeatuntil", "whileloop"].includes(step.kind)) {
|
|
9880
|
+
const body = bodyof(step.kind);
|
|
9881
|
+
body.bound = override.deltas.loopbound;
|
|
9882
|
+
payload[step.kind] = body;
|
|
9883
|
+
}
|
|
9884
|
+
if ((override.deltas.stepms !== void 0 || override.deltas.runms !== void 0) && step.kind === "trycatch") {
|
|
9885
|
+
const body = bodyof("trycatch");
|
|
9886
|
+
const timeout = body.timeout !== void 0 && typeof body.timeout === "object" && !Array.isArray(body.timeout) ? body.timeout : {};
|
|
9887
|
+
if (override.deltas.stepms !== void 0) timeout.stepms = override.deltas.stepms;
|
|
9888
|
+
if (override.deltas.runms !== void 0) timeout.runms = override.deltas.runms;
|
|
9889
|
+
body.timeout = timeout;
|
|
9890
|
+
payload.trycatch = body;
|
|
9891
|
+
}
|
|
9892
|
+
if (override.deltas.waitms !== void 0 && step.kind === "waitelement") {
|
|
9893
|
+
payload.timeout = override.deltas.waitms;
|
|
9894
|
+
}
|
|
9895
|
+
if (override.deltas.delaybase !== void 0 && step.kind === "delay") {
|
|
9896
|
+
payload.base = override.deltas.delaybase;
|
|
9897
|
+
}
|
|
9898
|
+
const changed = Object.keys(payload).length > 0;
|
|
9899
|
+
return changed ? { ...step, options: JSON.stringify(payload) } : step;
|
|
9900
|
+
};
|
|
9901
|
+
return { ...record2, steps: record2.steps.map(apply) };
|
|
9902
|
+
}
|
|
9903
|
+
function addedge(model, edge) {
|
|
9904
|
+
const from = model.nodes.findIndex((node) => nodeidof(node) === edge.from);
|
|
9905
|
+
const to = model.nodes.findIndex((node) => nodeidof(node) === edge.to);
|
|
9906
|
+
if (from < 0) throw new Error(`The canvas edge references the unknown source step ${edge.from}.`);
|
|
9907
|
+
if (to < 0) throw new Error(`The canvas edge references the unknown target step ${edge.to}.`);
|
|
9908
|
+
if (from >= to) throw new Error(`The canvas edge of ${edge.variable} would run backwards from ${edge.from} into ${edge.to} and form a cycle.`);
|
|
9909
|
+
if (!/^[a-z][a-z0-9]*$/.test(edge.variable)) throw new Error("The bound variable name must be a lowercase word.");
|
|
9910
|
+
const edges = [...model.edges.filter((candidate) => !(candidate.from === edge.from && candidate.to === edge.to && candidate.variable === edge.variable)), { ...edge, ...edge.path !== void 0 ? { path: edge.path } : {} }];
|
|
9911
|
+
const next = { ...model, edges };
|
|
9912
|
+
return withundo(model, next);
|
|
9913
|
+
}
|
|
9914
|
+
function removeedge(model, from, to, variable) {
|
|
9915
|
+
const edges = model.edges.filter((candidate) => !(candidate.from === from && candidate.to === to && candidate.variable === variable));
|
|
9916
|
+
if (edges.length === model.edges.length) throw new Error(`No canvas edge of ${variable} links ${from} into ${to}.`);
|
|
9917
|
+
const next = { ...model, edges };
|
|
9918
|
+
return withundo(model, next);
|
|
9919
|
+
}
|
|
9920
|
+
function removenode(model, nodeid) {
|
|
9921
|
+
const index = model.nodes.findIndex((node) => nodeidof(node) === nodeid);
|
|
9922
|
+
if (index < 0) throw new Error(`No canvas node matches ${nodeid}.`);
|
|
9923
|
+
const nodes = model.nodes.filter((_, position) => position !== index);
|
|
9924
|
+
const edges = model.edges.filter((edge) => edge.from !== nodeid && edge.to !== nodeid);
|
|
9925
|
+
const next = { ...model, nodes, edges };
|
|
9926
|
+
return withundo(model, { ...next, minimap: renderminimap(next).minimap });
|
|
9927
|
+
}
|
|
9928
|
+
function undoedit(model) {
|
|
9929
|
+
const undo = model.undo ?? [];
|
|
9930
|
+
if (undo.length === 0) return model;
|
|
9931
|
+
const previous = undo[undo.length - 1];
|
|
9932
|
+
const current = snapshotof(model);
|
|
9933
|
+
return { ...previous, undo: undo.slice(0, -1), redo: [...model.redo ?? [], current] };
|
|
9934
|
+
}
|
|
9935
|
+
function redoedit(model) {
|
|
9936
|
+
const redo = model.redo ?? [];
|
|
9937
|
+
if (redo.length === 0) return model;
|
|
9938
|
+
const next = redo[redo.length - 1];
|
|
9939
|
+
const current = snapshotof(model);
|
|
9940
|
+
return { ...next, redo: redo.slice(0, -1), undo: [...model.undo ?? [], current] };
|
|
9941
|
+
}
|
|
9942
|
+
function serializefile(file, format) {
|
|
9943
|
+
if (format === "json") return JSON.stringify(file, null, 2);
|
|
9944
|
+
return yamlvalue(file, 0).join("\n") + "\n";
|
|
9945
|
+
}
|
|
9946
|
+
function parsefile(contents, format) {
|
|
9947
|
+
if (format === "json") {
|
|
9948
|
+
const parsed = JSON.parse(contents);
|
|
9949
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("The workflow file is not a json object.");
|
|
9950
|
+
return parsed;
|
|
9951
|
+
}
|
|
9952
|
+
const lines = contents.split(/\r?\n/).map((line) => line.replace(/\t/g, " ")).filter((line) => line.trim() !== "" && !line.trim().startsWith("#"));
|
|
9953
|
+
if (lines.length === 0) throw new Error("The yaml workflow file is empty.");
|
|
9954
|
+
const { value, next } = yamlblock(lines, 0, indentof(lines[0]));
|
|
9955
|
+
if (next < lines.length) throw new Error("The yaml workflow file carries content outside the documented subset.");
|
|
9956
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("The yaml workflow file is not a mapping.");
|
|
9957
|
+
return value;
|
|
9958
|
+
}
|
|
9959
|
+
function indentof(line) {
|
|
9960
|
+
const match = /^ */.exec(line);
|
|
9961
|
+
return match ? match[0].length : 0;
|
|
9962
|
+
}
|
|
9963
|
+
function yamlscalar(value) {
|
|
9964
|
+
if (value === null || value === void 0) return "null";
|
|
9965
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
9966
|
+
return JSON.stringify(String(value));
|
|
9967
|
+
}
|
|
9968
|
+
function yamlvalue(value, indent) {
|
|
9969
|
+
const pad = " ".repeat(indent);
|
|
9970
|
+
if (value === null || value === void 0 || typeof value !== "object") return [`${pad}${yamlscalar(value)}`];
|
|
9971
|
+
if (Array.isArray(value)) {
|
|
9972
|
+
if (value.length === 0) return [`${pad}[]`];
|
|
9973
|
+
const lines2 = [];
|
|
9974
|
+
for (const item of value) {
|
|
9975
|
+
if (item !== null && typeof item === "object") {
|
|
9976
|
+
lines2.push(`${pad}-`);
|
|
9977
|
+
lines2.push(...yamlvalue(item, indent + 2));
|
|
9978
|
+
} else {
|
|
9979
|
+
lines2.push(`${pad}- ${yamlscalar(item)}`);
|
|
9980
|
+
}
|
|
9981
|
+
}
|
|
9982
|
+
return lines2;
|
|
9983
|
+
}
|
|
9984
|
+
const entries = Object.entries(value);
|
|
9985
|
+
if (entries.length === 0) return [`${pad}{}`];
|
|
9986
|
+
const lines = [];
|
|
9987
|
+
for (const [key, entry] of entries) {
|
|
9988
|
+
if (entry !== null && typeof entry === "object") {
|
|
9989
|
+
if (Array.isArray(entry) && entry.length === 0) {
|
|
9990
|
+
lines.push(`${pad}${key}: []`);
|
|
9991
|
+
continue;
|
|
9992
|
+
}
|
|
9993
|
+
if (!Array.isArray(entry) && Object.keys(entry).length === 0) {
|
|
9994
|
+
lines.push(`${pad}${key}: {}`);
|
|
9995
|
+
continue;
|
|
9996
|
+
}
|
|
9997
|
+
lines.push(`${pad}${key}:`);
|
|
9998
|
+
lines.push(...yamlvalue(entry, indent + 2));
|
|
9999
|
+
} else {
|
|
10000
|
+
lines.push(`${pad}${key}: ${yamlscalar(entry)}`);
|
|
10001
|
+
}
|
|
10002
|
+
}
|
|
10003
|
+
return lines;
|
|
10004
|
+
}
|
|
10005
|
+
function yamlblock(lines, start, indent) {
|
|
10006
|
+
const first = lines[start];
|
|
10007
|
+
if (/^\s*-\s/.test(first) || /^\s*-$/.test(first)) {
|
|
10008
|
+
const items = [];
|
|
10009
|
+
let index2 = start;
|
|
10010
|
+
while (index2 < lines.length) {
|
|
10011
|
+
const line = lines[index2];
|
|
10012
|
+
if (indentof(line) !== indent || !/^\s*-\s?/.test(line)) break;
|
|
10013
|
+
const rest = line.slice(indent + 1).trim();
|
|
10014
|
+
if (rest !== "") {
|
|
10015
|
+
items.push(yamlscalarvalue(rest));
|
|
10016
|
+
index2 += 1;
|
|
10017
|
+
continue;
|
|
10018
|
+
}
|
|
10019
|
+
const nested = yamlblock(lines, index2 + 1, indent + 2);
|
|
10020
|
+
items.push(nested.value);
|
|
10021
|
+
index2 = nested.next;
|
|
10022
|
+
}
|
|
10023
|
+
return { value: items, next: index2 };
|
|
10024
|
+
}
|
|
10025
|
+
const mapping = {};
|
|
10026
|
+
let index = start;
|
|
10027
|
+
while (index < lines.length) {
|
|
10028
|
+
const line = lines[index];
|
|
10029
|
+
if (indentof(line) !== indent) break;
|
|
10030
|
+
const match = /^([A-Za-z][A-Za-z0-9]*):(?:\s(.*))?$/.exec(line.slice(indent));
|
|
10031
|
+
if (!match) break;
|
|
10032
|
+
const key = match[1];
|
|
10033
|
+
const rest = match[2];
|
|
10034
|
+
if (rest !== void 0 && rest !== "") {
|
|
10035
|
+
if (rest === "[]") {
|
|
10036
|
+
mapping[key] = [];
|
|
10037
|
+
index += 1;
|
|
10038
|
+
continue;
|
|
10039
|
+
}
|
|
10040
|
+
if (rest === "{}") {
|
|
10041
|
+
mapping[key] = {};
|
|
10042
|
+
index += 1;
|
|
10043
|
+
continue;
|
|
10044
|
+
}
|
|
10045
|
+
mapping[key] = yamlscalarvalue(rest);
|
|
10046
|
+
index += 1;
|
|
10047
|
+
continue;
|
|
10048
|
+
}
|
|
10049
|
+
const nested = yamlblock(lines, index + 1, indent + 2);
|
|
10050
|
+
mapping[key] = nested.value;
|
|
10051
|
+
index = nested.next;
|
|
10052
|
+
}
|
|
10053
|
+
if (index === start) throw new Error("The yaml workflow file left the documented subset.");
|
|
10054
|
+
return { value: mapping, next: index };
|
|
10055
|
+
}
|
|
10056
|
+
function yamlscalarvalue(text2) {
|
|
10057
|
+
if (text2.startsWith('"')) {
|
|
10058
|
+
const parsed = JSON.parse(text2);
|
|
10059
|
+
return typeof parsed === "string" ? parsed : text2;
|
|
10060
|
+
}
|
|
10061
|
+
if (text2 === "true") return true;
|
|
10062
|
+
if (text2 === "false") return false;
|
|
10063
|
+
if (text2 === "null") return null;
|
|
10064
|
+
if (/^-?\d+(?:\.\d+)?$/.test(text2)) return Number(text2);
|
|
10065
|
+
return text2;
|
|
10066
|
+
}
|
|
8437
10067
|
export {
|
|
8438
10068
|
activelayers,
|
|
10069
|
+
addedge,
|
|
10070
|
+
addnode,
|
|
8439
10071
|
agentgrammarvalid,
|
|
8440
10072
|
agentpresetof,
|
|
8441
10073
|
allowlistcovers,
|
|
@@ -8445,12 +10077,15 @@ export {
|
|
|
8445
10077
|
apientries,
|
|
8446
10078
|
apikeyconsentgranted,
|
|
8447
10079
|
apireplayspecof,
|
|
10080
|
+
applycooldown,
|
|
8448
10081
|
applyheaderules,
|
|
8449
10082
|
applylayer,
|
|
10083
|
+
applyoverride,
|
|
8450
10084
|
applyretry,
|
|
8451
10085
|
applyruntimeout,
|
|
8452
10086
|
applytimeout,
|
|
8453
10087
|
argkind,
|
|
10088
|
+
armrule,
|
|
8454
10089
|
assetentries,
|
|
8455
10090
|
attachcdpsession,
|
|
8456
10091
|
attachtargetof,
|
|
@@ -8460,6 +10095,7 @@ export {
|
|
|
8460
10095
|
authreport,
|
|
8461
10096
|
autointervalof,
|
|
8462
10097
|
backoffdelay,
|
|
10098
|
+
bindparam,
|
|
8463
10099
|
bindvariables,
|
|
8464
10100
|
blackboxedurls,
|
|
8465
10101
|
blackboxmatches,
|
|
@@ -8479,6 +10115,7 @@ export {
|
|
|
8479
10115
|
buildname,
|
|
8480
10116
|
buildpdf,
|
|
8481
10117
|
buildsheet,
|
|
10118
|
+
buildsteplibrary,
|
|
8482
10119
|
buildstitchplan,
|
|
8483
10120
|
callgraphql,
|
|
8484
10121
|
callrest,
|
|
@@ -8513,6 +10150,7 @@ export {
|
|
|
8513
10150
|
collectmessages,
|
|
8514
10151
|
composeworkflow,
|
|
8515
10152
|
conditionof,
|
|
10153
|
+
confirmmanualrun,
|
|
8516
10154
|
consolecapture,
|
|
8517
10155
|
consoleconsentcovers,
|
|
8518
10156
|
consolediff,
|
|
@@ -8531,6 +10169,8 @@ export {
|
|
|
8531
10169
|
correlationid,
|
|
8532
10170
|
cpusnap,
|
|
8533
10171
|
crashinterrupted,
|
|
10172
|
+
cronnext,
|
|
10173
|
+
cronparse,
|
|
8534
10174
|
croprect,
|
|
8535
10175
|
crossesviewport,
|
|
8536
10176
|
cursorfrom,
|
|
@@ -8540,6 +10180,7 @@ export {
|
|
|
8540
10180
|
debugwaitbudgetallowed,
|
|
8541
10181
|
dedupeimages,
|
|
8542
10182
|
defaultloopbound,
|
|
10183
|
+
defaulttriggercooldown,
|
|
8543
10184
|
delayjitter,
|
|
8544
10185
|
actionrisk as deriveactionrisk,
|
|
8545
10186
|
detachcdpsession,
|
|
@@ -8547,9 +10188,14 @@ export {
|
|
|
8547
10188
|
diffresponse,
|
|
8548
10189
|
diffreviewgrade,
|
|
8549
10190
|
diffsessionrecords,
|
|
10191
|
+
diffversions,
|
|
8550
10192
|
downloadreport,
|
|
10193
|
+
drainqueue,
|
|
8551
10194
|
dryrunprojection,
|
|
8552
10195
|
dryrunworkflow,
|
|
10196
|
+
editorsavegate,
|
|
10197
|
+
editorstate,
|
|
10198
|
+
editstep,
|
|
8553
10199
|
emugate,
|
|
8554
10200
|
emulationkinds,
|
|
8555
10201
|
emulationreport,
|
|
@@ -8559,14 +10205,19 @@ export {
|
|
|
8559
10205
|
errorcapture,
|
|
8560
10206
|
errorreportresponse,
|
|
8561
10207
|
evaluatecondition,
|
|
10208
|
+
evaluatetrigger,
|
|
8562
10209
|
eventresponse,
|
|
10210
|
+
eventrulematches,
|
|
8563
10211
|
exchangesreport,
|
|
8564
10212
|
expandblocks,
|
|
10213
|
+
expandtemplate,
|
|
8565
10214
|
expirelayers,
|
|
8566
10215
|
expireprofilerecords,
|
|
8567
10216
|
expiresessions,
|
|
10217
|
+
exportcontentreview,
|
|
8568
10218
|
exportpresetlibrary,
|
|
8569
10219
|
exportsessionfile,
|
|
10220
|
+
exportworkflow,
|
|
8570
10221
|
expressioneval,
|
|
8571
10222
|
expressionof,
|
|
8572
10223
|
expressionoperators,
|
|
@@ -8590,6 +10241,7 @@ export {
|
|
|
8590
10241
|
generatedvalueallowed,
|
|
8591
10242
|
graphqlopenvelope,
|
|
8592
10243
|
graphqlrequestof,
|
|
10244
|
+
groupselect,
|
|
8593
10245
|
growsampleof,
|
|
8594
10246
|
growthtrend,
|
|
8595
10247
|
headerfilterof,
|
|
@@ -8606,6 +10258,7 @@ export {
|
|
|
8606
10258
|
imagenames,
|
|
8607
10259
|
importpresetlibrary,
|
|
8608
10260
|
importsessionfile,
|
|
10261
|
+
importworkflow,
|
|
8609
10262
|
iscdpkind,
|
|
8610
10263
|
iscontrolflowkind,
|
|
8611
10264
|
iscontrolkind,
|
|
@@ -8616,6 +10269,8 @@ export {
|
|
|
8616
10269
|
isprofilekind,
|
|
8617
10270
|
issessionkind,
|
|
8618
10271
|
issocketkind,
|
|
10272
|
+
istriggeraction,
|
|
10273
|
+
istriggerkind,
|
|
8619
10274
|
iswatchkind,
|
|
8620
10275
|
isworkflowkind,
|
|
8621
10276
|
joinbranches,
|
|
@@ -8625,6 +10280,8 @@ export {
|
|
|
8625
10280
|
layernames,
|
|
8626
10281
|
layoutreport,
|
|
8627
10282
|
levelrank,
|
|
10283
|
+
listdue,
|
|
10284
|
+
loadworkflow,
|
|
8628
10285
|
locationconsentcovers,
|
|
8629
10286
|
locationconsentgate,
|
|
8630
10287
|
locationpresetof,
|
|
@@ -8632,9 +10289,13 @@ export {
|
|
|
8632
10289
|
loglevels,
|
|
8633
10290
|
longtaskcapture,
|
|
8634
10291
|
loopof,
|
|
10292
|
+
manualpreview,
|
|
10293
|
+
manualrunpreview,
|
|
8635
10294
|
mapresponse,
|
|
8636
10295
|
mapurlof,
|
|
10296
|
+
markbreakpoint,
|
|
8637
10297
|
matchmessage,
|
|
10298
|
+
matchurl,
|
|
8638
10299
|
matchurlpattern,
|
|
8639
10300
|
measure,
|
|
8640
10301
|
mediaentries,
|
|
@@ -8642,6 +10303,7 @@ export {
|
|
|
8642
10303
|
mediareport,
|
|
8643
10304
|
messagefilterof,
|
|
8644
10305
|
methoddomain,
|
|
10306
|
+
minimapfocus,
|
|
8645
10307
|
mockfor,
|
|
8646
10308
|
mockspecof,
|
|
8647
10309
|
multipartchunks,
|
|
@@ -8665,12 +10327,15 @@ export {
|
|
|
8665
10327
|
oauthflowof,
|
|
8666
10328
|
observationmodeof,
|
|
8667
10329
|
observationresponse,
|
|
10330
|
+
observeevents,
|
|
8668
10331
|
openchannel,
|
|
8669
10332
|
outcomeresponse,
|
|
8670
10333
|
overrideinputof,
|
|
8671
10334
|
overridematches,
|
|
8672
10335
|
pairexchange,
|
|
8673
10336
|
pairstates,
|
|
10337
|
+
palettecategories,
|
|
10338
|
+
palettenodes,
|
|
8674
10339
|
parallelof,
|
|
8675
10340
|
parsehtmlbody,
|
|
8676
10341
|
parseproposal,
|
|
@@ -8679,6 +10344,7 @@ export {
|
|
|
8679
10344
|
parseworkflowproposal,
|
|
8680
10345
|
passwordconsentgranted,
|
|
8681
10346
|
patternorigin,
|
|
10347
|
+
pauseall,
|
|
8682
10348
|
pauseretentionwindow,
|
|
8683
10349
|
pauserun,
|
|
8684
10350
|
payloadshapeof,
|
|
@@ -8710,6 +10376,7 @@ export {
|
|
|
8710
10376
|
publishmessage,
|
|
8711
10377
|
pushscope,
|
|
8712
10378
|
quarantinereport,
|
|
10379
|
+
queuefire,
|
|
8713
10380
|
randomid,
|
|
8714
10381
|
rankapis,
|
|
8715
10382
|
ratelimitbudgetallowed,
|
|
@@ -8723,10 +10390,15 @@ export {
|
|
|
8723
10390
|
recordwatchvalue,
|
|
8724
10391
|
redactconsoletext,
|
|
8725
10392
|
redactedcookies,
|
|
10393
|
+
redoedit,
|
|
8726
10394
|
regexextract,
|
|
8727
10395
|
regexruleof,
|
|
8728
10396
|
regionsteps,
|
|
8729
10397
|
rejectioncapture,
|
|
10398
|
+
removeedge,
|
|
10399
|
+
removenode,
|
|
10400
|
+
renderminimap,
|
|
10401
|
+
reordersteps,
|
|
8730
10402
|
repeatuntilof,
|
|
8731
10403
|
replaytrace,
|
|
8732
10404
|
replayurl,
|
|
@@ -8738,31 +10410,44 @@ export {
|
|
|
8738
10410
|
restoreoriginsgranted,
|
|
8739
10411
|
restoreplanof,
|
|
8740
10412
|
restorereviewgranted,
|
|
10413
|
+
resumeall,
|
|
8741
10414
|
retryafterof,
|
|
8742
10415
|
revertalllayers,
|
|
8743
10416
|
revertlayer,
|
|
8744
10417
|
revertplanof,
|
|
8745
10418
|
revertrule,
|
|
10419
|
+
reviewedkinds,
|
|
8746
10420
|
revocationruleof,
|
|
8747
10421
|
rewritesourcelocation,
|
|
8748
10422
|
rotatelogs,
|
|
8749
10423
|
rotationruleof,
|
|
10424
|
+
ruleorigins,
|
|
10425
|
+
ruleoriginsgranted,
|
|
8750
10426
|
runcatch,
|
|
8751
10427
|
runcontrolstep,
|
|
8752
10428
|
runforeach,
|
|
10429
|
+
runhistoryquery,
|
|
10430
|
+
runhistoryreport,
|
|
8753
10431
|
runloop,
|
|
8754
10432
|
runparallel,
|
|
8755
10433
|
runrepeatuntil,
|
|
10434
|
+
runreviewgranted,
|
|
8756
10435
|
runstep,
|
|
10436
|
+
runtobreakpoint,
|
|
8757
10437
|
runtry,
|
|
10438
|
+
runurllist,
|
|
8758
10439
|
runwhile,
|
|
8759
10440
|
runworkflow,
|
|
8760
10441
|
safetyresponse,
|
|
10442
|
+
saveworkflow,
|
|
8761
10443
|
scaledrect,
|
|
10444
|
+
schedulecron,
|
|
10445
|
+
scheduleinterval,
|
|
8762
10446
|
seamweights,
|
|
8763
10447
|
searchfields,
|
|
8764
10448
|
searchqueryof,
|
|
8765
10449
|
searchsessionrecords,
|
|
10450
|
+
searchsteps,
|
|
8766
10451
|
seededrandom,
|
|
8767
10452
|
selectorresponse,
|
|
8768
10453
|
sendcdpcommand,
|
|
@@ -8780,8 +10465,10 @@ export {
|
|
|
8780
10465
|
sessionrestoregate,
|
|
8781
10466
|
sessiontabof,
|
|
8782
10467
|
setvariable,
|
|
10468
|
+
shareworkflow,
|
|
8783
10469
|
shiftentryof,
|
|
8784
10470
|
signalsreport,
|
|
10471
|
+
snapnode,
|
|
8785
10472
|
snapshotplanof,
|
|
8786
10473
|
snapshotretentionwindow,
|
|
8787
10474
|
snapshotsections,
|
|
@@ -8818,6 +10505,7 @@ export {
|
|
|
8818
10505
|
timelinereport,
|
|
8819
10506
|
timelineretentionwindow,
|
|
8820
10507
|
timelinesources,
|
|
10508
|
+
timezonevalid,
|
|
8821
10509
|
tokenrequest,
|
|
8822
10510
|
tracecategories,
|
|
8823
10511
|
traceceilingof,
|
|
@@ -8825,30 +10513,50 @@ export {
|
|
|
8825
10513
|
tracetofile,
|
|
8826
10514
|
trailreport,
|
|
8827
10515
|
transformgrammar,
|
|
10516
|
+
triggereventcatalog,
|
|
10517
|
+
triggerfamilies,
|
|
10518
|
+
triggerfamilyof,
|
|
10519
|
+
triggerfired,
|
|
10520
|
+
triggergate,
|
|
10521
|
+
triggerkinds,
|
|
10522
|
+
triggerlist,
|
|
10523
|
+
triggerorigins,
|
|
10524
|
+
triggerpayloadof,
|
|
10525
|
+
triggersummary,
|
|
8828
10526
|
tryof,
|
|
10527
|
+
undoedit,
|
|
8829
10528
|
unwrapgraphql,
|
|
10529
|
+
updaterule,
|
|
8830
10530
|
urlencodeform,
|
|
8831
10531
|
validatebreakpointcondition,
|
|
8832
10532
|
validatecontrolpayload,
|
|
8833
10533
|
validatefieldmatch,
|
|
8834
10534
|
validateformrecord,
|
|
8835
10535
|
validateregexrule,
|
|
10536
|
+
validatesiteoverride,
|
|
8836
10537
|
validatestep,
|
|
8837
10538
|
validatetargetref,
|
|
8838
10539
|
validatevaluegen,
|
|
8839
10540
|
validateworkflow,
|
|
10541
|
+
verifywebhook,
|
|
10542
|
+
visitmatch,
|
|
8840
10543
|
waitelementplan,
|
|
8841
10544
|
watchcdpevents,
|
|
10545
|
+
watchdogconfigvalid,
|
|
10546
|
+
watchdogpass,
|
|
8842
10547
|
watcherdetached,
|
|
8843
10548
|
watchexpressionof,
|
|
8844
10549
|
watchgate,
|
|
10550
|
+
webhooksecretok,
|
|
8845
10551
|
whileof,
|
|
8846
10552
|
wizardreport,
|
|
8847
10553
|
workflowblockof,
|
|
10554
|
+
workflowfileversion,
|
|
8848
10555
|
workflowgate,
|
|
8849
10556
|
workflowkinds,
|
|
8850
10557
|
workflowoutcome,
|
|
8851
10558
|
workflowreport,
|
|
8852
|
-
workflowstepof
|
|
10559
|
+
workflowstepof,
|
|
10560
|
+
zoomcanvas
|
|
8853
10561
|
};
|
|
8854
10562
|
//# sourceMappingURL=index.js.map
|