@wcstack/typescript 1.32.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/README.ja.md +106 -0
- package/README.md +106 -0
- package/dist/index.d.ts +177 -0
- package/dist/index.esm.js +508 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/schema-core.cjs +6795 -0
- package/dist/tsc-core.cjs +545 -0
- package/dist/wcs-schema.mjs +682 -0
- package/dist/wcs-schema.mjs.map +1 -0
- package/dist/wcs-tsc.mjs +283 -0
- package/dist/wcs-tsc.mjs.map +1 -0
- package/package.json +84 -0
package/README.ja.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# @wcstack/typescript
|
|
2
|
+
|
|
3
|
+
> 🤖 **AI coding agents**: This README is a package-level reference, not the primary entry point for building a wcstack application. If you have not already done so, first read the repository [README](https://github.com/wcstack/wcstack#readme) and [AGENTS.md](https://github.com/wcstack/wcstack/blob/main/AGENTS.md), then use the [wcstack-app skill](https://github.com/wcstack/wcstack-skill).
|
|
4
|
+
|
|
5
|
+
**もし、state に書いた型がそれをバインドする HTML まで届いたら?**
|
|
6
|
+
|
|
7
|
+
`@wcstack/state` のページは DOM と state をパス文字列(`users.*.name`)で結びます。TypeScript の state ファイルはそのパス全部と型を知っています — しかし HTML は `.d.ts` を消費できず、静的検証器(`wcs-validate`・VS Code 拡張)はインラインスクリプトを型注釈を理解しない正規表現アナライザで読みます。`users: [] as { name: string }[]` では `users.*.name` が解決できず、本物の typo は warning 止まり、正しいバインドには偽警告が付きます。
|
|
8
|
+
|
|
9
|
+
`@wcstack/typescript` はこの穴をコマンド 1 つで塞ぎます。`wcs-schema` は state ファイルを TypeScript コンパイラ API でコンパイルし、検証器が消費する sidecar `wcstack.manifest.json` の `stateSchema` を書き出します — CI でも、どのエディタでも。manifest は**派生物**です: 正本は型のままで、乖離すれば `wcs-schema check` が CI を落とします。
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -D @wcstack/typescript typescript
|
|
13
|
+
npx wcs-schema emit src/state.ts # ./wcstack.manifest.json を書く
|
|
14
|
+
npx wcs-validate --strict index.html # typo は error に、偽警告は消える
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
ランタイム依存ゼロ。`typescript` は peer dependency — プロジェクト自身のコンパイラを使います。
|
|
18
|
+
|
|
19
|
+
## コマンド
|
|
20
|
+
|
|
21
|
+
### `wcs-schema emit <state.ts|state.js> [options]`
|
|
22
|
+
|
|
23
|
+
ファイルの `export default` から `states[<name>].stateSchema` を生成します(`defineState(...)` は構文的に剥がすので、このパッケージは `@wcstack/state` を解決できなくても動きます)。生成結果は書き出す前に検証器自身の manifest 検査を通します。
|
|
24
|
+
|
|
25
|
+
| オプション | 説明 |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `--state=<name>` | state 名(既定 `default`) |
|
|
28
|
+
| `--out=<path>` | 出力先 manifest(作業ディレクトリ相対・既定 `wcstack.manifest.json`)。`--out=-` で stdout |
|
|
29
|
+
| `--merge` | 既存 manifest の他の内容(他 state・`filters`・`listContexts`)を保持し、この state の `stateSchema` だけ置き換える。同じ state の手書き schema は合成されず置き換わる |
|
|
30
|
+
| `--tsconfig=<path>` | コンパイルに使う `tsconfig.json`(既定: state ファイルから上に辿って最も近いもの、無ければ組み込み既定) |
|
|
31
|
+
| `--max-depth=<n>` | ここより深い入れ子は素の `{}` で打ち切る(既定 `5` = 検証器の探索予算と同じ) |
|
|
32
|
+
|
|
33
|
+
exit code: `0` 書き出し済み · `2` usage エラー / 読めない / 構文エラー / 生成物が自己検査に落ちた。
|
|
34
|
+
|
|
35
|
+
### `wcs-schema check <state.ts|state.js> [options]`
|
|
36
|
+
|
|
37
|
+
schema を再生成し、`--manifest=<path>`(既定 `wcstack.manifest.json`)の内容とキー順を無視して比較します。
|
|
38
|
+
|
|
39
|
+
exit code: `0` 最新 · `1` 乖離あり(変更は JSON pointer で列挙: `+` 型にだけある / `-` manifest にだけある / `~` 違う) · `2` usage エラー / manifest が無い / その state の `stateSchema` がまだ無い。
|
|
40
|
+
|
|
41
|
+
推奨する CI ゲート:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx wcs-schema check src/state.ts && npx wcs-validate --strict index.html
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### `wcs-tsc [--url-imports=any|error] [--wcs-defaults] [tsc の引数...]`
|
|
48
|
+
|
|
49
|
+
`.html` に対する `tsc`: 各 `<wcs-state>` のインライン `<script type="module">` を VS Code 拡張と同じ言語プラグイン(型付き `this`・自動 `defineState` ラップ・bare でも CDN URL でも `@wcstack/state` import を除去)で型検査し、診断は HTML の位置を指します:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm i -D @volar/typescript@~2.4.0 @volar/language-core@~2.4.0 # optional peer・wcs-tsc だけが必要
|
|
53
|
+
npx wcs-tsc --noEmit
|
|
54
|
+
# index.html(9,14): error TS2551: Property 'coutn' does not exist on type '_WcsThis<…>'. Did you mean 'count'?
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
| オプション | 説明 |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `--url-imports=any`(既定) | 全ての `http(s)://` モジュール import を `any` に型付ける — buildless なページは tsc が解決できない CDN から import する |
|
|
60
|
+
| `--url-imports=error` | URL import をそのまま残す(それぞれ TS2307) |
|
|
61
|
+
| `--wcs-defaults` | プロジェクトの tsconfig に `**/*.html` を覆う `include`・`noImplicitThis`・`allowJs`・`checkJs` が無ければ、元を extends してそれらを足した一時 config で実行する(無指定なら警告だけで HTML が検査されないことがある) |
|
|
62
|
+
| それ以外 | tsc にそのまま渡す(`-p`・`--noEmit` …) |
|
|
63
|
+
|
|
64
|
+
exit code は tsc のもの(`0` クリーン・診断があれば非ゼロ)。`typescript` / `@volar/typescript` が解決できないときと不正なオプションは `2`。1 ページに複数の `<wcs-state>` があれば 1 本の仮想モジュールに合成(import は巻き上げ・各ブロックは自分のスコープ)し、インライン state の無いページは空のモジュールになります。仕組みは vue-tsc と同じ: `@volar/typescript` の `runTsc` がプロジェクト自身の `typescript/lib/tsc.js` にパッチを当てます。
|
|
65
|
+
|
|
66
|
+
## 生成される schema の中身
|
|
67
|
+
|
|
68
|
+
sidecar 規範が許す JSON-Schema サブセットだけ(`type` / `properties` / `required` / `items` / `enum` / `const` / `anyOf`):
|
|
69
|
+
|
|
70
|
+
| TypeScript | stateSchema |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `string` / `number` / `boolean` / `null` | `{ "type": … }`(`integer` は使わず全て `number`) |
|
|
73
|
+
| `"a" \| "b"`、`1 \| 2` | `{ "type": "string", "enum": ["a", "b"] }` |
|
|
74
|
+
| `T[]`、`readonly T[]`、タプル | `{ "type": "array", "items": … }` |
|
|
75
|
+
| `T \| null` | `{ "anyOf": [T, { "type": "null" }] }` |
|
|
76
|
+
| `T \| undefined`、`x?: T` | `T`。`x` は `required` に入らない |
|
|
77
|
+
| `A \| B`(オブジェクトや混在プリミティブ) | `{ "anyOf": [A, B] }` |
|
|
78
|
+
| 自分のコードのオブジェクトリテラル / interface / class | `{ "type": "object", "properties": …, "required": … }` |
|
|
79
|
+
| `Date`、`Map`、`Set`、DOM 型、ライブラリ由来の型、`any`、`unknown`、`Record<string, T>` | **素の `{}`** |
|
|
80
|
+
| `get x(): T` | `x: T` — getter はメンバー |
|
|
81
|
+
| `get "users.*.ageCategory"(): string` | `users.items.properties.ageCategory` に注入 — パス getter は計算先のパスのメンバー |
|
|
82
|
+
| メソッド、関数値プロパティ、`$` 始まりキー(`$watch`・`$commandTokens` …) | 捨てる |
|
|
83
|
+
| `--max-depth` より深い入れ子 | 素の `{}` |
|
|
84
|
+
|
|
85
|
+
素の `{}` が要点です: 検証器はこれを *unknown* として扱いその下では沈黙しますが、メンバーを欠く型付きオブジェクトは *nonexistent* で **error** です。`Date` や `Record<string, unknown>` の下のパスが偽 error になることはありません。
|
|
86
|
+
|
|
87
|
+
## ライブラリ API
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { generateStateSchema, buildManifest, compareStateSchema } from "@wcstack/typescript";
|
|
91
|
+
|
|
92
|
+
const { schema, warnings } = generateStateSchema("src/state.ts"); // { tsconfig?, maxDepth? }
|
|
93
|
+
const manifest = buildManifest("default", schema /*, 既存の manifest オブジェクト */);
|
|
94
|
+
const result = compareStateSchema(JSON.stringify(manifest), "default", schema); // { kind: "same" } | { kind: "differs", changes } | …
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`loadStateFile` / `stateTypeToSchema` は 2 つの半分(コンパイラ program・型 → schema)を別々に組み合わせたいツール向けに公開しています。
|
|
98
|
+
|
|
99
|
+
## 位置づけ
|
|
100
|
+
|
|
101
|
+
- 検証器側 — 最近傍 `wcstack.manifest.json` の発見、`wcs/path-nonexistent`、`wcs/path-type-mismatch` — は [`@wcstack/lint`](../lint/README.ja.md#state-の契約を宣言するstateschema) と、規範としては [`docs/wcstack-manifest-schema.md`](https://github.com/wcstack/wcstack/blob/main/docs/wcstack-manifest-schema.md) にあります。
|
|
102
|
+
- wcstack アプリの TypeScript の話全体(`defineState` による `this` の型付け、このパッケージ、tag name map、`wcs-tsc`)は [`docs/typescript.ja.md`](https://github.com/wcstack/wcstack/blob/main/docs/typescript.ja.md) にまとめています。
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# @wcstack/typescript
|
|
2
|
+
|
|
3
|
+
> 🤖 **AI coding agents**: This README is a package-level reference, not the primary entry point for building a wcstack application. If you have not already done so, first read the repository [README](https://github.com/wcstack/wcstack#readme) and [AGENTS.md](https://github.com/wcstack/wcstack/blob/main/AGENTS.md), then use the [wcstack-app skill](https://github.com/wcstack/wcstack-skill).
|
|
4
|
+
|
|
5
|
+
**What if the types you already wrote for your state reached the HTML that binds to it?**
|
|
6
|
+
|
|
7
|
+
`@wcstack/state` pages bind DOM to state through path strings (`users.*.name`). A TypeScript state file already knows every one of those paths and its type — but HTML cannot consume a `.d.ts`, and the static validator (`wcs-validate`, the VS Code extension) reads the inline script with a regular-expression analyzer that does not understand annotations: `users: [] as { name: string }[]` leaves `users.*.name` unresolvable, so a real typo is only a warning and a correct binding gets a false one.
|
|
8
|
+
|
|
9
|
+
`@wcstack/typescript` closes that gap with one command. `wcs-schema` compiles the state file with the TypeScript compiler API and writes the sidecar `wcstack.manifest.json` `stateSchema` that the validator consumes — in CI and in every editor. The manifest is a **derived artifact**: the type stays the source of truth, and `wcs-schema check` fails CI when they drift.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -D @wcstack/typescript typescript
|
|
13
|
+
npx wcs-schema emit src/state.ts # writes ./wcstack.manifest.json
|
|
14
|
+
npx wcs-validate --strict index.html # typos are now errors, false warnings are gone
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Zero runtime dependencies. `typescript` is a peer dependency — the project's own compiler is used.
|
|
18
|
+
|
|
19
|
+
## Commands
|
|
20
|
+
|
|
21
|
+
### `wcs-schema emit <state.ts|state.js> [options]`
|
|
22
|
+
|
|
23
|
+
Generates `states[<name>].stateSchema` from the file's `export default` (a `defineState(...)` call is unwrapped syntactically, so the package does not even need to resolve `@wcstack/state`). The result is run through the validator core's own manifest check before anything is written.
|
|
24
|
+
|
|
25
|
+
| Option | Description |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `--state=<name>` | State name (default `default`) |
|
|
28
|
+
| `--out=<path>` | Output manifest, relative to the working directory (default `wcstack.manifest.json`). `--out=-` prints to stdout |
|
|
29
|
+
| `--merge` | Keep everything else in an existing manifest (other states, `filters`, `listContexts`) and replace only this state's `stateSchema`. A hand-written schema for the same state is replaced, not merged |
|
|
30
|
+
| `--tsconfig=<path>` | `tsconfig.json` to compile with (default: the nearest one above the state file, else built-in defaults) |
|
|
31
|
+
| `--max-depth=<n>` | Object nesting depth at which the schema stops with a bare `{}` (default `5`, the validator's own budget) |
|
|
32
|
+
|
|
33
|
+
Exit codes: `0` written · `2` usage error, unreadable file, syntax error, or a generated manifest that failed its self-check.
|
|
34
|
+
|
|
35
|
+
### `wcs-schema check <state.ts|state.js> [options]`
|
|
36
|
+
|
|
37
|
+
Regenerates the schema and compares it with `--manifest=<path>` (default `wcstack.manifest.json`), key order ignored.
|
|
38
|
+
|
|
39
|
+
Exit codes: `0` up to date · `1` drift (each change is listed as a JSON pointer: `+` only in the type, `-` only in the manifest, `~` different) · `2` usage error, missing manifest, or the state has no `stateSchema` yet.
|
|
40
|
+
|
|
41
|
+
The recommended CI gate is
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx wcs-schema check src/state.ts && npx wcs-validate --strict index.html
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### `wcs-tsc [--url-imports=any|error] [--wcs-defaults] [tsc arguments...]`
|
|
48
|
+
|
|
49
|
+
`tsc` over `.html`: every `<wcs-state>` inline `<script type="module">` is type-checked through the same language plugin the VS Code extension uses (typed `this`, automatic `defineState` wrap, `@wcstack/state` imports stripped whether bare or a CDN URL), and diagnostics point into the HTML:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm i -D @volar/typescript@~2.4.0 @volar/language-core@~2.4.0 # optional peers, only wcs-tsc needs them
|
|
53
|
+
npx wcs-tsc --noEmit
|
|
54
|
+
# index.html(9,14): error TS2551: Property 'coutn' does not exist on type '_WcsThis<…>'. Did you mean 'count'?
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
| Option | Description |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `--url-imports=any` (default) | Every `http(s)://` module import types as `any` — buildless pages import from a CDN that tsc cannot resolve |
|
|
60
|
+
| `--url-imports=error` | Leave URL imports alone (TS2307 each) |
|
|
61
|
+
| `--wcs-defaults` | If the project tsconfig lacks `include` covering `**/*.html`, `noImplicitThis`, `allowJs` or `checkJs`, run with a temporary config that extends it and adds them (otherwise only a warning is printed and HTML may go unchecked) |
|
|
62
|
+
| anything else | passed to tsc verbatim (`-p`, `--noEmit`, …) |
|
|
63
|
+
|
|
64
|
+
Exit codes are tsc's (`0` clean, non-zero with diagnostics); `2` when `typescript` / `@volar/typescript` cannot be resolved or an option is invalid. A page with several `<wcs-state>` blocks is combined into one virtual module (imports hoisted, each block in its own scope); a page without inline state is an empty module. The mechanism is vue-tsc's: `@volar/typescript`'s `runTsc` patches the project's own `typescript/lib/tsc.js`.
|
|
65
|
+
|
|
66
|
+
## What the generated schema contains
|
|
67
|
+
|
|
68
|
+
Only the JSON-Schema subset the sidecar spec allows (`type`, `properties`, `required`, `items`, `enum`, `const`, `anyOf`):
|
|
69
|
+
|
|
70
|
+
| TypeScript | stateSchema |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `string` / `number` / `boolean` / `null` | `{ "type": … }` (`integer`-less: numbers are `number`) |
|
|
73
|
+
| `"a" \| "b"`, `1 \| 2` | `{ "type": "string", "enum": ["a", "b"] }` |
|
|
74
|
+
| `T[]`, `readonly T[]`, tuples | `{ "type": "array", "items": … }` |
|
|
75
|
+
| `T \| null` | `{ "anyOf": [T, { "type": "null" }] }` |
|
|
76
|
+
| `T \| undefined`, `x?: T` | `T`, and `x` is left out of `required` |
|
|
77
|
+
| `A \| B` (objects or mixed primitives) | `{ "anyOf": [A, B] }` |
|
|
78
|
+
| object literals, interfaces, classes from your code | `{ "type": "object", "properties": …, "required": … }` |
|
|
79
|
+
| `Date`, `Map`, `Set`, DOM types, anything from a library, `any`, `unknown`, `Record<string, T>` | a **bare `{}`** |
|
|
80
|
+
| `get x(): T` | `x: T` — getters are members |
|
|
81
|
+
| `get "users.*.ageCategory"(): string` | injected as `users.items.properties.ageCategory` — a path getter is a member at the path it computes |
|
|
82
|
+
| methods, function-valued properties, `$`-prefixed keys (`$watch`, `$commandTokens`, …) | dropped |
|
|
83
|
+
| nesting deeper than `--max-depth` | a bare `{}` |
|
|
84
|
+
|
|
85
|
+
The bare `{}` matters: the validator treats it as *unknown* and stays silent below it, whereas a typed object that lacks a member is *nonexistent* and an **error**. A `Date` or a `Record<string, unknown>` therefore never produces false errors for the paths under it.
|
|
86
|
+
|
|
87
|
+
## Library API
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { generateStateSchema, buildManifest, compareStateSchema } from "@wcstack/typescript";
|
|
91
|
+
|
|
92
|
+
const { schema, warnings } = generateStateSchema("src/state.ts"); // { tsconfig?, maxDepth? }
|
|
93
|
+
const manifest = buildManifest("default", schema /*, existingManifestObject */);
|
|
94
|
+
const result = compareStateSchema(JSON.stringify(manifest), "default", schema); // { kind: "same" } | { kind: "differs", changes } | …
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`loadStateFile` / `stateTypeToSchema` expose the two halves (compiler program, type → schema) for tools that want to compose them differently.
|
|
98
|
+
|
|
99
|
+
## Where this fits
|
|
100
|
+
|
|
101
|
+
- The validator side — discovery of the nearest `wcstack.manifest.json`, `wcs/path-nonexistent`, `wcs/path-type-mismatch` — is documented in [`@wcstack/lint`](../lint/README.md#declaring-a-state-contract-stateschema) and normatively in [`docs/wcstack-manifest-schema.md`](https://github.com/wcstack/wcstack/blob/main/docs/wcstack-manifest-schema.md).
|
|
102
|
+
- The full TypeScript story for wcstack apps (typing `this` with `defineState`, this package, tag name maps, `wcs-tsc`) is collected in [`docs/typescript.md`](https://github.com/wcstack/wcstack/blob/main/docs/typescript.md).
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* program.ts — open a state file with the TypeScript compiler API and locate the
|
|
5
|
+
* type of its `export default`.
|
|
6
|
+
*
|
|
7
|
+
* `defineState(x)` is an identity function, so the exported type is `x`'s type —
|
|
8
|
+
* but the call is unwrapped syntactically anyway: when `@wcstack/state` is not
|
|
9
|
+
* resolvable from the state file (a CDN-only page, a fixture in a temp dir), the
|
|
10
|
+
* call expression would type as `any` and every path would be lost, while the
|
|
11
|
+
* argument literal still carries the full object type.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface LoadStateOptions {
|
|
15
|
+
/** Explicit tsconfig.json path. Default: the nearest tsconfig.json above the state file, else built-in defaults. */
|
|
16
|
+
readonly tsconfig?: string;
|
|
17
|
+
}
|
|
18
|
+
interface LoadedState {
|
|
19
|
+
readonly program: ts.Program;
|
|
20
|
+
readonly checker: ts.TypeChecker;
|
|
21
|
+
readonly sourceFile: ts.SourceFile;
|
|
22
|
+
/** The type of the default export (the `defineState(...)` argument when wrapped). */
|
|
23
|
+
readonly type: ts.Type;
|
|
24
|
+
/** Node to use as the location for `getTypeOfSymbolAtLocation`. */
|
|
25
|
+
readonly location: ts.Node;
|
|
26
|
+
/** Non-fatal notes for the caller (e.g. the state type resolved to `any`). */
|
|
27
|
+
readonly warnings: readonly string[];
|
|
28
|
+
}
|
|
29
|
+
/** Defaults used when no tsconfig.json is found — enough to type a plain state file. */
|
|
30
|
+
declare const DEFAULT_COMPILER_OPTIONS: ts.CompilerOptions;
|
|
31
|
+
declare function resolveCompilerOptions(stateFile: string, options?: LoadStateOptions): ts.CompilerOptions;
|
|
32
|
+
declare function loadStateFile(file: string, options?: LoadStateOptions): LoadedState;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* typeToSchema.ts — TypeScript type → `stateSchema` (the JSON-Schema subset of
|
|
36
|
+
* docs/wcstack-manifest-schema.md §4: type / properties / required / items /
|
|
37
|
+
* enum / const / anyOf only).
|
|
38
|
+
*
|
|
39
|
+
* Rules (docs/app-testing-and-typescript-impl-plan.md §4-2-2):
|
|
40
|
+
* - `$`-prefixed keys are dropped (runtime namespaces, never data paths).
|
|
41
|
+
* - Members with call signatures (methods, function-valued properties) are dropped.
|
|
42
|
+
* - Getters contribute their return type. Path getters (`get "users.*.ageCategory"()`)
|
|
43
|
+
* are injected at the path they compute, so the validator sees them as members.
|
|
44
|
+
* - Arrays → `items`; unions split `null` out into `anyOf`; literal unions → `enum`.
|
|
45
|
+
* - Built-in / library object types (`Date`, `Map`, DOM types, …) become a **bare `{}`**
|
|
46
|
+
* — never `{ "type": "object" }`: the validator treats a bare `{}` as *unknown*
|
|
47
|
+
* (silent) and a typed object without the member as *nonexistent* (error).
|
|
48
|
+
* - Nesting stops at `maxDepth` (default 5 = the validator's candidate budget) with a bare `{}`.
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
interface JsonSchemaNode {
|
|
52
|
+
type?: string;
|
|
53
|
+
properties?: Record<string, JsonSchemaNode>;
|
|
54
|
+
required?: string[];
|
|
55
|
+
items?: JsonSchemaNode;
|
|
56
|
+
enum?: unknown[];
|
|
57
|
+
const?: unknown;
|
|
58
|
+
anyOf?: JsonSchemaNode[];
|
|
59
|
+
}
|
|
60
|
+
declare const DEFAULT_MAX_DEPTH = 5;
|
|
61
|
+
interface SchemaOptions {
|
|
62
|
+
/** Object nesting depth at which a bare `{}` is emitted instead of descending. Default 5. */
|
|
63
|
+
readonly maxDepth?: number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Convert the type of a state object into a `stateSchema` node.
|
|
67
|
+
*/
|
|
68
|
+
declare function stateTypeToSchema(checker: ts.TypeChecker, program: ts.Program, type: ts.Type, location: ts.Node, options?: SchemaOptions): JsonSchemaNode;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* generate.ts — one call from a state file to its `stateSchema`.
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
interface GenerateOptions extends LoadStateOptions, SchemaOptions {
|
|
75
|
+
}
|
|
76
|
+
interface GeneratedSchema {
|
|
77
|
+
readonly schema: JsonSchemaNode;
|
|
78
|
+
/** Non-fatal notes (e.g. the state type resolved to `any`). */
|
|
79
|
+
readonly warnings: readonly string[];
|
|
80
|
+
}
|
|
81
|
+
declare function generateStateSchema(file: string, options?: GenerateOptions): GeneratedSchema;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* manifest.ts — build / merge / compare the `application` sidecar artifact.
|
|
85
|
+
*
|
|
86
|
+
* The manifest is a **derived artifact** (D9): the TypeScript type is the source
|
|
87
|
+
* of truth, `wcs-schema emit` writes the manifest, and `wcs-schema check`
|
|
88
|
+
* detects drift between the two in CI. `--merge` replaces exactly one
|
|
89
|
+
* `states[name].stateSchema` and keeps everything else (other states, filters,
|
|
90
|
+
* listContexts) — a hand-written schema for the same state does not survive,
|
|
91
|
+
* by design: there is no implicit merge in the sidecar spec (§5).
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
declare const APPLICATION_MANIFEST_FILENAME = "wcstack.manifest.json";
|
|
95
|
+
declare const SCHEMA_VERSION = 1;
|
|
96
|
+
declare const APPLICATION_NAMESPACE = "wcstack.application";
|
|
97
|
+
interface ApplicationManifest {
|
|
98
|
+
schemaVersion: number;
|
|
99
|
+
kind: "application";
|
|
100
|
+
manifestExtensions: {
|
|
101
|
+
[APPLICATION_NAMESPACE]: {
|
|
102
|
+
version: number;
|
|
103
|
+
states?: Record<string, {
|
|
104
|
+
stateSchema: JsonSchemaNode;
|
|
105
|
+
}>;
|
|
106
|
+
[key: string]: unknown;
|
|
107
|
+
};
|
|
108
|
+
[namespace: string]: unknown;
|
|
109
|
+
};
|
|
110
|
+
[key: string]: unknown;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Create the manifest object for one state, or graft the state into `existing`
|
|
114
|
+
* (a parsed manifest object; envelope fields are filled in when absent).
|
|
115
|
+
*/
|
|
116
|
+
declare function buildManifest(stateName: string, schema: JsonSchemaNode, existing?: unknown): ApplicationManifest;
|
|
117
|
+
/** Read `states[name].stateSchema` from a parsed manifest object, or undefined. */
|
|
118
|
+
declare function readStateSchema(manifest: unknown, stateName: string): unknown;
|
|
119
|
+
/** JSON with object keys sorted at every level — the canonical form used for comparison. */
|
|
120
|
+
declare function stableStringify(value: unknown): string;
|
|
121
|
+
type SchemaComparison = {
|
|
122
|
+
readonly kind: "same";
|
|
123
|
+
} | {
|
|
124
|
+
readonly kind: "differs";
|
|
125
|
+
readonly changes: readonly string[];
|
|
126
|
+
} | {
|
|
127
|
+
readonly kind: "missing-state";
|
|
128
|
+
} | {
|
|
129
|
+
readonly kind: "broken";
|
|
130
|
+
readonly message: string;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Compare the schema generated from the type with the one stored in `manifestText`.
|
|
134
|
+
* `changes` lists JSON pointers: `+ ptr` (only in generated), `- ptr` (only in
|
|
135
|
+
* manifest), `~ ptr` (both, different value).
|
|
136
|
+
*/
|
|
137
|
+
declare function compareStateSchema(manifestText: string, stateName: string, generated: JsonSchemaNode): SchemaComparison;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* schemaCore.ts — the validator core bundle (`dist/schema-core.cjs`, built from
|
|
141
|
+
* packages/vscode-wcs by scripts/build-schema-core.mjs), loaded lazily.
|
|
142
|
+
*
|
|
143
|
+
* The bundle is a self-contained CJS file that requires neither `typescript` nor
|
|
144
|
+
* `vscode`; it is located relative to the running module so the same loader
|
|
145
|
+
* works from `dist/index.esm.js`, `dist/wcs-schema.mjs`, and the TypeScript
|
|
146
|
+
* sources under vitest (`src/…` → `../dist`).
|
|
147
|
+
*/
|
|
148
|
+
interface WcsDiagnostic {
|
|
149
|
+
readonly code: string;
|
|
150
|
+
readonly start: number;
|
|
151
|
+
readonly end: number;
|
|
152
|
+
readonly message: string;
|
|
153
|
+
readonly severity: "error" | "warning" | "info";
|
|
154
|
+
}
|
|
155
|
+
interface SchemaCore {
|
|
156
|
+
validateManifestArtifact(artifact: {
|
|
157
|
+
text: string;
|
|
158
|
+
source: string;
|
|
159
|
+
}): WcsDiagnostic[];
|
|
160
|
+
validateDocument(text: string, options?: {
|
|
161
|
+
bindAttribute?: string;
|
|
162
|
+
stateTagName?: string;
|
|
163
|
+
locale?: string;
|
|
164
|
+
fileReader?: (relativePath: string) => string | undefined;
|
|
165
|
+
applicationStates?: ReadonlyMap<string, unknown>;
|
|
166
|
+
}): WcsDiagnostic[];
|
|
167
|
+
ALLOWED_SCHEMA_KEYWORDS: ReadonlySet<string>;
|
|
168
|
+
WcsDiagnosticCode: Readonly<Record<string, string>>;
|
|
169
|
+
}
|
|
170
|
+
/** Candidate locations, nearest first: dist/ (built), ../dist (src/), ../../dist (src/cli/). */
|
|
171
|
+
declare function schemaCoreCandidates(fromUrl?: string): string[];
|
|
172
|
+
declare function loadSchemaCore(): SchemaCore;
|
|
173
|
+
|
|
174
|
+
declare const VERSION: string;
|
|
175
|
+
|
|
176
|
+
export { APPLICATION_MANIFEST_FILENAME, APPLICATION_NAMESPACE, DEFAULT_COMPILER_OPTIONS, DEFAULT_MAX_DEPTH, SCHEMA_VERSION, VERSION, buildManifest, compareStateSchema, generateStateSchema, loadSchemaCore, loadStateFile, readStateSchema, resolveCompilerOptions, schemaCoreCandidates, stableStringify, stateTypeToSchema };
|
|
177
|
+
export type { ApplicationManifest, GenerateOptions, GeneratedSchema, JsonSchemaNode, LoadStateOptions, LoadedState, SchemaComparison, SchemaCore, SchemaOptions, WcsDiagnostic };
|