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.
Files changed (69) hide show
  1. package/README.md +420 -11
  2. package/dist/authoring/index.cjs +13 -350
  3. package/dist/authoring/index.d.cts +2 -90
  4. package/dist/authoring/index.d.ts +2 -90
  5. package/dist/authoring/index.js +2 -304
  6. package/dist/authoring-D3V0iyTe.js +362 -0
  7. package/dist/authoring-Dnrg5TXm.cjs +389 -0
  8. package/dist/chunk-BgLxgFZR.js +18 -0
  9. package/dist/definition/index.cjs +10 -2
  10. package/dist/definition/index.d.cts +3 -21
  11. package/dist/definition/index.d.ts +3 -21
  12. package/dist/definition/index.js +2 -2
  13. package/dist/definition-BLLBpLKv.cjs +1187 -0
  14. package/dist/definition-DmYnDmQO.js +1115 -0
  15. package/dist/index-4aMF0cTE.d.ts +368 -0
  16. package/dist/index-BqfGAU1K.d.ts +56 -0
  17. package/dist/{index-eK2Xgrwp.d.ts → index-CBbd4hXJ.d.ts} +13 -2
  18. package/dist/index-CIim1dG7.d.cts +92 -0
  19. package/dist/{index-TACcK_mi.d.cts → index-CNjYUKvu.d.cts} +13 -2
  20. package/dist/index-CQynt7ft.d.cts +420 -0
  21. package/dist/index-D3mfwaSO.d.cts +56 -0
  22. package/dist/index-DDMwVy2I.d.cts +368 -0
  23. package/dist/index-DM5DSLLL.d.ts +99 -0
  24. package/dist/index-DXWoNYFB.d.ts +92 -0
  25. package/dist/index-DhmHCGpT.d.cts +99 -0
  26. package/dist/index-bQy-C-1l.d.ts +418 -0
  27. package/dist/index.cjs +64 -52
  28. package/dist/index.d.cts +7 -7
  29. package/dist/index.d.ts +7 -7
  30. package/dist/index.js +7 -5
  31. package/dist/integrations/json-schema/index.cjs +328 -0
  32. package/dist/integrations/json-schema/index.d.cts +53 -0
  33. package/dist/integrations/json-schema/index.d.ts +53 -0
  34. package/dist/integrations/json-schema/index.js +324 -0
  35. package/dist/integrations/standard-schema/index.cjs +32 -0
  36. package/dist/integrations/standard-schema/index.d.cts +22 -0
  37. package/dist/integrations/standard-schema/index.d.ts +22 -0
  38. package/dist/integrations/standard-schema/index.js +32 -0
  39. package/dist/payload/index.cjs +1 -1
  40. package/dist/payload/index.d.cts +1 -55
  41. package/dist/payload/index.d.ts +1 -55
  42. package/dist/payload/index.js +1 -1
  43. package/dist/relations/index.cjs +3 -1
  44. package/dist/relations/index.d.cts +2 -2
  45. package/dist/relations/index.d.ts +2 -2
  46. package/dist/relations/index.js +2 -2
  47. package/dist/{relations-CMz_0nf4.cjs → relations-Cymx4iTc.cjs} +158 -5
  48. package/dist/{relations-By0bK3UK.js → relations-ysXDmi-Z.js} +147 -6
  49. package/dist/spec/index.cjs +9 -0
  50. package/dist/spec/index.d.cts +2 -2
  51. package/dist/spec/index.d.ts +2 -2
  52. package/dist/spec/index.js +3 -1
  53. package/dist/standard/index.cjs +810 -0
  54. package/dist/standard/index.d.cts +3 -0
  55. package/dist/standard/index.d.ts +3 -0
  56. package/dist/standard/index.js +777 -0
  57. package/dist/standard-DhKsxu9U.js +471 -0
  58. package/dist/standard-etPFZDJI.cjs +513 -0
  59. package/dist/type-interpreter/index.cjs +3 -3
  60. package/dist/type-interpreter/index.d.cts +2 -99
  61. package/dist/type-interpreter/index.d.ts +2 -99
  62. package/dist/type-interpreter/index.js +2 -2
  63. package/dist/{type-interpreter-Ck0VDhzn.js → type-interpreter-DnrId8aM.js} +5 -284
  64. package/dist/{type-interpreter-DAIBUqKf.cjs → type-interpreter-OLt2W2yd.cjs} +15 -294
  65. package/package.json +38 -3
  66. package/dist/definition-BXj3T94L.js +0 -467
  67. package/dist/definition-DSZZTz4e.cjs +0 -473
  68. package/dist/index-CEzHfht2.d.ts +0 -269
  69. 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
- Once the package is published:
30
+ Install the published package from npm:
13
31
 
14
32
  ```sh
15
33
  pnpm add schemair
16
34
  ```
17
35
 
18
- Until then, run `pnpm install` in this directory to work with the repository
19
- copy. The package is configured to publish ESM `.js` files for `import` and
20
- CJS `.cjs` files for `require`, with matching TypeScript declarations.
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 { record, requiredField, string } from 'schemair/authoring';
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 { string } from 'schemair/authoring';
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 { arrayOf, string } from 'schemair/authoring';
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` | Data-construction helpers |
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