@opsydyn/elysia-spectral 0.4.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.2](https://github.com/opsydyn/elysia-spectral/compare/v0.5.1...v0.5.2) (2026-04-15)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * replace export * with named exports in core/index.ts for stable public API ([d0ef1c9](https://github.com/opsydyn/elysia-spectral/commit/d0ef1c95618ecd5a83e8224f2ba831b226a7ab38))
9
+
10
+ ## [0.5.1](https://github.com/opsydyn/elysia-spectral/compare/v0.5.0...v0.5.1) (2026-04-15)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * tighten public API surface and align LintRunResult.ok with threshold ([8ca3331](https://github.com/opsydyn/elysia-spectral/commit/8ca3331db59e5753d454e759730e3ecd931d2087))
16
+
17
+ ## [0.5.0](https://github.com/opsydyn/elysia-spectral/compare/v0.4.0...v0.5.0) (2026-04-15)
18
+
19
+
20
+ ### Features
21
+
22
+ * add Bruno collection output via output.brunoCollectionPath ([45abcd6](https://github.com/opsydyn/elysia-spectral/commit/45abcd6225bd94098d5974a27ef2c7642614c77f))
23
+
3
24
  ## [0.4.0](https://github.com/opsydyn/elysia-spectral/compare/v0.3.0...v0.4.0) (2026-04-15)
4
25
 
5
26
 
package/README.md CHANGED
@@ -49,6 +49,8 @@ Current package scope:
49
49
 
50
50
  - startup linting
51
51
  - threshold-based failure
52
+ - first-party governance presets: `recommended`, `server`, `strict`
53
+ - RFC 9457 Problem Details enforcement (strict preset)
52
54
  - repo-level and local rulesets
53
55
  - YAML, JS, TS, and in-memory rulesets
54
56
  - resolver pipeline for advanced ruleset loading
@@ -57,6 +59,7 @@ Current package scope:
57
59
  - JUnit report output
58
60
  - SARIF report output
59
61
  - OpenAPI snapshot output
62
+ - Bruno collection output (OpenCollection YAML or JSON)
60
63
  - reusable runtime for CI and tests
61
64
  - opt-in healthcheck endpoint for cached and fresh runs
62
65
 
@@ -497,6 +500,47 @@ JUnit output lets CI systems that consume test XML (Buildkite, CircleCI, GitLab,
497
500
  if: always()
498
501
  ```
499
502
 
503
+ ### Generate a Bruno collection
504
+
505
+ Export the generated OpenAPI spec as a Bruno collection. Bruno is an open-source API client — generated collections let your team test API endpoints without manual import steps.
506
+
507
+ The output format is determined by the file extension:
508
+
509
+ - `.yml` / `.yaml` — OpenCollection YAML (recommended, Bruno v3.0.0+)
510
+ - `.json` — Bruno collection JSON (compatible with all Bruno versions)
511
+
512
+ ```ts
513
+ // OpenCollection YAML — recommended
514
+ spectralPlugin({
515
+ output: {
516
+ brunoCollectionPath: './bruno/collection.yml'
517
+ }
518
+ })
519
+
520
+ // Bruno JSON — for older Bruno versions
521
+ spectralPlugin({
522
+ output: {
523
+ brunoCollectionPath: './bruno/collection.json'
524
+ }
525
+ })
526
+ ```
527
+
528
+ The collection is written after each lint run — at startup, on healthcheck, or in CI. Commit it alongside the spec snapshot so it stays in sync with the API surface.
529
+
530
+ To regenerate in CI as part of your lint script:
531
+
532
+ ```ts
533
+ const runtime = createOpenApiLintRuntime({
534
+ preset: 'strict',
535
+ output: {
536
+ specSnapshotPath: './reports/openapi-snapshot.json',
537
+ brunoCollectionPath: './bruno/collection.yml',
538
+ },
539
+ })
540
+
541
+ await runtime.run(app)
542
+ ```
543
+
500
544
  ### Track OpenAPI snapshot drift
501
545
 
502
546
  Commit the generated OpenAPI snapshot and use `git diff --exit-code` to detect when the API surface changes unexpectedly in a PR.
@@ -521,6 +565,43 @@ git commit -m "chore: add openapi snapshot"
521
565
 
522
566
  If the snapshot has changed, the CI step fails and the diff is visible in the logs. Deliberate API changes are acknowledged by updating the committed snapshot — accidental ones are caught before they ship.
523
567
 
568
+ ### Generate a typed client
569
+
570
+ **If your consumer is a TypeScript project that can import from your Elysia app, use [Eden Treaty](https://elysiajs.com/eden/treaty/overview) instead.** It derives types directly from the app instance with no codegen, no snapshot, and no drift.
571
+
572
+ ```ts
573
+ import { treaty } from '@elysiajs/eden'
574
+ import type { App } from '../server'
575
+
576
+ const client = treaty<App>('localhost:3000')
577
+ // fully typed — zero codegen
578
+ ```
579
+
580
+ For vendor-agnostic consumers — cross-repo TypeScript, non-TypeScript clients, or a published SDK — use the committed OpenAPI snapshot as input to [`openapi-ts`](https://openapi-ts.dev):
581
+
582
+ ```json
583
+ {
584
+ "scripts": {
585
+ "generate:client": "openapi-ts --input ./reports/openapi-snapshot.json --output ./src/generated/client --client @hey-api/client-fetch"
586
+ }
587
+ }
588
+ ```
589
+
590
+ Chain it after the lint step in CI and guard against drift:
591
+
592
+ ```yaml
593
+ - name: Lint OpenAPI spec
594
+ run: bun scripts/lint-openapi.ts
595
+
596
+ - name: Generate typed client
597
+ run: bun run generate:client
598
+
599
+ - name: Check for client drift
600
+ run: git diff --exit-code src/generated/client/
601
+ ```
602
+
603
+ The lint gate runs first — if the spec is invalid the codegen step never runs.
604
+
524
605
  ### Work on this repository locally
525
606
 
526
607
  From the monorepo root:
@@ -551,63 +632,38 @@ That example uses `startup.mode: 'report'`, so the app still boots while the pac
551
632
  ### Package API
552
633
 
553
634
  ```ts
554
- type PresetName = 'recommended' | 'server' | 'strict'
635
+ // ── Vocabulary types ──────────────────────────────────────────────────────────
555
636
 
637
+ type PresetName = 'recommended' | 'server' | 'strict'
638
+ type LintSeverity = 'error' | 'warn' | 'info' | 'hint'
556
639
  type SeverityThreshold = 'error' | 'warn' | 'info' | 'hint' | 'never'
557
-
558
640
  type StartupLintMode = 'enforce' | 'report' | 'off'
559
-
641
+ type LintRunSource = 'startup' | 'healthcheck' | 'manual'
560
642
  type ArtifactWriteFailureMode = 'warn' | 'error'
643
+ type OpenApiLintRuntimeStatus = 'idle' | 'running' | 'passed' | 'failed'
561
644
 
562
- type OpenApiLintArtifacts = {
563
- jsonReportPath?: string
564
- junitReportPath?: string
565
- sarifReportPath?: string
566
- specSnapshotPath?: string
567
- }
568
-
569
- type OpenApiLintSink = {
570
- name: string
571
- write: (
572
- result: LintRunResult,
573
- context: {
574
- spec: Record<string, unknown>
575
- logger: SpectralLogger
576
- }
577
- ) => undefined | Partial<OpenApiLintArtifacts> | Promise<undefined | Partial<OpenApiLintArtifacts>>
578
- }
579
-
580
- type RulesetResolver = (
581
- input: string | RulesetDefinition | Record<string, unknown> | undefined,
582
- context: {
583
- baseDir: string
584
- defaultRuleset: RulesetDefinition
585
- mergeAutodiscoveredWithDefault: boolean
586
- }
587
- ) => Promise<LoadedRuleset | undefined>
588
-
589
- type LoadResolvedRulesetOptions = {
590
- baseDir?: string
591
- resolvers?: RulesetResolver[]
592
- mergeAutodiscoveredWithDefault?: boolean
593
- /** Override the base ruleset used for autodiscovery merging and the fallback when no ruleset is configured. */
594
- defaultRuleset?: RulesetDefinition
595
- }
645
+ // ── Plugin options ────────────────────────────────────────────────────────────
596
646
 
597
647
  type SpectralPluginOptions = {
598
648
  /** First-party governance preset. Sets the base ruleset and autodiscovery merge target. */
599
649
  preset?: PresetName
600
650
  /** Custom ruleset path, object, or inline definition. Merged on top of preset when both are set. */
601
651
  ruleset?: string | RulesetDefinition | Record<string, unknown>
652
+ /** Severity level at which the lint run is considered failed. Defaults to 'error'. */
602
653
  failOn?: SeverityThreshold
603
654
  healthcheck?: false | { path?: string }
604
655
  output?: {
656
+ /** Print findings to the console. Default: true. */
605
657
  console?: boolean
606
658
  jsonReportPath?: string
607
659
  junitReportPath?: string
608
660
  sarifReportPath?: string
661
+ /** true derives the path from the consuming app's package name. */
609
662
  specSnapshotPath?: string | true
663
+ /** .yml/.yaml → OpenCollection YAML (Bruno v3+), .json → Bruno collection JSON */
664
+ brunoCollectionPath?: string
610
665
  pretty?: boolean
666
+ /** Whether artifact write failures throw or warn. Default: 'warn'. */
611
667
  artifactWriteFailures?: ArtifactWriteFailureMode
612
668
  sinks?: OpenApiLintSink[]
613
669
  }
@@ -615,16 +671,160 @@ type SpectralPluginOptions = {
615
671
  specPath?: string
616
672
  baseUrl?: string
617
673
  }
618
- enabled?: boolean | ((env: Record<string, string | undefined>) => boolean)
674
+ /**
675
+ * Controls startup lint behaviour.
676
+ * startup.mode takes precedence over the legacy enabled option.
677
+ * 'enforce' — lint runs at startup and throws on threshold failure (default)
678
+ * 'report' — lint runs at startup, prints findings, but never blocks boot
679
+ * 'off' — startup lint is skipped entirely
680
+ */
619
681
  startup?: {
620
682
  mode?: StartupLintMode
621
683
  }
622
- logger?: {
623
- info: (message: string) => void
624
- warn: (message: string) => void
625
- error: (message: string) => void
684
+ /**
685
+ * Legacy enable flag. Prefer startup.mode for new code.
686
+ * false or () => false is equivalent to startup.mode: 'off'.
687
+ * The function form receives process.env for environment-based toggling.
688
+ */
689
+ enabled?: boolean | ((env: Record<string, string | undefined>) => boolean)
690
+ logger?: SpectralLogger
691
+ }
692
+
693
+ // ── Result types ──────────────────────────────────────────────────────────────
694
+
695
+ type LintFinding = {
696
+ code: string
697
+ message: string
698
+ severity: LintSeverity
699
+ path: Array<string | number>
700
+ documentPointer?: string
701
+ recommendation?: string
702
+ source?: string
703
+ range?: {
704
+ start?: { line: number; character: number }
705
+ end?: { line: number; character: number }
706
+ }
707
+ operation?: {
708
+ method?: string
709
+ path?: string
710
+ operationId?: string
626
711
  }
627
712
  }
713
+
714
+ type LintRunResult = {
715
+ /** True when no findings meet or exceed the configured failOn threshold. */
716
+ ok: boolean
717
+ generatedAt: string
718
+ /** Where the lint run was triggered from. */
719
+ source: LintRunSource
720
+ summary: {
721
+ error: number
722
+ warn: number
723
+ info: number
724
+ hint: number
725
+ total: number
726
+ }
727
+ artifacts?: OpenApiLintArtifacts
728
+ findings: LintFinding[]
729
+ }
730
+
731
+ type OpenApiLintArtifacts = {
732
+ jsonReportPath?: string
733
+ junitReportPath?: string
734
+ sarifReportPath?: string
735
+ specSnapshotPath?: string
736
+ brunoCollectionPath?: string
737
+ }
738
+
739
+ type OpenApiLintRuntimeFailure = {
740
+ name: string
741
+ message: string
742
+ generatedAt: string
743
+ }
744
+
745
+ // ── Runtime ───────────────────────────────────────────────────────────────────
746
+
747
+ type OpenApiLintRuntime = {
748
+ status: OpenApiLintRuntimeStatus
749
+ startedAt: string | null
750
+ completedAt: string | null
751
+ durationMs: number | null
752
+ latest: LintRunResult | null
753
+ lastSuccess: LintRunResult | null
754
+ lastFailure: OpenApiLintRuntimeFailure | null
755
+ running: boolean
756
+ run: (app: AnyElysia, source?: LintRunSource) => Promise<LintRunResult>
757
+ }
758
+
759
+ function createOpenApiLintRuntime(options?: SpectralPluginOptions): OpenApiLintRuntime
760
+
761
+ // ── Extension points (advanced) ───────────────────────────────────────────────
762
+
763
+ type SpectralLogger = {
764
+ info: (message: string) => void
765
+ warn: (message: string) => void
766
+ error: (message: string) => void
767
+ }
768
+
769
+ type OpenApiLintSinkContext = {
770
+ spec: Record<string, unknown>
771
+ logger: SpectralLogger
772
+ }
773
+
774
+ type OpenApiLintSink = {
775
+ name: string
776
+ write: (
777
+ result: LintRunResult,
778
+ context: OpenApiLintSinkContext,
779
+ ) => undefined | Partial<OpenApiLintArtifacts> | Promise<undefined | Partial<OpenApiLintArtifacts>>
780
+ }
781
+
782
+ type RulesetResolver = (
783
+ input: string | RulesetDefinition | Record<string, unknown> | undefined,
784
+ context: RulesetResolverContext,
785
+ ) => Promise<ResolvedRulesetCandidate | undefined>
786
+
787
+ type RulesetResolverContext = {
788
+ baseDir: string
789
+ defaultRuleset: RulesetDefinition
790
+ mergeAutodiscoveredWithDefault: boolean
791
+ }
792
+
793
+ type ResolvedRulesetCandidate = {
794
+ ruleset: unknown
795
+ source?: LoadedRuleset['source']
796
+ }
797
+
798
+ type LoadedRuleset = {
799
+ ruleset: RulesetDefinition
800
+ source?: {
801
+ path: string
802
+ autodiscovered: boolean
803
+ mergedWithDefault: boolean
804
+ }
805
+ }
806
+
807
+ type LoadResolvedRulesetOptions = {
808
+ baseDir?: string
809
+ resolvers?: RulesetResolver[]
810
+ mergeAutodiscoveredWithDefault?: boolean
811
+ /** Override the base ruleset used for autodiscovery merging and the fallback when no ruleset is configured. */
812
+ defaultRuleset?: RulesetDefinition
813
+ }
814
+
815
+ // ── Error classes ─────────────────────────────────────────────────────────────
816
+
817
+ class OpenApiLintThresholdError extends Error {
818
+ readonly threshold: SeverityThreshold
819
+ readonly result: LintRunResult
820
+ }
821
+
822
+ class OpenApiLintArtifactWriteError extends Error {
823
+ readonly artifact: string
824
+ readonly cause: unknown
825
+ }
826
+
827
+ class RulesetLoadError extends Error {}
628
828
  ```
629
829
 
630
830
  ### Presets
@@ -715,7 +915,7 @@ Example successful response:
715
915
 
716
916
  The current output model has two layers:
717
917
 
718
- - convenience options such as `jsonReportPath`, `junitReportPath`, `specSnapshotPath`, and `sarifReportPath`
918
+ - convenience options such as `jsonReportPath`, `junitReportPath`, `specSnapshotPath`, `sarifReportPath`, and `brunoCollectionPath`
719
919
  - sink abstractions under `output.sinks`
720
920
 
721
921
  The convenience options compile down to built-in sinks so the current API stays simple while the internal output model becomes extensible.
@@ -764,4 +964,4 @@ Production-grade linting needs more than a pass/fail boolean. The runtime tracks
764
964
 
765
965
  ### Project status
766
966
 
767
- This package currently implements the narrowed v0.1 scope from [project.md](../../project.md) and the ongoing hardening work tracked in [roadmap.md](../../roadmap.md).
967
+ The package is actively developed toward a stable `v1`. Milestones 0.2 through 0.6 are complete. Ongoing work is tracked in [roadmap.md](../../roadmap.md).
@@ -1,2 +1,2 @@
1
- import { _ as defaultRulesetResolvers, a as OpenApiLintArtifactWriteError, b as lintOpenApi, c as resolveStartupMode, d as LoadedRuleset, f as ResolvedRulesetCandidate, g as RulesetResolverInput, h as RulesetResolverContext, i as shouldFail, l as normalizeFindings, m as RulesetResolver, n as enforceThreshold, o as createOpenApiLintRuntime, p as RulesetLoadError, r as exceedsThreshold, s as isEnabled, t as OpenApiLintThresholdError, u as LoadResolvedRulesetOptions, v as loadResolvedRuleset, y as loadRuleset } from "../index-CMyl_MsI.mjs";
2
- export { LoadResolvedRulesetOptions, LoadedRuleset, OpenApiLintArtifactWriteError, OpenApiLintThresholdError, ResolvedRulesetCandidate, RulesetLoadError, RulesetResolver, RulesetResolverContext, RulesetResolverInput, createOpenApiLintRuntime, defaultRulesetResolvers, enforceThreshold, exceedsThreshold, isEnabled, lintOpenApi, loadResolvedRuleset, loadRuleset, normalizeFindings, resolveStartupMode, shouldFail };
1
+ import { a as createOpenApiLintRuntime, c as ResolvedRulesetCandidate, d as RulesetResolverContext, f as RulesetResolverInput, g as lintOpenApi, h as loadRuleset, i as OpenApiLintArtifactWriteError, l as RulesetLoadError, m as loadResolvedRuleset, n as enforceThreshold, o as LoadResolvedRulesetOptions, p as defaultRulesetResolvers, r as shouldFail, s as LoadedRuleset, t as OpenApiLintThresholdError, u as RulesetResolver } from "../index-11HnbLDN.mjs";
2
+ export { LoadResolvedRulesetOptions, LoadedRuleset, OpenApiLintArtifactWriteError, OpenApiLintThresholdError, ResolvedRulesetCandidate, RulesetLoadError, RulesetResolver, RulesetResolverContext, RulesetResolverInput, createOpenApiLintRuntime, defaultRulesetResolvers, enforceThreshold, lintOpenApi, loadResolvedRuleset, loadRuleset, shouldFail };
@@ -1,2 +1,2 @@
1
- import { a as OpenApiLintThresholdError, c as shouldFail, g as loadRuleset, h as loadResolvedRuleset, i as resolveStartupMode, m as defaultRulesetResolvers, n as createOpenApiLintRuntime, o as enforceThreshold, p as RulesetLoadError, r as isEnabled, s as exceedsThreshold, t as OpenApiLintArtifactWriteError, v as lintOpenApi, y as normalizeFindings } from "../core-D_ro1XEW.mjs";
2
- export { OpenApiLintArtifactWriteError, OpenApiLintThresholdError, RulesetLoadError, createOpenApiLintRuntime, defaultRulesetResolvers, enforceThreshold, exceedsThreshold, isEnabled, lintOpenApi, loadResolvedRuleset, loadRuleset, normalizeFindings, resolveStartupMode, shouldFail };
1
+ import { a as enforceThreshold, d as RulesetLoadError, f as defaultRulesetResolvers, g as lintOpenApi, i as OpenApiLintThresholdError, m as loadRuleset, n as createOpenApiLintRuntime, o as shouldFail, p as loadResolvedRuleset, t as OpenApiLintArtifactWriteError } from "../core-DTKNy6TU.mjs";
2
+ export { OpenApiLintArtifactWriteError, OpenApiLintThresholdError, RulesetLoadError, createOpenApiLintRuntime, defaultRulesetResolvers, enforceThreshold, lintOpenApi, loadResolvedRuleset, loadRuleset, shouldFail };
@@ -7,6 +7,7 @@ import spectralRulesets from "@stoplight/spectral-rulesets";
7
7
  import YAML from "yaml";
8
8
  import signale from "signale";
9
9
  import { styleText } from "node:util";
10
+ import { brunoToOpenCollection, openApiToBruno } from "@usebruno/converters";
10
11
  //#region src/core/finding-guidance.ts
11
12
  const guidanceByCode = {
12
13
  "elysia-operation-summary": "Add detail.summary to the Elysia route options so generated docs and clients have a short operation label.",
@@ -408,7 +409,7 @@ const normalizeExtends = (value) => {
408
409
  };
409
410
  const resolveExtendsEntry = (value) => {
410
411
  const resolved = extendsMap[value];
411
- if (!resolved) throw new RulesetLoadError(`Unsupported ruleset extend target: ${value}. v0.1 supports spectral:oas.`);
412
+ if (!resolved) throw new RulesetLoadError(`Unsupported ruleset extend target: "${value}". Supported extend targets: spectral:oas.`);
412
413
  return resolved;
413
414
  };
414
415
  const normalizeRules = (value, availableFunctions) => {
@@ -669,6 +670,17 @@ const buildSpecReference = (finding, specSnapshotPath) => {
669
670
  return `${specSnapshotPath}#${finding.documentPointer}`;
670
671
  };
671
672
  //#endregion
673
+ //#region src/output/bruno-reporter.ts
674
+ const isYamlPath = (filePath) => filePath.endsWith(".yml") || filePath.endsWith(".yaml");
675
+ const writeBrunoCollection = async (outputPath, spec) => {
676
+ const resolvedPath = path.resolve(process.cwd(), outputPath);
677
+ const collection = openApiToBruno(spec);
678
+ const content = isYamlPath(outputPath) ? YAML.stringify(brunoToOpenCollection(collection)) : JSON.stringify(collection, null, 2);
679
+ await mkdir(path.dirname(resolvedPath), { recursive: true });
680
+ await writeFile(resolvedPath, content, "utf8");
681
+ return resolvedPath;
682
+ };
683
+ //#endregion
672
684
  //#region src/output/json-reporter.ts
673
685
  const DEFAULT_SPEC_SNAPSHOT_FILENAME = "open-api.json";
674
686
  const writeJsonArtifact = async (artifactPath, payload, pretty = true) => {
@@ -867,6 +879,16 @@ const createOutputSinks = (options) => {
867
879
  return { junitReportPath: writtenJunitReportPath };
868
880
  }
869
881
  });
882
+ const configuredBrunoCollectionPath = options.output?.brunoCollectionPath;
883
+ if (configuredBrunoCollectionPath) sinks.push({
884
+ name: "Bruno collection",
885
+ kind: "artifact",
886
+ async write(_result, context) {
887
+ const writtenPath = await writeBrunoCollection(configuredBrunoCollectionPath, context.spec);
888
+ reporter.artifact(`OpenAPI lint wrote Bruno collection to ${writtenPath}.`);
889
+ return { brunoCollectionPath: writtenPath };
890
+ }
891
+ });
870
892
  if (configuredSarifReportPath) sinks.push({
871
893
  name: "SARIF report",
872
894
  kind: "artifact",
@@ -1176,6 +1198,7 @@ const createOpenApiLintRuntime = (options = {}) => {
1176
1198
  else if (loadedRuleset.source?.path) reporter.ruleset(`OpenAPI lint loaded ruleset ${loadedRuleset.source.path}.`);
1177
1199
  const result = await lintOpenApi(spec, loadedRuleset.ruleset);
1178
1200
  result.source = source;
1201
+ result.ok = !shouldFail(result, options.failOn ?? "error");
1179
1202
  await writeOutputSinks(result, spec, options, artifactWriteFailureMode);
1180
1203
  runtime.latest = result;
1181
1204
  reporter.complete("OpenAPI lint completed.");
@@ -1245,13 +1268,10 @@ const mergeArtifacts = (current, next) => ({
1245
1268
  ...current,
1246
1269
  ...next
1247
1270
  });
1248
- const isEnabled = (options = {}) => {
1249
- return resolveStartupMode(options) !== "off";
1250
- };
1251
1271
  const resolveStartupMode = (options = {}) => {
1252
1272
  if (options.startup?.mode) return options.startup.mode;
1253
1273
  if (typeof options.enabled === "function") return options.enabled(process.env) ? "enforce" : "off";
1254
1274
  return options.enabled === false ? "off" : "enforce";
1255
1275
  };
1256
1276
  //#endregion
1257
- export { recommended as _, OpenApiLintThresholdError as a, shouldFail as c, server as d, resolveReporter as f, loadRuleset as g, loadResolvedRuleset as h, resolveStartupMode as i, presets as l, defaultRulesetResolvers as m, createOpenApiLintRuntime as n, enforceThreshold as o, RulesetLoadError as p, isEnabled as r, exceedsThreshold as s, OpenApiLintArtifactWriteError as t, strict as u, lintOpenApi as v, normalizeFindings as y };
1277
+ export { enforceThreshold as a, strict as c, RulesetLoadError as d, defaultRulesetResolvers as f, lintOpenApi as g, recommended as h, OpenApiLintThresholdError as i, server as l, loadRuleset as m, createOpenApiLintRuntime as n, shouldFail as o, loadResolvedRuleset as p, resolveStartupMode as r, presets as s, OpenApiLintArtifactWriteError as t, resolveReporter as u };
@@ -1,4 +1,4 @@
1
- import { ISpectralDiagnostic, RulesetDefinition } from "@stoplight/spectral-core";
1
+ import { RulesetDefinition } from "@stoplight/spectral-core";
2
2
  import { AnyElysia } from "elysia";
3
3
 
4
4
  //#region src/types.d.ts
@@ -19,6 +19,7 @@ type OpenApiLintArtifacts = {
19
19
  junitReportPath?: string;
20
20
  sarifReportPath?: string;
21
21
  specSnapshotPath?: string;
22
+ brunoCollectionPath?: string;
22
23
  };
23
24
  type OpenApiLintSinkContext = {
24
25
  spec: Record<string, unknown>;
@@ -41,6 +42,7 @@ type SpectralPluginOptions = {
41
42
  junitReportPath?: string;
42
43
  sarifReportPath?: string;
43
44
  specSnapshotPath?: string | true;
45
+ brunoCollectionPath?: string;
44
46
  pretty?: boolean;
45
47
  artifactWriteFailures?: ArtifactWriteFailureMode;
46
48
  sinks?: OpenApiLintSink[];
@@ -93,9 +95,6 @@ type LintRunResult = {
93
95
  artifacts?: OpenApiLintArtifacts;
94
96
  findings: LintFinding[];
95
97
  };
96
- interface SpecProvider {
97
- getSpec(): Promise<unknown>;
98
- }
99
98
  type OpenApiLintRuntimeFailure = {
100
99
  name: string;
101
100
  message: string;
@@ -151,9 +150,6 @@ declare const loadRuleset: (input?: RulesetResolverInput, baseDirOrOptions?: str
151
150
  declare const loadResolvedRuleset: (input?: RulesetResolverInput, baseDirOrOptions?: string | LoadResolvedRulesetOptions) => Promise<LoadedRuleset>;
152
151
  declare const defaultRulesetResolvers: RulesetResolver[];
153
152
  //#endregion
154
- //#region src/core/normalize-findings.d.ts
155
- declare const normalizeFindings: (diagnostics: ISpectralDiagnostic[], spec: unknown) => LintRunResult;
156
- //#endregion
157
153
  //#region src/core/runtime.d.ts
158
154
  declare const createOpenApiLintRuntime: (options?: SpectralPluginOptions) => OpenApiLintRuntime;
159
155
  declare class OpenApiLintArtifactWriteError extends Error {
@@ -161,8 +157,6 @@ declare class OpenApiLintArtifactWriteError extends Error {
161
157
  readonly cause: unknown;
162
158
  constructor(artifact: string, cause: unknown);
163
159
  }
164
- declare const isEnabled: (options?: SpectralPluginOptions) => boolean;
165
- declare const resolveStartupMode: (options?: SpectralPluginOptions) => StartupLintMode;
166
160
  //#endregion
167
161
  //#region src/core/thresholds.d.ts
168
162
  declare class OpenApiLintThresholdError extends Error {
@@ -170,8 +164,7 @@ declare class OpenApiLintThresholdError extends Error {
170
164
  readonly result: LintRunResult;
171
165
  constructor(threshold: SeverityThreshold, result: LintRunResult);
172
166
  }
173
- declare const exceedsThreshold: (severity: LintSeverity, threshold: SeverityThreshold) => boolean;
174
167
  declare const shouldFail: (result: LintRunResult, threshold: SeverityThreshold) => boolean;
175
168
  declare const enforceThreshold: (result: LintRunResult, threshold: SeverityThreshold) => void;
176
169
  //#endregion
177
- export { OpenApiLintSink as A, LintRunResult as C, OpenApiLintRuntime as D, OpenApiLintArtifacts as E, SpectralLogger as F, SpectralPluginOptions as I, StartupLintMode as L, PresetName as M, SeverityThreshold as N, OpenApiLintRuntimeFailure as O, SpecProvider as P, LintFinding as S, LintSeverity as T, defaultRulesetResolvers as _, OpenApiLintArtifactWriteError as a, lintOpenApi as b, resolveStartupMode as c, LoadedRuleset as d, ResolvedRulesetCandidate as f, RulesetResolverInput as g, RulesetResolverContext as h, shouldFail as i, OpenApiLintSinkContext as j, OpenApiLintRuntimeStatus as k, normalizeFindings as l, RulesetResolver as m, enforceThreshold as n, createOpenApiLintRuntime as o, RulesetLoadError as p, exceedsThreshold as r, isEnabled as s, OpenApiLintThresholdError as t, LoadResolvedRulesetOptions as u, loadResolvedRuleset as v, LintRunSource as w, ArtifactWriteFailureMode as x, loadRuleset as y };
170
+ export { SpectralLogger as A, OpenApiLintRuntime as C, OpenApiLintSinkContext as D, OpenApiLintSink as E, StartupLintMode as M, PresetName as O, OpenApiLintArtifacts as S, OpenApiLintRuntimeStatus as T, ArtifactWriteFailureMode as _, createOpenApiLintRuntime as a, LintRunSource as b, ResolvedRulesetCandidate as c, RulesetResolverContext as d, RulesetResolverInput as f, lintOpenApi as g, loadRuleset as h, OpenApiLintArtifactWriteError as i, SpectralPluginOptions as j, SeverityThreshold as k, RulesetLoadError as l, loadResolvedRuleset as m, enforceThreshold as n, LoadResolvedRulesetOptions as o, defaultRulesetResolvers as p, shouldFail as r, LoadedRuleset as s, OpenApiLintThresholdError as t, RulesetResolver as u, LintFinding as v, OpenApiLintRuntimeFailure as w, LintSeverity as x, LintRunResult as y };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as OpenApiLintSink, C as LintRunResult, D as OpenApiLintRuntime, E as OpenApiLintArtifacts, F as SpectralLogger, I as SpectralPluginOptions, L as StartupLintMode, M as PresetName, N as SeverityThreshold, O as OpenApiLintRuntimeFailure, P as SpecProvider, S as LintFinding, T as LintSeverity, _ as defaultRulesetResolvers, a as OpenApiLintArtifactWriteError, b as lintOpenApi, c as resolveStartupMode, d as LoadedRuleset, f as ResolvedRulesetCandidate, g as RulesetResolverInput, h as RulesetResolverContext, i as shouldFail, j as OpenApiLintSinkContext, k as OpenApiLintRuntimeStatus, l as normalizeFindings, m as RulesetResolver, n as enforceThreshold, o as createOpenApiLintRuntime, p as RulesetLoadError, r as exceedsThreshold, s as isEnabled, t as OpenApiLintThresholdError, u as LoadResolvedRulesetOptions, v as loadResolvedRuleset, w as LintRunSource, x as ArtifactWriteFailureMode, y as loadRuleset } from "./index-CMyl_MsI.mjs";
1
+ import { A as SpectralLogger, C as OpenApiLintRuntime, D as OpenApiLintSinkContext, E as OpenApiLintSink, M as StartupLintMode, O as PresetName, S as OpenApiLintArtifacts, T as OpenApiLintRuntimeStatus, _ as ArtifactWriteFailureMode, a as createOpenApiLintRuntime, b as LintRunSource, c as ResolvedRulesetCandidate, d as RulesetResolverContext, f as RulesetResolverInput, g as lintOpenApi, h as loadRuleset, i as OpenApiLintArtifactWriteError, j as SpectralPluginOptions, k as SeverityThreshold, l as RulesetLoadError, m as loadResolvedRuleset, n as enforceThreshold, o as LoadResolvedRulesetOptions, p as defaultRulesetResolvers, r as shouldFail, s as LoadedRuleset, t as OpenApiLintThresholdError, u as RulesetResolver, v as LintFinding, w as OpenApiLintRuntimeFailure, x as LintSeverity, y as LintRunResult } from "./index-11HnbLDN.mjs";
2
2
  import { RulesetDefinition } from "@stoplight/spectral-core";
3
3
  import { Elysia } from "elysia";
4
4
 
@@ -72,4 +72,4 @@ declare const strict: RulesetDefinition;
72
72
  //#region src/presets/index.d.ts
73
73
  declare const presets: Record<PresetName, RulesetDefinition>;
74
74
  //#endregion
75
- export { ArtifactWriteFailureMode, LintFinding, LintRunResult, LintRunSource, LintSeverity, LoadResolvedRulesetOptions, LoadedRuleset, OpenApiLintArtifactWriteError, OpenApiLintArtifacts, OpenApiLintRuntime, OpenApiLintRuntimeFailure, OpenApiLintRuntimeStatus, OpenApiLintSink, OpenApiLintSinkContext, OpenApiLintThresholdError, PresetName, ResolvedRulesetCandidate, RulesetLoadError, RulesetResolver, RulesetResolverContext, RulesetResolverInput, SeverityThreshold, SpecProvider, SpectralLogger, SpectralPluginOptions, StartupLintMode, createOpenApiLintRuntime, defaultRulesetResolvers, enforceThreshold, exceedsThreshold, isEnabled, lintOpenApi, loadResolvedRuleset, loadRuleset, normalizeFindings, presets, recommended, resolveStartupMode, server, shouldFail, spectralPlugin, strict };
75
+ export { ArtifactWriteFailureMode, LintFinding, LintRunResult, LintRunSource, LintSeverity, LoadResolvedRulesetOptions, LoadedRuleset, OpenApiLintArtifactWriteError, OpenApiLintArtifacts, OpenApiLintRuntime, OpenApiLintRuntimeFailure, OpenApiLintRuntimeStatus, OpenApiLintSink, OpenApiLintSinkContext, OpenApiLintThresholdError, PresetName, ResolvedRulesetCandidate, RulesetLoadError, RulesetResolver, RulesetResolverContext, RulesetResolverInput, SeverityThreshold, SpectralLogger, SpectralPluginOptions, StartupLintMode, createOpenApiLintRuntime, defaultRulesetResolvers, enforceThreshold, lintOpenApi, loadResolvedRuleset, loadRuleset, presets, recommended, server, shouldFail, spectralPlugin, strict };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as recommended, a as OpenApiLintThresholdError, c as shouldFail, d as server, f as resolveReporter, g as loadRuleset, h as loadResolvedRuleset, i as resolveStartupMode, l as presets, m as defaultRulesetResolvers, n as createOpenApiLintRuntime, o as enforceThreshold, p as RulesetLoadError, r as isEnabled, s as exceedsThreshold, t as OpenApiLintArtifactWriteError, u as strict, v as lintOpenApi, y as normalizeFindings } from "./core-D_ro1XEW.mjs";
1
+ import { a as enforceThreshold, c as strict, d as RulesetLoadError, f as defaultRulesetResolvers, g as lintOpenApi, h as recommended, i as OpenApiLintThresholdError, l as server, m as loadRuleset, n as createOpenApiLintRuntime, o as shouldFail, p as loadResolvedRuleset, r as resolveStartupMode, s as presets, t as OpenApiLintArtifactWriteError, u as resolveReporter } from "./core-DTKNy6TU.mjs";
2
2
  import { Elysia } from "elysia";
3
3
  //#region src/plugin.ts
4
4
  const spectralPlugin = (options = {}) => {
@@ -82,4 +82,4 @@ const spectralPlugin = (options = {}) => {
82
82
  return plugin;
83
83
  };
84
84
  //#endregion
85
- export { OpenApiLintArtifactWriteError, OpenApiLintThresholdError, RulesetLoadError, createOpenApiLintRuntime, defaultRulesetResolvers, enforceThreshold, exceedsThreshold, isEnabled, lintOpenApi, loadResolvedRuleset, loadRuleset, normalizeFindings, presets, recommended, resolveStartupMode, server, shouldFail, spectralPlugin, strict };
85
+ export { OpenApiLintArtifactWriteError, OpenApiLintThresholdError, RulesetLoadError, createOpenApiLintRuntime, defaultRulesetResolvers, enforceThreshold, lintOpenApi, loadResolvedRuleset, loadRuleset, presets, recommended, server, shouldFail, spectralPlugin, strict };
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@opsydyn/elysia-spectral",
3
- "version": "0.4.0",
3
+ "version": "0.5.2",
4
4
  "description": "Thin Elysia plugin that lints generated OpenAPI documents with Spectral.",
5
- "packageManager": "bun@1.2.9",
5
+ "packageManager": "bun@1.3.11",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
@@ -71,6 +71,8 @@
71
71
  "@stoplight/spectral-functions": "^1.10.2",
72
72
  "@stoplight/spectral-parsers": "^1.0.4",
73
73
  "@stoplight/spectral-rulesets": "^1.22.1",
74
+ "@usebruno/converters": "^0.18.1",
75
+ "@usebruno/lang": "^0.35.0",
74
76
  "signale": "^1.4.0",
75
77
  "yaml": "^2.8.1"
76
78
  },