@mintjamsinc/ichigojs 0.1.71 → 0.1.73

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.
@@ -6945,42 +6945,70 @@
6945
6945
  functionDependencies[funcName] = [];
6946
6946
  }
6947
6947
  }
6948
- // Second pass: recursively resolve function dependencies
6949
- const resolvedDependencies = {};
6950
- const resolving = new Set(); // To detect circular dependencies
6951
- const resolveDependencies = (funcName) => {
6952
- // If already resolved, return cached result
6953
- if (resolvedDependencies[funcName]) {
6954
- return resolvedDependencies[funcName];
6955
- }
6956
- // Check for circular dependency
6957
- if (resolving.has(funcName)) {
6958
- console.warn(`Circular dependency detected for function: ${funcName}`);
6959
- return [];
6960
- }
6961
- resolving.add(funcName);
6962
- const allDependencies = new Set();
6963
- const directDependencies = functionDependencies[funcName] || [];
6964
- for (const dep of directDependencies) {
6948
+ // Second pass: resolve each function's transitive reactive dependencies.
6949
+ //
6950
+ // A function's direct dependency list mixes two kinds of identifiers: references to other
6951
+ // functions (call edges) and references to reactive data properties (the values we actually
6952
+ // care about). We want, for every function, the full set of reactive properties reachable by
6953
+ // following call edges to any depth.
6954
+ //
6955
+ // Functions are free to reference each other cyclically — a poller that reschedules itself
6956
+ // (`scheduleOperationPoll` -> `pollOperation` -> `scheduleOperationPoll`), a pair of mutually
6957
+ // recursive helpers, or any state machine. This is valid JavaScript, so the resolver must not
6958
+ // treat it as an error. Every function in a cycle (more precisely, a strongly connected
6959
+ // component) is reachable from the others, so they all share the same transitive dependency
6960
+ // set: the union of every member's direct reactive dependencies.
6961
+ //
6962
+ // A depth-first walk that bails out on back-edges cannot compute that correctly — it drops the
6963
+ // dependencies contributed around the cycle and caches incomplete, traversal-order-dependent
6964
+ // results, which would silently break reactivity for any computed that reads reactive data
6965
+ // through a cyclic method. We instead split each function's direct dependencies into reactive
6966
+ // properties and call edges, then propagate reactive properties along call edges until the sets
6967
+ // stop growing (a fixpoint). This converges regardless of cycles and yields the exact transitive
6968
+ // closure for every function, including those participating in a cycle.
6969
+ const directReactive = {};
6970
+ const callEdges = {};
6971
+ for (const funcName of Object.keys(functions)) {
6972
+ const reactive = new Set();
6973
+ const edges = [];
6974
+ for (const dep of functionDependencies[funcName] || []) {
6965
6975
  if (dep === funcName)
6966
- continue; // Skip self-references
6976
+ continue; // Skip self-references.
6967
6977
  if (functions[dep]) {
6968
- // It's a function, recursively resolve its dependencies
6969
- const subDependencies = resolveDependencies(dep);
6970
- subDependencies.forEach(subDep => allDependencies.add(subDep));
6978
+ edges.push(dep); // Call edge to another function.
6971
6979
  }
6972
6980
  else {
6973
- // It's a variable, add directly
6974
- allDependencies.add(dep);
6981
+ reactive.add(dep); // Reactive data property (terminal dependency).
6975
6982
  }
6976
6983
  }
6977
- resolving.delete(funcName);
6978
- resolvedDependencies[funcName] = Array.from(allDependencies);
6979
- return resolvedDependencies[funcName];
6980
- };
6981
- // Resolve all functions
6984
+ directReactive[funcName] = reactive;
6985
+ callEdges[funcName] = edges;
6986
+ }
6987
+ // Seed each function with its own direct reactive dependencies, then propagate along call edges
6988
+ // until no set changes. Each pass can only add properties, and the universe of properties is
6989
+ // finite, so the loop is guaranteed to terminate.
6990
+ const resolved = {};
6991
+ for (const funcName of Object.keys(functions)) {
6992
+ resolved[funcName] = new Set(directReactive[funcName]);
6993
+ }
6994
+ let changed = true;
6995
+ while (changed) {
6996
+ changed = false;
6997
+ for (const funcName of Object.keys(functions)) {
6998
+ const target = resolved[funcName];
6999
+ for (const edge of callEdges[funcName]) {
7000
+ for (const dep of resolved[edge]) {
7001
+ if (!target.has(dep)) {
7002
+ target.add(dep);
7003
+ changed = true;
7004
+ }
7005
+ }
7006
+ }
7007
+ }
7008
+ }
7009
+ const resolvedDependencies = {};
6982
7010
  for (const funcName of Object.keys(functions)) {
6983
- resolveDependencies(funcName);
7011
+ resolvedDependencies[funcName] = Array.from(resolved[funcName]);
6984
7012
  }
6985
7013
  return resolvedDependencies;
6986
7014
  }
@@ -7995,6 +8023,41 @@
7995
8023
  * This allows mapping variable names to their actual source paths for dependency tracking.
7996
8024
  */
7997
8025
  static pathAliases = new Map();
8026
+ /**
8027
+ * The `Object.prototype.toString` tags of the *only* value types we deeply
8028
+ * proxy. Everything else is returned untouched (raw).
8029
+ *
8030
+ * Rationale: a Proxy can only safely intercept plain data containers. Built-in
8031
+ * and host objects — `Date`, `RegExp`, `File`, `Blob`, `FileList`, `Promise`,
8032
+ * `WeakMap`/`WeakSet`, typed arrays, DOM nodes, etc. — carry private internal
8033
+ * slots, and invoking their native methods with a Proxy as the `this`/receiver
8034
+ * throws "Illegal invocation". They must therefore never be wrapped. This is an
8035
+ * allow-list rather than a deny-list so that *every* such exotic type is
8036
+ * excluded by default, not just the handful we happened to enumerate.
8037
+ *
8038
+ * Plain user-defined class instances report `[object Object]` (unless they set
8039
+ * `Symbol.toStringTag`) and so remain reactive, preserving prior behaviour.
8040
+ * `Map`/`Set` stay reactive because the proxy specially wraps their mutation
8041
+ * methods (see the `get` trap); `WeakMap`/`WeakSet` are intentionally excluded
8042
+ * since they cannot be wrapped that way and would break the same as `File`.
8043
+ *
8044
+ * Callers that want a plain object kept non-reactive can still opt out
8045
+ * explicitly via {@link markRaw}.
8046
+ */
8047
+ static REACTIVE_TYPE_TAGS = new Set([
8048
+ '[object Object]',
8049
+ '[object Array]',
8050
+ '[object Arguments]',
8051
+ '[object Map]',
8052
+ '[object Set]',
8053
+ ]);
8054
+ /**
8055
+ * Whether the given non-null object value is one we deeply proxy.
8056
+ * See {@link REACTIVE_TYPE_TAGS} for the reasoning.
8057
+ */
8058
+ static isReactivable(value) {
8059
+ return this.REACTIVE_TYPE_TAGS.has(Object.prototype.toString.call(value));
8060
+ }
7998
8061
  /**
7999
8062
  * Creates a reactive proxy for the given object.
8000
8063
  * The proxy will call the onChange callback whenever a property is modified.
@@ -8014,11 +8077,10 @@
8014
8077
  if (this.rawObjects.has(target)) {
8015
8078
  return target;
8016
8079
  }
8017
- // Don't wrap built-in objects that have internal slots
8018
- // These objects require their methods to be called with the correct 'this' context
8019
- // Use Object.prototype.toString for more reliable type checking
8020
- const typeTag = Object.prototype.toString.call(target);
8021
- if (typeTag === '[object Date]' || typeTag === '[object RegExp]' || typeTag === '[object Error]') {
8080
+ // Only deeply proxy plain data containers; return every built-in/host
8081
+ // object (Date, File, Blob, Promise, WeakMap, DOM nodes, …) as-is, since
8082
+ // their native methods break when invoked through a Proxy receiver.
8083
+ if (!this.isReactivable(target)) {
8022
8084
  return target;
8023
8085
  }
8024
8086
  // Check if the target is already a proxy - if so, return it as-is to prevent double-wrapping
@@ -8054,10 +8116,10 @@
8054
8116
  if (ReactiveProxy.rawObjects.has(value)) {
8055
8117
  return value;
8056
8118
  }
8057
- // Don't wrap built-in objects that have internal slots
8058
- // Use Object.prototype.toString for more reliable type checking
8059
- const valueTypeTag = Object.prototype.toString.call(value);
8060
- if (valueTypeTag === '[object Date]' || valueTypeTag === '[object RegExp]' || valueTypeTag === '[object Error]') {
8119
+ // Only deeply proxy plain data containers; hand back built-in
8120
+ // and host objects (Date, File, Blob, Promise, WeakMap, DOM
8121
+ // nodes, …) untouched so their native methods keep working.
8122
+ if (!ReactiveProxy.isReactivable(value)) {
8061
8123
  return value;
8062
8124
  }
8063
8125
  // Build the nested path