@opencode/codemode 0.0.0-dev-20097 → 0.0.0-dev-20103

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.
@@ -9,7 +9,7 @@ import { globals } from "./globals.js";
9
9
  import { assign, Callable, define, frozen, get, hostCursor, IteratorObj, has, hidden, hasPrototype, keys, Native, parseArrayIndex, Arr, Fn, GeneratorObj, Obj, PromiseObj, record, remove, set, coerceToNumber, coerceToString, } from "./objects.js";
10
10
  import { preserveConsumerError } from "./callback.js";
11
11
  import { Pending, resolvePromise, resolvePromiseValue } from "./promises.js";
12
- import { containsOpaqueReference, describeValue, rejectCircularInsertion, typeofValue } from "./references.js";
12
+ import { describeValue, isOpaque, rejectCircularInsertion, typeofValue } from "./references.js";
13
13
  import { ScopeStack } from "./scope.js";
14
14
  import { constructRegExp } from "../stdlib/regexp.js";
15
15
  import { enumerableSource } from "../stdlib/object.js";
@@ -388,9 +388,6 @@ class Frame {
388
388
  const self = this;
389
389
  return Effect.gen(function* () {
390
390
  const discriminant = yield* self.evaluateExpression(node.discriminant);
391
- if (containsOpaqueReference(discriminant)) {
392
- throw invalidData("Switch discriminants must be data values.", node);
393
- }
394
391
  self.scopes.push();
395
392
  return yield* Effect.gen(function* () {
396
393
  const cases = node.cases;
@@ -405,11 +402,7 @@ class Frame {
405
402
  defaultIndex = index;
406
403
  continue;
407
404
  }
408
- const candidate = yield* self.evaluateExpression(test);
409
- if (containsOpaqueReference(candidate)) {
410
- throw invalidData("Switch case values must be data values.", test);
411
- }
412
- if (candidate === discriminant) {
405
+ if ((yield* self.evaluateExpression(test)) === discriminant) {
413
406
  selected = index;
414
407
  break;
415
408
  }
@@ -1077,16 +1070,19 @@ class Frame {
1077
1070
  return lhs === rhs;
1078
1071
  if (operator === "!==")
1079
1072
  return lhs !== rhs;
1080
- if (operator === "in" && rhs instanceof Obj && !containsOpaqueReference(lhs)) {
1073
+ if (operator === "==")
1074
+ return this.looselyEqual(lhs, rhs, node);
1075
+ if (operator === "!=")
1076
+ return !this.looselyEqual(lhs, rhs, node);
1077
+ if (operator === "in" && rhs instanceof Obj && !isOpaque(lhs)) {
1081
1078
  return has(rhs, lhs !== null && typeof lhs === "object" ? coerceToString(lhs) : lhs);
1082
1079
  }
1083
- if (containsOpaqueReference(lhs) || containsOpaqueReference(rhs)) {
1080
+ if (isOpaque(lhs) || isOpaque(rhs)) {
1084
1081
  throw invalidData("Binary operators require data values.", node);
1085
1082
  }
1086
- // Addition and loose equality use the default hint; every other operator asks for a number.
1087
- const hint = operator === "+" || operator === "==" || operator === "!=" ? "default" : "number";
1083
+ // Addition uses the default hint; every other operator asks for a number.
1084
+ const hint = operator === "+" ? "default" : "number";
1088
1085
  const coerceOperand = (operand) => (operand instanceof Obj ? operand.toPrimitive(hint) : operand);
1089
- const bothObjects = lhs !== null && typeof lhs === "object" && rhs !== null && typeof rhs === "object";
1090
1086
  const l = coerceOperand(lhs);
1091
1087
  const r = coerceOperand(rhs);
1092
1088
  switch (operator) {
@@ -1106,10 +1102,6 @@ class Frame {
1106
1102
  return l % r;
1107
1103
  case "**":
1108
1104
  return l ** r;
1109
- case "==":
1110
- return bothObjects ? lhs === rhs : l == r;
1111
- case "!=":
1112
- return bothObjects ? lhs !== rhs : l != r;
1113
1105
  case "<":
1114
1106
  return l < r;
1115
1107
  case "<=":
@@ -1139,6 +1131,22 @@ class Frame {
1139
1131
  throw typeError(`Unsupported binary operator '${operator}'.`, node);
1140
1132
  }
1141
1133
  }
1134
+ // IsLooselyEqual: objects (including functions and tool references) compare by identity, and only a
1135
+ // data object facing a non-nullish primitive needs to coerce, so an opaque value is rejected only there.
1136
+ looselyEqual(lhs, rhs, node) {
1137
+ const lhsObject = lhs !== null && typeof lhs === "object";
1138
+ const rhsObject = rhs !== null && typeof rhs === "object";
1139
+ if (lhsObject === rhsObject)
1140
+ return lhsObject ? lhs === rhs : lhs == rhs;
1141
+ const object = lhsObject ? lhs : rhs;
1142
+ const primitive = lhsObject ? rhs : lhs;
1143
+ if (primitive === null || primitive === undefined)
1144
+ return false;
1145
+ if (!(object instanceof Obj) || isOpaque(object)) {
1146
+ throw invalidData("Binary operators require data values.", node);
1147
+ }
1148
+ return object.toPrimitive("default") == primitive;
1149
+ }
1142
1150
  evaluateLogicalExpression(node) {
1143
1151
  const operator = node.operator;
1144
1152
  return Effect.flatMap(this.evaluateExpression(node.left), (left) => {
@@ -1167,7 +1175,7 @@ class Frame {
1167
1175
  return !value;
1168
1176
  if (operator === "void")
1169
1177
  return undefined;
1170
- if (containsOpaqueReference(value)) {
1178
+ if (isOpaque(value)) {
1171
1179
  throw invalidData("Unary operators require data values.", node);
1172
1180
  }
1173
1181
  const operand = value instanceof Obj ? value.toPrimitive("number") : value;
@@ -1257,7 +1265,7 @@ class Frame {
1257
1265
  // CodeMode numeric coercion, not host Number(): null-prototype data objects would make
1258
1266
  // the host throw during ToPrimitive, and opaque runtime references must reject clearly.
1259
1267
  const operand = (current) => {
1260
- if (containsOpaqueReference(current)) {
1268
+ if (isOpaque(current)) {
1261
1269
  throw invalidData(`'${operator}' requires a data value.`, argument);
1262
1270
  }
1263
1271
  return coerceToNumber(current);
@@ -118,16 +118,7 @@ export const objectGlobal = (ctx) => {
118
118
  (_, args) => new Arr(builtins.Array, entries(enumerableSource(ctx, "Object.entries(...)", args[0])).map((entry) => new Arr(builtins.Array, entry))),
119
119
  ],
120
120
  ["hasOwn", 2, (_, args) => hasOwn(enumerableSource(ctx, "Object.hasOwn(...)", args[0]), propertyKey(args[1]))],
121
- [
122
- "is",
123
- 2,
124
- (_, args) => {
125
- if (containsOpaqueReference(args[0]) || containsOpaqueReference(args[1])) {
126
- throw invalidData("Object.is requires data values.");
127
- }
128
- return Object.is(args[0], args[1]);
129
- },
130
- ],
121
+ ["is", 2, (_, args) => Object.is(args[0], args[1])],
131
122
  ["assign", 2, (_, args) => objectAssign(ctx, args)],
132
123
  ["fromEntries", 1, (_, args) => objectFromEntries(ctx, args[0])],
133
124
  ]);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@opencode/codemode",
4
- "version": "0.0.0-dev-20097",
4
+ "version": "0.0.0-dev-20103",
5
5
  "description": "Effect-native confined code execution over schema-described tools",
6
6
  "type": "module",
7
7
  "license": "MIT",