@prisma/param-graph 7.4.0-integration-parameterization.9 → 7.4.0-integration-parameterization.11

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.
@@ -4,6 +4,108 @@
4
4
  * This module handles compact binary encoding/decoding of the param graph structure.
5
5
  * The format uses a hybrid approach: JSON string array for field names + binary blob
6
6
  * for structural data (nodes, edges, roots).
7
+ *
8
+ * ## Serialized Representation
9
+ *
10
+ * ```
11
+ * SerializedParamGraph {
12
+ * strings: string[] // String table (field names, enum names, root keys)
13
+ * graph: string // Base64url-encoded binary blob
14
+ * }
15
+ * ```
16
+ *
17
+ * ## Why Hybrid?
18
+ *
19
+ * - **Strings stay as JSON**: V8's JSON.parse is highly optimized for string arrays
20
+ * - **Structure goes binary**: Indices, flags, masks benefit from compact encoding
21
+ * - **Best of both**: Fast parsing + compact size where it matters
22
+ *
23
+ * ## Format Selection
24
+ *
25
+ * Two binary formats based on data size (selected automatically at serialization):
26
+ *
27
+ * - **Compact (0x00)**: 16-bit indices, for graphs with ≤65534 items
28
+ * - **Wide (0x01)**: 32-bit indices, for larger graphs
29
+ *
30
+ * Sentinel values for "none/undefined": 0xFFFF (compact) or 0xFFFFFFFF (wide)
31
+ *
32
+ * ## Binary Blob Layout
33
+ *
34
+ * All multi-byte integers are little-endian.
35
+ *
36
+ * ```
37
+ * ┌───────────────────────────────────────────────────────────────────┐
38
+ * │ HEADER │
39
+ * ├───────────────────────────────────────────────────────────────────┤
40
+ * │ format: u8 │ 0x00 = compact (16-bit), 0x01 = wide │
41
+ * │ padding: 1|3 bytes │ Alignment padding (1 compact, 3 wide) │
42
+ * │ inputNodeCount: word │ Number of input nodes │
43
+ * │ outputNodeCount: word │ Number of output nodes │
44
+ * │ rootCount: word │ Number of root entries │
45
+ * └───────────────────────────────────────────────────────────────────┘
46
+ *
47
+ * ┌───────────────────────────────────────────────────────────────────┐
48
+ * │ INPUT NODES (repeated inputNodeCount times) │
49
+ * ├───────────────────────────────────────────────────────────────────┤
50
+ * │ edgeCount: word │ Number of edges in this node │
51
+ * │ edges[] │ Edge data (see Input Edge below) │
52
+ * └───────────────────────────────────────────────────────────────────┘
53
+ *
54
+ * ┌───────────────────────────────────────────────────────────────────┐
55
+ * │ INPUT EDGE (compact: 10 bytes, wide: 20 bytes) │
56
+ * ├───────────────────────────────────────────────────────────────────┤
57
+ * │ fieldIndex: word │ Index into string table │
58
+ * │ scalarMask: u16 │ Scalar type bitmask (0 if none) │
59
+ * │ [padding: 2 bytes] │ (wide format only, for alignment) │
60
+ * │ childNodeId: word │ Child input node ID (sentinel=none) │
61
+ * │ enumNameIndex: word │ Enum name in string table (sentinel=none) │
62
+ * │ flags: u8 │ Edge capability flags │
63
+ * │ padding: 1|3 bytes │ Alignment padding (1 compact, 3 wide) │
64
+ * └───────────────────────────────────────────────────────────────────┘
65
+ *
66
+ * ┌───────────────────────────────────────────────────────────────────┐
67
+ * │ OUTPUT NODES (repeated outputNodeCount times) │
68
+ * ├───────────────────────────────────────────────────────────────────┤
69
+ * │ edgeCount: word │ Number of edges in this node │
70
+ * │ edges[] │ Edge data (see Output Edge below) │
71
+ * └───────────────────────────────────────────────────────────────────┘
72
+ *
73
+ * ┌───────────────────────────────────────────────────────────────────┐
74
+ * │ OUTPUT EDGE (compact: 6 bytes, wide: 12 bytes) │
75
+ * ├───────────────────────────────────────────────────────────────────┤
76
+ * │ fieldIndex: word │ Index into string table │
77
+ * │ argsNodeId: word │ Args input node ID (sentinel=none) │
78
+ * │ outputNodeId: word │ Child output node ID (sentinel=none) │
79
+ * └───────────────────────────────────────────────────────────────────┘
80
+ *
81
+ * ┌───────────────────────────────────────────────────────────────────┐
82
+ * │ ROOTS (repeated rootCount times) │
83
+ * ├───────────────────────────────────────────────────────────────────┤
84
+ * │ keyIndex: word │ Root key index in string table │
85
+ * │ argsNodeId: word │ Args input node ID (sentinel=none) │
86
+ * │ outputNodeId: word │ Output node ID (sentinel=none) │
87
+ * └───────────────────────────────────────────────────────────────────┘
88
+ * ```
89
+ *
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
+ * ## Embedding in Generated Client
102
+ *
103
+ * ```js
104
+ * config.parameterizationSchema = {
105
+ * strings: JSON.parse('["where","id","email",...]'),
106
+ * graph: "base64url_encoded_binary_blob..."
107
+ * }
108
+ * ```
7
109
  */
8
110
  import type { ParamGraphData } from './types';
9
111
  /**
@@ -22,6 +22,12 @@ __export(serialization_exports, {
22
22
  serializeParamGraph: () => serializeParamGraph
23
23
  });
24
24
  module.exports = __toCommonJS(serialization_exports);
25
+ function serializeParamGraph(data) {
26
+ return new Serializer(data).serialize();
27
+ }
28
+ function deserializeParamGraph(serialized) {
29
+ return new Deserializer(serialized).deserialize();
30
+ }
25
31
  const FORMAT_COMPACT = 0;
26
32
  const FORMAT_WIDE = 1;
27
33
  const NONE_16 = 65535;
@@ -33,277 +39,254 @@ function encodeBase64url(bytes) {
33
39
  function decodeBase64url(str) {
34
40
  return Buffer.from(str, "base64url");
35
41
  }
36
- function serializeParamGraph(data) {
37
- const rootKeys = Object.keys(data.roots);
38
- const maxIndex = Math.max(data.strings.length, data.inputNodes.length, data.outputNodes.length, rootKeys.length);
39
- const useWide = maxIndex > MAX_COMPACT_INDEX;
40
- let size = 1;
41
- size += useWide ? 12 : 6;
42
- for (const node of data.inputNodes) {
43
- size += useWide ? 4 : 2;
44
- const edgeCount = Object.keys(node.edges).length;
45
- size += edgeCount * (useWide ? 20 : 10);
42
+ class Serializer {
43
+ #data;
44
+ #useWide;
45
+ #buffer;
46
+ #view;
47
+ #offset = 0;
48
+ #rootKeys;
49
+ constructor(data) {
50
+ this.#data = data;
51
+ 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
+ const size = this.#calculateBufferSize();
60
+ this.#buffer = new ArrayBuffer(size);
61
+ this.#view = new DataView(this.#buffer);
46
62
  }
47
- for (const node of data.outputNodes) {
48
- size += useWide ? 4 : 2;
49
- const edgeCount = Object.keys(node.edges).length;
50
- size += edgeCount * (useWide ? 12 : 6);
63
+ serialize() {
64
+ this.#writeHeader();
65
+ this.#writeInputNodes();
66
+ this.#writeOutputNodes();
67
+ this.#writeRoots();
68
+ return {
69
+ strings: this.#data.strings,
70
+ graph: encodeBase64url(new Uint8Array(this.#buffer))
71
+ };
51
72
  }
52
- size += rootKeys.length * (useWide ? 12 : 6);
53
- const buffer = new ArrayBuffer(size);
54
- const view = new DataView(buffer);
55
- let offset = 0;
56
- view.setUint8(offset++, useWide ? FORMAT_WIDE : FORMAT_COMPACT);
57
- if (useWide) {
58
- view.setUint32(offset, data.inputNodes.length, true);
59
- offset += 4;
60
- view.setUint32(offset, data.outputNodes.length, true);
61
- offset += 4;
62
- view.setUint32(offset, rootKeys.length, true);
63
- offset += 4;
64
- } else {
65
- view.setUint16(offset, data.inputNodes.length, true);
66
- offset += 2;
67
- view.setUint16(offset, data.outputNodes.length, true);
68
- offset += 2;
69
- view.setUint16(offset, rootKeys.length, true);
70
- offset += 2;
73
+ get #wordSize() {
74
+ return this.#useWide ? 4 : 2;
71
75
  }
72
- for (const node of data.inputNodes) {
73
- const fieldIndices = Object.keys(node.edges).map(Number);
74
- if (useWide) {
75
- view.setUint32(offset, fieldIndices.length, true);
76
- offset += 4;
76
+ get #noneValue() {
77
+ return this.#useWide ? NONE_32 : NONE_16;
78
+ }
79
+ #writeWord(value) {
80
+ if (this.#useWide) {
81
+ this.#view.setUint32(this.#offset, value, true);
77
82
  } else {
78
- view.setUint16(offset, fieldIndices.length, true);
79
- offset += 2;
83
+ this.#view.setUint16(this.#offset, value, true);
80
84
  }
81
- for (const fieldIndex of fieldIndices) {
82
- const edge = node.edges[fieldIndex];
83
- if (useWide) {
84
- view.setUint32(offset, fieldIndex, true);
85
- offset += 4;
86
- view.setUint16(offset, edge.scalarMask ?? 0, true);
87
- offset += 2;
88
- offset += 2;
89
- view.setUint32(offset, edge.childNodeId ?? NONE_32, true);
90
- offset += 4;
91
- view.setUint32(offset, edge.enumNameIndex ?? NONE_32, true);
92
- offset += 4;
93
- view.setUint8(offset, edge.flags);
94
- offset += 1;
95
- offset += 3;
96
- } else {
97
- view.setUint16(offset, fieldIndex, true);
98
- offset += 2;
99
- view.setUint16(offset, edge.scalarMask ?? 0, true);
100
- offset += 2;
101
- view.setUint16(offset, edge.childNodeId ?? NONE_16, true);
102
- offset += 2;
103
- view.setUint16(offset, edge.enumNameIndex ?? NONE_16, true);
104
- offset += 2;
105
- view.setUint8(offset, edge.flags);
106
- offset += 1;
107
- offset += 1;
108
- }
85
+ this.#offset += this.#wordSize;
86
+ }
87
+ #writeOptionalWord(value) {
88
+ this.#writeWord(value ?? this.#noneValue);
89
+ }
90
+ #writeByte(value) {
91
+ this.#view.setUint8(this.#offset, value);
92
+ this.#offset += 1;
93
+ }
94
+ #writeU16(value) {
95
+ this.#view.setUint16(this.#offset, value, true);
96
+ this.#offset += 2;
97
+ }
98
+ #skip(bytes) {
99
+ this.#offset += bytes;
100
+ }
101
+ #calculateBufferSize() {
102
+ let size = this.#useWide ? 16 : 8;
103
+ for (const node of this.#data.inputNodes) {
104
+ size += this.#wordSize;
105
+ const edgeCount = Object.keys(node.edges).length;
106
+ size += edgeCount * (this.#useWide ? 20 : 10);
107
+ }
108
+ for (const node of this.#data.outputNodes) {
109
+ size += this.#wordSize;
110
+ const edgeCount = Object.keys(node.edges).length;
111
+ size += edgeCount * (this.#useWide ? 12 : 6);
109
112
  }
113
+ size += this.#rootKeys.length * (this.#useWide ? 12 : 6);
114
+ return size;
110
115
  }
111
- for (const node of data.outputNodes) {
112
- const fieldIndices = Object.keys(node.edges).map(Number);
113
- if (useWide) {
114
- view.setUint32(offset, fieldIndices.length, true);
115
- offset += 4;
116
- } else {
117
- view.setUint16(offset, fieldIndices.length, true);
118
- offset += 2;
116
+ #writeHeader() {
117
+ this.#writeByte(this.#useWide ? FORMAT_WIDE : FORMAT_COMPACT);
118
+ this.#skip(this.#useWide ? 3 : 1);
119
+ this.#writeWord(this.#data.inputNodes.length);
120
+ this.#writeWord(this.#data.outputNodes.length);
121
+ this.#writeWord(this.#rootKeys.length);
122
+ }
123
+ #writeInputNodes() {
124
+ for (const node of this.#data.inputNodes) {
125
+ const fieldIndices = Object.keys(node.edges).map(Number);
126
+ this.#writeWord(fieldIndices.length);
127
+ for (const fieldIndex of fieldIndices) {
128
+ const edge = node.edges[fieldIndex];
129
+ this.#writeWord(fieldIndex);
130
+ this.#writeU16(edge.scalarMask ?? 0);
131
+ if (this.#useWide) {
132
+ this.#skip(2);
133
+ }
134
+ this.#writeOptionalWord(edge.childNodeId);
135
+ this.#writeOptionalWord(edge.enumNameIndex);
136
+ this.#writeByte(edge.flags);
137
+ this.#skip(this.#useWide ? 3 : 1);
138
+ }
119
139
  }
120
- for (const fieldIndex of fieldIndices) {
121
- const edge = node.edges[fieldIndex];
122
- if (useWide) {
123
- view.setUint32(offset, fieldIndex, true);
124
- offset += 4;
125
- view.setUint32(offset, edge.argsNodeId ?? NONE_32, true);
126
- offset += 4;
127
- view.setUint32(offset, edge.outputNodeId ?? NONE_32, true);
128
- offset += 4;
129
- } else {
130
- view.setUint16(offset, fieldIndex, true);
131
- offset += 2;
132
- view.setUint16(offset, edge.argsNodeId ?? NONE_16, true);
133
- offset += 2;
134
- view.setUint16(offset, edge.outputNodeId ?? NONE_16, true);
135
- offset += 2;
140
+ }
141
+ #writeOutputNodes() {
142
+ for (const node of this.#data.outputNodes) {
143
+ const fieldIndices = Object.keys(node.edges).map(Number);
144
+ this.#writeWord(fieldIndices.length);
145
+ for (const fieldIndex of fieldIndices) {
146
+ const edge = node.edges[fieldIndex];
147
+ this.#writeWord(fieldIndex);
148
+ this.#writeOptionalWord(edge.argsNodeId);
149
+ this.#writeOptionalWord(edge.outputNodeId);
136
150
  }
137
151
  }
138
152
  }
139
- for (const key of rootKeys) {
140
- const root = data.roots[key];
141
- const keyIndex = data.strings.indexOf(key);
142
- if (useWide) {
143
- view.setUint32(offset, keyIndex, true);
144
- offset += 4;
145
- view.setUint32(offset, root.argsNodeId ?? NONE_32, true);
146
- offset += 4;
147
- view.setUint32(offset, root.outputNodeId ?? NONE_32, true);
148
- offset += 4;
149
- } else {
150
- view.setUint16(offset, keyIndex, true);
151
- offset += 2;
152
- view.setUint16(offset, root.argsNodeId ?? NONE_16, true);
153
- offset += 2;
154
- view.setUint16(offset, root.outputNodeId ?? NONE_16, true);
155
- offset += 2;
153
+ #writeRoots() {
154
+ for (const key of this.#rootKeys) {
155
+ const root = this.#data.roots[key];
156
+ const keyIndex = this.#data.strings.indexOf(key);
157
+ if (keyIndex === -1) {
158
+ throw new Error(`Root key "${key}" not found in strings table`);
159
+ }
160
+ this.#writeWord(keyIndex);
161
+ this.#writeOptionalWord(root.argsNodeId);
162
+ this.#writeOptionalWord(root.outputNodeId);
156
163
  }
157
164
  }
158
- return {
159
- strings: data.strings,
160
- graph: encodeBase64url(new Uint8Array(buffer))
161
- };
162
165
  }
163
- function deserializeParamGraph(serialized) {
164
- const bytes = decodeBase64url(serialized.graph);
165
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
166
- let offset = 0;
167
- const format = view.getUint8(offset++);
168
- const useWide = format === FORMAT_WIDE;
169
- let inputNodeCount;
170
- let outputNodeCount;
171
- let rootCount;
172
- if (useWide) {
173
- inputNodeCount = view.getUint32(offset, true);
174
- offset += 4;
175
- outputNodeCount = view.getUint32(offset, true);
176
- offset += 4;
177
- rootCount = view.getUint32(offset, true);
178
- offset += 4;
179
- } else {
180
- inputNodeCount = view.getUint16(offset, true);
181
- offset += 2;
182
- outputNodeCount = view.getUint16(offset, true);
183
- offset += 2;
184
- rootCount = view.getUint16(offset, true);
185
- offset += 2;
166
+ class Deserializer {
167
+ #serialized;
168
+ #view;
169
+ #offset = 0;
170
+ #useWide = false;
171
+ constructor(serialized) {
172
+ this.#serialized = serialized;
173
+ const bytes = decodeBase64url(serialized.graph);
174
+ this.#view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
175
+ }
176
+ deserialize() {
177
+ const { inputNodeCount, outputNodeCount, rootCount } = this.#readHeader();
178
+ const inputNodes = this.#readInputNodes(inputNodeCount);
179
+ const outputNodes = this.#readOutputNodes(outputNodeCount);
180
+ const roots = this.#readRoots(rootCount);
181
+ return {
182
+ strings: this.#serialized.strings,
183
+ inputNodes,
184
+ outputNodes,
185
+ roots
186
+ };
186
187
  }
187
- const inputNodes = [];
188
- for (let i = 0; i < inputNodeCount; i++) {
189
- let edgeCount;
190
- if (useWide) {
191
- edgeCount = view.getUint32(offset, true);
192
- offset += 4;
188
+ get #wordSize() {
189
+ return this.#useWide ? 4 : 2;
190
+ }
191
+ get #noneValue() {
192
+ return this.#useWide ? NONE_32 : NONE_16;
193
+ }
194
+ #readWord() {
195
+ let value;
196
+ if (this.#useWide) {
197
+ value = this.#view.getUint32(this.#offset, true);
193
198
  } else {
194
- edgeCount = view.getUint16(offset, true);
195
- offset += 2;
199
+ value = this.#view.getUint16(this.#offset, true);
196
200
  }
197
- const edges = {};
198
- for (let j = 0; j < edgeCount; j++) {
199
- let fieldIndex;
200
- let scalarMask;
201
- let childNodeId;
202
- let enumNameIndex;
203
- let flags;
204
- if (useWide) {
205
- fieldIndex = view.getUint32(offset, true);
206
- offset += 4;
207
- scalarMask = view.getUint16(offset, true);
208
- offset += 2;
209
- offset += 2;
210
- childNodeId = view.getUint32(offset, true);
211
- offset += 4;
212
- enumNameIndex = view.getUint32(offset, true);
213
- offset += 4;
214
- flags = view.getUint8(offset);
215
- offset += 1;
216
- offset += 3;
217
- } else {
218
- fieldIndex = view.getUint16(offset, true);
219
- offset += 2;
220
- scalarMask = view.getUint16(offset, true);
221
- offset += 2;
222
- childNodeId = view.getUint16(offset, true);
223
- offset += 2;
224
- enumNameIndex = view.getUint16(offset, true);
225
- offset += 2;
226
- flags = view.getUint8(offset);
227
- offset += 1;
228
- offset += 1;
229
- }
230
- const edge = { flags };
231
- if (scalarMask !== 0) edge.scalarMask = scalarMask;
232
- if (childNodeId !== (useWide ? NONE_32 : NONE_16)) edge.childNodeId = childNodeId;
233
- if (enumNameIndex !== (useWide ? NONE_32 : NONE_16)) edge.enumNameIndex = enumNameIndex;
234
- edges[fieldIndex] = edge;
201
+ this.#offset += this.#wordSize;
202
+ return value;
203
+ }
204
+ #readOptionalWord() {
205
+ const value = this.#readWord();
206
+ return value === this.#noneValue ? void 0 : value;
207
+ }
208
+ #readByte() {
209
+ const value = this.#view.getUint8(this.#offset);
210
+ this.#offset += 1;
211
+ return value;
212
+ }
213
+ #readU16() {
214
+ const value = this.#view.getUint16(this.#offset, true);
215
+ this.#offset += 2;
216
+ return value;
217
+ }
218
+ #skip(bytes) {
219
+ this.#offset += bytes;
220
+ }
221
+ #readHeader() {
222
+ const format = this.#readByte();
223
+ if (format !== FORMAT_COMPACT && format !== FORMAT_WIDE) {
224
+ throw new Error(`Unknown param graph format: 0x${format.toString(16).padStart(2, "0")}`);
235
225
  }
236
- inputNodes.push({ edges });
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();
231
+ return { inputNodeCount, outputNodeCount, rootCount };
237
232
  }
238
- const outputNodes = [];
239
- for (let i = 0; i < outputNodeCount; i++) {
240
- let edgeCount;
241
- if (useWide) {
242
- edgeCount = view.getUint32(offset, true);
243
- offset += 4;
244
- } else {
245
- edgeCount = view.getUint16(offset, true);
246
- offset += 2;
233
+ #readInputNodes(count) {
234
+ const inputNodes = [];
235
+ for (let i = 0; i < count; i++) {
236
+ const edgeCount = this.#readWord();
237
+ const edges = {};
238
+ for (let j = 0; j < edgeCount; j++) {
239
+ const fieldIndex = this.#readWord();
240
+ const scalarMask = this.#readU16();
241
+ if (this.#useWide) {
242
+ this.#skip(2);
243
+ }
244
+ const childNodeId = this.#readOptionalWord();
245
+ const enumNameIndex = this.#readOptionalWord();
246
+ const flags = this.#readByte();
247
+ this.#skip(this.#useWide ? 3 : 1);
248
+ const edge = { flags };
249
+ if (scalarMask !== 0) edge.scalarMask = scalarMask;
250
+ if (childNodeId !== void 0) edge.childNodeId = childNodeId;
251
+ if (enumNameIndex !== void 0) edge.enumNameIndex = enumNameIndex;
252
+ edges[fieldIndex] = edge;
253
+ }
254
+ inputNodes.push({ edges });
247
255
  }
248
- const edges = {};
249
- for (let j = 0; j < edgeCount; j++) {
250
- let fieldIndex;
251
- let argsNodeId;
252
- let outputNodeId;
253
- if (useWide) {
254
- fieldIndex = view.getUint32(offset, true);
255
- offset += 4;
256
- argsNodeId = view.getUint32(offset, true);
257
- offset += 4;
258
- outputNodeId = view.getUint32(offset, true);
259
- offset += 4;
260
- } else {
261
- fieldIndex = view.getUint16(offset, true);
262
- offset += 2;
263
- argsNodeId = view.getUint16(offset, true);
264
- offset += 2;
265
- outputNodeId = view.getUint16(offset, true);
266
- offset += 2;
256
+ return inputNodes;
257
+ }
258
+ #readOutputNodes(count) {
259
+ const outputNodes = [];
260
+ for (let i = 0; i < count; i++) {
261
+ const edgeCount = this.#readWord();
262
+ const edges = {};
263
+ for (let j = 0; j < edgeCount; j++) {
264
+ const fieldIndex = this.#readWord();
265
+ const argsNodeId = this.#readOptionalWord();
266
+ const outputNodeId = this.#readOptionalWord();
267
+ const edge = {};
268
+ if (argsNodeId !== void 0) edge.argsNodeId = argsNodeId;
269
+ if (outputNodeId !== void 0) edge.outputNodeId = outputNodeId;
270
+ edges[fieldIndex] = edge;
267
271
  }
268
- const edge = {};
269
- if (argsNodeId !== (useWide ? NONE_32 : NONE_16)) edge.argsNodeId = argsNodeId;
270
- if (outputNodeId !== (useWide ? NONE_32 : NONE_16)) edge.outputNodeId = outputNodeId;
271
- edges[fieldIndex] = edge;
272
+ outputNodes.push({ edges });
272
273
  }
273
- outputNodes.push({ edges });
274
+ return outputNodes;
274
275
  }
275
- const roots = {};
276
- for (let i = 0; i < rootCount; i++) {
277
- let keyIndex;
278
- let argsNodeId;
279
- let outputNodeId;
280
- if (useWide) {
281
- keyIndex = view.getUint32(offset, true);
282
- offset += 4;
283
- argsNodeId = view.getUint32(offset, true);
284
- offset += 4;
285
- outputNodeId = view.getUint32(offset, true);
286
- offset += 4;
287
- } else {
288
- keyIndex = view.getUint16(offset, true);
289
- offset += 2;
290
- argsNodeId = view.getUint16(offset, true);
291
- offset += 2;
292
- outputNodeId = view.getUint16(offset, true);
293
- offset += 2;
276
+ #readRoots(count) {
277
+ const roots = {};
278
+ for (let i = 0; i < count; i++) {
279
+ const keyIndex = this.#readWord();
280
+ const argsNodeId = this.#readOptionalWord();
281
+ const outputNodeId = this.#readOptionalWord();
282
+ const key = this.#serialized.strings[keyIndex];
283
+ const root = {};
284
+ if (argsNodeId !== void 0) root.argsNodeId = argsNodeId;
285
+ if (outputNodeId !== void 0) root.outputNodeId = outputNodeId;
286
+ roots[key] = root;
294
287
  }
295
- const key = serialized.strings[keyIndex];
296
- const root = {};
297
- if (argsNodeId !== (useWide ? NONE_32 : NONE_16)) root.argsNodeId = argsNodeId;
298
- if (outputNodeId !== (useWide ? NONE_32 : NONE_16)) root.outputNodeId = outputNodeId;
299
- roots[key] = root;
288
+ return roots;
300
289
  }
301
- return {
302
- strings: serialized.strings,
303
- inputNodes,
304
- outputNodes,
305
- roots
306
- };
307
290
  }
308
291
  // Annotate the CommonJS export names for ESM import in node:
309
292
  0 && (module.exports = {
@@ -1,3 +1,9 @@
1
+ function serializeParamGraph(data) {
2
+ return new Serializer(data).serialize();
3
+ }
4
+ function deserializeParamGraph(serialized) {
5
+ return new Deserializer(serialized).deserialize();
6
+ }
1
7
  const FORMAT_COMPACT = 0;
2
8
  const FORMAT_WIDE = 1;
3
9
  const NONE_16 = 65535;
@@ -9,277 +15,254 @@ function encodeBase64url(bytes) {
9
15
  function decodeBase64url(str) {
10
16
  return Buffer.from(str, "base64url");
11
17
  }
12
- function serializeParamGraph(data) {
13
- const rootKeys = Object.keys(data.roots);
14
- const maxIndex = Math.max(data.strings.length, data.inputNodes.length, data.outputNodes.length, rootKeys.length);
15
- const useWide = maxIndex > MAX_COMPACT_INDEX;
16
- let size = 1;
17
- size += useWide ? 12 : 6;
18
- for (const node of data.inputNodes) {
19
- size += useWide ? 4 : 2;
20
- const edgeCount = Object.keys(node.edges).length;
21
- size += edgeCount * (useWide ? 20 : 10);
18
+ class Serializer {
19
+ #data;
20
+ #useWide;
21
+ #buffer;
22
+ #view;
23
+ #offset = 0;
24
+ #rootKeys;
25
+ constructor(data) {
26
+ this.#data = data;
27
+ 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
+ const size = this.#calculateBufferSize();
36
+ this.#buffer = new ArrayBuffer(size);
37
+ this.#view = new DataView(this.#buffer);
22
38
  }
23
- for (const node of data.outputNodes) {
24
- size += useWide ? 4 : 2;
25
- const edgeCount = Object.keys(node.edges).length;
26
- size += edgeCount * (useWide ? 12 : 6);
39
+ serialize() {
40
+ this.#writeHeader();
41
+ this.#writeInputNodes();
42
+ this.#writeOutputNodes();
43
+ this.#writeRoots();
44
+ return {
45
+ strings: this.#data.strings,
46
+ graph: encodeBase64url(new Uint8Array(this.#buffer))
47
+ };
27
48
  }
28
- size += rootKeys.length * (useWide ? 12 : 6);
29
- const buffer = new ArrayBuffer(size);
30
- const view = new DataView(buffer);
31
- let offset = 0;
32
- view.setUint8(offset++, useWide ? FORMAT_WIDE : FORMAT_COMPACT);
33
- if (useWide) {
34
- view.setUint32(offset, data.inputNodes.length, true);
35
- offset += 4;
36
- view.setUint32(offset, data.outputNodes.length, true);
37
- offset += 4;
38
- view.setUint32(offset, rootKeys.length, true);
39
- offset += 4;
40
- } else {
41
- view.setUint16(offset, data.inputNodes.length, true);
42
- offset += 2;
43
- view.setUint16(offset, data.outputNodes.length, true);
44
- offset += 2;
45
- view.setUint16(offset, rootKeys.length, true);
46
- offset += 2;
49
+ get #wordSize() {
50
+ return this.#useWide ? 4 : 2;
47
51
  }
48
- for (const node of data.inputNodes) {
49
- const fieldIndices = Object.keys(node.edges).map(Number);
50
- if (useWide) {
51
- view.setUint32(offset, fieldIndices.length, true);
52
- offset += 4;
52
+ get #noneValue() {
53
+ return this.#useWide ? NONE_32 : NONE_16;
54
+ }
55
+ #writeWord(value) {
56
+ if (this.#useWide) {
57
+ this.#view.setUint32(this.#offset, value, true);
53
58
  } else {
54
- view.setUint16(offset, fieldIndices.length, true);
55
- offset += 2;
59
+ this.#view.setUint16(this.#offset, value, true);
56
60
  }
57
- for (const fieldIndex of fieldIndices) {
58
- const edge = node.edges[fieldIndex];
59
- if (useWide) {
60
- view.setUint32(offset, fieldIndex, true);
61
- offset += 4;
62
- view.setUint16(offset, edge.scalarMask ?? 0, true);
63
- offset += 2;
64
- offset += 2;
65
- view.setUint32(offset, edge.childNodeId ?? NONE_32, true);
66
- offset += 4;
67
- view.setUint32(offset, edge.enumNameIndex ?? NONE_32, true);
68
- offset += 4;
69
- view.setUint8(offset, edge.flags);
70
- offset += 1;
71
- offset += 3;
72
- } else {
73
- view.setUint16(offset, fieldIndex, true);
74
- offset += 2;
75
- view.setUint16(offset, edge.scalarMask ?? 0, true);
76
- offset += 2;
77
- view.setUint16(offset, edge.childNodeId ?? NONE_16, true);
78
- offset += 2;
79
- view.setUint16(offset, edge.enumNameIndex ?? NONE_16, true);
80
- offset += 2;
81
- view.setUint8(offset, edge.flags);
82
- offset += 1;
83
- offset += 1;
84
- }
61
+ this.#offset += this.#wordSize;
62
+ }
63
+ #writeOptionalWord(value) {
64
+ this.#writeWord(value ?? this.#noneValue);
65
+ }
66
+ #writeByte(value) {
67
+ this.#view.setUint8(this.#offset, value);
68
+ this.#offset += 1;
69
+ }
70
+ #writeU16(value) {
71
+ this.#view.setUint16(this.#offset, value, true);
72
+ this.#offset += 2;
73
+ }
74
+ #skip(bytes) {
75
+ this.#offset += bytes;
76
+ }
77
+ #calculateBufferSize() {
78
+ let size = this.#useWide ? 16 : 8;
79
+ for (const node of this.#data.inputNodes) {
80
+ size += this.#wordSize;
81
+ const edgeCount = Object.keys(node.edges).length;
82
+ size += edgeCount * (this.#useWide ? 20 : 10);
83
+ }
84
+ for (const node of this.#data.outputNodes) {
85
+ size += this.#wordSize;
86
+ const edgeCount = Object.keys(node.edges).length;
87
+ size += edgeCount * (this.#useWide ? 12 : 6);
85
88
  }
89
+ size += this.#rootKeys.length * (this.#useWide ? 12 : 6);
90
+ return size;
86
91
  }
87
- for (const node of data.outputNodes) {
88
- const fieldIndices = Object.keys(node.edges).map(Number);
89
- if (useWide) {
90
- view.setUint32(offset, fieldIndices.length, true);
91
- offset += 4;
92
- } else {
93
- view.setUint16(offset, fieldIndices.length, true);
94
- offset += 2;
92
+ #writeHeader() {
93
+ this.#writeByte(this.#useWide ? FORMAT_WIDE : FORMAT_COMPACT);
94
+ this.#skip(this.#useWide ? 3 : 1);
95
+ this.#writeWord(this.#data.inputNodes.length);
96
+ this.#writeWord(this.#data.outputNodes.length);
97
+ this.#writeWord(this.#rootKeys.length);
98
+ }
99
+ #writeInputNodes() {
100
+ for (const node of this.#data.inputNodes) {
101
+ const fieldIndices = Object.keys(node.edges).map(Number);
102
+ this.#writeWord(fieldIndices.length);
103
+ for (const fieldIndex of fieldIndices) {
104
+ const edge = node.edges[fieldIndex];
105
+ this.#writeWord(fieldIndex);
106
+ this.#writeU16(edge.scalarMask ?? 0);
107
+ if (this.#useWide) {
108
+ this.#skip(2);
109
+ }
110
+ this.#writeOptionalWord(edge.childNodeId);
111
+ this.#writeOptionalWord(edge.enumNameIndex);
112
+ this.#writeByte(edge.flags);
113
+ this.#skip(this.#useWide ? 3 : 1);
114
+ }
95
115
  }
96
- for (const fieldIndex of fieldIndices) {
97
- const edge = node.edges[fieldIndex];
98
- if (useWide) {
99
- view.setUint32(offset, fieldIndex, true);
100
- offset += 4;
101
- view.setUint32(offset, edge.argsNodeId ?? NONE_32, true);
102
- offset += 4;
103
- view.setUint32(offset, edge.outputNodeId ?? NONE_32, true);
104
- offset += 4;
105
- } else {
106
- view.setUint16(offset, fieldIndex, true);
107
- offset += 2;
108
- view.setUint16(offset, edge.argsNodeId ?? NONE_16, true);
109
- offset += 2;
110
- view.setUint16(offset, edge.outputNodeId ?? NONE_16, true);
111
- offset += 2;
116
+ }
117
+ #writeOutputNodes() {
118
+ for (const node of this.#data.outputNodes) {
119
+ const fieldIndices = Object.keys(node.edges).map(Number);
120
+ this.#writeWord(fieldIndices.length);
121
+ for (const fieldIndex of fieldIndices) {
122
+ const edge = node.edges[fieldIndex];
123
+ this.#writeWord(fieldIndex);
124
+ this.#writeOptionalWord(edge.argsNodeId);
125
+ this.#writeOptionalWord(edge.outputNodeId);
112
126
  }
113
127
  }
114
128
  }
115
- for (const key of rootKeys) {
116
- const root = data.roots[key];
117
- const keyIndex = data.strings.indexOf(key);
118
- if (useWide) {
119
- view.setUint32(offset, keyIndex, true);
120
- offset += 4;
121
- view.setUint32(offset, root.argsNodeId ?? NONE_32, true);
122
- offset += 4;
123
- view.setUint32(offset, root.outputNodeId ?? NONE_32, true);
124
- offset += 4;
125
- } else {
126
- view.setUint16(offset, keyIndex, true);
127
- offset += 2;
128
- view.setUint16(offset, root.argsNodeId ?? NONE_16, true);
129
- offset += 2;
130
- view.setUint16(offset, root.outputNodeId ?? NONE_16, true);
131
- offset += 2;
129
+ #writeRoots() {
130
+ for (const key of this.#rootKeys) {
131
+ const root = this.#data.roots[key];
132
+ const keyIndex = this.#data.strings.indexOf(key);
133
+ if (keyIndex === -1) {
134
+ throw new Error(`Root key "${key}" not found in strings table`);
135
+ }
136
+ this.#writeWord(keyIndex);
137
+ this.#writeOptionalWord(root.argsNodeId);
138
+ this.#writeOptionalWord(root.outputNodeId);
132
139
  }
133
140
  }
134
- return {
135
- strings: data.strings,
136
- graph: encodeBase64url(new Uint8Array(buffer))
137
- };
138
141
  }
139
- function deserializeParamGraph(serialized) {
140
- const bytes = decodeBase64url(serialized.graph);
141
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
142
- let offset = 0;
143
- const format = view.getUint8(offset++);
144
- const useWide = format === FORMAT_WIDE;
145
- let inputNodeCount;
146
- let outputNodeCount;
147
- let rootCount;
148
- if (useWide) {
149
- inputNodeCount = view.getUint32(offset, true);
150
- offset += 4;
151
- outputNodeCount = view.getUint32(offset, true);
152
- offset += 4;
153
- rootCount = view.getUint32(offset, true);
154
- offset += 4;
155
- } else {
156
- inputNodeCount = view.getUint16(offset, true);
157
- offset += 2;
158
- outputNodeCount = view.getUint16(offset, true);
159
- offset += 2;
160
- rootCount = view.getUint16(offset, true);
161
- offset += 2;
142
+ class Deserializer {
143
+ #serialized;
144
+ #view;
145
+ #offset = 0;
146
+ #useWide = false;
147
+ constructor(serialized) {
148
+ this.#serialized = serialized;
149
+ const bytes = decodeBase64url(serialized.graph);
150
+ this.#view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
151
+ }
152
+ deserialize() {
153
+ const { inputNodeCount, outputNodeCount, rootCount } = this.#readHeader();
154
+ const inputNodes = this.#readInputNodes(inputNodeCount);
155
+ const outputNodes = this.#readOutputNodes(outputNodeCount);
156
+ const roots = this.#readRoots(rootCount);
157
+ return {
158
+ strings: this.#serialized.strings,
159
+ inputNodes,
160
+ outputNodes,
161
+ roots
162
+ };
162
163
  }
163
- const inputNodes = [];
164
- for (let i = 0; i < inputNodeCount; i++) {
165
- let edgeCount;
166
- if (useWide) {
167
- edgeCount = view.getUint32(offset, true);
168
- offset += 4;
164
+ get #wordSize() {
165
+ return this.#useWide ? 4 : 2;
166
+ }
167
+ get #noneValue() {
168
+ return this.#useWide ? NONE_32 : NONE_16;
169
+ }
170
+ #readWord() {
171
+ let value;
172
+ if (this.#useWide) {
173
+ value = this.#view.getUint32(this.#offset, true);
169
174
  } else {
170
- edgeCount = view.getUint16(offset, true);
171
- offset += 2;
175
+ value = this.#view.getUint16(this.#offset, true);
172
176
  }
173
- const edges = {};
174
- for (let j = 0; j < edgeCount; j++) {
175
- let fieldIndex;
176
- let scalarMask;
177
- let childNodeId;
178
- let enumNameIndex;
179
- let flags;
180
- if (useWide) {
181
- fieldIndex = view.getUint32(offset, true);
182
- offset += 4;
183
- scalarMask = view.getUint16(offset, true);
184
- offset += 2;
185
- offset += 2;
186
- childNodeId = view.getUint32(offset, true);
187
- offset += 4;
188
- enumNameIndex = view.getUint32(offset, true);
189
- offset += 4;
190
- flags = view.getUint8(offset);
191
- offset += 1;
192
- offset += 3;
193
- } else {
194
- fieldIndex = view.getUint16(offset, true);
195
- offset += 2;
196
- scalarMask = view.getUint16(offset, true);
197
- offset += 2;
198
- childNodeId = view.getUint16(offset, true);
199
- offset += 2;
200
- enumNameIndex = view.getUint16(offset, true);
201
- offset += 2;
202
- flags = view.getUint8(offset);
203
- offset += 1;
204
- offset += 1;
205
- }
206
- const edge = { flags };
207
- if (scalarMask !== 0) edge.scalarMask = scalarMask;
208
- if (childNodeId !== (useWide ? NONE_32 : NONE_16)) edge.childNodeId = childNodeId;
209
- if (enumNameIndex !== (useWide ? NONE_32 : NONE_16)) edge.enumNameIndex = enumNameIndex;
210
- edges[fieldIndex] = edge;
177
+ this.#offset += this.#wordSize;
178
+ return value;
179
+ }
180
+ #readOptionalWord() {
181
+ const value = this.#readWord();
182
+ return value === this.#noneValue ? void 0 : value;
183
+ }
184
+ #readByte() {
185
+ const value = this.#view.getUint8(this.#offset);
186
+ this.#offset += 1;
187
+ return value;
188
+ }
189
+ #readU16() {
190
+ const value = this.#view.getUint16(this.#offset, true);
191
+ this.#offset += 2;
192
+ return value;
193
+ }
194
+ #skip(bytes) {
195
+ this.#offset += bytes;
196
+ }
197
+ #readHeader() {
198
+ const format = this.#readByte();
199
+ if (format !== FORMAT_COMPACT && format !== FORMAT_WIDE) {
200
+ throw new Error(`Unknown param graph format: 0x${format.toString(16).padStart(2, "0")}`);
211
201
  }
212
- inputNodes.push({ edges });
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();
207
+ return { inputNodeCount, outputNodeCount, rootCount };
213
208
  }
214
- const outputNodes = [];
215
- for (let i = 0; i < outputNodeCount; i++) {
216
- let edgeCount;
217
- if (useWide) {
218
- edgeCount = view.getUint32(offset, true);
219
- offset += 4;
220
- } else {
221
- edgeCount = view.getUint16(offset, true);
222
- offset += 2;
209
+ #readInputNodes(count) {
210
+ const inputNodes = [];
211
+ for (let i = 0; i < count; i++) {
212
+ const edgeCount = this.#readWord();
213
+ const edges = {};
214
+ for (let j = 0; j < edgeCount; j++) {
215
+ const fieldIndex = this.#readWord();
216
+ const scalarMask = this.#readU16();
217
+ if (this.#useWide) {
218
+ this.#skip(2);
219
+ }
220
+ const childNodeId = this.#readOptionalWord();
221
+ const enumNameIndex = this.#readOptionalWord();
222
+ const flags = this.#readByte();
223
+ this.#skip(this.#useWide ? 3 : 1);
224
+ const edge = { flags };
225
+ if (scalarMask !== 0) edge.scalarMask = scalarMask;
226
+ if (childNodeId !== void 0) edge.childNodeId = childNodeId;
227
+ if (enumNameIndex !== void 0) edge.enumNameIndex = enumNameIndex;
228
+ edges[fieldIndex] = edge;
229
+ }
230
+ inputNodes.push({ edges });
223
231
  }
224
- const edges = {};
225
- for (let j = 0; j < edgeCount; j++) {
226
- let fieldIndex;
227
- let argsNodeId;
228
- let outputNodeId;
229
- if (useWide) {
230
- fieldIndex = view.getUint32(offset, true);
231
- offset += 4;
232
- argsNodeId = view.getUint32(offset, true);
233
- offset += 4;
234
- outputNodeId = view.getUint32(offset, true);
235
- offset += 4;
236
- } else {
237
- fieldIndex = view.getUint16(offset, true);
238
- offset += 2;
239
- argsNodeId = view.getUint16(offset, true);
240
- offset += 2;
241
- outputNodeId = view.getUint16(offset, true);
242
- offset += 2;
232
+ return inputNodes;
233
+ }
234
+ #readOutputNodes(count) {
235
+ const outputNodes = [];
236
+ for (let i = 0; i < count; i++) {
237
+ const edgeCount = this.#readWord();
238
+ const edges = {};
239
+ for (let j = 0; j < edgeCount; j++) {
240
+ const fieldIndex = this.#readWord();
241
+ const argsNodeId = this.#readOptionalWord();
242
+ const outputNodeId = this.#readOptionalWord();
243
+ const edge = {};
244
+ if (argsNodeId !== void 0) edge.argsNodeId = argsNodeId;
245
+ if (outputNodeId !== void 0) edge.outputNodeId = outputNodeId;
246
+ edges[fieldIndex] = edge;
243
247
  }
244
- const edge = {};
245
- if (argsNodeId !== (useWide ? NONE_32 : NONE_16)) edge.argsNodeId = argsNodeId;
246
- if (outputNodeId !== (useWide ? NONE_32 : NONE_16)) edge.outputNodeId = outputNodeId;
247
- edges[fieldIndex] = edge;
248
+ outputNodes.push({ edges });
248
249
  }
249
- outputNodes.push({ edges });
250
+ return outputNodes;
250
251
  }
251
- const roots = {};
252
- for (let i = 0; i < rootCount; i++) {
253
- let keyIndex;
254
- let argsNodeId;
255
- let outputNodeId;
256
- if (useWide) {
257
- keyIndex = view.getUint32(offset, true);
258
- offset += 4;
259
- argsNodeId = view.getUint32(offset, true);
260
- offset += 4;
261
- outputNodeId = view.getUint32(offset, true);
262
- offset += 4;
263
- } else {
264
- keyIndex = view.getUint16(offset, true);
265
- offset += 2;
266
- argsNodeId = view.getUint16(offset, true);
267
- offset += 2;
268
- outputNodeId = view.getUint16(offset, true);
269
- offset += 2;
252
+ #readRoots(count) {
253
+ const roots = {};
254
+ for (let i = 0; i < count; i++) {
255
+ const keyIndex = this.#readWord();
256
+ const argsNodeId = this.#readOptionalWord();
257
+ const outputNodeId = this.#readOptionalWord();
258
+ const key = this.#serialized.strings[keyIndex];
259
+ const root = {};
260
+ if (argsNodeId !== void 0) root.argsNodeId = argsNodeId;
261
+ if (outputNodeId !== void 0) root.outputNodeId = outputNodeId;
262
+ roots[key] = root;
270
263
  }
271
- const key = serialized.strings[keyIndex];
272
- const root = {};
273
- if (argsNodeId !== (useWide ? NONE_32 : NONE_16)) root.argsNodeId = argsNodeId;
274
- if (outputNodeId !== (useWide ? NONE_32 : NONE_16)) root.outputNodeId = outputNodeId;
275
- roots[key] = root;
264
+ return roots;
276
265
  }
277
- return {
278
- strings: serialized.strings,
279
- inputNodes,
280
- outputNodes,
281
- roots
282
- };
283
266
  }
284
267
  export {
285
268
  deserializeParamGraph,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/param-graph",
3
- "version": "7.4.0-integration-parameterization.9",
3
+ "version": "7.4.0-integration-parameterization.11",
4
4
  "description": "This package is intended for Prisma's internal use",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",