@json-eval-rs/webcore 0.0.44 → 0.0.46

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/index.js ADDED
@@ -0,0 +1,487 @@
1
+ /**
2
+ * @json-eval-rs/webcore
3
+ * High-level JavaScript API for JSON Eval RS WASM bindings
4
+ *
5
+ * This package provides a clean, ergonomic API that works with any WASM target:
6
+ */
7
+ /**
8
+ * Get the library version from the WASM module
9
+ * @param wasmModule - WASM module
10
+ * @returns Version string
11
+ */
12
+ export function getVersion(wasmModule) {
13
+ if (wasmModule && typeof wasmModule.getVersion === 'function') {
14
+ return wasmModule.getVersion();
15
+ }
16
+ return 'unknown';
17
+ }
18
+ /**
19
+ * Return format for path-based methods
20
+ */
21
+ export var ReturnFormat;
22
+ (function (ReturnFormat) {
23
+ /** Nested object preserving the path hierarchy (default) */
24
+ ReturnFormat[ReturnFormat["Nested"] = 0] = "Nested";
25
+ /** Flat object with dotted keys */
26
+ ReturnFormat[ReturnFormat["Flat"] = 1] = "Flat";
27
+ /** Array of values in the order of requested paths */
28
+ ReturnFormat[ReturnFormat["Array"] = 2] = "Array";
29
+ })(ReturnFormat || (ReturnFormat = {}));
30
+ /**
31
+ * JSONEval - High-level JavaScript API for JSON Eval RS
32
+ *
33
+ * This is an internal abstraction layer. Use specific packages instead:
34
+ * - @json-eval-rs/bundler (for bundlers like Webpack, Vite, Next.js)
35
+ * - @json-eval-rs/vanilla (for direct browser usage)
36
+ * - @json-eval-rs/node (for Node.js/SSR)
37
+ *
38
+ * @example
39
+ * ```js
40
+ * import { JSONEval } from '@json-eval-rs/webcore';
41
+ *
42
+ * const evaluator = new JSONEval({
43
+ * schema: { type: 'object', properties: { ... } }
44
+ * });
45
+ *
46
+ * await evaluator.init();
47
+ * const result = await evaluator.validate({ data: { name: 'John' } });
48
+ * ```
49
+ */
50
+ export class JSONEvalCore {
51
+ /**
52
+ * @param wasmModule - WASM module (injected by wrapper package)
53
+ * @param options
54
+ */
55
+ constructor(wasmModule, { schema, context, data, fromCache = false }) {
56
+ this._instance = null;
57
+ this._ready = false;
58
+ this._schema = schema;
59
+ this._wasmModule = wasmModule;
60
+ this._context = context;
61
+ this._data = data;
62
+ this._instance = null;
63
+ this._ready = false;
64
+ this._isMsgpackSchema = schema instanceof Uint8Array;
65
+ this._isFromCache = fromCache;
66
+ }
67
+ /**
68
+ * Initialize the WASM instance
69
+ * Call this before using other methods, or use the async methods which call it automatically
70
+ */
71
+ async init() {
72
+ if (this._ready)
73
+ return;
74
+ // If WASM module not provided, throw error - user must provide it or install peer dependency
75
+ if (!this._wasmModule) {
76
+ throw new Error('No WASM module provided. Please either:\n' +
77
+ '1. Pass wasmModule in constructor: new JSONEval({ schema, wasmModule: await import("@json-eval-rs/bundler") })\n' +
78
+ '2. Or install a peer dependency: yarn install @json-eval-rs/bundler (or @json-eval-rs/vanilla or @json-eval-rs/node)');
79
+ }
80
+ try {
81
+ const { JSONEvalWasm } = this._wasmModule;
82
+ // Create instance from cache, MessagePack, or JSON
83
+ if (this._isFromCache) {
84
+ this._instance = JSONEvalWasm.newFromCache(this._schema, // cache key
85
+ this._context ? JSON.stringify(this._context) : null, this._data ? JSON.stringify(this._data) : null);
86
+ }
87
+ else if (this._isMsgpackSchema) {
88
+ this._instance = JSONEvalWasm.newFromMsgpack(this._schema, this._context ? JSON.stringify(this._context) : null, this._data ? JSON.stringify(this._data) : null);
89
+ }
90
+ else {
91
+ this._instance = new JSONEvalWasm(JSON.stringify(this._schema), this._context ? JSON.stringify(this._context) : null, this._data ? JSON.stringify(this._data) : null);
92
+ }
93
+ this._ready = true;
94
+ }
95
+ catch (error) {
96
+ throw new Error(`Failed to create JSONEval instance: ${error.message || error}`);
97
+ }
98
+ }
99
+ /**
100
+ * Create a new JSONEval instance from a cached ParsedSchema
101
+ * Static factory method for convenience
102
+ *
103
+ * @param wasmModule - WASM module
104
+ * @param cacheKey - Cache key to lookup in ParsedSchemaCache
105
+ * @param context - Optional context data
106
+ * @param data - Optional initial data
107
+ * @returns New instance
108
+ */
109
+ static fromCache(wasmModule, cacheKey, context, data) {
110
+ return new JSONEvalCore(wasmModule, {
111
+ schema: cacheKey,
112
+ context,
113
+ data,
114
+ fromCache: true
115
+ });
116
+ }
117
+ /**
118
+ * Validate data against schema (returns parsed JavaScript object)
119
+ * Uses validateJS for Worker-safe serialization
120
+ */
121
+ async validate({ data, context }) {
122
+ await this.init();
123
+ try {
124
+ // Use validateJS for proper serialization (Worker-safe)
125
+ return this._instance.validateJS(JSON.stringify(data), context ? JSON.stringify(context) : null);
126
+ }
127
+ catch (error) {
128
+ throw new Error(`Validation failed: ${error.message || error}`);
129
+ }
130
+ }
131
+ /**
132
+ * Evaluate schema with data (returns parsed JavaScript object)
133
+ */
134
+ async evaluate({ data, context, paths }) {
135
+ await this.init();
136
+ try {
137
+ return this._instance.evaluateJS(JSON.stringify(data), context ? JSON.stringify(context) : null, paths || null);
138
+ }
139
+ catch (error) {
140
+ throw new Error(`Evaluation failed: ${error.message || error}`);
141
+ }
142
+ }
143
+ /**
144
+ * Evaluate dependent fields (returns parsed JavaScript object, processes transitively)
145
+ */
146
+ async evaluateDependents({ changedPaths, data, context, reEvaluate = false }) {
147
+ await this.init();
148
+ try {
149
+ return this._instance.evaluateDependentsJS(JSON.stringify(changedPaths), data ? JSON.stringify(data) : null, context ? JSON.stringify(context) : null, reEvaluate);
150
+ }
151
+ catch (error) {
152
+ throw new Error(`Dependent evaluation failed: ${error.message || error}`);
153
+ }
154
+ }
155
+ /**
156
+ * Get evaluated schema
157
+ */
158
+ async getEvaluatedSchema({ skipLayout = false } = {}) {
159
+ await this.init();
160
+ return this._instance.getEvaluatedSchemaJS(skipLayout);
161
+ }
162
+ /**
163
+ * Get evaluated schema as MessagePack binary data
164
+ */
165
+ async getEvaluatedSchemaMsgpack({ skipLayout = false } = {}) {
166
+ await this.init();
167
+ return this._instance.getEvaluatedSchemaMsgpack(skipLayout);
168
+ }
169
+ /**
170
+ * Get schema values (evaluations ending with .value)
171
+ */
172
+ async getSchemaValue() {
173
+ await this.init();
174
+ return this._instance.getSchemaValue();
175
+ }
176
+ /**
177
+ * Get evaluated schema without $params field
178
+ */
179
+ async getEvaluatedSchemaWithoutParams({ skipLayout = false } = {}) {
180
+ await this.init();
181
+ return this._instance.getEvaluatedSchemaWithoutParamsJS(skipLayout);
182
+ }
183
+ /**
184
+ * Get a value from the evaluated schema using dotted path notation
185
+ */
186
+ async getEvaluatedSchemaByPath({ path, skipLayout = false }) {
187
+ await this.init();
188
+ return this._instance.getEvaluatedSchemaByPathJS(path, skipLayout);
189
+ }
190
+ /**
191
+ * Get values from the evaluated schema using multiple dotted path notations
192
+ * Returns data in the specified format (skips paths that are not found)
193
+ */
194
+ async getEvaluatedSchemaByPaths({ paths, skipLayout = false, format = 0 }) {
195
+ await this.init();
196
+ return this._instance.getEvaluatedSchemaByPathsJS(JSON.stringify(paths), skipLayout, format);
197
+ }
198
+ /**
199
+ * Get a value from the schema using dotted path notation
200
+ */
201
+ async getSchemaByPath({ path }) {
202
+ await this.init();
203
+ return this._instance.getSchemaByPathJS(path);
204
+ }
205
+ /**
206
+ * Get values from the schema using multiple dotted path notations
207
+ * Returns data in the specified format (skips paths that are not found)
208
+ */
209
+ async getSchemaByPaths({ paths, format = 0 }) {
210
+ await this.init();
211
+ return this._instance.getSchemaByPathsJS(JSON.stringify(paths), format);
212
+ }
213
+ /**
214
+ * Reload schema with new data
215
+ */
216
+ async reloadSchema({ schema, context, data }) {
217
+ if (!this._instance) {
218
+ throw new Error('Instance not initialized. Call init() first.');
219
+ }
220
+ try {
221
+ await this._instance.reloadSchema(JSON.stringify(schema), context ? JSON.stringify(context) : null, data ? JSON.stringify(data) : null);
222
+ // Update internal state
223
+ this._schema = schema;
224
+ this._context = context;
225
+ this._data = data;
226
+ }
227
+ catch (error) {
228
+ throw new Error(`Failed to reload schema: ${error.message || error}`);
229
+ }
230
+ }
231
+ /**
232
+ * Reload schema from MessagePack bytes
233
+ */
234
+ async reloadSchemaMsgpack(schemaMsgpack, context, data) {
235
+ if (!this._instance) {
236
+ throw new Error('Instance not initialized. Call init() first.');
237
+ }
238
+ if (!(schemaMsgpack instanceof Uint8Array)) {
239
+ throw new Error('schemaMsgpack must be a Uint8Array');
240
+ }
241
+ try {
242
+ await this._instance.reloadSchemaMsgpack(schemaMsgpack, context ? JSON.stringify(context) : null, data ? JSON.stringify(data) : null);
243
+ // Update internal state
244
+ this._schema = schemaMsgpack;
245
+ this._context = context;
246
+ this._data = data;
247
+ this._isMsgpackSchema = true;
248
+ }
249
+ catch (error) {
250
+ throw new Error(`Failed to reload schema from MessagePack: ${error.message || error}`);
251
+ }
252
+ }
253
+ /**
254
+ * Reload schema from ParsedSchemaCache using a cache key
255
+ */
256
+ async reloadSchemaFromCache(cacheKey, context, data) {
257
+ if (!this._instance) {
258
+ throw new Error('Instance not initialized. Call init() first.');
259
+ }
260
+ if (typeof cacheKey !== 'string' || !cacheKey) {
261
+ throw new Error('cacheKey must be a non-empty string');
262
+ }
263
+ try {
264
+ await this._instance.reloadSchemaFromCache(cacheKey, context ? JSON.stringify(context) : null, data ? JSON.stringify(data) : null);
265
+ // Update internal state
266
+ this._context = context;
267
+ this._data = data;
268
+ // Note: schema is not updated as we don't have access to it from the cache key
269
+ }
270
+ catch (error) {
271
+ throw new Error(`Failed to reload schema from cache: ${error.message || error}`);
272
+ }
273
+ }
274
+ /**
275
+ * Get cache statistics
276
+ */
277
+ async cacheStats() {
278
+ await this.init();
279
+ return this._instance.cacheStats();
280
+ }
281
+ /**
282
+ * Clear the evaluation cache
283
+ */
284
+ async clearCache() {
285
+ await this.init();
286
+ this._instance.clearCache();
287
+ }
288
+ /**
289
+ * Get the number of cached entries
290
+ */
291
+ async cacheLen() {
292
+ await this.init();
293
+ return this._instance.cacheLen();
294
+ }
295
+ /**
296
+ * Enable evaluation caching
297
+ */
298
+ async enableCache() {
299
+ await this.init();
300
+ this._instance.enableCache();
301
+ }
302
+ /**
303
+ * Disable evaluation caching
304
+ */
305
+ async disableCache() {
306
+ await this.init();
307
+ this._instance.disableCache();
308
+ }
309
+ /**
310
+ * Check if evaluation caching is enabled
311
+ */
312
+ isCacheEnabled() {
313
+ if (!this._instance)
314
+ return true; // Default is enabled
315
+ return this._instance.isCacheEnabled();
316
+ }
317
+ /**
318
+ * Resolve layout with optional evaluation
319
+ */
320
+ async resolveLayout(options = {}) {
321
+ const { evaluate = false } = options;
322
+ await this.init();
323
+ return this._instance.resolveLayout(evaluate);
324
+ }
325
+ /**
326
+ * Set timezone offset for datetime operations (TODAY, NOW)
327
+ * @param offsetMinutes - Timezone offset in minutes from UTC
328
+ * (e.g., 420 for UTC+7, -300 for UTC-5)
329
+ * Pass null or undefined to reset to UTC
330
+ */
331
+ setTimezoneOffset(offsetMinutes) {
332
+ if (!this._instance) {
333
+ throw new Error('Instance not initialized. Call init() first.');
334
+ }
335
+ this._instance.setTimezoneOffset(offsetMinutes);
336
+ }
337
+ /**
338
+ * Compile and run JSON logic from a JSON logic string
339
+ */
340
+ async compileAndRunLogic({ logicStr, data, context }) {
341
+ await this.init();
342
+ const logic = typeof logicStr === 'string' ? logicStr : JSON.stringify(logicStr);
343
+ const result = await this._instance.compileAndRunLogic(logic, data ? JSON.stringify(data) : null, context ? JSON.stringify(context) : null);
344
+ // Parse result if it's a string
345
+ return typeof result === 'string' ? JSON.parse(result) : result;
346
+ }
347
+ /**
348
+ * Compile JSON logic and return a global ID
349
+ */
350
+ async compileLogic(logicStr) {
351
+ await this.init();
352
+ const logic = typeof logicStr === 'string' ? logicStr : JSON.stringify(logicStr);
353
+ return this._instance.compileLogic(logic);
354
+ }
355
+ /**
356
+ * Run pre-compiled logic by ID
357
+ */
358
+ async runLogic(logicId, data, context) {
359
+ await this.init();
360
+ const result = await this._instance.runLogic(logicId, data ? JSON.stringify(data) : null, context ? JSON.stringify(context) : null);
361
+ // Parse result if it's a string
362
+ return typeof result === 'string' ? JSON.parse(result) : result;
363
+ }
364
+ /**
365
+ * Validate data against schema rules with optional path filtering
366
+ */
367
+ async validatePaths({ data, context, paths }) {
368
+ await this.init();
369
+ try {
370
+ // Use validatePathsJS for proper serialization (Worker-safe)
371
+ return this._instance.validatePathsJS(JSON.stringify(data), context ? JSON.stringify(context) : null, paths || null);
372
+ }
373
+ catch (error) {
374
+ throw new Error(`Validation failed: ${error.message || error}`);
375
+ }
376
+ }
377
+ // ============================================================================
378
+ // Subform Methods
379
+ // ============================================================================
380
+ /**
381
+ * Evaluate a subform with data
382
+ */
383
+ async evaluateSubform({ subformPath, data, context, paths }) {
384
+ await this.init();
385
+ return this._instance.evaluateSubform(subformPath, JSON.stringify(data), context ? JSON.stringify(context) : null, paths || null);
386
+ }
387
+ /**
388
+ * Validate subform data against its schema rules
389
+ */
390
+ async validateSubform({ subformPath, data, context }) {
391
+ await this.init();
392
+ return this._instance.validateSubform(subformPath, JSON.stringify(data), context ? JSON.stringify(context) : null);
393
+ }
394
+ /**
395
+ * Evaluate dependent fields in subform
396
+ */
397
+ async evaluateDependentsSubform({ subformPath, changedPaths, data, context, reEvaluate = false }) {
398
+ await this.init();
399
+ // For backward compatibility, accept single changedPath too (though types say array)
400
+ const paths = Array.isArray(changedPaths) ? changedPaths : [changedPaths];
401
+ return this._instance.evaluateDependentsSubformJS(subformPath, paths[0], // WASM still expects single path (wraps internally)
402
+ data ? JSON.stringify(data) : null, context ? JSON.stringify(context) : null);
403
+ }
404
+ /**
405
+ * Resolve layout for subform
406
+ */
407
+ async resolveLayoutSubform({ subformPath, evaluate = false }) {
408
+ await this.init();
409
+ return this._instance.resolveLayoutSubform(subformPath, evaluate);
410
+ }
411
+ /**
412
+ * Get evaluated schema from subform
413
+ */
414
+ async getEvaluatedSchemaSubform({ subformPath, resolveLayout = false }) {
415
+ await this.init();
416
+ return this._instance.getEvaluatedSchemaSubformJS(subformPath, resolveLayout);
417
+ }
418
+ /**
419
+ * Get schema value from subform (all .value fields)
420
+ */
421
+ async getSchemaValueSubform({ subformPath }) {
422
+ await this.init();
423
+ return this._instance.getSchemaValueSubform(subformPath);
424
+ }
425
+ /**
426
+ * Get evaluated schema without $params from subform
427
+ */
428
+ async getEvaluatedSchemaWithoutParamsSubform({ subformPath, resolveLayout = false }) {
429
+ await this.init();
430
+ return this._instance.getEvaluatedSchemaWithoutParamsSubformJS(subformPath, resolveLayout);
431
+ }
432
+ /**
433
+ * Get evaluated schema by specific path from subform
434
+ */
435
+ async getEvaluatedSchemaByPathSubform({ subformPath, schemaPath, skipLayout = false }) {
436
+ await this.init();
437
+ return this._instance.getEvaluatedSchemaByPathSubformJS(subformPath, schemaPath, skipLayout);
438
+ }
439
+ /**
440
+ * Get evaluated schema by multiple paths from subform
441
+ * Returns data in the specified format (skips paths that are not found)
442
+ */
443
+ async getEvaluatedSchemaByPathsSubform({ subformPath, schemaPaths, skipLayout = false, format = 0 }) {
444
+ await this.init();
445
+ return this._instance.getEvaluatedSchemaByPathsSubformJS(subformPath, JSON.stringify(schemaPaths), skipLayout, format);
446
+ }
447
+ /**
448
+ * Get list of available subform paths
449
+ */
450
+ async getSubformPaths() {
451
+ await this.init();
452
+ return this._instance.getSubformPaths();
453
+ }
454
+ /**
455
+ * Get schema by specific path from subform
456
+ */
457
+ async getSchemaByPathSubform({ subformPath, schemaPath }) {
458
+ await this.init();
459
+ return this._instance.getSchemaByPathSubformJS(subformPath, schemaPath);
460
+ }
461
+ /**
462
+ * Get schema by multiple paths from subform
463
+ * Returns data in the specified format (skips paths that are not found)
464
+ */
465
+ async getSchemaByPathsSubform({ subformPath, schemaPaths, format = 0 }) {
466
+ await this.init();
467
+ return this._instance.getSchemaByPathsSubformJS(subformPath, JSON.stringify(schemaPaths), format);
468
+ }
469
+ /**
470
+ * Check if a subform exists at the given path
471
+ */
472
+ async hasSubform(subformPath) {
473
+ await this.init();
474
+ return this._instance.hasSubform(subformPath);
475
+ }
476
+ /**
477
+ * Free WASM resources
478
+ */
479
+ free() {
480
+ if (this._instance) {
481
+ this._instance.free();
482
+ this._instance = null;
483
+ this._ready = false;
484
+ }
485
+ }
486
+ }
487
+ export default JSONEvalCore;
package/package.json CHANGED
@@ -1,14 +1,17 @@
1
1
  {
2
2
  "name": "@json-eval-rs/webcore",
3
- "version": "0.0.44",
3
+ "version": "0.0.46",
4
4
  "description": "JSON Eval RS core JavaScript wrapper (internal package - not published)",
5
- "main": "index.js",
6
- "types": "index.d.ts",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
7
  "type": "module",
8
8
  "files": [
9
- "index.js",
10
- "index.d.ts"
9
+ "dist"
11
10
  ],
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "prepack": "npm run build"
14
+ },
12
15
  "keywords": [
13
16
  "json",
14
17
  "json-logic",