@vishalsharma7nov/react-native-proto 0.2.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/android/src/main/java/com/vishalsharma7nov/reactnativeproto/ReactNativeProtoNativeGrpcModule.java +142 -0
- package/app.plugin.js +63 -0
- package/bin/react-native-proto.js +956 -0
- package/ios/ReactNativeProtoNativeGrpc.m +129 -0
- package/lib/commonjs/auth.js +72 -0
- package/lib/commonjs/auth.js.map +1 -0
- package/lib/commonjs/client-factory.js +81 -0
- package/lib/commonjs/client-factory.js.map +1 -0
- package/lib/commonjs/codecs.js +41 -0
- package/lib/commonjs/codecs.js.map +1 -0
- package/lib/commonjs/connect/envelope.js +100 -0
- package/lib/commonjs/connect/envelope.js.map +1 -0
- package/lib/commonjs/connect/errors.js +71 -0
- package/lib/commonjs/connect/errors.js.map +1 -0
- package/lib/commonjs/errors.js +54 -0
- package/lib/commonjs/errors.js.map +1 -0
- package/lib/commonjs/generated/create-api.js +29 -0
- package/lib/commonjs/generated/create-api.js.map +1 -0
- package/lib/commonjs/generated/message-types.js +2 -0
- package/lib/commonjs/generated/message-types.js.map +1 -0
- package/lib/commonjs/generated/messages.js +47 -0
- package/lib/commonjs/generated/messages.js.map +1 -0
- package/lib/commonjs/generated/messages.pb.js +2787 -0
- package/lib/commonjs/generated/messages.pb.js.map +1 -0
- package/lib/commonjs/generated/method-map.js +68 -0
- package/lib/commonjs/generated/method-map.js.map +1 -0
- package/lib/commonjs/index.js +283 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/interceptors.js +19 -0
- package/lib/commonjs/interceptors.js.map +1 -0
- package/lib/commonjs/logging.js +54 -0
- package/lib/commonjs/logging.js.map +1 -0
- package/lib/commonjs/metro/index.js +35 -0
- package/lib/commonjs/metro/index.js.map +1 -0
- package/lib/commonjs/metro/transformer.js +33 -0
- package/lib/commonjs/metro/transformer.js.map +1 -0
- package/lib/commonjs/mock-server/index.js +125 -0
- package/lib/commonjs/mock-server/index.js.map +1 -0
- package/lib/commonjs/native-grpc/index.js +242 -0
- package/lib/commonjs/native-grpc/index.js.map +1 -0
- package/lib/commonjs/offline-queue.js +137 -0
- package/lib/commonjs/offline-queue.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/path-templates.js +44 -0
- package/lib/commonjs/path-templates.js.map +1 -0
- package/lib/commonjs/react-query/index.js +30 -0
- package/lib/commonjs/react-query/index.js.map +1 -0
- package/lib/commonjs/streaming.js +251 -0
- package/lib/commonjs/streaming.js.map +1 -0
- package/lib/commonjs/transport.js +323 -0
- package/lib/commonjs/transport.js.map +1 -0
- package/lib/commonjs/types.js +6 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/commonjs/validation/zod.js +48 -0
- package/lib/commonjs/validation/zod.js.map +1 -0
- package/lib/commonjs/version-check.js +102 -0
- package/lib/commonjs/version-check.js.map +1 -0
- package/lib/module/auth.js +67 -0
- package/lib/module/auth.js.map +1 -0
- package/lib/module/client-factory.js +77 -0
- package/lib/module/client-factory.js.map +1 -0
- package/lib/module/codecs.js +36 -0
- package/lib/module/codecs.js.map +1 -0
- package/lib/module/connect/envelope.js +93 -0
- package/lib/module/connect/envelope.js.map +1 -0
- package/lib/module/connect/errors.js +65 -0
- package/lib/module/connect/errors.js.map +1 -0
- package/lib/module/errors.js +46 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/generated/create-api.js +13 -0
- package/lib/module/generated/create-api.js.map +1 -0
- package/lib/module/generated/message-types.js +2 -0
- package/lib/module/generated/message-types.js.map +1 -0
- package/lib/module/generated/messages.js +43 -0
- package/lib/module/generated/messages.js.map +1 -0
- package/lib/module/generated/messages.pb.js +2787 -0
- package/lib/module/generated/messages.pb.js.map +1 -0
- package/lib/module/generated/method-map.js +64 -0
- package/lib/module/generated/method-map.js.map +1 -0
- package/lib/module/index.js +53 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/interceptors.js +15 -0
- package/lib/module/interceptors.js.map +1 -0
- package/lib/module/logging.js +50 -0
- package/lib/module/logging.js.map +1 -0
- package/lib/module/metro/index.js +25 -0
- package/lib/module/metro/index.js.map +1 -0
- package/lib/module/metro/transformer.js +29 -0
- package/lib/module/metro/transformer.js.map +1 -0
- package/lib/module/mock-server/index.js +120 -0
- package/lib/module/mock-server/index.js.map +1 -0
- package/lib/module/native-grpc/index.js +233 -0
- package/lib/module/native-grpc/index.js.map +1 -0
- package/lib/module/offline-queue.js +132 -0
- package/lib/module/offline-queue.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/path-templates.js +39 -0
- package/lib/module/path-templates.js.map +1 -0
- package/lib/module/react-query/index.js +25 -0
- package/lib/module/react-query/index.js.map +1 -0
- package/lib/module/streaming.js +246 -0
- package/lib/module/streaming.js.map +1 -0
- package/lib/module/transport.js +316 -0
- package/lib/module/transport.js.map +1 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/validation/zod.js +43 -0
- package/lib/module/validation/zod.js.map +1 -0
- package/lib/module/version-check.js +93 -0
- package/lib/module/version-check.js.map +1 -0
- package/lib/typescript/src/auth.d.ts +26 -0
- package/lib/typescript/src/auth.d.ts.map +1 -0
- package/lib/typescript/src/client-factory.d.ts +7 -0
- package/lib/typescript/src/client-factory.d.ts.map +1 -0
- package/lib/typescript/src/codecs.d.ts +5 -0
- package/lib/typescript/src/codecs.d.ts.map +1 -0
- package/lib/typescript/src/connect/envelope.d.ts +18 -0
- package/lib/typescript/src/connect/envelope.d.ts.map +1 -0
- package/lib/typescript/src/connect/errors.d.ts +11 -0
- package/lib/typescript/src/connect/errors.d.ts.map +1 -0
- package/lib/typescript/src/errors.d.ts +21 -0
- package/lib/typescript/src/errors.d.ts.map +1 -0
- package/lib/typescript/src/generated/create-api.d.ts +34 -0
- package/lib/typescript/src/generated/create-api.d.ts.map +1 -0
- package/lib/typescript/src/generated/message-types.d.ts +54 -0
- package/lib/typescript/src/generated/message-types.d.ts.map +1 -0
- package/lib/typescript/src/generated/messages.d.ts +17 -0
- package/lib/typescript/src/generated/messages.d.ts.map +1 -0
- package/lib/typescript/src/generated/method-map.d.ts +7 -0
- package/lib/typescript/src/generated/method-map.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +53 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/interceptors.d.ts +17 -0
- package/lib/typescript/src/interceptors.d.ts.map +1 -0
- package/lib/typescript/src/logging.d.ts +16 -0
- package/lib/typescript/src/logging.d.ts.map +1 -0
- package/lib/typescript/src/metro/index.d.ts +15 -0
- package/lib/typescript/src/metro/index.d.ts.map +1 -0
- package/lib/typescript/src/metro/transformer.d.ts +16 -0
- package/lib/typescript/src/metro/transformer.d.ts.map +1 -0
- package/lib/typescript/src/mock-server/index.d.ts +27 -0
- package/lib/typescript/src/mock-server/index.d.ts.map +1 -0
- package/lib/typescript/src/native-grpc/index.d.ts +36 -0
- package/lib/typescript/src/native-grpc/index.d.ts.map +1 -0
- package/lib/typescript/src/offline-queue.d.ts +47 -0
- package/lib/typescript/src/offline-queue.d.ts.map +1 -0
- package/lib/typescript/src/path-templates.d.ts +17 -0
- package/lib/typescript/src/path-templates.d.ts.map +1 -0
- package/lib/typescript/src/react-query/index.d.ts +14 -0
- package/lib/typescript/src/react-query/index.d.ts.map +1 -0
- package/lib/typescript/src/streaming.d.ts +10 -0
- package/lib/typescript/src/streaming.d.ts.map +1 -0
- package/lib/typescript/src/transport.d.ts +19 -0
- package/lib/typescript/src/transport.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +78 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/validation/zod.d.ts +33 -0
- package/lib/typescript/src/validation/zod.d.ts.map +1 -0
- package/lib/typescript/src/version-check.d.ts +40 -0
- package/lib/typescript/src/version-check.d.ts.map +1 -0
- package/metro/index.js +23 -0
- package/metro/transformer.js +21 -0
- package/package.json +144 -0
- package/react-native-proto.podspec +15 -0
- package/src/auth.ts +97 -0
- package/src/client-factory.ts +109 -0
- package/src/codecs.ts +42 -0
- package/src/connect/envelope.ts +112 -0
- package/src/connect/errors.ts +86 -0
- package/src/errors.ts +81 -0
- package/src/generated/create-api.ts +40 -0
- package/src/generated/message-types.ts +63 -0
- package/src/generated/messages.pb.js +3207 -0
- package/src/generated/messages.ts +45 -0
- package/src/generated/method-map.ts +71 -0
- package/src/index.ts +113 -0
- package/src/interceptors.ts +32 -0
- package/src/logging.ts +67 -0
- package/src/metro/index.ts +27 -0
- package/src/metro/transformer.ts +38 -0
- package/src/mock-server/index.ts +164 -0
- package/src/native-grpc/index.ts +302 -0
- package/src/offline-queue.ts +160 -0
- package/src/path-templates.ts +65 -0
- package/src/react-query/index.ts +40 -0
- package/src/streaming.ts +326 -0
- package/src/transport.ts +427 -0
- package/src/types.ts +94 -0
- package/src/validation/zod.ts +75 -0
- package/src/version-check.ts +121 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-explicit-any */
|
|
2
|
+
import type { Type } from 'protobufjs';
|
|
3
|
+
import { createCodecRegistry } from '../codecs';
|
|
4
|
+
// Auto-generated wrapper. Do not edit by hand.
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
6
|
+
const pb = require('./messages.pb.js');
|
|
7
|
+
|
|
8
|
+
function isType(value: unknown): value is Type {
|
|
9
|
+
if (!value) return false;
|
|
10
|
+
if (typeof value !== 'object' && typeof value !== 'function') return false;
|
|
11
|
+
return typeof (value as Type).encode === 'function' && typeof (value as Type).decode === 'function';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function collectTypes(obj: any, prefix = '', acc: Record<string, Type> = {}) {
|
|
15
|
+
if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) return acc;
|
|
16
|
+
if (isType(obj) && prefix) acc[prefix] = obj;
|
|
17
|
+
for (const key of Object.keys(obj)) {
|
|
18
|
+
if (['encode','decode','create','fromObject','toObject','encodeDelimited','decodeDelimited','verify','getTypeUrl'].includes(key)) continue;
|
|
19
|
+
const next = obj[key];
|
|
20
|
+
const nextPrefix = prefix ? `${prefix}.${key}` : key;
|
|
21
|
+
if (isType(next)) {
|
|
22
|
+
acc[nextPrefix] = next;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (next && typeof next === 'object') collectTypes(next, nextPrefix, acc);
|
|
26
|
+
}
|
|
27
|
+
return acc;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const messageTypes = collectTypes(pb);
|
|
31
|
+
export const defaultCodecs = createCodecRegistry(messageTypes);
|
|
32
|
+
|
|
33
|
+
export const demo = pb.demo;
|
|
34
|
+
export const AddUserRequest = pb?.demo?.AddUserRequest as Type;
|
|
35
|
+
export const DeleteUserRequest = pb?.demo?.DeleteUserRequest as Type;
|
|
36
|
+
export const DeleteUserResponse = pb?.demo?.DeleteUserResponse as Type;
|
|
37
|
+
export const GetUserByEmailRequest = pb?.demo?.GetUserByEmailRequest as Type;
|
|
38
|
+
export const GetUserByPhoneRequest = pb?.demo?.GetUserByPhoneRequest as Type;
|
|
39
|
+
export const GetUserRequest = pb?.demo?.GetUserRequest as Type;
|
|
40
|
+
export const ListUsersRequest = pb?.demo?.ListUsersRequest as Type;
|
|
41
|
+
export const ListUsersResponse = pb?.demo?.ListUsersResponse as Type;
|
|
42
|
+
export const UpdateUserRequest = pb?.demo?.UpdateUserRequest as Type;
|
|
43
|
+
export const User = pb?.demo?.User as Type;
|
|
44
|
+
|
|
45
|
+
export default pb;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { MethodMap } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Auto-generated method map. Do not edit by hand.
|
|
5
|
+
* Sources: user.proto
|
|
6
|
+
*/
|
|
7
|
+
export const methodMap: MethodMap = [
|
|
8
|
+
{
|
|
9
|
+
"serviceName": "UserService",
|
|
10
|
+
"methodName": "AddUser",
|
|
11
|
+
"packageName": "demo",
|
|
12
|
+
"rpcPath": "demo.UserService/AddUser",
|
|
13
|
+
"requestType": "demo.AddUserRequest",
|
|
14
|
+
"responseType": "demo.User",
|
|
15
|
+
"kind": "unary"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"serviceName": "UserService",
|
|
19
|
+
"methodName": "GetUser",
|
|
20
|
+
"packageName": "demo",
|
|
21
|
+
"rpcPath": "demo.UserService/GetUser",
|
|
22
|
+
"requestType": "demo.GetUserRequest",
|
|
23
|
+
"responseType": "demo.User",
|
|
24
|
+
"kind": "unary"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"serviceName": "UserService",
|
|
28
|
+
"methodName": "GetUserByEmail",
|
|
29
|
+
"packageName": "demo",
|
|
30
|
+
"rpcPath": "demo.UserService/GetUserByEmail",
|
|
31
|
+
"requestType": "demo.GetUserByEmailRequest",
|
|
32
|
+
"responseType": "demo.User",
|
|
33
|
+
"kind": "unary"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"serviceName": "UserService",
|
|
37
|
+
"methodName": "ListUsers",
|
|
38
|
+
"packageName": "demo",
|
|
39
|
+
"rpcPath": "demo.UserService/ListUsers",
|
|
40
|
+
"requestType": "demo.ListUsersRequest",
|
|
41
|
+
"responseType": "demo.ListUsersResponse",
|
|
42
|
+
"kind": "unary"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"serviceName": "UserService",
|
|
46
|
+
"methodName": "UpdateUser",
|
|
47
|
+
"packageName": "demo",
|
|
48
|
+
"rpcPath": "demo.UserService/UpdateUser",
|
|
49
|
+
"requestType": "demo.UpdateUserRequest",
|
|
50
|
+
"responseType": "demo.User",
|
|
51
|
+
"kind": "unary"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"serviceName": "UserService",
|
|
55
|
+
"methodName": "DeleteUser",
|
|
56
|
+
"packageName": "demo",
|
|
57
|
+
"rpcPath": "demo.UserService/DeleteUser",
|
|
58
|
+
"requestType": "demo.DeleteUserRequest",
|
|
59
|
+
"responseType": "demo.DeleteUserResponse",
|
|
60
|
+
"kind": "unary"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"serviceName": "UserService",
|
|
64
|
+
"methodName": "GetUserByPhone",
|
|
65
|
+
"packageName": "demo",
|
|
66
|
+
"rpcPath": "demo.UserService/GetUserByPhone",
|
|
67
|
+
"requestType": "demo.GetUserByPhoneRequest",
|
|
68
|
+
"responseType": "demo.User",
|
|
69
|
+
"kind": "unary"
|
|
70
|
+
}
|
|
71
|
+
];
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { createClientFromMap } from "./client-factory";
|
|
2
|
+
import { getErrorCode, isProtoClientError, ProtoClientError } from "./errors";
|
|
3
|
+
import { methodMap } from "./generated/method-map";
|
|
4
|
+
import { defaultCodecs } from "./generated/messages";
|
|
5
|
+
import { isNativeGrpcAvailable } from "./native-grpc";
|
|
6
|
+
import type {
|
|
7
|
+
CodecRegistry,
|
|
8
|
+
MethodMap,
|
|
9
|
+
ProtoClient,
|
|
10
|
+
ProtoClientConfig,
|
|
11
|
+
} from "./types";
|
|
12
|
+
|
|
13
|
+
export type {
|
|
14
|
+
CodecRegistry,
|
|
15
|
+
HeaderMap,
|
|
16
|
+
MethodKind,
|
|
17
|
+
MethodMap,
|
|
18
|
+
MethodMapEntry,
|
|
19
|
+
ProtoClient,
|
|
20
|
+
ProtoClientConfig,
|
|
21
|
+
TransportKind,
|
|
22
|
+
} from "./types";
|
|
23
|
+
|
|
24
|
+
export type { CallContext, Interceptor, UnaryHandler } from "./interceptors";
|
|
25
|
+
export type { PathTemplatePreset } from "./path-templates";
|
|
26
|
+
export type { AuthInterceptorOptions } from "./auth";
|
|
27
|
+
export type { LoggingOptions } from "./logging";
|
|
28
|
+
export type { OfflineQueueOptions, QueuedMutation } from "./offline-queue";
|
|
29
|
+
export type { VersionCheckOptions } from "./version-check";
|
|
30
|
+
export type { ZodLikeSchema } from "./validation/zod";
|
|
31
|
+
|
|
32
|
+
export { getErrorCode, isProtoClientError, ProtoClientError };
|
|
33
|
+
|
|
34
|
+
export { createClientFromMap } from "./client-factory";
|
|
35
|
+
export { createCodecRegistry, codecFromType } from "./codecs";
|
|
36
|
+
export { composeInterceptors } from "./interceptors";
|
|
37
|
+
export { createAuthInterceptor, createBearerTokenHeaders } from "./auth";
|
|
38
|
+
export { createLoggingInterceptor } from "./logging";
|
|
39
|
+
export { OfflineMutationQueue } from "./offline-queue";
|
|
40
|
+
export { createPathForMethod, pathFromTemplate } from "./path-templates";
|
|
41
|
+
export {
|
|
42
|
+
CLIENT_VERSION_HEADER,
|
|
43
|
+
SERVER_MIN_CLIENT_HEADER,
|
|
44
|
+
METHOD_MAP_HASH_HEADER,
|
|
45
|
+
compareDottedVersions,
|
|
46
|
+
versionHeaders,
|
|
47
|
+
createVersionInterceptor,
|
|
48
|
+
createVersionAwareFetch,
|
|
49
|
+
assertServerMinClient,
|
|
50
|
+
} from "./version-check";
|
|
51
|
+
export { withZodValidation, wrapCodecRegistryWithZod } from "./validation/zod";
|
|
52
|
+
export {
|
|
53
|
+
encodeConnectEnvelope,
|
|
54
|
+
decodeConnectEnvelopes,
|
|
55
|
+
} from "./connect/envelope";
|
|
56
|
+
export { methodMap } from "./generated/method-map";
|
|
57
|
+
export {
|
|
58
|
+
AddUserRequest,
|
|
59
|
+
defaultCodecs,
|
|
60
|
+
DeleteUserRequest,
|
|
61
|
+
DeleteUserResponse,
|
|
62
|
+
GetUserByEmailRequest,
|
|
63
|
+
GetUserRequest,
|
|
64
|
+
ListUsersRequest,
|
|
65
|
+
ListUsersResponse,
|
|
66
|
+
UpdateUserRequest,
|
|
67
|
+
User,
|
|
68
|
+
} from "./generated/messages";
|
|
69
|
+
export {
|
|
70
|
+
isNativeGrpcAvailable,
|
|
71
|
+
installNativeGrpcBridgeFromNativeModule,
|
|
72
|
+
} from "./native-grpc";
|
|
73
|
+
|
|
74
|
+
export type CreateClientOptions = ProtoClientConfig & {
|
|
75
|
+
/** Override generated method map (advanced / tests) */
|
|
76
|
+
methodMap?: MethodMap;
|
|
77
|
+
/** Override message codecs (advanced / tests) */
|
|
78
|
+
codecs?: CodecRegistry;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Create a dynamic API client from package method metadata + your app config.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* const api = createClient({
|
|
86
|
+
* baseUrl: 'https://api.example.com',
|
|
87
|
+
* getHeaders: () => ({ Authorization: `Bearer ${token}` }),
|
|
88
|
+
* });
|
|
89
|
+
* const user = await api.userService.getUser({ id: '1' });
|
|
90
|
+
*/
|
|
91
|
+
export function createClient(config: CreateClientOptions): ProtoClient {
|
|
92
|
+
const map = config.methodMap ?? methodMap;
|
|
93
|
+
const codecs = config.codecs ?? defaultCodecs;
|
|
94
|
+
return createClientFromMap(config, map, codecs);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Alias preferred in generated `create-api.ts` drop-ins. */
|
|
98
|
+
export const createApi = createClient;
|
|
99
|
+
|
|
100
|
+
export function getDefaultMethodMap(): MethodMap {
|
|
101
|
+
return methodMap;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function getTransportCapabilities() {
|
|
105
|
+
return {
|
|
106
|
+
http: true,
|
|
107
|
+
connect: true,
|
|
108
|
+
connectStreaming: true,
|
|
109
|
+
nativeGrpc: isNativeGrpcAvailable(),
|
|
110
|
+
interceptors: true,
|
|
111
|
+
mockServer: true,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { HeaderMap, MethodMapEntry } from './types';
|
|
2
|
+
|
|
3
|
+
export type CallContext = {
|
|
4
|
+
entry: MethodMapEntry;
|
|
5
|
+
request: Record<string, unknown>;
|
|
6
|
+
headers: HeaderMap;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type UnaryHandler = (
|
|
11
|
+
ctx: CallContext
|
|
12
|
+
) => Promise<Record<string, unknown>>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Interceptor around a unary RPC. Call `next(ctx)` to continue the chain.
|
|
16
|
+
*/
|
|
17
|
+
export type Interceptor = (
|
|
18
|
+
ctx: CallContext,
|
|
19
|
+
next: UnaryHandler
|
|
20
|
+
) => Promise<Record<string, unknown>>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Compose interceptors left-to-outer (first runs first on the way in).
|
|
24
|
+
*/
|
|
25
|
+
export function composeInterceptors(
|
|
26
|
+
interceptors: Interceptor[],
|
|
27
|
+
core: UnaryHandler
|
|
28
|
+
): UnaryHandler {
|
|
29
|
+
return interceptors.reduceRight<UnaryHandler>((next, interceptor) => {
|
|
30
|
+
return (ctx) => interceptor(ctx, next);
|
|
31
|
+
}, core);
|
|
32
|
+
}
|
package/src/logging.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Interceptor } from './interceptors';
|
|
2
|
+
|
|
3
|
+
export type LoggingOptions = {
|
|
4
|
+
/** Log function (default console.debug) */
|
|
5
|
+
log?: (message: string, meta?: Record<string, unknown>) => void;
|
|
6
|
+
/** Redact these header names (case-insensitive). Default: authorization, cookie. */
|
|
7
|
+
redactHeaders?: string[];
|
|
8
|
+
/** Include request object in logs (default false — can contain PII). */
|
|
9
|
+
logRequestBody?: boolean;
|
|
10
|
+
/** Include response object in logs (default false). */
|
|
11
|
+
logResponseBody?: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function redact(
|
|
15
|
+
headers: Record<string, string>,
|
|
16
|
+
names: string[]
|
|
17
|
+
): Record<string, string> {
|
|
18
|
+
const blocked = new Set(names.map((n) => n.toLowerCase()));
|
|
19
|
+
const out: Record<string, string> = {};
|
|
20
|
+
const keys = Object.keys(headers);
|
|
21
|
+
for (let i = 0; i < keys.length; i += 1) {
|
|
22
|
+
const key = keys[i];
|
|
23
|
+
if (!key) continue;
|
|
24
|
+
out[key] = blocked.has(key.toLowerCase()) ? '[redacted]' : headers[key]!;
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Request/response logging interceptor for debug builds.
|
|
31
|
+
*/
|
|
32
|
+
export function createLoggingInterceptor(
|
|
33
|
+
options: LoggingOptions = {}
|
|
34
|
+
): Interceptor {
|
|
35
|
+
const log = options.log ?? ((msg, meta) => console.debug(msg, meta));
|
|
36
|
+
const redactHeaders = options.redactHeaders ?? [
|
|
37
|
+
'authorization',
|
|
38
|
+
'cookie',
|
|
39
|
+
'set-cookie',
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
return async (ctx, next) => {
|
|
43
|
+
const started = Date.now();
|
|
44
|
+
log('proto:request', {
|
|
45
|
+
rpcPath: ctx.entry.rpcPath,
|
|
46
|
+
headers: redact(ctx.headers, redactHeaders),
|
|
47
|
+
...(options.logRequestBody ? { request: ctx.request } : {}),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const response = await next(ctx);
|
|
52
|
+
log('proto:response', {
|
|
53
|
+
rpcPath: ctx.entry.rpcPath,
|
|
54
|
+
durationMs: Date.now() - started,
|
|
55
|
+
...(options.logResponseBody ? { response } : {}),
|
|
56
|
+
});
|
|
57
|
+
return response;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
log('proto:error', {
|
|
60
|
+
rpcPath: ctx.entry.rpcPath,
|
|
61
|
+
durationMs: Date.now() - started,
|
|
62
|
+
error,
|
|
63
|
+
});
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metro helper: treat .proto files as raw text assets for tooling/debug.
|
|
3
|
+
* Prefer `react-native-proto generate` for production clients.
|
|
4
|
+
*/
|
|
5
|
+
export function createProtoMetroConfig(baseConfig: {
|
|
6
|
+
resolver?: { sourceExts?: string[]; assetExts?: string[] };
|
|
7
|
+
transformer?: { babelTransformerPath?: string };
|
|
8
|
+
}): typeof baseConfig {
|
|
9
|
+
const resolver = baseConfig.resolver ?? {};
|
|
10
|
+
const sourceExts = new Set([...(resolver.sourceExts ?? []), 'proto']);
|
|
11
|
+
const assetExts = (resolver.assetExts ?? []).filter((ext) => ext !== 'proto');
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
...baseConfig,
|
|
15
|
+
resolver: {
|
|
16
|
+
...resolver,
|
|
17
|
+
sourceExts: Array.from(sourceExts),
|
|
18
|
+
assetExts,
|
|
19
|
+
},
|
|
20
|
+
transformer: {
|
|
21
|
+
...baseConfig.transformer,
|
|
22
|
+
babelTransformerPath: require.resolve('./transformer'),
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { transform } from './transformer';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
type TransformArgs = {
|
|
2
|
+
filename: string;
|
|
3
|
+
src: string;
|
|
4
|
+
options?: Record<string, unknown>;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
type TransformResult = {
|
|
8
|
+
code: string;
|
|
9
|
+
map?: null;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Minimal Metro transformer for .proto files.
|
|
14
|
+
* Non-proto files fall through to the upstream Babel transformer when present.
|
|
15
|
+
*/
|
|
16
|
+
export async function transform(
|
|
17
|
+
args: TransformArgs
|
|
18
|
+
): Promise<TransformResult> {
|
|
19
|
+
const { filename, src } = args;
|
|
20
|
+
|
|
21
|
+
if (!filename.endsWith('.proto')) {
|
|
22
|
+
try {
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
24
|
+
const upstream = require('@react-native/metro-babel-transformer');
|
|
25
|
+
if (upstream && typeof upstream.transform === 'function') {
|
|
26
|
+
return upstream.transform(args);
|
|
27
|
+
}
|
|
28
|
+
} catch {
|
|
29
|
+
// Fall through to identity JS export of source text
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const escaped = JSON.stringify(src);
|
|
34
|
+
return {
|
|
35
|
+
code: `module.exports = ${escaped};`,
|
|
36
|
+
map: null,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import http from "http";
|
|
2
|
+
import { ProtoClientError } from "../errors";
|
|
3
|
+
import type { CodecRegistry, MethodMap, MethodMapEntry } from "../types";
|
|
4
|
+
|
|
5
|
+
export type MockHandler = (
|
|
6
|
+
request: Record<string, unknown>,
|
|
7
|
+
entry: MethodMapEntry,
|
|
8
|
+
) =>
|
|
9
|
+
| Record<string, unknown>
|
|
10
|
+
| Promise<Record<string, unknown>>
|
|
11
|
+
| AsyncIterable<Record<string, unknown>>;
|
|
12
|
+
|
|
13
|
+
export type MockServerOptions = {
|
|
14
|
+
methodMap: MethodMap;
|
|
15
|
+
codecs: CodecRegistry;
|
|
16
|
+
/** Map rpcPath or MethodName → handler. Fallback: echo empty response encode attempt. */
|
|
17
|
+
handlers?: Record<string, MockHandler>;
|
|
18
|
+
host?: string;
|
|
19
|
+
port?: number;
|
|
20
|
+
/** Default transport content-type behavior */
|
|
21
|
+
contentType?: "application/x-protobuf" | "application/proto";
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function matchEntry(
|
|
25
|
+
methodMap: MethodMap,
|
|
26
|
+
urlPath: string,
|
|
27
|
+
): MethodMapEntry | undefined {
|
|
28
|
+
const normalized = urlPath.replace(/^\//, "");
|
|
29
|
+
for (let i = 0; i < methodMap.length; i += 1) {
|
|
30
|
+
const entry = methodMap[i];
|
|
31
|
+
if (!entry) continue;
|
|
32
|
+
const connectPath = `${entry.packageName ? `${entry.packageName}.` : ""}${entry.serviceName}/${entry.methodName}`;
|
|
33
|
+
if (
|
|
34
|
+
normalized === connectPath ||
|
|
35
|
+
normalized === entry.rpcPath ||
|
|
36
|
+
urlPath.endsWith(`/${entry.methodName}`)
|
|
37
|
+
) {
|
|
38
|
+
return entry;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function readRequestBody(req: http.IncomingMessage): Promise<Buffer> {
|
|
45
|
+
const chunks: Buffer[] = [];
|
|
46
|
+
for await (const chunk of req) {
|
|
47
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
48
|
+
}
|
|
49
|
+
return Buffer.concat(chunks);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Tiny local protobuf HTTP mock driven by the same method map + codecs as the client.
|
|
54
|
+
* Useful for demos and integration tests.
|
|
55
|
+
*/
|
|
56
|
+
export function createMockServer(options: MockServerOptions): {
|
|
57
|
+
listen: () => Promise<{ host: string; port: number; baseUrl: string }>;
|
|
58
|
+
close: () => Promise<void>;
|
|
59
|
+
server: http.Server;
|
|
60
|
+
} {
|
|
61
|
+
const host = options.host ?? "127.0.0.1";
|
|
62
|
+
const port = options.port ?? 0;
|
|
63
|
+
const contentType = options.contentType ?? "application/x-protobuf";
|
|
64
|
+
const handlers = options.handlers ?? {};
|
|
65
|
+
|
|
66
|
+
const server = http.createServer(async (req, res) => {
|
|
67
|
+
try {
|
|
68
|
+
if (req.method !== "POST" || !req.url) {
|
|
69
|
+
res.writeHead(404);
|
|
70
|
+
res.end("Not found");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const url = new URL(req.url, `http://${host}`);
|
|
75
|
+
const entry = matchEntry(options.methodMap, url.pathname);
|
|
76
|
+
if (!entry) {
|
|
77
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
78
|
+
res.end(`Unknown RPC path: ${url.pathname}`);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const requestCodec = options.codecs.get(entry.requestType);
|
|
83
|
+
const responseCodec = options.codecs.get(entry.responseType);
|
|
84
|
+
if (!requestCodec || !responseCodec) {
|
|
85
|
+
res.writeHead(500);
|
|
86
|
+
res.end("Missing codecs");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const raw = await readRequestBody(req);
|
|
91
|
+
const decoded = requestCodec.decode(new Uint8Array(raw));
|
|
92
|
+
|
|
93
|
+
const handler =
|
|
94
|
+
handlers[entry.rpcPath] ||
|
|
95
|
+
handlers[entry.methodName] ||
|
|
96
|
+
handlers[`${entry.serviceName}/${entry.methodName}`];
|
|
97
|
+
|
|
98
|
+
let result: Record<string, unknown>;
|
|
99
|
+
if (handler) {
|
|
100
|
+
const maybe = await handler(decoded, entry);
|
|
101
|
+
const asyncIter = maybe as AsyncIterable<Record<string, unknown>>;
|
|
102
|
+
if (
|
|
103
|
+
maybe &&
|
|
104
|
+
typeof maybe === "object" &&
|
|
105
|
+
typeof asyncIter[Symbol.asyncIterator] === "function"
|
|
106
|
+
) {
|
|
107
|
+
let first: Record<string, unknown> | undefined;
|
|
108
|
+
for await (const msg of asyncIter) {
|
|
109
|
+
first = msg;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
result = first ?? {};
|
|
113
|
+
} else {
|
|
114
|
+
result = (maybe as Record<string, unknown>) ?? {};
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
result = {};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const bytes = responseCodec.encode(result);
|
|
121
|
+
res.writeHead(200, {
|
|
122
|
+
"Content-Type": contentType,
|
|
123
|
+
"Content-Length": String(bytes.byteLength),
|
|
124
|
+
});
|
|
125
|
+
res.end(Buffer.from(bytes));
|
|
126
|
+
} catch (error) {
|
|
127
|
+
const message =
|
|
128
|
+
error instanceof Error ? error.message : "Mock server error";
|
|
129
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
130
|
+
res.end(message);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
server,
|
|
136
|
+
listen() {
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
server.once("error", reject);
|
|
139
|
+
server.listen(port, host, () => {
|
|
140
|
+
const address = server.address();
|
|
141
|
+
if (!address || typeof address === "string") {
|
|
142
|
+
reject(
|
|
143
|
+
new ProtoClientError({
|
|
144
|
+
code: "INTERNAL",
|
|
145
|
+
message: "Failed to bind mock server",
|
|
146
|
+
}),
|
|
147
|
+
);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
resolve({
|
|
151
|
+
host: address.address,
|
|
152
|
+
port: address.port,
|
|
153
|
+
baseUrl: `http://${address.address}:${address.port}`,
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
close() {
|
|
159
|
+
return new Promise((resolve, reject) => {
|
|
160
|
+
server.close((err) => (err ? reject(err) : resolve()));
|
|
161
|
+
});
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|