@volter/twin-temporal 0.1.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.
Files changed (55) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +104 -0
  3. package/dist/src/cli.d.ts +2 -0
  4. package/dist/src/cli.js +36 -0
  5. package/dist/src/generated/hpack.json +1 -0
  6. package/dist/src/generated/temporal-api.json +1 -0
  7. package/dist/src/index.d.ts +18 -0
  8. package/dist/src/index.js +70 -0
  9. package/dist/src/temporal-budget.d.ts +31 -0
  10. package/dist/src/temporal-budget.js +50 -0
  11. package/dist/src/temporal-capabilities.d.ts +7 -0
  12. package/dist/src/temporal-capabilities.js +650 -0
  13. package/dist/src/temporal-conformance.d.ts +8 -0
  14. package/dist/src/temporal-conformance.js +54 -0
  15. package/dist/src/temporal-connector.d.ts +32 -0
  16. package/dist/src/temporal-connector.js +121 -0
  17. package/dist/src/temporal-engine.d.ts +274 -0
  18. package/dist/src/temporal-engine.js +2132 -0
  19. package/dist/src/temporal-grpc.d.ts +51 -0
  20. package/dist/src/temporal-grpc.js +124 -0
  21. package/dist/src/temporal-h2.d.ts +40 -0
  22. package/dist/src/temporal-h2.js +644 -0
  23. package/dist/src/temporal-history.d.ts +27 -0
  24. package/dist/src/temporal-history.js +85 -0
  25. package/dist/src/temporal-http.d.ts +17 -0
  26. package/dist/src/temporal-http.js +160 -0
  27. package/dist/src/temporal-json.d.ts +7 -0
  28. package/dist/src/temporal-json.js +68 -0
  29. package/dist/src/temporal-proto.d.ts +17 -0
  30. package/dist/src/temporal-proto.js +46 -0
  31. package/dist/src/temporal-server.d.ts +25 -0
  32. package/dist/src/temporal-server.js +56 -0
  33. package/dist/src/temporal-twin.d.ts +25 -0
  34. package/dist/src/temporal-twin.js +100 -0
  35. package/dist/src/temporal-visibility.d.ts +49 -0
  36. package/dist/src/temporal-visibility.js +293 -0
  37. package/package.json +59 -0
  38. package/src/cli.ts +36 -0
  39. package/src/generated/hpack.json +1 -0
  40. package/src/generated/temporal-api.json +1 -0
  41. package/src/index.ts +81 -0
  42. package/src/temporal-budget.ts +75 -0
  43. package/src/temporal-capabilities.ts +679 -0
  44. package/src/temporal-conformance.ts +52 -0
  45. package/src/temporal-connector.ts +118 -0
  46. package/src/temporal-engine.ts +2176 -0
  47. package/src/temporal-grpc.ts +139 -0
  48. package/src/temporal-h2.ts +614 -0
  49. package/src/temporal-history.ts +99 -0
  50. package/src/temporal-http.ts +146 -0
  51. package/src/temporal-json.ts +77 -0
  52. package/src/temporal-proto.ts +53 -0
  53. package/src/temporal-server.ts +67 -0
  54. package/src/temporal-twin.ts +102 -0
  55. package/src/temporal-visibility.ts +245 -0
@@ -0,0 +1,51 @@
1
+ import type { H2Handler } from './temporal-h2.js';
2
+ export declare const TEMPORAL_LOOPBACK_HOST = "127.0.0.1";
3
+ /** gRPC status codes (https://grpc.github.io/grpc/core/md_doc_statuscodes.html). */
4
+ export declare const GrpcStatus: {
5
+ readonly OK: 0;
6
+ readonly CANCELLED: 1;
7
+ readonly UNKNOWN: 2;
8
+ readonly INVALID_ARGUMENT: 3;
9
+ readonly DEADLINE_EXCEEDED: 4;
10
+ readonly NOT_FOUND: 5;
11
+ readonly ALREADY_EXISTS: 6;
12
+ readonly PERMISSION_DENIED: 7;
13
+ readonly RESOURCE_EXHAUSTED: 8;
14
+ readonly FAILED_PRECONDITION: 9;
15
+ readonly ABORTED: 10;
16
+ readonly OUT_OF_RANGE: 11;
17
+ readonly UNIMPLEMENTED: 12;
18
+ readonly INTERNAL: 13;
19
+ readonly UNAVAILABLE: 14;
20
+ readonly DATA_LOSS: 15;
21
+ readonly UNAUTHENTICATED: 16;
22
+ };
23
+ export declare class GrpcError extends Error {
24
+ readonly code: number;
25
+ constructor(code: number, message: string);
26
+ }
27
+ export type GrpcCallContext = {
28
+ /** aborted when the client cancels the call or its deadline passes */
29
+ signal: AbortSignal;
30
+ /** the client's deadline as epoch ms, when it sent one */
31
+ deadline?: number;
32
+ metadata: Record<string, string>;
33
+ };
34
+ /** Answers one unary call; a method the handler does not serve throws UNIMPLEMENTED. */
35
+ export type GrpcHandler = (service: string, method: string, request: Record<string, unknown>, ctx: GrpcCallContext) => Promise<Record<string, unknown>>;
36
+ /** `grpc-timeout`: an integer and a unit (H M S m u n), PROTOCOL-HTTP2.md "Timeout". */
37
+ export declare function parseGrpcTimeout(value: string | undefined): number | undefined;
38
+ /** Length-prefixed messages out of a request body: [compressed flag][u32 length][bytes]... */
39
+ export declare function splitGrpcFrames(body: Uint8Array): Array<{
40
+ compressed: boolean;
41
+ message: Uint8Array;
42
+ }>;
43
+ export declare function grpcFrame(message: Uint8Array): Uint8Array;
44
+ /** grpc-message is percent-encoded (PROTOCOL-HTTP2.md "Status-Message"). */
45
+ export declare function encodeGrpcMessage(text: string): string;
46
+ /** The HTTP/2 stream handler that answers gRPC for `services` (fully-qualified proto service names)
47
+ * through `handler`; the listener that feeds it sockets is temporal-server.ts's. */
48
+ export declare function grpcStreamHandler(options: {
49
+ services: string[];
50
+ handler: GrpcHandler;
51
+ }): H2Handler;
@@ -0,0 +1,124 @@
1
+ import { decodeMessage, encodeMessage, serviceMethods } from "./temporal-proto.js";
2
+ export const TEMPORAL_LOOPBACK_HOST = '127.0.0.1';
3
+ /** gRPC status codes (https://grpc.github.io/grpc/core/md_doc_statuscodes.html). */
4
+ export const GrpcStatus = {
5
+ OK: 0, CANCELLED: 1, UNKNOWN: 2, INVALID_ARGUMENT: 3, DEADLINE_EXCEEDED: 4, NOT_FOUND: 5, ALREADY_EXISTS: 6,
6
+ PERMISSION_DENIED: 7, RESOURCE_EXHAUSTED: 8, FAILED_PRECONDITION: 9, ABORTED: 10, OUT_OF_RANGE: 11,
7
+ UNIMPLEMENTED: 12, INTERNAL: 13, UNAVAILABLE: 14, DATA_LOSS: 15, UNAUTHENTICATED: 16,
8
+ };
9
+ export class GrpcError extends Error {
10
+ code;
11
+ constructor(code, message) {
12
+ super(message);
13
+ this.code = code;
14
+ }
15
+ }
16
+ /** `grpc-timeout`: an integer and a unit (H M S m u n), PROTOCOL-HTTP2.md "Timeout". */
17
+ export function parseGrpcTimeout(value) {
18
+ const match = /^(\d{1,8})([HMSmun])$/.exec(value ?? '');
19
+ if (!match)
20
+ return undefined;
21
+ const amount = Number(match[1]);
22
+ const ms = { H: 3_600_000, M: 60_000, S: 1000, m: 1, u: 0.001, n: 0.000001 }[match[2]];
23
+ return amount * ms;
24
+ }
25
+ /** Length-prefixed messages out of a request body: [compressed flag][u32 length][bytes]... */
26
+ export function splitGrpcFrames(body) {
27
+ const frames = [];
28
+ let offset = 0;
29
+ while (offset < body.length) {
30
+ if (body.length - offset < 5)
31
+ throw new GrpcError(GrpcStatus.INTERNAL, 'truncated gRPC frame header');
32
+ const view = new DataView(body.buffer, body.byteOffset + offset, 5);
33
+ const length = view.getUint32(1);
34
+ if (body.length - offset - 5 < length)
35
+ throw new GrpcError(GrpcStatus.INTERNAL, 'truncated gRPC message');
36
+ frames.push({ compressed: body[offset] === 1, message: body.subarray(offset + 5, offset + 5 + length) });
37
+ offset += 5 + length;
38
+ }
39
+ return frames;
40
+ }
41
+ export function grpcFrame(message) {
42
+ const out = new Uint8Array(5 + message.length);
43
+ new DataView(out.buffer).setUint32(1, message.length);
44
+ out.set(message, 5);
45
+ return out;
46
+ }
47
+ /** grpc-message is percent-encoded (PROTOCOL-HTTP2.md "Status-Message"). */
48
+ export function encodeGrpcMessage(text) {
49
+ return Array.from(new TextEncoder().encode(text), (byte) => (byte >= 0x20 && byte <= 0x7e && byte !== 0x25 ? String.fromCharCode(byte) : `%${byte.toString(16).toUpperCase().padStart(2, '0')}`)).join('');
50
+ }
51
+ /** The HTTP/2 stream handler that answers gRPC for `services` (fully-qualified proto service names)
52
+ * through `handler`; the listener that feeds it sockets is temporal-server.ts's. */
53
+ export function grpcStreamHandler(options) {
54
+ const tables = new Map(options.services.map((name) => [name, serviceMethods(name)]));
55
+ return (request, response) => {
56
+ const headers = request.headers;
57
+ const fail = (code, message) => {
58
+ if (response.closed)
59
+ return;
60
+ response.headers([[':status', '200'], ['content-type', 'application/grpc'], ['grpc-status', String(code)], ['grpc-message', encodeGrpcMessage(message)]], true);
61
+ };
62
+ const contentType = headers['content-type'] ?? '';
63
+ if (headers[':method'] !== 'POST' || !contentType.startsWith('application/grpc')) {
64
+ response.headers([[':status', '415'], ['content-type', 'text/plain']], true);
65
+ return;
66
+ }
67
+ const path = headers[':path'] ?? '';
68
+ const match = /^\/([^/]+)\/([^/]+)$/.exec(path);
69
+ const table = match ? tables.get(match[1]) : undefined;
70
+ const method = match && table ? table.get(match[2]) : undefined;
71
+ if (!match || !table || !method) {
72
+ fail(GrpcStatus.UNIMPLEMENTED, `unknown method ${path}`);
73
+ return;
74
+ }
75
+ if (method.requestStream || method.responseStream) {
76
+ fail(GrpcStatus.UNIMPLEMENTED, `${match[1]}/${match[2]} is a streaming method; this server answers unary calls only`);
77
+ return;
78
+ }
79
+ const timeout = parseGrpcTimeout(headers['grpc-timeout']);
80
+ const controller = new AbortController();
81
+ const timer = timeout !== undefined ? setTimeout(() => controller.abort(), timeout) : undefined;
82
+ request.signal.addEventListener('abort', () => controller.abort(), { once: true });
83
+ const metadata = {};
84
+ for (const [key, value] of Object.entries(headers))
85
+ if (!key.startsWith(':'))
86
+ metadata[key] = value;
87
+ let decoded;
88
+ try {
89
+ const frames = splitGrpcFrames(request.body);
90
+ if (frames.length !== 1)
91
+ throw new GrpcError(GrpcStatus.INTERNAL, `a unary call carries exactly one message (got ${frames.length})`);
92
+ if (frames[0].compressed)
93
+ throw new GrpcError(GrpcStatus.UNIMPLEMENTED, 'compressed messages are not accepted (this server advertises no grpc-encoding)');
94
+ decoded = decodeMessage(method.requestType, frames[0].message);
95
+ }
96
+ catch (error) {
97
+ if (timer)
98
+ clearTimeout(timer);
99
+ fail(error instanceof GrpcError ? error.code : GrpcStatus.INTERNAL, error.message);
100
+ return;
101
+ }
102
+ void (async () => {
103
+ try {
104
+ const answer = await options.handler(match[1], match[2], decoded, { signal: controller.signal, ...(timeout !== undefined ? { deadline: Date.now() + timeout } : {}), metadata });
105
+ if (response.closed)
106
+ return;
107
+ const bytes = grpcFrame(encodeMessage(method.responseType, answer));
108
+ response.headers([[':status', '200'], ['content-type', 'application/grpc']]);
109
+ response.data(bytes);
110
+ response.trailers([['grpc-status', '0']]);
111
+ }
112
+ catch (error) {
113
+ if (error instanceof GrpcError)
114
+ fail(error.code, error.message);
115
+ else
116
+ fail(GrpcStatus.INTERNAL, `internal error: ${error?.message ?? String(error)}`);
117
+ }
118
+ finally {
119
+ if (timer)
120
+ clearTimeout(timer);
121
+ }
122
+ })();
123
+ };
124
+ }
@@ -0,0 +1,40 @@
1
+ import type { Socket } from 'node:net';
2
+ export declare class HpackError extends Error {
3
+ }
4
+ export declare function huffmanDecode(bytes: Uint8Array): string;
5
+ export declare class HpackDecoder {
6
+ private table;
7
+ private size;
8
+ private maxSize;
9
+ /** the SETTINGS_HEADER_TABLE_SIZE this endpoint advertised (the decoder's ceiling; 4096 is the
10
+ * protocol default, which this server never changes) */
11
+ private readonly limit;
12
+ constructor(headerTableSize?: number);
13
+ decode(block: Uint8Array): Array<[string, string]>;
14
+ private insert;
15
+ private evict;
16
+ }
17
+ /** Every header as a literal without indexing, new name, no huffman (RFC 7541 §6.2.2). */
18
+ export declare function hpackEncode(headers: Array<[string, string]>): Uint8Array;
19
+ export type H2Request = {
20
+ headers: Record<string, string>;
21
+ body: Uint8Array;
22
+ /** aborted when the peer resets the stream or the connection goes away */
23
+ signal: AbortSignal;
24
+ };
25
+ export type H2Response = {
26
+ /** send response headers; `end` closes the stream with them (a trailers-only answer) */
27
+ headers(headers: Array<[string, string]>, end?: boolean): void;
28
+ data(bytes: Uint8Array): void;
29
+ /** close the stream with trailing headers */
30
+ trailers(headers: Array<[string, string]>): void;
31
+ readonly closed: boolean;
32
+ };
33
+ export type H2Handler = (request: H2Request, response: H2Response) => void;
34
+ /** An HTTP/1 request arriving on the same port, answered by a fetch handler. */
35
+ export type Http1Handler = (request: Request) => Response | Promise<Response>;
36
+ /** Speak HTTP/2 (h2c with prior knowledge) on an accepted socket; an HTTP/1 request line is handed to
37
+ * `http1` when given. The listener that accepts sockets is the server module's (temporal-server.ts). */
38
+ export declare function acceptH2Connection(socket: Socket, handler: H2Handler, http1?: Http1Handler): {
39
+ teardown(): void;
40
+ };