plumb-fhir 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.0 (2026-10-05)
4
+
5
+ - **`plumb check`** reports every `MedplumClient` read or write of a type a
6
+ selected profile fully holds that goes around `readProfiled`,
7
+ `searchProfiled`, `createProfiled`, `updateProfiled` or `stampProfiled`,
8
+ by the types the compiler infers, so an inferred write is found as surely as
9
+ a literal one. A committed baseline lets a project adopt it with a backlog:
10
+ only new or grown findings fail. `// plumb-check: <reason>` marks a
11
+ deliberate exception. It needs the project's TypeScript 5 or 6 (design 07).
12
+ - **`stampProfiled` returns its resource branded** (`T & Stamped`, an optional
13
+ unique-symbol property), which `check` reads to accept a stamped write
14
+ through a variable. Callers see no difference.
15
+ - `check` in the config: `tsconfig`, `baseline` and `ignore`.
16
+
3
17
  ## 0.7.0 (2026-10-05)
4
18
 
5
19
  - **`stampProfiled(resource, options?)`** returns the copy `createProfiled`
package/README.md CHANGED
@@ -379,6 +379,61 @@ read would refuse. History is not offered typed: an old version may predate
379
379
  its stamp, so read it with `medplum.readHistory` and `asProfiled` it if you
380
380
  must.
381
381
 
382
+ ## Find code that goes around Plumb
383
+
384
+ The profiled reads and writes only help where they are called. `plumb check`
385
+ reads your code the way the compiler does and reports every `MedplumClient`
386
+ read or write of a profiled type that does not go through them, including a
387
+ write whose type is only inferred:
388
+
389
+ ```ts
390
+ export default defineConfig({
391
+ // …igs, profiles, out
392
+ check: {
393
+ tsconfig: ['apps/web/tsconfig.json'],
394
+ baseline: './plumb-check-baseline.json',
395
+ ignore: ['**/*.test.ts', '**/*.stories.tsx'],
396
+ },
397
+ });
398
+ ```
399
+
400
+ ```bash
401
+ npx plumb check
402
+ ```
403
+
404
+ ```text
405
+ plumb check
406
+ ✖ check 2 new, 248 in the baseline, in 2767 files 6.2s
407
+ apps/web/src/lib/goals/goal.ts:41:10 readResource Goal → readProfiled
408
+ apps/web/src/lib/coverage/save.ts:90:5 createResource Coverage → createProfiled, updateProfiled or stampProfiled
409
+ Failed in 6.2s
410
+ ```
411
+
412
+ - **What it reports:** `readResource`, `searchResources`, `searchOne`,
413
+ `searchResourcePages`, `createResource`, `updateResource`, `upsertResource`
414
+ and `createResourceIfNoneExist` on a `MedplumClient` (or a class deriving
415
+ from it), for a type every resource of which a selected profile holds: one
416
+ with a profile that routes on no keys, or a `defaultProfile`. A type whose
417
+ profiles are keyed on content (one code of Observation) is not reported, and
418
+ neither is `string` or `ResourceType`.
419
+ - **A stamped write passes,** through a variable too: `stampProfiled` returns
420
+ its resource branded, and `check` reads the brand from the type.
421
+ - **A deliberate exception** takes a comment with a reason on the line before:
422
+ `// plumb-check: the backfill reads unstamped records to stamp them`.
423
+ - **The baseline** is a committed count per file, method and type. `check`
424
+ fails only when a count grows or a new one appears, so new raw access is
425
+ refused at once while the backlog shrinks. `--update-baseline` records
426
+ fixes; it refuses growth unless `--allow-growth` is given.
427
+ - **Paths** in the output, the baseline and `ignore` are relative to the
428
+ deepest folder holding the config and every tsconfig, so a monorepo's config
429
+ in one package checks code in another.
430
+ - **TypeScript:** `check` compiles with your project's own `typescript`, which
431
+ needs its compiler API: TypeScript 5 or 6. TypeScript 7's native package has
432
+ none yet, and `check` exits 2 on it.
433
+
434
+ Exit codes: 0 when nothing is new, 1 when something is (or the baseline would
435
+ grow), 2 for usage, config and set-up errors.
436
+
382
437
  ## Check stored data, then load profiles
383
438
 
384
439
  Medplum validates a resource when it is written, and never again. Tightening a
@@ -0,0 +1,89 @@
1
+ import type * as TS from 'typescript5';
2
+ import type { PlumbConfig } from './config.js';
3
+ import { type Route } from './emit/routes.js';
4
+ /** A `MedplumClient` read or write of a profiled type that goes around Plumb. */
5
+ export interface Finding {
6
+ /** Relative to the config's folder, with forward slashes. */
7
+ file: string;
8
+ line: number;
9
+ column: number;
10
+ method: string;
11
+ /** The profiled resource types the call can touch, sorted. */
12
+ resourceTypes: string[];
13
+ /** What to call instead, or why the finding stands. */
14
+ instead: string;
15
+ }
16
+ export interface FindOptions {
17
+ /** The project's TypeScript module, with its compiler API. */
18
+ ts: typeof TS;
19
+ /** The folder findings are relative to, and `ignore` is matched from. */
20
+ base: string;
21
+ tsconfig: string[];
22
+ /** Resource types a selected profile constrains. */
23
+ profiledTypes: ReadonlySet<string>;
24
+ /** The generated folder, never checked. */
25
+ out: string;
26
+ ignore?: string[];
27
+ }
28
+ /**
29
+ * Compiles each project and reports every raw `MedplumClient` read or write of
30
+ * a profiled type, with the type the checker infers, not the text written.
31
+ */
32
+ export declare function findRawAccess(options: FindOptions): {
33
+ findings: Finding[];
34
+ files: number;
35
+ };
36
+ /** Accepted findings, counted per file, method and resource types. */
37
+ export type Baseline = Record<string, number>;
38
+ export declare function countFindings(findings: Finding[]): Baseline;
39
+ export interface BaselineComparison {
40
+ /** Findings under a key that is new or grew: every one of them, since a line cannot be told apart. */
41
+ fresh: Finding[];
42
+ /** How many findings the baseline accepts that are now gone. */
43
+ fixed: number;
44
+ /** Findings the baseline accepts. */
45
+ accepted: number;
46
+ }
47
+ /** A finding fails only where its key's count exceeds the baseline's. */
48
+ export declare function compareBaseline(findings: Finding[], baseline: Baseline): BaselineComparison;
49
+ export interface CheckResult {
50
+ ok: boolean;
51
+ findings: Finding[];
52
+ comparison: BaselineComparison;
53
+ files: number;
54
+ /** Set when `updateBaseline` wrote the file. */
55
+ baselineWritten?: boolean;
56
+ errors: {
57
+ code: 'no-check-config' | 'no-compiler-api' | 'no-lock' | 'baseline-growth';
58
+ message: string;
59
+ }[];
60
+ }
61
+ export interface CheckOptions {
62
+ config: PlumbConfig;
63
+ configPath: string;
64
+ /** The project's TypeScript; resolved from the config's folder by default. */
65
+ ts?: typeof TS;
66
+ cacheDir?: string;
67
+ updateBaseline?: boolean;
68
+ allowGrowth?: boolean;
69
+ }
70
+ /**
71
+ * `plumb check`: loads the selected profiles for the types they constrain,
72
+ * compiles the configured projects, and compares what it finds with the
73
+ * baseline, or rewrites the baseline.
74
+ */
75
+ export declare function checkProject(options: CheckOptions): Promise<CheckResult>;
76
+ /**
77
+ * The folder findings and `ignore` globs are relative to: the deepest one
78
+ * holding the config and every tsconfig, so a monorepo's config in one package
79
+ * checks code in another, and baseline keys do not depend on where it runs.
80
+ */
81
+ export declare function checkRoot(configPath: string, tsconfig: string[]): string;
82
+ /**
83
+ * The types every resource of which a selected profile holds: one with a
84
+ * profile that routes on no keys, or a `defaultProfile`. A profile keyed on
85
+ * content (one code of Observation) holds only some, so raw access to the type
86
+ * is not wrong and is not reported.
87
+ */
88
+ export declare function enforcedTypes(routes: Record<string, Route[]>, defaultProfile?: Record<string, string[]>): Set<string>;
89
+ //# sourceMappingURL=check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/check.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,KAAK,KAAK,EAAe,MAAM,kBAAkB,CAAC;AAI3D,iFAAiF;AACjF,MAAM,WAAW,OAAO;IACtB,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;CACjB;AAiBD,MAAM,WAAW,WAAW;IAC1B,8DAA8D;IAC9D,EAAE,EAAE,OAAO,EAAE,CAAC;IACd,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,oDAAoD;IACpD,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG;IAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAe1F;AAoHD,sEAAsE;AACtE,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAI9C,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,QAAQ,CAI3D;AAED,MAAM,WAAW,kBAAkB;IACjC,sGAAsG;IACtG,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,GAAG,kBAAkB,CAS3F;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,MAAM,EAAE;QACN,IAAI,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,SAAS,GAAG,iBAAiB,CAAC;QAC5E,OAAO,EAAE,MAAM,CAAC;KACjB,EAAE,CAAC;CACL;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA2D9E;AAeD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAQxE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAC/B,cAAc,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAM,GAC5C,GAAG,CAAC,MAAM,CAAC,CAKb"}
@@ -1,3 +1,4 @@
1
+ import type * as TS from 'typescript5';
1
2
  import { type Checked } from './conformance.js';
2
3
  import { type Step } from './generate.js';
3
4
  export interface CliIo {
@@ -9,6 +10,8 @@ export interface CliIo {
9
10
  stderr: (text: string) => void;
10
11
  cacheDir?: string;
11
12
  fetch?: typeof globalThis.fetch;
13
+ /** The TypeScript `check` compiles with; the project's own by default. */
14
+ typescript?: typeof TS;
12
15
  }
13
16
  /** What a step did, as one line: its name, its counts and its time. */
14
17
  export declare function formatStep(step: Step, out: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/commands.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,KAAK,OAAO,EAIb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAiC,KAAK,IAAI,EAAE,MAAM,eAAe,CAAC;AAGzE,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACxC,6DAA6D;IAC7D,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAyDD,uEAAuE;AACvE,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAe1D;AAED,2EAA2E;AAC3E,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAuBpE;AAqID,KAAK,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,CAAC,CAAC;AAIjD,kGAAkG;AAClG,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,EAAE,CAexD"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/commands.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAGvC,OAAO,EACL,KAAK,OAAO,EAIb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAiC,KAAK,IAAI,EAAE,MAAM,eAAe,CAAC;AAGzE,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACxC,6DAA6D;IAC7D,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,0EAA0E;IAC1E,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC;CACxB;AA+DD,uEAAuE;AACvE,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAe1D;AAED,2EAA2E;AAC3E,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAwBpE;AA8KD,KAAK,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,CAAC,CAAC;AAIjD,kGAAkG;AAClG,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,EAAE,CAexD"}
@@ -36,6 +36,17 @@ export interface PlumbConfig {
36
36
  defaultProfile?: Record<string, string[]>;
37
37
  /** What `push` writes to each environment's project besides its profiles. */
38
38
  project?: ProjectConfig;
39
+ /** What `plumb check` reads. */
40
+ check?: CheckConfig;
41
+ }
42
+ /** What `plumb check` reads: the project's code, for raw access to profiled types. */
43
+ export interface CheckConfig {
44
+ /** The TypeScript projects to check, each compiled once; a path or several. */
45
+ tsconfig: string | string[];
46
+ /** The committed file of accepted findings; without one, every finding fails. */
47
+ baseline?: string;
48
+ /** Globs, relative to the config, of files not to check (tests, stories). */
49
+ ignore?: string[];
39
50
  }
40
51
  /**
41
52
  * A project's configuration, as a project admin can write it. Policies and
@@ -94,7 +105,7 @@ export interface ResolvedEnvironment {
94
105
  clientId: string;
95
106
  clientSecret: string;
96
107
  }
97
- export type ConfigErrorCode = 'config-not-found' | 'unsupported-syntax' | 'unresolved-import' | 'no-default-export' | 'unknown-key' | 'missing-out' | 'invalid-type' | 'invalid-ig' | 'unlisted-ig' | 'invalid-profile' | 'invalid-max-codes' | 'invalid-base-url' | 'unknown-environment' | 'missing-variable' | 'invalid-route' | 'unselected-route' | 'invalid-route-element' | 'invalid-default-profile' | 'versioned-url' | 'fsh-and-local' | 'no-sushi-config' | 'invalid-setting' | 'super-admin-field' | 'unknown-access-policy' | 'duplicate-key';
108
+ export type ConfigErrorCode = 'config-not-found' | 'unsupported-syntax' | 'unresolved-import' | 'no-default-export' | 'unknown-key' | 'missing-out' | 'invalid-type' | 'invalid-ig' | 'unlisted-ig' | 'invalid-profile' | 'invalid-max-codes' | 'invalid-base-url' | 'unknown-environment' | 'missing-variable' | 'invalid-route' | 'unselected-route' | 'invalid-route-element' | 'invalid-default-profile' | 'versioned-url' | 'fsh-and-local' | 'no-sushi-config' | 'invalid-setting' | 'super-admin-field' | 'unknown-access-policy' | 'duplicate-key' | 'invalid-check';
98
109
  export interface ConfigError {
99
110
  code: ConfigErrorCode;
100
111
  message: string;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,YAAY,EAEZ,MAAM,EACN,mBAAmB,EACpB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,WAAW;IAC1B,4DAA4D;IAC5D,GAAG,EAAE,MAAM,EAAE,CAAC;IACd;;;OAGG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE;QACT,0FAA0F;QAC1F,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC3C;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,KAAK,CAAC,CAAC;IAC1C;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1C,6EAA6E;IAC7E,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,kEAAkE;IAClE,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACjD,6DAA6D;IAC7D,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACpD,sEAAsE;IACtE,qBAAqB,CAAC,EAAE;QACtB,WAAW,EAAE,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;QAC5C,YAAY,EAAE,MAAM,CAAC;KACtB,EAAE,CAAC;IACJ,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACtE;AAED,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC;AAEjE,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,YAAY,EAAE,cAAc,GAAG,IAAI,GAAG,MAAM,CAAC,CAAC;AAEpF;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;AAE3D;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1B,YAAY,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,+EAA+E;AAC/E,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,eAAe,GACvB,kBAAkB,GAClB,oBAAoB,GACpB,mBAAmB,GACnB,mBAAmB,GACnB,aAAa,GACb,aAAa,GACb,cAAc,GACd,YAAY,GACZ,aAAa,GACb,iBAAiB,GACjB,mBAAmB,GACnB,kBAAkB,GAClB,qBAAqB,GACrB,kBAAkB,GAClB,eAAe,GACf,kBAAkB,GAClB,uBAAuB,GACvB,yBAAyB,GACzB,eAAe,GACf,eAAe,GACf,iBAAiB,GACjB,iBAAiB,GACjB,mBAAmB,GACnB,uBAAuB,GACvB,eAAe,CAAC;AAEpB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GACrD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,EAAE,CAAA;CAAE,CAAC;AAE7D,0DAA0D;AAC1D,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAE7D;AAoBD,QAAA,MAAM,aAAa,YAAI,SAAS,EAAE,cAAc,EAAE,eAAe,EAAE,OAAO,CAAU,CAAC;AAOrF;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA+C5B;AA+aD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,CAE/E;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,mBAAmB,GAAG,sBAAsB,GAAG,6BAA6B,CAAC;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,aAAa,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,aAAa,EAAE,CA+B5F;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,EACnC,QAAQ,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,mBAAmB,CAAA;CAAE,EAAE,GACnD,WAAW,EAAE,CAiBf;AAUD,yEAAyE;AACzE,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACtC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,mBAAmB,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,EAAE,CAAA;CAAE,CAsCvF"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"print.d.ts","sourceRoot":"","sources":["../../../src/emit/print.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAS,OAAO,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAiB,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE5E,uFAAuF;AACvF,eAAO,MAAM,MAAM,0BAA0B,CAAC;AAqG9C,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,CAgD7C;AA+VD,KAAK,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC,CAAC;AAkR5F,kHAAkH;AAClH,wBAAgB,UAAU,CACxB,MAAM,EAAE,YAAY,EAAE,EACtB,MAAM,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,MAAM,EACvC,OAAO,GAAE,YAAiF,GACzF,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAyBrB"}
1
+ {"version":3,"file":"print.d.ts","sourceRoot":"","sources":["../../../src/emit/print.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAS,OAAO,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAiB,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE5E,uFAAuF;AACvF,eAAO,MAAM,MAAM,0BAA0B,CAAC;AAqG9C,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,CAgD7C;AA2WD,KAAK,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC,CAAC;AAkR5F,kHAAkH;AAClH,wBAAgB,UAAU,CACxB,MAAM,EAAE,YAAY,EAAE,EACtB,MAAM,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,MAAM,EACvC,OAAO,GAAE,YAAiF,GACzF,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAyBrB"}
@@ -44,7 +44,8 @@ var KEYS = /* @__PURE__ */ new Set([
44
44
  "environments",
45
45
  "routes",
46
46
  "defaultProfile",
47
- "project"
47
+ "project",
48
+ "check"
48
49
  ]);
49
50
  var ENVIRONMENT_KEYS = ["baseUrl", "clientId", "clientSecret", "settings"];
50
51
  var PROJECT_KEYS = ["settings", "secrets", "accessPolicies", "defaultAccessPolicies", "clients"];
@@ -92,6 +93,7 @@ async function loadConfig(options) {
92
93
  config: {
93
94
  ...config,
94
95
  out: (0, import_node_path.resolve)(base2, config.out),
96
+ ...config.check ? { check: resolveCheck(base2, config.check) } : {},
95
97
  ...fsh ? { fsh } : {},
96
98
  ...local ? { local } : {}
97
99
  }
@@ -139,7 +141,8 @@ function check(config) {
139
141
  ...checkEnvironments(record.environments),
140
142
  ...checkRouteRows(record),
141
143
  ...checkDefaultProfile(record.defaultProfile),
142
- ...checkProject(record.project)
144
+ ...checkProject(record.project),
145
+ ...checkCheck(record.check)
143
146
  );
144
147
  for (const key of ["local", "fsh"]) {
145
148
  if (record[key] !== void 0 && typeof record[key] !== "string") {
@@ -184,6 +187,32 @@ function checkWildcards(record) {
184
187
  ] : [];
185
188
  });
186
189
  }
190
+ function checkCheck(check2) {
191
+ if (check2 === void 0) return [];
192
+ if (typeof check2 !== "object" || check2 === null || Array.isArray(check2)) {
193
+ return [{ code: "invalid-type", path: "check", message: '"check" must be an object.' }];
194
+ }
195
+ const errors = Object.keys(check2).filter((key) => !["tsconfig", "baseline", "ignore"].includes(key)).map((key) => ({
196
+ code: "unknown-key",
197
+ path: `check.${key}`,
198
+ message: `Unknown config key "check.${key}".`
199
+ }));
200
+ const { tsconfig, baseline, ignore } = check2;
201
+ const strings = (v) => Array.isArray(v) && v.every((x) => typeof x === "string");
202
+ const invalid = (key, expected) => ({
203
+ code: "invalid-check",
204
+ path: `check.${key}`,
205
+ message: `"check.${key}" must be ${expected}.`
206
+ });
207
+ if (!(typeof tsconfig === "string" || strings(tsconfig) && tsconfig.length > 0)) {
208
+ errors.push(invalid("tsconfig", "a tsconfig path, or a list of them"));
209
+ }
210
+ if (baseline !== void 0 && typeof baseline !== "string") {
211
+ errors.push(invalid("baseline", "a path"));
212
+ }
213
+ if (ignore !== void 0 && !strings(ignore)) errors.push(invalid("ignore", "a list of globs"));
214
+ return errors;
215
+ }
187
216
  function checkBindings(bindings) {
188
217
  if (bindings === void 0) return [];
189
218
  if (typeof bindings !== "object" || bindings === null || Array.isArray(bindings)) {
@@ -453,6 +482,13 @@ function checkClients(clients, checkPolicyKey) {
453
482
  return errors;
454
483
  });
455
484
  }
485
+ function resolveCheck(base2, check2) {
486
+ return {
487
+ tsconfig: [check2.tsconfig].flat().map((path) => (0, import_node_path.resolve)(base2, path)),
488
+ ...check2.baseline ? { baseline: (0, import_node_path.resolve)(base2, check2.baseline) } : {},
489
+ ignore: check2.ignore ?? []
490
+ };
491
+ }
456
492
 
457
493
  // src/validate.ts
458
494
  var import_node_path4 = require("node:path");