edinburgh 0.1.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +146 -84
- package/build/src/datapack.d.ts +119 -0
- package/build/src/datapack.js +620 -0
- package/build/src/datapack.js.map +1 -0
- package/build/src/edinburgh.d.ts +15 -3
- package/build/src/edinburgh.js +64 -30
- package/build/src/edinburgh.js.map +1 -1
- package/build/src/indexes.d.ts +58 -91
- package/build/src/indexes.js +360 -285
- package/build/src/indexes.js.map +1 -1
- package/build/src/models.d.ts +41 -45
- package/build/src/models.js +191 -239
- package/build/src/models.js.map +1 -1
- package/build/src/types.d.ts +188 -256
- package/build/src/types.js +381 -316
- package/build/src/types.js.map +1 -1
- package/build/src/utils.d.ts +9 -5
- package/build/src/utils.js +34 -5
- package/build/src/utils.js.map +1 -1
- package/package.json +17 -15
- package/src/datapack.ts +655 -0
- package/src/edinburgh.ts +68 -29
- package/src/indexes.ts +398 -319
- package/src/models.ts +224 -262
- package/src/types.ts +461 -385
- package/src/utils.ts +37 -9
- package/build/src/bytes.d.ts +0 -155
- package/build/src/bytes.js +0 -455
- package/build/src/bytes.js.map +0 -1
- package/src/bytes.ts +0 -500
package/src/types.ts
CHANGED
|
@@ -1,593 +1,668 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DataPack } from "./datapack.js";
|
|
2
2
|
import * as olmdb from "olmdb";
|
|
3
3
|
import { DatabaseError } from "olmdb";
|
|
4
4
|
import { Model, modelRegistry, getMockModel } from "./models.js";
|
|
5
5
|
import { assert, addErrorPath } from "./utils.js";
|
|
6
6
|
|
|
7
|
+
|
|
7
8
|
/**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
* @internal Abstract base class for all type wrappers in the Edinburgh ORM system.
|
|
10
|
+
*
|
|
11
|
+
* This is an implementation detail and should not be referenced directly in user code.
|
|
12
|
+
* Type wrappers define how values are serialized to/from the database and how they are validated.
|
|
13
|
+
* Each type wrapper must implement serialization, deserialization, and validation logic.
|
|
14
|
+
*
|
|
15
|
+
* @template T - The TypeScript type this wrapper represents.
|
|
16
|
+
*/
|
|
16
17
|
export abstract class TypeWrapper<const T> {
|
|
17
18
|
/** @internal Used for TypeScript type inference - this field is required for the type system */
|
|
18
19
|
_T!: T;
|
|
19
20
|
|
|
20
21
|
/** A string identifier for this type, used during serialization */
|
|
21
22
|
abstract kind: string;
|
|
22
|
-
|
|
23
|
-
constructor() {}
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
* Serialize a value from an object property to bytes.
|
|
27
|
-
* @param obj - The object containing the value.
|
|
28
|
-
* @param prop - The property name or index.
|
|
29
|
-
* @param bytes - The Bytes instance to write to.
|
|
30
|
-
* @param model - Optional model instance for context.
|
|
31
|
-
*/
|
|
32
|
-
abstract serialize(obj: any, prop: string|number, bytes: Bytes, model?: any): void;
|
|
24
|
+
constructor() {}
|
|
33
25
|
|
|
34
26
|
/**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
abstract deserialize(obj: any, prop: string|number, bytes: Bytes, model?: any): void;
|
|
42
|
-
|
|
27
|
+
* Serialize a value from an object property to a Pack.
|
|
28
|
+
* @param value - The value to serialize.
|
|
29
|
+
* @param pack - The Pack instance to write to.
|
|
30
|
+
*/
|
|
31
|
+
abstract serialize(value: T, pack: DataPack): void;
|
|
32
|
+
|
|
43
33
|
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
abstract getErrors(obj: any, prop: string | number): DatabaseError[];
|
|
50
|
-
|
|
34
|
+
* Deserialize a value from a Pack into an object property.
|
|
35
|
+
* @param pack - The Pack instance to read from.
|
|
36
|
+
*/
|
|
37
|
+
abstract deserialize(pack: DataPack): T;
|
|
38
|
+
|
|
51
39
|
/**
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
* @throws {DatabaseError} If validation fails.
|
|
58
|
-
*/
|
|
59
|
-
validateAndSerialize(obj: any, prop: string|number, bytes: Bytes, model?: any): void {
|
|
60
|
-
const errors = this.getErrors(obj, prop);
|
|
61
|
-
if (errors.length) throw errors[0];
|
|
62
|
-
this.serialize(obj, prop, bytes, model);
|
|
63
|
-
}
|
|
40
|
+
* Validate a value.
|
|
41
|
+
* @param value - The value to validate.
|
|
42
|
+
* @returns - A DatabaseError if validation fails.
|
|
43
|
+
*/
|
|
44
|
+
abstract getError(value: T): DatabaseError | void;
|
|
64
45
|
|
|
65
46
|
/**
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
serializeType(
|
|
47
|
+
* Serialize type metadata to a Pack (for schema serialization).
|
|
48
|
+
* @param pack - The Pack instance to write to.
|
|
49
|
+
*/
|
|
50
|
+
serializeType(pack: DataPack) {}
|
|
70
51
|
|
|
71
52
|
/**
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
53
|
+
* Check if indexing should be skipped for this field value.
|
|
54
|
+
* @param obj - The object containing the value.
|
|
55
|
+
* @param prop - The property name or index.
|
|
56
|
+
* @returns true if indexing should be skipped.
|
|
57
|
+
*/
|
|
58
|
+
containsNull(value: T): boolean {
|
|
78
59
|
return false;
|
|
79
60
|
}
|
|
80
|
-
|
|
61
|
+
|
|
81
62
|
toString(): string {
|
|
82
63
|
return `${this.kind}`;
|
|
83
64
|
}
|
|
65
|
+
|
|
66
|
+
clone(value: T): T {
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
equals(value1: T, value2: T): boolean {
|
|
71
|
+
return value1 === value2;
|
|
72
|
+
}
|
|
84
73
|
}
|
|
85
74
|
|
|
86
|
-
|
|
87
|
-
* Optional interface for type wrappers that can provide default values.
|
|
88
|
-
* @template T - The TypeScript type this wrapper represents.
|
|
89
|
-
*/
|
|
75
|
+
|
|
90
76
|
export interface TypeWrapper<T> {
|
|
91
77
|
/**
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
78
|
+
* Generate a default value for this type.
|
|
79
|
+
* @param model - The model instance.
|
|
80
|
+
* @returns The default value.
|
|
81
|
+
*/
|
|
96
82
|
default?(model: any): T;
|
|
97
83
|
}
|
|
98
84
|
|
|
99
|
-
|
|
100
|
-
* @internal Type wrapper for string values.
|
|
101
|
-
*/
|
|
85
|
+
|
|
102
86
|
class StringType extends TypeWrapper<string> {
|
|
103
87
|
kind = 'string';
|
|
104
88
|
|
|
105
|
-
serialize(
|
|
106
|
-
|
|
89
|
+
serialize(value: string, pack: DataPack) {
|
|
90
|
+
pack.write(value);
|
|
107
91
|
}
|
|
108
92
|
|
|
109
|
-
deserialize(
|
|
110
|
-
|
|
93
|
+
deserialize(pack: DataPack): string {
|
|
94
|
+
return pack.readString();
|
|
111
95
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (typeof
|
|
115
|
-
return
|
|
96
|
+
|
|
97
|
+
getError(value: string) {
|
|
98
|
+
if (typeof value !== 'string') {
|
|
99
|
+
return new DatabaseError(`Expected string, got ${typeof value}`, 'INVALID_TYPE');
|
|
116
100
|
}
|
|
117
|
-
return [];
|
|
118
101
|
}
|
|
119
102
|
}
|
|
120
103
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
104
|
+
|
|
105
|
+
class OrderedStringType extends StringType {
|
|
106
|
+
serialize(value: string, pack: DataPack) {
|
|
107
|
+
pack.writeOrderedString(value);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
124
112
|
class NumberType extends TypeWrapper<number> {
|
|
125
113
|
kind = 'number';
|
|
126
|
-
|
|
127
|
-
serialize(
|
|
128
|
-
|
|
114
|
+
|
|
115
|
+
serialize(value: number, pack: DataPack) {
|
|
116
|
+
pack.write(value);
|
|
129
117
|
}
|
|
130
|
-
|
|
131
|
-
deserialize(
|
|
132
|
-
|
|
118
|
+
|
|
119
|
+
deserialize(pack: DataPack): number {
|
|
120
|
+
return pack.readNumber();
|
|
133
121
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const value = obj[prop];
|
|
122
|
+
|
|
123
|
+
getError(value: number) {
|
|
137
124
|
if (typeof value !== 'number' || isNaN(value)) {
|
|
138
|
-
return
|
|
125
|
+
return new DatabaseError(`Expected number, got ${typeof value}`, 'INVALID_TYPE');
|
|
139
126
|
}
|
|
140
|
-
return [];
|
|
141
127
|
}
|
|
142
128
|
}
|
|
143
129
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
130
|
+
class DateTimeType extends TypeWrapper<Date> {
|
|
131
|
+
kind = 'dateTime';
|
|
132
|
+
|
|
133
|
+
serialize(value: Date, pack: DataPack) {
|
|
134
|
+
pack.write(value);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
deserialize(pack: DataPack): Date {
|
|
138
|
+
return pack.readDate();;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
getError(value: Date) {
|
|
142
|
+
if (!(value instanceof Date)) {
|
|
143
|
+
return new DatabaseError(`Expected Date, got ${typeof value}`, 'INVALID_TYPE');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
default(): Date {
|
|
148
|
+
return new Date();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
}
|
|
152
|
+
|
|
147
153
|
class BooleanType extends TypeWrapper<boolean> {
|
|
148
154
|
kind = 'boolean';
|
|
149
|
-
|
|
150
|
-
serialize(
|
|
151
|
-
|
|
155
|
+
|
|
156
|
+
serialize(value: boolean, pack: DataPack) {
|
|
157
|
+
pack.write(value);
|
|
152
158
|
}
|
|
153
|
-
|
|
154
|
-
deserialize(
|
|
155
|
-
|
|
159
|
+
|
|
160
|
+
deserialize(pack: DataPack): boolean {
|
|
161
|
+
return pack.readBoolean();
|
|
156
162
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (typeof
|
|
160
|
-
return
|
|
163
|
+
|
|
164
|
+
getError(value: boolean) {
|
|
165
|
+
if (typeof value !== 'boolean') {
|
|
166
|
+
return new DatabaseError(`Expected boolean, got ${typeof value}`, 'INVALID_TYPE');
|
|
161
167
|
}
|
|
162
|
-
return [];
|
|
163
168
|
}
|
|
164
169
|
}
|
|
165
170
|
|
|
166
171
|
/**
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
172
|
+
* @internal Type wrapper for array values with optional length constraints.
|
|
173
|
+
* @template T - The type of array elements.
|
|
174
|
+
*/
|
|
170
175
|
class ArrayType<T> extends TypeWrapper<T[]> {
|
|
171
176
|
kind = 'array';
|
|
172
177
|
|
|
173
178
|
/**
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
179
|
+
* Create a new ArrayType.
|
|
180
|
+
* @param inner - Type wrapper for array elements.
|
|
181
|
+
* @param opts - Array constraints (min/max length).
|
|
182
|
+
*/
|
|
178
183
|
constructor(public inner: TypeWrapper<T>, public opts: {min?: number, max?: number} = {}) {
|
|
179
184
|
super();
|
|
180
185
|
}
|
|
181
|
-
|
|
182
|
-
serialize(
|
|
183
|
-
|
|
184
|
-
bytes.writeNumber(value.length);
|
|
186
|
+
|
|
187
|
+
serialize(value: T[], pack: DataPack) {
|
|
188
|
+
pack.write(value.length);
|
|
185
189
|
for(let i=0; i<value.length; i++) {
|
|
186
|
-
this.inner.serialize(value
|
|
190
|
+
this.inner.serialize(value[i], pack);
|
|
187
191
|
}
|
|
188
192
|
}
|
|
189
193
|
|
|
190
|
-
deserialize(
|
|
191
|
-
const length =
|
|
194
|
+
deserialize(pack: DataPack): T[] {
|
|
195
|
+
const length = pack.readNumber();
|
|
192
196
|
const result: T[] = [];
|
|
193
197
|
for (let i = 0; i < length; i++) {
|
|
194
|
-
this.inner.deserialize(
|
|
198
|
+
result.push(this.inner.deserialize(pack));
|
|
195
199
|
}
|
|
196
|
-
|
|
200
|
+
return result;
|
|
197
201
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const value = obj[prop];
|
|
202
|
+
|
|
203
|
+
getError(value: T[]) {
|
|
201
204
|
if (!Array.isArray(value)) {
|
|
202
|
-
return
|
|
205
|
+
return new DatabaseError(`Expected array, got ${typeof value}`, 'INVALID_TYPE');
|
|
203
206
|
}
|
|
204
|
-
|
|
207
|
+
|
|
205
208
|
if (this.opts.min !== undefined && value.length < this.opts.min) {
|
|
206
|
-
|
|
209
|
+
return new DatabaseError(`Array length ${value.length} is less than minimum ${this.opts.min}`, 'OUT_OF_BOUNDS');
|
|
207
210
|
}
|
|
208
211
|
if (this.opts.max !== undefined && value.length > this.opts.max) {
|
|
209
|
-
|
|
212
|
+
return new DatabaseError(`Array length ${value.length} is greater than maximum ${this.opts.max}`, 'OUT_OF_BOUNDS');
|
|
210
213
|
}
|
|
211
214
|
for (let i = 0; i < value.length; i++) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
+
let error = this.inner.getError(value[i]);
|
|
216
|
+
if (error) return addErrorPath(error, i);
|
|
215
217
|
}
|
|
216
|
-
return errors;
|
|
217
218
|
}
|
|
218
219
|
|
|
219
|
-
serializeType(
|
|
220
|
-
serializeType(this.inner,
|
|
220
|
+
serializeType(pack: DataPack): void {
|
|
221
|
+
serializeType(this.inner, pack);
|
|
221
222
|
}
|
|
222
223
|
|
|
223
|
-
static deserializeType(
|
|
224
|
-
const inner = deserializeType(
|
|
224
|
+
static deserializeType(pack: DataPack, featureFlags: number): ArrayType<any> {
|
|
225
|
+
const inner = deserializeType(pack, featureFlags);
|
|
225
226
|
return new ArrayType(inner);
|
|
226
227
|
}
|
|
228
|
+
|
|
229
|
+
clone(value: T[]): T[] {
|
|
230
|
+
return value.map(this.inner.clone.bind(this.inner));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
equals(a: T[], b: T[]): boolean {
|
|
234
|
+
if (a.length !== b.length) return false;
|
|
235
|
+
for (let i = 0; i < a.length; i++) {
|
|
236
|
+
if (!this.inner.equals(a[i], b[i])) return false;
|
|
237
|
+
}
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
|
|
227
241
|
}
|
|
228
242
|
|
|
229
243
|
/**
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
244
|
+
* @internal Type wrapper for array values with optional length constraints.
|
|
245
|
+
* @template T - The type of array elements.
|
|
246
|
+
*/
|
|
247
|
+
export class SetType<T> extends TypeWrapper<Set<T>> {
|
|
248
|
+
kind = 'set';
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Create a new SetType.
|
|
252
|
+
* @param inner - Type wrapper for set elements.
|
|
253
|
+
*/
|
|
254
|
+
constructor(public inner: TypeWrapper<T>, public opts: {min?: number, max?: number} = {}) {
|
|
255
|
+
super();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
serialize(value: Set<T>, pack: DataPack) {
|
|
259
|
+
pack.write(value.size);
|
|
260
|
+
for (const item of value) {
|
|
261
|
+
this.inner.serialize(item, pack);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
deserialize(pack: DataPack): Set<T> {
|
|
266
|
+
const length = pack.readNumber();
|
|
267
|
+
const result = new Set<T>();
|
|
268
|
+
for (let i = 0; i < length; i++) {
|
|
269
|
+
result.add(this.inner.deserialize(pack));
|
|
270
|
+
}
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
getError(value: Set<T>) {
|
|
275
|
+
if (!(value instanceof Set)) {
|
|
276
|
+
return new DatabaseError(`Expected Set, got ${typeof value}`, 'INVALID_TYPE');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (this.opts.min !== undefined && value.size < this.opts.min) {
|
|
280
|
+
return new DatabaseError(`Set size ${value.size} is less than minimum ${this.opts.min}`, 'OUT_OF_BOUNDS');
|
|
281
|
+
}
|
|
282
|
+
if (this.opts.max !== undefined && value.size > this.opts.max) {
|
|
283
|
+
return new DatabaseError(`Set size ${value.size} is greater than maximum ${this.opts.max}`, 'OUT_OF_BOUNDS');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
for (const item of value) {
|
|
288
|
+
this.inner.getError(item);
|
|
289
|
+
}
|
|
290
|
+
} catch (err) {
|
|
291
|
+
throw addErrorPath(err, 'item');
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
serializeType(pack: DataPack): void {
|
|
296
|
+
serializeType(this.inner, pack);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
static deserializeType(pack: DataPack, featureFlags: number): SetType<any> {
|
|
300
|
+
const inner = deserializeType(pack, featureFlags);
|
|
301
|
+
return new SetType(inner);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
default(): Set<T> {
|
|
305
|
+
return new Set<T>();
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
clone(value: Set<T>): Set<T> {
|
|
309
|
+
const cloned = new Set<T>();
|
|
310
|
+
for (const item of value) {
|
|
311
|
+
cloned.add(this.inner.clone(item));
|
|
312
|
+
}
|
|
313
|
+
return cloned;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
equals(a: Set<T>, b: Set<T>): boolean {
|
|
317
|
+
if (a.size !== b.size) return false;
|
|
318
|
+
for(const v of a) {
|
|
319
|
+
if (!b.has(v)) return false;
|
|
320
|
+
}
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* @internal Type wrapper for union/discriminated union types.
|
|
328
|
+
* @template T - The union type this wrapper represents.
|
|
329
|
+
*/
|
|
233
330
|
class OrType<const T> extends TypeWrapper<T> {
|
|
234
331
|
kind = 'or';
|
|
235
332
|
|
|
236
333
|
/**
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
334
|
+
* Create a new OrType.
|
|
335
|
+
* @param choices - Array of type wrappers representing the union choices.
|
|
336
|
+
*/
|
|
240
337
|
constructor(public choices: TypeWrapper<T>[]) {
|
|
241
338
|
super();
|
|
242
339
|
}
|
|
243
340
|
|
|
244
|
-
_getChoiceIndex(
|
|
341
|
+
_getChoiceIndex(value: any): number {
|
|
245
342
|
for (const [i, choice] of this.choices.entries()) {
|
|
246
|
-
if (choice.
|
|
247
|
-
return i;
|
|
248
|
-
}
|
|
343
|
+
if (!choice.getError(value)) return i;
|
|
249
344
|
}
|
|
250
|
-
throw new DatabaseError(`Value does not match any union type: ${
|
|
345
|
+
throw new DatabaseError(`Value does not match any union type: ${value}`, 'INVALID_TYPE');
|
|
251
346
|
}
|
|
252
347
|
|
|
253
|
-
serialize(
|
|
254
|
-
const choiceIndex = this._getChoiceIndex(
|
|
255
|
-
|
|
256
|
-
this.choices[choiceIndex].serialize(
|
|
348
|
+
serialize(value: T, pack: DataPack) {
|
|
349
|
+
const choiceIndex = this._getChoiceIndex(value);
|
|
350
|
+
pack.write(choiceIndex);
|
|
351
|
+
this.choices[choiceIndex].serialize(value, pack);
|
|
257
352
|
}
|
|
258
353
|
|
|
259
|
-
deserialize(
|
|
260
|
-
const index =
|
|
354
|
+
deserialize(pack: DataPack) {
|
|
355
|
+
const index = pack.readNumber();
|
|
261
356
|
if (index < 0 || index >= this.choices.length) {
|
|
262
357
|
throw new DatabaseError(`Could not deserialize invalid union index ${index}`, 'DESERIALIZATION_ERROR');
|
|
263
358
|
}
|
|
264
359
|
const type = this.choices[index];
|
|
265
|
-
type.deserialize(
|
|
360
|
+
return type.deserialize(pack);
|
|
266
361
|
}
|
|
267
362
|
|
|
268
|
-
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
const type = this.choices[i];
|
|
272
|
-
const subErrors = type.getErrors(obj, prop);
|
|
273
|
-
if (subErrors.length === 0) return [];
|
|
274
|
-
for (let err of subErrors) {
|
|
275
|
-
errors.push(addErrorPath(err, `opt${i+1}`));
|
|
276
|
-
}
|
|
363
|
+
getError(value: any) {
|
|
364
|
+
for (const choice of this.choices.values()) {
|
|
365
|
+
if (!choice.getError(value)) return;
|
|
277
366
|
}
|
|
278
|
-
return
|
|
367
|
+
return new DatabaseError(`Value does not match any union type: ${value}`, 'INVALID_TYPE');
|
|
279
368
|
}
|
|
280
369
|
|
|
281
|
-
|
|
282
|
-
const choiceIndex = this._getChoiceIndex(
|
|
283
|
-
return this.choices[choiceIndex].
|
|
370
|
+
containsNull(value: T): boolean {
|
|
371
|
+
const choiceIndex = this._getChoiceIndex(value);
|
|
372
|
+
return this.choices[choiceIndex].containsNull(value);
|
|
284
373
|
}
|
|
285
374
|
|
|
286
|
-
serializeType(
|
|
287
|
-
|
|
375
|
+
serializeType(pack: DataPack): void {
|
|
376
|
+
pack.write(this.choices.length);
|
|
288
377
|
for (const choice of this.choices) {
|
|
289
|
-
serializeType(choice,
|
|
378
|
+
serializeType(choice, pack);
|
|
290
379
|
}
|
|
291
380
|
}
|
|
292
381
|
|
|
293
|
-
static deserializeType(
|
|
294
|
-
const count =
|
|
382
|
+
static deserializeType(pack: DataPack, featureFlags: number): OrType<any> {
|
|
383
|
+
const count = pack.readNumber();
|
|
295
384
|
const choices: TypeWrapper<unknown>[] = [];
|
|
296
385
|
for (let i = 0; i < count; i++) {
|
|
297
|
-
choices.push(deserializeType(
|
|
386
|
+
choices.push(deserializeType(pack, featureFlags));
|
|
298
387
|
}
|
|
299
388
|
return new OrType(choices);
|
|
300
389
|
}
|
|
390
|
+
|
|
391
|
+
clone(value: T): T {
|
|
392
|
+
const choiceIndex = this._getChoiceIndex(value);
|
|
393
|
+
return this.choices[choiceIndex].clone(value);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
equals(a: T, b: T): boolean {
|
|
397
|
+
const ca = this._getChoiceIndex(a);
|
|
398
|
+
const cb = this._getChoiceIndex(b);
|
|
399
|
+
return ca === cb && this.choices[ca].equals(a, b);
|
|
400
|
+
}
|
|
301
401
|
}
|
|
302
402
|
|
|
303
403
|
/**
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
404
|
+
* @internal Type wrapper for literal values (constants).
|
|
405
|
+
* @template T - The literal type this wrapper represents.
|
|
406
|
+
*/
|
|
307
407
|
class LiteralType<const T> extends TypeWrapper<T> {
|
|
308
408
|
kind = 'literal';
|
|
309
409
|
|
|
310
410
|
/**
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
411
|
+
* Create a new LiteralType.
|
|
412
|
+
* @param value - The literal value this type represents.
|
|
413
|
+
*/
|
|
314
414
|
constructor(public value: T) {
|
|
315
415
|
super();
|
|
316
416
|
}
|
|
317
417
|
|
|
318
|
-
serialize(
|
|
418
|
+
serialize(value: T, pack: DataPack) {
|
|
319
419
|
// Literal values don't need to be serialized since they're constants
|
|
320
420
|
}
|
|
321
421
|
|
|
322
|
-
deserialize(
|
|
323
|
-
|
|
422
|
+
deserialize(pack: DataPack) {
|
|
423
|
+
return this.value;
|
|
324
424
|
}
|
|
325
425
|
|
|
326
|
-
|
|
327
|
-
|
|
426
|
+
getError(value: any) {
|
|
427
|
+
if (this.value!==value) {
|
|
428
|
+
return new DatabaseError(`Invalid literal value ${value} instead of ${this.value}`, 'INVALID_TYPE');
|
|
429
|
+
}
|
|
328
430
|
}
|
|
329
431
|
|
|
330
|
-
serializeType(
|
|
331
|
-
|
|
432
|
+
serializeType(pack: DataPack): void {
|
|
433
|
+
pack.write(this.value===undefined ? "" : JSON.stringify(this.value));
|
|
332
434
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
return
|
|
435
|
+
|
|
436
|
+
containsNull(value: T): boolean {
|
|
437
|
+
return value == null;
|
|
336
438
|
}
|
|
337
439
|
|
|
338
|
-
static deserializeType(
|
|
339
|
-
const json =
|
|
440
|
+
static deserializeType(pack: DataPack, featureFlags: number): LiteralType<any> {
|
|
441
|
+
const json = pack.readString();
|
|
340
442
|
const value = json==="" ? undefined : JSON.parse(json);
|
|
341
443
|
return new LiteralType(value);
|
|
342
444
|
}
|
|
343
|
-
|
|
344
|
-
default(
|
|
445
|
+
|
|
446
|
+
default(): T {
|
|
345
447
|
return this.value;
|
|
346
448
|
}
|
|
347
449
|
}
|
|
348
450
|
|
|
349
|
-
const ID_SIZE =
|
|
451
|
+
const ID_SIZE = 8;
|
|
350
452
|
|
|
351
453
|
/**
|
|
352
|
-
|
|
353
|
-
|
|
454
|
+
* @internal Type wrapper for auto-generated unique identifier strings.
|
|
455
|
+
*/
|
|
354
456
|
class IdentifierType extends TypeWrapper<string> {
|
|
355
457
|
kind = 'id';
|
|
356
|
-
|
|
357
|
-
serialize(
|
|
358
|
-
const value = obj[prop];
|
|
458
|
+
|
|
459
|
+
serialize(value: string, pack: DataPack): void {
|
|
359
460
|
assert(value.length === ID_SIZE);
|
|
360
|
-
|
|
461
|
+
pack.writeIdentifier(value);
|
|
361
462
|
}
|
|
362
|
-
|
|
363
|
-
deserialize(
|
|
364
|
-
|
|
463
|
+
|
|
464
|
+
deserialize(pack: DataPack) {
|
|
465
|
+
return pack.readIdentifier();
|
|
365
466
|
}
|
|
366
467
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
if (typeof value !== 'string' || value.length !== ID_SIZE) return [new DatabaseError(`Invalid ID format: ${value}`, 'VALUE_ERROR')];
|
|
370
|
-
return [];
|
|
468
|
+
getError(value: any) {
|
|
469
|
+
if (typeof value !== 'string' || value.length !== ID_SIZE) return new DatabaseError(`Invalid ID format: ${value}`, 'VALUE_ERROR');
|
|
371
470
|
}
|
|
372
471
|
|
|
373
|
-
serializeType(
|
|
472
|
+
serializeType(pack: DataPack): void {
|
|
374
473
|
}
|
|
375
474
|
|
|
376
|
-
static deserializeType(
|
|
475
|
+
static deserializeType(pack: DataPack, featureFlags: number): IdentifierType {
|
|
377
476
|
return new IdentifierType();
|
|
378
477
|
}
|
|
379
|
-
|
|
478
|
+
|
|
380
479
|
default(model: Model<any>): string {
|
|
381
480
|
// Generate a random ID, and if it already exists in the database, retry.
|
|
382
481
|
let id: string;
|
|
383
482
|
do {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
// Bit 0...14 are random bits (partly overlapping with the date, adding up to 31ms of jitter)
|
|
387
|
-
let num = Math.floor(+new Date() * (1<<9) + Math.random() * (1<<14));
|
|
388
|
-
|
|
389
|
-
id = '';
|
|
390
|
-
for(let i = 0; i < 7; i++) {
|
|
391
|
-
id = Bytes.BASE64_CHARS[num & 0x3f] + id;
|
|
392
|
-
num = Math.floor(num / 64);
|
|
393
|
-
}
|
|
394
|
-
} while (olmdb.get(new Bytes().writeNumber(model.constructor._pk!._cachedIndexId!).writeBase64(id).getBuffer()));
|
|
483
|
+
id = DataPack.generateIdentifier();
|
|
484
|
+
} while (olmdb.get(new DataPack().write(model.constructor._primary!._cachedIndexId!).writeIdentifier(id).toUint8Array()));
|
|
395
485
|
return id;
|
|
396
486
|
}
|
|
397
487
|
}
|
|
398
488
|
|
|
399
|
-
const WANT_PK_ARRAY = {};
|
|
400
|
-
|
|
401
|
-
type KeysOfType<T, TProp> = { [P in keyof T]: T[P] extends TProp? P : never}[keyof T];
|
|
402
|
-
|
|
403
489
|
/**
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
export class LinkType<T extends typeof Model<
|
|
490
|
+
* @internal Type wrapper for model relationships (foreign keys).
|
|
491
|
+
* @template T - The target model class type.
|
|
492
|
+
*/
|
|
493
|
+
export class LinkType<T extends typeof Model<unknown>> extends TypeWrapper<InstanceType<T>> {
|
|
408
494
|
kind = 'link';
|
|
409
495
|
TargetModel: T;
|
|
410
|
-
|
|
496
|
+
|
|
411
497
|
/**
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
498
|
+
* Create a new LinkType.
|
|
499
|
+
* @param TargetModel - The model class this link points to.
|
|
500
|
+
*/
|
|
415
501
|
constructor(TargetModel: T) {
|
|
416
502
|
super();
|
|
417
503
|
this.TargetModel = getMockModel(TargetModel);
|
|
418
504
|
}
|
|
419
|
-
|
|
420
|
-
serialize(
|
|
421
|
-
|
|
422
|
-
const pk = this.TargetModel._pk!;
|
|
423
|
-
// If obj[prop] is getter(), it will return the primary key array (based on WANT_PK_ARRAY
|
|
424
|
-
// being the receiver). Otherwise, it will just return the value, which is a model instance.
|
|
425
|
-
let value = Reflect.get(obj, prop, WANT_PK_ARRAY) as any[] | Model<InstanceType<T>>;
|
|
426
|
-
if (value instanceof Array) {
|
|
427
|
-
// It's a pk array, and the object has not been loaded. We can just serialize it.
|
|
428
|
-
pk._serializeArgs(value, bytes);
|
|
429
|
-
} else {
|
|
430
|
-
// It's a model instance that has been loaded
|
|
431
|
-
pk._serializeModel(value, bytes);
|
|
432
|
-
}
|
|
505
|
+
|
|
506
|
+
serialize(model: InstanceType<T>, pack: DataPack) {
|
|
507
|
+
pack.write(model._getCreatePrimaryKey());
|
|
433
508
|
}
|
|
434
509
|
|
|
435
|
-
deserialize(
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
get: function() {
|
|
443
|
-
// Special case to return the primary key array instead of load the model, used by serialize.
|
|
444
|
-
if (this === WANT_PK_ARRAY) return pkArray;
|
|
445
|
-
const targetModel = TargetModel._pk!.get(...pkArray); // load by primary key Uint8Array
|
|
446
|
-
if (!targetModel) {
|
|
447
|
-
throw new DatabaseError(`Linked ${TargetModel.tableName} instance ${pkArray.join(', ')} not found`, 'BROKEN_LINK');
|
|
448
|
-
}
|
|
449
|
-
this[prop] = targetModel; // Cause set() to be called, so our property will be come a regular value
|
|
450
|
-
return targetModel;
|
|
451
|
-
},
|
|
452
|
-
set: function(newValue) {
|
|
453
|
-
// Convert back to a regular value property
|
|
454
|
-
Object.defineProperty(this, prop, {
|
|
455
|
-
value: newValue,
|
|
456
|
-
writable: true,
|
|
457
|
-
enumerable: true,
|
|
458
|
-
configurable: true
|
|
459
|
-
});
|
|
460
|
-
},
|
|
461
|
-
enumerable: true,
|
|
462
|
-
configurable: true
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
getErrors(obj: any, prop: string | number): DatabaseError[] {
|
|
467
|
-
if (!(obj[prop] instanceof this.TargetModel)) {
|
|
468
|
-
return [new DatabaseError(`Expected instance of ${this.TargetModel.tableName}, got ${typeof obj[prop]}`, 'VALUE_ERROR')];
|
|
510
|
+
deserialize(pack: DataPack) {
|
|
511
|
+
return this.TargetModel._primary!.getLazy(pack.readUint8Array());
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
getError(value: InstanceType<T>) {
|
|
515
|
+
if (!(value instanceof this.TargetModel)) {
|
|
516
|
+
return new DatabaseError(`Expected instance of ${this.TargetModel.tableName}, got ${typeof value}`, 'VALUE_ERROR');
|
|
469
517
|
}
|
|
470
|
-
return [];
|
|
471
518
|
}
|
|
472
519
|
|
|
473
|
-
serializeType(
|
|
474
|
-
|
|
520
|
+
serializeType(pack: DataPack): void {
|
|
521
|
+
pack.write(this.TargetModel.tableName);
|
|
475
522
|
}
|
|
476
523
|
|
|
477
|
-
static deserializeType(
|
|
478
|
-
const tableName =
|
|
524
|
+
static deserializeType(pack: DataPack, featureFlags: number): LinkType<any> {
|
|
525
|
+
const tableName = pack.readString();
|
|
479
526
|
const targetModel = modelRegistry[tableName];
|
|
480
527
|
if (!targetModel) throw new DatabaseError(`Could not deserialize undefined model ${tableName}`, 'DESERIALIZATION_ERROR');
|
|
481
528
|
return new LinkType(targetModel);
|
|
482
529
|
}
|
|
483
530
|
}
|
|
484
531
|
|
|
485
|
-
/**
|
|
486
|
-
export const string = new StringType()
|
|
532
|
+
/** Type wrapper instance for the string type. */
|
|
533
|
+
export const string = new StringType() as TypeWrapper<string>;
|
|
534
|
+
|
|
535
|
+
/** Type wrapper instance for the ordered string type, which is just like a string
|
|
536
|
+
* except that it sorts lexicographically in the database (instead of by incrementing
|
|
537
|
+
* length first), making it suitable for index fields that want lexicographic range
|
|
538
|
+
* scans. Ordered strings are implemented as null-terminated UTF-8 strings, so they
|
|
539
|
+
* may not contain null characters.
|
|
540
|
+
*/
|
|
541
|
+
export const orderedString = new OrderedStringType() as TypeWrapper<string>;
|
|
542
|
+
|
|
543
|
+
/** Type wrapper instance for the number type. */
|
|
544
|
+
export const number = new NumberType() as TypeWrapper<number>;
|
|
545
|
+
|
|
546
|
+
/** Type wrapper instance for the date/time type. */
|
|
547
|
+
export const dateTime = new DateTimeType() as TypeWrapper<Date>;
|
|
487
548
|
|
|
488
|
-
/**
|
|
489
|
-
export const
|
|
549
|
+
/** Type wrapper instance for the boolean type. */
|
|
550
|
+
export const boolean = new BooleanType() as TypeWrapper<boolean>;
|
|
490
551
|
|
|
491
|
-
/**
|
|
492
|
-
export const
|
|
552
|
+
/** Type wrapper instance for the identifier type. */
|
|
553
|
+
export const identifier = new IdentifierType() as TypeWrapper<string>;
|
|
493
554
|
|
|
494
|
-
/**
|
|
495
|
-
export const
|
|
555
|
+
/** Type wrapper instance for the 'undefined' type. */
|
|
556
|
+
export const undef = new LiteralType(undefined) as TypeWrapper<undefined>;
|
|
496
557
|
|
|
497
558
|
/**
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
export function literal<const T>(value: T) {
|
|
510
|
-
return new LiteralType
|
|
559
|
+
* Create a literal type wrapper for a constant value.
|
|
560
|
+
* @template T - The literal type.
|
|
561
|
+
* @param value - The literal value.
|
|
562
|
+
* @returns A literal type instance.
|
|
563
|
+
*
|
|
564
|
+
* @example
|
|
565
|
+
* ```typescript
|
|
566
|
+
* const statusType = E.literal("active");
|
|
567
|
+
* const countType = E.literal(42);
|
|
568
|
+
* ```
|
|
569
|
+
*/
|
|
570
|
+
export function literal<const T>(value: T): TypeWrapper<T> {
|
|
571
|
+
return new LiteralType(value);
|
|
511
572
|
}
|
|
512
573
|
|
|
513
574
|
/**
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
export function or<const T extends (TypeWrapper<unknown>|BasicType)[]>(...choices: T) {
|
|
526
|
-
return new OrType
|
|
575
|
+
* Create a union type wrapper from multiple type choices.
|
|
576
|
+
* @template T - Array of type wrapper or basic types.
|
|
577
|
+
* @param choices - The type choices for the union.
|
|
578
|
+
* @returns A union type instance.
|
|
579
|
+
*
|
|
580
|
+
* @example
|
|
581
|
+
* ```typescript
|
|
582
|
+
* const stringOrNumber = E.or(E.string, E.number);
|
|
583
|
+
* const status = E.or("active", "inactive", "pending");
|
|
584
|
+
* ```
|
|
585
|
+
*/
|
|
586
|
+
export function or<const T extends (TypeWrapper<unknown>|BasicType)[]>(...choices: T): TypeWrapper<UnwrapTypes<T>> {
|
|
587
|
+
return new OrType(choices.map(wrapIfLiteral));
|
|
527
588
|
}
|
|
528
589
|
|
|
529
|
-
/** Constant representing the 'undefined' type. */
|
|
530
|
-
export const undef = new LiteralType(undefined);
|
|
531
|
-
|
|
532
590
|
/**
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
export function opt<const T extends TypeWrapper<unknown>|BasicType>(inner: T) {
|
|
591
|
+
* Create an optional type wrapper (allows undefined).
|
|
592
|
+
* @template T - Type wrapper or basic type to make optional.
|
|
593
|
+
* @param inner - The inner type to make optional.
|
|
594
|
+
* @returns A union type that accepts the inner type or undefined.
|
|
595
|
+
*
|
|
596
|
+
* @example
|
|
597
|
+
* ```typescript
|
|
598
|
+
* const optionalString = E.opt(E.string);
|
|
599
|
+
* const optionalNumber = E.opt(E.number);
|
|
600
|
+
* ```
|
|
601
|
+
*/
|
|
602
|
+
export function opt<const T extends TypeWrapper<unknown>|BasicType>(inner: T): TypeWrapper<UnwrapTypes<[T, typeof undef]>> {
|
|
545
603
|
return or(undef, inner);
|
|
546
604
|
}
|
|
547
605
|
|
|
548
606
|
/**
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
export function array<const T>(inner: TypeWrapper<T>, opts: {min?: number, max?: number} = {}) {
|
|
562
|
-
return new ArrayType
|
|
607
|
+
* Create an array type wrapper with optional length constraints.
|
|
608
|
+
* @template T - The element type.
|
|
609
|
+
* @param inner - Type wrapper for array elements.
|
|
610
|
+
* @param opts - Optional constraints (min/max length).
|
|
611
|
+
* @returns An array type instance.
|
|
612
|
+
*
|
|
613
|
+
* @example
|
|
614
|
+
* ```typescript
|
|
615
|
+
* const stringArray = E.array(E.string);
|
|
616
|
+
* const boundedArray = E.array(E.number, {min: 1, max: 10});
|
|
617
|
+
* ```
|
|
618
|
+
*/
|
|
619
|
+
export function array<const T>(inner: TypeWrapper<T>, opts: {min?: number, max?: number} = {}): TypeWrapper<T[]> {
|
|
620
|
+
return new ArrayType(wrapIfLiteral(inner), opts);
|
|
563
621
|
}
|
|
564
622
|
|
|
565
623
|
/**
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
624
|
+
* Create a Set type wrapper with optional length constraints.
|
|
625
|
+
* @template T - The element type.
|
|
626
|
+
* @param inner - Type wrapper for set elements.
|
|
627
|
+
* @param opts - Optional constraints (min/max length).
|
|
628
|
+
* @returns A set type instance.
|
|
629
|
+
*
|
|
630
|
+
* @example
|
|
631
|
+
* ```typescript
|
|
632
|
+
* const stringSet = E.set(E.string);
|
|
633
|
+
* const boundedSet = E.set(E.number, {min: 1, max: 10});
|
|
634
|
+
* ```
|
|
635
|
+
*/
|
|
636
|
+
export function set<const T>(inner: TypeWrapper<T>, opts: {min?: number, max?: number} = {}): TypeWrapper<Set<T>> {
|
|
637
|
+
return new SetType(wrapIfLiteral(inner), opts);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Create a link type wrapper for model relationships.
|
|
642
|
+
* @template T - The target model class.
|
|
643
|
+
* @param TargetModel - The model class this link points to.
|
|
644
|
+
* @returns A link type instance.
|
|
645
|
+
*
|
|
646
|
+
* @example
|
|
647
|
+
* ```typescript
|
|
648
|
+
* class User extends E.Model<User> {
|
|
649
|
+
* posts = E.field(E.array(E.link(Post, 'author')));
|
|
650
|
+
* }
|
|
651
|
+
*
|
|
652
|
+
* class Post extends E.Model<Post> {
|
|
653
|
+
* author = E.field(E.link(User));
|
|
654
|
+
* }
|
|
655
|
+
* ```
|
|
656
|
+
*/
|
|
657
|
+
export function link<const T extends typeof Model<any>>(TargetModel: T): TypeWrapper<InstanceType<T>> {
|
|
658
|
+
return new LinkType(TargetModel);
|
|
584
659
|
}
|
|
585
660
|
|
|
586
661
|
|
|
587
662
|
// Utility types and functions
|
|
588
|
-
export type BasicType =
|
|
663
|
+
export type BasicType = string | number | boolean | undefined | null; // TypeWrapper<any>
|
|
589
664
|
|
|
590
|
-
export type UnwrapTypes<T extends BasicType[]> = {
|
|
665
|
+
export type UnwrapTypes<T extends (TypeWrapper<unknown> | BasicType)[]> = {
|
|
591
666
|
[K in keyof T]: T[K] extends TypeWrapper<infer U> ? U : T[K];
|
|
592
667
|
}[number];
|
|
593
668
|
|
|
@@ -598,16 +673,16 @@ function wrapIfLiteral(type: any) {
|
|
|
598
673
|
}
|
|
599
674
|
|
|
600
675
|
/**
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
export function serializeType(arg: TypeWrapper<any>,
|
|
606
|
-
|
|
607
|
-
arg.serializeType(
|
|
676
|
+
* Serialize a type wrapper to a Pack for schema persistence.
|
|
677
|
+
* @param arg - The type wrapper to serialize.
|
|
678
|
+
* @param pack - The Pack instance to write to.
|
|
679
|
+
*/
|
|
680
|
+
export function serializeType(arg: TypeWrapper<any>, pack: DataPack) {
|
|
681
|
+
pack.write(arg.kind);
|
|
682
|
+
arg.serializeType(pack);
|
|
608
683
|
}
|
|
609
684
|
|
|
610
|
-
const TYPE_WRAPPERS: Record<string, TypeWrapper<any> | {deserializeType: (
|
|
685
|
+
const TYPE_WRAPPERS: Record<string, TypeWrapper<any> | {deserializeType: (pack: DataPack, featureFlags: number) => TypeWrapper<any>}> = {
|
|
611
686
|
string: string,
|
|
612
687
|
number: number,
|
|
613
688
|
array: ArrayType,
|
|
@@ -616,19 +691,20 @@ const TYPE_WRAPPERS: Record<string, TypeWrapper<any> | {deserializeType: (bytes:
|
|
|
616
691
|
boolean: boolean,
|
|
617
692
|
id: identifier,
|
|
618
693
|
link: LinkType,
|
|
694
|
+
set: SetType
|
|
619
695
|
};
|
|
620
696
|
|
|
621
697
|
/**
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
export function deserializeType(
|
|
628
|
-
const kind =
|
|
698
|
+
* Deserialize a type wrapper from a Pack.
|
|
699
|
+
* @param pack - The Pack instance to read from.
|
|
700
|
+
* @param featureFlags - Feature flags for version compatibility.
|
|
701
|
+
* @returns The deserialized type wrapper.
|
|
702
|
+
*/
|
|
703
|
+
export function deserializeType(pack: DataPack, featureFlags: number): TypeWrapper<any> {
|
|
704
|
+
const kind = pack.readString();
|
|
629
705
|
const TypeWrapper = TYPE_WRAPPERS[kind];
|
|
630
706
|
if ('deserializeType' in TypeWrapper) {
|
|
631
|
-
return TypeWrapper.deserializeType(
|
|
707
|
+
return TypeWrapper.deserializeType(pack, featureFlags);
|
|
632
708
|
} else {
|
|
633
709
|
return TypeWrapper;
|
|
634
710
|
}
|