@prisma/param-graph 7.4.0-integration-parameterization.11 → 7.4.0-integration-parameterization.12
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/serialization.d.ts +28 -42
- package/dist/serialization.js +85 -104
- package/dist/serialization.mjs +85 -104
- package/dist/serialization.test.js +101 -0
- package/dist/serialization.test.mjs +101 -0
- package/package.json +1 -1
package/dist/serialization.d.ts
CHANGED
|
@@ -20,84 +20,70 @@
|
|
|
20
20
|
* - **Structure goes binary**: Indices, flags, masks benefit from compact encoding
|
|
21
21
|
* - **Best of both**: Fast parsing + compact size where it matters
|
|
22
22
|
*
|
|
23
|
-
* ##
|
|
23
|
+
* ## Variable-Length Encoding
|
|
24
24
|
*
|
|
25
|
-
*
|
|
25
|
+
* All integer values (except fixed-size fields like `scalarMask` and `flags`) use
|
|
26
|
+
* unsigned LEB128 (Little Endian Base 128) variable-length encoding:
|
|
26
27
|
*
|
|
27
|
-
* -
|
|
28
|
-
* -
|
|
28
|
+
* - Values 0-127: 1 byte
|
|
29
|
+
* - Values 128-16383: 2 bytes
|
|
30
|
+
* - Values 16384-2097151: 3 bytes
|
|
31
|
+
* - And so on...
|
|
29
32
|
*
|
|
30
|
-
*
|
|
33
|
+
* Optional values use value+1 encoding: 0 means "none/undefined", N+1 means actual value N.
|
|
31
34
|
*
|
|
32
35
|
* ## Binary Blob Layout
|
|
33
36
|
*
|
|
34
|
-
* All multi-byte integers are little-endian.
|
|
35
|
-
*
|
|
36
37
|
* ```
|
|
37
38
|
* ┌───────────────────────────────────────────────────────────────────┐
|
|
38
39
|
* │ HEADER │
|
|
39
40
|
* ├───────────────────────────────────────────────────────────────────┤
|
|
40
|
-
* │
|
|
41
|
-
* │
|
|
42
|
-
* │
|
|
43
|
-
* │ outputNodeCount: word │ Number of output nodes │
|
|
44
|
-
* │ rootCount: word │ Number of root entries │
|
|
41
|
+
* │ inputNodeCount: varuint │
|
|
42
|
+
* │ outputNodeCount: varuint │
|
|
43
|
+
* │ rootCount: varuint │
|
|
45
44
|
* └───────────────────────────────────────────────────────────────────┘
|
|
46
45
|
*
|
|
47
46
|
* ┌───────────────────────────────────────────────────────────────────┐
|
|
48
47
|
* │ INPUT NODES (repeated inputNodeCount times) │
|
|
49
48
|
* ├───────────────────────────────────────────────────────────────────┤
|
|
50
|
-
* │ edgeCount:
|
|
51
|
-
* │ edges[]
|
|
49
|
+
* │ edgeCount: varuint │
|
|
50
|
+
* │ edges[] │
|
|
52
51
|
* └───────────────────────────────────────────────────────────────────┘
|
|
53
52
|
*
|
|
54
53
|
* ┌───────────────────────────────────────────────────────────────────┐
|
|
55
|
-
* │ INPUT EDGE
|
|
54
|
+
* │ INPUT EDGE │
|
|
56
55
|
* ├───────────────────────────────────────────────────────────────────┤
|
|
57
|
-
* │ fieldIndex:
|
|
58
|
-
* │ scalarMask: u16
|
|
59
|
-
* │
|
|
60
|
-
* │
|
|
61
|
-
* │
|
|
62
|
-
* │ flags: u8 │ Edge capability flags │
|
|
63
|
-
* │ padding: 1|3 bytes │ Alignment padding (1 compact, 3 wide) │
|
|
56
|
+
* │ fieldIndex: varuint │
|
|
57
|
+
* │ scalarMask: u16 │
|
|
58
|
+
* │ childNodeId: varuint (0=none, N+1=actual) │
|
|
59
|
+
* │ enumNameIndex: varuint (0=none, N+1=actual) │
|
|
60
|
+
* │ flags: u8 │
|
|
64
61
|
* └───────────────────────────────────────────────────────────────────┘
|
|
65
62
|
*
|
|
66
63
|
* ┌───────────────────────────────────────────────────────────────────┐
|
|
67
64
|
* │ OUTPUT NODES (repeated outputNodeCount times) │
|
|
68
65
|
* ├───────────────────────────────────────────────────────────────────┤
|
|
69
|
-
* │ edgeCount:
|
|
70
|
-
* │ edges[]
|
|
66
|
+
* │ edgeCount: varuint │
|
|
67
|
+
* │ edges[] │
|
|
71
68
|
* └───────────────────────────────────────────────────────────────────┘
|
|
72
69
|
*
|
|
73
70
|
* ┌───────────────────────────────────────────────────────────────────┐
|
|
74
|
-
* │ OUTPUT EDGE
|
|
71
|
+
* │ OUTPUT EDGE │
|
|
75
72
|
* ├───────────────────────────────────────────────────────────────────┤
|
|
76
|
-
* │ fieldIndex:
|
|
77
|
-
* │ argsNodeId:
|
|
78
|
-
* │ outputNodeId:
|
|
73
|
+
* │ fieldIndex: varuint │
|
|
74
|
+
* │ argsNodeId: varuint (0=none, N+1=actual) │
|
|
75
|
+
* │ outputNodeId: varuint (0=none, N+1=actual) │
|
|
79
76
|
* └───────────────────────────────────────────────────────────────────┘
|
|
80
77
|
*
|
|
81
78
|
* ┌───────────────────────────────────────────────────────────────────┐
|
|
82
79
|
* │ ROOTS (repeated rootCount times) │
|
|
83
80
|
* ├───────────────────────────────────────────────────────────────────┤
|
|
84
|
-
* │ keyIndex:
|
|
85
|
-
* │ argsNodeId:
|
|
86
|
-
* │ outputNodeId:
|
|
81
|
+
* │ keyIndex: varuint │
|
|
82
|
+
* │ argsNodeId: varuint (0=none, N+1=actual) │
|
|
83
|
+
* │ outputNodeId: varuint (0=none, N+1=actual) │
|
|
87
84
|
* └───────────────────────────────────────────────────────────────────┘
|
|
88
85
|
* ```
|
|
89
86
|
*
|
|
90
|
-
* Where "word" is u16 (compact) or u32 (wide).
|
|
91
|
-
*
|
|
92
|
-
* ## Size Summary
|
|
93
|
-
*
|
|
94
|
-
* | Component | Compact | Wide |
|
|
95
|
-
* |----------------|----------|----------|
|
|
96
|
-
* | Header | 8 bytes | 16 bytes |
|
|
97
|
-
* | Input Edge | 10 bytes | 20 bytes |
|
|
98
|
-
* | Output Edge | 6 bytes | 12 bytes |
|
|
99
|
-
* | Root Entry | 6 bytes | 12 bytes |
|
|
100
|
-
*
|
|
101
87
|
* ## Embedding in Generated Client
|
|
102
88
|
*
|
|
103
89
|
* ```js
|
package/dist/serialization.js
CHANGED
|
@@ -28,20 +28,22 @@ function serializeParamGraph(data) {
|
|
|
28
28
|
function deserializeParamGraph(serialized) {
|
|
29
29
|
return new Deserializer(serialized).deserialize();
|
|
30
30
|
}
|
|
31
|
-
const FORMAT_COMPACT = 0;
|
|
32
|
-
const FORMAT_WIDE = 1;
|
|
33
|
-
const NONE_16 = 65535;
|
|
34
|
-
const NONE_32 = 4294967295;
|
|
35
|
-
const MAX_COMPACT_INDEX = 65534;
|
|
36
31
|
function encodeBase64url(bytes) {
|
|
37
32
|
return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64url");
|
|
38
33
|
}
|
|
39
34
|
function decodeBase64url(str) {
|
|
40
35
|
return Buffer.from(str, "base64url");
|
|
41
36
|
}
|
|
37
|
+
function varuintSize(value) {
|
|
38
|
+
let size = 1;
|
|
39
|
+
while (value >= 128) {
|
|
40
|
+
size++;
|
|
41
|
+
value >>>= 7;
|
|
42
|
+
}
|
|
43
|
+
return size;
|
|
44
|
+
}
|
|
42
45
|
class Serializer {
|
|
43
46
|
#data;
|
|
44
|
-
#useWide;
|
|
45
47
|
#buffer;
|
|
46
48
|
#view;
|
|
47
49
|
#offset = 0;
|
|
@@ -49,13 +51,6 @@ class Serializer {
|
|
|
49
51
|
constructor(data) {
|
|
50
52
|
this.#data = data;
|
|
51
53
|
this.#rootKeys = Object.keys(data.roots);
|
|
52
|
-
const maxIndex = Math.max(
|
|
53
|
-
data.strings.length,
|
|
54
|
-
data.inputNodes.length,
|
|
55
|
-
data.outputNodes.length,
|
|
56
|
-
this.#rootKeys.length
|
|
57
|
-
);
|
|
58
|
-
this.#useWide = maxIndex > MAX_COMPACT_INDEX;
|
|
59
54
|
const size = this.#calculateBufferSize();
|
|
60
55
|
this.#buffer = new ArrayBuffer(size);
|
|
61
56
|
this.#view = new DataView(this.#buffer);
|
|
@@ -67,25 +62,18 @@ class Serializer {
|
|
|
67
62
|
this.#writeRoots();
|
|
68
63
|
return {
|
|
69
64
|
strings: this.#data.strings,
|
|
70
|
-
graph: encodeBase64url(new Uint8Array(this.#buffer))
|
|
65
|
+
graph: encodeBase64url(new Uint8Array(this.#buffer, 0, this.#offset))
|
|
71
66
|
};
|
|
72
67
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return this.#useWide ? NONE_32 : NONE_16;
|
|
78
|
-
}
|
|
79
|
-
#writeWord(value) {
|
|
80
|
-
if (this.#useWide) {
|
|
81
|
-
this.#view.setUint32(this.#offset, value, true);
|
|
82
|
-
} else {
|
|
83
|
-
this.#view.setUint16(this.#offset, value, true);
|
|
68
|
+
#writeVaruint(value) {
|
|
69
|
+
while (value >= 128) {
|
|
70
|
+
this.#view.setUint8(this.#offset++, value & 127 | 128);
|
|
71
|
+
value >>>= 7;
|
|
84
72
|
}
|
|
85
|
-
this.#offset
|
|
73
|
+
this.#view.setUint8(this.#offset++, value);
|
|
86
74
|
}
|
|
87
|
-
#
|
|
88
|
-
this.#
|
|
75
|
+
#writeOptionalVaruint(value) {
|
|
76
|
+
this.#writeVaruint(value === void 0 ? 0 : value + 1);
|
|
89
77
|
}
|
|
90
78
|
#writeByte(value) {
|
|
91
79
|
this.#view.setUint8(this.#offset, value);
|
|
@@ -95,58 +83,70 @@ class Serializer {
|
|
|
95
83
|
this.#view.setUint16(this.#offset, value, true);
|
|
96
84
|
this.#offset += 2;
|
|
97
85
|
}
|
|
98
|
-
#skip(bytes) {
|
|
99
|
-
this.#offset += bytes;
|
|
100
|
-
}
|
|
101
86
|
#calculateBufferSize() {
|
|
102
|
-
let size =
|
|
87
|
+
let size = 0;
|
|
88
|
+
size += varuintSize(this.#data.inputNodes.length);
|
|
89
|
+
size += varuintSize(this.#data.outputNodes.length);
|
|
90
|
+
size += varuintSize(this.#rootKeys.length);
|
|
103
91
|
for (const node of this.#data.inputNodes) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
92
|
+
const fieldIndices = Object.keys(node.edges).map(Number);
|
|
93
|
+
size += varuintSize(fieldIndices.length);
|
|
94
|
+
for (const fieldIndex of fieldIndices) {
|
|
95
|
+
const edge = node.edges[fieldIndex];
|
|
96
|
+
size += varuintSize(fieldIndex);
|
|
97
|
+
size += 2;
|
|
98
|
+
size += varuintSize(edge.childNodeId === void 0 ? 0 : edge.childNodeId + 1);
|
|
99
|
+
size += varuintSize(edge.enumNameIndex === void 0 ? 0 : edge.enumNameIndex + 1);
|
|
100
|
+
size += 1;
|
|
101
|
+
}
|
|
107
102
|
}
|
|
108
103
|
for (const node of this.#data.outputNodes) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
104
|
+
const fieldIndices = Object.keys(node.edges).map(Number);
|
|
105
|
+
size += varuintSize(fieldIndices.length);
|
|
106
|
+
for (const fieldIndex of fieldIndices) {
|
|
107
|
+
const edge = node.edges[fieldIndex];
|
|
108
|
+
size += varuintSize(fieldIndex);
|
|
109
|
+
size += varuintSize(edge.argsNodeId === void 0 ? 0 : edge.argsNodeId + 1);
|
|
110
|
+
size += varuintSize(edge.outputNodeId === void 0 ? 0 : edge.outputNodeId + 1);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
for (const key of this.#rootKeys) {
|
|
114
|
+
const root = this.#data.roots[key];
|
|
115
|
+
const keyIndex = this.#data.strings.indexOf(key);
|
|
116
|
+
size += varuintSize(keyIndex);
|
|
117
|
+
size += varuintSize(root.argsNodeId === void 0 ? 0 : root.argsNodeId + 1);
|
|
118
|
+
size += varuintSize(root.outputNodeId === void 0 ? 0 : root.outputNodeId + 1);
|
|
112
119
|
}
|
|
113
|
-
size += this.#rootKeys.length * (this.#useWide ? 12 : 6);
|
|
114
120
|
return size;
|
|
115
121
|
}
|
|
116
122
|
#writeHeader() {
|
|
117
|
-
this.#
|
|
118
|
-
this.#
|
|
119
|
-
this.#
|
|
120
|
-
this.#writeWord(this.#data.outputNodes.length);
|
|
121
|
-
this.#writeWord(this.#rootKeys.length);
|
|
123
|
+
this.#writeVaruint(this.#data.inputNodes.length);
|
|
124
|
+
this.#writeVaruint(this.#data.outputNodes.length);
|
|
125
|
+
this.#writeVaruint(this.#rootKeys.length);
|
|
122
126
|
}
|
|
123
127
|
#writeInputNodes() {
|
|
124
128
|
for (const node of this.#data.inputNodes) {
|
|
125
129
|
const fieldIndices = Object.keys(node.edges).map(Number);
|
|
126
|
-
this.#
|
|
130
|
+
this.#writeVaruint(fieldIndices.length);
|
|
127
131
|
for (const fieldIndex of fieldIndices) {
|
|
128
132
|
const edge = node.edges[fieldIndex];
|
|
129
|
-
this.#
|
|
133
|
+
this.#writeVaruint(fieldIndex);
|
|
130
134
|
this.#writeU16(edge.scalarMask ?? 0);
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
this.#writeOptionalWord(edge.childNodeId);
|
|
135
|
-
this.#writeOptionalWord(edge.enumNameIndex);
|
|
135
|
+
this.#writeOptionalVaruint(edge.childNodeId);
|
|
136
|
+
this.#writeOptionalVaruint(edge.enumNameIndex);
|
|
136
137
|
this.#writeByte(edge.flags);
|
|
137
|
-
this.#skip(this.#useWide ? 3 : 1);
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
#writeOutputNodes() {
|
|
142
142
|
for (const node of this.#data.outputNodes) {
|
|
143
143
|
const fieldIndices = Object.keys(node.edges).map(Number);
|
|
144
|
-
this.#
|
|
144
|
+
this.#writeVaruint(fieldIndices.length);
|
|
145
145
|
for (const fieldIndex of fieldIndices) {
|
|
146
146
|
const edge = node.edges[fieldIndex];
|
|
147
|
-
this.#
|
|
148
|
-
this.#
|
|
149
|
-
this.#
|
|
147
|
+
this.#writeVaruint(fieldIndex);
|
|
148
|
+
this.#writeOptionalVaruint(edge.argsNodeId);
|
|
149
|
+
this.#writeOptionalVaruint(edge.outputNodeId);
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
}
|
|
@@ -157,9 +157,9 @@ class Serializer {
|
|
|
157
157
|
if (keyIndex === -1) {
|
|
158
158
|
throw new Error(`Root key "${key}" not found in strings table`);
|
|
159
159
|
}
|
|
160
|
-
this.#
|
|
161
|
-
this.#
|
|
162
|
-
this.#
|
|
160
|
+
this.#writeVaruint(keyIndex);
|
|
161
|
+
this.#writeOptionalVaruint(root.argsNodeId);
|
|
162
|
+
this.#writeOptionalVaruint(root.outputNodeId);
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
165
|
}
|
|
@@ -167,7 +167,6 @@ class Deserializer {
|
|
|
167
167
|
#serialized;
|
|
168
168
|
#view;
|
|
169
169
|
#offset = 0;
|
|
170
|
-
#useWide = false;
|
|
171
170
|
constructor(serialized) {
|
|
172
171
|
this.#serialized = serialized;
|
|
173
172
|
const bytes = decodeBase64url(serialized.graph);
|
|
@@ -185,25 +184,20 @@ class Deserializer {
|
|
|
185
184
|
roots
|
|
186
185
|
};
|
|
187
186
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
value = this.#view.getUint32(this.#offset, true);
|
|
198
|
-
} else {
|
|
199
|
-
value = this.#view.getUint16(this.#offset, true);
|
|
200
|
-
}
|
|
201
|
-
this.#offset += this.#wordSize;
|
|
187
|
+
#readVaruint() {
|
|
188
|
+
let value = 0;
|
|
189
|
+
let shift = 0;
|
|
190
|
+
let byte;
|
|
191
|
+
do {
|
|
192
|
+
byte = this.#view.getUint8(this.#offset++);
|
|
193
|
+
value |= (byte & 127) << shift;
|
|
194
|
+
shift += 7;
|
|
195
|
+
} while (byte >= 128);
|
|
202
196
|
return value;
|
|
203
197
|
}
|
|
204
|
-
#
|
|
205
|
-
const value = this.#
|
|
206
|
-
return value ===
|
|
198
|
+
#readOptionalVaruint() {
|
|
199
|
+
const value = this.#readVaruint();
|
|
200
|
+
return value === 0 ? void 0 : value - 1;
|
|
207
201
|
}
|
|
208
202
|
#readByte() {
|
|
209
203
|
const value = this.#view.getUint8(this.#offset);
|
|
@@ -215,36 +209,23 @@ class Deserializer {
|
|
|
215
209
|
this.#offset += 2;
|
|
216
210
|
return value;
|
|
217
211
|
}
|
|
218
|
-
#skip(bytes) {
|
|
219
|
-
this.#offset += bytes;
|
|
220
|
-
}
|
|
221
212
|
#readHeader() {
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
this.#useWide = format === FORMAT_WIDE;
|
|
227
|
-
this.#skip(this.#useWide ? 3 : 1);
|
|
228
|
-
const inputNodeCount = this.#readWord();
|
|
229
|
-
const outputNodeCount = this.#readWord();
|
|
230
|
-
const rootCount = this.#readWord();
|
|
213
|
+
const inputNodeCount = this.#readVaruint();
|
|
214
|
+
const outputNodeCount = this.#readVaruint();
|
|
215
|
+
const rootCount = this.#readVaruint();
|
|
231
216
|
return { inputNodeCount, outputNodeCount, rootCount };
|
|
232
217
|
}
|
|
233
218
|
#readInputNodes(count) {
|
|
234
219
|
const inputNodes = [];
|
|
235
220
|
for (let i = 0; i < count; i++) {
|
|
236
|
-
const edgeCount = this.#
|
|
221
|
+
const edgeCount = this.#readVaruint();
|
|
237
222
|
const edges = {};
|
|
238
223
|
for (let j = 0; j < edgeCount; j++) {
|
|
239
|
-
const fieldIndex = this.#
|
|
224
|
+
const fieldIndex = this.#readVaruint();
|
|
240
225
|
const scalarMask = this.#readU16();
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
const childNodeId = this.#readOptionalWord();
|
|
245
|
-
const enumNameIndex = this.#readOptionalWord();
|
|
226
|
+
const childNodeId = this.#readOptionalVaruint();
|
|
227
|
+
const enumNameIndex = this.#readOptionalVaruint();
|
|
246
228
|
const flags = this.#readByte();
|
|
247
|
-
this.#skip(this.#useWide ? 3 : 1);
|
|
248
229
|
const edge = { flags };
|
|
249
230
|
if (scalarMask !== 0) edge.scalarMask = scalarMask;
|
|
250
231
|
if (childNodeId !== void 0) edge.childNodeId = childNodeId;
|
|
@@ -258,12 +239,12 @@ class Deserializer {
|
|
|
258
239
|
#readOutputNodes(count) {
|
|
259
240
|
const outputNodes = [];
|
|
260
241
|
for (let i = 0; i < count; i++) {
|
|
261
|
-
const edgeCount = this.#
|
|
242
|
+
const edgeCount = this.#readVaruint();
|
|
262
243
|
const edges = {};
|
|
263
244
|
for (let j = 0; j < edgeCount; j++) {
|
|
264
|
-
const fieldIndex = this.#
|
|
265
|
-
const argsNodeId = this.#
|
|
266
|
-
const outputNodeId = this.#
|
|
245
|
+
const fieldIndex = this.#readVaruint();
|
|
246
|
+
const argsNodeId = this.#readOptionalVaruint();
|
|
247
|
+
const outputNodeId = this.#readOptionalVaruint();
|
|
267
248
|
const edge = {};
|
|
268
249
|
if (argsNodeId !== void 0) edge.argsNodeId = argsNodeId;
|
|
269
250
|
if (outputNodeId !== void 0) edge.outputNodeId = outputNodeId;
|
|
@@ -276,9 +257,9 @@ class Deserializer {
|
|
|
276
257
|
#readRoots(count) {
|
|
277
258
|
const roots = {};
|
|
278
259
|
for (let i = 0; i < count; i++) {
|
|
279
|
-
const keyIndex = this.#
|
|
280
|
-
const argsNodeId = this.#
|
|
281
|
-
const outputNodeId = this.#
|
|
260
|
+
const keyIndex = this.#readVaruint();
|
|
261
|
+
const argsNodeId = this.#readOptionalVaruint();
|
|
262
|
+
const outputNodeId = this.#readOptionalVaruint();
|
|
282
263
|
const key = this.#serialized.strings[keyIndex];
|
|
283
264
|
const root = {};
|
|
284
265
|
if (argsNodeId !== void 0) root.argsNodeId = argsNodeId;
|
package/dist/serialization.mjs
CHANGED
|
@@ -4,20 +4,22 @@ function serializeParamGraph(data) {
|
|
|
4
4
|
function deserializeParamGraph(serialized) {
|
|
5
5
|
return new Deserializer(serialized).deserialize();
|
|
6
6
|
}
|
|
7
|
-
const FORMAT_COMPACT = 0;
|
|
8
|
-
const FORMAT_WIDE = 1;
|
|
9
|
-
const NONE_16 = 65535;
|
|
10
|
-
const NONE_32 = 4294967295;
|
|
11
|
-
const MAX_COMPACT_INDEX = 65534;
|
|
12
7
|
function encodeBase64url(bytes) {
|
|
13
8
|
return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64url");
|
|
14
9
|
}
|
|
15
10
|
function decodeBase64url(str) {
|
|
16
11
|
return Buffer.from(str, "base64url");
|
|
17
12
|
}
|
|
13
|
+
function varuintSize(value) {
|
|
14
|
+
let size = 1;
|
|
15
|
+
while (value >= 128) {
|
|
16
|
+
size++;
|
|
17
|
+
value >>>= 7;
|
|
18
|
+
}
|
|
19
|
+
return size;
|
|
20
|
+
}
|
|
18
21
|
class Serializer {
|
|
19
22
|
#data;
|
|
20
|
-
#useWide;
|
|
21
23
|
#buffer;
|
|
22
24
|
#view;
|
|
23
25
|
#offset = 0;
|
|
@@ -25,13 +27,6 @@ class Serializer {
|
|
|
25
27
|
constructor(data) {
|
|
26
28
|
this.#data = data;
|
|
27
29
|
this.#rootKeys = Object.keys(data.roots);
|
|
28
|
-
const maxIndex = Math.max(
|
|
29
|
-
data.strings.length,
|
|
30
|
-
data.inputNodes.length,
|
|
31
|
-
data.outputNodes.length,
|
|
32
|
-
this.#rootKeys.length
|
|
33
|
-
);
|
|
34
|
-
this.#useWide = maxIndex > MAX_COMPACT_INDEX;
|
|
35
30
|
const size = this.#calculateBufferSize();
|
|
36
31
|
this.#buffer = new ArrayBuffer(size);
|
|
37
32
|
this.#view = new DataView(this.#buffer);
|
|
@@ -43,25 +38,18 @@ class Serializer {
|
|
|
43
38
|
this.#writeRoots();
|
|
44
39
|
return {
|
|
45
40
|
strings: this.#data.strings,
|
|
46
|
-
graph: encodeBase64url(new Uint8Array(this.#buffer))
|
|
41
|
+
graph: encodeBase64url(new Uint8Array(this.#buffer, 0, this.#offset))
|
|
47
42
|
};
|
|
48
43
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return this.#useWide ? NONE_32 : NONE_16;
|
|
54
|
-
}
|
|
55
|
-
#writeWord(value) {
|
|
56
|
-
if (this.#useWide) {
|
|
57
|
-
this.#view.setUint32(this.#offset, value, true);
|
|
58
|
-
} else {
|
|
59
|
-
this.#view.setUint16(this.#offset, value, true);
|
|
44
|
+
#writeVaruint(value) {
|
|
45
|
+
while (value >= 128) {
|
|
46
|
+
this.#view.setUint8(this.#offset++, value & 127 | 128);
|
|
47
|
+
value >>>= 7;
|
|
60
48
|
}
|
|
61
|
-
this.#offset
|
|
49
|
+
this.#view.setUint8(this.#offset++, value);
|
|
62
50
|
}
|
|
63
|
-
#
|
|
64
|
-
this.#
|
|
51
|
+
#writeOptionalVaruint(value) {
|
|
52
|
+
this.#writeVaruint(value === void 0 ? 0 : value + 1);
|
|
65
53
|
}
|
|
66
54
|
#writeByte(value) {
|
|
67
55
|
this.#view.setUint8(this.#offset, value);
|
|
@@ -71,58 +59,70 @@ class Serializer {
|
|
|
71
59
|
this.#view.setUint16(this.#offset, value, true);
|
|
72
60
|
this.#offset += 2;
|
|
73
61
|
}
|
|
74
|
-
#skip(bytes) {
|
|
75
|
-
this.#offset += bytes;
|
|
76
|
-
}
|
|
77
62
|
#calculateBufferSize() {
|
|
78
|
-
let size =
|
|
63
|
+
let size = 0;
|
|
64
|
+
size += varuintSize(this.#data.inputNodes.length);
|
|
65
|
+
size += varuintSize(this.#data.outputNodes.length);
|
|
66
|
+
size += varuintSize(this.#rootKeys.length);
|
|
79
67
|
for (const node of this.#data.inputNodes) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
68
|
+
const fieldIndices = Object.keys(node.edges).map(Number);
|
|
69
|
+
size += varuintSize(fieldIndices.length);
|
|
70
|
+
for (const fieldIndex of fieldIndices) {
|
|
71
|
+
const edge = node.edges[fieldIndex];
|
|
72
|
+
size += varuintSize(fieldIndex);
|
|
73
|
+
size += 2;
|
|
74
|
+
size += varuintSize(edge.childNodeId === void 0 ? 0 : edge.childNodeId + 1);
|
|
75
|
+
size += varuintSize(edge.enumNameIndex === void 0 ? 0 : edge.enumNameIndex + 1);
|
|
76
|
+
size += 1;
|
|
77
|
+
}
|
|
83
78
|
}
|
|
84
79
|
for (const node of this.#data.outputNodes) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
80
|
+
const fieldIndices = Object.keys(node.edges).map(Number);
|
|
81
|
+
size += varuintSize(fieldIndices.length);
|
|
82
|
+
for (const fieldIndex of fieldIndices) {
|
|
83
|
+
const edge = node.edges[fieldIndex];
|
|
84
|
+
size += varuintSize(fieldIndex);
|
|
85
|
+
size += varuintSize(edge.argsNodeId === void 0 ? 0 : edge.argsNodeId + 1);
|
|
86
|
+
size += varuintSize(edge.outputNodeId === void 0 ? 0 : edge.outputNodeId + 1);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
for (const key of this.#rootKeys) {
|
|
90
|
+
const root = this.#data.roots[key];
|
|
91
|
+
const keyIndex = this.#data.strings.indexOf(key);
|
|
92
|
+
size += varuintSize(keyIndex);
|
|
93
|
+
size += varuintSize(root.argsNodeId === void 0 ? 0 : root.argsNodeId + 1);
|
|
94
|
+
size += varuintSize(root.outputNodeId === void 0 ? 0 : root.outputNodeId + 1);
|
|
88
95
|
}
|
|
89
|
-
size += this.#rootKeys.length * (this.#useWide ? 12 : 6);
|
|
90
96
|
return size;
|
|
91
97
|
}
|
|
92
98
|
#writeHeader() {
|
|
93
|
-
this.#
|
|
94
|
-
this.#
|
|
95
|
-
this.#
|
|
96
|
-
this.#writeWord(this.#data.outputNodes.length);
|
|
97
|
-
this.#writeWord(this.#rootKeys.length);
|
|
99
|
+
this.#writeVaruint(this.#data.inputNodes.length);
|
|
100
|
+
this.#writeVaruint(this.#data.outputNodes.length);
|
|
101
|
+
this.#writeVaruint(this.#rootKeys.length);
|
|
98
102
|
}
|
|
99
103
|
#writeInputNodes() {
|
|
100
104
|
for (const node of this.#data.inputNodes) {
|
|
101
105
|
const fieldIndices = Object.keys(node.edges).map(Number);
|
|
102
|
-
this.#
|
|
106
|
+
this.#writeVaruint(fieldIndices.length);
|
|
103
107
|
for (const fieldIndex of fieldIndices) {
|
|
104
108
|
const edge = node.edges[fieldIndex];
|
|
105
|
-
this.#
|
|
109
|
+
this.#writeVaruint(fieldIndex);
|
|
106
110
|
this.#writeU16(edge.scalarMask ?? 0);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
this.#writeOptionalWord(edge.childNodeId);
|
|
111
|
-
this.#writeOptionalWord(edge.enumNameIndex);
|
|
111
|
+
this.#writeOptionalVaruint(edge.childNodeId);
|
|
112
|
+
this.#writeOptionalVaruint(edge.enumNameIndex);
|
|
112
113
|
this.#writeByte(edge.flags);
|
|
113
|
-
this.#skip(this.#useWide ? 3 : 1);
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
#writeOutputNodes() {
|
|
118
118
|
for (const node of this.#data.outputNodes) {
|
|
119
119
|
const fieldIndices = Object.keys(node.edges).map(Number);
|
|
120
|
-
this.#
|
|
120
|
+
this.#writeVaruint(fieldIndices.length);
|
|
121
121
|
for (const fieldIndex of fieldIndices) {
|
|
122
122
|
const edge = node.edges[fieldIndex];
|
|
123
|
-
this.#
|
|
124
|
-
this.#
|
|
125
|
-
this.#
|
|
123
|
+
this.#writeVaruint(fieldIndex);
|
|
124
|
+
this.#writeOptionalVaruint(edge.argsNodeId);
|
|
125
|
+
this.#writeOptionalVaruint(edge.outputNodeId);
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
}
|
|
@@ -133,9 +133,9 @@ class Serializer {
|
|
|
133
133
|
if (keyIndex === -1) {
|
|
134
134
|
throw new Error(`Root key "${key}" not found in strings table`);
|
|
135
135
|
}
|
|
136
|
-
this.#
|
|
137
|
-
this.#
|
|
138
|
-
this.#
|
|
136
|
+
this.#writeVaruint(keyIndex);
|
|
137
|
+
this.#writeOptionalVaruint(root.argsNodeId);
|
|
138
|
+
this.#writeOptionalVaruint(root.outputNodeId);
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
}
|
|
@@ -143,7 +143,6 @@ class Deserializer {
|
|
|
143
143
|
#serialized;
|
|
144
144
|
#view;
|
|
145
145
|
#offset = 0;
|
|
146
|
-
#useWide = false;
|
|
147
146
|
constructor(serialized) {
|
|
148
147
|
this.#serialized = serialized;
|
|
149
148
|
const bytes = decodeBase64url(serialized.graph);
|
|
@@ -161,25 +160,20 @@ class Deserializer {
|
|
|
161
160
|
roots
|
|
162
161
|
};
|
|
163
162
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
value = this.#view.getUint32(this.#offset, true);
|
|
174
|
-
} else {
|
|
175
|
-
value = this.#view.getUint16(this.#offset, true);
|
|
176
|
-
}
|
|
177
|
-
this.#offset += this.#wordSize;
|
|
163
|
+
#readVaruint() {
|
|
164
|
+
let value = 0;
|
|
165
|
+
let shift = 0;
|
|
166
|
+
let byte;
|
|
167
|
+
do {
|
|
168
|
+
byte = this.#view.getUint8(this.#offset++);
|
|
169
|
+
value |= (byte & 127) << shift;
|
|
170
|
+
shift += 7;
|
|
171
|
+
} while (byte >= 128);
|
|
178
172
|
return value;
|
|
179
173
|
}
|
|
180
|
-
#
|
|
181
|
-
const value = this.#
|
|
182
|
-
return value ===
|
|
174
|
+
#readOptionalVaruint() {
|
|
175
|
+
const value = this.#readVaruint();
|
|
176
|
+
return value === 0 ? void 0 : value - 1;
|
|
183
177
|
}
|
|
184
178
|
#readByte() {
|
|
185
179
|
const value = this.#view.getUint8(this.#offset);
|
|
@@ -191,36 +185,23 @@ class Deserializer {
|
|
|
191
185
|
this.#offset += 2;
|
|
192
186
|
return value;
|
|
193
187
|
}
|
|
194
|
-
#skip(bytes) {
|
|
195
|
-
this.#offset += bytes;
|
|
196
|
-
}
|
|
197
188
|
#readHeader() {
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
this.#useWide = format === FORMAT_WIDE;
|
|
203
|
-
this.#skip(this.#useWide ? 3 : 1);
|
|
204
|
-
const inputNodeCount = this.#readWord();
|
|
205
|
-
const outputNodeCount = this.#readWord();
|
|
206
|
-
const rootCount = this.#readWord();
|
|
189
|
+
const inputNodeCount = this.#readVaruint();
|
|
190
|
+
const outputNodeCount = this.#readVaruint();
|
|
191
|
+
const rootCount = this.#readVaruint();
|
|
207
192
|
return { inputNodeCount, outputNodeCount, rootCount };
|
|
208
193
|
}
|
|
209
194
|
#readInputNodes(count) {
|
|
210
195
|
const inputNodes = [];
|
|
211
196
|
for (let i = 0; i < count; i++) {
|
|
212
|
-
const edgeCount = this.#
|
|
197
|
+
const edgeCount = this.#readVaruint();
|
|
213
198
|
const edges = {};
|
|
214
199
|
for (let j = 0; j < edgeCount; j++) {
|
|
215
|
-
const fieldIndex = this.#
|
|
200
|
+
const fieldIndex = this.#readVaruint();
|
|
216
201
|
const scalarMask = this.#readU16();
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
const childNodeId = this.#readOptionalWord();
|
|
221
|
-
const enumNameIndex = this.#readOptionalWord();
|
|
202
|
+
const childNodeId = this.#readOptionalVaruint();
|
|
203
|
+
const enumNameIndex = this.#readOptionalVaruint();
|
|
222
204
|
const flags = this.#readByte();
|
|
223
|
-
this.#skip(this.#useWide ? 3 : 1);
|
|
224
205
|
const edge = { flags };
|
|
225
206
|
if (scalarMask !== 0) edge.scalarMask = scalarMask;
|
|
226
207
|
if (childNodeId !== void 0) edge.childNodeId = childNodeId;
|
|
@@ -234,12 +215,12 @@ class Deserializer {
|
|
|
234
215
|
#readOutputNodes(count) {
|
|
235
216
|
const outputNodes = [];
|
|
236
217
|
for (let i = 0; i < count; i++) {
|
|
237
|
-
const edgeCount = this.#
|
|
218
|
+
const edgeCount = this.#readVaruint();
|
|
238
219
|
const edges = {};
|
|
239
220
|
for (let j = 0; j < edgeCount; j++) {
|
|
240
|
-
const fieldIndex = this.#
|
|
241
|
-
const argsNodeId = this.#
|
|
242
|
-
const outputNodeId = this.#
|
|
221
|
+
const fieldIndex = this.#readVaruint();
|
|
222
|
+
const argsNodeId = this.#readOptionalVaruint();
|
|
223
|
+
const outputNodeId = this.#readOptionalVaruint();
|
|
243
224
|
const edge = {};
|
|
244
225
|
if (argsNodeId !== void 0) edge.argsNodeId = argsNodeId;
|
|
245
226
|
if (outputNodeId !== void 0) edge.outputNodeId = outputNodeId;
|
|
@@ -252,9 +233,9 @@ class Deserializer {
|
|
|
252
233
|
#readRoots(count) {
|
|
253
234
|
const roots = {};
|
|
254
235
|
for (let i = 0; i < count; i++) {
|
|
255
|
-
const keyIndex = this.#
|
|
256
|
-
const argsNodeId = this.#
|
|
257
|
-
const outputNodeId = this.#
|
|
236
|
+
const keyIndex = this.#readVaruint();
|
|
237
|
+
const argsNodeId = this.#readOptionalVaruint();
|
|
238
|
+
const outputNodeId = this.#readOptionalVaruint();
|
|
258
239
|
const key = this.#serialized.strings[keyIndex];
|
|
259
240
|
const root = {};
|
|
260
241
|
if (argsNodeId !== void 0) root.argsNodeId = argsNodeId;
|
|
@@ -83,4 +83,105 @@ var import_serialization = require("./serialization");
|
|
|
83
83
|
const serialized = (0, import_serialization.serializeParamGraph)(data);
|
|
84
84
|
(0, import_vitest.expect)(serialized.graph).toMatch(/^[A-Za-z0-9_-]+$/);
|
|
85
85
|
});
|
|
86
|
+
(0, import_vitest.test)("handles large indices requiring multi-byte varints (128-16383)", () => {
|
|
87
|
+
const strings = Array.from({ length: 200 }, (_, i) => `field${i}`);
|
|
88
|
+
const data = {
|
|
89
|
+
strings,
|
|
90
|
+
inputNodes: [
|
|
91
|
+
{
|
|
92
|
+
edges: {
|
|
93
|
+
// Use indices that require 2-byte encoding (128+)
|
|
94
|
+
128: { flags: 1, childNodeId: 150 },
|
|
95
|
+
150: { flags: 2, enumNameIndex: 180 }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
outputNodes: [{ edges: { 199: { argsNodeId: 0, outputNodeId: 0 } } }],
|
|
100
|
+
roots: { field0: { argsNodeId: 0, outputNodeId: 0 } }
|
|
101
|
+
};
|
|
102
|
+
const serialized = (0, import_serialization.serializeParamGraph)(data);
|
|
103
|
+
const deserialized = (0, import_serialization.deserializeParamGraph)(serialized);
|
|
104
|
+
(0, import_vitest.expect)(deserialized.inputNodes[0].edges[128].flags).toBe(1);
|
|
105
|
+
(0, import_vitest.expect)(deserialized.inputNodes[0].edges[128].childNodeId).toBe(150);
|
|
106
|
+
(0, import_vitest.expect)(deserialized.inputNodes[0].edges[150].flags).toBe(2);
|
|
107
|
+
(0, import_vitest.expect)(deserialized.inputNodes[0].edges[150].enumNameIndex).toBe(180);
|
|
108
|
+
(0, import_vitest.expect)(deserialized.outputNodes[0].edges[199].argsNodeId).toBe(0);
|
|
109
|
+
(0, import_vitest.expect)(deserialized.outputNodes[0].edges[199].outputNodeId).toBe(0);
|
|
110
|
+
});
|
|
111
|
+
(0, import_vitest.test)("handles very large indices requiring 3-byte varints (16384+)", () => {
|
|
112
|
+
const strings = Array.from({ length: 17e3 }, (_, i) => `f${i}`);
|
|
113
|
+
const data = {
|
|
114
|
+
strings,
|
|
115
|
+
inputNodes: [
|
|
116
|
+
{
|
|
117
|
+
edges: {
|
|
118
|
+
16384: { flags: 1, childNodeId: 16500 }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
outputNodes: [{ edges: { 16999: { argsNodeId: 0, outputNodeId: 0 } } }],
|
|
123
|
+
roots: { f0: { argsNodeId: 0, outputNodeId: 0 } }
|
|
124
|
+
};
|
|
125
|
+
const serialized = (0, import_serialization.serializeParamGraph)(data);
|
|
126
|
+
const deserialized = (0, import_serialization.deserializeParamGraph)(serialized);
|
|
127
|
+
(0, import_vitest.expect)(deserialized.inputNodes[0].edges[16384].flags).toBe(1);
|
|
128
|
+
(0, import_vitest.expect)(deserialized.inputNodes[0].edges[16384].childNodeId).toBe(16500);
|
|
129
|
+
(0, import_vitest.expect)(deserialized.outputNodes[0].edges[16999].argsNodeId).toBe(0);
|
|
130
|
+
});
|
|
131
|
+
(0, import_vitest.test)("varint boundary values encode and decode correctly", () => {
|
|
132
|
+
const boundaryValues = [0, 1, 127, 128, 16383, 16384];
|
|
133
|
+
for (const value of boundaryValues) {
|
|
134
|
+
const strings = Array.from({ length: Math.max(value + 1, 2) }, (_, i) => `s${i}`);
|
|
135
|
+
const data = {
|
|
136
|
+
strings,
|
|
137
|
+
inputNodes: [{ edges: { [value]: { flags: 0 } } }],
|
|
138
|
+
outputNodes: [],
|
|
139
|
+
roots: { s0: {} }
|
|
140
|
+
};
|
|
141
|
+
const serialized = (0, import_serialization.serializeParamGraph)(data);
|
|
142
|
+
const deserialized = (0, import_serialization.deserializeParamGraph)(serialized);
|
|
143
|
+
(0, import_vitest.expect)(deserialized.inputNodes[0].edges[value]).toBeDefined();
|
|
144
|
+
(0, import_vitest.expect)(deserialized.inputNodes[0].edges[value].flags).toBe(0);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
(0, import_vitest.test)("optional value 0 is correctly distinguished from undefined", () => {
|
|
148
|
+
const data = {
|
|
149
|
+
strings: ["root", "field"],
|
|
150
|
+
inputNodes: [
|
|
151
|
+
{ edges: { 1: { flags: 0, childNodeId: 0 } } }
|
|
152
|
+
// childNodeId = 0 (not undefined)
|
|
153
|
+
],
|
|
154
|
+
outputNodes: [
|
|
155
|
+
{ edges: { 1: { argsNodeId: 0, outputNodeId: 0 } } }
|
|
156
|
+
// both are 0 (not undefined)
|
|
157
|
+
],
|
|
158
|
+
roots: { root: { argsNodeId: 0, outputNodeId: 0 } }
|
|
159
|
+
// both are 0 (not undefined)
|
|
160
|
+
};
|
|
161
|
+
const serialized = (0, import_serialization.serializeParamGraph)(data);
|
|
162
|
+
const deserialized = (0, import_serialization.deserializeParamGraph)(serialized);
|
|
163
|
+
(0, import_vitest.expect)(deserialized.inputNodes[0].edges[1].childNodeId).toBe(0);
|
|
164
|
+
(0, import_vitest.expect)(deserialized.outputNodes[0].edges[1].argsNodeId).toBe(0);
|
|
165
|
+
(0, import_vitest.expect)(deserialized.outputNodes[0].edges[1].outputNodeId).toBe(0);
|
|
166
|
+
(0, import_vitest.expect)(deserialized.roots["root"].argsNodeId).toBe(0);
|
|
167
|
+
(0, import_vitest.expect)(deserialized.roots["root"].outputNodeId).toBe(0);
|
|
168
|
+
});
|
|
169
|
+
(0, import_vitest.test)("serialization produces more compact output with varint encoding", () => {
|
|
170
|
+
const data = {
|
|
171
|
+
strings: ["findMany", "create", "update", "where", "data", "id", "name", "email"],
|
|
172
|
+
inputNodes: [
|
|
173
|
+
{ edges: { 3: { flags: 1, scalarMask: 1 }, 4: { flags: 0, scalarMask: 2 } } },
|
|
174
|
+
{ edges: { 5: { flags: 2 }, 6: { flags: 2 }, 7: { flags: 2 } } }
|
|
175
|
+
],
|
|
176
|
+
outputNodes: [{ edges: { 5: { argsNodeId: 0 }, 6: {}, 7: {} } }],
|
|
177
|
+
roots: {
|
|
178
|
+
findMany: { argsNodeId: 0, outputNodeId: 0 },
|
|
179
|
+
create: { argsNodeId: 1, outputNodeId: 0 },
|
|
180
|
+
update: { argsNodeId: 0, outputNodeId: 0 }
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
const serialized = (0, import_serialization.serializeParamGraph)(data);
|
|
184
|
+
const binarySize = Buffer.from(serialized.graph, "base64url").length;
|
|
185
|
+
(0, import_vitest.expect)(binarySize).toBeLessThan(80);
|
|
186
|
+
});
|
|
86
187
|
});
|
|
@@ -82,4 +82,105 @@ describe("param-graph serialization", () => {
|
|
|
82
82
|
const serialized = serializeParamGraph(data);
|
|
83
83
|
expect(serialized.graph).toMatch(/^[A-Za-z0-9_-]+$/);
|
|
84
84
|
});
|
|
85
|
+
test("handles large indices requiring multi-byte varints (128-16383)", () => {
|
|
86
|
+
const strings = Array.from({ length: 200 }, (_, i) => `field${i}`);
|
|
87
|
+
const data = {
|
|
88
|
+
strings,
|
|
89
|
+
inputNodes: [
|
|
90
|
+
{
|
|
91
|
+
edges: {
|
|
92
|
+
// Use indices that require 2-byte encoding (128+)
|
|
93
|
+
128: { flags: 1, childNodeId: 150 },
|
|
94
|
+
150: { flags: 2, enumNameIndex: 180 }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
],
|
|
98
|
+
outputNodes: [{ edges: { 199: { argsNodeId: 0, outputNodeId: 0 } } }],
|
|
99
|
+
roots: { field0: { argsNodeId: 0, outputNodeId: 0 } }
|
|
100
|
+
};
|
|
101
|
+
const serialized = serializeParamGraph(data);
|
|
102
|
+
const deserialized = deserializeParamGraph(serialized);
|
|
103
|
+
expect(deserialized.inputNodes[0].edges[128].flags).toBe(1);
|
|
104
|
+
expect(deserialized.inputNodes[0].edges[128].childNodeId).toBe(150);
|
|
105
|
+
expect(deserialized.inputNodes[0].edges[150].flags).toBe(2);
|
|
106
|
+
expect(deserialized.inputNodes[0].edges[150].enumNameIndex).toBe(180);
|
|
107
|
+
expect(deserialized.outputNodes[0].edges[199].argsNodeId).toBe(0);
|
|
108
|
+
expect(deserialized.outputNodes[0].edges[199].outputNodeId).toBe(0);
|
|
109
|
+
});
|
|
110
|
+
test("handles very large indices requiring 3-byte varints (16384+)", () => {
|
|
111
|
+
const strings = Array.from({ length: 17e3 }, (_, i) => `f${i}`);
|
|
112
|
+
const data = {
|
|
113
|
+
strings,
|
|
114
|
+
inputNodes: [
|
|
115
|
+
{
|
|
116
|
+
edges: {
|
|
117
|
+
16384: { flags: 1, childNodeId: 16500 }
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
outputNodes: [{ edges: { 16999: { argsNodeId: 0, outputNodeId: 0 } } }],
|
|
122
|
+
roots: { f0: { argsNodeId: 0, outputNodeId: 0 } }
|
|
123
|
+
};
|
|
124
|
+
const serialized = serializeParamGraph(data);
|
|
125
|
+
const deserialized = deserializeParamGraph(serialized);
|
|
126
|
+
expect(deserialized.inputNodes[0].edges[16384].flags).toBe(1);
|
|
127
|
+
expect(deserialized.inputNodes[0].edges[16384].childNodeId).toBe(16500);
|
|
128
|
+
expect(deserialized.outputNodes[0].edges[16999].argsNodeId).toBe(0);
|
|
129
|
+
});
|
|
130
|
+
test("varint boundary values encode and decode correctly", () => {
|
|
131
|
+
const boundaryValues = [0, 1, 127, 128, 16383, 16384];
|
|
132
|
+
for (const value of boundaryValues) {
|
|
133
|
+
const strings = Array.from({ length: Math.max(value + 1, 2) }, (_, i) => `s${i}`);
|
|
134
|
+
const data = {
|
|
135
|
+
strings,
|
|
136
|
+
inputNodes: [{ edges: { [value]: { flags: 0 } } }],
|
|
137
|
+
outputNodes: [],
|
|
138
|
+
roots: { s0: {} }
|
|
139
|
+
};
|
|
140
|
+
const serialized = serializeParamGraph(data);
|
|
141
|
+
const deserialized = deserializeParamGraph(serialized);
|
|
142
|
+
expect(deserialized.inputNodes[0].edges[value]).toBeDefined();
|
|
143
|
+
expect(deserialized.inputNodes[0].edges[value].flags).toBe(0);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
test("optional value 0 is correctly distinguished from undefined", () => {
|
|
147
|
+
const data = {
|
|
148
|
+
strings: ["root", "field"],
|
|
149
|
+
inputNodes: [
|
|
150
|
+
{ edges: { 1: { flags: 0, childNodeId: 0 } } }
|
|
151
|
+
// childNodeId = 0 (not undefined)
|
|
152
|
+
],
|
|
153
|
+
outputNodes: [
|
|
154
|
+
{ edges: { 1: { argsNodeId: 0, outputNodeId: 0 } } }
|
|
155
|
+
// both are 0 (not undefined)
|
|
156
|
+
],
|
|
157
|
+
roots: { root: { argsNodeId: 0, outputNodeId: 0 } }
|
|
158
|
+
// both are 0 (not undefined)
|
|
159
|
+
};
|
|
160
|
+
const serialized = serializeParamGraph(data);
|
|
161
|
+
const deserialized = deserializeParamGraph(serialized);
|
|
162
|
+
expect(deserialized.inputNodes[0].edges[1].childNodeId).toBe(0);
|
|
163
|
+
expect(deserialized.outputNodes[0].edges[1].argsNodeId).toBe(0);
|
|
164
|
+
expect(deserialized.outputNodes[0].edges[1].outputNodeId).toBe(0);
|
|
165
|
+
expect(deserialized.roots["root"].argsNodeId).toBe(0);
|
|
166
|
+
expect(deserialized.roots["root"].outputNodeId).toBe(0);
|
|
167
|
+
});
|
|
168
|
+
test("serialization produces more compact output with varint encoding", () => {
|
|
169
|
+
const data = {
|
|
170
|
+
strings: ["findMany", "create", "update", "where", "data", "id", "name", "email"],
|
|
171
|
+
inputNodes: [
|
|
172
|
+
{ edges: { 3: { flags: 1, scalarMask: 1 }, 4: { flags: 0, scalarMask: 2 } } },
|
|
173
|
+
{ edges: { 5: { flags: 2 }, 6: { flags: 2 }, 7: { flags: 2 } } }
|
|
174
|
+
],
|
|
175
|
+
outputNodes: [{ edges: { 5: { argsNodeId: 0 }, 6: {}, 7: {} } }],
|
|
176
|
+
roots: {
|
|
177
|
+
findMany: { argsNodeId: 0, outputNodeId: 0 },
|
|
178
|
+
create: { argsNodeId: 1, outputNodeId: 0 },
|
|
179
|
+
update: { argsNodeId: 0, outputNodeId: 0 }
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
const serialized = serializeParamGraph(data);
|
|
183
|
+
const binarySize = Buffer.from(serialized.graph, "base64url").length;
|
|
184
|
+
expect(binarySize).toBeLessThan(80);
|
|
185
|
+
});
|
|
85
186
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma/param-graph",
|
|
3
|
-
"version": "7.4.0-integration-parameterization.
|
|
3
|
+
"version": "7.4.0-integration-parameterization.12",
|
|
4
4
|
"description": "This package is intended for Prisma's internal use",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|