@mikrojs/schema 0.0.0 → 0.19.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/LICENSE +21 -0
- package/README.md +67 -0
- package/dist/config.d.ts +79 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +980 -0
- package/dist/config.js.map +1 -0
- package/dist/core.d.ts +163 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +353 -0
- package/dist/core.js.map +1 -0
- package/package.json +38 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Bjørge Næss
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# @mikrojs/schema
|
|
2
|
+
|
|
3
|
+
The schema DSL behind [Mikro.js](https://mikrojs.dev): declare a shape once, infer its
|
|
4
|
+
TypeScript type, validate values against it, and serialize it as plain JSON for something
|
|
5
|
+
else to read. Zero dependencies, no Node APIs, so it runs on Node, Bun, Deno, Cloudflare
|
|
6
|
+
Workers and in a browser.
|
|
7
|
+
|
|
8
|
+
Apps on a device import `mikro/schema` instead, which is this same DSL backed by a native
|
|
9
|
+
module plus a `Result`-returning `parse()`. This package is the host half: the CLI evaluating
|
|
10
|
+
`mikro.config.ts`, a registry validating what an operator saves, and anything else that needs
|
|
11
|
+
to read a published schema.
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
import {number, object, string, validate} from '@mikrojs/schema'
|
|
15
|
+
|
|
16
|
+
const Config = object({
|
|
17
|
+
mqttUrl: string({title: 'Broker URL', format: 'url'}),
|
|
18
|
+
interval: number({title: 'Publish interval', unit: 's', default: 60, min: 1, max: 3600}),
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
validate(Config, {mqttUrl: 'mqtt://box.local', interval: 60}, '') // => null
|
|
22
|
+
validate(Config, {mqttUrl: 'mqtt://box.local', interval: 0}, '')
|
|
23
|
+
// => {ok: false, error: {name: 'ValidationFailed', message: 'below the minimum of 1', path: '.interval'}}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`validate()` returns `null` when the value is good, and covers shape plus every numeric and
|
|
27
|
+
length bound. `format` is not checked here; see `@mikrojs/schema/config`.
|
|
28
|
+
|
|
29
|
+
## Annotations
|
|
30
|
+
|
|
31
|
+
Nodes carry annotations, and they divide in two. **Display** annotations (`title`,
|
|
32
|
+
`description`, `mask`) describe how a value should be presented and never change what
|
|
33
|
+
validates, so a consumer may ignore any it does not recognise. **Constraints** (`min`, `max`,
|
|
34
|
+
`integer`, `minLength`, `maxLength`, `minItems`, `maxItems`, `format`, `unit`) do change what
|
|
35
|
+
validates, and a consumer must not ignore one it does not recognise: ignoring a constraint
|
|
36
|
+
means accepting a value the author ruled out.
|
|
37
|
+
|
|
38
|
+
`unit` is a closed enum following [SenML](https://www.rfc-editor.org/rfc/rfc8428) with ASCII
|
|
39
|
+
keys. It is a display hint and constrains nothing. Never convert a stored value through a
|
|
40
|
+
unit's scale — see the note beside `UNITS`.
|
|
41
|
+
|
|
42
|
+
## `@mikrojs/schema/config`
|
|
43
|
+
|
|
44
|
+
The host-side machinery for Mikro.js OTA config, which is what a registry needs:
|
|
45
|
+
|
|
46
|
+
- `validateConfig(schema, value)` — `validate()` plus `format`, returning `{ok, value}` or
|
|
47
|
+
`{ok: false, error}`
|
|
48
|
+
- `parseConfigSchema(value)` — validate a schema that arrived as JSON, rejecting malformed
|
|
49
|
+
annotations and unknown `format` or `unit` values
|
|
50
|
+
- `deriveOverlay` / `parseEffective` / `materializeDefaults` — the sparse-overlay model, where
|
|
51
|
+
a registry stores only deviations from the author's defaults and serves the complete document
|
|
52
|
+
- `diffConfigSchemas(previous, next)` — what changed between two releases, and whether it needs
|
|
53
|
+
an operator's attention
|
|
54
|
+
- `UNITS`, `FORMATS` — the unit table and the format names
|
|
55
|
+
|
|
56
|
+
`format` lives here rather than in the main entry for two reasons: the expressions are a
|
|
57
|
+
denial-of-service surface a device should not carry, and a device has no regular-expression
|
|
58
|
+
engine to carry them with.
|
|
59
|
+
|
|
60
|
+
The wire format is the contract. It is specified in the
|
|
61
|
+
[OTA Registry Spec](https://mikrojs.dev/registry-spec) under "The config schema", and that
|
|
62
|
+
document, not this package's version number, is what a third-party registry should build
|
|
63
|
+
against.
|
|
64
|
+
|
|
65
|
+
## Documentation
|
|
66
|
+
|
|
67
|
+
[mikrojs.dev/api/schema](https://mikrojs.dev/api/schema)
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { type Format, type Schema, SchemaError, type Unit } from './core.js';
|
|
2
|
+
export type SchemaCheck<T> = {
|
|
3
|
+
ok: true;
|
|
4
|
+
value: T;
|
|
5
|
+
} | {
|
|
6
|
+
ok: false;
|
|
7
|
+
error: SchemaError;
|
|
8
|
+
};
|
|
9
|
+
export declare const FORMATS: readonly Format[];
|
|
10
|
+
/**
|
|
11
|
+
* Validates a value against a config schema, `format` included. `validate()` in
|
|
12
|
+
* core.ts covers shape and every numeric and length bound; this adds the one
|
|
13
|
+
* annotation that stays host-side, so every path that validates an operator's
|
|
14
|
+
* value goes through here rather than calling validate() directly.
|
|
15
|
+
*/
|
|
16
|
+
export declare function validateConfig(schema: Schema, value: unknown): SchemaCheck<unknown>;
|
|
17
|
+
/** How a unit relates to the primary it measures in, plus the symbol to render
|
|
18
|
+
* when the ASCII key is not what a person should read.
|
|
19
|
+
*
|
|
20
|
+
* `scale` and `offset` exist so a form can show a read-only hint beside a
|
|
21
|
+
* field ("30000000 us (30 s)"). They MUST NOT be used to convert a stored
|
|
22
|
+
* value. The registry stores only deviations from the schema defaults and
|
|
23
|
+
* hashes the effective document for its rev, so a lossy round trip stops a
|
|
24
|
+
* default-equal value being stripped and two operators entering the same
|
|
25
|
+
* thing produce different revs. Having the numbers here makes converting look
|
|
26
|
+
* easy; it is still wrong. */
|
|
27
|
+
export interface UnitDefinition {
|
|
28
|
+
readonly primary: Unit;
|
|
29
|
+
readonly scale: number;
|
|
30
|
+
readonly offset: number;
|
|
31
|
+
readonly symbol?: string;
|
|
32
|
+
}
|
|
33
|
+
export declare const UNITS: Record<Unit, UnitDefinition>;
|
|
34
|
+
/**
|
|
35
|
+
* Validates an untrusted serialized schema AST as a config schema: well-formed
|
|
36
|
+
* nodes only, an object at the root, no `unknown()`, no `optional()` around an
|
|
37
|
+
* object or array (an overlay needs every absence to mean exactly one thing),
|
|
38
|
+
* defaults that match their own node, and nesting of at most 8 levels.
|
|
39
|
+
* The size cap is the caller's, since only the caller sees encoded bytes.
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseConfigSchema(value: unknown): SchemaCheck<Schema>;
|
|
42
|
+
/**
|
|
43
|
+
* Derives the overlay to store or serve from operator-supplied values: drops
|
|
44
|
+
* keys the schema does not know, strips values structurally equal to the
|
|
45
|
+
* schema default, and prunes empty objects and arrays. Returns undefined when
|
|
46
|
+
* nothing deviates from the defaults. Wholesale nodes (arrays, tuples, unions)
|
|
47
|
+
* are compared and kept as units; nothing inside them is stripped.
|
|
48
|
+
*/
|
|
49
|
+
export declare function deriveOverlay(schema: Schema, values: unknown): unknown;
|
|
50
|
+
export declare function structuralEquals(a: unknown, b: unknown): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* The effective config for an overlay: defaults filled in, then validated.
|
|
53
|
+
* What a registry runs before serving and what `ota.config()` runs on read.
|
|
54
|
+
*/
|
|
55
|
+
export declare function parseEffective(schema: Schema, overlay: unknown): SchemaCheck<unknown>;
|
|
56
|
+
/**
|
|
57
|
+
* The partial defaults a schema materializes with no overrides: every field a
|
|
58
|
+
* default covers, and nothing else. Unlike parseEffective it never fails on a
|
|
59
|
+
* required defaultless field, it omits it. This is what pack bakes into the
|
|
60
|
+
* manifest and what a device reads when it holds no served document.
|
|
61
|
+
*
|
|
62
|
+
* Plain objects compose, so a nested one is included only when defaults fill
|
|
63
|
+
* it completely: a half-filled object would not validate, and the read type
|
|
64
|
+
* makes that field optional anyway. Wholesale units (array, tuple, union,
|
|
65
|
+
* taggedUnion) need a whole-value default on the node itself, matching
|
|
66
|
+
* applyDefaults, where a default inside an element or branch is a form hint.
|
|
67
|
+
* Optional fields rest on absence, so they are omitted too.
|
|
68
|
+
*/
|
|
69
|
+
export declare function materializeDefaults(schema: Schema): Record<string, unknown>;
|
|
70
|
+
/**
|
|
71
|
+
* Human-readable warnings for what changed between two releases' config
|
|
72
|
+
* schemas, per the spec's change taxonomy (registry-spec.md, "Schema changes
|
|
73
|
+
* between releases"). Safe changes (new defaulted or optional fields, added
|
|
74
|
+
* union members, loosened requirements) produce nothing. "requires an
|
|
75
|
+
* operator" lines gate offers under rule 5 until someone supplies or fixes a
|
|
76
|
+
* value; "note" lines are compatible but worth telling the operator about.
|
|
77
|
+
*/
|
|
78
|
+
export declare function diffConfigSchemas(previous: Schema, next: Schema): string[];
|
|
79
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,KAAK,MAAM,EAEX,KAAK,MAAM,EACX,WAAW,EACX,KAAK,IAAI,EAEV,MAAM,WAAW,CAAA;AAElB,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAC,GAAG;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAC,CAAA;AAgBnF,eAAO,MAAM,OAAO,EAAmC,SAAS,MAAM,EAAE,CAAA;AAExE;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAMnF;AA8ED;;;;;;;;;+BAS+B;AAC/B,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IAKvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAKD,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE,cAAc,CAoH9C,CAAA;AAkCD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,CAOrE;AAoQD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAuCtE;AAkBD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAmBhE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAMrF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE3E;AA8LD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAoI1E"}
|