@orpc/openapi-client 0.0.0-next.999d654 → 0.0.0-next.99d5d75

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.
@@ -0,0 +1,107 @@
1
+ import { ClientContext, ClientOptions } from '@orpc/client';
2
+ import { StandardLinkCodec, StandardLinkOptions, StandardLink, StandardLinkClient } from '@orpc/client/standard';
3
+ import { ORPCError, AnyContractRouter } from '@orpc/contract';
4
+ import { Segment, Value, Promisable } from '@orpc/shared';
5
+ import { StandardHeaders, StandardLazyResponse, StandardRequest } from '@orpc/standard-server';
6
+
7
+ type StandardBracketNotationSerialized = [string, unknown][];
8
+ interface StandardBracketNotationSerializerOptions {
9
+ /**
10
+ * Maximum allowed array index for bracket notation deserialization.
11
+ *
12
+ * This helps protect against memory exhaustion attacks where malicious input
13
+ * uses extremely large array indices (e.g., `?arr[4294967296]=value`).
14
+ *
15
+ * While bracket notation creates sparse arrays that handle large indices efficiently,
16
+ * downstream code might inadvertently convert these sparse arrays to dense arrays,
17
+ * potentially creating millions of undefined elements and causing memory issues.
18
+ *
19
+ * @note Only applies to deserialization.
20
+ * @default 9_999 (array with 10,000 elements)
21
+ */
22
+ maxBracketNotationArrayIndex?: number;
23
+ }
24
+ declare class StandardBracketNotationSerializer {
25
+ private readonly maxArrayIndex;
26
+ constructor(options?: StandardBracketNotationSerializerOptions);
27
+ serialize(data: unknown, segments?: Segment[], result?: StandardBracketNotationSerialized): StandardBracketNotationSerialized;
28
+ deserialize(serialized: StandardBracketNotationSerialized): Record<string, unknown> | unknown[];
29
+ stringifyPath(segments: readonly Segment[]): string;
30
+ parsePath(path: string): string[];
31
+ }
32
+
33
+ type StandardOpenAPIJsonSerialized = [json: unknown, hasBlob: boolean];
34
+ interface StandardOpenAPICustomJsonSerializer {
35
+ condition(data: unknown): boolean;
36
+ serialize(data: any): unknown;
37
+ }
38
+ interface StandardOpenAPIJsonSerializerOptions {
39
+ customJsonSerializers?: readonly StandardOpenAPICustomJsonSerializer[];
40
+ }
41
+ declare class StandardOpenAPIJsonSerializer {
42
+ private readonly customSerializers;
43
+ constructor(options?: StandardOpenAPIJsonSerializerOptions);
44
+ serialize(data: unknown, hasBlobRef?: {
45
+ value: boolean;
46
+ }): StandardOpenAPIJsonSerialized;
47
+ }
48
+
49
+ interface StandardOpenAPISerializeOptions {
50
+ outputFormat?: 'plain' | 'URLSearchParams';
51
+ }
52
+ declare class StandardOpenAPISerializer {
53
+ #private;
54
+ private readonly jsonSerializer;
55
+ private readonly bracketNotation;
56
+ constructor(jsonSerializer: StandardOpenAPIJsonSerializer, bracketNotation: StandardBracketNotationSerializer);
57
+ serialize(data: unknown, options?: StandardOpenAPISerializeOptions): unknown;
58
+ deserialize(data: unknown): unknown;
59
+ }
60
+
61
+ interface StandardOpenapiLinkCodecOptions<T extends ClientContext> {
62
+ /**
63
+ * Base url for all requests.
64
+ */
65
+ url: Value<Promisable<string | URL>, [
66
+ options: ClientOptions<T>,
67
+ path: readonly string[],
68
+ input: unknown
69
+ ]>;
70
+ /**
71
+ * Inject headers to the request.
72
+ */
73
+ headers?: Value<Promisable<StandardHeaders | Headers>, [
74
+ options: ClientOptions<T>,
75
+ path: readonly string[],
76
+ input: unknown
77
+ ]>;
78
+ /**
79
+ * Customize how a response body is decoded into an ORPC error.
80
+ * Useful when the default decoder cannot fully interpret
81
+ * your server's error format.
82
+ *
83
+ * @remarks
84
+ * - Return `null | undefined` to fallback to default behavior.
85
+ */
86
+ customErrorResponseBodyDecoder?: (deserializedBody: unknown, response: StandardLazyResponse) => ORPCError<any, any> | null | undefined;
87
+ }
88
+ declare class StandardOpenapiLinkCodec<T extends ClientContext> implements StandardLinkCodec<T> {
89
+ #private;
90
+ private readonly contract;
91
+ private readonly serializer;
92
+ private readonly baseUrl;
93
+ private readonly headers;
94
+ private readonly customErrorResponseBodyDecoder;
95
+ constructor(contract: AnyContractRouter, serializer: StandardOpenAPISerializer, options: StandardOpenapiLinkCodecOptions<T>);
96
+ encode(path: readonly string[], input: unknown, options: ClientOptions<T>): Promise<StandardRequest>;
97
+ decode(response: StandardLazyResponse, _options: ClientOptions<T>, path: readonly string[]): Promise<unknown>;
98
+ }
99
+
100
+ interface StandardOpenAPILinkOptions<T extends ClientContext> extends StandardLinkOptions<T>, StandardOpenapiLinkCodecOptions<T>, StandardOpenAPIJsonSerializerOptions {
101
+ }
102
+ declare class StandardOpenAPILink<T extends ClientContext> extends StandardLink<T> {
103
+ constructor(contract: AnyContractRouter, linkClient: StandardLinkClient<T>, options: StandardOpenAPILinkOptions<T>);
104
+ }
105
+
106
+ export { StandardBracketNotationSerializer as b, StandardOpenAPIJsonSerializer as f, StandardOpenAPILink as h, StandardOpenapiLinkCodec as j, StandardOpenAPISerializer as l };
107
+ export type { StandardBracketNotationSerialized as S, StandardBracketNotationSerializerOptions as a, StandardOpenAPIJsonSerialized as c, StandardOpenAPICustomJsonSerializer as d, StandardOpenAPIJsonSerializerOptions as e, StandardOpenAPILinkOptions as g, StandardOpenapiLinkCodecOptions as i, StandardOpenAPISerializeOptions as k };
@@ -0,0 +1,146 @@
1
+ import { isObject, NullProtoObj } from '@orpc/shared';
2
+
3
+ class StandardBracketNotationSerializer {
4
+ maxArrayIndex;
5
+ constructor(options = {}) {
6
+ this.maxArrayIndex = options.maxBracketNotationArrayIndex ?? 9999;
7
+ }
8
+ serialize(data, segments = [], result = []) {
9
+ if (Array.isArray(data)) {
10
+ data.forEach((item, i) => {
11
+ this.serialize(item, [...segments, i], result);
12
+ });
13
+ } else if (isObject(data)) {
14
+ for (const key in data) {
15
+ this.serialize(data[key], [...segments, key], result);
16
+ }
17
+ } else {
18
+ result.push([this.stringifyPath(segments), data]);
19
+ }
20
+ return result;
21
+ }
22
+ deserialize(serialized) {
23
+ if (serialized.length === 0) {
24
+ return {};
25
+ }
26
+ const arrayPushStyles = /* @__PURE__ */ new WeakSet();
27
+ const ref = { value: [] };
28
+ for (const [path, value] of serialized) {
29
+ const segments = this.parsePath(path);
30
+ let currentRef = ref;
31
+ let nextSegment = "value";
32
+ segments.forEach((segment, i) => {
33
+ if (!Array.isArray(currentRef[nextSegment]) && !isObject(currentRef[nextSegment])) {
34
+ currentRef[nextSegment] = [];
35
+ }
36
+ if (i !== segments.length - 1) {
37
+ if (Array.isArray(currentRef[nextSegment]) && !isValidArrayIndex(segment, this.maxArrayIndex)) {
38
+ if (arrayPushStyles.has(currentRef[nextSegment])) {
39
+ arrayPushStyles.delete(currentRef[nextSegment]);
40
+ currentRef[nextSegment] = pushStyleArrayToObject(currentRef[nextSegment]);
41
+ } else {
42
+ currentRef[nextSegment] = arrayToObject(currentRef[nextSegment]);
43
+ }
44
+ }
45
+ } else {
46
+ if (Array.isArray(currentRef[nextSegment])) {
47
+ if (segment === "") {
48
+ if (currentRef[nextSegment].length && !arrayPushStyles.has(currentRef[nextSegment])) {
49
+ currentRef[nextSegment] = arrayToObject(currentRef[nextSegment]);
50
+ }
51
+ } else {
52
+ if (arrayPushStyles.has(currentRef[nextSegment])) {
53
+ arrayPushStyles.delete(currentRef[nextSegment]);
54
+ currentRef[nextSegment] = pushStyleArrayToObject(currentRef[nextSegment]);
55
+ } else if (!isValidArrayIndex(segment, this.maxArrayIndex)) {
56
+ currentRef[nextSegment] = arrayToObject(currentRef[nextSegment]);
57
+ }
58
+ }
59
+ }
60
+ }
61
+ currentRef = currentRef[nextSegment];
62
+ nextSegment = segment;
63
+ });
64
+ if (Array.isArray(currentRef) && nextSegment === "") {
65
+ arrayPushStyles.add(currentRef);
66
+ currentRef.push(value);
67
+ } else if (nextSegment in currentRef) {
68
+ if (Array.isArray(currentRef[nextSegment])) {
69
+ currentRef[nextSegment].push(value);
70
+ } else {
71
+ currentRef[nextSegment] = [currentRef[nextSegment], value];
72
+ }
73
+ } else {
74
+ currentRef[nextSegment] = value;
75
+ }
76
+ }
77
+ return ref.value;
78
+ }
79
+ stringifyPath(segments) {
80
+ return segments.map((segment) => {
81
+ return segment.toString().replace(/[\\[\]]/g, (match) => {
82
+ switch (match) {
83
+ case "\\":
84
+ return "\\\\";
85
+ case "[":
86
+ return "\\[";
87
+ case "]":
88
+ return "\\]";
89
+ /* v8 ignore next 2 */
90
+ default:
91
+ return match;
92
+ }
93
+ });
94
+ }).reduce((result, segment, i) => {
95
+ if (i === 0) {
96
+ return segment;
97
+ }
98
+ return `${result}[${segment}]`;
99
+ }, "");
100
+ }
101
+ parsePath(path) {
102
+ const segments = [];
103
+ let inBrackets = false;
104
+ let currentSegment = "";
105
+ let backslashCount = 0;
106
+ for (let i = 0; i < path.length; i++) {
107
+ const char = path[i];
108
+ const nextChar = path[i + 1];
109
+ if (inBrackets && char === "]" && (nextChar === void 0 || nextChar === "[") && backslashCount % 2 === 0) {
110
+ if (nextChar === void 0) {
111
+ inBrackets = false;
112
+ }
113
+ segments.push(currentSegment);
114
+ currentSegment = "";
115
+ i++;
116
+ } else if (segments.length === 0 && char === "[" && backslashCount % 2 === 0) {
117
+ inBrackets = true;
118
+ segments.push(currentSegment);
119
+ currentSegment = "";
120
+ } else if (char === "\\") {
121
+ backslashCount++;
122
+ } else {
123
+ currentSegment += "\\".repeat(backslashCount / 2) + char;
124
+ backslashCount = 0;
125
+ }
126
+ }
127
+ return inBrackets || segments.length === 0 ? [path] : segments;
128
+ }
129
+ }
130
+ function isValidArrayIndex(value, maxIndex) {
131
+ return /^0$|^[1-9]\d*$/.test(value) && Number(value) <= maxIndex;
132
+ }
133
+ function arrayToObject(array) {
134
+ const obj = new NullProtoObj();
135
+ array.forEach((item, i) => {
136
+ obj[i] = item;
137
+ });
138
+ return obj;
139
+ }
140
+ function pushStyleArrayToObject(array) {
141
+ const obj = new NullProtoObj();
142
+ obj[""] = array.length === 1 ? array[0] : array;
143
+ return obj;
144
+ }
145
+
146
+ export { StandardBracketNotationSerializer as S };
package/package.json CHANGED
@@ -1,16 +1,15 @@
1
1
  {
2
2
  "name": "@orpc/openapi-client",
3
3
  "type": "module",
4
- "version": "0.0.0-next.999d654",
4
+ "version": "0.0.0-next.99d5d75",
5
5
  "license": "MIT",
6
- "homepage": "https://orpc.unnoq.com",
6
+ "homepage": "https://orpc.dev",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git+https://github.com/unnoq/orpc.git",
9
+ "url": "git+https://github.com/middleapi/orpc.git",
10
10
  "directory": "packages/openapi-client"
11
11
  },
12
12
  "keywords": [
13
- "unnoq",
14
13
  "orpc"
15
14
  ],
16
15
  "exports": {
@@ -19,19 +18,33 @@
19
18
  "import": "./dist/index.mjs",
20
19
  "default": "./dist/index.mjs"
21
20
  },
21
+ "./helpers": {
22
+ "types": "./dist/helpers/index.d.mts",
23
+ "import": "./dist/helpers/index.mjs",
24
+ "default": "./dist/helpers/index.mjs"
25
+ },
22
26
  "./standard": {
23
27
  "types": "./dist/adapters/standard/index.d.mts",
24
28
  "import": "./dist/adapters/standard/index.mjs",
25
29
  "default": "./dist/adapters/standard/index.mjs"
30
+ },
31
+ "./fetch": {
32
+ "types": "./dist/adapters/fetch/index.d.mts",
33
+ "import": "./dist/adapters/fetch/index.mjs",
34
+ "default": "./dist/adapters/fetch/index.mjs"
26
35
  }
27
36
  },
28
37
  "files": [
29
38
  "dist"
30
39
  ],
31
40
  "dependencies": {
32
- "@orpc/client": "0.0.0-next.999d654",
33
- "@orpc/standard-server": "0.0.0-next.999d654",
34
- "@orpc/shared": "0.0.0-next.999d654"
41
+ "@orpc/client": "0.0.0-next.99d5d75",
42
+ "@orpc/contract": "0.0.0-next.99d5d75",
43
+ "@orpc/shared": "0.0.0-next.99d5d75",
44
+ "@orpc/standard-server": "0.0.0-next.99d5d75"
45
+ },
46
+ "devDependencies": {
47
+ "@orpc/server": "0.0.0-next.99d5d75"
35
48
  },
36
49
  "scripts": {
37
50
  "build": "unbuild",