@nmtjs/protocol 0.12.6 → 0.12.7

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.
@@ -1,116 +0,0 @@
1
- import { defer } from '@nmtjs/common'
2
- import { concat, encodeText } from '../common/binary.ts'
3
- import type { ProtocolBlobMetadata } from '../common/blob.ts'
4
-
5
- export class ProtocolClientBlobStream extends TransformStream<
6
- any,
7
- ArrayBuffer
8
- > {
9
- #queue: ArrayBuffer
10
- #reader: ReadableStreamDefaultReader
11
-
12
- constructor(
13
- readonly source: ReadableStream,
14
- readonly id: number,
15
- readonly metadata: ProtocolBlobMetadata,
16
- ) {
17
- super({
18
- start: () => {
19
- defer(() => source.pipeThrough(this))
20
- },
21
- transform: (chunk, controller) => {
22
- if (chunk instanceof ArrayBuffer) {
23
- controller.enqueue(chunk)
24
- } else if (chunk instanceof Uint8Array) {
25
- controller.enqueue(chunk.buffer as unknown as ArrayBuffer)
26
- } else if (typeof chunk === 'string') {
27
- controller.enqueue(encodeText(chunk))
28
- } else {
29
- throw new Error(
30
- 'Invalid chunk data type. Expected ArrayBuffer, Uint8Array, or string.',
31
- )
32
- }
33
- },
34
- })
35
-
36
- this.#queue = new ArrayBuffer(0)
37
- this.#reader = this.readable.getReader()
38
- }
39
-
40
- async read(size: number) {
41
- while (this.#queue.byteLength < size) {
42
- const { done, value } = await this.#reader.read()
43
- if (done) {
44
- if (this.#queue.byteLength > 0) {
45
- const chunk = this.#queue
46
- this.#queue = new ArrayBuffer(0)
47
- return chunk
48
- }
49
- return null
50
- }
51
- const buffer = value as ArrayBuffer
52
- this.#queue = concat(this.#queue, buffer)
53
- }
54
- const chunk = this.#queue.slice(0, size)
55
- this.#queue = this.#queue.slice(size)
56
- return chunk
57
- }
58
-
59
- abort(error = new Error('Stream aborted')) {
60
- this.#reader.cancel(error)
61
- this.source.cancel(error)
62
- }
63
-
64
- end() {
65
- return this.source.cancel('Stream ended')
66
- }
67
- }
68
-
69
- export interface ProtocolServerStreamInterface<T = any> {
70
- [Symbol.asyncIterator](): AsyncGenerator<T>
71
- abort(error?: Error): void
72
- }
73
-
74
- export class ProtocolServerStream<T = any>
75
- extends TransformStream<any, T>
76
- implements ProtocolServerStreamInterface<T>
77
- {
78
- #writer: WritableStreamDefaultWriter
79
-
80
- constructor(options?: Transformer<any, T>) {
81
- super(options)
82
-
83
- this.#writer = this.writable.getWriter()
84
- }
85
-
86
- async *[Symbol.asyncIterator]() {
87
- const reader = this.readable.getReader()
88
- while (true) {
89
- const { done, value } = await reader.read()
90
- if (!done) yield value
91
- else break
92
- }
93
- }
94
-
95
- async push(chunk: T) {
96
- await this.#writer.write(chunk)
97
- }
98
-
99
- async end() {
100
- await this.#writer.close()
101
- }
102
-
103
- async abort(error = new Error('Stream aborted')) {
104
- await this.#writer.abort(error)
105
- }
106
- }
107
-
108
- export class ProtocolServerBlobStream extends ProtocolServerStream<ArrayBuffer> {
109
- constructor(
110
- readonly id: number,
111
- readonly metadata: ProtocolBlobMetadata,
112
- options?: Transformer<any, ArrayBuffer>,
113
- ) {
114
- super(options)
115
- }
116
- }
@@ -1,14 +0,0 @@
1
- import type { ProtocolBlob, ProtocolBlobInterface } from '../common/blob.ts'
2
- import type { ProtocolServerBlobStream } from './stream.ts'
3
-
4
- export type InputType<T> = T extends ProtocolBlobInterface
5
- ? ProtocolBlob
6
- : T extends object
7
- ? { [K in keyof T]: InputType<T[K]> }
8
- : T
9
-
10
- export type OutputType<T> = T extends ProtocolBlobInterface
11
- ? ProtocolServerBlobStream
12
- : T extends object
13
- ? { [K in keyof T]: OutputType<T[K]> }
14
- : T
@@ -1,60 +0,0 @@
1
- // TODO: get rid of lib DOM somehow...
2
- /// <reference lib="dom" />
3
-
4
- const utf8decoder = new TextDecoder()
5
- const utf8encoder = new TextEncoder()
6
-
7
- export type BinaryTypes = {
8
- Int8: number
9
- Int16: number
10
- Int32: number
11
- Uint8: number
12
- Uint16: number
13
- Uint32: number
14
- Float32: number
15
- Float64: number
16
- BigInt64: bigint
17
- BigUint64: bigint
18
- }
19
-
20
- export const encodeNumber = <T extends keyof BinaryTypes>(
21
- value: BinaryTypes[T],
22
- type: T,
23
- littleEndian = false,
24
- ) => {
25
- const bytesNeeded = globalThis[`${type}Array`].BYTES_PER_ELEMENT
26
- const ab = new ArrayBuffer(bytesNeeded)
27
- const dv = new DataView(ab)
28
- dv[`set${type}`](0, value as never, littleEndian)
29
- return ab
30
- }
31
-
32
- export const decodeNumber = <T extends keyof BinaryTypes>(
33
- buffer: ArrayBuffer,
34
- type: T,
35
- offset = 0,
36
- littleEndian = false,
37
- ): BinaryTypes[T] => {
38
- const view = new DataView(buffer)
39
- return view[`get${type}`](offset, littleEndian) as BinaryTypes[T]
40
- }
41
-
42
- export const encodeText = (text: string) =>
43
- new Uint8Array(utf8encoder.encode(text)).buffer as ArrayBuffer
44
-
45
- export const decodeText = (buffer: Parameters<typeof utf8decoder.decode>[0]) =>
46
- utf8decoder.decode(buffer)
47
-
48
- export const concat = (...buffers: ArrayBuffer[]) => {
49
- const totalLength = buffers.reduce(
50
- (acc, buffer) => acc + buffer.byteLength,
51
- 0,
52
- )
53
- const view = new Uint8Array(totalLength)
54
- let offset = 0
55
- for (const buffer of buffers) {
56
- view.set(new Uint8Array(buffer), offset)
57
- offset += buffer.byteLength
58
- }
59
- return view.buffer
60
- }
@@ -1,77 +0,0 @@
1
- export const BlobKey: unique symbol = Symbol.for('neemata:BlobKey')
2
- export type BlobKey = typeof BlobKey
3
-
4
- export type ProtocolBlobMetadata = {
5
- type: string
6
- size?: number
7
- filename?: string
8
- }
9
-
10
- export interface ProtocolBlobInterface {
11
- readonly metadata: ProtocolBlobMetadata
12
- readonly [BlobKey]: true
13
- }
14
-
15
- export class ProtocolBlob implements ProtocolBlobInterface {
16
- readonly [BlobKey] = true
17
-
18
- public readonly metadata: ProtocolBlobMetadata
19
- public readonly source: any
20
-
21
- constructor(
22
- source: any,
23
- size?: number,
24
- type = 'application/octet-stream',
25
- filename?: string,
26
- ) {
27
- if (typeof size !== 'undefined' && size <= 0)
28
- throw new Error('Blob size is invalid')
29
-
30
- this.source = source
31
- this.metadata = {
32
- size,
33
- type,
34
- filename,
35
- }
36
- }
37
-
38
- static from(
39
- source: any,
40
- metadata: {
41
- size?: number
42
- type?: string
43
- filename?: string
44
- } = {},
45
- ) {
46
- let _source: any
47
-
48
- if (source instanceof globalThis.ReadableStream) {
49
- _source = source
50
- } else if ('File' in globalThis && source instanceof globalThis.File) {
51
- _source = source.stream()
52
- metadata.size = source.size
53
- metadata.filename = source.name
54
- } else if (source instanceof globalThis.Blob) {
55
- _source = source.stream()
56
- metadata.size = source.size
57
- } else if (typeof source === 'string') {
58
- const blob = new Blob([source])
59
- _source = blob.stream()
60
- metadata.size = blob.size
61
- metadata.type = metadata.type || 'text/plain'
62
- } else if (source instanceof globalThis.ArrayBuffer) {
63
- const blob = new Blob([source])
64
- _source = blob.stream()
65
- metadata.size = blob.size
66
- } else {
67
- _source = source
68
- }
69
-
70
- return new ProtocolBlob(
71
- _source,
72
- metadata.size,
73
- metadata.type,
74
- metadata.filename,
75
- )
76
- }
77
- }
@@ -1,48 +0,0 @@
1
- export enum ClientMessageType {
2
- Rpc = 10,
3
- RpcAbort = 11,
4
- RpcStreamAbort = 12,
5
-
6
- ClientStreamPush = 20,
7
- ClientStreamEnd = 21,
8
- ClientStreamAbort = 22,
9
- ServerStreamAbort = 23,
10
- ServerStreamPull = 24,
11
- }
12
-
13
- export enum ServerMessageType {
14
- Event = 1,
15
-
16
- RpcResponse = 10,
17
- RpcStreamResponse = 11,
18
- RpcStreamChunk = 12,
19
- RpcStreamEnd = 13,
20
- RpcStreamAbort = 14,
21
-
22
- ServerStreamPush = 20,
23
- ServerStreamEnd = 21,
24
- ServerStreamAbort = 22,
25
-
26
- ClientStreamAbort = 23,
27
- ClientStreamPull = 24,
28
- }
29
-
30
- export enum TransportType {
31
- Bidirectional = 'Bidirectional',
32
- Unidirectional = 'Unidirectional',
33
- }
34
-
35
- export enum ErrorCode {
36
- ValidationError = 'ValidationError',
37
- BadRequest = 'BadRequest',
38
- NotFound = 'NotFound',
39
- Forbidden = 'Forbidden',
40
- Unauthorized = 'Unauthorized',
41
- InternalServerError = 'InternalServerError',
42
- NotAcceptable = 'NotAcceptable',
43
- RequestTimeout = 'RequestTimeout',
44
- GatewayTimeout = 'GatewayTimeout',
45
- ServiceUnavailable = 'ServiceUnavailable',
46
- ClientRequestError = 'ClientRequestError',
47
- ConnectionError = 'ConnectionError',
48
- }
@@ -1,4 +0,0 @@
1
- export * from './binary.ts'
2
- export * from './blob.ts'
3
- export * from './enums.ts'
4
- export * from './types.ts'
@@ -1,41 +0,0 @@
1
- import type { OneOf } from '@nmtjs/common'
2
- import type { ProtocolBlob, ProtocolBlobMetadata } from './blob.ts'
3
-
4
- type Stream = any
5
-
6
- export interface BaseProtocolError {
7
- code: string
8
- message: string
9
- data?: any
10
- }
11
-
12
- export type ProtocolRPC = {
13
- callId: number
14
- namespace: string
15
- procedure: string
16
- payload: any
17
- }
18
-
19
- export type ProtocolRPCResponse<T = Stream> = OneOf<
20
- [
21
- {
22
- callId: number
23
- error: BaseProtocolError
24
- },
25
- {
26
- callId: number
27
- result: any
28
- streams: Record<number, T>
29
- },
30
- ]
31
- >
32
-
33
- export interface EncodeRPCContext<T = Stream> {
34
- getStream: (id: number) => T
35
- addStream: (blob: ProtocolBlob) => T
36
- }
37
-
38
- export interface DecodeRPCContext<T = Stream> {
39
- getStream: (id: number, callId: number) => T
40
- addStream: (id: number, callId: number, metadata: ProtocolBlobMetadata) => T
41
- }
package/src/server/api.ts DELETED
@@ -1,63 +0,0 @@
1
- import type { Async } from '@nmtjs/common'
2
- import type { Container, Hook, MetadataStore } from '@nmtjs/core'
3
- import type { Connection } from './connection.ts'
4
- import { kIterableResponse } from './constants.ts'
5
-
6
- export type ProtocolApiCallOptions = {
7
- connection: Connection
8
- namespace: string
9
- procedure: string
10
- container: Container
11
- payload: any
12
- signal: AbortSignal
13
- metadata?: (metadata: MetadataStore) => void
14
- }
15
-
16
- export type ProtocolAnyIterable<T> =
17
- | ((signal: AbortSignal) => Async<AsyncIterable<T>>)
18
- | AsyncIterable<T>
19
-
20
- export interface ProtocolApiCallBaseResult<T = unknown> {
21
- output: T
22
- }
23
-
24
- export interface ProtocolApiCallIterableResult<Y = unknown, O = unknown>
25
- extends ProtocolApiCallBaseResult<O> {
26
- [kIterableResponse]: true
27
- iterable: ProtocolAnyIterable<Y>
28
- onFinish?: () => void
29
- }
30
-
31
- export type ProtocolApiCallResult =
32
- | ProtocolApiCallBaseResult
33
- | ProtocolApiCallIterableResult
34
-
35
- export interface ProtocolApi {
36
- call(options: ProtocolApiCallOptions): Promise<ProtocolApiCallResult>
37
- }
38
-
39
- export function isIterableResult(
40
- value: ProtocolApiCallResult,
41
- ): value is ProtocolApiCallIterableResult {
42
- return value && value[kIterableResponse] === true
43
- }
44
-
45
- export function createStreamResponse<Y, O>(
46
- iterable: ProtocolAnyIterable<Y>,
47
- output = undefined as O,
48
- onFinish?: () => void,
49
- ): ProtocolApiCallIterableResult<Y, O> {
50
- return {
51
- [kIterableResponse]: true as const,
52
- iterable,
53
- output,
54
- onFinish,
55
- }
56
- }
57
-
58
- declare module '@nmtjs/core' {
59
- export interface HookType {
60
- [Hook.OnConnect]: (connection: Connection) => any
61
- [Hook.OnDisconnect]: (connection: Connection) => any
62
- }
63
- }
@@ -1,40 +0,0 @@
1
- import { randomUUID } from 'node:crypto'
2
- import type { Container } from '@nmtjs/core'
3
- import type { BaseServerDecoder, BaseServerEncoder } from './format.ts'
4
- import type { ProtocolClientStream, ProtocolServerStream } from './stream.ts'
5
-
6
- export type ConnectionOptions<Data = unknown> = {
7
- id?: string
8
- data: Data
9
- }
10
-
11
- export class Connection<Data = unknown> {
12
- readonly id: string
13
- readonly data: Data
14
-
15
- constructor(options: ConnectionOptions<Data>) {
16
- this.id = options.id ?? randomUUID()
17
- this.data = options.data
18
- }
19
- }
20
-
21
- export class ConnectionContext {
22
- streamId = 1
23
- rpcs = new Map<number, AbortController>()
24
- clientStreams = new Map<number, ProtocolClientStream>()
25
- serverStreams = new Map<number, ProtocolServerStream>()
26
- rpcStreams = new Map<number, AbortController>()
27
- container: Container
28
- format: {
29
- encoder: BaseServerEncoder
30
- decoder: BaseServerDecoder
31
- }
32
-
33
- constructor(
34
- container: ConnectionContext['container'],
35
- format: ConnectionContext['format'],
36
- ) {
37
- this.container = container
38
- this.format = format
39
- }
40
- }
@@ -1,9 +0,0 @@
1
- export const kTransportPlugin: unique symbol = Symbol.for(
2
- 'neemata:TransportPluginKey',
3
- )
4
- export type kTransportPlugin = typeof kTransportPlugin
5
-
6
- export const kIterableResponse: unique symbol = Symbol.for(
7
- 'neemata:IterableResponseKey',
8
- )
9
- export type kIterableResponse = typeof kIterableResponse
@@ -1,117 +0,0 @@
1
- import type { OneOf } from '@nmtjs/common'
2
- import { match, type Pattern } from '@nmtjs/core'
3
- import type {
4
- DecodeRPCContext,
5
- EncodeRPCContext,
6
- ProtocolRPC,
7
- ProtocolRPCResponse,
8
- } from '../common/types.ts'
9
- import type { ProtocolClientStream, ProtocolServerStream } from './stream.ts'
10
-
11
- export interface BaseServerDecoder {
12
- accept: Pattern[]
13
- decode(buffer: ArrayBuffer): any
14
- decodeRPC(
15
- buffer: ArrayBuffer,
16
- context: DecodeRPCContext<ProtocolClientStream>,
17
- ): ProtocolRPC
18
- }
19
-
20
- export interface BaseServerEncoder {
21
- contentType: string
22
- encode(data: any): ArrayBuffer
23
- encodeRPC(
24
- rpc: OneOf<
25
- [{ callId: number; error: any }, { callId: number; result: any }]
26
- >,
27
- context: EncodeRPCContext<ProtocolServerStream>,
28
- ): ArrayBuffer
29
- }
30
-
31
- export abstract class BaseServerFormat
32
- implements BaseServerDecoder, BaseServerEncoder
33
- {
34
- abstract accept: Pattern[]
35
- abstract contentType: string
36
-
37
- abstract encode(data: any): ArrayBuffer
38
- abstract encodeRPC(
39
- rpc: ProtocolRPCResponse,
40
- context: EncodeRPCContext<ProtocolServerStream>,
41
- ): ArrayBuffer
42
- abstract decode(buffer: ArrayBuffer): any
43
- abstract decodeRPC(
44
- buffer: ArrayBuffer,
45
- context: DecodeRPCContext<ProtocolClientStream>,
46
- ): ProtocolRPC
47
- }
48
-
49
- export const parseContentTypes = (types: string) => {
50
- if (types === '*/*') return ['*/*']
51
- return types
52
- .split(',')
53
- .map((t) => {
54
- const [type, ...rest] = t.split(';')
55
- const params = new Map(
56
- rest.map((p) =>
57
- p
58
- .trim()
59
- .split('=')
60
- .slice(0, 2)
61
- .map((p) => p.trim()),
62
- ) as [string, string][],
63
- )
64
- return {
65
- type,
66
- q: params.has('q') ? Number.parseFloat(params.get('q')!) : 1,
67
- }
68
- })
69
- .sort((a, b) => {
70
- if (a.type === '*/*') return 1
71
- if (b.type === '*/*') return -1
72
- return b.q - a.q ? -1 : 1
73
- })
74
- .map((t) => t.type)
75
- }
76
-
77
- export class Format {
78
- decoders = new Map<Pattern, BaseServerDecoder>()
79
- encoders = new Map<Pattern, BaseServerEncoder>()
80
-
81
- constructor(formats: BaseServerFormat[]) {
82
- for (const format of formats) {
83
- this.encoders.set(format.contentType, format)
84
- for (const acceptType of format.accept) {
85
- this.decoders.set(acceptType, format)
86
- }
87
- }
88
- }
89
-
90
- supportsDecoder(contentType: string, throwIfUnsupported = false) {
91
- return this.supports(this.decoders, contentType, throwIfUnsupported)
92
- }
93
-
94
- supportsEncoder(contentType: string, throwIfUnsupported = false) {
95
- return this.supports(this.encoders, contentType, throwIfUnsupported)
96
- }
97
-
98
- private supports<T extends BaseServerEncoder | BaseServerDecoder>(
99
- formats: Map<Pattern, T>,
100
- contentType: string,
101
- throwIfUnsupported = false,
102
- ): T | null {
103
- // TODO: Use node:utils.MIMEType (not implemented yet in Deno and Bun yet)
104
- const types = parseContentTypes(contentType)
105
-
106
- for (const type of types) {
107
- for (const [pattern, format] of formats) {
108
- if (type === '*/*' || match(type, pattern)) return format
109
- }
110
- }
111
-
112
- if (throwIfUnsupported)
113
- throw new Error(`No supported format found: ${contentType}`)
114
-
115
- return null
116
- }
117
- }
@@ -1,11 +0,0 @@
1
- export * from './api.ts'
2
- export * from './connection.ts'
3
- export * from './constants.ts'
4
- export * from './format.ts'
5
- export * from './injectables.ts'
6
- export * from './protocol.ts'
7
- export * from './registry.ts'
8
- export * from './stream.ts'
9
- export * from './transport.ts'
10
- export * from './types.ts'
11
- export * from './utils.ts'
@@ -1,52 +0,0 @@
1
- import {
2
- createFactoryInjectable,
3
- createLazyInjectable,
4
- Scope,
5
- } from '@nmtjs/core'
6
- import type { Connection } from './connection.ts'
7
-
8
- const connection = createLazyInjectable<Connection, Scope.Connection>(
9
- Scope.Connection,
10
- 'RPC connection',
11
- )
12
-
13
- const connectionData = createLazyInjectable<any, Scope.Connection>(
14
- Scope.Connection,
15
- "RPC connection's data",
16
- )
17
-
18
- const connectionAbortSignal = createLazyInjectable<
19
- AbortSignal,
20
- Scope.Connection
21
- >(Scope.Connection, 'Connection abort signal')
22
-
23
- const rpcClientAbortSignal = createLazyInjectable<AbortSignal, Scope.Call>(
24
- Scope.Call,
25
- 'RPC client abort signal',
26
- )
27
-
28
- const rpcTimeoutSignal = createLazyInjectable<AbortSignal, Scope.Call>(
29
- Scope.Call,
30
- 'RPC timeout signal',
31
- )
32
-
33
- const rpcAbortSignal = createFactoryInjectable(
34
- {
35
- dependencies: {
36
- rpcTimeoutSignal,
37
- rpcClientAbortSignal,
38
- connectionAbortSignal,
39
- },
40
- factory: (ctx) => AbortSignal.any(Object.values(ctx)),
41
- },
42
- 'Any RPC abort signal',
43
- )
44
-
45
- export const ProtocolInjectables = {
46
- connection,
47
- connectionData,
48
- connectionAbortSignal,
49
- rpcClientAbortSignal,
50
- rpcTimeoutSignal,
51
- rpcAbortSignal,
52
- } as const