@sverka/playground 0.0.0 → 0.1.1
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/dist/index.d.mts +127 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +167 -0
- package/dist/index.mjs.map +1 -0
- package/index.html +115 -0
- package/package.json +40 -10
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
/** Severity levels ordered from least to most severe. */
|
|
3
|
+
type Severity = "info" | "low" | "medium" | "high" | "critical";
|
|
4
|
+
/** Source tool that produced a finding. */
|
|
5
|
+
interface FindingSource {
|
|
6
|
+
tool: string;
|
|
7
|
+
version: string | null;
|
|
8
|
+
format: "sarif" | "json" | "text" | "custom";
|
|
9
|
+
originalRuleId: string;
|
|
10
|
+
originalSeverity: string | null;
|
|
11
|
+
}
|
|
12
|
+
/** A normalized finding — structurally compatible with @sverka/verification Finding. */
|
|
13
|
+
interface Finding {
|
|
14
|
+
id: string;
|
|
15
|
+
fingerprint: string;
|
|
16
|
+
checkId: string;
|
|
17
|
+
severity: Severity;
|
|
18
|
+
confidence: number;
|
|
19
|
+
message: string;
|
|
20
|
+
rule: string;
|
|
21
|
+
file: string;
|
|
22
|
+
startLine: number;
|
|
23
|
+
endLine: number;
|
|
24
|
+
startColumn?: number;
|
|
25
|
+
endColumn?: number;
|
|
26
|
+
helpUrl?: string;
|
|
27
|
+
source: FindingSource;
|
|
28
|
+
snippet?: string;
|
|
29
|
+
}
|
|
30
|
+
/** Simplified finding that FunctionStep functions return. The runner
|
|
31
|
+
* fills in fingerprint, id, and source defaults. */
|
|
32
|
+
interface PlaygroundFinding {
|
|
33
|
+
rule: string;
|
|
34
|
+
file: string;
|
|
35
|
+
line: number;
|
|
36
|
+
severity: Severity;
|
|
37
|
+
message: string;
|
|
38
|
+
tool?: string;
|
|
39
|
+
snippet?: string;
|
|
40
|
+
}
|
|
41
|
+
/** Result of running a single step. */
|
|
42
|
+
interface StepResult {
|
|
43
|
+
stepId: string;
|
|
44
|
+
status: "success" | "failure";
|
|
45
|
+
findings: Finding[];
|
|
46
|
+
durationMs: number;
|
|
47
|
+
error?: string;
|
|
48
|
+
}
|
|
49
|
+
/** Result of running a full pipeline. */
|
|
50
|
+
interface PipelineResult {
|
|
51
|
+
steps: StepResult[];
|
|
52
|
+
findings: Finding[];
|
|
53
|
+
totalDurationMs: number;
|
|
54
|
+
success: boolean;
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/pipeline.d.ts
|
|
58
|
+
/** Base class for all constructs. */
|
|
59
|
+
declare abstract class Construct {
|
|
60
|
+
readonly id: string;
|
|
61
|
+
readonly scope: Construct | null;
|
|
62
|
+
constructor(scope: Construct | null, id: string);
|
|
63
|
+
/** Full path from root to this construct. */
|
|
64
|
+
get path(): string;
|
|
65
|
+
}
|
|
66
|
+
/** Root construct representing a project. */
|
|
67
|
+
export declare class Project extends Construct {
|
|
68
|
+
readonly pipelines: Pipeline[];
|
|
69
|
+
constructor(id: string);
|
|
70
|
+
/** Add a pipeline to this project. */
|
|
71
|
+
addPipeline(pipeline: Pipeline): void;
|
|
72
|
+
}
|
|
73
|
+
/** A pipeline containing steps. */
|
|
74
|
+
export declare class Pipeline extends Construct {
|
|
75
|
+
readonly steps: Step[];
|
|
76
|
+
readonly entries: Entry[];
|
|
77
|
+
constructor(project: Project, id: string);
|
|
78
|
+
/** Add a step to this pipeline. */
|
|
79
|
+
addStep(step: Step): void;
|
|
80
|
+
/** Add an entry to this pipeline. */
|
|
81
|
+
addEntry(entry: Entry): void;
|
|
82
|
+
}
|
|
83
|
+
/** Base class for all step types. */
|
|
84
|
+
declare abstract class Step extends Construct {
|
|
85
|
+
readonly dependencies: string[];
|
|
86
|
+
constructor(pipeline: Pipeline, id: string, dependencies?: string[]);
|
|
87
|
+
/** Execute this step and return findings. */
|
|
88
|
+
abstract execute(): Promise<PlaygroundFinding[]> | PlaygroundFinding[];
|
|
89
|
+
}
|
|
90
|
+
/** A step that runs a JavaScript function in the browser. */
|
|
91
|
+
export declare class FunctionStep extends Step {
|
|
92
|
+
readonly fn: () => Promise<PlaygroundFinding[]> | PlaygroundFinding[];
|
|
93
|
+
constructor(pipeline: Pipeline, id: string, options: {
|
|
94
|
+
fn: () => Promise<PlaygroundFinding[]> | PlaygroundFinding[];
|
|
95
|
+
dependencies?: string[];
|
|
96
|
+
});
|
|
97
|
+
execute(): Promise<PlaygroundFinding[]> | PlaygroundFinding[];
|
|
98
|
+
}
|
|
99
|
+
/** An entry point that triggers a pipeline. */
|
|
100
|
+
export declare class Entry {
|
|
101
|
+
readonly pipeline: Pipeline;
|
|
102
|
+
readonly id: string;
|
|
103
|
+
readonly trigger: {
|
|
104
|
+
kind: string;
|
|
105
|
+
};
|
|
106
|
+
readonly roots: string[];
|
|
107
|
+
constructor(pipeline: Pipeline, id: string, options: {
|
|
108
|
+
trigger: {
|
|
109
|
+
kind: string;
|
|
110
|
+
};
|
|
111
|
+
roots: string[];
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/runner.d.ts
|
|
116
|
+
/**
|
|
117
|
+
* Run all FunctionSteps in a project's pipelines and collect findings.
|
|
118
|
+
* Browser-safe — no Node.js APIs, no child_process, no fs.
|
|
119
|
+
*
|
|
120
|
+
* If a pipeline has entries with `roots`, only steps whose id matches a
|
|
121
|
+
* root are executed. If no entries exist or all entries have empty roots,
|
|
122
|
+
* all steps run.
|
|
123
|
+
*/
|
|
124
|
+
export declare function runPipeline(project: Project): Promise<PipelineResult>;
|
|
125
|
+
//#endregion
|
|
126
|
+
export type { Construct, Finding, FindingSource, PipelineResult, PlaygroundFinding, Severity, Step, StepResult };
|
|
127
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/pipeline.ts","../src/runner.ts"],"mappings":";;KAGY;;UAGK;EACf;EACA;EACA;EACA;EACA;;;UAIe;EACf;EACA;EACA;EACA,UAAU;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;EACR;;;;UAKe;EACf;EACA;EACA;EACA,UAAU;EACV;EACA;EACA;;;UAIe;EACf;EACA;EACA,UAAU;EACV;EACA;;;UAIe;EACf,OAAO;EACP,UAAU;EACV;EACA;;;;;uBCrDoB;WACX;WACA,OAAO;EAEJ,YAAA,OAAO,kBAAkB;;MAMjC;;;qBASO,gBAAgB;WAClB,WAAW;EAER,YAAA;;EAKZ,YAAY,UAAU;;;qBAMX,iBAAiB;WACnB,OAAO;WACP,SAAS;EAEN,YAAA,SAAS,SAAS;;EAM9B,QAAQ,MAAM;;EAKd,SAAS,OAAO;;;uBAMI,aAAa;WACxB;EAEG,YAAA,UAAU,UAAU,YAAY;;WAOnC,WAAW,QAAQ,uBAAuB;;;qBAIxC,qBAAqB;WACvB,UAAU,QAAQ,uBAAuB;EAGhD,YAAA,UAAU,UACV,YACA;IACE,UAAU,QAAQ,uBAAuB;IACzC;;EAOJ,WAAW,QAAQ,uBAAuB;;;qBAM/B;WACF,UAAU;WACV;WACA;IAAW;;WACX;EAGP,YAAA,UAAU,UACV,YACA;IAAW;MAAW;;IAAgB;;;;;;;;;;;;;wBC3BpB,YAAY,SAAS,UAAU,QAAQ"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
//#region src/pipeline.ts
|
|
2
|
+
/** Base class for all constructs. */
|
|
3
|
+
var Construct = class {
|
|
4
|
+
id;
|
|
5
|
+
scope;
|
|
6
|
+
constructor(scope, id) {
|
|
7
|
+
this.id = id;
|
|
8
|
+
this.scope = scope;
|
|
9
|
+
}
|
|
10
|
+
/** Full path from root to this construct. */
|
|
11
|
+
get path() {
|
|
12
|
+
if (this.scope && this.scope.id !== "root") return `${this.scope.path}/${this.id}`;
|
|
13
|
+
return this.id;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
/** Root construct representing a project. */
|
|
17
|
+
var Project = class extends Construct {
|
|
18
|
+
pipelines = [];
|
|
19
|
+
constructor(id) {
|
|
20
|
+
super(null, id);
|
|
21
|
+
}
|
|
22
|
+
/** Add a pipeline to this project. */
|
|
23
|
+
addPipeline(pipeline) {
|
|
24
|
+
this.pipelines.push(pipeline);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
/** A pipeline containing steps. */
|
|
28
|
+
var Pipeline = class extends Construct {
|
|
29
|
+
steps = [];
|
|
30
|
+
entries = [];
|
|
31
|
+
constructor(project, id) {
|
|
32
|
+
super(project, id);
|
|
33
|
+
project.addPipeline(this);
|
|
34
|
+
}
|
|
35
|
+
/** Add a step to this pipeline. */
|
|
36
|
+
addStep(step) {
|
|
37
|
+
this.steps.push(step);
|
|
38
|
+
}
|
|
39
|
+
/** Add an entry to this pipeline. */
|
|
40
|
+
addEntry(entry) {
|
|
41
|
+
this.entries.push(entry);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
/** Base class for all step types. */
|
|
45
|
+
var Step = class extends Construct {
|
|
46
|
+
dependencies;
|
|
47
|
+
constructor(pipeline, id, dependencies = []) {
|
|
48
|
+
super(pipeline, id);
|
|
49
|
+
this.dependencies = dependencies;
|
|
50
|
+
pipeline.addStep(this);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
/** A step that runs a JavaScript function in the browser. */
|
|
54
|
+
var FunctionStep = class extends Step {
|
|
55
|
+
fn;
|
|
56
|
+
constructor(pipeline, id, options) {
|
|
57
|
+
super(pipeline, id, options.dependencies ?? []);
|
|
58
|
+
this.fn = options.fn;
|
|
59
|
+
}
|
|
60
|
+
execute() {
|
|
61
|
+
return this.fn();
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
/** An entry point that triggers a pipeline. */
|
|
65
|
+
var Entry = class {
|
|
66
|
+
pipeline;
|
|
67
|
+
id;
|
|
68
|
+
trigger;
|
|
69
|
+
roots;
|
|
70
|
+
constructor(pipeline, id, options) {
|
|
71
|
+
this.pipeline = pipeline;
|
|
72
|
+
this.id = id;
|
|
73
|
+
this.trigger = options.trigger;
|
|
74
|
+
this.roots = options.roots;
|
|
75
|
+
pipeline.addEntry(this);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/runner.ts
|
|
80
|
+
/** Simple deterministic hash for fingerprint generation. Browser-safe. */
|
|
81
|
+
function simpleHash(input) {
|
|
82
|
+
let hash = 5381;
|
|
83
|
+
for (let i = 0; i < input.length; i++) hash = (hash << 5) + hash + input.charCodeAt(i) | 0;
|
|
84
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
85
|
+
}
|
|
86
|
+
/** Convert a PlaygroundFinding to a full Finding with fingerprint and id. */
|
|
87
|
+
function toFinding(pf, stepId) {
|
|
88
|
+
const fingerprint = simpleHash(`${stepId}:${pf.rule}:${pf.file}:${pf.line}`);
|
|
89
|
+
return {
|
|
90
|
+
id: `${stepId}:${fingerprint}`,
|
|
91
|
+
fingerprint,
|
|
92
|
+
checkId: stepId,
|
|
93
|
+
severity: pf.severity,
|
|
94
|
+
confidence: .8,
|
|
95
|
+
message: pf.message,
|
|
96
|
+
rule: pf.rule,
|
|
97
|
+
file: pf.file,
|
|
98
|
+
startLine: pf.line,
|
|
99
|
+
endLine: pf.line,
|
|
100
|
+
source: {
|
|
101
|
+
tool: pf.tool ?? stepId,
|
|
102
|
+
version: null,
|
|
103
|
+
format: "custom",
|
|
104
|
+
originalRuleId: pf.rule,
|
|
105
|
+
originalSeverity: null
|
|
106
|
+
},
|
|
107
|
+
...pf.snippet ? { snippet: pf.snippet } : {}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** Execute a single step and return its result. */
|
|
111
|
+
async function executeStep(step) {
|
|
112
|
+
const start = Date.now();
|
|
113
|
+
try {
|
|
114
|
+
const findings = (await step.execute()).map((pf) => toFinding(pf, step.id));
|
|
115
|
+
return {
|
|
116
|
+
stepId: step.id,
|
|
117
|
+
status: "success",
|
|
118
|
+
findings,
|
|
119
|
+
durationMs: Date.now() - start
|
|
120
|
+
};
|
|
121
|
+
} catch (e) {
|
|
122
|
+
return {
|
|
123
|
+
stepId: step.id,
|
|
124
|
+
status: "failure",
|
|
125
|
+
findings: [],
|
|
126
|
+
durationMs: Date.now() - start,
|
|
127
|
+
error: e instanceof Error ? e.message : String(e)
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Run all FunctionSteps in a project's pipelines and collect findings.
|
|
133
|
+
* Browser-safe — no Node.js APIs, no child_process, no fs.
|
|
134
|
+
*
|
|
135
|
+
* If a pipeline has entries with `roots`, only steps whose id matches a
|
|
136
|
+
* root are executed. If no entries exist or all entries have empty roots,
|
|
137
|
+
* all steps run.
|
|
138
|
+
*/
|
|
139
|
+
async function runPipeline(project) {
|
|
140
|
+
const start = Date.now();
|
|
141
|
+
const stepResults = [];
|
|
142
|
+
const allFindings = [];
|
|
143
|
+
for (const pipeline of project.pipelines) {
|
|
144
|
+
let stepsToRun = pipeline.steps;
|
|
145
|
+
const entriesWithRoots = pipeline.entries.filter((e) => e.roots.length > 0);
|
|
146
|
+
if (entriesWithRoots.length > 0) {
|
|
147
|
+
const rootSet = /* @__PURE__ */ new Set();
|
|
148
|
+
for (const entry of entriesWithRoots) for (const root of entry.roots) rootSet.add(root);
|
|
149
|
+
stepsToRun = pipeline.steps.filter((s) => rootSet.has(s.id));
|
|
150
|
+
}
|
|
151
|
+
const results = await Promise.all(stepsToRun.map((step) => executeStep(step)));
|
|
152
|
+
for (const result of results) {
|
|
153
|
+
stepResults.push(result);
|
|
154
|
+
allFindings.push(...result.findings);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
steps: stepResults,
|
|
159
|
+
findings: allFindings,
|
|
160
|
+
totalDurationMs: Date.now() - start,
|
|
161
|
+
success: stepResults.every((s) => s.status === "success")
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
//#endregion
|
|
165
|
+
export { Entry, FunctionStep, Pipeline, Project, runPipeline };
|
|
166
|
+
|
|
167
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/pipeline.ts","../src/runner.ts"],"sourcesContent":["// @sverka/playground — minimal pipeline model. Browser-safe.\n// Mirrors @sverka/workflow Construct API but simplified for browser use.\n\nimport type { PlaygroundFinding } from \"./types.js\";\n\n/** Base class for all constructs. */\nexport abstract class Construct {\n readonly id: string;\n readonly scope: Construct | null;\n\n constructor(scope: Construct | null, id: string) {\n this.id = id;\n this.scope = scope;\n }\n\n /** Full path from root to this construct. */\n get path(): string {\n if (this.scope && this.scope.id !== \"root\") {\n return `${this.scope.path}/${this.id}`;\n }\n return this.id;\n }\n}\n\n/** Root construct representing a project. */\nexport class Project extends Construct {\n readonly pipelines: Pipeline[] = [];\n\n constructor(id: string) {\n super(null, id);\n }\n\n /** Add a pipeline to this project. */\n addPipeline(pipeline: Pipeline): void {\n this.pipelines.push(pipeline);\n }\n}\n\n/** A pipeline containing steps. */\nexport class Pipeline extends Construct {\n readonly steps: Step[] = [];\n readonly entries: Entry[] = [];\n\n constructor(project: Project, id: string) {\n super(project, id);\n project.addPipeline(this);\n }\n\n /** Add a step to this pipeline. */\n addStep(step: Step): void {\n this.steps.push(step);\n }\n\n /** Add an entry to this pipeline. */\n addEntry(entry: Entry): void {\n this.entries.push(entry);\n }\n}\n\n/** Base class for all step types. */\nexport abstract class Step extends Construct {\n readonly dependencies: string[];\n\n constructor(pipeline: Pipeline, id: string, dependencies: string[] = []) {\n super(pipeline, id);\n this.dependencies = dependencies;\n pipeline.addStep(this);\n }\n\n /** Execute this step and return findings. */\n abstract execute(): Promise<PlaygroundFinding[]> | PlaygroundFinding[];\n}\n\n/** A step that runs a JavaScript function in the browser. */\nexport class FunctionStep extends Step {\n readonly fn: () => Promise<PlaygroundFinding[]> | PlaygroundFinding[];\n\n constructor(\n pipeline: Pipeline,\n id: string,\n options: {\n fn: () => Promise<PlaygroundFinding[]> | PlaygroundFinding[];\n dependencies?: string[];\n },\n ) {\n super(pipeline, id, options.dependencies ?? []);\n this.fn = options.fn;\n }\n\n execute(): Promise<PlaygroundFinding[]> | PlaygroundFinding[] {\n return this.fn();\n }\n}\n\n/** An entry point that triggers a pipeline. */\nexport class Entry {\n readonly pipeline: Pipeline;\n readonly id: string;\n readonly trigger: { kind: string };\n readonly roots: string[];\n\n constructor(\n pipeline: Pipeline,\n id: string,\n options: { trigger: { kind: string }; roots: string[] },\n ) {\n this.pipeline = pipeline;\n this.id = id;\n this.trigger = options.trigger;\n this.roots = options.roots;\n pipeline.addEntry(this);\n }\n}\n","// @sverka/playground — pipeline runner. Browser-safe.\n// Executes FunctionSteps and collects findings.\n\nimport type { Project } from \"./pipeline.js\";\nimport type { Pipeline } from \"./pipeline.js\";\nimport type { Step } from \"./pipeline.js\";\nimport type { Finding, PlaygroundFinding, PipelineResult, StepResult } from \"./types.js\";\n\n/** Simple deterministic hash for fingerprint generation. Browser-safe. */\nfunction simpleHash(input: string): string {\n let hash = 5381;\n for (let i = 0; i < input.length; i++) {\n hash = ((hash << 5) + hash + input.charCodeAt(i)) | 0;\n }\n return (hash >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/** Convert a PlaygroundFinding to a full Finding with fingerprint and id. */\nfunction toFinding(pf: PlaygroundFinding, stepId: string): Finding {\n // Include checkId (stepId) in fingerprint so identical findings from\n // different checks get distinct fingerprints.\n const fingerprintInput = `${stepId}:${pf.rule}:${pf.file}:${pf.line}`;\n const fingerprint = simpleHash(fingerprintInput);\n const id = `${stepId}:${fingerprint}`;\n return {\n id,\n fingerprint,\n checkId: stepId,\n severity: pf.severity,\n confidence: 0.8,\n message: pf.message,\n rule: pf.rule,\n file: pf.file,\n startLine: pf.line,\n endLine: pf.line,\n source: {\n tool: pf.tool ?? stepId,\n version: null,\n format: \"custom\",\n originalRuleId: pf.rule,\n originalSeverity: null,\n },\n ...(pf.snippet ? { snippet: pf.snippet } : {}),\n };\n}\n\n/** Execute a single step and return its result. */\nasync function executeStep(step: Step): Promise<StepResult> {\n const start = Date.now();\n try {\n const playgroundFindings = await step.execute();\n const findings = playgroundFindings.map((pf) => toFinding(pf, step.id));\n return {\n stepId: step.id,\n status: \"success\",\n findings,\n durationMs: Date.now() - start,\n };\n } catch (e) {\n return {\n stepId: step.id,\n status: \"failure\",\n findings: [],\n durationMs: Date.now() - start,\n error: e instanceof Error ? e.message : String(e),\n };\n }\n}\n\n/**\n * Run all FunctionSteps in a project's pipelines and collect findings.\n * Browser-safe — no Node.js APIs, no child_process, no fs.\n *\n * If a pipeline has entries with `roots`, only steps whose id matches a\n * root are executed. If no entries exist or all entries have empty roots,\n * all steps run.\n */\nexport async function runPipeline(project: Project): Promise<PipelineResult> {\n const start = Date.now();\n const stepResults: StepResult[] = [];\n const allFindings: Finding[] = [];\n\n for (const pipeline of project.pipelines) {\n // Determine which steps to run based on entry roots.\n let stepsToRun = pipeline.steps;\n const entriesWithRoots = pipeline.entries.filter((e) => e.roots.length > 0);\n if (entriesWithRoots.length > 0) {\n const rootSet = new Set<string>();\n for (const entry of entriesWithRoots) {\n for (const root of entry.roots) rootSet.add(root);\n }\n stepsToRun = pipeline.steps.filter((s) => rootSet.has(s.id));\n }\n\n // Execute all selected steps in parallel — dependencies are for\n // ordering, not data flow in the playground.\n const results = await Promise.all(\n stepsToRun.map((step) => executeStep(step)),\n );\n\n for (const result of results) {\n stepResults.push(result);\n allFindings.push(...result.findings);\n }\n }\n\n return {\n steps: stepResults,\n findings: allFindings,\n totalDurationMs: Date.now() - start,\n success: stepResults.every((s) => s.status === \"success\"),\n };\n}\n"],"mappings":"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"}
|
package/index.html
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Sverka Playground</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root { color-scheme: dark; }
|
|
9
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
10
|
+
body {
|
|
11
|
+
background: #0d1117;
|
|
12
|
+
color: #c9d1d9;
|
|
13
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
14
|
+
height: 100vh;
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
}
|
|
19
|
+
header {
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: space-between;
|
|
23
|
+
padding: 0.5rem 1rem;
|
|
24
|
+
background: #161b22;
|
|
25
|
+
border-bottom: 1px solid #30363d;
|
|
26
|
+
flex-shrink: 0;
|
|
27
|
+
}
|
|
28
|
+
header h1 { font-size: 1rem; color: #f0f6fc; }
|
|
29
|
+
header .actions { display: flex; gap: 0.5rem; align-items: center; }
|
|
30
|
+
.status { font-size: 0.8rem; color: #8b949e; }
|
|
31
|
+
.status.success { color: #3fb950; }
|
|
32
|
+
.status.failure { color: #f85149; }
|
|
33
|
+
.status.running { color: #d29922; }
|
|
34
|
+
button {
|
|
35
|
+
padding: 0.4rem 1rem;
|
|
36
|
+
border: 1px solid #30363d;
|
|
37
|
+
border-radius: 6px;
|
|
38
|
+
background: #1f6feb;
|
|
39
|
+
color: #fff;
|
|
40
|
+
cursor: pointer;
|
|
41
|
+
font-size: 0.85rem;
|
|
42
|
+
font-weight: 600;
|
|
43
|
+
}
|
|
44
|
+
button:hover { background: #388bfd; }
|
|
45
|
+
button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
46
|
+
.main {
|
|
47
|
+
display: flex;
|
|
48
|
+
flex: 1;
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
}
|
|
51
|
+
.editor-panel {
|
|
52
|
+
flex: 1;
|
|
53
|
+
border-right: 1px solid #30363d;
|
|
54
|
+
display: flex;
|
|
55
|
+
flex-direction: column;
|
|
56
|
+
}
|
|
57
|
+
.panel-header {
|
|
58
|
+
padding: 0.4rem 0.75rem;
|
|
59
|
+
background: #161b22;
|
|
60
|
+
border-bottom: 1px solid #21262d;
|
|
61
|
+
font-size: 0.75rem;
|
|
62
|
+
color: #8b949e;
|
|
63
|
+
font-weight: 600;
|
|
64
|
+
text-transform: uppercase;
|
|
65
|
+
letter-spacing: 0.05em;
|
|
66
|
+
}
|
|
67
|
+
#editor {
|
|
68
|
+
flex: 1;
|
|
69
|
+
width: 100%;
|
|
70
|
+
}
|
|
71
|
+
.findings-panel {
|
|
72
|
+
flex: 1;
|
|
73
|
+
display: flex;
|
|
74
|
+
flex-direction: column;
|
|
75
|
+
}
|
|
76
|
+
#findings-frame {
|
|
77
|
+
flex: 1;
|
|
78
|
+
width: 100%;
|
|
79
|
+
border: none;
|
|
80
|
+
background: #0d1117;
|
|
81
|
+
}
|
|
82
|
+
.splitter {
|
|
83
|
+
width: 4px;
|
|
84
|
+
background: #30363d;
|
|
85
|
+
cursor: col-resize;
|
|
86
|
+
flex-shrink: 0;
|
|
87
|
+
}
|
|
88
|
+
.splitter:hover { background: #58a6ff; }
|
|
89
|
+
</style>
|
|
90
|
+
</head>
|
|
91
|
+
<body>
|
|
92
|
+
<header>
|
|
93
|
+
<h1>Sverka Playground</h1>
|
|
94
|
+
<div class="actions">
|
|
95
|
+
<span class="status" id="status">Ready</span>
|
|
96
|
+
<button id="run-btn">Run</button>
|
|
97
|
+
</div>
|
|
98
|
+
</header>
|
|
99
|
+
<div class="main">
|
|
100
|
+
<div class="editor-panel">
|
|
101
|
+
<div class="panel-header">Pipeline Definition</div>
|
|
102
|
+
<div id="editor"></div>
|
|
103
|
+
</div>
|
|
104
|
+
<div class="splitter" id="splitter"></div>
|
|
105
|
+
<div class="findings-panel">
|
|
106
|
+
<div class="panel-header">Findings</div>
|
|
107
|
+
<iframe id="findings-frame" title="Findings"></iframe>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
<!-- Monaco editor loaded from CDN at runtime (see app.ts).
|
|
111
|
+
This is an external network dependency. For offline use, bundle
|
|
112
|
+
monaco-editor locally via vite instead of the CDN loader. -->
|
|
113
|
+
<script type="module" src="/src/app.ts"></script>
|
|
114
|
+
</body>
|
|
115
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,18 +1,48 @@
|
|
|
1
1
|
{
|
|
2
|
-
"bugs": "https://github.com/sverka-dev/sverka/issues",
|
|
3
|
-
"description": "Placeholder for @sverka/playground published by @nx-devkit/prepare-for-release.",
|
|
4
|
-
"homepage": "https://sverka.dev",
|
|
5
|
-
"license": "MIT",
|
|
6
2
|
"name": "@sverka/playground",
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
},
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Sverka Playground — browser sandbox for building and running check pipelines",
|
|
5
|
+
"license": "MIT",
|
|
11
6
|
"repository": {
|
|
12
7
|
"type": "git",
|
|
13
8
|
"url": "https://github.com/sverka-dev/sverka",
|
|
14
9
|
"directory": "packages/playground"
|
|
15
10
|
},
|
|
11
|
+
"homepage": "https://sverka.dev",
|
|
12
|
+
"bugs": "https://github.com/sverka-dev/sverka/issues",
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
16
|
"type": "module",
|
|
17
|
-
"
|
|
18
|
-
|
|
17
|
+
"main": "./dist/index.mjs",
|
|
18
|
+
"module": "./dist/index.mjs",
|
|
19
|
+
"types": "./dist/index.d.mts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.mts",
|
|
23
|
+
"import": "./dist/index.mjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"dist-web",
|
|
29
|
+
"index.html"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsdown",
|
|
33
|
+
"build:web": "vite build",
|
|
34
|
+
"dev": "vite",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"lint": "eslint src",
|
|
37
|
+
"typecheck": "tsc --noEmit"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@sverka/sarif-viewer-web": "^0.1.1"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"tsdown": "^0.23.0",
|
|
44
|
+
"typescript": "^5.8.0",
|
|
45
|
+
"vitest": "^5.0.0",
|
|
46
|
+
"vite": "^8.3.0"
|
|
47
|
+
}
|
|
48
|
+
}
|