schemair 0.1.0 → 0.2.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.md +420 -11
- package/dist/authoring/index.cjs +13 -350
- package/dist/authoring/index.d.cts +2 -90
- package/dist/authoring/index.d.ts +2 -90
- package/dist/authoring/index.js +2 -304
- package/dist/authoring-D3V0iyTe.js +362 -0
- package/dist/authoring-Dnrg5TXm.cjs +389 -0
- package/dist/chunk-BgLxgFZR.js +18 -0
- package/dist/definition/index.cjs +10 -2
- package/dist/definition/index.d.cts +3 -21
- package/dist/definition/index.d.ts +3 -21
- package/dist/definition/index.js +2 -2
- package/dist/definition-BLLBpLKv.cjs +1187 -0
- package/dist/definition-DmYnDmQO.js +1115 -0
- package/dist/index-4aMF0cTE.d.ts +368 -0
- package/dist/index-BqfGAU1K.d.ts +56 -0
- package/dist/{index-eK2Xgrwp.d.ts → index-CBbd4hXJ.d.ts} +13 -2
- package/dist/index-CIim1dG7.d.cts +92 -0
- package/dist/{index-TACcK_mi.d.cts → index-CNjYUKvu.d.cts} +13 -2
- package/dist/index-CQynt7ft.d.cts +420 -0
- package/dist/index-D3mfwaSO.d.cts +56 -0
- package/dist/index-DDMwVy2I.d.cts +368 -0
- package/dist/index-DM5DSLLL.d.ts +99 -0
- package/dist/index-DXWoNYFB.d.ts +92 -0
- package/dist/index-DhmHCGpT.d.cts +99 -0
- package/dist/index-bQy-C-1l.d.ts +418 -0
- package/dist/index.cjs +64 -52
- package/dist/index.d.cts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +7 -5
- package/dist/integrations/json-schema/index.cjs +328 -0
- package/dist/integrations/json-schema/index.d.cts +53 -0
- package/dist/integrations/json-schema/index.d.ts +53 -0
- package/dist/integrations/json-schema/index.js +324 -0
- package/dist/integrations/standard-schema/index.cjs +32 -0
- package/dist/integrations/standard-schema/index.d.cts +22 -0
- package/dist/integrations/standard-schema/index.d.ts +22 -0
- package/dist/integrations/standard-schema/index.js +32 -0
- package/dist/payload/index.cjs +1 -1
- package/dist/payload/index.d.cts +1 -55
- package/dist/payload/index.d.ts +1 -55
- package/dist/payload/index.js +1 -1
- package/dist/relations/index.cjs +3 -1
- package/dist/relations/index.d.cts +2 -2
- package/dist/relations/index.d.ts +2 -2
- package/dist/relations/index.js +2 -2
- package/dist/{relations-CMz_0nf4.cjs → relations-Cymx4iTc.cjs} +158 -5
- package/dist/{relations-By0bK3UK.js → relations-ysXDmi-Z.js} +147 -6
- package/dist/spec/index.cjs +9 -0
- package/dist/spec/index.d.cts +2 -2
- package/dist/spec/index.d.ts +2 -2
- package/dist/spec/index.js +3 -1
- package/dist/standard/index.cjs +810 -0
- package/dist/standard/index.d.cts +3 -0
- package/dist/standard/index.d.ts +3 -0
- package/dist/standard/index.js +777 -0
- package/dist/standard-DhKsxu9U.js +471 -0
- package/dist/standard-etPFZDJI.cjs +513 -0
- package/dist/type-interpreter/index.cjs +3 -3
- package/dist/type-interpreter/index.d.cts +2 -99
- package/dist/type-interpreter/index.d.ts +2 -99
- package/dist/type-interpreter/index.js +2 -2
- package/dist/{type-interpreter-Ck0VDhzn.js → type-interpreter-DnrId8aM.js} +5 -284
- package/dist/{type-interpreter-DAIBUqKf.cjs → type-interpreter-OLt2W2yd.cjs} +15 -294
- package/package.json +38 -3
- package/dist/definition-BXj3T94L.js +0 -467
- package/dist/definition-DSZZTz4e.cjs +0 -473
- package/dist/index-CEzHfht2.d.ts +0 -269
- package/dist/index-rzkFjgrt.d.cts +0 -269
package/README.md
CHANGED
|
@@ -7,17 +7,35 @@ model and evaluation rules, start with the
|
|
|
7
7
|
[project README](https://github.com/schemair/schemair#readme) and
|
|
8
8
|
[protocol](https://github.com/schemair/schemair/tree/main/spec).
|
|
9
9
|
|
|
10
|
+
The `schemair/authoring` entry point exposes two namespaces: `schema` contains
|
|
11
|
+
builders for serializable `SchemaNode` contracts, while `expression` contains
|
|
12
|
+
builders for serializable `SchemaTypeExpr` calculations. A schema node and an
|
|
13
|
+
expression are both plain data and can be stored, exchanged, or evaluated by
|
|
14
|
+
the interpreter.
|
|
15
|
+
|
|
16
|
+
For statically authored contracts, `schemair/authoring` also exports the
|
|
17
|
+
TypeScript-only `Infer<Schema>` utility. It projects a source-known schema into
|
|
18
|
+
a payload carrier type; it does not alter the portable declaration or replace
|
|
19
|
+
runtime payload validation.
|
|
20
|
+
|
|
21
|
+
The optional `schemair/standard` entry point provides the protocol's `std`
|
|
22
|
+
semantic and constraint vocabulary, including reusable paths, definitions,
|
|
23
|
+
standard payload predicates, constraint reasoning, and an opt-in relation
|
|
24
|
+
context. Core validation and relation APIs remain provider-driven; import
|
|
25
|
+
`standardPayloadContext` or `standardRelationContext` when the standard
|
|
26
|
+
vocabulary policy is wanted.
|
|
27
|
+
|
|
10
28
|
## Install
|
|
11
29
|
|
|
12
|
-
|
|
30
|
+
Install the published package from npm:
|
|
13
31
|
|
|
14
32
|
```sh
|
|
15
33
|
pnpm add schemair
|
|
16
34
|
```
|
|
17
35
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
36
|
+
To work on the implementation in this repository, run `pnpm install` in this
|
|
37
|
+
directory. The published package provides ESM `.js` files for `import` and CJS
|
|
38
|
+
`.cjs` files for `require`, with matching TypeScript declarations.
|
|
21
39
|
|
|
22
40
|
## Define and validate a schema
|
|
23
41
|
|
|
@@ -26,11 +44,11 @@ Builders return ordinary serializable objects. You can also write the same
|
|
|
26
44
|
before using them.
|
|
27
45
|
|
|
28
46
|
```ts
|
|
29
|
-
import {
|
|
47
|
+
import { schema as s } from 'schemair/authoring';
|
|
30
48
|
import { validateSchemaNodeDefinition } from 'schemair/definition';
|
|
31
49
|
import { validatePayload } from 'schemair/payload';
|
|
32
50
|
|
|
33
|
-
const user = record([requiredField('name', string())]);
|
|
51
|
+
const user = s.record([s.requiredField('name', s.string())]);
|
|
34
52
|
const definition = validateSchemaNodeDefinition(user);
|
|
35
53
|
if (!definition.ok) throw new Error(definition.issues[0]?.message);
|
|
36
54
|
|
|
@@ -44,16 +62,244 @@ if (result.status !== 'accepted') console.error(result.issues);
|
|
|
44
62
|
needed `resolveRef`, `semanticProvider`, or `constraintProvider` in the payload
|
|
45
63
|
context. Missing capabilities can produce `unknown`.
|
|
46
64
|
|
|
65
|
+
## Infer a TypeScript payload type
|
|
66
|
+
|
|
67
|
+
`Infer<Schema, Registry>` is available when the schema is authored in TypeScript and its
|
|
68
|
+
literal structure remains available to the compiler. It supports primitive,
|
|
69
|
+
literal, literal-union, nullable, array, tuple, record, and tagged-union
|
|
70
|
+
nodes.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { schema as s, type Infer } from 'schemair/authoring';
|
|
74
|
+
|
|
75
|
+
const user = s.record([
|
|
76
|
+
s.requiredField('id', s.string()),
|
|
77
|
+
s.requiredField('age', s.number()),
|
|
78
|
+
s.optionalField('nickname', s.string()),
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
type User = Infer<typeof user>;
|
|
82
|
+
// { id: string; age: number; nickname?: string }
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`Infer` is intentionally conservative. Named `ref` declarations can be resolved
|
|
86
|
+
by passing a statically authored registry as the second parameter, including
|
|
87
|
+
references nested inside another registered schema:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
const registry = s.defineSchemaRegistry([
|
|
91
|
+
{ refPath: ['users', 'User'] as const, name: 'User', schema: user },
|
|
92
|
+
]);
|
|
93
|
+
const order = s.record([s.requiredField('owner', s.ref(['users', 'User']))]);
|
|
94
|
+
type Order = Infer<typeof order, typeof registry>;
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Unresolved, dynamically shaped, ambiguous, or cyclic references project to
|
|
98
|
+
`unknown`. `Infer` is intentionally conservative. Typed
|
|
99
|
+
additional record fields, broad `SchemaNode` values, and unsupported or
|
|
100
|
+
dynamically loaded declarations project to `unknown`. A structural `selfRef`
|
|
101
|
+
can refer back to the root schema, so common tree-shaped records can be
|
|
102
|
+
inferred recursively. Semantic paths and constraints remain their carrier
|
|
103
|
+
types: an email is `string`, and an integer is `number`. Always use
|
|
104
|
+
payload validation for data received at runtime.
|
|
105
|
+
|
|
106
|
+
For statically known references, pair `InferRef` with a plain declaration
|
|
107
|
+
registry:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { schema as s, type InferRef } from 'schemair/authoring';
|
|
111
|
+
|
|
112
|
+
const registry = s.defineSchemaRegistry([
|
|
113
|
+
{ refPath: ['users', 'User'], name: 'User', schema: user },
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
type UserFromRef = InferRef<typeof registry, ['users', 'User']>;
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Missing or dynamically shaped paths project to `unknown`. The registry helper
|
|
120
|
+
does not resolve references or replace runtime definition validation.
|
|
121
|
+
|
|
122
|
+
`InferResult<Schema, Registry>` is the diagnostic form of the same projection.
|
|
123
|
+
It keeps failures out of ordinary payload types and reports a small, stable
|
|
124
|
+
reason such as `unresolved-ref`, `dynamic-ref-path`, or `unsupported-schema`:
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import type { InferResult } from 'schemair/authoring';
|
|
128
|
+
|
|
129
|
+
type CheckedUser = InferResult<typeof user>;
|
|
130
|
+
// { ok: true; type: { id: string; age: number; nickname?: string } }
|
|
131
|
+
type Missing = InferResult<typeof s.ref(['users', 'Missing'])>;
|
|
132
|
+
// { ok: false; reason: 'unresolved-ref' }
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
This is a compile-time utility, not runtime validation. Use the definition
|
|
136
|
+
validators when the declaration comes from JSON, a database, or an AI tool.
|
|
137
|
+
|
|
138
|
+
## Validate expressions before evaluation
|
|
139
|
+
|
|
140
|
+
Use `validateSchemaTypeExprDefinition(unknown)` to check an expression from
|
|
141
|
+
JSON, a database, or an AI proposal. Use `validateSchemaTypeTermDefinition`
|
|
142
|
+
when the declaration may be either a `SchemaNode` or a `SchemaTypeExpr`.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { validateSchemaTypeExprDefinition } from 'schemair/definition';
|
|
146
|
+
import { expression as e } from 'schemair/authoring';
|
|
147
|
+
|
|
148
|
+
const result = validateSchemaTypeExprDefinition(e.fieldOf(e.typeVar('T'), 'name'));
|
|
149
|
+
if (result.ok) console.log(result.expression);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Validation preserves the original declaration and reports diagnostics with
|
|
153
|
+
paths. It checks nested terms, selectors, constraints, and JSON compatibility;
|
|
154
|
+
it does not evaluate expressions, bind variables, resolve references, or call
|
|
155
|
+
providers. Unbound variables and unresolved references are valid declarations.
|
|
156
|
+
A structurally valid expression can still be rejected during evaluation (for
|
|
157
|
+
example, selecting a field from a string). Validation has a depth limit of 64
|
|
158
|
+
and a work limit of 10,000 visited JSON values; exceeding either rejects the
|
|
159
|
+
check with an `expression.budget` diagnostic.
|
|
160
|
+
|
|
161
|
+
## Use the standard vocabulary
|
|
162
|
+
|
|
163
|
+
The optional `schemair/standard` entry provides 20 semantic definitions and
|
|
164
|
+
13 constraint definitions, their path constants, registry metadata, and
|
|
165
|
+
payload/relation providers. Install the providers explicitly when you want
|
|
166
|
+
standard vocabulary decisions:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { schema as s } from 'schemair/authoring';
|
|
170
|
+
import { validatePayload } from 'schemair/payload';
|
|
171
|
+
import {
|
|
172
|
+
STANDARD_SEMANTIC_PATHS, STANDARD_CONSTRAINT_PATHS,
|
|
173
|
+
standardPayloadContext,
|
|
174
|
+
} from 'schemair/standard';
|
|
175
|
+
|
|
176
|
+
const email = s.string({
|
|
177
|
+
semanticPath: [...STANDARD_SEMANTIC_PATHS.string.email],
|
|
178
|
+
constraints: [{ constraintPath: [...STANDARD_CONSTRAINT_PATHS.string.maxLength], args: 254 }],
|
|
179
|
+
});
|
|
180
|
+
const result = validatePayload('ada@example.com', email, standardPayloadContext);
|
|
181
|
+
console.log(result.status); // accepted
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Use `STANDARD_SEMANTIC_DEFINITIONS` and `STANDARD_CONSTRAINT_DEFINITIONS` to
|
|
185
|
+
build vocabulary pickers, show validation versus annotation roles, or inspect
|
|
186
|
+
constraint targets and argument domains. `standardRelationContext` supplies
|
|
187
|
+
accepted-set reasoning to `checkSchemaRelation`; `createStandardRelationContext`
|
|
188
|
+
lets the host select an additional semantic identity policy.
|
|
189
|
+
|
|
190
|
+
The portable vocabulary specifies declarations and meaning. The TypeScript
|
|
191
|
+
providers implement those meanings within a JavaScript host: numbers use
|
|
192
|
+
finite binary64 values; string lengths count Unicode code points; patterns
|
|
193
|
+
use the ECMAScript regex engine. Unsupported pattern syntax and unknown or
|
|
194
|
+
extension paths produce `unknown` and need an appropriate host provider.
|
|
195
|
+
An annotation such as `std/string/secret/password` labels a string; it imposes no
|
|
196
|
+
password-strength validation. `standardSchemaSatisfiability` reports
|
|
197
|
+
`inhabited`, `emptySet`, or `unknown`; an undecidable combination is never
|
|
198
|
+
silently treated as empty. See the [standard vocabulary specification](../spec/protocol/standard-vocabulary.md)
|
|
199
|
+
for portable semantics and implementation boundaries.
|
|
200
|
+
|
|
201
|
+
## Define an operation
|
|
202
|
+
|
|
203
|
+
An operation describes its input and output contracts as data. The host owns
|
|
204
|
+
invocation and execution.
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
import { schema as s } from 'schemair/authoring';
|
|
208
|
+
import type { OperationDefinition } from 'schemair/spec';
|
|
209
|
+
|
|
210
|
+
const lookupUser: OperationDefinition = {
|
|
211
|
+
input: { kind: 'structured', schema: s.record([s.requiredField('id', s.string())]) },
|
|
212
|
+
output: { kind: 'structured', schema: s.record([s.requiredField('name', s.string())]) },
|
|
213
|
+
};
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
For TypeScript consumers, operation inference projects structured payloads and
|
|
217
|
+
lets the host choose the carrier for binary payloads. The carrier is a host
|
|
218
|
+
policy and is not part of the portable operation declaration:
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
import type {
|
|
222
|
+
InferOperationInput, InferOperationOutput, InferOperationErrors,
|
|
223
|
+
InferOperationEmits,
|
|
224
|
+
} from 'schemair/authoring';
|
|
225
|
+
|
|
226
|
+
type Binary = { bytes: Uint8Array; mime?: string };
|
|
227
|
+
type Input = InferOperationInput<typeof lookupUser>;
|
|
228
|
+
type Output = InferOperationOutput<typeof lookupUser, never, Binary>;
|
|
229
|
+
type Errors = InferOperationErrors<typeof lookupUser>;
|
|
230
|
+
type Events = InferOperationEmits<typeof lookupUser, never, Binary>;
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Absent input/output or payload data is represented as `undefined`; named error
|
|
234
|
+
and event maps retain their keys. This is a TypeScript convenience layer and
|
|
235
|
+
does not add functions, generic templates, or host carriers to the protocol.
|
|
236
|
+
|
|
237
|
+
An operation definition is a concrete, transport-neutral contract. Its
|
|
238
|
+
structured payloads contain `SchemaNode` data; functions, unresolved
|
|
239
|
+
`SchemaTypeExpr` terms, and type-variable templates belong to the authoring
|
|
240
|
+
layer and must be resolved before publishing the operation. This makes the
|
|
241
|
+
same declaration usable by visual tools, workflow systems, and AI-assisted
|
|
242
|
+
contract tools without requiring them to understand HTTP details.
|
|
243
|
+
|
|
244
|
+
## Validate portable operation declarations
|
|
245
|
+
|
|
246
|
+
Use `validateOperationDefinition` and `validateOperationContainerDefinition`
|
|
247
|
+
from `schemair/definition` when loading declarations from JSON, a database, or
|
|
248
|
+
an AI-assisted tool. Successful results return the original `operation` or
|
|
249
|
+
`container`; failures return `issues` with `code`, `path`, and `message`.
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
import { validateOperationDefinition } from 'schemair/definition';
|
|
253
|
+
|
|
254
|
+
const declaration: unknown = JSON.parse('{"emits":{"progress":{}}}');
|
|
255
|
+
const result = validateOperationDefinition(declaration);
|
|
256
|
+
if (result.ok) console.log(result.operation);
|
|
257
|
+
else console.error(result.issues);
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Operation payloads and error details contain concrete `SchemaNode` contracts.
|
|
261
|
+
Type expressions, generic templates, functions, and execution bindings are
|
|
262
|
+
outside this model. Each embedded schema is validated as its own root;
|
|
263
|
+
reference resolution and vocabulary policy are supplied separately.
|
|
264
|
+
|
|
265
|
+
## Compare operation contracts
|
|
266
|
+
|
|
267
|
+
`checkOperationRelation(source, target)` explains whether `source` can replace
|
|
268
|
+
`target` for a caller that follows the target contract. Its diagnostics retain
|
|
269
|
+
the operation path and include `details.component` (`input`, `output`, `error`,
|
|
270
|
+
or `emission`) plus the comparison direction when the issue comes from a
|
|
271
|
+
nested schema relation. An input comparison is reported in the reversed
|
|
272
|
+
direction because callers supply the target's input shape. An unproved
|
|
273
|
+
semantic, constraint, reference, or binary metadata implication remains
|
|
274
|
+
`unknown`.
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
import { checkOperationRelation } from 'schemair/relations';
|
|
278
|
+
|
|
279
|
+
const relation = checkOperationRelation(sourceOperation, targetOperation);
|
|
280
|
+
for (const issue of relation.issues) {
|
|
281
|
+
console.log(issue.path, issue.details?.component, issue.message);
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
`validateStructuredOperationDefinition` and
|
|
286
|
+
`validateStructuredOperationContainerDefinition` apply the same checks and
|
|
287
|
+
reject binary payloads anywhere in the declaration. Their boolean companions,
|
|
288
|
+
`isStructuredOperationDefinition` and
|
|
289
|
+
`isStructuredOperationContainerDefinition`, narrow unknown input to the
|
|
290
|
+
exported `StructuredOperation*` types. A successful check preserves the
|
|
291
|
+
original declaration, including absent payloads and empty channels.
|
|
292
|
+
|
|
47
293
|
## Compare types
|
|
48
294
|
|
|
49
295
|
`checkSchemaRelation(source, target)` asks whether values admitted by the
|
|
50
296
|
source can be used where the target is expected.
|
|
51
297
|
|
|
52
298
|
```ts
|
|
53
|
-
import {
|
|
299
|
+
import { schema as s } from 'schemair/authoring';
|
|
54
300
|
import { checkSchemaRelation } from 'schemair/relations';
|
|
55
301
|
|
|
56
|
-
const relation = checkSchemaRelation(string(), string());
|
|
302
|
+
const relation = checkSchemaRelation(s.string(), s.string());
|
|
57
303
|
if (relation.status === 'assignable') {
|
|
58
304
|
// The source can be connected to the target.
|
|
59
305
|
}
|
|
@@ -70,10 +316,10 @@ result always has a `term`; it also has `schema` when that term can be
|
|
|
70
316
|
materialized as a `SchemaNode`.
|
|
71
317
|
|
|
72
318
|
```ts
|
|
73
|
-
import {
|
|
319
|
+
import { schema as s, expression as e } from 'schemair/authoring';
|
|
74
320
|
import { evaluateExpression } from 'schemair/type-interpreter';
|
|
75
321
|
|
|
76
|
-
const result = evaluateExpression(arrayOf(string()));
|
|
322
|
+
const result = evaluateExpression(e.arrayOf(s.string()));
|
|
77
323
|
if (result.status === 'evaluated') {
|
|
78
324
|
console.log(result.term);
|
|
79
325
|
console.log(result.schema);
|
|
@@ -85,6 +331,54 @@ Unbound type variables remain in the evaluated term and appear in
|
|
|
85
331
|
with structured issues. `checkTypeTermRelation` compares expression terms;
|
|
86
332
|
`solveTypeVariables` infers invocation-local bindings for generic ports.
|
|
87
333
|
|
|
334
|
+
## Compare expression terms
|
|
335
|
+
|
|
336
|
+
Unlike `checkSchemaRelation`, this API also compares terms that cannot become
|
|
337
|
+
`SchemaNode`, such as opaque host types. Equal host paths match directly;
|
|
338
|
+
the host supplies any other native type relationship.
|
|
339
|
+
|
|
340
|
+
```ts
|
|
341
|
+
import { schema as s, expression as e } from 'schemair/authoring';
|
|
342
|
+
import { checkTypeTermRelation } from 'schemair/type-interpreter';
|
|
343
|
+
|
|
344
|
+
const mouseEvent = e.opaqueHostType(['dom', 'MouseEvent']);
|
|
345
|
+
const event = e.opaqueHostType(['dom', 'Event']);
|
|
346
|
+
const relation = checkTypeTermRelation(mouseEvent, event, {
|
|
347
|
+
hostTypeRelation: (source, target) =>
|
|
348
|
+
source.join('/') === 'dom/MouseEvent' && target.join('/') === 'dom/Event',
|
|
349
|
+
});
|
|
350
|
+
console.log(relation.status); // 'assignable'
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## Solve type variables
|
|
354
|
+
|
|
355
|
+
For a map-like operation, infer `T` from its input array and `U` from the
|
|
356
|
+
mapper's output. The source-to-target constraints represent port connections.
|
|
357
|
+
|
|
358
|
+
```ts
|
|
359
|
+
import { schema as s, expression as e } from 'schemair/authoring';
|
|
360
|
+
import { evaluateExpression, solveTypeVariables } from 'schemair/type-interpreter';
|
|
361
|
+
|
|
362
|
+
const solution = solveTypeVariables({
|
|
363
|
+
variables: ['T', 'U'],
|
|
364
|
+
constraints: [
|
|
365
|
+
{ source: s.array(s.number()), target: e.arrayOf(e.typeVar('T')) },
|
|
366
|
+
{ source: s.string(), target: e.typeVar('U') },
|
|
367
|
+
],
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
if (solution.status === 'solved') {
|
|
371
|
+
const output = evaluateExpression(e.arrayOf(e.typeVar('U')), {
|
|
372
|
+
typeVars: solution.bindings,
|
|
373
|
+
});
|
|
374
|
+
if (output.status === 'evaluated') console.log(output.schema);
|
|
375
|
+
// { kind: 'array', elementSchema: { kind: 'primitive', name: 'string' } }
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
When a variable has no evidence, the solver lists it in `unresolved` instead
|
|
380
|
+
of inventing a binding. Inspect `status` and `issues` before using a result.
|
|
381
|
+
|
|
88
382
|
## Sync and async APIs
|
|
89
383
|
|
|
90
384
|
Use synchronous calls with in-memory resolvers and providers. For database or
|
|
@@ -102,6 +396,118 @@ network-backed capabilities, use the asynchronous companions:
|
|
|
102
396
|
await caller-provided capabilities and use the same result categories as their
|
|
103
397
|
sync counterparts.
|
|
104
398
|
|
|
399
|
+
For example, a reference catalog may live in a database:
|
|
400
|
+
|
|
401
|
+
```ts
|
|
402
|
+
import { schema as s } from 'schemair/authoring';
|
|
403
|
+
import { validatePayloadAsync } from 'schemair/payload';
|
|
404
|
+
|
|
405
|
+
const user = s.record([s.requiredField('name', s.string())]);
|
|
406
|
+
const result = await validatePayloadAsync(
|
|
407
|
+
{ name: 'Ada' },
|
|
408
|
+
s.ref(['catalog', 'user']),
|
|
409
|
+
{
|
|
410
|
+
resolveRef: async (path) =>
|
|
411
|
+
path.join('/') === 'catalog/user' ? user : undefined,
|
|
412
|
+
},
|
|
413
|
+
);
|
|
414
|
+
console.log(result.status); // 'accepted'
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Async relation checks, expression evaluation, and variable solving accept
|
|
418
|
+
async resolvers in their corresponding contexts. Payload semantic and
|
|
419
|
+
constraint providers may also be async.
|
|
420
|
+
|
|
421
|
+
## Standard Schema integration
|
|
422
|
+
|
|
423
|
+
The `schemair/standard-schema` subpath exposes a runtime-only Standard Schema
|
|
424
|
+
adapter for dynamic `SchemaNode` data:
|
|
425
|
+
|
|
426
|
+
```ts
|
|
427
|
+
import { schemaIRToStandardSchema } from 'schemair/standard-schema';
|
|
428
|
+
import { schema as s } from 'schemair/authoring';
|
|
429
|
+
|
|
430
|
+
const standard = schemaIRToStandardSchema(
|
|
431
|
+
s.record([s.requiredField('name', s.string())]),
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
const result = await standard['~standard'].validate({ name: 'Ada' });
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
The adapter always uses the asynchronous Standard Schema validator so the
|
|
438
|
+
caller can provide asynchronous `resolveRef`, `semanticProvider`, and
|
|
439
|
+
`constraintProvider` callbacks. Successful validation returns the original
|
|
440
|
+
input reference. Failed, unknown, unsupported, and unresolved validation
|
|
441
|
+
outcomes are projected to Standard Schema `issues`; native SchemaIR payload
|
|
442
|
+
diagnostics remain available through `schemair/payload`.
|
|
443
|
+
|
|
444
|
+
Because the adapter receives runtime schema data, it intentionally does not
|
|
445
|
+
claim a static `~standard.types` input/output type. It is a runtime adapter,
|
|
446
|
+
not a typed authoring inference surface.
|
|
447
|
+
|
|
448
|
+
## JSON Schema and OpenAPI integration
|
|
449
|
+
|
|
450
|
+
The `schemair/json-schema` subpath projects a `SchemaNode` to JSON Schema
|
|
451
|
+
Draft 2020-12 by default. Draft-07 is also supported with its legacy tuple and
|
|
452
|
+
exclusive-bound keywords. OpenAPI 3.0 is available as a separate Schema Object
|
|
453
|
+
projection through `schemaNodeToOpenApiSchema` or the Standard JSON Schema V1
|
|
454
|
+
target. It is intentionally described as a projection rather than a full JSON
|
|
455
|
+
Schema dialect because OpenAPI 3.0 cannot represent every SchemaIR construct.
|
|
456
|
+
|
|
457
|
+
The exporter uses native keywords whenever the meaning is equivalent and
|
|
458
|
+
returns projection diagnostics for anything that cannot be represented exactly:
|
|
459
|
+
|
|
460
|
+
```ts
|
|
461
|
+
import { schemaNodeToJsonSchema } from 'schemair/json-schema';
|
|
462
|
+
|
|
463
|
+
const result = schemaNodeToJsonSchema({
|
|
464
|
+
kind: 'primitive',
|
|
465
|
+
name: 'string',
|
|
466
|
+
semanticPath: ['ext', 'money'],
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// result.schema contains { type: 'string', 'x-schemair': { semanticPath: [...] } }
|
|
470
|
+
// result.fidelity is 'exact', 'lossy', or 'unsupported'.
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
Select a JSON Schema dialect explicitly when needed:
|
|
474
|
+
|
|
475
|
+
```ts
|
|
476
|
+
const draft07 = schemaNodeToJsonSchema(tupleNode, { target: 'draft-07' });
|
|
477
|
+
// Draft-07 tuples use items: [...] and additionalItems: false.
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
OpenAPI 3.0 uses `nullable: true` for nullable schemas and preserves tuple
|
|
481
|
+
positions, literals, semantic identity, and other non-native details under
|
|
482
|
+
`x-schemair`. Standalone `null` schemas and unresolved references are reported
|
|
483
|
+
as `unsupported`; lossy projections are reported in `diagnostics` and are not
|
|
484
|
+
silently treated as exact.
|
|
485
|
+
|
|
486
|
+
Unmapped semantic paths, constraints, and references are retained under the
|
|
487
|
+
`x-schemair` object. A caller may supply `semanticMapper`, `constraintMapper`,
|
|
488
|
+
and `refStrategy` options for host-owned vocabularies and reference catalogs.
|
|
489
|
+
The exporter never silently drops SchemaIR metadata.
|
|
490
|
+
|
|
491
|
+
The same subpath also implements [Standard JSON Schema V1](https://standardschema.dev/json-schema):
|
|
492
|
+
|
|
493
|
+
```ts
|
|
494
|
+
import { schemaIRToStandardJsonSchema } from 'schemair/json-schema';
|
|
495
|
+
|
|
496
|
+
const standard = schemaIRToStandardJsonSchema(user);
|
|
497
|
+
const jsonSchema = standard['~standard'].jsonSchema.input({
|
|
498
|
+
target: 'draft-2020-12',
|
|
499
|
+
});
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
`input()` and `output()` are identical because SchemaIR declarations do not
|
|
503
|
+
perform value transformations. Unsupported targets or projections throw; a
|
|
504
|
+
lossy projection is returned when its original metadata is preserved in
|
|
505
|
+
`x-schemair`.
|
|
506
|
+
|
|
507
|
+
See the [JSON Schema compatibility matrix](./docs/json-schema-compatibility.md)
|
|
508
|
+
for the target-by-target fidelity rules, extension fields, capability
|
|
509
|
+
callbacks, and required diagnostics.
|
|
510
|
+
|
|
105
511
|
## Package entry points
|
|
106
512
|
|
|
107
513
|
| Import path | Contents |
|
|
@@ -112,7 +518,10 @@ sync counterparts.
|
|
|
112
518
|
| `schemair/payload` | Payload validation |
|
|
113
519
|
| `schemair/relations` | SchemaNode assignability |
|
|
114
520
|
| `schemair/type-interpreter` | Expression evaluation and type-term relations |
|
|
115
|
-
| `schemair/authoring` |
|
|
521
|
+
| `schemair/authoring` | `schema` builders, `expression` builders, and TypeScript-only `Infer<Schema>` |
|
|
522
|
+
| `schemair/standard` | Standard semantic/constraint paths, registries, predicates, and relation helpers |
|
|
523
|
+
| `schemair/standard-schema` | Standard Schema runtime adapter |
|
|
524
|
+
| `schemair/json-schema` | JSON Schema Draft-2020-12/Draft-07 and OpenAPI 3.0 projections |
|
|
116
525
|
|
|
117
526
|
`src/internal/` is not exported. The package provides no ref registry,
|
|
118
527
|
semantic vocabulary implementation, persistence layer, graph scheduler, or
|