@raoh/core 0.9.0-dev.15.20261004151401.ge5893e0cc07d
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 +184 -0
- package/README.md +254 -0
- package/dist/catalog.d.ts +5 -0
- package/dist/catalog.js +7 -0
- package/dist/copy.d.ts +11 -0
- package/dist/copy.js +30 -0
- package/dist/decimal.d.ts +36 -0
- package/dist/decimal.js +148 -0
- package/dist/decoder.d.ts +83 -0
- package/dist/decoder.js +178 -0
- package/dist/encode.d.ts +28 -0
- package/dist/encode.js +45 -0
- package/dist/float.d.ts +58 -0
- package/dist/float.js +257 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +14 -0
- package/dist/input.d.ts +78 -0
- package/dist/input.js +465 -0
- package/dist/issue.d.ts +85 -0
- package/dist/issue.js +130 -0
- package/dist/messages.d.ts +59 -0
- package/dist/messages.js +225 -0
- package/dist/meta.d.ts +23 -0
- package/dist/meta.js +128 -0
- package/dist/path.d.ts +48 -0
- package/dist/path.js +118 -0
- package/dist/scalars.d.ts +140 -0
- package/dist/scalars.js +427 -0
- package/dist/set.d.ts +22 -0
- package/dist/set.js +61 -0
- package/dist/structure.d.ts +153 -0
- package/dist/structure.js +397 -0
- package/dist/text.d.ts +32 -0
- package/dist/text.js +204 -0
- package/dist/wire.d.ts +36 -0
- package/dist/wire.js +92 -0
- package/package.json +54 -0
package/dist/text.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// Recognizers for the text formats the string operations read: e-mail addresses, IP addresses,
|
|
2
|
+
// ULIDs, CUIDs, UUIDs and URIs. Each is written from the RFC it names, in ASCII.
|
|
3
|
+
const ATEXT = "A-Za-z0-9!#$%&'*+\\-/=?^_`{|}~";
|
|
4
|
+
const LOCAL_PART = new RegExp(`^[${ATEXT}]+(?:\\.[${ATEXT}]+)*$`);
|
|
5
|
+
const LABEL = /^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/;
|
|
6
|
+
/**
|
|
7
|
+
* Whether `text` is in the ASCII lexical profile of RFC 5321's Mailbox the `email` operation
|
|
8
|
+
* reads: atoms of atext joined by single dots, of at most 64 octets, `@`, and labels joined by
|
|
9
|
+
* single dots, each at most 63 octets, the domain at most 255 and the whole at most 254.
|
|
10
|
+
*/
|
|
11
|
+
export function isEmail(text) {
|
|
12
|
+
const at = text.lastIndexOf("@");
|
|
13
|
+
if (at < 0 || text.length > 254) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
const local = text.slice(0, at);
|
|
17
|
+
const domain = text.slice(at + 1);
|
|
18
|
+
return (local.length <= 64 &&
|
|
19
|
+
LOCAL_PART.test(local) &&
|
|
20
|
+
domain.length <= 255 &&
|
|
21
|
+
domain.split(".").every((label) => label.length <= 63 && LABEL.test(label)));
|
|
22
|
+
}
|
|
23
|
+
const DEC_OCTET = "(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])";
|
|
24
|
+
const IPV4 = new RegExp(`^${DEC_OCTET}(?:\\.${DEC_OCTET}){3}$`);
|
|
25
|
+
/** Whether `text` is RFC 3986's IPv4address: four decimal numbers from 0 to 255, no leading zero. */
|
|
26
|
+
export function isIpv4(text) {
|
|
27
|
+
return IPV4.test(text);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The sixteen bytes of RFC 3986's IPv6address, without a zone: eight groups of one to four
|
|
31
|
+
* hexadecimal digits, at most one `::` standing for one or more zero groups, and optionally an
|
|
32
|
+
* IPv4 address in place of the last two groups. `undefined` where `text` is not one.
|
|
33
|
+
*/
|
|
34
|
+
function ipv6Groups(text) {
|
|
35
|
+
const halves = text.split("::");
|
|
36
|
+
if (halves.length > 2) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
const groupsOf = (half, last) => {
|
|
40
|
+
if (half === "") {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
const parts = half.split(":");
|
|
44
|
+
const out = [];
|
|
45
|
+
for (let i = 0; i < parts.length; i += 1) {
|
|
46
|
+
const part = parts[i];
|
|
47
|
+
if (last && i === parts.length - 1 && part.includes(".")) {
|
|
48
|
+
if (!isIpv4(part)) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
const [a, b, c, d] = part.split(".").map(Number);
|
|
52
|
+
out.push((a << 8) | b, (c << 8) | d);
|
|
53
|
+
}
|
|
54
|
+
else if (/^[0-9A-Fa-f]{1,4}$/.test(part)) {
|
|
55
|
+
out.push(Number.parseInt(part, 16));
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
};
|
|
63
|
+
if (halves.length === 1) {
|
|
64
|
+
const groups = groupsOf(text, true);
|
|
65
|
+
return groups !== undefined && groups.length === 8 ? groups : undefined;
|
|
66
|
+
}
|
|
67
|
+
const head = groupsOf(halves[0], false);
|
|
68
|
+
const tail = groupsOf(halves[1], true);
|
|
69
|
+
if (head === undefined || tail === undefined || head.length + tail.length > 7) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
return [...head, ...new Array(8 - head.length - tail.length).fill(0), ...tail];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Whether `text` is an IPv6 address as the `ipv6` operation reads one: RFC 3986's IPv6address,
|
|
76
|
+
* optionally followed by `%` and a zone ID (non-empty, without `%` or U+0000) where the address is
|
|
77
|
+
* link-local unicast (fe80::/10) or multicast with a scope from 1 to D.
|
|
78
|
+
*/
|
|
79
|
+
export function isIpv6(text) {
|
|
80
|
+
const percent = text.indexOf("%");
|
|
81
|
+
const address = percent < 0 ? text : text.slice(0, percent);
|
|
82
|
+
const groups = ipv6Groups(address);
|
|
83
|
+
if (groups === undefined) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
if (percent < 0) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
const zone = text.slice(percent + 1);
|
|
90
|
+
if (zone === "" || zone.includes("%") || zone.includes("\u0000")) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const first = groups[0];
|
|
94
|
+
const linkLocal = (first & 0xffc0) === 0xfe80;
|
|
95
|
+
const scope = first & 0x000f;
|
|
96
|
+
const multicast = (first & 0xff00) === 0xff00 && scope >= 0x1 && scope <= 0xd;
|
|
97
|
+
return linkLocal || multicast;
|
|
98
|
+
}
|
|
99
|
+
const ULID = /^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{25}$/;
|
|
100
|
+
/** Whether `text` is a ULID in its canonical text form, of at most 128 bits. */
|
|
101
|
+
export function isUlid(text) {
|
|
102
|
+
return ULID.test(text);
|
|
103
|
+
}
|
|
104
|
+
const CUID = /^c[a-z0-9]{24}$/;
|
|
105
|
+
/** Whether `text` is a CUID of version 1. */
|
|
106
|
+
export function isCuid(text) {
|
|
107
|
+
return CUID.test(text);
|
|
108
|
+
}
|
|
109
|
+
const UUID = /^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/;
|
|
110
|
+
/** Whether `text` is a UUID's 32 hexadecimal digits grouped 8-4-4-4-12, in either case. */
|
|
111
|
+
export function isUuid(text) {
|
|
112
|
+
return UUID.test(text);
|
|
113
|
+
}
|
|
114
|
+
const UNRESERVED = "A-Za-z0-9\\-._~";
|
|
115
|
+
const SUB_DELIMS = "!$&'()*+,;=";
|
|
116
|
+
const PCT_ENCODED = "%[0-9A-Fa-f]{2}";
|
|
117
|
+
const PCHAR = `(?:[${UNRESERVED}${SUB_DELIMS}:@]|${PCT_ENCODED})`;
|
|
118
|
+
const SCHEME = /^[A-Za-z][A-Za-z0-9+\-.]*$/;
|
|
119
|
+
const USERINFO = new RegExp(`^(?:[${UNRESERVED}${SUB_DELIMS}:]|${PCT_ENCODED})*$`);
|
|
120
|
+
const REG_NAME = new RegExp(`^(?:[${UNRESERVED}${SUB_DELIMS}]|${PCT_ENCODED})*$`);
|
|
121
|
+
const IPV_FUTURE = new RegExp(`^[vV][0-9A-Fa-f]+\\.[${UNRESERVED}${SUB_DELIMS}:]+$`);
|
|
122
|
+
const PORT = /^[0-9]*$/;
|
|
123
|
+
const PATH_ABEMPTY = new RegExp(`^(?:/${PCHAR}*)*$`);
|
|
124
|
+
const PATH_ABSOLUTE = new RegExp(`^/(?:${PCHAR}+(?:/${PCHAR}*)*)?$`);
|
|
125
|
+
const PATH_ROOTLESS = new RegExp(`^${PCHAR}+(?:/${PCHAR}*)*$`);
|
|
126
|
+
const QUERY = new RegExp(`^(?:${PCHAR}|[/?])*$`);
|
|
127
|
+
/**
|
|
128
|
+
* The scheme and host of `text` where it is a URI as the URI production of RFC 3986 section 3
|
|
129
|
+
* derives it, and `undefined` where it is not: a relative reference is not one, and neither is an
|
|
130
|
+
* IPv6 host with a zone identifier.
|
|
131
|
+
*/
|
|
132
|
+
export function uriParts(text) {
|
|
133
|
+
const colon = text.indexOf(":");
|
|
134
|
+
if (colon < 0) {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
const scheme = text.slice(0, colon);
|
|
138
|
+
if (!SCHEME.test(scheme)) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
let rest = text.slice(colon + 1);
|
|
142
|
+
const hash = rest.indexOf("#");
|
|
143
|
+
if (hash >= 0) {
|
|
144
|
+
if (!QUERY.test(rest.slice(hash + 1))) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
rest = rest.slice(0, hash);
|
|
148
|
+
}
|
|
149
|
+
const question = rest.indexOf("?");
|
|
150
|
+
if (question >= 0) {
|
|
151
|
+
if (!QUERY.test(rest.slice(question + 1))) {
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
rest = rest.slice(0, question);
|
|
155
|
+
}
|
|
156
|
+
if (!rest.startsWith("//")) {
|
|
157
|
+
const path = rest === "" || PATH_ABSOLUTE.test(rest) || PATH_ROOTLESS.test(rest);
|
|
158
|
+
return path ? { scheme, host: undefined } : undefined;
|
|
159
|
+
}
|
|
160
|
+
const afterSlashes = rest.slice(2);
|
|
161
|
+
const slash = afterSlashes.indexOf("/");
|
|
162
|
+
const authority = slash < 0 ? afterSlashes : afterSlashes.slice(0, slash);
|
|
163
|
+
const path = slash < 0 ? "" : afterSlashes.slice(slash);
|
|
164
|
+
if (!PATH_ABEMPTY.test(path)) {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
const host = hostOf(authority);
|
|
168
|
+
return host === undefined ? undefined : { scheme, host };
|
|
169
|
+
}
|
|
170
|
+
/** The host of an authority, `[ userinfo "@" ] host [ ":" port ]`, or `undefined` where it is not one. */
|
|
171
|
+
function hostOf(authority) {
|
|
172
|
+
const at = authority.indexOf("@");
|
|
173
|
+
if (at >= 0 && !USERINFO.test(authority.slice(0, at))) {
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
const hostAndPort = at >= 0 ? authority.slice(at + 1) : authority;
|
|
177
|
+
let host;
|
|
178
|
+
let port;
|
|
179
|
+
if (hostAndPort.startsWith("[")) {
|
|
180
|
+
const close = hostAndPort.indexOf("]");
|
|
181
|
+
if (close < 0) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
host = hostAndPort.slice(0, close + 1);
|
|
185
|
+
const after = hostAndPort.slice(close + 1);
|
|
186
|
+
if (after !== "" && !after.startsWith(":")) {
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
port = after.slice(1);
|
|
190
|
+
const literal = host.slice(1, -1);
|
|
191
|
+
if (ipv6Groups(literal) === undefined && !IPV_FUTURE.test(literal)) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
const colon = hostAndPort.indexOf(":");
|
|
197
|
+
host = colon < 0 ? hostAndPort : hostAndPort.slice(0, colon);
|
|
198
|
+
port = colon < 0 ? "" : hostAndPort.slice(colon + 1);
|
|
199
|
+
if (!REG_NAME.test(host)) {
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return PORT.test(port) ? host : undefined;
|
|
204
|
+
}
|
package/dist/wire.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { JsonNumber } from "./input.ts";
|
|
2
|
+
import type { Issue } from "./issue.ts";
|
|
3
|
+
import { type MessageResolver } from "./messages.ts";
|
|
4
|
+
/** A JSON value as this library writes one: every number a JsonNumber of its exact text. */
|
|
5
|
+
export type Wire = null | boolean | string | JsonNumber | readonly Wire[] | {
|
|
6
|
+
readonly [member: string]: Wire;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* An issue held in another's metadata, the candidates of a `one_of_failed`, as the
|
|
10
|
+
* specification observes the `issues` type: a path, a code, a message and metadata, and no
|
|
11
|
+
* message key.
|
|
12
|
+
*/
|
|
13
|
+
export interface NestedIssueWire {
|
|
14
|
+
readonly path: string;
|
|
15
|
+
readonly code: string;
|
|
16
|
+
readonly message: string;
|
|
17
|
+
readonly meta: {
|
|
18
|
+
readonly [name: string]: Wire;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** An issue as JSON: a nested issue's parts, and its message key. */
|
|
22
|
+
export interface IssueWire extends NestedIssueWire {
|
|
23
|
+
readonly messageKey: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* `issue` as JSON: its path as a JSON Pointer, its code, its message key, its sentence as
|
|
27
|
+
* `resolver` writes it (or as it was given), and its metadata, each value as its observation.
|
|
28
|
+
*/
|
|
29
|
+
export declare function issueWire(issue: Issue, resolver?: MessageResolver): IssueWire;
|
|
30
|
+
/** `issue` as JSON where it is held in another issue's metadata: everything but its message key. */
|
|
31
|
+
export declare function nestedIssueWire(issue: Issue, resolver?: MessageResolver): NestedIssueWire;
|
|
32
|
+
/**
|
|
33
|
+
* A metadata value as its observation. A JavaScript number is an int32 where it is an integer
|
|
34
|
+
* and a float64 otherwise; a value of another class is written as what its `toJSON` gives.
|
|
35
|
+
*/
|
|
36
|
+
export declare function wire(value: unknown, resolver?: MessageResolver): Wire;
|
package/dist/wire.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// How issues and the values of the value model are written as JSON.
|
|
2
|
+
//
|
|
3
|
+
// There is one way, and it is the Raoh Specification's observation of a value
|
|
4
|
+
// (spec/observation.md): an integer is a JSON number of all its digits, a float its canonical
|
|
5
|
+
// decimal or, where JSON cannot carry it, a tag ({"float": "-0"}, {"float": "NaN"},
|
|
6
|
+
// {"float": "+Infinity"}, {"float": "-Infinity"}), a decimal the text of its coefficient and
|
|
7
|
+
// scale, a list an array, a record an object, and the issues of a one_of_failed an array of
|
|
8
|
+
// issues. Every number is held as a JsonNumber of the text to write, so none is rounded on the
|
|
9
|
+
// way out.
|
|
10
|
+
import { Decimal } from "./decimal.js";
|
|
11
|
+
import { Float, floatJson } from "./float.js";
|
|
12
|
+
import { JsonNumber, lexemeOf } from "./input.js";
|
|
13
|
+
import { Issues } from "./issue.js";
|
|
14
|
+
import { Messages } from "./messages.js";
|
|
15
|
+
import { ValueSet } from "./set.js";
|
|
16
|
+
import { ofThisCopy } from "./copy.js";
|
|
17
|
+
/**
|
|
18
|
+
* `issue` as JSON: its path as a JSON Pointer, its code, its message key, its sentence as
|
|
19
|
+
* `resolver` writes it (or as it was given), and its metadata, each value as its observation.
|
|
20
|
+
*/
|
|
21
|
+
export function issueWire(issue, resolver = Messages.english) {
|
|
22
|
+
const { path, code, message, meta } = nestedIssueWire(issue, resolver);
|
|
23
|
+
return { path, code, messageKey: issue.messageKey, message, meta };
|
|
24
|
+
}
|
|
25
|
+
/** `issue` as JSON where it is held in another issue's metadata: everything but its message key. */
|
|
26
|
+
export function nestedIssueWire(issue, resolver = Messages.english) {
|
|
27
|
+
return {
|
|
28
|
+
path: issue.path.toString(),
|
|
29
|
+
code: issue.code,
|
|
30
|
+
message: issue.message(resolver),
|
|
31
|
+
meta: record(issue.meta, resolver),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A metadata value as its observation. A JavaScript number is an int32 where it is an integer
|
|
36
|
+
* and a float64 otherwise; a value of another class is written as what its `toJSON` gives.
|
|
37
|
+
*/
|
|
38
|
+
export function wire(value, resolver = Messages.english) {
|
|
39
|
+
switch (typeof value) {
|
|
40
|
+
case "boolean":
|
|
41
|
+
case "string":
|
|
42
|
+
return value;
|
|
43
|
+
case "bigint":
|
|
44
|
+
return new JsonNumber(value.toString());
|
|
45
|
+
case "number":
|
|
46
|
+
return Number.isInteger(value) && !Object.is(value, -0) ? new JsonNumber(String(value)) : floatJson(value, 64);
|
|
47
|
+
case "undefined":
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
if (value === null || ofThisCopy(value, JsonNumber, "JsonNumber")) {
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
// A value of the value model is written as its observation writes it, before it is asked whether
|
|
54
|
+
// the input model reads it as a number: a Decimal is one there too, and its observation is a string.
|
|
55
|
+
if (ofThisCopy(value, Float, "Float")) {
|
|
56
|
+
return floatJson(value.value, value.width);
|
|
57
|
+
}
|
|
58
|
+
if (ofThisCopy(value, Decimal, "Decimal")) {
|
|
59
|
+
return value.toString();
|
|
60
|
+
}
|
|
61
|
+
// A number the input model reads from an object, which JSON.rawJSON makes, is written as the
|
|
62
|
+
// number it is, by the one rule that reads its text. Metadata is not input, so what is asked is
|
|
63
|
+
// only whether the value is such a number, and not whether it is a value of the input model.
|
|
64
|
+
const lexeme = lexemeOf(value);
|
|
65
|
+
if (lexeme !== undefined) {
|
|
66
|
+
return new JsonNumber(lexeme);
|
|
67
|
+
}
|
|
68
|
+
if (ofThisCopy(value, Issues, "Issues")) {
|
|
69
|
+
return value.list.map((issue) => nestedIssueWire(issue, resolver));
|
|
70
|
+
}
|
|
71
|
+
if (Array.isArray(value) || ofThisCopy(value, ValueSet, "ValueSet")) {
|
|
72
|
+
return [...value].map((item) => wire(item, resolver));
|
|
73
|
+
}
|
|
74
|
+
if (value instanceof Map) {
|
|
75
|
+
return record(Object.fromEntries(value), resolver);
|
|
76
|
+
}
|
|
77
|
+
if (typeof value === "object") {
|
|
78
|
+
const toJSON = value.toJSON;
|
|
79
|
+
if (typeof toJSON === "function") {
|
|
80
|
+
return wire(toJSON.call(value), resolver);
|
|
81
|
+
}
|
|
82
|
+
return record(value, resolver);
|
|
83
|
+
}
|
|
84
|
+
throw new TypeError(`a ${typeof value} has no JSON`);
|
|
85
|
+
}
|
|
86
|
+
function record(fields, resolver) {
|
|
87
|
+
const out = {};
|
|
88
|
+
for (const [name, value] of Object.entries(fields)) {
|
|
89
|
+
Object.defineProperty(out, name, { value: wire(value, resolver), enumerable: true, writable: true, configurable: true });
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@raoh/core",
|
|
3
|
+
"version": "0.9.0-dev.15.20261004151401.ge5893e0cc07d",
|
|
4
|
+
"description": "Decoders that turn untyped boundary input into typed domain values, reporting every issue with its path",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/raoh-project/raoh-ts.git"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"decoder",
|
|
12
|
+
"validation",
|
|
13
|
+
"json",
|
|
14
|
+
"parse",
|
|
15
|
+
"domain"
|
|
16
|
+
],
|
|
17
|
+
"type": "module",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=22.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devEngines": {
|
|
36
|
+
"runtime": {
|
|
37
|
+
"name": "node",
|
|
38
|
+
"version": ">=22.18.0",
|
|
39
|
+
"onFail": "error"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc -p tsconfig.build.json",
|
|
44
|
+
"prepare": "npm run build",
|
|
45
|
+
"typecheck": "tsc --noEmit",
|
|
46
|
+
"test": "node --test test/*.test.ts",
|
|
47
|
+
"compile-tests": "tsc -p tsconfig.runtime.json",
|
|
48
|
+
"catalog": "node scripts/catalog.ts"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^22.20.5",
|
|
52
|
+
"typescript": "^5.9.0"
|
|
53
|
+
}
|
|
54
|
+
}
|