@unyt/datex 0.0.9 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/esm/datex-core/datex_core_js.internal.d.ts +248 -264
  2. package/esm/datex-core/datex_core_js.internal.d.ts.map +1 -1
  3. package/esm/datex-core/datex_core_js.internal.js +584 -547
  4. package/esm/datex-core/datex_core_js.js +7 -3
  5. package/esm/datex-core/datex_core_js.wasm +0 -0
  6. package/esm/datex-core/wasm_url.node.js +1 -1
  7. package/esm/default.d.ts +8 -0
  8. package/esm/default.d.ts.map +1 -1
  9. package/esm/default.js +8 -0
  10. package/esm/deno.json +5 -3
  11. package/esm/dif/core.d.ts +5 -3
  12. package/esm/dif/core.d.ts.map +1 -1
  13. package/esm/dif/core.js +7 -5
  14. package/esm/dif/definitions.d.ts +45 -34
  15. package/esm/dif/definitions.d.ts.map +1 -1
  16. package/esm/dif/definitions.js +14 -9
  17. package/esm/dif/dif-handler.d.ts +125 -34
  18. package/esm/dif/dif-handler.d.ts.map +1 -1
  19. package/esm/dif/dif-handler.js +366 -159
  20. package/esm/dif/display.d.ts +3 -3
  21. package/esm/dif/display.d.ts.map +1 -1
  22. package/esm/dif/display.js +5 -5
  23. package/esm/dif/js-lib.d.ts +8 -0
  24. package/esm/dif/js-lib.d.ts.map +1 -0
  25. package/esm/dif/js-lib.js +3 -0
  26. package/esm/dif/mod.d.ts +5 -1
  27. package/esm/dif/mod.d.ts.map +1 -1
  28. package/esm/dif/mod.js +5 -1
  29. package/esm/dif/type-registry.d.ts +80 -0
  30. package/esm/dif/type-registry.d.ts.map +1 -0
  31. package/esm/dif/type-registry.js +166 -0
  32. package/esm/global.d.ts +2 -0
  33. package/esm/global.d.ts.map +1 -0
  34. package/esm/global.js +1 -0
  35. package/esm/{runtime/special-core-types.d.ts → lib/special-core-types/endpoint.d.ts} +1 -1
  36. package/esm/lib/special-core-types/endpoint.d.ts.map +1 -0
  37. package/esm/mod.d.ts +16 -10
  38. package/esm/mod.d.ts.map +1 -1
  39. package/esm/mod.js +16 -10
  40. package/esm/network/interface-impls/base.d.ts +9 -1
  41. package/esm/network/interface-impls/base.d.ts.map +1 -1
  42. package/esm/refs/ref.d.ts.map +1 -1
  43. package/esm/refs/ref.js +1 -2
  44. package/esm/runtime/runtime.d.ts +19 -49
  45. package/esm/runtime/runtime.d.ts.map +1 -1
  46. package/esm/runtime/runtime.js +32 -7
  47. package/esm/utils/exceptions.d.ts +4 -0
  48. package/esm/utils/exceptions.d.ts.map +1 -0
  49. package/esm/utils/exceptions.js +9 -0
  50. package/esm/utils/js-runtime-compat/js-runtime.d.ts.map +1 -1
  51. package/package.json +1 -1
  52. package/esm/dif/builders.d.ts +0 -16
  53. package/esm/dif/builders.d.ts.map +0 -1
  54. package/esm/dif/builders.js +0 -28
  55. package/esm/runtime/special-core-types.d.ts.map +0 -1
  56. /package/esm/{runtime/special-core-types.js → lib/special-core-types/endpoint.js} +0 -0
@@ -9,16 +9,19 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
10
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
11
  };
12
- var _DIFHandler_runtime, _DIFHandler_handle, _DIFHandler_transceiver_id, _DIFHandler_cache, _DIFHandler_observers;
12
+ var _DIFHandler_runtime, _DIFHandler_handle, _DIFHandler_transceiver_id, _DIFHandler_cache, _DIFHandler_proxyMapping, _DIFHandler_referenceMetadata, _DIFHandler_observers, _DIFHandler_type_registry;
13
13
  import { Ref } from "../refs/ref.js";
14
- import { Endpoint } from "../runtime/special-core-types.js";
15
- import { DIFReferenceMutability, DIFUpdateKind, } from "./definitions.js";
14
+ import { Endpoint } from "../lib/special-core-types/endpoint.js";
15
+ import { DIFReferenceMutability, DIFTypeDefinitionKind, DIFUpdateKind, } from "./definitions.js";
16
16
  import { CoreTypeAddress, CoreTypeAddressRanges } from "./core.js";
17
- import { difValueContainerToDisplayString } from "./display.js";
17
+ import { TypeRegistry } from "./type-registry.js";
18
+ import { panic } from "../utils/exceptions.js";
19
+ import { JsLibTypeAddress } from "./js-lib.js";
20
+ export const IS_PROXY_ACCESS = Symbol("IS_PROXY_ACCESS");
18
21
  /**
19
22
  * The DIFHandler class provides methods to interact with the DATEX Core DIF runtime,
20
- * including executing Datex scripts, creating and managing pointers, and observing changes.
21
- * It includes a local pointer cache to optimize performance and reduce cross-language calls.
23
+ * including executing Datex scripts, creating and managing references, and observing changes.
24
+ * It includes a local reference cache to optimize performance and reduce cross-language calls.
22
25
  */
23
26
  export class DIFHandler {
24
27
  /**
@@ -42,6 +45,9 @@ export class DIFHandler {
42
45
  get _transceiver_id() {
43
46
  return __classPrivateFieldGet(this, _DIFHandler_transceiver_id, "f");
44
47
  }
48
+ get type_registry() {
49
+ return __classPrivateFieldGet(this, _DIFHandler_type_registry, "f");
50
+ }
45
51
  /**
46
52
  * Creates a new DIFHandler instance.
47
53
  * @param runtime - The JSRuntime instance for executing Datex scripts.
@@ -53,11 +59,22 @@ export class DIFHandler {
53
59
  _DIFHandler_handle.set(this, void 0);
54
60
  // always 0 for now - potentially used for multi DIF transceivers using the same underlying runtime
55
61
  _DIFHandler_transceiver_id.set(this, 0);
56
- /** The pointer cache for storing and reusing object instances on the JS side
57
- * The observerId is only set if the pointer is being observed (if not final).
62
+ /**
63
+ * The reference cache for storing and reusing object instances on the JS side
64
+ * The observerId is only set if the reference is being observed (if not final).
58
65
  */
59
66
  _DIFHandler_cache.set(this, new Map());
67
+ /**
68
+ * Maps the original value to a proxy value
69
+ * (if the values is bound to a custom proxy wrapper)
70
+ */
71
+ _DIFHandler_proxyMapping.set(this, new WeakMap());
72
+ /**
73
+ * The reference metadata map, storing metadata for each cached reference.
74
+ */
75
+ _DIFHandler_referenceMetadata.set(this, new WeakMap());
60
76
  _DIFHandler_observers.set(this, new Map());
77
+ _DIFHandler_type_registry.set(this, new TypeRegistry(this));
61
78
  __classPrivateFieldSet(this, _DIFHandler_runtime, runtime, "f");
62
79
  __classPrivateFieldSet(this, _DIFHandler_handle, runtime.dif(), "f");
63
80
  }
@@ -82,31 +99,21 @@ export class DIFHandler {
82
99
  return __classPrivateFieldGet(this, _DIFHandler_runtime, "f").execute_sync(datexScript, this.convertToDIFValues(values));
83
100
  }
84
101
  /**
85
- * Creates a new pointer for the specified value.
86
- * @param difValue - The DIFValue value to create a pointer for.
102
+ * Creates a new pointer for the specified DIF value.
103
+ * @param difValueContainer - The DIFValueContainer value to create a pointer for.
87
104
  * @param allowedType - The allowed type for the pointer.
88
105
  * @param mutability - The mutability of the pointer.
89
106
  * @returns The created pointer address.
90
107
  */
91
- createPointer(difValue, allowedType = null, mutability) {
92
- return __classPrivateFieldGet(this, _DIFHandler_handle, "f").create_pointer(difValue, allowedType, mutability);
93
- }
94
- /**
95
- * Creates a new pointer that points to an existing address.
96
- * @param address - The address to create a reference pointer for.
97
- * @param allowedType - The allowed type for the pointer.
98
- * @param mutability - The mutability of the pointer.
99
- * @returns A Promise that resolves to the created pointer address.
100
- */
101
- createRefPointer(address, allowedType = null, mutability) {
102
- return __classPrivateFieldGet(this, _DIFHandler_handle, "f").create_pointer(address, allowedType, mutability);
108
+ createReferenceFromDIFValue(difValueContainer, allowedType = null, mutability) {
109
+ return __classPrivateFieldGet(this, _DIFHandler_handle, "f").create_pointer(difValueContainer, allowedType, mutability);
103
110
  }
104
111
  /**
105
112
  * Updates the DIF value at the specified address.
106
113
  * @param address - The address of the DIF value to update.
107
114
  * @param dif - The DIFUpdate object containing the update information.
108
115
  */
109
- updatePointer(address, dif) {
116
+ updateReference(address, dif) {
110
117
  __classPrivateFieldGet(this, _DIFHandler_handle, "f").update(__classPrivateFieldGet(this, _DIFHandler_transceiver_id, "f"), address, dif);
111
118
  }
112
119
  /**
@@ -159,7 +166,7 @@ export class DIFHandler {
159
166
  * @param address - The address of the DIF value being observed.
160
167
  * @param observerId - The observer ID returned by the observePointer method.
161
168
  */
162
- unobservePointerBindDirect(address, observerId) {
169
+ unobserveReferenceBindDirect(address, observerId) {
163
170
  __classPrivateFieldGet(this, _DIFHandler_runtime, "f").dif().unobserve_pointer(address, observerId);
164
171
  }
165
172
  /**
@@ -228,22 +235,27 @@ export class DIFHandler {
228
235
  * @param value
229
236
  */
230
237
  resolveDIFValue(value) {
231
- console.log("RESOLVE", value);
232
238
  let type = value.type;
239
+ let convertMapToJSObject = false;
240
+ // no type specified since it is inferable from the value
233
241
  if (type === undefined) {
234
242
  if (Array.isArray(value.value)) {
243
+ // [[x,y,]] -> map
235
244
  if (Array.isArray(value.value[0])) {
236
245
  type = CoreTypeAddress.map;
237
- }
246
+ } // [x,y] or [] -> list
238
247
  else {
239
248
  type = CoreTypeAddress.list;
240
249
  }
241
250
  }
251
+ else if (typeof value.value === "object" && value.value !== null) {
252
+ type = CoreTypeAddress.map;
253
+ convertMapToJSObject = true;
254
+ } // primitive JS value, no type specified
242
255
  else {
243
256
  return value.value;
244
257
  }
245
258
  }
246
- console.log(type, value);
247
259
  // null, boolean and text types values are just returned as is
248
260
  if (type === CoreTypeAddress.boolean ||
249
261
  type == CoreTypeAddress.text ||
@@ -255,8 +267,7 @@ export class DIFHandler {
255
267
  this.isPointerAddressInAdresses(type, CoreTypeAddressRanges.small_unsigned_integers))) {
256
268
  return Number(value.value);
257
269
  } // big integers are interpreted as JS BigInt
258
- else if (typeof type === "string" && (this.isPointerAddressInAdresses(type, CoreTypeAddressRanges.big_signed_integers) ||
259
- this.isPointerAddressInAdresses(type, CoreTypeAddressRanges.big_unsigned_integers))) {
270
+ else if (typeof type === "string" && (this.isPointerAddressInAdresses(type, CoreTypeAddressRanges.big_signed_integers))) {
260
271
  return BigInt(value.value);
261
272
  } // decimal types are interpreted as JS numbers
262
273
  else if (typeof type === "string" &&
@@ -273,25 +284,39 @@ export class DIFHandler {
273
284
  if (Array.isArray(value.value)) {
274
285
  const resolvedMap = new Map();
275
286
  for (const [key, val] of value.value) {
276
- // TODO: currently always converting to an object here, but this should be a Map per default
277
287
  resolvedMap.set(this.resolveDIFValueContainer(key), this.resolveDIFValueContainer(val));
278
288
  }
279
289
  // TODO: map promises
280
290
  return resolvedMap;
281
291
  }
282
292
  else {
283
- const resolvedObj = {};
284
- for (const [key, val] of Object.entries(value.value)) {
285
- // TODO: currently always converting to an object here, but this should be a Map per default
286
- resolvedObj[key] = this.resolveDIFValueContainer(val);
293
+ if (convertMapToJSObject) {
294
+ const resolvedObj = {};
295
+ for (const [key, val] of Object.entries(value.value)) {
296
+ resolvedObj[key] = this.resolveDIFValueContainer(val);
297
+ }
298
+ return resolvedObj;
299
+ }
300
+ else {
301
+ const resolvedMap = new Map();
302
+ for (const [key, val] of Object.entries(value.value)) {
303
+ resolvedMap.set(key, this.resolveDIFValueContainer(val));
304
+ }
305
+ return resolvedMap;
287
306
  }
288
- return this.promiseFromObjectOrSync(resolvedObj);
307
+ }
308
+ } // impl types
309
+ else if (typeof type == "object" &&
310
+ type.kind === DIFTypeDefinitionKind.ImplType) {
311
+ // undefined (null + js.undefined)
312
+ if (type.def[0] === CoreTypeAddress.null &&
313
+ type.def[1].length === 1 &&
314
+ type.def[1][0] == JsLibTypeAddress.undefined) {
315
+ return undefined;
289
316
  }
290
317
  }
291
- else {
292
- // custom types not implemented yet
293
- throw new Error("Custom type resolution not implemented yet");
294
- }
318
+ // custom types not implemented yet
319
+ throw new Error("Custom type resolution not implemented yet");
295
320
  }
296
321
  /**
297
322
  * Converts an array of Promises or resolved values to either a Promise of an array of resolved values,
@@ -366,6 +391,20 @@ export class DIFHandler {
366
391
  }
367
392
  return result;
368
393
  }
394
+ /**
395
+ * Resolves a DIFProperty to its corresponding JS value.
396
+ */
397
+ resolveDIFPropertySync(property) {
398
+ if (property.kind === "text") {
399
+ return property.value;
400
+ }
401
+ else if (property.kind === "index") {
402
+ return property.value;
403
+ }
404
+ else {
405
+ return this.resolveDIFValueContainerSync(property.value);
406
+ }
407
+ }
369
408
  /**
370
409
  * Resolves a pointer address to its corresponding JS value.
371
410
  * If the pointer address is not yet loaded in memory, it returns a Promise that resolves to the value.
@@ -375,7 +414,7 @@ export class DIFHandler {
375
414
  */
376
415
  resolvePointerAddress(address) {
377
416
  // check cache first
378
- const cached = this.getCachedPointer(address);
417
+ const cached = this.getCachedReference(address);
379
418
  if (cached) {
380
419
  return cached;
381
420
  }
@@ -385,7 +424,7 @@ export class DIFHandler {
385
424
  const value = this.resolveDIFValueContainer(reference.value);
386
425
  return this.mapPromise(value, (v) => {
387
426
  // init pointer
388
- this.initPointer(address, v, reference.mut, reference.allowed_type);
427
+ this.initReference(address, v, reference.mut, reference.allowed_type);
389
428
  return v;
390
429
  });
391
430
  });
@@ -400,7 +439,7 @@ export class DIFHandler {
400
439
  */
401
440
  resolvePointerAddressSync(address) {
402
441
  // check cache first
403
- const cached = this.getCachedPointer(address);
442
+ const cached = this.getCachedReference(address);
404
443
  if (cached) {
405
444
  return cached;
406
445
  }
@@ -408,16 +447,46 @@ export class DIFHandler {
408
447
  const entry = __classPrivateFieldGet(this, _DIFHandler_handle, "f")
409
448
  .resolve_pointer_address_sync(address);
410
449
  const value = this.resolveDIFValueContainerSync(entry.value);
411
- this.initPointer(address, value, entry.mut, entry.allowed_type);
450
+ this.initReference(address, value, entry.mut, entry.allowed_type);
412
451
  return value;
413
452
  }
453
+ /**
454
+ * Retrieves the original value from a proxy value if available
455
+ * @param proxy
456
+ * @returns
457
+ */
458
+ getOriginalValueFromProxy(proxy) {
459
+ const address = this.getPointerAddressForValue(proxy);
460
+ if (address) {
461
+ const cached = __classPrivateFieldGet(this, _DIFHandler_cache, "f").get(address);
462
+ if (cached && cached.originalValue) {
463
+ return cached.originalValue;
464
+ }
465
+ }
466
+ return null;
467
+ }
468
+ /**
469
+ * Retrieves the proxy value for a given original value if available
470
+ * @param original
471
+ * @returns
472
+ */
473
+ getProxyValueFromOriginal(original) {
474
+ const ref = __classPrivateFieldGet(this, _DIFHandler_proxyMapping, "f").get(original);
475
+ if (ref) {
476
+ const proxied = ref.deref();
477
+ if (proxied) {
478
+ return proxied;
479
+ }
480
+ }
481
+ return null;
482
+ }
414
483
  /**
415
484
  * Converts an array of JS values to an array of DIFValues.
416
485
  * If the input is null, it returns null.
417
486
  * @param values
418
487
  */
419
488
  convertToDIFValues(values) {
420
- return values?.map((value) => this.convertJSValueToDIFValue(value)) ||
489
+ return values?.map((value) => this.convertJSValueToDIFValueContainer(value)) ||
421
490
  null;
422
491
  }
423
492
  /**
@@ -427,21 +496,41 @@ export class DIFHandler {
427
496
  return range.has(address);
428
497
  }
429
498
  /**
430
- * Initializes a pointer with the given value and mutability, by
499
+ * Initializes a reference with the given value and mutability, by
431
500
  * adding a proxy wrapper if necessary, and setting up observation and caching on the JS side.
432
501
  */
433
- initPointer(ptrAddress, value, mutability, allowedType = null) {
434
- const refValue = this.wrapJSValueInProxy(value, ptrAddress, allowedType);
502
+ initReference(pointerAddress, value, mutability, allowedType = null) {
503
+ let wrappedValue = this.wrapJSValue(value, pointerAddress, allowedType);
504
+ let typeBinding = null;
505
+ let metadata = undefined;
506
+ // bind js value (if mutable, nominal type)
507
+ const bindJSValue = mutability !== DIFReferenceMutability.Immutable &&
508
+ typeof allowedType == "string";
509
+ if (bindJSValue && !(wrappedValue instanceof Ref)) {
510
+ typeBinding = this.type_registry.getTypeBinding(allowedType);
511
+ if (typeBinding) {
512
+ const { value, metadata: newMetadata } = typeBinding
513
+ .bindValue(wrappedValue, pointerAddress);
514
+ metadata = newMetadata;
515
+ wrappedValue = value;
516
+ }
517
+ }
435
518
  // if not immutable, observe to keep the pointer 'live' and receive updates
436
519
  let observerId = null;
437
520
  if (mutability !== DIFReferenceMutability.Immutable) {
438
- observerId = this.observePointerBindDirect(ptrAddress, (update) => {
521
+ observerId = this.observePointerBindDirect(pointerAddress, (update) => {
439
522
  // if source_id is not own transceiver id, handle pointer update
440
523
  if (update.source_id !== __classPrivateFieldGet(this, _DIFHandler_transceiver_id, "f")) {
441
- this.handlePointerUpdate(ptrAddress, update.data);
524
+ try {
525
+ this.handlePointerUpdate(pointerAddress, wrappedValue, update.data, typeBinding);
526
+ }
527
+ catch (e) {
528
+ console.error("Error handling pointer update", e);
529
+ throw e;
530
+ }
442
531
  }
443
532
  // call all local observers
444
- const observers = __classPrivateFieldGet(this, _DIFHandler_observers, "f").get(ptrAddress);
533
+ const observers = __classPrivateFieldGet(this, _DIFHandler_observers, "f").get(pointerAddress);
445
534
  if (observers) {
446
535
  for (const cb of observers.values()) {
447
536
  try {
@@ -455,55 +544,69 @@ export class DIFHandler {
455
544
  console.debug("Pointer update received", update);
456
545
  });
457
546
  }
458
- this.cacheWrappedPointerValue(ptrAddress, refValue, observerId);
459
- return refValue;
547
+ this.cacheWrappedReferenceValue(pointerAddress, value, wrappedValue, observerId, metadata);
548
+ // set up observers
549
+ return wrappedValue;
460
550
  }
461
- /**
462
- * Handles a pointer update received from the DATEX core runtime.
463
- * If the pointer is cached and has a dereferenceable value, it updates the value.
464
- * @param address - The address of the pointer being updated.
465
- * @param update - The DIFUpdateData containing the update information.
466
- * @returns True if the pointer was found and updated, false otherwise.
467
- */
468
- handlePointerUpdate(address, update) {
469
- const cached = __classPrivateFieldGet(this, _DIFHandler_cache, "f").get(address);
551
+ handlePointerUpdate(pointerAddress, value, update, typeBinding) {
552
+ const cached = __classPrivateFieldGet(this, _DIFHandler_cache, "f").get(pointerAddress);
470
553
  if (!cached)
471
554
  return false;
472
- const deref = cached.val.deref();
555
+ const deref = cached.value.deref();
473
556
  if (!deref)
474
557
  return false;
475
558
  if (deref instanceof Ref && update.kind === DIFUpdateKind.Replace) {
476
559
  deref.updateValueSilently(this.resolveDIFValueContainerSync(update.value));
477
560
  }
478
- // TODO: handle generic updates for values (depending on type interface definition)
561
+ // handle generic updates for values (depending on type interface definition)
562
+ if (typeBinding) {
563
+ typeBinding.handleDifUpdate(value, pointerAddress, update);
564
+ }
479
565
  return true;
480
566
  }
481
567
  /**
482
- * Caches the given pointer value with the given address in the JS side cache.
483
- * The pointer must already be wrapped if necessary.
568
+ * Caches the given reference value with the given address in the JS side cache.
569
+ * The reference must already be wrapped if necessary.
484
570
  */
485
- cacheWrappedPointerValue(address, value, observerId) {
571
+ cacheWrappedReferenceValue(address, originalValue, proxiedValue, observerId, metadata = {}) {
572
+ const isProxifiedValue = this.isWeakKey(originalValue) &&
573
+ originalValue !== proxiedValue;
486
574
  __classPrivateFieldGet(this, _DIFHandler_cache, "f").set(address, {
487
- val: new WeakRef(value),
575
+ value: new WeakRef(proxiedValue),
576
+ originalValue: isProxifiedValue ? originalValue : null,
488
577
  observerId,
489
578
  });
490
- // register finalizer to clean up the cache and free the pointer in the runtime
579
+ __classPrivateFieldGet(this, _DIFHandler_referenceMetadata, "f").set(proxiedValue, {
580
+ address,
581
+ customMetadata: metadata,
582
+ });
583
+ // store in proxy mapping if original value is not identical to proxied value
584
+ // and original value is a weak key
585
+ if (isProxifiedValue) {
586
+ __classPrivateFieldGet(this, _DIFHandler_proxyMapping, "f").set(originalValue, new WeakRef(proxiedValue));
587
+ }
588
+ // register finalizer to clean up the cache and free the reference in the runtime
491
589
  // when the object is garbage collected
492
590
  const finalizationRegistry = new FinalizationRegistry((address) => {
591
+ const originalValue = __classPrivateFieldGet(this, _DIFHandler_cache, "f").get(address)?.originalValue;
592
+ // remove from proxy mapping if applicable
593
+ if (originalValue) {
594
+ __classPrivateFieldGet(this, _DIFHandler_proxyMapping, "f").delete(originalValue);
595
+ }
493
596
  __classPrivateFieldGet(this, _DIFHandler_cache, "f").delete(address);
494
597
  // remove local observers
495
598
  __classPrivateFieldGet(this, _DIFHandler_observers, "f").delete(address);
496
599
  // if observer is active, unregister it
497
600
  if (observerId !== null) {
498
- this.unobservePointerBindDirect(address, observerId);
601
+ this.unobserveReferenceBindDirect(address, observerId);
499
602
  }
500
603
  });
501
- finalizationRegistry.register(value, address);
604
+ finalizationRegistry.register(proxiedValue, address);
502
605
  }
503
- getCachedPointer(address) {
606
+ getCachedReference(address) {
504
607
  const cached = __classPrivateFieldGet(this, _DIFHandler_cache, "f").get(address);
505
608
  if (cached) {
506
- const deref = cached.val.deref();
609
+ const deref = cached.value.deref();
507
610
  if (deref) {
508
611
  return deref;
509
612
  }
@@ -511,92 +614,51 @@ export class DIFHandler {
511
614
  return undefined;
512
615
  }
513
616
  /**
514
- * Creates a new pointer containg the given JS value.
617
+ * Creates a new reference containg the given JS value.
515
618
  * The returned value is a proxy object that behaves like the original object,
516
619
  * but also propagates changes between JS and the DATEX runtime.
620
+ * If a reference for the given value already exists, an error is thrown.
517
621
  */
518
- createPointerFromJSValue(value, allowedType = null, mutability = DIFReferenceMutability.Mutable) {
519
- const difValue = this.convertJSValueToDIFValue(value);
520
- console.log("DIF", difValueContainerToDisplayString(difValue));
521
- const ptrAddress = this.createPointer(difValue, allowedType, mutability);
622
+ createTransparentReference(value, allowedType = null, mutability = DIFReferenceMutability.Mutable) {
623
+ // if already bound to a reference, return the existing reference proxy (or the value itself)
624
+ const pointerAddress = this.getPointerAddressForValue(value);
625
+ if (pointerAddress) {
626
+ throw new Error(`Value is already bound to a reference ($${pointerAddress}). Cannot create a new reference for the same value.`);
627
+ }
628
+ const difValue = this.convertJSValueToDIFValueContainer(value);
629
+ const ptrAddress = this.createReferenceFromDIFValue(difValue, allowedType, mutability);
522
630
  // get inferred allowed type from pointer if not explicitly set
523
631
  if (!allowedType) {
524
632
  allowedType = __classPrivateFieldGet(this, _DIFHandler_handle, "f").resolve_pointer_address_sync(ptrAddress).allowed_type;
525
633
  }
526
- return this.initPointer(ptrAddress, value, mutability, allowedType); // TODO: map to correct pointer wrapper type
634
+ return this.initReference(ptrAddress, value, mutability, allowedType);
527
635
  }
528
- wrapJSValueInProxy(value, pointerAddress, _type = null) {
529
- // primitive values are always wrapped in a Ref proxy
530
- if (value === null || value === undefined ||
636
+ isPrimitiveValue(value) {
637
+ return value === null || value === undefined ||
531
638
  typeof value === "boolean" ||
532
639
  typeof value === "number" || typeof value === "bigint" ||
533
- typeof value === "string") {
534
- return new Ref(value, pointerAddress, this);
535
- }
536
- else if (value instanceof Map) {
537
- return this.proxifyJSMap(value, pointerAddress);
538
- }
539
- else if (typeof value === "object") {
540
- return this.wrapJSObjectInProxy(value);
640
+ typeof value === "string" || typeof value === "symbol";
641
+ }
642
+ isWeakKey(value) {
643
+ // non-registered symbols are valid WeakKeys
644
+ return (typeof value === "symbol" && !Symbol.keyFor(value)) ||
645
+ !this.isPrimitiveValue(value);
646
+ }
647
+ /**
648
+ * Wraps a given JS value in a Ref proxy if necessary.
649
+ */
650
+ wrapJSValue(value, pointerAddress, _type = null) {
651
+ // primitive values are always wrapped in a Ref proxy
652
+ if (this.isWeakKey(value)) {
653
+ return value;
541
654
  }
542
655
  else {
543
- return value;
656
+ return new Ref(value, pointerAddress, this);
544
657
  }
545
658
  }
546
659
  isRef(value) {
547
660
  return value instanceof Ref;
548
661
  }
549
- proxifyJSMap(map, pointerAddress) {
550
- const originalSet = map.set;
551
- const originalDelete = map.delete;
552
- const originalClear = map.clear;
553
- // deno-lint-ignore no-this-alias
554
- const self = this;
555
- Object.defineProperties(map, {
556
- set: {
557
- value: function (key, value) {
558
- self.updatePointer(pointerAddress, {
559
- kind: DIFUpdateKind.Set,
560
- key: {
561
- kind: "value",
562
- value: self.convertJSValueToDIFValue(key),
563
- },
564
- value: self.convertJSValueToDIFValue(value),
565
- });
566
- return originalSet.call(this, key, value);
567
- },
568
- configurable: true,
569
- writable: true,
570
- },
571
- delete: {
572
- value: function (key) {
573
- self.updatePointer(pointerAddress, {
574
- kind: DIFUpdateKind.Remove,
575
- key: {
576
- kind: "value",
577
- value: self.convertJSValueToDIFValue(key),
578
- },
579
- });
580
- const result = originalDelete.call(this, key);
581
- return result;
582
- },
583
- configurable: true,
584
- writable: true,
585
- },
586
- clear: {
587
- value: function () {
588
- self.updatePointer(pointerAddress, {
589
- kind: DIFUpdateKind.Clear,
590
- });
591
- const result = originalClear.call(this);
592
- return result;
593
- },
594
- configurable: true,
595
- writable: true,
596
- },
597
- });
598
- return map;
599
- }
600
662
  wrapJSObjectInProxy(value) {
601
663
  // deno-lint-ignore no-this-alias
602
664
  const self = this;
@@ -624,22 +686,64 @@ export class DIFHandler {
624
686
  },
625
687
  });
626
688
  }
627
- /// Returns the pointer address for the given value if it is already cached, or null otherwise.
628
- /// TODO: optimize by using a reverse map or direct Symbols on the objects
629
- /// But this is probably not important for normal use cases
689
+ /**
690
+ * Returns the pointer address for the given value if it is already cached, or null otherwise.
691
+ */
630
692
  getPointerAddressForValue(value) {
631
- for (const [address, entry] of __classPrivateFieldGet(this, _DIFHandler_cache, "f")) {
632
- const deref = entry.val.deref();
633
- if (deref === value) {
634
- return address;
693
+ return __classPrivateFieldGet(this, _DIFHandler_referenceMetadata, "f").get(value)?.address || null;
694
+ }
695
+ /**
696
+ * Returns the reference metadata for the given value if it is registered.
697
+ * The caller must ensure that the correct type M is used and the reference is already registered.
698
+ * If the reference is not found, an error is thrown.
699
+ */
700
+ getReferenceMetadataUnsafe(value) {
701
+ const metadata = this.tryGetReferenceMetadata(value);
702
+ if (!metadata) {
703
+ panic("Reference metadata not found for the given value");
704
+ }
705
+ return metadata;
706
+ }
707
+ /**
708
+ * Returns the reference metadata for the given value if it is registered, or null otherwise.
709
+ */
710
+ tryGetReferenceMetadata(value) {
711
+ return (__classPrivateFieldGet(this, _DIFHandler_referenceMetadata, "f").get(value) ??
712
+ __classPrivateFieldGet(this, _DIFHandler_referenceMetadata, "f").get(__classPrivateFieldGet(this, _DIFHandler_proxyMapping, "f").get(value)?.deref()) ??
713
+ null);
714
+ }
715
+ isReference(value) {
716
+ return __classPrivateFieldGet(this, _DIFHandler_referenceMetadata, "f").has(value) ||
717
+ __classPrivateFieldGet(this, _DIFHandler_proxyMapping, "f").has(value);
718
+ }
719
+ getReferenceProxy(value) {
720
+ const reference = __classPrivateFieldGet(this, _DIFHandler_referenceMetadata, "f").get(value);
721
+ if (reference) {
722
+ return value;
723
+ }
724
+ const proxyRef = __classPrivateFieldGet(this, _DIFHandler_proxyMapping, "f").get(value);
725
+ if (proxyRef) {
726
+ const deref = proxyRef.deref();
727
+ if (deref) {
728
+ return deref;
729
+ }
730
+ else {
731
+ panic("Reference proxy has been garbage collected");
635
732
  }
636
733
  }
637
- return null;
734
+ else {
735
+ return null;
736
+ }
638
737
  }
639
738
  /**
640
- * Converts a given JS value to its DIFValue representation.
739
+ * Converts a given JS value to its DIFValueContainer representation.
641
740
  */
642
- convertJSValueToDIFValue(value) {
741
+ convertJSValueToDIFValueContainer(value) {
742
+ // if the value is a registered reference, return its address
743
+ const existingReference = this.tryGetReferenceMetadata(value);
744
+ if (existingReference) {
745
+ return existingReference.address;
746
+ }
643
747
  // assuming core values
644
748
  // TODO: handle custom types
645
749
  if (value === null) {
@@ -647,6 +751,18 @@ export class DIFHandler {
647
751
  value: null,
648
752
  };
649
753
  }
754
+ else if (value === undefined) {
755
+ return {
756
+ type: {
757
+ kind: DIFTypeDefinitionKind.ImplType,
758
+ def: [
759
+ CoreTypeAddress.null,
760
+ [JsLibTypeAddress.undefined],
761
+ ],
762
+ },
763
+ value: null,
764
+ };
765
+ }
650
766
  else if (typeof value === "boolean") {
651
767
  return {
652
768
  value,
@@ -659,7 +775,7 @@ export class DIFHandler {
659
775
  }
660
776
  else if (typeof value === "bigint") {
661
777
  return {
662
- type: CoreTypeAddress.integer_big,
778
+ type: CoreTypeAddress.integer_ibig,
663
779
  value: value.toString(), // convert bigint to string for DIFValue
664
780
  };
665
781
  }
@@ -676,13 +792,14 @@ export class DIFHandler {
676
792
  }
677
793
  else if (Array.isArray(value)) {
678
794
  return {
679
- value: value.map((v) => this.convertJSValueToDIFValue(v)),
795
+ value: value.map((v) => this.convertJSValueToDIFValueContainer(v)),
680
796
  };
681
797
  }
682
798
  else if (value instanceof Map) {
683
- const map = value.entries().map(([k, v]) => [
684
- this.convertJSValueToDIFValue(k),
685
- this.convertJSValueToDIFValue(v),
799
+ const map = value
800
+ .entries().map(([k, v]) => [
801
+ this.convertJSValueToDIFValueContainer(k),
802
+ this.convertJSValueToDIFValueContainer(v),
686
803
  ]).toArray();
687
804
  return {
688
805
  type: CoreTypeAddress.map,
@@ -692,14 +809,104 @@ export class DIFHandler {
692
809
  else if (typeof value === "object") {
693
810
  const map = {};
694
811
  for (const [key, val] of Object.entries(value)) {
695
- map[key] = this.convertJSValueToDIFValue(val);
812
+ map[key] = this.convertJSValueToDIFValueContainer(val);
696
813
  }
697
814
  return {
698
- type: CoreTypeAddress.map,
699
815
  value: map,
700
816
  };
701
817
  }
702
818
  throw new Error("Unsupported type for conversion to DIFValue");
703
819
  }
820
+ /** DIF update handler utilities */
821
+ /**
822
+ * Triggers a 'set' update for the given pointer address, key and value.
823
+ */
824
+ triggerSet(pointerAddress, key, value) {
825
+ const difKey = this.convertJSValueToDIFValueContainer(key);
826
+ const difValue = this.convertJSValueToDIFValueContainer(value);
827
+ const update = {
828
+ kind: DIFUpdateKind.Set,
829
+ key: { kind: "value", value: difKey },
830
+ value: difValue,
831
+ };
832
+ console.log("Triggering set update", update);
833
+ this.updateReference(pointerAddress, update);
834
+ }
835
+ /**
836
+ * Triggers a 'set' update for the given pointer address, index and value.
837
+ */
838
+ triggerIndexSet(pointerAddress, index, value) {
839
+ if (typeof index !== "bigint" && !Number.isInteger(index)) {
840
+ throw new Error("Index must be a non-negative integer");
841
+ }
842
+ const difValue = this.convertJSValueToDIFValueContainer(value);
843
+ const update = {
844
+ kind: DIFUpdateKind.Set,
845
+ key: { kind: "index", value: Number(index) },
846
+ value: difValue,
847
+ };
848
+ console.log("Triggering index set update", update);
849
+ this.updateReference(pointerAddress, update);
850
+ }
851
+ /**
852
+ * Triggers an 'append' update for the given pointer address and value.
853
+ */
854
+ triggerAppend(pointerAddress, value) {
855
+ const difValue = this.convertJSValueToDIFValueContainer(value);
856
+ const update = {
857
+ kind: DIFUpdateKind.Append,
858
+ value: difValue,
859
+ };
860
+ console.log("Triggering append update", update);
861
+ this.updateReference(pointerAddress, update);
862
+ }
863
+ /**
864
+ * Triggers a 'replace' update for the given pointer address and key.
865
+ */
866
+ triggerReplace(pointerAddress, value) {
867
+ const difValue = this.convertJSValueToDIFValueContainer(value);
868
+ const update = {
869
+ kind: DIFUpdateKind.Replace,
870
+ value: difValue,
871
+ };
872
+ console.log("Triggering replace update", update);
873
+ this.updateReference(pointerAddress, update);
874
+ }
875
+ /**
876
+ * Triggers a 'delete' update for the given pointer address and key.
877
+ */
878
+ triggerDelete(pointerAddress, key) {
879
+ const difKey = this.convertJSValueToDIFValueContainer(key);
880
+ const update = {
881
+ kind: DIFUpdateKind.Delete,
882
+ key: { kind: "value", value: difKey },
883
+ };
884
+ console.log("Triggering delete update", update);
885
+ this.updateReference(pointerAddress, update);
886
+ }
887
+ /**
888
+ * Triggers a 'clear' update for the given pointer address.
889
+ */
890
+ triggerClear(pointerAddress) {
891
+ const update = {
892
+ kind: DIFUpdateKind.Clear,
893
+ };
894
+ console.log("Triggering clear update", update);
895
+ this.updateReference(pointerAddress, update);
896
+ }
897
+ /**
898
+ * Triggers a 'list splice' update for the given pointer address.
899
+ */
900
+ triggerListSplice(pointerAddress, start, deleteCount, items) {
901
+ const difItems = items.map((item) => this.convertJSValueToDIFValueContainer(item));
902
+ const update = {
903
+ kind: DIFUpdateKind.ListSplice,
904
+ start,
905
+ delete_count: deleteCount,
906
+ items: difItems,
907
+ };
908
+ console.log("Triggering list splice update", update);
909
+ this.updateReference(pointerAddress, update);
910
+ }
704
911
  }
705
- _DIFHandler_runtime = new WeakMap(), _DIFHandler_handle = new WeakMap(), _DIFHandler_transceiver_id = new WeakMap(), _DIFHandler_cache = new WeakMap(), _DIFHandler_observers = new WeakMap();
912
+ _DIFHandler_runtime = new WeakMap(), _DIFHandler_handle = new WeakMap(), _DIFHandler_transceiver_id = new WeakMap(), _DIFHandler_cache = new WeakMap(), _DIFHandler_proxyMapping = new WeakMap(), _DIFHandler_referenceMetadata = new WeakMap(), _DIFHandler_observers = new WeakMap(), _DIFHandler_type_registry = new WeakMap();