@botejs/core 0.4.0 → 0.6.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/open.d.ts CHANGED
@@ -1,86 +1,47 @@
1
- import type { Source } from './sources.ts';
2
- import { type Path, type Segment, type StandardSchemaV1 } from './validate.ts';
3
- import { type IterOptions } from './args.ts';
4
- type InferOutput<Sch> = Sch extends StandardSchemaV1<unknown, infer O> ? O : never;
5
- type SelectMapShape<S> = {
6
- -readonly [K in keyof S]: unknown;
7
- };
8
- /** Zero-based index of an array element. */
9
- export type IterIndex = number;
10
- /** One `walk` step: the member's key paired with a cursor anchored at its value. */
11
- export type WalkEntry = [key: string, cursor: Cursor];
1
+ import { type RootCursor } from './cursor.ts';
2
+ import type { SeekableSource, ForwardSource } from './source/base.ts';
12
3
  export declare const DEFAULT_SOURCE_CHUNK_BYTES: number;
13
- export declare const DEFAULT_ITER_BATCH = 1000;
14
- export declare const MAX_ITER_BATCH = 1000000;
15
4
  export interface OpenOptions {
16
5
  /**
17
- * Slot budget for the structural-index cache: one slot per cached container
18
- * plus one per tabled object member. When a scan tips the cache over this
19
- * budget, the deepest (least navigationally useful) containers are evicted
20
- * first, LRU-tiebroken, keeping the shallow backbone that resumes future
21
- * scans. Bounds resident cache memory regardless of document size. `0`
22
- * disables the cache entirely. Omit for the native default (1024).
6
+ * How much of the index that speeds up repeat lookups to keep in memory,
7
+ * measured in entries. Higher means faster repeat queries but more memory;
8
+ * lower means less memory but slower repeats. Set to `0` to turn the cache
9
+ * off. Defaults to 1024.
23
10
  */
24
11
  indexCacheEntries?: number;
25
12
  /**
26
- * Max object members tabled per walked container in the structural-index
27
- * cache. The table is a dense prefix; past the cap, lookups of later members
28
- * resume-scan from the cap boundary. Lower trades cache memory for resume work
29
- * on pathologically large objects. `0` disables object member indexing. Omit
30
- * for the native default (unbounded).
13
+ * How many keys per object to index for fast lookup. Higher speeds up access
14
+ * to keys later in large objects but uses more memory; lower saves memory at
15
+ * the cost of slower lookups for those keys. Set to `0` to skip indexing
16
+ * object keys. Defaults to unlimited.
31
17
  */
32
18
  objectMemberCap?: number;
33
19
  /**
34
- * Element-index stride between sampled array members in the structural-index
35
- * cache. A later index resumes from the nearest array member at or before it, so
36
- * a smaller stride means denser array members (more memory, shorter resume
37
- * scans). `0` disables array-member indexing. Omit for the native default (16).
38
- *
39
- * Setting both `objectMemberCap` and `arrayIndexInterval` to `0` disables the
40
- * cache entirely (no source bytes are ever cached either way), as does
41
- * `indexCacheEntries: 0`.
20
+ * How often to index array positions, e.g. every 16th element. Lower means
21
+ * faster access to arbitrary array elements but more memory; higher saves
22
+ * memory at the cost of slower access. Set to `0` to skip indexing array
23
+ * positions. Defaults to 16.
42
24
  */
43
25
  arrayIndexInterval?: number;
44
26
  }
45
- export interface Cursor {
46
- hop(...path: Segment[]): Promise<Cursor | null>;
47
- has(...path: Segment[]): Promise<boolean>;
48
- has(...args: [...Segment[], StandardSchemaV1]): Promise<boolean>;
49
- get(...path: Segment[]): Promise<unknown>;
50
- get<Sch extends StandardSchemaV1>(...args: [...Segment[], Sch]): Promise<InferOutput<Sch>>;
51
- count(...path: Segment[]): Promise<number>;
52
- iter(...path: Segment[]): AsyncIterable<unknown[]>;
53
- iter<Sch extends StandardSchemaV1>(...args: [...Segment[], Sch]): AsyncIterable<InferOutput<Sch>[]>;
54
- iter<Sch extends StandardSchemaV1>(...args: [...Segment[], IterOptions & {
55
- withIndex: true;
56
- schema: Sch;
57
- }]): AsyncIterable<[IterIndex, InferOutput<Sch>][]>;
58
- iter<Sch extends StandardSchemaV1>(...args: [...Segment[], IterOptions & {
59
- schema: Sch;
60
- }]): AsyncIterable<InferOutput<Sch>[]>;
61
- iter<S extends Record<string, Segment | Path>>(...args: [...Segment[], IterOptions & {
62
- withIndex: true;
63
- select: S;
64
- }]): AsyncIterable<[IterIndex, SelectMapShape<S>][]>;
65
- iter<S extends Record<string, Segment | Path>>(...args: [...Segment[], IterOptions & {
66
- select: S;
67
- }]): AsyncIterable<SelectMapShape<S>[]>;
68
- iter(...args: [...Segment[], IterOptions & {
69
- withIndex: true;
70
- }]): AsyncIterable<[IterIndex, unknown][]>;
71
- iter(...args: [...Segment[], IterOptions]): AsyncIterable<unknown[]>;
72
- walk(...path: Segment[]): AsyncIterable<WalkEntry>;
73
- walk(...path: Segment[]): AsyncIterable<Cursor>;
74
- }
75
- export interface RootCursor extends Cursor, AsyncDisposable {
76
- /** Close the underlying source. Idempotent. */
77
- close(): Promise<void>;
78
- }
79
27
  /**
80
- * Open a cursor over a seekable source.
28
+ * Options for a forward source: every cache knob is forbidden (the structural-index
29
+ * cache is forced off). `Omit` would collapse to `{}` and silently permit them, so
30
+ * the knobs are mapped to `never` to reject them at compile time.
31
+ */
32
+ export type ForwardOpenOptions = {
33
+ [K in keyof OpenOptions]?: never;
34
+ };
35
+ /**
36
+ * Open a cursor over a source.
37
+ *
38
+ * A seekable source (`fromFile`/`fromBuffer`/`fromHttpRange`) supports the cache
39
+ * and repeated, out-of-order queries. A forward source (`fromReadable`/`fromHttpStream`)
40
+ * is a single forward pass: the cache is forced off, so its cache knobs are
41
+ * rejected at compile time and at runtime.
81
42
  *
82
- * The returned `RootCursor` owns the reader: `close()` (or `await using`)
83
- * drives the reader's own `close()` exactly once.
43
+ * The returned `RootCursor` owns the reader: `close()` (or `await using`) drives
44
+ * the reader's own `close()` exactly once.
84
45
  */
85
- export declare function open(source: Source, options?: OpenOptions): Promise<RootCursor>;
86
- export {};
46
+ export declare function open(source: SeekableSource, options?: OpenOptions): Promise<RootCursor>;
47
+ export declare function open(source: ForwardSource, options?: ForwardOpenOptions): Promise<RootCursor>;
package/dist/open.js CHANGED
@@ -1,32 +1,32 @@
1
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";
2
+ import { wrap } from "./cursor.js";
5
3
  export const DEFAULT_SOURCE_CHUNK_BYTES = 64 * 1024;
6
- export const DEFAULT_ITER_BATCH = 1000;
7
- export const MAX_ITER_BATCH = 1_000_000;
8
- /**
9
- * Open a cursor over a seekable source.
10
- *
11
- * The returned `RootCursor` owns the reader: `close()` (or `await using`)
12
- * drives the reader's own `close()` exactly once.
13
- */
4
+ const CACHE_KNOBS = ['indexCacheEntries', 'objectMemberCap', 'arrayIndexInterval'];
14
5
  export async function open(source, options) {
15
- const { indexCacheEntries, objectMemberCap, arrayIndexInterval } = options ?? {};
16
- for (const [name, value] of [
17
- ['indexCacheEntries', indexCacheEntries],
18
- ['objectMemberCap', objectMemberCap],
19
- ['arrayIndexInterval', arrayIndexInterval],
20
- ]) {
6
+ if (!source.seekable) {
7
+ for (const name of CACHE_KNOBS) {
8
+ if (options?.[name] !== undefined) {
9
+ throw new RangeError(`open: ${name} is not allowed for a forward source; the structural-index cache is forced off`);
10
+ }
11
+ }
12
+ }
13
+ for (const name of CACHE_KNOBS) {
14
+ const value = options?.[name];
21
15
  if (value !== undefined && (!Number.isSafeInteger(value) || value < 0)) {
22
16
  throw new RangeError(`open: ${name} must be a non-negative integer (0 disables), got ${value}`);
23
17
  }
24
18
  }
19
+ // A forward source disables every cache dimension, so the engine never resolves
20
+ // a cached container offset into a backward read on a stream it cannot rewind.
21
+ const knobs = source.seekable ? options : { indexCacheEntries: 0, objectMemberCap: 0, arrayIndexInterval: 0 };
25
22
  const reader = await source.open();
26
23
  const chunkBytes = reader.chunkBytes ?? DEFAULT_SOURCE_CHUNK_BYTES;
27
24
  let native;
28
25
  try {
29
- if (!Number.isInteger(reader.size) || reader.size < 0) {
26
+ if (reader.seekable && reader.size === undefined) {
27
+ throw new RangeError('open: a seekable source must report a size');
28
+ }
29
+ if (reader.size !== undefined && (!Number.isInteger(reader.size) || reader.size < 0)) {
30
30
  throw new RangeError(`open: source size must be a non-negative integer, got ${reader.size}`);
31
31
  }
32
32
  if (!Number.isSafeInteger(chunkBytes) || chunkBytes <= 0) {
@@ -38,10 +38,10 @@ export async function open(source, options) {
38
38
  native = openNative({
39
39
  size: reader.size,
40
40
  chunkBytes,
41
- indexCacheEntries,
42
- objectMemberCap,
43
- arrayIndexInterval,
44
- read: async ({ offset, length }) => reader.read(offset, length),
41
+ objectMemberCap: knobs?.objectMemberCap,
42
+ indexCacheEntries: knobs?.indexCacheEntries,
43
+ arrayIndexInterval: knobs?.arrayIndexInterval,
44
+ read: ({ offset, length }) => reader.read(offset, length),
45
45
  });
46
46
  }
47
47
  catch (err) {
@@ -50,15 +50,17 @@ export async function open(source, options) {
50
50
  await closeReader(reader);
51
51
  }
52
52
  catch (closeErr) {
53
- if (err instanceof Error)
53
+ if (err instanceof Error) {
54
54
  err.cause ??= closeErr;
55
+ }
55
56
  }
56
57
  throw err;
57
58
  }
58
59
  const state = { closed: false };
59
60
  const close = async () => {
60
- if (state.closed)
61
+ if (state.closed) {
61
62
  return;
63
+ }
62
64
  state.closed = true;
63
65
  await closeReader(reader);
64
66
  };
@@ -68,172 +70,7 @@ export async function open(source, options) {
68
70
  });
69
71
  }
70
72
  async function closeReader(reader) {
71
- if (reader.close)
73
+ if (reader.close) {
72
74
  await reader.close();
73
- }
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
- }
82
- }
83
- return err;
84
- }
85
- /** Throw a uniform error for any operation on a closed cursor, so use-after-close
86
- * is one defined contract regardless of source (some readers' reads keep working
87
- * after close, others throw an opaque I/O error). */
88
- function ensureOpen(state) {
89
- if (state.closed)
90
- throw new Error('bote: cursor is closed');
91
- }
92
- function wrap(native, state) {
93
- const cursor = {
94
- async hop(...path) {
95
- ensureOpen(state);
96
- validatePath(path);
97
- let child;
98
- try {
99
- child = await native.hop(path);
100
- }
101
- catch (err) {
102
- throw deserializeError(err, path);
103
- }
104
- return child ? wrap(child, state) : null;
105
- },
106
- async has(...args) {
107
- ensureOpen(state);
108
- const { path, tail: schema } = splitArgs(args);
109
- if (schema !== undefined && !isSchema(schema)) {
110
- throw new TypeError('has: expected a Standard Schema as the trailing argument');
111
- }
112
- if (!schema)
113
- return native.has(path);
114
- if (!(await native.has(path)))
115
- return false;
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');
119
- return !('skip' in result);
120
- },
121
- async get(...args) {
122
- ensureOpen(state);
123
- const { path, tail: schema } = splitArgs(args);
124
- if (schema !== undefined && !isSchema(schema)) {
125
- throw new TypeError('get: expected a Standard Schema as the trailing argument');
126
- }
127
- let value;
128
- try {
129
- const text = await native.get(path);
130
- value = text === undefined ? undefined : parseValue(text, path);
131
- }
132
- catch (err) {
133
- throw deserializeError(err, path);
134
- }
135
- if (!schema)
136
- return value;
137
- return runStandardSchema(schema, value, path);
138
- },
139
- async count(...path) {
140
- ensureOpen(state);
141
- validatePath(path);
142
- try {
143
- return await native.count(path);
144
- }
145
- catch (err) {
146
- throw deserializeError(err, path);
147
- }
148
- },
149
- iter(...args) {
150
- ensureOpen(state);
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}`);
155
- }
156
- if (withIndex !== undefined && typeof withIndex !== 'boolean') {
157
- throw new TypeError(`iter: withIndex must be a boolean, got ${typeof withIndex}`);
158
- }
159
- if (onInvalid !== undefined && onInvalid !== 'throw' && onInvalid !== 'skip') {
160
- throw new RangeError(`iter: onInvalid must be "throw" or "skip", got ${JSON.stringify(onInvalid)}`);
161
- }
162
- const resolvedBatch = batch ?? DEFAULT_ITER_BATCH;
163
- const selectIr = select !== undefined ? serializeSelect(select) : undefined;
164
- const inner = native.iter(path, { selectIr, batch: resolvedBatch });
165
- if (!schema) {
166
- return {
167
- async *[Symbol.asyncIterator]() {
168
- let i = 0;
169
- try {
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
- }
182
- }
183
- catch (err) {
184
- throw deserializeError(err, path);
185
- }
186
- },
187
- };
188
- }
189
- const policy = onInvalid ?? 'throw';
190
- return {
191
- async *[Symbol.asyncIterator]() {
192
- let i = 0;
193
- try {
194
- for await (const b of inner) {
195
- const out = [];
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) {
200
- continue;
201
- }
202
- out.push(withIndex ? [index, result.value] : result.value);
203
- }
204
- yield out;
205
- }
206
- }
207
- catch (err) {
208
- throw deserializeError(err, path);
209
- }
210
- },
211
- };
212
- },
213
- walk(...path) {
214
- ensureOpen(state);
215
- validatePath(path);
216
- return {
217
- async *[Symbol.asyncIterator]() {
218
- try {
219
- for await (const [key, child] of native.walk(path)) {
220
- yield [key, wrap(child, state)];
221
- }
222
- }
223
- catch (err) {
224
- throw deserializeError(err, path);
225
- }
226
- },
227
- };
228
- },
229
- };
230
- return cursor;
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
75
  }
239
76
  }
package/dist/path.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- import type { Segment } from './validate.ts';
1
+ export type Segment = string | number;
2
+ export type Path = readonly Segment[];
2
3
  /** Upper bound on numeric segments (napi takes them as `u32`). 2^32 - 1
3
4
  * comfortably covers any in-memory JSON array. */
4
5
  export declare const MAX_ARRAY_INDEX = 4294967295;
5
6
  export declare function validatePath(path: readonly unknown[]): asserts path is readonly Segment[];
7
+ export declare function formatPath(path: Path): string;
package/dist/path.js CHANGED
@@ -4,17 +4,41 @@ export const MAX_ARRAY_INDEX = 0xffffffff;
4
4
  export function validatePath(path) {
5
5
  for (let i = 0; i < path.length; i++) {
6
6
  const s = path[i];
7
- if (typeof s === 'string')
7
+ if (typeof s === 'string') {
8
8
  continue;
9
- if (typeof s === 'number' && Number.isInteger(s) && s >= 0 && s <= MAX_ARRAY_INDEX)
9
+ }
10
+ if (typeof s === 'number' && Number.isInteger(s) && s >= 0 && s <= MAX_ARRAY_INDEX) {
10
11
  continue;
12
+ }
11
13
  throw new TypeError(`path segment ${i}: expected string or non-negative integer (<= ${MAX_ARRAY_INDEX}), got ${describeBadSegment(s)}`);
12
14
  }
13
15
  }
16
+ export function formatPath(path) {
17
+ if (path.length === 0) {
18
+ return '(root)';
19
+ }
20
+ let out = '';
21
+ for (let i = 0; i < path.length; i++) {
22
+ const seg = path[i];
23
+ if (typeof seg === 'number') {
24
+ out += `[${seg}]`;
25
+ continue;
26
+ }
27
+ if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(seg)) {
28
+ out += i === 0 ? seg : `.${seg}`;
29
+ }
30
+ else {
31
+ out += `[${JSON.stringify(seg)}]`;
32
+ }
33
+ }
34
+ return out;
35
+ }
14
36
  function describeBadSegment(s) {
15
- if (typeof s === 'number')
37
+ if (typeof s === 'number') {
16
38
  return `${s}`;
17
- if (s === null)
39
+ }
40
+ if (s === null) {
18
41
  return 'null';
42
+ }
19
43
  return typeof s;
20
44
  }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * The bytes a `read` resolves to, plus an end-of-stream flag. `eof` is `true`
3
+ * iff this read reached the end of the underlying stream. A seekable reader can
4
+ * compute it from `size`; a forward reader discovers it as the stream drains.
5
+ */
6
+ export interface ReadResult {
7
+ readonly data: Uint8Array;
8
+ readonly eof: boolean;
9
+ }
10
+ /**
11
+ * A handle on an opened byte stream. The reader owns whatever resources back the
12
+ * stream (a file handle, a `fetch` body, an `AbortController`, etc.) and surfaces
13
+ * them through `close()`. Constructed by `Source.open()`; never by callers directly.
14
+ *
15
+ * `seekable` declares the access model:
16
+ * - `true`: `read(offset, length)` may be called at any offset, in any order.
17
+ * `size` is required. This random access is what lets the structural-index
18
+ * cache resume scans near a target.
19
+ * - `false`: a single forward pass. `read` is called with non-decreasing
20
+ * offsets; the cache is forced off. `size` may be omitted (the end is found
21
+ * via `eof`). Rewinding to an earlier offset re-acquires the stream (see
22
+ * `fromReadable`'s `rewind` option) or throws {@link ForwardReplayError}.
23
+ */
24
+ export interface Reader {
25
+ /** Whether reads may target any offset/order (`true`) or a single forward pass (`false`). */
26
+ readonly seekable: boolean;
27
+ /** Total length in bytes. Required for seekable readers; optional for forward ones. */
28
+ readonly size?: number;
29
+ /** Preferred read granularity in bytes. Must be a non-zero multiple of 64. */
30
+ readonly chunkBytes?: number;
31
+ /**
32
+ * Read up to `length` bytes starting at `offset`. `data.byteLength` is the
33
+ * actual count read (`<= length`); `eof` is `true` iff the read reached the
34
+ * end of the stream.
35
+ */
36
+ read(offset: number, length: number): Promise<ReadResult>;
37
+ /** Release resources held by the reader. Driven once by the `open()` lifecycle. */
38
+ close?(): Promise<void> | void;
39
+ }
40
+ /**
41
+ * Describes how to obtain a byte stream. `open()` is called once per cursor for
42
+ * a seekable source; a forward source's reader re-acquires its stream on demand.
43
+ * Provide your own object to plug in a custom backend.
44
+ */
45
+ export interface Source {
46
+ /** Mirrors {@link Reader.seekable}; lets `open()` enforce the right knobs at compile time. */
47
+ readonly seekable: boolean;
48
+ /** Acquire the stream. Resolves to a `Reader` that owns any underlying resources. */
49
+ open(): Promise<Reader>;
50
+ }
51
+ /**
52
+ * A {@link Source} statically known to be seekable (random access, cache-eligible).
53
+ * `open()` discriminates on this, so a custom backend must brand its `seekable`
54
+ * as the literal `true` (annotate the object `SeekableSource`) to be accepted.
55
+ */
56
+ export type SeekableSource = Source & {
57
+ readonly seekable: true;
58
+ };
59
+ /**
60
+ * A {@link Source} statically known to be forward-only (single pass, cache forced
61
+ * off). Brand a custom backend's object `ForwardSource` so `open()` accepts it and
62
+ * rejects cache knobs at compile time.
63
+ */
64
+ export type ForwardSource = Source & {
65
+ readonly seekable: false;
66
+ };
67
+ export interface FactoryOptions {
68
+ /** Override the factory's default chunk size. Must be a non-zero multiple of 64. */
69
+ chunkBytes?: number;
70
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,49 @@
1
+ import type { FactoryOptions, ForwardSource } from './base.ts';
2
+ /**
3
+ * A function that produces a fresh readable stream each time it is called. A
4
+ * forward reader invokes it once up front, and again on every re-acquisition, so
5
+ * it is also the seam for per-acquisition customization (a freshly minted auth
6
+ * token, a new `AbortSignal`, etc.).
7
+ */
8
+ export type ReadableProducer = () => NodeJS.ReadableStream | ReadableStream<Uint8Array> | Promise<NodeJS.ReadableStream | ReadableStream<Uint8Array>>;
9
+ export interface ReadableOptions extends FactoryOptions {
10
+ /** Known total length, if any. Forwarded to the engine so it can skip rediscovering the end. */
11
+ size?: number;
12
+ /** Transform applied to every (re)acquired stream, e.g. `s => s.pipeThrough(new DecompressionStream('gzip'))`. */
13
+ decode?: (raw: ReadableStream<Uint8Array>) => ReadableStream<Uint8Array>;
14
+ /**
15
+ * What a later query that must re-read from an earlier offset does. Defaults
16
+ * to `'forbid'`. The three settings trade resident memory for re-read ability:
17
+ * - `'forbid'`: a single forward pass; a rewind throws {@link ForwardReplayError}.
18
+ * - `'replay'`: re-acquire the stream from the start. Only safe when the
19
+ * producer is idempotent (yields the same bytes each call). No extra memory.
20
+ * - `'buffer'`: snapshot the whole stream into memory on first read, enabling
21
+ * random access at O(n) resident memory.
22
+ */
23
+ rewind?: 'forbid' | 'replay' | 'buffer';
24
+ }
25
+ export interface HttpRequestOptions extends Omit<ReadableOptions, 'size'> {
26
+ /** Merged into every `fetch` (headers, credentials, signal, etc.). */
27
+ init?: RequestInit;
28
+ }
29
+ /**
30
+ * A forward-only source backed by a re-openable readable stream. `produce` is
31
+ * called to acquire each pass: once up front, and again on a rewind when
32
+ * `rewind: 'replay'` is set. A plain `Readable` instance cannot be re-streamed,
33
+ * so pass a thunk (`() => createReadStream(path)`), not a live stream.
34
+ *
35
+ * Because every cursor operation is an independent scan from the start, a single
36
+ * forward pass serves exactly one query; a second query (or `hop` then `get`)
37
+ * rewinds. By default that throws {@link ForwardReplayError}; opt into
38
+ * `rewind: 'replay'` (idempotent producer) or `rewind: 'buffer'` (in-memory
39
+ * snapshot) for multi-query access.
40
+ */
41
+ export declare function fromReadable(produce: ReadableProducer, options?: ReadableOptions): ForwardSource;
42
+ /**
43
+ * A forward-only source over an HTTP request (GET is default), streamed in a single pass. A
44
+ * convenience wrapper around {@link fromReadable} whose producer re-fetches `url`
45
+ * (reusing `init`, so auth headers, credentials, and an `AbortSignal` survive
46
+ * each acquisition). For repeated or random access over HTTP, prefer the seekable
47
+ * {@link fromHttpRange}.
48
+ */
49
+ export declare function fromHttpRequest(url: string, options?: HttpRequestOptions): ForwardSource;