holosphere 2.0.0-alpha2 → 2.0.0-alpha5
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/2019-D2OG2idw.js +6680 -0
- package/dist/2019-D2OG2idw.js.map +1 -0
- package/dist/2019-EION3wKo.cjs +8 -0
- package/dist/2019-EION3wKo.cjs.map +1 -0
- package/dist/_commonjsHelpers-C37NGDzP.cjs +2 -0
- package/dist/_commonjsHelpers-C37NGDzP.cjs.map +1 -0
- package/dist/_commonjsHelpers-CUmg6egw.js +7 -0
- package/dist/_commonjsHelpers-CUmg6egw.js.map +1 -0
- package/dist/browser-BSniCNqO.js +3058 -0
- package/dist/browser-BSniCNqO.js.map +1 -0
- package/dist/browser-Cq59Ij19.cjs +2 -0
- package/dist/browser-Cq59Ij19.cjs.map +1 -0
- package/dist/cjs/holosphere.cjs +1 -1
- package/dist/esm/holosphere.js +50 -53
- package/dist/index-BG8FStkt.cjs +12 -0
- package/dist/index-BG8FStkt.cjs.map +1 -0
- package/dist/index-Bbey4GkP.js +37869 -0
- package/dist/index-Bbey4GkP.js.map +1 -0
- package/dist/index-Cp3xctq8.js +15104 -0
- package/dist/index-Cp3xctq8.js.map +1 -0
- package/dist/index-hfVGRwSr.cjs +5 -0
- package/dist/index-hfVGRwSr.cjs.map +1 -0
- package/dist/indexeddb-storage-BD70pN7q.cjs +2 -0
- package/dist/indexeddb-storage-BD70pN7q.cjs.map +1 -0
- package/dist/{indexeddb-storage-CMW4qRQS.js → indexeddb-storage-Bjg84U5R.js} +49 -13
- package/dist/indexeddb-storage-Bjg84U5R.js.map +1 -0
- package/dist/{memory-storage-DQzcAZlf.js → memory-storage-CD0XFayE.js} +6 -2
- package/dist/memory-storage-CD0XFayE.js.map +1 -0
- package/dist/{memory-storage-DmePEP2q.cjs → memory-storage-DmMyJtOo.cjs} +2 -2
- package/dist/memory-storage-DmMyJtOo.cjs.map +1 -0
- package/dist/{secp256k1-vOXp40Fx.js → secp256k1-69sS9O-P.js} +2 -393
- package/dist/secp256k1-69sS9O-P.js.map +1 -0
- package/dist/secp256k1-TcN6vWGh.cjs +12 -0
- package/dist/secp256k1-TcN6vWGh.cjs.map +1 -0
- package/docs/CONTRACTS.md +797 -0
- package/examples/demo.html +47 -0
- package/package.json +10 -5
- package/src/contracts/abis/Appreciative.json +1280 -0
- package/src/contracts/abis/AppreciativeFactory.json +101 -0
- package/src/contracts/abis/Bundle.json +1435 -0
- package/src/contracts/abis/BundleFactory.json +106 -0
- package/src/contracts/abis/Holon.json +881 -0
- package/src/contracts/abis/Holons.json +330 -0
- package/src/contracts/abis/Managed.json +1262 -0
- package/src/contracts/abis/ManagedFactory.json +149 -0
- package/src/contracts/abis/Membrane.json +261 -0
- package/src/contracts/abis/Splitter.json +1624 -0
- package/src/contracts/abis/SplitterFactory.json +220 -0
- package/src/contracts/abis/TestToken.json +321 -0
- package/src/contracts/abis/Zoned.json +1461 -0
- package/src/contracts/abis/ZonedFactory.json +154 -0
- package/src/contracts/chain-manager.js +375 -0
- package/src/contracts/deployer.js +443 -0
- package/src/contracts/event-listener.js +507 -0
- package/src/contracts/holon-contracts.js +344 -0
- package/src/contracts/index.js +83 -0
- package/src/contracts/networks.js +224 -0
- package/src/contracts/operations.js +670 -0
- package/src/contracts/queries.js +589 -0
- package/src/core/holosphere.js +453 -1
- package/src/crypto/nostr-utils.js +263 -0
- package/src/federation/handshake.js +455 -0
- package/src/federation/hologram.js +1 -1
- package/src/hierarchical/upcast.js +6 -5
- package/src/index.js +463 -1939
- package/src/lib/ai-methods.js +308 -0
- package/src/lib/contract-methods.js +293 -0
- package/src/lib/errors.js +23 -0
- package/src/lib/federation-methods.js +238 -0
- package/src/lib/index.js +26 -0
- package/src/spatial/h3-operations.js +2 -2
- package/src/storage/backends/gundb-backend.js +377 -46
- package/src/storage/global-tables.js +28 -1
- package/src/storage/gun-auth.js +303 -0
- package/src/storage/gun-federation.js +776 -0
- package/src/storage/gun-references.js +198 -0
- package/src/storage/gun-schema.js +291 -0
- package/src/storage/gun-wrapper.js +347 -31
- package/src/storage/indexeddb-storage.js +49 -11
- package/src/storage/memory-storage.js +5 -0
- package/src/storage/nostr-async.js +194 -37
- package/src/storage/nostr-client.js +580 -51
- package/src/storage/persistent-storage.js +6 -1
- package/src/storage/unified-storage.js +119 -0
- package/src/subscriptions/manager.js +1 -1
- package/types/index.d.ts +133 -0
- package/dist/index-CDfIuXew.js +0 -15974
- package/dist/index-CDfIuXew.js.map +0 -1
- package/dist/index-ifOgtDvd.cjs +0 -3
- package/dist/index-ifOgtDvd.cjs.map +0 -1
- package/dist/indexeddb-storage-CMW4qRQS.js.map +0 -1
- package/dist/indexeddb-storage-DLZOgetM.cjs +0 -2
- package/dist/indexeddb-storage-DLZOgetM.cjs.map +0 -1
- package/dist/memory-storage-DQzcAZlf.js.map +0 -1
- package/dist/memory-storage-DmePEP2q.cjs.map +0 -1
- package/dist/secp256k1-CP0ZkpAx.cjs +0 -13
- package/dist/secp256k1-CP0ZkpAx.cjs.map +0 -1
- package/dist/secp256k1-vOXp40Fx.js.map +0 -1
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GunDB Reference Handler
|
|
3
|
+
* Handles creation and resolution of data references for federation
|
|
4
|
+
*
|
|
5
|
+
* References use a "soul" pattern: appname/holon/lens/key
|
|
6
|
+
* This allows lightweight propagation without data duplication
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { read } from './gun-wrapper.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Reference handler for GunDB backend
|
|
13
|
+
*/
|
|
14
|
+
export class GunReferenceHandler {
|
|
15
|
+
/**
|
|
16
|
+
* Create a new reference handler
|
|
17
|
+
* @param {string} appname - Application namespace
|
|
18
|
+
*/
|
|
19
|
+
constructor(appname) {
|
|
20
|
+
this.appname = appname;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Create a reference to data
|
|
25
|
+
* @param {string} holon - Holon ID
|
|
26
|
+
* @param {string} lens - Lens name
|
|
27
|
+
* @param {Object} data - Data object (must have 'id' field)
|
|
28
|
+
* @returns {Object} Reference object with id and soul
|
|
29
|
+
*/
|
|
30
|
+
createReference(holon, lens, data) {
|
|
31
|
+
if (!data || !data.id) {
|
|
32
|
+
throw new Error('createReference: data must have an id field');
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
id: data.id,
|
|
36
|
+
soul: `${this.appname}/${holon}/${lens}/${data.id}`
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Check if data is a reference
|
|
42
|
+
* @param {Object} data - Data to check
|
|
43
|
+
* @returns {boolean} True if data is a reference
|
|
44
|
+
*/
|
|
45
|
+
isReference(data) {
|
|
46
|
+
if (!data) return false;
|
|
47
|
+
|
|
48
|
+
// Simple reference format: { id, soul }
|
|
49
|
+
if (data.soul && data.id && Object.keys(data).filter(k => !k.startsWith('_')).length <= 2) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Legacy reference format: { _federation: { isReference: true } }
|
|
54
|
+
if (data._federation && data._federation.isReference) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Gun internal reference format: { _: { "#": "path/to/data" } }
|
|
59
|
+
if (data._ && data._['#']) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Parse a soul path into its components
|
|
68
|
+
* @param {string} soul - Soul path (appname/holon/lens/key)
|
|
69
|
+
* @returns {Object|null} Parsed components or null if invalid
|
|
70
|
+
*/
|
|
71
|
+
parseSoulPath(soul) {
|
|
72
|
+
if (!soul || typeof soul !== 'string') {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const parts = soul.split('/');
|
|
77
|
+
if (parts.length < 4) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
appname: parts[0],
|
|
83
|
+
holon: parts[1],
|
|
84
|
+
lens: parts[2],
|
|
85
|
+
key: parts.slice(3).join('/') // Handle keys with slashes
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Resolve a reference to get the actual data
|
|
91
|
+
* @param {Object} gun - Gun instance
|
|
92
|
+
* @param {Object} reference - Reference object
|
|
93
|
+
* @param {Object} options - Resolution options
|
|
94
|
+
* @param {boolean} options.followReferences - Whether to recursively resolve nested references (default: true)
|
|
95
|
+
* @returns {Promise<Object|null>} Resolved data or null if not found
|
|
96
|
+
*/
|
|
97
|
+
async resolveReference(gun, reference, options = {}) {
|
|
98
|
+
const { followReferences = true } = options;
|
|
99
|
+
|
|
100
|
+
if (!reference) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let soul = null;
|
|
105
|
+
let originInfo = null;
|
|
106
|
+
|
|
107
|
+
// Handle simple reference format { id, soul }
|
|
108
|
+
if (reference.soul) {
|
|
109
|
+
soul = reference.soul;
|
|
110
|
+
}
|
|
111
|
+
// Handle legacy reference format { _federation: { isReference: true, origin, lens } }
|
|
112
|
+
else if (reference._federation && reference._federation.isReference) {
|
|
113
|
+
const fed = reference._federation;
|
|
114
|
+
if (fed.origin && fed.lens && reference.id) {
|
|
115
|
+
soul = `${this.appname}/${fed.origin}/${fed.lens}/${reference.id}`;
|
|
116
|
+
originInfo = fed;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Handle Gun internal reference format { _: { "#": "path/to/data" } }
|
|
120
|
+
else if (reference._ && reference._['#']) {
|
|
121
|
+
soul = reference._['#'];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!soul) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Parse soul to get path components
|
|
129
|
+
const parsed = this.parseSoulPath(soul);
|
|
130
|
+
if (!parsed) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
// Build path and read data
|
|
136
|
+
const path = `${parsed.appname}/${parsed.holon}/${parsed.lens}/${parsed.key}`;
|
|
137
|
+
const data = await read(gun, path);
|
|
138
|
+
|
|
139
|
+
if (!data) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Check if resolved data is also a reference and we should follow
|
|
144
|
+
if (followReferences && this.isReference(data)) {
|
|
145
|
+
const nested = await this.resolveReference(gun, data, { followReferences: true });
|
|
146
|
+
if (nested) {
|
|
147
|
+
return {
|
|
148
|
+
...nested,
|
|
149
|
+
_federation: {
|
|
150
|
+
resolved: true,
|
|
151
|
+
soul: soul,
|
|
152
|
+
origin: parsed.holon,
|
|
153
|
+
lens: parsed.lens,
|
|
154
|
+
timestamp: Date.now()
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Add federation metadata to resolved data
|
|
161
|
+
return {
|
|
162
|
+
...data,
|
|
163
|
+
_federation: {
|
|
164
|
+
resolved: true,
|
|
165
|
+
soul: soul,
|
|
166
|
+
origin: originInfo?.origin || parsed.holon,
|
|
167
|
+
lens: originInfo?.lens || parsed.lens,
|
|
168
|
+
timestamp: Date.now()
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.warn('Error resolving reference:', error);
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get the soul path for data
|
|
179
|
+
* @param {string} holon - Holon ID
|
|
180
|
+
* @param {string} lens - Lens name
|
|
181
|
+
* @param {string} key - Data key
|
|
182
|
+
* @returns {string} Soul path
|
|
183
|
+
*/
|
|
184
|
+
getSoul(holon, lens, key) {
|
|
185
|
+
return `${this.appname}/${holon}/${lens}/${key}`;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Create a reference handler instance
|
|
191
|
+
* @param {string} appname - Application namespace
|
|
192
|
+
* @returns {GunReferenceHandler} Reference handler instance
|
|
193
|
+
*/
|
|
194
|
+
export function createReferenceHandler(appname) {
|
|
195
|
+
return new GunReferenceHandler(appname);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export default GunReferenceHandler;
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GunDB Schema Validator
|
|
3
|
+
* Handles JSON Schema validation for data stored in GunDB
|
|
4
|
+
*
|
|
5
|
+
* Schemas are stored in the global 'schemas' table
|
|
6
|
+
* Validation uses AJV (Another JSON Schema Validator) with JSON Schema 2019
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { writeGlobal, readGlobal, readAllGlobal, deleteGlobal } from './gun-wrapper.js';
|
|
10
|
+
|
|
11
|
+
// Default meta-schema for validating schema definitions
|
|
12
|
+
const META_SCHEMA = {
|
|
13
|
+
type: 'object',
|
|
14
|
+
required: ['type'],
|
|
15
|
+
properties: {
|
|
16
|
+
$schema: { type: 'string' },
|
|
17
|
+
$id: { type: 'string' },
|
|
18
|
+
type: { type: 'string' },
|
|
19
|
+
properties: { type: 'object' },
|
|
20
|
+
required: { type: 'array', items: { type: 'string' } },
|
|
21
|
+
additionalProperties: { type: 'boolean' }
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Schema validator for GunDB backend
|
|
27
|
+
*/
|
|
28
|
+
export class GunSchemaValidator {
|
|
29
|
+
/**
|
|
30
|
+
* Create a new schema validator
|
|
31
|
+
* @param {Object} options - Validator options
|
|
32
|
+
* @param {boolean} options.strict - Whether to enforce strict validation (default: false)
|
|
33
|
+
* @param {number} options.cacheMaxAge - Schema cache TTL in ms (default: 3600000 = 1 hour)
|
|
34
|
+
*/
|
|
35
|
+
constructor(options = {}) {
|
|
36
|
+
this.strict = options.strict || false;
|
|
37
|
+
this.cacheMaxAge = options.cacheMaxAge || 3600000; // 1 hour
|
|
38
|
+
this.schemaCache = new Map();
|
|
39
|
+
this.validator = null;
|
|
40
|
+
this.Ajv = null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Initialize the validator (lazy load AJV)
|
|
45
|
+
* @returns {Promise<boolean>} True if initialized successfully
|
|
46
|
+
*/
|
|
47
|
+
async init() {
|
|
48
|
+
if (this.validator) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
// Dynamically import AJV to avoid requiring it if not needed
|
|
54
|
+
const AjvModule = await import('ajv/dist/2019.js');
|
|
55
|
+
this.Ajv = AjvModule.default || AjvModule;
|
|
56
|
+
|
|
57
|
+
this.validator = new this.Ajv({
|
|
58
|
+
allErrors: true,
|
|
59
|
+
strict: false,
|
|
60
|
+
validateSchema: true
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return true;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.warn('AJV not available, schema validation disabled:', error.message);
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Set a schema for a lens
|
|
72
|
+
* @param {Object} gun - Gun instance
|
|
73
|
+
* @param {string} appname - Application namespace
|
|
74
|
+
* @param {string} lens - Lens name
|
|
75
|
+
* @param {Object} schema - JSON Schema definition
|
|
76
|
+
* @returns {Promise<boolean>} Success indicator
|
|
77
|
+
*/
|
|
78
|
+
async setSchema(gun, appname, lens, schema) {
|
|
79
|
+
if (!schema || typeof schema !== 'object') {
|
|
80
|
+
throw new Error('setSchema: schema must be an object');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Validate the schema against meta-schema
|
|
84
|
+
if (this.strict && this.validator) {
|
|
85
|
+
const isValid = this.validateSchema(META_SCHEMA, schema);
|
|
86
|
+
if (!isValid) {
|
|
87
|
+
throw new Error('setSchema: Invalid schema definition');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Store schema in global 'schemas' table
|
|
92
|
+
const schemaRecord = {
|
|
93
|
+
id: lens,
|
|
94
|
+
lens: lens,
|
|
95
|
+
schema: schema,
|
|
96
|
+
timestamp: Date.now()
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
await writeGlobal(gun, appname, 'schemas', schemaRecord);
|
|
100
|
+
|
|
101
|
+
// Update cache
|
|
102
|
+
this.schemaCache.set(lens, {
|
|
103
|
+
schema: schema,
|
|
104
|
+
timestamp: Date.now()
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get a schema for a lens
|
|
112
|
+
* @param {Object} gun - Gun instance
|
|
113
|
+
* @param {string} appname - Application namespace
|
|
114
|
+
* @param {string} lens - Lens name
|
|
115
|
+
* @param {Object} options - Options
|
|
116
|
+
* @param {boolean} options.useCache - Whether to use cache (default: true)
|
|
117
|
+
* @returns {Promise<Object|null>} Schema or null if not found
|
|
118
|
+
*/
|
|
119
|
+
async getSchema(gun, appname, lens, options = {}) {
|
|
120
|
+
const { useCache = true } = options;
|
|
121
|
+
|
|
122
|
+
// Check cache first
|
|
123
|
+
if (useCache) {
|
|
124
|
+
const cached = this.schemaCache.get(lens);
|
|
125
|
+
if (cached && (Date.now() - cached.timestamp) < this.cacheMaxAge) {
|
|
126
|
+
return cached.schema;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Fetch from storage
|
|
131
|
+
const schemaRecord = await readGlobal(gun, appname, 'schemas', lens);
|
|
132
|
+
|
|
133
|
+
if (schemaRecord && schemaRecord.schema) {
|
|
134
|
+
// Update cache
|
|
135
|
+
this.schemaCache.set(lens, {
|
|
136
|
+
schema: schemaRecord.schema,
|
|
137
|
+
timestamp: Date.now()
|
|
138
|
+
});
|
|
139
|
+
return schemaRecord.schema;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get all schemas
|
|
147
|
+
* @param {Object} gun - Gun instance
|
|
148
|
+
* @param {string} appname - Application namespace
|
|
149
|
+
* @returns {Promise<Object[]>} Array of schema records
|
|
150
|
+
*/
|
|
151
|
+
async getAllSchemas(gun, appname) {
|
|
152
|
+
return readAllGlobal(gun, appname, 'schemas');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Delete a schema
|
|
157
|
+
* @param {Object} gun - Gun instance
|
|
158
|
+
* @param {string} appname - Application namespace
|
|
159
|
+
* @param {string} lens - Lens name
|
|
160
|
+
* @returns {Promise<boolean>} Success indicator
|
|
161
|
+
*/
|
|
162
|
+
async deleteSchema(gun, appname, lens) {
|
|
163
|
+
// Remove from cache
|
|
164
|
+
this.schemaCache.delete(lens);
|
|
165
|
+
|
|
166
|
+
// Delete from storage
|
|
167
|
+
return deleteGlobal(gun, appname, 'schemas', lens);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Validate data against a schema
|
|
172
|
+
* @param {Object} schema - JSON Schema definition
|
|
173
|
+
* @param {Object} data - Data to validate
|
|
174
|
+
* @returns {Object} Validation result { valid: boolean, errors: Array }
|
|
175
|
+
*/
|
|
176
|
+
validate(schema, data) {
|
|
177
|
+
if (!this.validator) {
|
|
178
|
+
// If validator not available, skip validation
|
|
179
|
+
return { valid: true, errors: [] };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const validate = this.validator.compile(schema);
|
|
184
|
+
const valid = validate(data);
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
valid: valid,
|
|
188
|
+
errors: valid ? [] : this.formatErrors(validate.errors)
|
|
189
|
+
};
|
|
190
|
+
} catch (error) {
|
|
191
|
+
return {
|
|
192
|
+
valid: false,
|
|
193
|
+
errors: [{ message: error.message, path: '' }]
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Validate data against a lens schema (fetch schema from storage)
|
|
200
|
+
* @param {Object} gun - Gun instance
|
|
201
|
+
* @param {string} appname - Application namespace
|
|
202
|
+
* @param {string} lens - Lens name
|
|
203
|
+
* @param {Object} data - Data to validate
|
|
204
|
+
* @returns {Promise<Object>} Validation result { valid: boolean, errors: Array }
|
|
205
|
+
*/
|
|
206
|
+
async validateData(gun, appname, lens, data) {
|
|
207
|
+
const schema = await this.getSchema(gun, appname, lens);
|
|
208
|
+
|
|
209
|
+
if (!schema) {
|
|
210
|
+
// No schema defined, validation passes
|
|
211
|
+
return { valid: true, errors: [] };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return this.validate(schema, data);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Validate a schema definition against meta-schema
|
|
219
|
+
* @param {Object} metaSchema - Meta schema to validate against
|
|
220
|
+
* @param {Object} schema - Schema to validate
|
|
221
|
+
* @returns {boolean} True if valid
|
|
222
|
+
*/
|
|
223
|
+
validateSchema(metaSchema, schema) {
|
|
224
|
+
if (!this.validator) {
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
const validate = this.validator.compile(metaSchema);
|
|
230
|
+
return validate(schema);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Format validation errors into a consistent structure
|
|
238
|
+
* @private
|
|
239
|
+
* @param {Array} errors - AJV error objects
|
|
240
|
+
* @returns {Array} Formatted errors
|
|
241
|
+
*/
|
|
242
|
+
formatErrors(errors) {
|
|
243
|
+
if (!errors) return [];
|
|
244
|
+
|
|
245
|
+
return errors.map(err => ({
|
|
246
|
+
message: err.message || 'Validation error',
|
|
247
|
+
path: err.instancePath || err.dataPath || '',
|
|
248
|
+
keyword: err.keyword,
|
|
249
|
+
params: err.params
|
|
250
|
+
}));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Clear the schema cache
|
|
255
|
+
* @param {string} lens - Specific lens to clear, or null for all
|
|
256
|
+
*/
|
|
257
|
+
clearCache(lens = null) {
|
|
258
|
+
if (lens) {
|
|
259
|
+
this.schemaCache.delete(lens);
|
|
260
|
+
} else {
|
|
261
|
+
this.schemaCache.clear();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Check if strict mode is enabled
|
|
267
|
+
* @returns {boolean} True if strict mode enabled
|
|
268
|
+
*/
|
|
269
|
+
isStrict() {
|
|
270
|
+
return this.strict;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Enable or disable strict mode
|
|
275
|
+
* @param {boolean} strict - Whether to enable strict mode
|
|
276
|
+
*/
|
|
277
|
+
setStrict(strict) {
|
|
278
|
+
this.strict = strict;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Create a schema validator instance
|
|
284
|
+
* @param {Object} options - Validator options
|
|
285
|
+
* @returns {GunSchemaValidator} Validator instance
|
|
286
|
+
*/
|
|
287
|
+
export function createSchemaValidator(options = {}) {
|
|
288
|
+
return new GunSchemaValidator(options);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export default GunSchemaValidator;
|