@nmtjs/protocol 0.15.0-beta.1 → 0.15.0-beta.2
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/client/format.js +2 -0
- package/dist/client/index.js +6 -0
- package/dist/client/protocol.js +24 -0
- package/dist/client/stream.js +189 -0
- package/dist/client/versions/v1.js +127 -0
- package/dist/common/binary.js +39 -0
- package/dist/common/blob.js +66 -0
- package/dist/common/constants.js +1 -0
- package/dist/common/enums.js +58 -0
- package/dist/common/index.js +6 -0
- package/dist/common/types.js +1 -0
- package/dist/common/utils.js +6 -0
- package/dist/server/format.js +62 -0
- package/dist/server/index.js +8 -0
- package/dist/server/protocol.js +24 -0
- package/dist/server/stream.js +41 -0
- package/dist/server/types.js +1 -0
- package/dist/server/utils.js +15 -0
- package/dist/server/versions/v1.js +118 -0
- package/package.json +9 -9
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "./format.js";
|
|
2
|
+
export * from "./protocol.js";
|
|
3
|
+
export * from "./stream.js";
|
|
4
|
+
import { ProtocolVersion } from "../common/enums.js";
|
|
5
|
+
import { ProtocolVersion1 } from "./versions/v1.js";
|
|
6
|
+
export const versions = { [ProtocolVersion.v1]: new ProtocolVersion1() };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { concat } from "../common/binary.js";
|
|
2
|
+
export class ProtocolVersionInterface {
|
|
3
|
+
encode(...chunks) {
|
|
4
|
+
return concat(...chunks);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class ProtocolError extends Error {
|
|
8
|
+
code;
|
|
9
|
+
data;
|
|
10
|
+
constructor(code, message, data) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.data = data;
|
|
14
|
+
}
|
|
15
|
+
get message() {
|
|
16
|
+
return `${this.code} ${super.message}`;
|
|
17
|
+
}
|
|
18
|
+
toString() {
|
|
19
|
+
return `${this.code} ${this.message}`;
|
|
20
|
+
}
|
|
21
|
+
toJSON() {
|
|
22
|
+
return { code: this.code, message: this.message, data: this.data };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { DuplexStream } from '@nmtjs/common';
|
|
2
|
+
import { concat, decodeText, encodeText } from "../common/binary.js";
|
|
3
|
+
import { kBlobKey } from "../common/constants.js";
|
|
4
|
+
export class ProtocolClientBlobStream extends DuplexStream {
|
|
5
|
+
source;
|
|
6
|
+
id;
|
|
7
|
+
metadata;
|
|
8
|
+
[kBlobKey] = true;
|
|
9
|
+
#queue;
|
|
10
|
+
#reader;
|
|
11
|
+
#sourceReader = null;
|
|
12
|
+
constructor(source, id, metadata) {
|
|
13
|
+
let sourceReader = null;
|
|
14
|
+
super({
|
|
15
|
+
start: () => {
|
|
16
|
+
sourceReader = source.getReader();
|
|
17
|
+
},
|
|
18
|
+
pull: async (controller) => {
|
|
19
|
+
const { done, value } = await sourceReader.read();
|
|
20
|
+
if (done) {
|
|
21
|
+
controller.close();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const chunk = value;
|
|
25
|
+
controller.enqueue(chunk instanceof Uint8Array
|
|
26
|
+
? chunk
|
|
27
|
+
: new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));
|
|
28
|
+
},
|
|
29
|
+
transform: (chunk) => {
|
|
30
|
+
if (chunk instanceof ArrayBuffer) {
|
|
31
|
+
return new Uint8Array(chunk);
|
|
32
|
+
}
|
|
33
|
+
else if (chunk instanceof Uint8Array) {
|
|
34
|
+
return chunk;
|
|
35
|
+
}
|
|
36
|
+
else if (typeof chunk === 'string') {
|
|
37
|
+
return encodeText(chunk);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
throw new Error('Invalid chunk data type. Expected ArrayBuffer, Uint8Array, or string.');
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
cancel: (reason) => {
|
|
44
|
+
// Use reader.cancel() if reader exists (stream is locked), otherwise source.cancel()
|
|
45
|
+
if (sourceReader) {
|
|
46
|
+
sourceReader.cancel(reason);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
source.cancel(reason);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
this.source = source;
|
|
54
|
+
this.id = id;
|
|
55
|
+
this.metadata = metadata;
|
|
56
|
+
this.#queue = new Uint8Array(0);
|
|
57
|
+
this.#reader = this.readable.getReader();
|
|
58
|
+
this.#sourceReader = sourceReader;
|
|
59
|
+
}
|
|
60
|
+
async abort(reason = 'Stream aborted') {
|
|
61
|
+
await this.#reader.cancel(reason);
|
|
62
|
+
this.#reader.releaseLock();
|
|
63
|
+
this.#sourceReader?.releaseLock();
|
|
64
|
+
}
|
|
65
|
+
async end() {
|
|
66
|
+
// Release the reader lock when the stream is finished
|
|
67
|
+
this.#reader.releaseLock();
|
|
68
|
+
this.#sourceReader?.releaseLock();
|
|
69
|
+
}
|
|
70
|
+
async read(size) {
|
|
71
|
+
while (this.#queue.byteLength < size) {
|
|
72
|
+
const { done, value } = await this.#reader.read();
|
|
73
|
+
if (done) {
|
|
74
|
+
if (this.#queue.byteLength > 0) {
|
|
75
|
+
const chunk = this.#queue;
|
|
76
|
+
this.#queue = new Uint8Array(0);
|
|
77
|
+
return chunk;
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const buffer = value;
|
|
82
|
+
this.#queue = concat(this.#queue, buffer);
|
|
83
|
+
}
|
|
84
|
+
const chunk = this.#queue.subarray(0, size);
|
|
85
|
+
this.#queue = this.#queue.subarray(size);
|
|
86
|
+
return chunk;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export class ProtocolServerStreamInterface extends DuplexStream {
|
|
90
|
+
async *[Symbol.asyncIterator]() {
|
|
91
|
+
const reader = this.readable.getReader();
|
|
92
|
+
try {
|
|
93
|
+
while (true) {
|
|
94
|
+
const { done, value } = await reader.read();
|
|
95
|
+
if (!done)
|
|
96
|
+
yield value;
|
|
97
|
+
else
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
reader.releaseLock();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export class ProtocolServerStream extends ProtocolServerStreamInterface {
|
|
107
|
+
}
|
|
108
|
+
export class ProtocolServerRPCStream extends ProtocolServerStream {
|
|
109
|
+
createAsyncIterable(onDone) {
|
|
110
|
+
return {
|
|
111
|
+
[Symbol.asyncIterator]: () => {
|
|
112
|
+
const iterator = this[Symbol.asyncIterator]();
|
|
113
|
+
return {
|
|
114
|
+
async next() {
|
|
115
|
+
const result = await iterator.next();
|
|
116
|
+
if (result.done)
|
|
117
|
+
onDone();
|
|
118
|
+
return result;
|
|
119
|
+
},
|
|
120
|
+
async return(value) {
|
|
121
|
+
onDone();
|
|
122
|
+
return iterator.return?.(value) ?? { done: true, value };
|
|
123
|
+
},
|
|
124
|
+
async throw(error) {
|
|
125
|
+
onDone();
|
|
126
|
+
return iterator.throw?.(error) ?? Promise.reject(error);
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export class ProtocolServerBlobStream extends ProtocolServerStreamInterface {
|
|
134
|
+
metadata;
|
|
135
|
+
[kBlobKey] = true;
|
|
136
|
+
constructor(metadata, options) {
|
|
137
|
+
super(options);
|
|
138
|
+
this.metadata = metadata;
|
|
139
|
+
}
|
|
140
|
+
get size() {
|
|
141
|
+
return this.metadata.size || Number.NaN;
|
|
142
|
+
}
|
|
143
|
+
get type() {
|
|
144
|
+
return this.metadata.type || 'application/octet-stream';
|
|
145
|
+
}
|
|
146
|
+
async text() {
|
|
147
|
+
const chunks = [];
|
|
148
|
+
for await (const chunk of this)
|
|
149
|
+
chunks.push(chunk);
|
|
150
|
+
return decodeText(concat(...chunks));
|
|
151
|
+
}
|
|
152
|
+
async bytes() {
|
|
153
|
+
const chunks = [];
|
|
154
|
+
for await (const chunk of this)
|
|
155
|
+
chunks.push(chunk);
|
|
156
|
+
return concat(...chunks);
|
|
157
|
+
}
|
|
158
|
+
async arrayBuffer() {
|
|
159
|
+
const bytes = await this.bytes();
|
|
160
|
+
return bytes.buffer;
|
|
161
|
+
}
|
|
162
|
+
async json() {
|
|
163
|
+
const text = await this.text();
|
|
164
|
+
return JSON.parse(text);
|
|
165
|
+
}
|
|
166
|
+
stream() {
|
|
167
|
+
const transform = new TransformStream({
|
|
168
|
+
transform: (chunk, controller) => {
|
|
169
|
+
controller.enqueue(chunk instanceof Uint8Array
|
|
170
|
+
? chunk
|
|
171
|
+
: new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
this.readable.pipeThrough(transform);
|
|
175
|
+
return transform.readable;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Throws an error
|
|
179
|
+
*/
|
|
180
|
+
async formData() {
|
|
181
|
+
throw new Error('Method not implemented.');
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Throws an error
|
|
185
|
+
*/
|
|
186
|
+
slice() {
|
|
187
|
+
throw new Error('Unable to slice');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { decodeNumber, decodeText, encodeNumber, encodeText, } from "../../common/binary.js";
|
|
2
|
+
import { ClientMessageType, MessageByteLength, ProtocolVersion, ServerMessageType, } from "../../common/enums.js";
|
|
3
|
+
import { ProtocolVersionInterface } from "../protocol.js";
|
|
4
|
+
export class ProtocolVersion1 extends ProtocolVersionInterface {
|
|
5
|
+
version = ProtocolVersion.v1;
|
|
6
|
+
decodeMessage(context, buffer) {
|
|
7
|
+
const messageType = decodeNumber(buffer, 'Uint8');
|
|
8
|
+
const payload = buffer.subarray(MessageByteLength.MessageType);
|
|
9
|
+
switch (messageType) {
|
|
10
|
+
// case ServerMessageType.Event: {
|
|
11
|
+
// const { event, data } = context.decoder.decode(payload)
|
|
12
|
+
// return { type: messageType, event, data }
|
|
13
|
+
// }
|
|
14
|
+
case ServerMessageType.RpcResponse: {
|
|
15
|
+
const callId = decodeNumber(payload, 'Uint32');
|
|
16
|
+
const isError = decodeNumber(payload, 'Uint8', MessageByteLength.CallId);
|
|
17
|
+
const dataPayload = payload.subarray(MessageByteLength.CallId + MessageByteLength.MessageError);
|
|
18
|
+
if (isError) {
|
|
19
|
+
const error = context.decoder.decode(dataPayload);
|
|
20
|
+
return { type: messageType, callId, error };
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
const result = context.decoder.decodeRPC(dataPayload, {
|
|
24
|
+
addStream: (streamId, metadata) => {
|
|
25
|
+
return context.addServerStream(streamId, metadata);
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
return { type: messageType, callId, result };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
case ServerMessageType.RpcStreamResponse: {
|
|
32
|
+
const callId = decodeNumber(payload, 'Uint32');
|
|
33
|
+
const errorPayload = payload.subarray(MessageByteLength.CallId);
|
|
34
|
+
const error = errorPayload.byteLength > 0
|
|
35
|
+
? context.decoder.decode(errorPayload)
|
|
36
|
+
: undefined;
|
|
37
|
+
return { type: messageType, callId, error };
|
|
38
|
+
}
|
|
39
|
+
case ServerMessageType.RpcStreamChunk: {
|
|
40
|
+
const callId = decodeNumber(payload, 'Uint32');
|
|
41
|
+
const chunk = payload.subarray(MessageByteLength.CallId);
|
|
42
|
+
return { type: messageType, callId, chunk };
|
|
43
|
+
}
|
|
44
|
+
case ServerMessageType.RpcStreamEnd: {
|
|
45
|
+
const callId = decodeNumber(payload, 'Uint32');
|
|
46
|
+
return { type: messageType, callId };
|
|
47
|
+
}
|
|
48
|
+
case ServerMessageType.RpcStreamAbort: {
|
|
49
|
+
const callId = decodeNumber(payload, 'Uint32');
|
|
50
|
+
const reasonPayload = payload.subarray(MessageByteLength.CallId);
|
|
51
|
+
const reason = reasonPayload.byteLength > 0 ? decodeText(reasonPayload) : undefined;
|
|
52
|
+
return { type: messageType, callId, reason };
|
|
53
|
+
}
|
|
54
|
+
case ServerMessageType.ClientStreamPull: {
|
|
55
|
+
const streamId = decodeNumber(payload, 'Uint32');
|
|
56
|
+
const size = decodeNumber(payload, 'Uint32', MessageByteLength.StreamId);
|
|
57
|
+
return { type: messageType, streamId, size };
|
|
58
|
+
}
|
|
59
|
+
case ServerMessageType.ClientStreamAbort: {
|
|
60
|
+
const streamId = decodeNumber(payload, 'Uint32');
|
|
61
|
+
const reasonPayload = payload.subarray(MessageByteLength.StreamId);
|
|
62
|
+
const reason = reasonPayload.byteLength > 0 ? decodeText(reasonPayload) : undefined;
|
|
63
|
+
return { type: messageType, streamId, reason };
|
|
64
|
+
}
|
|
65
|
+
case ServerMessageType.ServerStreamPush: {
|
|
66
|
+
const streamId = decodeNumber(payload, 'Uint32');
|
|
67
|
+
const chunk = payload.subarray(MessageByteLength.StreamId);
|
|
68
|
+
return { type: messageType, streamId, chunk };
|
|
69
|
+
}
|
|
70
|
+
case ServerMessageType.ServerStreamEnd: {
|
|
71
|
+
const streamId = decodeNumber(payload, 'Uint32');
|
|
72
|
+
const reasonPayload = payload.subarray(MessageByteLength.StreamId);
|
|
73
|
+
const reason = reasonPayload.byteLength > 0 ? decodeText(reasonPayload) : undefined;
|
|
74
|
+
return { type: messageType, streamId, reason };
|
|
75
|
+
}
|
|
76
|
+
case ServerMessageType.ServerStreamAbort: {
|
|
77
|
+
const streamId = decodeNumber(payload, 'Uint32');
|
|
78
|
+
const reasonPayload = payload.subarray(MessageByteLength.StreamId);
|
|
79
|
+
const reason = reasonPayload.byteLength > 0 ? decodeText(reasonPayload) : undefined;
|
|
80
|
+
return { type: messageType, streamId, reason };
|
|
81
|
+
}
|
|
82
|
+
default:
|
|
83
|
+
throw new Error(`Unsupported message type: ${messageType}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
encodeMessage(context, messageType, payload) {
|
|
87
|
+
switch (messageType) {
|
|
88
|
+
case ClientMessageType.Rpc: {
|
|
89
|
+
const { callId, procedure, payload: rpcPayload, } = payload;
|
|
90
|
+
const procedureBuffer = encodeText(procedure);
|
|
91
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(callId, 'Uint32'), encodeNumber(procedureBuffer.byteLength, 'Uint16'), procedureBuffer, context.encoder.encodeRPC(rpcPayload, {
|
|
92
|
+
addStream: (blob) => context.addClientStream(blob),
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
case ClientMessageType.RpcAbort: {
|
|
96
|
+
const { callId, reason } = payload;
|
|
97
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(callId, 'Uint32'), reason ? encodeText(reason) : new Uint8Array(0));
|
|
98
|
+
}
|
|
99
|
+
case ClientMessageType.RpcPull: {
|
|
100
|
+
const { callId } = payload;
|
|
101
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(callId, 'Uint32'));
|
|
102
|
+
}
|
|
103
|
+
case ClientMessageType.ClientStreamPush: {
|
|
104
|
+
const { streamId, chunk } = payload;
|
|
105
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(streamId, 'Uint32'), chunk);
|
|
106
|
+
}
|
|
107
|
+
case ClientMessageType.ClientStreamEnd: {
|
|
108
|
+
const { streamId } = payload;
|
|
109
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(streamId, 'Uint32'));
|
|
110
|
+
}
|
|
111
|
+
case ClientMessageType.ClientStreamAbort: {
|
|
112
|
+
const { streamId, reason } = payload;
|
|
113
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(streamId, 'Uint32'), reason ? encodeText(reason) : new Uint8Array(0));
|
|
114
|
+
}
|
|
115
|
+
case ClientMessageType.ServerStreamPull: {
|
|
116
|
+
const { streamId, size } = payload;
|
|
117
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(streamId, 'Uint32'), encodeNumber(size, 'Uint32'));
|
|
118
|
+
}
|
|
119
|
+
case ClientMessageType.ServerStreamAbort: {
|
|
120
|
+
const { streamId, reason } = payload;
|
|
121
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(streamId, 'Uint32'), reason ? encodeText(reason) : new Uint8Array(0));
|
|
122
|
+
}
|
|
123
|
+
default:
|
|
124
|
+
throw new Error(`Unsupported message type: ${messageType}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// TODO: get rid of lib DOM somehow...
|
|
2
|
+
/// <reference lib="dom" />
|
|
3
|
+
const utf8decoder = new TextDecoder();
|
|
4
|
+
const utf8encoder = new TextEncoder();
|
|
5
|
+
export const encodeNumber = (value, type, littleEndian = true) => {
|
|
6
|
+
const bytesNeeded = globalThis[`${type}Array`].BYTES_PER_ELEMENT;
|
|
7
|
+
const ab = new ArrayBuffer(bytesNeeded);
|
|
8
|
+
const dv = new DataView(ab);
|
|
9
|
+
dv[`set${type}`](0, value, littleEndian);
|
|
10
|
+
return ab;
|
|
11
|
+
};
|
|
12
|
+
export const decodeNumber = (buffer, type, offset = 0, littleEndian = true) => {
|
|
13
|
+
const ab = buffer instanceof ArrayBuffer ? buffer : buffer.buffer;
|
|
14
|
+
const baseOffset = buffer instanceof ArrayBuffer ? 0 : buffer.byteOffset;
|
|
15
|
+
const view = new DataView(ab);
|
|
16
|
+
return view[`get${type}`](baseOffset + offset, littleEndian);
|
|
17
|
+
};
|
|
18
|
+
export const encodeText = (text) => utf8encoder.encode(text);
|
|
19
|
+
export const decodeText = (buffer) => utf8decoder.decode(buffer);
|
|
20
|
+
export const concat = (...buffers) => {
|
|
21
|
+
let totalLength = 0;
|
|
22
|
+
for (const buffer of buffers)
|
|
23
|
+
totalLength += buffer.byteLength;
|
|
24
|
+
const view = new Uint8Array(totalLength);
|
|
25
|
+
let offset = 0;
|
|
26
|
+
for (const buffer of buffers) {
|
|
27
|
+
const chunk = buffer instanceof ArrayBuffer
|
|
28
|
+
? new Uint8Array(buffer)
|
|
29
|
+
: new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
30
|
+
view.set(chunk, offset);
|
|
31
|
+
offset += chunk.byteLength;
|
|
32
|
+
}
|
|
33
|
+
return view;
|
|
34
|
+
};
|
|
35
|
+
export const UTF8Transform = () => new TransformStream({
|
|
36
|
+
transform(chunk, controller) {
|
|
37
|
+
controller.enqueue(encodeText(chunk));
|
|
38
|
+
},
|
|
39
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { kBlobKey } from "./constants.js";
|
|
2
|
+
export class ProtocolBlob {
|
|
3
|
+
[kBlobKey] = true;
|
|
4
|
+
source;
|
|
5
|
+
metadata;
|
|
6
|
+
encode;
|
|
7
|
+
toJSON;
|
|
8
|
+
constructor({ source, encode, size, type = 'application/octet-stream', filename, }) {
|
|
9
|
+
if (typeof size !== 'undefined' && size <= 0)
|
|
10
|
+
throw new Error('Blob size is invalid');
|
|
11
|
+
this.encode = encode;
|
|
12
|
+
this.source = source;
|
|
13
|
+
this.metadata = { size, type, filename };
|
|
14
|
+
if (encode) {
|
|
15
|
+
Object.defineProperty(this, 'toJSON', {
|
|
16
|
+
configurable: false,
|
|
17
|
+
enumerable: false,
|
|
18
|
+
writable: false,
|
|
19
|
+
value: encode,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
static from(_source, _metadata = {}, _encode) {
|
|
24
|
+
let source;
|
|
25
|
+
const metadata = { ..._metadata };
|
|
26
|
+
if (_source instanceof globalThis.ReadableStream) {
|
|
27
|
+
source = _source;
|
|
28
|
+
}
|
|
29
|
+
else if ('File' in globalThis && _source instanceof globalThis.File) {
|
|
30
|
+
source = _source.stream();
|
|
31
|
+
metadata.size ??= _source.size;
|
|
32
|
+
metadata.filename ??= _source.name;
|
|
33
|
+
}
|
|
34
|
+
else if (_source instanceof globalThis.Blob) {
|
|
35
|
+
source = _source.stream();
|
|
36
|
+
metadata.size ??= _source.size;
|
|
37
|
+
metadata.type ??= _source.type;
|
|
38
|
+
}
|
|
39
|
+
else if (typeof _source === 'string') {
|
|
40
|
+
const blob = new Blob([_source]);
|
|
41
|
+
source = blob.stream();
|
|
42
|
+
metadata.size ??= blob.size;
|
|
43
|
+
metadata.type ??= 'text/plain';
|
|
44
|
+
}
|
|
45
|
+
else if (globalThis.ArrayBuffer.isView(_source)) {
|
|
46
|
+
const blob = new Blob([_source]);
|
|
47
|
+
source = blob.stream();
|
|
48
|
+
metadata.size ??= blob.size;
|
|
49
|
+
}
|
|
50
|
+
else if (_source instanceof globalThis.ArrayBuffer) {
|
|
51
|
+
const blob = new Blob([_source]);
|
|
52
|
+
source = blob.stream();
|
|
53
|
+
metadata.size ??= blob.size;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
source = _source;
|
|
57
|
+
}
|
|
58
|
+
return new ProtocolBlob({
|
|
59
|
+
source,
|
|
60
|
+
encode: _encode,
|
|
61
|
+
size: metadata.size,
|
|
62
|
+
type: metadata.type,
|
|
63
|
+
filename: metadata.filename,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const kBlobKey = Symbol.for('neemata:blobKey');
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export var ProtocolVersion;
|
|
2
|
+
(function (ProtocolVersion) {
|
|
3
|
+
ProtocolVersion[ProtocolVersion["v1"] = 1] = "v1";
|
|
4
|
+
})(ProtocolVersion || (ProtocolVersion = {}));
|
|
5
|
+
export var ClientMessageType;
|
|
6
|
+
(function (ClientMessageType) {
|
|
7
|
+
ClientMessageType[ClientMessageType["Rpc"] = 10] = "Rpc";
|
|
8
|
+
ClientMessageType[ClientMessageType["RpcAbort"] = 11] = "RpcAbort";
|
|
9
|
+
ClientMessageType[ClientMessageType["RpcPull"] = 12] = "RpcPull";
|
|
10
|
+
ClientMessageType[ClientMessageType["ClientStreamPush"] = 20] = "ClientStreamPush";
|
|
11
|
+
ClientMessageType[ClientMessageType["ClientStreamEnd"] = 21] = "ClientStreamEnd";
|
|
12
|
+
ClientMessageType[ClientMessageType["ClientStreamAbort"] = 22] = "ClientStreamAbort";
|
|
13
|
+
ClientMessageType[ClientMessageType["ServerStreamAbort"] = 33] = "ServerStreamAbort";
|
|
14
|
+
ClientMessageType[ClientMessageType["ServerStreamPull"] = 34] = "ServerStreamPull";
|
|
15
|
+
})(ClientMessageType || (ClientMessageType = {}));
|
|
16
|
+
export var ServerMessageType;
|
|
17
|
+
(function (ServerMessageType) {
|
|
18
|
+
// Event = 1,
|
|
19
|
+
ServerMessageType[ServerMessageType["RpcResponse"] = 10] = "RpcResponse";
|
|
20
|
+
ServerMessageType[ServerMessageType["RpcStreamResponse"] = 11] = "RpcStreamResponse";
|
|
21
|
+
ServerMessageType[ServerMessageType["RpcStreamChunk"] = 12] = "RpcStreamChunk";
|
|
22
|
+
ServerMessageType[ServerMessageType["RpcStreamEnd"] = 13] = "RpcStreamEnd";
|
|
23
|
+
ServerMessageType[ServerMessageType["RpcStreamAbort"] = 14] = "RpcStreamAbort";
|
|
24
|
+
ServerMessageType[ServerMessageType["ServerStreamPush"] = 20] = "ServerStreamPush";
|
|
25
|
+
ServerMessageType[ServerMessageType["ServerStreamEnd"] = 21] = "ServerStreamEnd";
|
|
26
|
+
ServerMessageType[ServerMessageType["ServerStreamAbort"] = 22] = "ServerStreamAbort";
|
|
27
|
+
ServerMessageType[ServerMessageType["ClientStreamAbort"] = 33] = "ClientStreamAbort";
|
|
28
|
+
ServerMessageType[ServerMessageType["ClientStreamPull"] = 34] = "ClientStreamPull";
|
|
29
|
+
})(ServerMessageType || (ServerMessageType = {}));
|
|
30
|
+
export var ConnectionType;
|
|
31
|
+
(function (ConnectionType) {
|
|
32
|
+
ConnectionType["Bidirectional"] = "Bidirectional";
|
|
33
|
+
ConnectionType["Unidirectional"] = "Unidirectional";
|
|
34
|
+
})(ConnectionType || (ConnectionType = {}));
|
|
35
|
+
export var ErrorCode;
|
|
36
|
+
(function (ErrorCode) {
|
|
37
|
+
ErrorCode["ValidationError"] = "ValidationError";
|
|
38
|
+
ErrorCode["BadRequest"] = "BadRequest";
|
|
39
|
+
ErrorCode["NotFound"] = "NotFound";
|
|
40
|
+
ErrorCode["Forbidden"] = "Forbidden";
|
|
41
|
+
ErrorCode["Unauthorized"] = "Unauthorized";
|
|
42
|
+
ErrorCode["InternalServerError"] = "InternalServerError";
|
|
43
|
+
ErrorCode["NotAcceptable"] = "NotAcceptable";
|
|
44
|
+
ErrorCode["RequestTimeout"] = "RequestTimeout";
|
|
45
|
+
ErrorCode["GatewayTimeout"] = "GatewayTimeout";
|
|
46
|
+
ErrorCode["ServiceUnavailable"] = "ServiceUnavailable";
|
|
47
|
+
ErrorCode["ClientRequestError"] = "ClientRequestError";
|
|
48
|
+
ErrorCode["ConnectionError"] = "ConnectionError";
|
|
49
|
+
})(ErrorCode || (ErrorCode = {}));
|
|
50
|
+
export var MessageByteLength;
|
|
51
|
+
(function (MessageByteLength) {
|
|
52
|
+
MessageByteLength[MessageByteLength["MessageType"] = 1] = "MessageType";
|
|
53
|
+
MessageByteLength[MessageByteLength["MessageError"] = 1] = "MessageError";
|
|
54
|
+
MessageByteLength[MessageByteLength["ProcedureLength"] = 2] = "ProcedureLength";
|
|
55
|
+
MessageByteLength[MessageByteLength["CallId"] = 4] = "CallId";
|
|
56
|
+
MessageByteLength[MessageByteLength["StreamId"] = 4] = "StreamId";
|
|
57
|
+
MessageByteLength[MessageByteLength["ChunkSize"] = 4] = "ChunkSize";
|
|
58
|
+
})(MessageByteLength || (MessageByteLength = {}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { match } from '@nmtjs/common';
|
|
2
|
+
export class BaseServerFormat {
|
|
3
|
+
}
|
|
4
|
+
export const parseContentTypes = (types) => {
|
|
5
|
+
const normalized = types.trim();
|
|
6
|
+
if (normalized === '*/*')
|
|
7
|
+
return ['*/*'];
|
|
8
|
+
return normalized
|
|
9
|
+
.split(',')
|
|
10
|
+
.map((t) => t.trim())
|
|
11
|
+
.map((t) => {
|
|
12
|
+
const [rawType, ...rest] = t.split(';');
|
|
13
|
+
const params = new Map(rest.map((p) => p
|
|
14
|
+
.trim()
|
|
15
|
+
.split('=')
|
|
16
|
+
.slice(0, 2)
|
|
17
|
+
.map((part) => part.trim())));
|
|
18
|
+
return {
|
|
19
|
+
type: rawType.trim(),
|
|
20
|
+
q: params.has('q') ? Number.parseFloat(params.get('q')) : 1,
|
|
21
|
+
};
|
|
22
|
+
})
|
|
23
|
+
.sort((a, b) => {
|
|
24
|
+
if (a.type === '*/*')
|
|
25
|
+
return 1;
|
|
26
|
+
if (b.type === '*/*')
|
|
27
|
+
return -1;
|
|
28
|
+
return b.q - a.q;
|
|
29
|
+
})
|
|
30
|
+
.map((t) => t.type);
|
|
31
|
+
};
|
|
32
|
+
export class ProtocolFormats {
|
|
33
|
+
decoders = new Map();
|
|
34
|
+
encoders = new Map();
|
|
35
|
+
constructor(formats) {
|
|
36
|
+
for (const format of formats) {
|
|
37
|
+
this.encoders.set(format.contentType, format);
|
|
38
|
+
for (const acceptType of format.accept) {
|
|
39
|
+
this.decoders.set(acceptType, format);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
supportsDecoder(contentType, throwIfUnsupported = false) {
|
|
44
|
+
return this.supports(this.decoders, contentType, throwIfUnsupported);
|
|
45
|
+
}
|
|
46
|
+
supportsEncoder(contentType, throwIfUnsupported = false) {
|
|
47
|
+
return this.supports(this.encoders, contentType, throwIfUnsupported);
|
|
48
|
+
}
|
|
49
|
+
supports(formats, contentType, throwIfUnsupported = false) {
|
|
50
|
+
// TODO: Use node:utils.MIMEType (not implemented yet in Deno and Bun yet)
|
|
51
|
+
const types = parseContentTypes(contentType);
|
|
52
|
+
for (const type of types) {
|
|
53
|
+
for (const [pattern, format] of formats) {
|
|
54
|
+
if (type === '*/*' || match(type, pattern))
|
|
55
|
+
return format;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (throwIfUnsupported)
|
|
59
|
+
throw new Error(`No supported format found: ${contentType}`);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./format.js";
|
|
2
|
+
export * from "./protocol.js";
|
|
3
|
+
export * from "./stream.js";
|
|
4
|
+
export * from "./types.js";
|
|
5
|
+
export * from "./utils.js";
|
|
6
|
+
import { ProtocolVersion } from "../common/enums.js";
|
|
7
|
+
import { ProtocolVersion1 } from "./versions/v1.js";
|
|
8
|
+
export const versions = { [ProtocolVersion.v1]: new ProtocolVersion1() };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { concat } from "../common/binary.js";
|
|
2
|
+
export class ProtocolError extends Error {
|
|
3
|
+
code;
|
|
4
|
+
data;
|
|
5
|
+
constructor(code, message, data) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.data = data;
|
|
9
|
+
}
|
|
10
|
+
get message() {
|
|
11
|
+
return `${this.code} ${super.message}`;
|
|
12
|
+
}
|
|
13
|
+
toString() {
|
|
14
|
+
return `${this.code} ${this.message}`;
|
|
15
|
+
}
|
|
16
|
+
toJSON() {
|
|
17
|
+
return { code: this.code, message: this.message, data: this.data };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class ProtocolVersionInterface {
|
|
21
|
+
encode(...chunks) {
|
|
22
|
+
return concat(...chunks);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { PassThrough, Readable } from 'node:stream';
|
|
2
|
+
import { ReadableStream } from 'node:stream/web';
|
|
3
|
+
export class ProtocolClientStream extends PassThrough {
|
|
4
|
+
id;
|
|
5
|
+
metadata;
|
|
6
|
+
#read;
|
|
7
|
+
constructor(id, metadata, options) {
|
|
8
|
+
const { read, ...rest } = options ?? {};
|
|
9
|
+
super(rest);
|
|
10
|
+
this.id = id;
|
|
11
|
+
this.metadata = metadata;
|
|
12
|
+
this.#read = read;
|
|
13
|
+
}
|
|
14
|
+
_read(size) {
|
|
15
|
+
if (this.#read) {
|
|
16
|
+
this.#read.call(this, size);
|
|
17
|
+
}
|
|
18
|
+
super._read(size);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export class ProtocolServerStream extends PassThrough {
|
|
22
|
+
id;
|
|
23
|
+
metadata;
|
|
24
|
+
constructor(id, blob) {
|
|
25
|
+
let readable;
|
|
26
|
+
if (blob.source instanceof Readable) {
|
|
27
|
+
readable = blob.source;
|
|
28
|
+
}
|
|
29
|
+
else if (blob.source instanceof ReadableStream) {
|
|
30
|
+
readable = Readable.fromWeb(blob.source);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
throw new Error('Invalid source type');
|
|
34
|
+
}
|
|
35
|
+
super();
|
|
36
|
+
this.pause();
|
|
37
|
+
readable.pipe(this);
|
|
38
|
+
this.id = id;
|
|
39
|
+
this.metadata = blob.metadata;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export class UnsupportedFormatError extends Error {
|
|
2
|
+
}
|
|
3
|
+
export class UnsupportedContentTypeError extends UnsupportedFormatError {
|
|
4
|
+
}
|
|
5
|
+
export class UnsupportedAcceptTypeError extends UnsupportedFormatError {
|
|
6
|
+
}
|
|
7
|
+
export const getFormat = (format, { accept, contentType }) => {
|
|
8
|
+
const encoder = contentType ? format.supportsEncoder(contentType) : undefined;
|
|
9
|
+
if (!encoder)
|
|
10
|
+
throw new UnsupportedContentTypeError('Unsupported Content type');
|
|
11
|
+
const decoder = accept ? format.supportsDecoder(accept) : undefined;
|
|
12
|
+
if (!decoder)
|
|
13
|
+
throw new UnsupportedAcceptTypeError('Unsupported Accept type');
|
|
14
|
+
return { encoder, decoder };
|
|
15
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { decodeText, encodeNumber, encodeText } from "../../common/binary.js";
|
|
2
|
+
import { ClientMessageType, MessageByteLength, ProtocolVersion, ServerMessageType, } from "../../common/enums.js";
|
|
3
|
+
import { ProtocolVersionInterface } from "../protocol.js";
|
|
4
|
+
export class ProtocolVersion1 extends ProtocolVersionInterface {
|
|
5
|
+
version = ProtocolVersion.v1;
|
|
6
|
+
decodeMessage(context, buffer) {
|
|
7
|
+
const messageType = buffer.readUint8(0);
|
|
8
|
+
const messagePayload = buffer.subarray(MessageByteLength.MessageType);
|
|
9
|
+
switch (messageType) {
|
|
10
|
+
case ClientMessageType.Rpc: {
|
|
11
|
+
const callId = messagePayload.readUint32LE(0);
|
|
12
|
+
const procedureLength = messagePayload.readUInt16LE(MessageByteLength.CallId);
|
|
13
|
+
const procedureOffset = MessageByteLength.CallId + MessageByteLength.ProcedureLength;
|
|
14
|
+
const procedure = messagePayload.toString('utf-8', procedureOffset, procedureOffset + procedureLength);
|
|
15
|
+
const formatPayload = messagePayload.subarray(procedureOffset + procedureLength);
|
|
16
|
+
const payload = context.decoder.decodeRPC(formatPayload, {
|
|
17
|
+
addStream: (streamId, metadata) => {
|
|
18
|
+
return context.addClientStream({ callId, streamId, metadata });
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
return { type: messageType, rpc: { callId, procedure, payload } };
|
|
22
|
+
}
|
|
23
|
+
case ClientMessageType.RpcPull: {
|
|
24
|
+
const callId = messagePayload.readUInt32LE(0);
|
|
25
|
+
return { type: messageType, callId };
|
|
26
|
+
}
|
|
27
|
+
case ClientMessageType.RpcAbort: {
|
|
28
|
+
const callId = messagePayload.readUInt32LE(0);
|
|
29
|
+
const reasonPayload = messagePayload.subarray(MessageByteLength.CallId);
|
|
30
|
+
const reason = reasonPayload.byteLength > 0 ? decodeText(reasonPayload) : undefined;
|
|
31
|
+
return { type: messageType, callId, reason };
|
|
32
|
+
}
|
|
33
|
+
case ClientMessageType.ServerStreamAbort: {
|
|
34
|
+
const streamId = messagePayload.readUInt32LE(0);
|
|
35
|
+
const reasonPayload = messagePayload.subarray(MessageByteLength.StreamId);
|
|
36
|
+
const reason = reasonPayload.byteLength > 0 ? decodeText(reasonPayload) : undefined;
|
|
37
|
+
return { type: messageType, streamId, reason };
|
|
38
|
+
}
|
|
39
|
+
case ClientMessageType.ServerStreamPull: {
|
|
40
|
+
const streamId = messagePayload.readUInt32LE(0);
|
|
41
|
+
const size = messagePayload.readUInt32LE(MessageByteLength.StreamId);
|
|
42
|
+
return { type: messageType, streamId, size };
|
|
43
|
+
}
|
|
44
|
+
case ClientMessageType.ClientStreamAbort: {
|
|
45
|
+
const streamId = messagePayload.readUInt32LE(0);
|
|
46
|
+
const reasonPayload = messagePayload.subarray(MessageByteLength.StreamId);
|
|
47
|
+
const reason = reasonPayload.byteLength > 0 ? decodeText(reasonPayload) : undefined;
|
|
48
|
+
return { type: messageType, streamId, reason };
|
|
49
|
+
}
|
|
50
|
+
case ClientMessageType.ClientStreamEnd: {
|
|
51
|
+
return { type: messageType, streamId: messagePayload.readUInt32LE(0) };
|
|
52
|
+
}
|
|
53
|
+
case ClientMessageType.ClientStreamPush: {
|
|
54
|
+
const streamId = messagePayload.readUInt32LE(0);
|
|
55
|
+
const chunk = messagePayload.subarray(MessageByteLength.StreamId);
|
|
56
|
+
return { type: messageType, streamId, chunk };
|
|
57
|
+
}
|
|
58
|
+
default:
|
|
59
|
+
throw new Error(`Unsupported message type: ${messageType}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
encodeMessage(context, messageType, payload) {
|
|
63
|
+
switch (messageType) {
|
|
64
|
+
// case ServerMessageType.Event: {
|
|
65
|
+
// const { event, data } =
|
|
66
|
+
// payload as ServerMessageTypePayload[ServerMessageType.Event]
|
|
67
|
+
// return this.encode(
|
|
68
|
+
// encodeNumber(messageType, 'Uint8'),
|
|
69
|
+
// context.encoder.encode({ event, data }),
|
|
70
|
+
// )
|
|
71
|
+
// }
|
|
72
|
+
case ServerMessageType.RpcResponse: {
|
|
73
|
+
const { callId, result, streams, error } = payload;
|
|
74
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(callId, 'Uint32'), encodeNumber(error ? 1 : 0, 'Uint8'), error
|
|
75
|
+
? context.encoder.encode(error)
|
|
76
|
+
: context.encoder.encodeRPC(result, streams));
|
|
77
|
+
}
|
|
78
|
+
case ServerMessageType.RpcStreamResponse: {
|
|
79
|
+
const { callId } = payload;
|
|
80
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(callId, 'Uint32'));
|
|
81
|
+
}
|
|
82
|
+
case ServerMessageType.RpcStreamChunk: {
|
|
83
|
+
const { callId, chunk } = payload;
|
|
84
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(callId, 'Uint32'), chunk);
|
|
85
|
+
}
|
|
86
|
+
case ServerMessageType.RpcStreamEnd: {
|
|
87
|
+
const { callId } = payload;
|
|
88
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(callId, 'Uint32'));
|
|
89
|
+
}
|
|
90
|
+
case ServerMessageType.RpcStreamAbort: {
|
|
91
|
+
const { callId, reason } = payload;
|
|
92
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(callId, 'Uint32'), reason ? encodeText(reason) : Buffer.alloc(0));
|
|
93
|
+
}
|
|
94
|
+
case ServerMessageType.ClientStreamPull: {
|
|
95
|
+
const { size, streamId } = payload;
|
|
96
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(streamId, 'Uint32'), encodeNumber(size, 'Uint32'));
|
|
97
|
+
}
|
|
98
|
+
case ServerMessageType.ClientStreamAbort: {
|
|
99
|
+
const { streamId, reason } = payload;
|
|
100
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(streamId, 'Uint32'), reason ? encodeText(reason) : Buffer.alloc(0));
|
|
101
|
+
}
|
|
102
|
+
case ServerMessageType.ServerStreamPush: {
|
|
103
|
+
const { streamId, chunk } = payload;
|
|
104
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(streamId, 'Uint32'), chunk);
|
|
105
|
+
}
|
|
106
|
+
case ServerMessageType.ServerStreamEnd: {
|
|
107
|
+
const { streamId } = payload;
|
|
108
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(streamId, 'Uint32'));
|
|
109
|
+
}
|
|
110
|
+
case ServerMessageType.ServerStreamAbort: {
|
|
111
|
+
const { streamId, reason } = payload;
|
|
112
|
+
return this.encode(encodeNumber(messageType, 'Uint8'), encodeNumber(streamId, 'Uint32'), reason ? encodeText(reason) : Buffer.alloc(0));
|
|
113
|
+
}
|
|
114
|
+
default:
|
|
115
|
+
throw new Error(`Unsupported message type: ${messageType}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
package/package.json
CHANGED
|
@@ -8,25 +8,25 @@
|
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"hookable": "6.0.0-rc.1",
|
|
11
|
-
"@nmtjs/
|
|
12
|
-
"@nmtjs/contract": "0.15.0-beta.
|
|
13
|
-
"@nmtjs/
|
|
11
|
+
"@nmtjs/type": "0.15.0-beta.2",
|
|
12
|
+
"@nmtjs/contract": "0.15.0-beta.2",
|
|
13
|
+
"@nmtjs/common": "0.15.0-beta.2"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
|
-
"@nmtjs/core": "0.15.0-beta.
|
|
16
|
+
"@nmtjs/core": "0.15.0-beta.2"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"@nmtjs/
|
|
20
|
-
"@nmtjs/
|
|
21
|
-
"@nmtjs/type": "0.15.0-beta.
|
|
22
|
-
"@nmtjs/contract": "0.15.0-beta.
|
|
19
|
+
"@nmtjs/core": "0.15.0-beta.2",
|
|
20
|
+
"@nmtjs/common": "0.15.0-beta.2",
|
|
21
|
+
"@nmtjs/type": "0.15.0-beta.2",
|
|
22
|
+
"@nmtjs/contract": "0.15.0-beta.2"
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
25
|
"dist",
|
|
26
26
|
"LICENSE.md",
|
|
27
27
|
"README.md"
|
|
28
28
|
],
|
|
29
|
-
"version": "0.15.0-beta.
|
|
29
|
+
"version": "0.15.0-beta.2",
|
|
30
30
|
"scripts": {
|
|
31
31
|
"clean-build": "rm -rf ./dist",
|
|
32
32
|
"build": "tsc",
|