@opencode/codemode 2.0.5 → 2.0.6

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.
@@ -5,7 +5,7 @@ import { formatLocation, PendingThrow, Throw, sourceLocation, typeError } from "
5
5
  import { containsRuntimeReference } from "./references.js";
6
6
  import { createErrorValue, isErrorType } from "./intrinsics.js";
7
7
  import { constructor, methods, prototypeFrom, receiver } from "./native.js";
8
- import { define, get, hidden, Arr, ErrorObj, Obj } from "./objects.js";
8
+ import { define, get, has, hidden, Arr, ErrorObj, Obj } from "./objects.js";
9
9
  import { formatValue } from "../stdlib/console.js";
10
10
  import { coerceToString } from "../stdlib/value.js";
11
11
  export const normalizeError = (error) => {
@@ -128,9 +128,17 @@ export const errorGlobal = (type, ctx) => {
128
128
  const prototype = builtins[type];
129
129
  const construct = (args, newTarget) => {
130
130
  const proto = prototypeFrom(newTarget, prototype);
131
- return type === "AggregateError"
131
+ const created = type === "AggregateError"
132
132
  ? constructAggregateErrorValue(ctx, args, proto)
133
133
  : Effect.sync(() => createErrorValue(proto, args[0] === undefined ? undefined : coerceToString(args[0])));
134
+ // ES2022 `new Error(message, { cause })`: installed only when the options object has the property at all.
135
+ const options = args[type === "AggregateError" ? 2 : 1];
136
+ if (!(options instanceof Obj) || !has(options, "cause"))
137
+ return created;
138
+ return Effect.map(created, (value) => {
139
+ define(value, "cause", get(options, "cause"), hidden);
140
+ return value;
141
+ });
134
142
  };
135
143
  const ctor = constructor(builtins, prototype, {
136
144
  name: type,
@@ -3,9 +3,9 @@ import { coerceToString } from "../stdlib/value.js";
3
3
  import { hooked } from "../tool-runtime.js";
4
4
  import { createErrorValue, isErrorType } from "./intrinsics.js";
5
5
  import { MAX_VALUE_DEPTH } from "./limits.js";
6
- import { Throw, typeError } from "./model.js";
6
+ import { PendingThrow, Throw, typeError } from "./model.js";
7
7
  import { fn } from "./native.js";
8
- import { Callable, define, entries, get, Arr, Bytes, DateObj, ErrorObj, GeneratorObj, MapObj, Obj, PromiseObj, RegExpObj, SetObj, URLObj, URLSearchParamsObj, } from "./objects.js";
8
+ import { Callable, define, entries, get, has, hidden, keys, Arr, Bytes, DateObj, ErrorObj, GeneratorObj, MapObj, Obj, PromiseObj, RegExpObj, SetObj, URLObj, URLSearchParamsObj, } from "./objects.js";
9
9
  import { describeValue } from "./references.js";
10
10
  /**
11
11
  * The global bindings of one run's extensions. Everything crossing the boundary is converted: plain data and
@@ -43,15 +43,30 @@ export const extensionGlobals = (ctx, extensions) => {
43
43
  value instanceof PromiseObj) {
44
44
  throw typeError(`${label} contains ${describeValue(value)}, which cannot be passed to an extension.`);
45
45
  }
46
+ if (seen.has(value))
47
+ throw typeError(`${label} contains a circular value.`);
48
+ seen.add(value);
46
49
  if (value instanceof ErrorObj) {
47
50
  const name = coerceToString(get(value, "name"));
48
51
  const message = get(value, "message");
49
52
  const text = message === undefined ? "" : coerceToString(message);
50
- return name === "AggregateError" ? new AggregateError([], text) : new (hostErrors.get(name) ?? Error)(text);
53
+ const copied = name === "AggregateError" ? new AggregateError([], text) : new (hostErrors.get(name) ?? Error)(text);
54
+ for (const key of new Set(["cause", ...keys(value)])) {
55
+ if (uncrossed.has(key) || !has(value, key))
56
+ continue;
57
+ const item = crossing(() => next(get(value, key)));
58
+ if (item === left)
59
+ continue;
60
+ Object.defineProperty(copied, key, {
61
+ value: item,
62
+ writable: true,
63
+ configurable: true,
64
+ enumerable: key !== "cause",
65
+ });
66
+ }
67
+ seen.delete(value);
68
+ return copied;
51
69
  }
52
- if (seen.has(value))
53
- throw typeError(`${label} contains a circular value.`);
54
- seen.add(value);
55
70
  const copied = value instanceof Arr
56
71
  ? value.items.map(next)
57
72
  : Object.fromEntries(entries(value)
@@ -68,6 +83,7 @@ export const extensionGlobals = (ctx, extensions) => {
68
83
  if (typeof value === "function")
69
84
  return wrap(value, label);
70
85
  if (value !== null && typeof value === "object") {
86
+ const next = (item, path) => fromHost(item, path, depth + 1, seen);
71
87
  if (value instanceof Date)
72
88
  return new DateObj(builtins.Date, value.getTime());
73
89
  if (value instanceof RegExp)
@@ -77,14 +93,27 @@ export const extensionGlobals = (ctx, extensions) => {
77
93
  if (value instanceof ArrayBuffer)
78
94
  return new Bytes(builtins.Uint8Array, new Uint8Array(value.slice(0)));
79
95
  if (value instanceof Error) {
80
- return createErrorValue(builtins[isErrorType(value.name) ? value.name : "Error"], value.message);
96
+ if (seen.has(value))
97
+ throw typeError(`${label} produced a circular value.`);
98
+ seen.add(value);
99
+ const copied = createErrorValue(builtins[isErrorType(value.name) ? value.name : "Error"], value.message);
100
+ const fields = value;
101
+ for (const key of new Set(["cause", ...Object.keys(value)])) {
102
+ if (uncrossed.has(key) || !(key in value) || typeof fields[key] === "function")
103
+ continue;
104
+ const item = crossing(() => next(fields[key], `${label}.${key}`));
105
+ if (item === left)
106
+ continue;
107
+ define(copied, key, item, key === "cause" ? hidden : undefined);
108
+ }
109
+ seen.delete(value);
110
+ return copied;
81
111
  }
82
112
  if (value instanceof URL)
83
113
  return new URLObj(builtins.URL, builtins.URLSearchParams, new URL(value.href));
84
114
  if (value instanceof URLSearchParams) {
85
115
  return new URLSearchParamsObj(builtins.URLSearchParams, new URLSearchParams(value));
86
116
  }
87
- const next = (item, path) => fromHost(item, path, depth + 1, seen);
88
117
  if (value instanceof Map) {
89
118
  const wrapped = new MapObj(builtins.Map);
90
119
  for (const [key, item] of value)
@@ -138,6 +167,23 @@ export const extensionGlobals = (ctx, extensions) => {
138
167
  });
139
168
  return extensions.flatMap((extension) => Object.entries(extension.globals).map(([name, value]) => [name, wrap(value, name, (args) => ({ extension: extension.name, name, args }))]));
140
169
  };
170
+ /**
171
+ * An error crosses as its name, message, `cause`, and own enumerable fields, such as Node's `code`, `errno`,
172
+ * `syscall`, and `path`. `stack` stays on its own side, and no field may shadow an Error method. A field that cannot
173
+ * cross (a socket, a handle, a function) is left behind so the error itself always arrives.
174
+ */
175
+ const uncrossed = new Set(["stack", "constructor", "toString", "__proto__"]);
176
+ const left = Symbol("left behind");
177
+ const crossing = (convert) => {
178
+ try {
179
+ return convert();
180
+ }
181
+ catch (reason) {
182
+ if (reason instanceof PendingThrow)
183
+ return left;
184
+ throw reason;
185
+ }
186
+ };
141
187
  const hostErrors = new Map([
142
188
  ["TypeError", TypeError],
143
189
  ["RangeError", RangeError],
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": "2.0.5",
4
+ "version": "2.0.6",
5
5
  "description": "Effect-native confined code execution over schema-described tools",
6
6
  "type": "module",
7
7
  "license": "MIT",