@botejs/core 0.3.0 → 0.4.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/dist/args.js +9 -15
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -21
- package/dist/open.js +71 -52
- package/dist/path.js +4 -8
- package/dist/sources.js +5 -10
- package/dist/validate.d.ts +5 -2
- package/dist/validate.js +20 -14
- package/package.json +11 -4
package/dist/args.js
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.splitArgs = splitArgs;
|
|
4
|
-
exports.isSchema = isSchema;
|
|
5
|
-
exports.normalizeIterTail = normalizeIterTail;
|
|
6
|
-
exports.serializeSelect = serializeSelect;
|
|
7
|
-
const path_ts_1 = require("./path.js");
|
|
8
|
-
function splitArgs(args) {
|
|
1
|
+
import { validatePath } from "./path.js";
|
|
2
|
+
export function splitArgs(args) {
|
|
9
3
|
let pathArgs;
|
|
10
4
|
let tail;
|
|
11
5
|
if (args.length === 0) {
|
|
@@ -23,27 +17,27 @@ function splitArgs(args) {
|
|
|
23
17
|
tail = undefined;
|
|
24
18
|
}
|
|
25
19
|
}
|
|
26
|
-
|
|
20
|
+
validatePath(pathArgs);
|
|
27
21
|
return { path: pathArgs, tail };
|
|
28
22
|
}
|
|
29
|
-
function isSchema(value) {
|
|
23
|
+
export function isSchema(value) {
|
|
30
24
|
return typeof value === 'object' && value !== null && '~standard' in value;
|
|
31
25
|
}
|
|
32
|
-
function normalizeIterTail(tail) {
|
|
26
|
+
export function normalizeIterTail(tail) {
|
|
33
27
|
if (!tail)
|
|
34
28
|
return {};
|
|
35
29
|
if (isSchema(tail))
|
|
36
30
|
return { schema: tail };
|
|
37
31
|
return tail;
|
|
38
32
|
}
|
|
39
|
-
function serializeSelect(select) {
|
|
33
|
+
export function serializeSelect(select) {
|
|
40
34
|
if (typeof select === 'string' || typeof select === 'number') {
|
|
41
35
|
const one = [select];
|
|
42
|
-
|
|
36
|
+
validatePath(one);
|
|
43
37
|
return JSON.stringify({ one });
|
|
44
38
|
}
|
|
45
39
|
if (Array.isArray(select)) {
|
|
46
|
-
|
|
40
|
+
validatePath(select);
|
|
47
41
|
if (select.length === 0) {
|
|
48
42
|
throw new RangeError('iter: select sub-path must have at least one segment');
|
|
49
43
|
}
|
|
@@ -57,7 +51,7 @@ function serializeSelect(select) {
|
|
|
57
51
|
if (!Array.isArray(path)) {
|
|
58
52
|
throw new TypeError(`iter: select field ${JSON.stringify(k)} must be a segment or path, got ${describeSelect(sub)}`);
|
|
59
53
|
}
|
|
60
|
-
|
|
54
|
+
validatePath(path);
|
|
61
55
|
if (path.length === 0) {
|
|
62
56
|
throw new RangeError(`iter: select field ${JSON.stringify(k)} sub-path must have at least one segment`);
|
|
63
57
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { type IterOptions } from './args.ts';
|
|
2
|
-
export { ValidationError, PathError, formatPath, type Path, type Segment, type StandardSchemaV1, } from './validate.ts';
|
|
2
|
+
export { ValidationError, PathError, formatPath, type Path, type PathFaultCode, type Segment, type StandardSchemaV1, } from './validate.ts';
|
|
3
3
|
export { open, DEFAULT_ITER_BATCH, MAX_ITER_BATCH, type Cursor, type RootCursor, type OpenOptions, type WalkEntry, type IterIndex as IterKey, } from './open.ts';
|
|
4
4
|
export { fromBuffer, fromFile, fromHttpRange, type FactoryOptions, type Source, type SourceReader, type HttpRangeOptions, } from './sources.ts';
|
package/dist/index.js
CHANGED
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
// Node 18 and Node 20.3 predate `Symbol.asyncDispose`; mirror what TS emits for
|
|
5
|
-
// `await using` so the well-known symbol is available across our engine range.
|
|
6
|
-
if (!Symbol.asyncDispose) {
|
|
7
|
-
;
|
|
8
|
-
Symbol.asyncDispose = Symbol.for('Symbol.asyncDispose');
|
|
9
|
-
}
|
|
10
|
-
var validate_ts_1 = require("./validate.js");
|
|
11
|
-
Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return validate_ts_1.ValidationError; } });
|
|
12
|
-
Object.defineProperty(exports, "PathError", { enumerable: true, get: function () { return validate_ts_1.PathError; } });
|
|
13
|
-
Object.defineProperty(exports, "formatPath", { enumerable: true, get: function () { return validate_ts_1.formatPath; } });
|
|
14
|
-
var open_ts_1 = require("./open.js");
|
|
15
|
-
Object.defineProperty(exports, "open", { enumerable: true, get: function () { return open_ts_1.open; } });
|
|
16
|
-
Object.defineProperty(exports, "DEFAULT_ITER_BATCH", { enumerable: true, get: function () { return open_ts_1.DEFAULT_ITER_BATCH; } });
|
|
17
|
-
Object.defineProperty(exports, "MAX_ITER_BATCH", { enumerable: true, get: function () { return open_ts_1.MAX_ITER_BATCH; } });
|
|
18
|
-
var sources_ts_1 = require("./sources.js");
|
|
19
|
-
Object.defineProperty(exports, "fromBuffer", { enumerable: true, get: function () { return sources_ts_1.fromBuffer; } });
|
|
20
|
-
Object.defineProperty(exports, "fromFile", { enumerable: true, get: function () { return sources_ts_1.fromFile; } });
|
|
21
|
-
Object.defineProperty(exports, "fromHttpRange", { enumerable: true, get: function () { return sources_ts_1.fromHttpRange; } });
|
|
1
|
+
export { ValidationError, PathError, formatPath, } from "./validate.js";
|
|
2
|
+
export { open, DEFAULT_ITER_BATCH, MAX_ITER_BATCH, } from "./open.js";
|
|
3
|
+
export { fromBuffer, fromFile, fromHttpRange, } from "./sources.js";
|
package/dist/open.js
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const args_ts_1 = require("./args.js");
|
|
9
|
-
exports.DEFAULT_SOURCE_CHUNK_BYTES = 64 * 1024;
|
|
10
|
-
exports.DEFAULT_ITER_BATCH = 1000;
|
|
11
|
-
exports.MAX_ITER_BATCH = 1_000_000;
|
|
1
|
+
import { open as openNative } from '@botejs/native';
|
|
2
|
+
import { validatePath } from "./path.js";
|
|
3
|
+
import { runStandardSchema, validateItem, formatPath, PathError, } from "./validate.js";
|
|
4
|
+
import { splitArgs, isSchema, serializeSelect, normalizeIterTail, } from "./args.js";
|
|
5
|
+
export const DEFAULT_SOURCE_CHUNK_BYTES = 64 * 1024;
|
|
6
|
+
export const DEFAULT_ITER_BATCH = 1000;
|
|
7
|
+
export const MAX_ITER_BATCH = 1_000_000;
|
|
12
8
|
/**
|
|
13
9
|
* Open a cursor over a seekable source.
|
|
14
10
|
*
|
|
15
11
|
* The returned `RootCursor` owns the reader: `close()` (or `await using`)
|
|
16
12
|
* drives the reader's own `close()` exactly once.
|
|
17
13
|
*/
|
|
18
|
-
async function open(source, options) {
|
|
14
|
+
export async function open(source, options) {
|
|
19
15
|
const { indexCacheEntries, objectMemberCap, arrayIndexInterval } = options ?? {};
|
|
20
16
|
for (const [name, value] of [
|
|
21
17
|
['indexCacheEntries', indexCacheEntries],
|
|
@@ -27,7 +23,7 @@ async function open(source, options) {
|
|
|
27
23
|
}
|
|
28
24
|
}
|
|
29
25
|
const reader = await source.open();
|
|
30
|
-
const chunkBytes = reader.chunkBytes ??
|
|
26
|
+
const chunkBytes = reader.chunkBytes ?? DEFAULT_SOURCE_CHUNK_BYTES;
|
|
31
27
|
let native;
|
|
32
28
|
try {
|
|
33
29
|
if (!Number.isInteger(reader.size) || reader.size < 0) {
|
|
@@ -39,7 +35,7 @@ async function open(source, options) {
|
|
|
39
35
|
if (chunkBytes % 64 !== 0) {
|
|
40
36
|
throw new RangeError(`open: chunkBytes must be a multiple of 64, got ${chunkBytes}`);
|
|
41
37
|
}
|
|
42
|
-
native = (
|
|
38
|
+
native = openNative({
|
|
43
39
|
size: reader.size,
|
|
44
40
|
chunkBytes,
|
|
45
41
|
indexCacheEntries,
|
|
@@ -75,14 +71,14 @@ async function closeReader(reader) {
|
|
|
75
71
|
if (reader.close)
|
|
76
72
|
await reader.close();
|
|
77
73
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
74
|
+
const NATIVE_PATH_ERROR = /^bote:path:([a-z_]+)(?::(\d+))?$/;
|
|
75
|
+
function deserializeError(err, path) {
|
|
76
|
+
if (err instanceof Error && !(err instanceof PathError)) {
|
|
77
|
+
const match = NATIVE_PATH_ERROR.exec(err.message);
|
|
78
|
+
if (match) {
|
|
79
|
+
const segment = match[2] === undefined ? undefined : Number(match[2]);
|
|
80
|
+
return new PathError(path, match[1], segment);
|
|
81
|
+
}
|
|
86
82
|
}
|
|
87
83
|
return err;
|
|
88
84
|
}
|
|
@@ -97,62 +93,65 @@ function wrap(native, state) {
|
|
|
97
93
|
const cursor = {
|
|
98
94
|
async hop(...path) {
|
|
99
95
|
ensureOpen(state);
|
|
100
|
-
|
|
96
|
+
validatePath(path);
|
|
101
97
|
let child;
|
|
102
98
|
try {
|
|
103
99
|
child = await native.hop(path);
|
|
104
100
|
}
|
|
105
101
|
catch (err) {
|
|
106
|
-
throw
|
|
102
|
+
throw deserializeError(err, path);
|
|
107
103
|
}
|
|
108
104
|
return child ? wrap(child, state) : null;
|
|
109
105
|
},
|
|
110
106
|
async has(...args) {
|
|
111
107
|
ensureOpen(state);
|
|
112
|
-
const { path, tail: schema } =
|
|
113
|
-
if (schema !== undefined && !
|
|
108
|
+
const { path, tail: schema } = splitArgs(args);
|
|
109
|
+
if (schema !== undefined && !isSchema(schema)) {
|
|
114
110
|
throw new TypeError('has: expected a Standard Schema as the trailing argument');
|
|
115
111
|
}
|
|
116
112
|
if (!schema)
|
|
117
113
|
return native.has(path);
|
|
118
114
|
if (!(await native.has(path)))
|
|
119
115
|
return false;
|
|
120
|
-
const
|
|
116
|
+
const text = await native.get(path);
|
|
117
|
+
const value = text === undefined ? undefined : parseValue(text, path);
|
|
118
|
+
const result = await validateItem(schema, value, path, 'skip');
|
|
121
119
|
return !('skip' in result);
|
|
122
120
|
},
|
|
123
121
|
async get(...args) {
|
|
124
122
|
ensureOpen(state);
|
|
125
|
-
const { path, tail: schema } =
|
|
126
|
-
if (schema !== undefined && !
|
|
123
|
+
const { path, tail: schema } = splitArgs(args);
|
|
124
|
+
if (schema !== undefined && !isSchema(schema)) {
|
|
127
125
|
throw new TypeError('get: expected a Standard Schema as the trailing argument');
|
|
128
126
|
}
|
|
129
127
|
let value;
|
|
130
128
|
try {
|
|
131
|
-
|
|
129
|
+
const text = await native.get(path);
|
|
130
|
+
value = text === undefined ? undefined : parseValue(text, path);
|
|
132
131
|
}
|
|
133
132
|
catch (err) {
|
|
134
|
-
throw
|
|
133
|
+
throw deserializeError(err, path);
|
|
135
134
|
}
|
|
136
135
|
if (!schema)
|
|
137
136
|
return value;
|
|
138
|
-
return
|
|
137
|
+
return runStandardSchema(schema, value, path);
|
|
139
138
|
},
|
|
140
139
|
async count(...path) {
|
|
141
140
|
ensureOpen(state);
|
|
142
|
-
|
|
141
|
+
validatePath(path);
|
|
143
142
|
try {
|
|
144
143
|
return await native.count(path);
|
|
145
144
|
}
|
|
146
145
|
catch (err) {
|
|
147
|
-
throw
|
|
146
|
+
throw deserializeError(err, path);
|
|
148
147
|
}
|
|
149
148
|
},
|
|
150
149
|
iter(...args) {
|
|
151
150
|
ensureOpen(state);
|
|
152
|
-
const { path, tail } =
|
|
153
|
-
const { schema, select, batch, onInvalid, withIndex } =
|
|
154
|
-
if (batch !== undefined && (!Number.isInteger(batch) || batch <= 0 || batch >
|
|
155
|
-
throw new RangeError(`iter: batch must be an integer in 1..=${
|
|
151
|
+
const { path, tail } = splitArgs(args);
|
|
152
|
+
const { schema, select, batch, onInvalid, withIndex } = normalizeIterTail(tail);
|
|
153
|
+
if (batch !== undefined && (!Number.isInteger(batch) || batch <= 0 || batch > MAX_ITER_BATCH)) {
|
|
154
|
+
throw new RangeError(`iter: batch must be an integer in 1..=${MAX_ITER_BATCH}, got ${batch}`);
|
|
156
155
|
}
|
|
157
156
|
if (withIndex !== undefined && typeof withIndex !== 'boolean') {
|
|
158
157
|
throw new TypeError(`iter: withIndex must be a boolean, got ${typeof withIndex}`);
|
|
@@ -160,18 +159,29 @@ function wrap(native, state) {
|
|
|
160
159
|
if (onInvalid !== undefined && onInvalid !== 'throw' && onInvalid !== 'skip') {
|
|
161
160
|
throw new RangeError(`iter: onInvalid must be "throw" or "skip", got ${JSON.stringify(onInvalid)}`);
|
|
162
161
|
}
|
|
163
|
-
const resolvedBatch = batch ??
|
|
164
|
-
const selectIr = select !== undefined ?
|
|
165
|
-
const inner = native.iter(path, { selectIr, batch: resolvedBatch
|
|
162
|
+
const resolvedBatch = batch ?? DEFAULT_ITER_BATCH;
|
|
163
|
+
const selectIr = select !== undefined ? serializeSelect(select) : undefined;
|
|
164
|
+
const inner = native.iter(path, { selectIr, batch: resolvedBatch });
|
|
166
165
|
if (!schema) {
|
|
167
166
|
return {
|
|
168
167
|
async *[Symbol.asyncIterator]() {
|
|
168
|
+
let i = 0;
|
|
169
169
|
try {
|
|
170
|
-
for await (const b of inner)
|
|
171
|
-
|
|
170
|
+
for await (const b of inner) {
|
|
171
|
+
const batch = parseValue(b, path);
|
|
172
|
+
if (!withIndex) {
|
|
173
|
+
yield batch;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const out = new Array(batch.length);
|
|
177
|
+
for (let j = 0; j < batch.length; j++) {
|
|
178
|
+
out[j] = [i++, batch[j]];
|
|
179
|
+
}
|
|
180
|
+
yield out;
|
|
181
|
+
}
|
|
172
182
|
}
|
|
173
183
|
catch (err) {
|
|
174
|
-
throw
|
|
184
|
+
throw deserializeError(err, path);
|
|
175
185
|
}
|
|
176
186
|
},
|
|
177
187
|
};
|
|
@@ -183,25 +193,26 @@ function wrap(native, state) {
|
|
|
183
193
|
try {
|
|
184
194
|
for await (const b of inner) {
|
|
185
195
|
const out = [];
|
|
186
|
-
for (const v of b) {
|
|
187
|
-
const
|
|
188
|
-
const result = await
|
|
189
|
-
if ('skip' in result)
|
|
196
|
+
for (const v of parseValue(b, path)) {
|
|
197
|
+
const index = i++;
|
|
198
|
+
const result = await validateItem(schema, v, [...path, index], policy);
|
|
199
|
+
if ('skip' in result) {
|
|
190
200
|
continue;
|
|
191
|
-
|
|
201
|
+
}
|
|
202
|
+
out.push(withIndex ? [index, result.value] : result.value);
|
|
192
203
|
}
|
|
193
204
|
yield out;
|
|
194
205
|
}
|
|
195
206
|
}
|
|
196
207
|
catch (err) {
|
|
197
|
-
throw
|
|
208
|
+
throw deserializeError(err, path);
|
|
198
209
|
}
|
|
199
210
|
},
|
|
200
211
|
};
|
|
201
212
|
},
|
|
202
213
|
walk(...path) {
|
|
203
214
|
ensureOpen(state);
|
|
204
|
-
|
|
215
|
+
validatePath(path);
|
|
205
216
|
return {
|
|
206
217
|
async *[Symbol.asyncIterator]() {
|
|
207
218
|
try {
|
|
@@ -210,7 +221,7 @@ function wrap(native, state) {
|
|
|
210
221
|
}
|
|
211
222
|
}
|
|
212
223
|
catch (err) {
|
|
213
|
-
throw
|
|
224
|
+
throw deserializeError(err, path);
|
|
214
225
|
}
|
|
215
226
|
},
|
|
216
227
|
};
|
|
@@ -218,3 +229,11 @@ function wrap(native, state) {
|
|
|
218
229
|
};
|
|
219
230
|
return cursor;
|
|
220
231
|
}
|
|
232
|
+
function parseValue(text, path) {
|
|
233
|
+
try {
|
|
234
|
+
return JSON.parse(text);
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
throw new Error(`bote: malformed JSON value at ${formatPath(path)}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
package/dist/path.js
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MAX_ARRAY_INDEX = void 0;
|
|
4
|
-
exports.validatePath = validatePath;
|
|
5
1
|
/** Upper bound on numeric segments (napi takes them as `u32`). 2^32 - 1
|
|
6
2
|
* comfortably covers any in-memory JSON array. */
|
|
7
|
-
|
|
8
|
-
function validatePath(path) {
|
|
3
|
+
export const MAX_ARRAY_INDEX = 0xffffffff;
|
|
4
|
+
export function validatePath(path) {
|
|
9
5
|
for (let i = 0; i < path.length; i++) {
|
|
10
6
|
const s = path[i];
|
|
11
7
|
if (typeof s === 'string')
|
|
12
8
|
continue;
|
|
13
|
-
if (typeof s === 'number' && Number.isInteger(s) && s >= 0 && s <=
|
|
9
|
+
if (typeof s === 'number' && Number.isInteger(s) && s >= 0 && s <= MAX_ARRAY_INDEX)
|
|
14
10
|
continue;
|
|
15
|
-
throw new TypeError(`path segment ${i}: expected string or non-negative integer (<= ${
|
|
11
|
+
throw new TypeError(`path segment ${i}: expected string or non-negative integer (<= ${MAX_ARRAY_INDEX}), got ${describeBadSegment(s)}`);
|
|
16
12
|
}
|
|
17
13
|
}
|
|
18
14
|
function describeBadSegment(s) {
|
package/dist/sources.js
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.fromBuffer = fromBuffer;
|
|
4
|
-
exports.fromFile = fromFile;
|
|
5
|
-
exports.fromHttpRange = fromHttpRange;
|
|
6
|
-
const promises_1 = require("node:fs/promises");
|
|
1
|
+
import { open as fsOpen } from 'node:fs/promises';
|
|
7
2
|
/** Default chunk size, in bytes, for in-memory sources. */
|
|
8
3
|
const DEFAULT_BUFFER_CHUNK_BYTES = 4 * 1024;
|
|
9
4
|
/** Default chunk size, in bytes, for local files: matches typical filesystem readahead. */
|
|
10
5
|
const DEFAULT_FILE_CHUNK_BYTES = 64 * 1024;
|
|
11
6
|
/** Default chunk size, in bytes, for HTTP range reads: amortizes RTT across more data. */
|
|
12
7
|
const DEFAULT_URL_CHUNK_BYTES = 256 * 1024;
|
|
13
|
-
function fromBuffer(buf, options) {
|
|
8
|
+
export function fromBuffer(buf, options) {
|
|
14
9
|
const view = buf instanceof Uint8Array ? buf : new Uint8Array(buf);
|
|
15
10
|
const chunkBytes = options?.chunkBytes ?? DEFAULT_BUFFER_CHUNK_BYTES;
|
|
16
11
|
return {
|
|
@@ -21,11 +16,11 @@ function fromBuffer(buf, options) {
|
|
|
21
16
|
}),
|
|
22
17
|
};
|
|
23
18
|
}
|
|
24
|
-
function fromFile(path, options) {
|
|
19
|
+
export function fromFile(path, options) {
|
|
25
20
|
const chunkBytes = options?.chunkBytes ?? DEFAULT_FILE_CHUNK_BYTES;
|
|
26
21
|
return {
|
|
27
22
|
open: async () => {
|
|
28
|
-
const handle = await (
|
|
23
|
+
const handle = await fsOpen(path, 'r');
|
|
29
24
|
const stat = await handle.stat();
|
|
30
25
|
let closed = false;
|
|
31
26
|
return {
|
|
@@ -52,7 +47,7 @@ function fromFile(path, options) {
|
|
|
52
47
|
},
|
|
53
48
|
};
|
|
54
49
|
}
|
|
55
|
-
function fromHttpRange(url, options) {
|
|
50
|
+
export function fromHttpRange(url, options) {
|
|
56
51
|
const init = options?.init;
|
|
57
52
|
const chunkBytes = options?.chunkBytes ?? DEFAULT_URL_CHUNK_BYTES;
|
|
58
53
|
return {
|
package/dist/validate.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
-
|
|
2
|
+
import type { PathFaultCode } from '@botejs/native';
|
|
3
|
+
export type { StandardSchemaV1, PathFaultCode };
|
|
3
4
|
export type Segment = string | number;
|
|
4
5
|
export type Path = readonly Segment[];
|
|
5
6
|
export declare class ValidationError extends Error {
|
|
@@ -9,7 +10,9 @@ export declare class ValidationError extends Error {
|
|
|
9
10
|
}
|
|
10
11
|
export declare class PathError extends Error {
|
|
11
12
|
readonly path: Path;
|
|
12
|
-
|
|
13
|
+
/** The fault kind; stable across versions, safe to branch on. */
|
|
14
|
+
readonly code: PathFaultCode;
|
|
15
|
+
constructor(path: Path, code: PathFaultCode, segment?: number);
|
|
13
16
|
}
|
|
14
17
|
export declare function runStandardSchema<O>(schema: StandardSchemaV1<unknown, O>, value: unknown, path: Path): Promise<O>;
|
|
15
18
|
export declare function validateItem<O>(schema: StandardSchemaV1<unknown, O>, value: unknown, path: Path, onInvalid: 'throw' | 'skip'): Promise<{
|
package/dist/validate.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PathError = exports.ValidationError = void 0;
|
|
4
|
-
exports.runStandardSchema = runStandardSchema;
|
|
5
|
-
exports.validateItem = validateItem;
|
|
6
|
-
exports.formatPath = formatPath;
|
|
7
|
-
class ValidationError extends Error {
|
|
1
|
+
export class ValidationError extends Error {
|
|
8
2
|
issues;
|
|
9
3
|
path;
|
|
10
4
|
constructor(issues, path) {
|
|
@@ -14,23 +8,35 @@ class ValidationError extends Error {
|
|
|
14
8
|
this.path = path;
|
|
15
9
|
}
|
|
16
10
|
}
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
/** Human message per fault kind. The native layer ships only the code (and the
|
|
12
|
+
* offending `segment` where it matters), so this is the single source of the
|
|
13
|
+
* user-facing prose. Keyed by the Rust-generated [`PathFaultCode`]. */
|
|
14
|
+
const PATH_FAULT_MESSAGE = {
|
|
15
|
+
through_scalar: (segment) => `path traverses a non-container value at segment ${segment}`,
|
|
16
|
+
wrong_kind: (segment) => `path segment ${segment} does not match the container kind`,
|
|
17
|
+
scalar_target: () => 'target value is not a container',
|
|
18
|
+
iter_on_object: () => 'iter target is an object; use walk() to iterate object members',
|
|
19
|
+
walk_on_array: () => 'walk target is an array; use iter() to iterate array elements',
|
|
20
|
+
};
|
|
21
|
+
export class PathError extends Error {
|
|
19
22
|
path;
|
|
20
|
-
|
|
23
|
+
/** The fault kind; stable across versions, safe to branch on. */
|
|
24
|
+
code;
|
|
25
|
+
constructor(path, code, segment) {
|
|
26
|
+
const reason = (PATH_FAULT_MESSAGE[code] ?? (() => code))(segment);
|
|
21
27
|
super(`bote: cannot resolve ${formatPath(path)}: ${reason}`);
|
|
22
28
|
this.name = 'PathError';
|
|
23
29
|
this.path = path;
|
|
30
|
+
this.code = code;
|
|
24
31
|
}
|
|
25
32
|
}
|
|
26
|
-
|
|
27
|
-
async function runStandardSchema(schema, value, path) {
|
|
33
|
+
export async function runStandardSchema(schema, value, path) {
|
|
28
34
|
const result = await schema['~standard'].validate(value);
|
|
29
35
|
if (result.issues)
|
|
30
36
|
throw new ValidationError(result.issues, path);
|
|
31
37
|
return result.value;
|
|
32
38
|
}
|
|
33
|
-
async function validateItem(schema, value, path, onInvalid) {
|
|
39
|
+
export async function validateItem(schema, value, path, onInvalid) {
|
|
34
40
|
const result = await schema['~standard'].validate(value);
|
|
35
41
|
if (result.issues) {
|
|
36
42
|
if (onInvalid === 'skip')
|
|
@@ -39,7 +45,7 @@ async function validateItem(schema, value, path, onInvalid) {
|
|
|
39
45
|
}
|
|
40
46
|
return { value: result.value };
|
|
41
47
|
}
|
|
42
|
-
function formatPath(path) {
|
|
48
|
+
export function formatPath(path) {
|
|
43
49
|
if (path.length === 0)
|
|
44
50
|
return '(root)';
|
|
45
51
|
let out = '';
|
package/package.json
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@botejs/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"license": "MIT",
|
|
5
6
|
"repository": {
|
|
6
7
|
"type": "git",
|
|
7
8
|
"url": "git+https://github.com/jankdc/bote.git",
|
|
8
9
|
"directory": "packages/core"
|
|
9
10
|
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
10
17
|
"main": "dist/index.js",
|
|
11
18
|
"types": "dist/index.d.ts",
|
|
12
19
|
"files": [
|
|
@@ -14,7 +21,7 @@
|
|
|
14
21
|
"README.md"
|
|
15
22
|
],
|
|
16
23
|
"engines": {
|
|
17
|
-
"node": ">= 18.
|
|
24
|
+
"node": ">= 22.18.0"
|
|
18
25
|
},
|
|
19
26
|
"publishConfig": {
|
|
20
27
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -23,12 +30,12 @@
|
|
|
23
30
|
"scripts": {
|
|
24
31
|
"build": "tsc",
|
|
25
32
|
"build:debug": "tsc --sourceMap",
|
|
26
|
-
"test": "node --test
|
|
33
|
+
"test": "node --test __test__/*.spec.ts",
|
|
27
34
|
"lint": "oxlint src",
|
|
28
35
|
"prepublishOnly": "cp ../../README.md ./README.md && tsc"
|
|
29
36
|
},
|
|
30
37
|
"dependencies": {
|
|
31
|
-
"@botejs/native": "
|
|
38
|
+
"@botejs/native": "^0.4.0"
|
|
32
39
|
},
|
|
33
40
|
"devDependencies": {
|
|
34
41
|
"@types/node": "^22.0.0",
|