jexidb 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Database.cjs +9253 -437
- package/package.json +9 -2
- package/src/Database.mjs +1572 -212
- package/src/FileHandler.mjs +83 -44
- package/src/OperationQueue.mjs +23 -23
- package/src/SchemaManager.mjs +325 -268
- package/src/Serializer.mjs +234 -24
- package/src/managers/IndexManager.mjs +778 -87
- package/src/managers/QueryManager.mjs +340 -67
- package/src/managers/TermManager.mjs +7 -7
- package/src/utils/operatorNormalizer.mjs +116 -0
- package/.babelrc +0 -13
- package/.gitattributes +0 -2
- package/CHANGELOG.md +0 -140
- package/babel.config.json +0 -5
- package/docs/API.md +0 -1051
- package/docs/EXAMPLES.md +0 -701
- package/docs/README.md +0 -194
- package/examples/iterate-usage-example.js +0 -157
- package/examples/simple-iterate-example.js +0 -115
- package/jest.config.js +0 -24
- package/scripts/README.md +0 -47
- package/scripts/clean-test-files.js +0 -75
- package/scripts/prepare.js +0 -31
- package/scripts/run-tests.js +0 -80
- package/test/$not-operator-with-and.test.js +0 -282
- package/test/README.md +0 -8
- package/test/close-init-cycle.test.js +0 -256
- package/test/critical-bugs-fixes.test.js +0 -1069
- package/test/index-persistence.test.js +0 -306
- package/test/index-serialization.test.js +0 -314
- package/test/indexed-query-mode.test.js +0 -360
- package/test/iterate-method.test.js +0 -272
- package/test/query-operators.test.js +0 -238
- package/test/regex-array-fields.test.js +0 -129
- package/test/score-method.test.js +0 -238
- package/test/setup.js +0 -17
- package/test/term-mapping-minimal.test.js +0 -154
- package/test/term-mapping-simple.test.js +0 -257
- package/test/term-mapping.test.js +0 -514
- package/test/writebuffer-flush-resilience.test.js +0 -204
package/src/SchemaManager.mjs
CHANGED
|
@@ -1,268 +1,325 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SchemaManager - Manages field schemas for optimized array-based serialization
|
|
3
|
-
* This replaces the need for repeating field names in JSON objects
|
|
4
|
-
*/
|
|
5
|
-
export default class SchemaManager {
|
|
6
|
-
constructor(opts = {}) {
|
|
7
|
-
this.opts = Object.assign({
|
|
8
|
-
enableArraySerialization: true,
|
|
9
|
-
strictSchema: true,
|
|
10
|
-
debugMode: false
|
|
11
|
-
}, opts)
|
|
12
|
-
|
|
13
|
-
// Schema definition: array of field names in order
|
|
14
|
-
this.schema = []
|
|
15
|
-
this.fieldToIndex = new Map() // field name -> index
|
|
16
|
-
this.indexToField = new Map() // index -> field name
|
|
17
|
-
this.schemaVersion = 1
|
|
18
|
-
this.isInitialized = false
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Initialize schema from options or auto-detect from data
|
|
23
|
-
*/
|
|
24
|
-
initializeSchema(schemaOrData, autoDetect = false) {
|
|
25
|
-
if (this.isInitialized && this.opts.strictSchema) {
|
|
26
|
-
if (this.opts.debugMode) {
|
|
27
|
-
console.log('SchemaManager: Schema already initialized, skipping')
|
|
28
|
-
}
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (Array.isArray(schemaOrData)) {
|
|
33
|
-
// Explicit schema provided
|
|
34
|
-
this.setSchema(schemaOrData)
|
|
35
|
-
} else if (autoDetect && typeof schemaOrData === 'object') {
|
|
36
|
-
// Auto-detect schema from data
|
|
37
|
-
this.autoDetectSchema(schemaOrData)
|
|
38
|
-
} else if (schemaOrData && typeof schemaOrData === 'object') {
|
|
39
|
-
// Initialize from database options
|
|
40
|
-
this.initializeFromOptions(schemaOrData)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
this.isInitialized = true
|
|
44
|
-
if (this.opts.debugMode) {
|
|
45
|
-
console.log('SchemaManager: Schema initialized:', this.schema)
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Set explicit schema
|
|
51
|
-
*/
|
|
52
|
-
setSchema(fieldNames) {
|
|
53
|
-
this.schema = [...fieldNames] // Create copy
|
|
54
|
-
this.fieldToIndex.clear()
|
|
55
|
-
this.indexToField.clear()
|
|
56
|
-
|
|
57
|
-
this.schema.forEach((field, index) => {
|
|
58
|
-
this.fieldToIndex.set(field, index)
|
|
59
|
-
this.indexToField.set(index, field)
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
if (this.opts.debugMode) {
|
|
63
|
-
console.log('SchemaManager: Schema set:', this.schema)
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Auto-detect schema from sample data
|
|
69
|
-
*/
|
|
70
|
-
autoDetectSchema(sampleData) {
|
|
71
|
-
if (Array.isArray(sampleData)) {
|
|
72
|
-
// Use first record as template
|
|
73
|
-
if (sampleData.length > 0) {
|
|
74
|
-
this.autoDetectSchema(sampleData[0])
|
|
75
|
-
}
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (typeof sampleData === 'object' && sampleData !== null) {
|
|
80
|
-
const fields = Object.keys(sampleData).sort() // Sort for consistency
|
|
81
|
-
|
|
82
|
-
// CRITICAL FIX: Always include 'id' field in schema for proper array format
|
|
83
|
-
if (!fields.includes('id')) {
|
|
84
|
-
fields.push('id')
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
this.setSchema(fields)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Initialize schema from database options
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
this.
|
|
113
|
-
|
|
114
|
-
this.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (!
|
|
156
|
-
return arr //
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
1
|
+
/**
|
|
2
|
+
* SchemaManager - Manages field schemas for optimized array-based serialization
|
|
3
|
+
* This replaces the need for repeating field names in JSON objects
|
|
4
|
+
*/
|
|
5
|
+
export default class SchemaManager {
|
|
6
|
+
constructor(opts = {}) {
|
|
7
|
+
this.opts = Object.assign({
|
|
8
|
+
enableArraySerialization: true,
|
|
9
|
+
strictSchema: true,
|
|
10
|
+
debugMode: false
|
|
11
|
+
}, opts)
|
|
12
|
+
|
|
13
|
+
// Schema definition: array of field names in order
|
|
14
|
+
this.schema = []
|
|
15
|
+
this.fieldToIndex = new Map() // field name -> index
|
|
16
|
+
this.indexToField = new Map() // index -> field name
|
|
17
|
+
this.schemaVersion = 1
|
|
18
|
+
this.isInitialized = false
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Initialize schema from options or auto-detect from data
|
|
23
|
+
*/
|
|
24
|
+
initializeSchema(schemaOrData, autoDetect = false) {
|
|
25
|
+
if (this.isInitialized && this.opts.strictSchema) {
|
|
26
|
+
if (this.opts.debugMode) {
|
|
27
|
+
console.log('SchemaManager: Schema already initialized, skipping')
|
|
28
|
+
}
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (Array.isArray(schemaOrData)) {
|
|
33
|
+
// Explicit schema provided
|
|
34
|
+
this.setSchema(schemaOrData)
|
|
35
|
+
} else if (autoDetect && typeof schemaOrData === 'object') {
|
|
36
|
+
// Auto-detect schema from data
|
|
37
|
+
this.autoDetectSchema(schemaOrData)
|
|
38
|
+
} else if (schemaOrData && typeof schemaOrData === 'object') {
|
|
39
|
+
// Initialize from database options
|
|
40
|
+
this.initializeFromOptions(schemaOrData)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
this.isInitialized = true
|
|
44
|
+
if (this.opts.debugMode) {
|
|
45
|
+
console.log('SchemaManager: Schema initialized:', this.schema)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Set explicit schema
|
|
51
|
+
*/
|
|
52
|
+
setSchema(fieldNames) {
|
|
53
|
+
this.schema = [...fieldNames] // Create copy
|
|
54
|
+
this.fieldToIndex.clear()
|
|
55
|
+
this.indexToField.clear()
|
|
56
|
+
|
|
57
|
+
this.schema.forEach((field, index) => {
|
|
58
|
+
this.fieldToIndex.set(field, index)
|
|
59
|
+
this.indexToField.set(index, field)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
if (this.opts.debugMode) {
|
|
63
|
+
console.log('SchemaManager: Schema set:', this.schema)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Auto-detect schema from sample data
|
|
69
|
+
*/
|
|
70
|
+
autoDetectSchema(sampleData) {
|
|
71
|
+
if (Array.isArray(sampleData)) {
|
|
72
|
+
// Use first record as template
|
|
73
|
+
if (sampleData.length > 0) {
|
|
74
|
+
this.autoDetectSchema(sampleData[0])
|
|
75
|
+
}
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (typeof sampleData === 'object' && sampleData !== null) {
|
|
80
|
+
const fields = Object.keys(sampleData).sort() // Sort for consistency
|
|
81
|
+
|
|
82
|
+
// CRITICAL FIX: Always include 'id' field in schema for proper array format
|
|
83
|
+
if (!fields.includes('id')) {
|
|
84
|
+
fields.push('id')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.setSchema(fields)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Initialize schema from database options
|
|
93
|
+
* Note: schema option is no longer supported, use fields instead
|
|
94
|
+
*/
|
|
95
|
+
initializeFromOptions(opts) {
|
|
96
|
+
// Schema option is no longer supported - fields should be used instead
|
|
97
|
+
// This method is kept for compatibility but does nothing
|
|
98
|
+
// Schema initialization is handled by Database.initializeSchema() using fields
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Add new field to schema (for schema evolution)
|
|
103
|
+
*/
|
|
104
|
+
addField(fieldName) {
|
|
105
|
+
if (this.fieldToIndex.has(fieldName)) {
|
|
106
|
+
return this.fieldToIndex.get(fieldName)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const newIndex = this.schema.length
|
|
110
|
+
this.schema.push(fieldName)
|
|
111
|
+
this.fieldToIndex.set(fieldName, newIndex)
|
|
112
|
+
this.indexToField.set(newIndex, fieldName)
|
|
113
|
+
|
|
114
|
+
if (this.opts.debugMode) {
|
|
115
|
+
console.log('SchemaManager: Added field:', fieldName, 'at index:', newIndex)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return newIndex
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Convert object to array using schema with strict field enforcement
|
|
123
|
+
*/
|
|
124
|
+
objectToArray(obj) {
|
|
125
|
+
if (!this.isInitialized || !this.opts.enableArraySerialization) {
|
|
126
|
+
return obj // Fallback to object format
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
|
|
130
|
+
return obj // Don't convert non-objects or arrays
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const result = new Array(this.schema.length)
|
|
134
|
+
|
|
135
|
+
// Fill array with values in schema order
|
|
136
|
+
// Missing fields become undefined, extra fields are ignored
|
|
137
|
+
for (let i = 0; i < this.schema.length; i++) {
|
|
138
|
+
const fieldName = this.schema[i]
|
|
139
|
+
result[i] = obj[fieldName] !== undefined ? obj[fieldName] : undefined
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// CRITICAL FIX: Always append 'id' field if it exists and is not in schema
|
|
143
|
+
// The 'id' field must be preserved even if not in the schema
|
|
144
|
+
if (obj.id !== undefined && obj.id !== null && this.schema.indexOf('id') === -1) {
|
|
145
|
+
result.push(obj.id)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return result
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Convert array back to object using schema
|
|
153
|
+
*/
|
|
154
|
+
arrayToObject(arr) {
|
|
155
|
+
if (!this.isInitialized || !this.opts.enableArraySerialization) {
|
|
156
|
+
return arr // Fallback to array format
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!Array.isArray(arr)) {
|
|
160
|
+
return arr // Don't convert non-arrays
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const obj = {}
|
|
164
|
+
const idIndex = this.schema.indexOf('id')
|
|
165
|
+
|
|
166
|
+
// CRITICAL FIX: Handle schema migration where 'id' was first field in old schema
|
|
167
|
+
// but is not in current schema. Check if first element looks like an ID.
|
|
168
|
+
// Only do this if:
|
|
169
|
+
// 1. 'id' is not in current schema
|
|
170
|
+
// 2. Array has significantly more elements than current schema (2+ extra elements)
|
|
171
|
+
// This suggests the old schema had more fields, and 'id' was likely the first
|
|
172
|
+
// 3. First element is a very short string (max 20 chars) that looks like a generated ID
|
|
173
|
+
// (typically alphanumeric, often starting with letters like 'mit...' or similar patterns)
|
|
174
|
+
// 4. First field in current schema is not 'id' (to avoid false positives)
|
|
175
|
+
// 5. First element is not an array (to avoid false positives with array fields)
|
|
176
|
+
let arrayOffset = 0
|
|
177
|
+
if (idIndex === -1 && arr.length >= this.schema.length + 2 && this.schema.length > 0) {
|
|
178
|
+
// Only apply if array has at least 2 extra elements (suggests old schema had more fields)
|
|
179
|
+
const firstElement = arr[0]
|
|
180
|
+
const firstFieldName = this.schema[0]
|
|
181
|
+
|
|
182
|
+
// Only apply shift if:
|
|
183
|
+
// - First field is not 'id'
|
|
184
|
+
// - First element is a very short string (max 20 chars) that looks like a generated ID
|
|
185
|
+
// - First element is not an array (to avoid false positives)
|
|
186
|
+
// - Array has at least 2 extra elements (strong indicator of schema migration)
|
|
187
|
+
if (firstFieldName !== 'id' &&
|
|
188
|
+
typeof firstElement === 'string' &&
|
|
189
|
+
!Array.isArray(firstElement) &&
|
|
190
|
+
firstElement.length > 0 &&
|
|
191
|
+
firstElement.length <= 20 && // Very conservative: max 20 chars (typical ID length)
|
|
192
|
+
/^[a-zA-Z0-9_-]+$/.test(firstElement)) {
|
|
193
|
+
// First element is likely the ID from old schema
|
|
194
|
+
obj.id = firstElement
|
|
195
|
+
arrayOffset = 1
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Map array values to object properties
|
|
200
|
+
// Only include fields that are in the schema
|
|
201
|
+
for (let i = 0; i < Math.min(arr.length - arrayOffset, this.schema.length); i++) {
|
|
202
|
+
const fieldName = this.schema[i]
|
|
203
|
+
const arrayIndex = i + arrayOffset
|
|
204
|
+
// Only include non-undefined values to avoid cluttering the object
|
|
205
|
+
if (arr[arrayIndex] !== undefined) {
|
|
206
|
+
obj[fieldName] = arr[arrayIndex]
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// CRITICAL FIX: Always preserve 'id' field if it exists in the original object
|
|
211
|
+
// The 'id' field may not be in the schema but must be preserved
|
|
212
|
+
if (idIndex !== -1 && arr[idIndex] !== undefined) {
|
|
213
|
+
// 'id' is in schema and has a value
|
|
214
|
+
obj.id = arr[idIndex]
|
|
215
|
+
} else if (!obj.id && arr.length > this.schema.length + arrayOffset) {
|
|
216
|
+
// 'id' is not in schema but array has extra element(s) - check if last element could be ID
|
|
217
|
+
// This handles cases where ID was added after schema initialization
|
|
218
|
+
for (let i = this.schema.length + arrayOffset; i < arr.length; i++) {
|
|
219
|
+
// Try to infer if this is an ID (string that looks like an ID)
|
|
220
|
+
const potentialId = arr[i]
|
|
221
|
+
if (potentialId !== undefined && potentialId !== null && typeof potentialId === 'string' && potentialId.length > 0 && potentialId.length < 100) {
|
|
222
|
+
obj.id = potentialId
|
|
223
|
+
break // Use first potential ID found
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return obj
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Get field index by name
|
|
233
|
+
*/
|
|
234
|
+
getFieldIndex(fieldName) {
|
|
235
|
+
return this.fieldToIndex.get(fieldName)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Get field name by index
|
|
240
|
+
*/
|
|
241
|
+
getFieldName(index) {
|
|
242
|
+
return this.indexToField.get(index)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Check if field exists in schema
|
|
247
|
+
*/
|
|
248
|
+
hasField(fieldName) {
|
|
249
|
+
return this.fieldToIndex.has(fieldName)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Get schema as array of field names
|
|
254
|
+
*/
|
|
255
|
+
getSchema() {
|
|
256
|
+
return [...this.schema] // Return copy
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Get schema size
|
|
261
|
+
*/
|
|
262
|
+
getSchemaSize() {
|
|
263
|
+
return this.schema.length
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Validate that object conforms to schema
|
|
268
|
+
*/
|
|
269
|
+
validateObject(obj) {
|
|
270
|
+
if (!this.isInitialized || !this.opts.strictSchema) {
|
|
271
|
+
return true
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
|
|
275
|
+
return false
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Check if object has all required fields
|
|
279
|
+
for (const field of this.schema) {
|
|
280
|
+
if (!(field in obj)) {
|
|
281
|
+
if (this.opts.debugMode) {
|
|
282
|
+
console.warn('SchemaManager: Missing required field:', field)
|
|
283
|
+
}
|
|
284
|
+
return false
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return true
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Get schema metadata for serialization
|
|
293
|
+
*/
|
|
294
|
+
getSchemaMetadata() {
|
|
295
|
+
return {
|
|
296
|
+
version: this.schemaVersion,
|
|
297
|
+
fields: [...this.schema],
|
|
298
|
+
fieldCount: this.schema.length,
|
|
299
|
+
isInitialized: this.isInitialized
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Reset schema
|
|
305
|
+
*/
|
|
306
|
+
reset() {
|
|
307
|
+
this.schema = []
|
|
308
|
+
this.fieldToIndex.clear()
|
|
309
|
+
this.indexToField.clear()
|
|
310
|
+
this.isInitialized = false
|
|
311
|
+
this.schemaVersion++
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Get performance statistics
|
|
316
|
+
*/
|
|
317
|
+
getStats() {
|
|
318
|
+
return {
|
|
319
|
+
schemaSize: this.schema.length,
|
|
320
|
+
isInitialized: this.isInitialized,
|
|
321
|
+
version: this.schemaVersion,
|
|
322
|
+
enableArraySerialization: this.opts.enableArraySerialization
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|