@lsmsgpack/client 0.0.0-stage → 2026.10.2901

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/README.md CHANGED
@@ -1,3 +1,39 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # @lsmsgpack/client
2
+
3
+ Reads the `application/x-lsmsgpack` responses of [LsMsgPack's ASP.NET formatters](https://github.com/mlsomers/LsMsgPack/blob/master/docs/WebFormatters.md) into complete JavaScript objects, and writes request bodies they read, in browsers and Node 18+:
4
+
5
+ - every property of the .NET class is there, also the ones the server left out (`null`, `0`, `false`, `[DefaultValue]`...);
6
+ - polymorphic values get the properties of their actual class, and its name in `$type`;
7
+ - the schema (the property names) is sent once, then the server refers to it;
8
+ - optional generated models ([lsmsgpack-ts](https://github.com/mlsomers/LsMsgPack/blob/master/docs/TypeScriptModels.md)): instances of TypeScript classes with the values the .NET constructors set, typed request bodies, and the server's schemas held before the first request.
9
+
10
+ No dependencies. Request bodies are sent as plain MsgPack (`application/msgpack`), with `$type` as the type id: polymorphic bodies work too.
11
+
12
+ ```ts
13
+ import { LsMsgPackClient } from '@lsmsgpack/client';
14
+
15
+ const api = new LsMsgPackClient({ baseUrl: 'https://example.com/' });
16
+ const invoice = await api.get<Invoice>('api/invoices/42');
17
+ await api.post('api/invoices', invoice);
18
+ ```
19
+
20
+ With the generated models and schemas:
21
+
22
+ ```ts
23
+ import { models } from './api/models';
24
+ import { schemas } from './api/models.schemas';
25
+
26
+ const api = new LsMsgPackClient({ baseUrl: 'https://example.com/', models, schemas });
27
+ ```
28
+
29
+ Documentation: [docs/TypeScriptClient.md](https://github.com/mlsomers/LsMsgPack/blob/master/docs/TypeScriptClient.md) (options, the models, value conversions, polymorphic request bodies, CORS, the described schema).
30
+
31
+ ## Development
32
+
33
+ ```bash
34
+ npm install
35
+ npm test # builds, then runs test/*.test.mjs (node:test)
36
+ dotnet build test-server # enables the tests against the ASP.NET Core formatters (skipped without it)
37
+ dotnet build ../../LsMsgPackTypeScript # and the ones with models generated by lsmsgpack-ts (test/generated/)
38
+ npm run bench # reading and writing 100 invoices: JSON, @msgpack/msgpack, this client with and without models (needs the test server)
39
+ ```
@@ -0,0 +1,76 @@
1
+ import { Decoded, Decoder, DecodeOptions } from './decoder.js';
2
+ import { Schema } from './schema.js';
3
+ import { SchemaStore } from './store.js';
4
+ export declare const MediaTypes: {
5
+ readonly MsgPack: "application/msgpack";
6
+ readonly XMsgPack: "application/x-msgpack";
7
+ readonly XLsMsgPack: "application/x-lsmsgpack";
8
+ };
9
+ /** Request header: the ids of the schemas the client holds. */
10
+ export declare const SchemasHeader = "MsgPack-Schemas";
11
+ /** Response header: the id of the schema of the body. */
12
+ export declare const SchemaHeader = "MsgPack-Schema";
13
+ /** The Accept header of the client: the described schema, and application/x-lsmsgpack for servers (and content negotiation) that do not know the parameter. */
14
+ export declare const DescribedAccept: string;
15
+ export interface ClientOptions extends DecodeOptions {
16
+ /** Relative urls are resolved against it (default: the location of the page, if any). */
17
+ baseUrl?: string | URL;
18
+ /** The fetch to use (default: globalThis.fetch). */
19
+ fetch?: typeof globalThis.fetch;
20
+ /** Where the schemas are kept, share one between clients of the same servers (default: a new store). */
21
+ store?: SchemaStore;
22
+ /** Ask for the described schema, which gives omitted values their defaults and nested objects their properties (default true). */
23
+ describedSchema?: boolean;
24
+ /** Tell servers which schemas the client holds, so they refer to them instead of sending them (default true). */
25
+ negotiateSchemas?: boolean;
26
+ /** Headers added to every request. */
27
+ headers?: HeadersInit;
28
+ /**
29
+ * The schema set of lsmsgpack-ts (its schemas export): held from the start, so the first response of each type refers to its schema instead of carrying it.
30
+ * Shared stores: call store.preload once instead.
31
+ */
32
+ schemas?: Readonly<Record<string, string | Uint8Array>> | Iterable<string | Uint8Array>;
33
+ }
34
+ /** A request: the options of fetch, with a body that is encoded as MsgPack unless it is already text or bytes. */
35
+ export interface RequestOptions extends Omit<RequestInit, 'body'> {
36
+ body?: unknown;
37
+ }
38
+ export interface MsgPackResponse<T> {
39
+ value: T;
40
+ response: Response;
41
+ /** The schema of an application/x-lsmsgpack body. */
42
+ schema?: Schema;
43
+ /** The body referred to its schema instead of containing it. */
44
+ reference: boolean;
45
+ }
46
+ export declare class LsMsgPackClient {
47
+ readonly store: SchemaStore;
48
+ readonly decoder: Decoder;
49
+ private readonly fetchFn;
50
+ private readonly baseUrl?;
51
+ private readonly described;
52
+ private readonly negotiate;
53
+ private readonly headers?;
54
+ private readonly models?;
55
+ private readonly generateCode?;
56
+ constructor(options?: ClientOptions);
57
+ get<T = unknown>(url: string | URL, init?: RequestOptions): Promise<T>;
58
+ post<T = unknown>(url: string | URL, body?: unknown, init?: RequestOptions): Promise<T>;
59
+ put<T = unknown>(url: string | URL, body?: unknown, init?: RequestOptions): Promise<T>;
60
+ patch<T = unknown>(url: string | URL, body?: unknown, init?: RequestOptions): Promise<T>;
61
+ delete<T = unknown>(url: string | URL, init?: RequestOptions): Promise<T>;
62
+ /**
63
+ * Sends a request and decodes the response. Throws an HttpError for a status outside 200-299 (with the decoded body).
64
+ * A GET or HEAD whose response refers to a schema the store no longer holds is repeated once without advertising schemas.
65
+ */
66
+ send<T = unknown>(url: string | URL, init?: RequestOptions): Promise<MsgPackResponse<T>>;
67
+ /** Decodes a response (e.g. of your own fetch): MsgPack by its Content-Type, JSON, otherwise text. Learns the schema of an application/x-lsmsgpack body. */
68
+ read<T = unknown>(response: Response, origin?: string): Promise<MsgPackResponse<T>>;
69
+ /**
70
+ * Decodes an application/x-lsmsgpack body.
71
+ * @param origin the server (scheme://host:port), to learn its schema
72
+ * @param schemaHeader the response header MsgPack-Schema: an inline schema is kept only when its hash is this id
73
+ */
74
+ decode(bytes: Uint8Array, origin?: string, schemaHeader?: string | null): Decoded;
75
+ private resolve;
76
+ }
package/dist/client.js ADDED
@@ -0,0 +1,150 @@
1
+ // A fetch wrapper for the ASP.NET formatters of LsMsgPack: asks for application/x-lsmsgpack with the described schema, tells the server which schemas it holds,
2
+ // learns the schemas of the responses and decodes the bodies into complete objects. Request bodies are sent as plain MsgPack (application/msgpack).
3
+ import { Decoder } from './decoder.js';
4
+ import { HttpError, MissingSchemaError } from './errors.js';
5
+ import { Schema } from './schema.js';
6
+ import { SchemaStore } from './store.js';
7
+ import { encode } from './writer.js';
8
+ export const MediaTypes = {
9
+ MsgPack: 'application/msgpack',
10
+ XMsgPack: 'application/x-msgpack',
11
+ XLsMsgPack: 'application/x-lsmsgpack',
12
+ };
13
+ /** Request header: the ids of the schemas the client holds. */
14
+ export const SchemasHeader = 'MsgPack-Schemas';
15
+ /** Response header: the id of the schema of the body. */
16
+ export const SchemaHeader = 'MsgPack-Schema';
17
+ /** The Accept header of the client: the described schema, and application/x-lsmsgpack for servers (and content negotiation) that do not know the parameter. */
18
+ export const DescribedAccept = `${MediaTypes.XLsMsgPack}; schema=described, ${MediaTypes.XLsMsgPack}; q=0.9`;
19
+ export class LsMsgPackClient {
20
+ constructor(options = {}) {
21
+ this.store = options.store ?? new SchemaStore();
22
+ this.decoder = new Decoder(options);
23
+ this.models = options.models;
24
+ this.generateCode = options.generateCode;
25
+ if (options.schemas !== undefined)
26
+ this.store.preload(options.schemas);
27
+ this.fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);
28
+ this.baseUrl = options.baseUrl;
29
+ this.described = options.describedSchema ?? true;
30
+ this.negotiate = options.negotiateSchemas ?? true;
31
+ this.headers = options.headers;
32
+ }
33
+ async get(url, init) {
34
+ return (await this.send(url, { ...init, method: 'GET' })).value;
35
+ }
36
+ async post(url, body, init) {
37
+ return (await this.send(url, { ...init, method: 'POST', body })).value;
38
+ }
39
+ async put(url, body, init) {
40
+ return (await this.send(url, { ...init, method: 'PUT', body })).value;
41
+ }
42
+ async patch(url, body, init) {
43
+ return (await this.send(url, { ...init, method: 'PATCH', body })).value;
44
+ }
45
+ async delete(url, init) {
46
+ return (await this.send(url, { ...init, method: 'DELETE' })).value;
47
+ }
48
+ /**
49
+ * Sends a request and decodes the response. Throws an HttpError for a status outside 200-299 (with the decoded body).
50
+ * A GET or HEAD whose response refers to a schema the store no longer holds is repeated once without advertising schemas.
51
+ */
52
+ async send(url, init = {}) {
53
+ const target = this.resolve(url);
54
+ const headers = new Headers(this.headers);
55
+ new Headers(init.headers).forEach((value, name) => headers.set(name, value));
56
+ if (!headers.has('Accept'))
57
+ headers.set('Accept', this.described ? DescribedAccept : MediaTypes.XLsMsgPack);
58
+ const advertised = this.negotiate && !headers.has(SchemasHeader) ? this.store.advertise(target.origin) : undefined;
59
+ if (advertised !== undefined)
60
+ headers.set(SchemasHeader, advertised);
61
+ let body;
62
+ if (init.body === undefined || init.body === null || isBodyInit(init.body))
63
+ body = init.body;
64
+ else {
65
+ body = encode(init.body, { models: this.models, generateCode: this.generateCode });
66
+ if (!headers.has('Content-Type'))
67
+ headers.set('Content-Type', MediaTypes.MsgPack);
68
+ }
69
+ const request = { ...init, headers, body };
70
+ let result;
71
+ try {
72
+ result = await this.read(await this.fetchFn(target, request), target.origin);
73
+ }
74
+ catch (e) {
75
+ const method = (init.method ?? 'GET').toUpperCase();
76
+ if (!(e instanceof MissingSchemaError) || advertised === undefined || (method !== 'GET' && method !== 'HEAD'))
77
+ throw e;
78
+ headers.delete(SchemasHeader); // repeating a GET is safe: the server sends the schema inline
79
+ result = await this.read(await this.fetchFn(target, request), target.origin);
80
+ }
81
+ if (!result.response.ok)
82
+ throw new HttpError(result.response.status, result.response.statusText, result.value, result.response);
83
+ return result;
84
+ }
85
+ /** Decodes a response (e.g. of your own fetch): MsgPack by its Content-Type, JSON, otherwise text. Learns the schema of an application/x-lsmsgpack body. */
86
+ async read(response, origin) {
87
+ const mediaType = (response.headers.get('Content-Type') ?? '').split(';')[0].trim().toLowerCase();
88
+ if (mediaType === MediaTypes.XLsMsgPack) {
89
+ const bytes = new Uint8Array(await response.arrayBuffer());
90
+ const decoded = this.decode(bytes, origin ?? originOf(response.url), response.headers.get(SchemaHeader));
91
+ return { value: decoded.value, response, schema: decoded.schema, reference: decoded.reference };
92
+ }
93
+ if (mediaType === MediaTypes.MsgPack || mediaType === MediaTypes.XMsgPack) {
94
+ const bytes = new Uint8Array(await response.arrayBuffer());
95
+ return { value: this.decoder.decodePlain(bytes), response, reference: false };
96
+ }
97
+ const text = await response.text();
98
+ let value = text.length === 0 ? undefined : text; // e.g. 204 No Content for null
99
+ if (/[/+]json$/.test(mediaType) && text.length > 0)
100
+ value = JSON.parse(text);
101
+ return { value: value, response, reference: false };
102
+ }
103
+ /**
104
+ * Decodes an application/x-lsmsgpack body.
105
+ * @param origin the server (scheme://host:port), to learn its schema
106
+ * @param schemaHeader the response header MsgPack-Schema: an inline schema is kept only when its hash is this id
107
+ */
108
+ decode(bytes, origin, schemaHeader) {
109
+ const id = schemaHeader?.trim().toLowerCase();
110
+ const decoded = this.decoder.decode(bytes, (ref) => this.store.get(ref), (schemaBytes) => {
111
+ const known = id ? this.store.get(id) : undefined;
112
+ return known !== undefined && sameBytes(known.bytes, schemaBytes) ? known : new Schema(schemaBytes); // parsed once per schema while it is sent inline
113
+ });
114
+ if (this.negotiate && origin && decoded.schema !== undefined) {
115
+ if (decoded.reference)
116
+ this.store.touch(origin, decoded.schema.id);
117
+ else
118
+ this.store.learn(origin, id, decoded.schema);
119
+ }
120
+ return decoded;
121
+ }
122
+ resolve(url) {
123
+ const base = this.baseUrl ?? globalThis.location?.href;
124
+ return base === undefined ? new URL(url) : new URL(url, base);
125
+ }
126
+ }
127
+ function originOf(url) {
128
+ try {
129
+ return url ? new URL(url).origin : undefined;
130
+ }
131
+ catch {
132
+ return undefined;
133
+ }
134
+ }
135
+ function isBodyInit(body) {
136
+ return typeof body === 'string' || body instanceof Uint8Array || body instanceof ArrayBuffer || ArrayBuffer.isView(body)
137
+ || (typeof Blob !== 'undefined' && body instanceof Blob)
138
+ || (typeof FormData !== 'undefined' && body instanceof FormData)
139
+ || (typeof URLSearchParams !== 'undefined' && body instanceof URLSearchParams)
140
+ || (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream);
141
+ }
142
+ function sameBytes(a, b) {
143
+ if (a.length !== b.length)
144
+ return false;
145
+ for (let t = 0; t < a.length; t++)
146
+ if (a[t] !== b[t])
147
+ return false;
148
+ return true;
149
+ }
150
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,180 @@
1
+ import { MsgPackReader, ReaderOptions } from './reader.js';
2
+ import type { ModelSet } from './models.js';
3
+ import { PropertyNames } from './names.js';
4
+ import { Kind, Schema, SchemaEntry, TypeExpr } from './schema.js';
5
+ import { DecimalMode } from './values.js';
6
+ export interface DecodeOptions extends ReaderOptions {
7
+ /** long and ulong properties: 'number' (default, as JSON.parse: precision is lost beyond 2^53) or 'bigint' (always a bigint). Untyped values are a bigint only beyond 2^53. */
8
+ int64?: 'number' | 'bigint';
9
+ /** DateTime and DateTimeOffset properties: 'date' (default, a Date) or 'iso' (text: DateTime in UTC, DateTimeOffset with its offset). Untyped values are always a Date. */
10
+ dates?: 'date' | 'iso';
11
+ /**
12
+ * The generated models (lsmsgpack-ts, defineModels): an entry of their type name is read into a new instance of its class (its fields have the values the .NET constructor sets,
13
+ * which a value the data left out keeps), the declared types of its properties are used when the schema has only names. The schema of the payload still decides which property a value is.
14
+ */
15
+ models?: ModelSet;
16
+ /** Classes to create for schema entries (by the type name of the writer), e.g. { Invoice: Invoice }. A property the class does not initialize gets the default. The models come first. */
17
+ classes?: Record<string, new () => object>;
18
+ /**
19
+ * The names of the properties of the objects: 'camelCase' (default, invoiceDate, as System.Text.Json's JsonNamingPolicy.CamelCase names them, the JSON of ASP.NET Core;
20
+ * also key and value of a KeyValuePair) or 'dotnet' (the names of the .NET properties: InvoiceDate, the default of version 2026.10.22). Never dictionary keys or $type.
21
+ * The models give theirs (the names of their fields) when it is left out.
22
+ */
23
+ propertyNames?: PropertyNames;
24
+ /** The data starts with the indexed schema (default true, as the formatters write application/x-lsmsgpack). False: the body only (UseInexedSchema off). */
25
+ indexedSchema?: boolean;
26
+ /**
27
+ * Generate the code that creates and fills the objects of each schema entry (new Function, the property names as string literals), default true: about a third faster,
28
+ * V8 then sees one object shape per place in the code. Without it (or when a Content-Security-Policy does not allow it) a generic loop does the same.
29
+ */
30
+ generateCode?: boolean;
31
+ }
32
+ type Fill = (r: SchemaReader, o: Record<string, unknown>, n: number) => void;
33
+ /** The property that holds the type name of an object read with a type id, and that the encoder writes as the type id. */
34
+ export declare const TypeProperty = "$type";
35
+ /** Reads an object of the array layout (the header of n values is read) and returns it complete. */
36
+ type ReadArray = (r: SchemaReader, n: number) => Record<string, unknown>;
37
+ /** Sets a property, also one named __proto__ (as JSON.parse does: an own property, not the prototype). */
38
+ export declare function setOwn(o: Record<string, unknown>, name: string, value: unknown): void;
39
+ type Read = (r: SchemaReader) => unknown;
40
+ interface Default {
41
+ value: unknown;
42
+ /** For values that must not be shared (Date, objects). */
43
+ make?: () => unknown;
44
+ }
45
+ /** How to read the values of one schema entry. */
46
+ declare class Plan {
47
+ readonly entry: SchemaEntry;
48
+ readonly cls: (new () => object) | undefined;
49
+ readonly model: boolean;
50
+ readonly names: string[];
51
+ readonly reads: Read[];
52
+ readonly values: unknown[];
53
+ readonly makers: ((() => unknown) | undefined)[];
54
+ /** The properties whose default is made per object (Date, objects): null until the values are read, then made where the data had none (see complete). */
55
+ made?: number[];
56
+ readonly isObject: boolean;
57
+ content?: Read;
58
+ private _byName?;
59
+ private createFn?;
60
+ private createTypedFn?;
61
+ fillFn?: Fill;
62
+ /** An object with the defaults, copied by create without generated code (a spread copies an object of the same shape faster than setting each property). */
63
+ private template?;
64
+ /** Makes what create and fill use, once the properties are known: the template, and with generateCode their code. */
65
+ prepare(generateCode: boolean): void;
66
+ /** readArrayFn for plain objects and the models (not for DecodeOptions.classes: their constructors may do more than set fields). */
67
+ private generateReadArray;
68
+ /** The value of a nil in the array layout: what the writer left out (the default of the schema or of the type), not what a class sets. */
69
+ nilValue(t: number): unknown;
70
+ /** Reads an object of the array layout into a new object (see generateReadArray), undefined without generated code. */
71
+ readArrayFn?: ReadArray;
72
+ /** The properties the class does not initialize (they get the defaults of the schema: e.g. a property the server has and the models do not have yet). */
73
+ readonly missing: number[];
74
+ /**
75
+ * @param names the names of the properties in JavaScript (DecodeOptions.propertyNames)
76
+ * @param cls the class of the entry (a model, or one of DecodeOptions.classes)
77
+ * @param model the class is a model: it has $type on its prototype
78
+ */
79
+ constructor(entry: SchemaEntry, names: string[], cls: (new () => object) | undefined, model?: boolean);
80
+ /** A new object with every property at its default (null for the ones that are made, see complete), in the order of the schema. */
81
+ create(): Record<string, unknown>;
82
+ /** create for an object that came with a type id: with its type name in $type (the first property; on instances of your classes after their own). */
83
+ createTyped(): Record<string, unknown>;
84
+ /** Makes the defaults the data did not replace (a Date or struct is never null, so null means not read). */
85
+ complete(o: Record<string, unknown>): Record<string, unknown>;
86
+ /** A new object with every default made (e.g. the default of a struct). */
87
+ createComplete(): Record<string, unknown>;
88
+ indexOf(name: string): number;
89
+ private _byWriterName?;
90
+ /** The index of a property by the name the writer gave it (a key of ObjectLayout.Map without the schema): the .NET name. */
91
+ indexOfWriterName(name: string): number;
92
+ }
93
+ /** The plans of one schema for one set of options, made when first used. */
94
+ declare class Plans {
95
+ readonly schema: Schema;
96
+ private readonly options;
97
+ private readonly plans;
98
+ private readonly inline;
99
+ readonly root: Read;
100
+ private readonly field;
101
+ private readonly keyName;
102
+ private readonly valueName;
103
+ constructor(schema: Schema, options: ResolvedOptions);
104
+ plan(index: number): Plan;
105
+ /** The plan of an entry by index, or of a struct described inline. */
106
+ planOf(entry: number | SchemaEntry): Plan;
107
+ private build;
108
+ /** A read function for values of the type (each handles nil and values wrapped with a type id). */
109
+ compile(type: TypeExpr): Read;
110
+ /** The kinds the formatters write most have their usual format read in place, without the dispatch of value(). */
111
+ private kind;
112
+ /** Converts a value of the kind as it was read (undefined: no conversion). Values in another form are returned as they are. */
113
+ converter(kind: Kind): ((value: unknown) => unknown) | undefined;
114
+ /** The value of a property the writer left out: the default of its type. */
115
+ defaultOf(type: TypeExpr): Default;
116
+ /** A [DefaultValue] of the schema (as read: decimals raw), converted as the values of the property are. */
117
+ private convertDefault;
118
+ }
119
+ interface ResolvedOptions {
120
+ decimal: DecimalMode;
121
+ int64: 'number' | 'bigint';
122
+ dates: 'date' | 'iso';
123
+ models?: ModelSet;
124
+ classes?: Record<string, new () => object>;
125
+ propertyNames: PropertyNames;
126
+ generateCode: boolean;
127
+ }
128
+ /** The reader of a body: values wrapped with a type id ({ "": index, "@": content }, or { "": index, 0: value, ... }) become what their schema entry describes. */
129
+ export declare class SchemaReader extends MsgPackReader {
130
+ private readonly plans;
131
+ constructor(bytes: Uint8Array, options: ReaderOptions, plans: Plans | null, pos: number);
132
+ /** The first key of the map whose header was just read is "" (a type id). */
133
+ private firstKeyIsTypeId;
134
+ protected map(n: number): unknown;
135
+ /** A value wrapped with a type id, the header (n entries) is read. */
136
+ private wrapped;
137
+ /** An object of the plan: an array of its values (the array layout), a map from property index to value, or a value with a type id. */
138
+ readObject(plan: Plan): unknown;
139
+ /**
140
+ * Values by position, values beyond the entry are skipped. A nil is the value the writer left out (Plan.nilValue: the object made with the defaults has it, an instance of a class gets it),
141
+ * a value missing after the end of the array keeps what create made (for a class: what its constructor sets).
142
+ */
143
+ private fill;
144
+ /** ObjectLayout.Map: the key is the index of the property in the entry (or its name). */
145
+ private setByKey;
146
+ readList(element: Read): unknown;
147
+ readDictionary(key: Read, value: Read): unknown;
148
+ /** An array of items of the given types (pairs, tuples), extra items as they are. */
149
+ readItems(items: Read[]): unknown;
150
+ /** A DateTime: the timestamps of 4 and 8 bytes read in place. */
151
+ readDate(): unknown;
152
+ readRoot(): unknown;
153
+ }
154
+ /** What decoding a payload found. */
155
+ export interface Decoded {
156
+ value: unknown;
157
+ /** The schema of the payload (inline or referred to), undefined without one. */
158
+ schema?: Schema;
159
+ /** The body referred to its schema instead of containing it. */
160
+ reference: boolean;
161
+ }
162
+ /** Decodes payloads with the given options (one per client). Thread-safe in the sense of JavaScript: no state between calls except the plans per schema. */
163
+ export declare class Decoder {
164
+ private readonly options;
165
+ private readonly readerOptions;
166
+ private readonly indexedSchema;
167
+ private readonly plans;
168
+ constructor(options?: DecodeOptions);
169
+ /**
170
+ * Decodes application/x-lsmsgpack data: the schema (or a reference to it) followed by the body.
171
+ * @param lookup finds a schema the data refers to by its id
172
+ * @param inline makes the schema of the inline bytes (e.g. the known instance when the bytes are the same), default: parses them
173
+ */
174
+ decode(bytes: Uint8Array, lookup?: (id: string) => Schema | undefined, inline?: (schemaBytes: Uint8Array) => Schema): Decoded;
175
+ /** Plain MsgPack (application/msgpack): any value, maps as objects. */
176
+ decodePlain(bytes: Uint8Array): unknown;
177
+ private read;
178
+ private plansOf;
179
+ }
180
+ export {};