@poe-platform/safe-js 0.1.38 → 0.1.40

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/README.md CHANGED
@@ -168,6 +168,7 @@ This prints `2`. Evaluations share declarations, closures and object identity wi
168
168
  | --- | --- |
169
169
  | `extensions` | Explicit `defineExtension(...)` registrations; `[]`. Setup runs once, on first evaluation, not on construction or unused close. |
170
170
  | `grants` | Granted capability names; `[]`. Every requested capability must be granted before any extension setup runs. |
171
+ | `builtinOverrides` | Optional `{ console: "extension-name" }` authorizes that registered extension to replace only the builtin console. It must declare `console` and export a host object created in the realm. No overrides by default. |
171
172
  | `limits` | Positive integer caps: `extensions: 32`, `hostObjects: 1024`, `callbacks: 1024`, `guestReferences: 1024`, `cleanups: 1024`, `nestedEvaluations: 16`. Collection budgets also apply. |
172
173
 
173
174
  Ordinary host arguments/results are still copied. To preserve live native identity, explicitly create a host object. A guest function crossing to the host becomes an opaque callback: invoke it with `realm.invokeCallback(callback, { thisValue?, args? })`, then `realm.releaseCallback(callback)` when no longer needed. Callbacks and live objects cannot cross realms or survive close. For deferred arguments that must preserve guest identity, opt into retained references as described below.
@@ -209,6 +210,30 @@ try {
209
210
 
210
211
  The manifest requires `version: 1` and a nonempty `name`. Optional `capabilities` and `globals` are name arrays; `modules` maps module names to export-name arrays. Synchronous `setup(context)` returns `{ globals?, modules? }` matching those declarations exactly. Module exports use the existing record/Map registry. Duplicate names, incompatible versions, missing grants and conflicts with intrinsics or caller values are rejected before setup. Accessor-based declarations and asynchronous factories are unsupported.
211
212
 
213
+ **Sharing an owned console.** An extension can expose the same host object as `console`, `window.console` and `self.console`:
214
+
215
+ ```js
216
+ const browser = defineExtension({
217
+ manifest: { version: 1, name: "browser", globals: ["console", "window", "self"] },
218
+ setup(context) {
219
+ const console = context.createHostObject({ methods: {
220
+ log: (...args) => journal.log(...args),
221
+ warn: (...args) => journal.warn(...args)
222
+ } });
223
+ const window = context.createHostObject({ properties: {
224
+ console: { get: () => console }
225
+ } });
226
+ return { globals: { console, window, self: window } };
227
+ }
228
+ });
229
+ const realm = createRealm({
230
+ extensions: [browser],
231
+ builtinOverrides: { console: "browser" }
232
+ });
233
+ ```
234
+
235
+ Supply your own bounded `journal`; this does not add browser console behavior. Without authorization, registration still fails before setup. Caller-provided console bindings, another extension claiming console, unknown override names and missing capability grants still reject. JSON and other intrinsics cannot be overridden this way. The replacement uses normal capability accounting and revocation; its calls do not also go to the builtin `sink`. Close the realm when finished, as in the example above.
236
+
212
237
  | Context member | Contract |
213
238
  | --- | --- |
214
239
  | `signal` | Realm cancellation signal; aborted on close or failure. |
@@ -303,7 +328,7 @@ For one-shot use, `run(source, { extensions, grants, ... })` accepts the same re
303
328
  | --- | --- |
304
329
  | `bindings` | Global input values and host functions; none by default. |
305
330
  | `modules` | Module names mapped to export records or Maps; none by default. |
306
- | `extensions`, `grants`, `limits` | Opt into a one-shot extension realm; see the supported options and lifetime rules above. |
331
+ | `extensions`, `grants`, `builtinOverrides`, `limits` | Opt into a one-shot extension realm; see the supported options and lifetime rules above. |
307
332
  | `budget` | A `Budget` instance. Without one, only the default call-depth limit of 1,000 is configured. |
308
333
  | `signal` | Host `AbortSignal` for cancellation. |
309
334
  | `filename` | Diagnostic filename; defaults to `<input>`. |
@@ -30844,6 +30844,14 @@ var RealmState = class {
30844
30844
  this.modules = readModules(options.modules);
30845
30845
  const grants = new Set(readStringList(options.grants ?? [], "Realm grants"));
30846
30846
  for (const extension of this.extensions) getExtensionSetup(extension);
30847
+ const overrides = readDataRecord(options.builtinOverrides === void 0 ? {} : options.builtinOverrides, "Builtin overrides");
30848
+ for (const [name, extensionName] of Object.entries(overrides)) {
30849
+ if (name !== "console" || typeof extensionName !== "string" || extensionName.length === 0 || extensionName.length > 256)
30850
+ throw new TypeError("Builtin overrides only supports console with a nonempty extension name.");
30851
+ if (!this.extensions.some((extension) => extension.manifest.name === extensionName && extension.manifest.globals?.includes(name)))
30852
+ throw new TypeError("Console override requires a registered extension declaring console.");
30853
+ this.consoleExtension = extensionName;
30854
+ }
30847
30855
  this.budget = options.budget ?? new Budget({ maxCallDepth: 1e3 });
30848
30856
  this.lease = this.budget.acquireCompileOwner(true);
30849
30857
  this.compilation = new CompileScope(this.lease.owner);
@@ -30864,7 +30872,10 @@ var RealmState = class {
30864
30872
  random: createReplayableRandom({ seed: options.randomSeed }).next
30865
30873
  });
30866
30874
  const names = /* @__PURE__ */ new Set();
30867
- const globals = /* @__PURE__ */ new Set([...Object.keys(this.builtinBindings), ...Object.keys(this.globals)]);
30875
+ const globals = /* @__PURE__ */ new Set([
30876
+ ...Object.keys(this.builtinBindings).filter((name) => name !== "console" || this.consoleExtension === void 0),
30877
+ ...Object.keys(this.globals)
30878
+ ]);
30868
30879
  const modules = new Map(
30869
30880
  Object.entries(this.modules).map(([name, exports]) => [name, new Set(Object.keys(exports))])
30870
30881
  );
@@ -30878,6 +30889,8 @@ var RealmState = class {
30878
30889
  throw new TypeError(`Missing grant '${capability}' for extension '${manifest.name}'.`);
30879
30890
  }
30880
30891
  for (const name of manifest.globals ?? []) {
30892
+ if (name === "console" && this.consoleExtension !== void 0 && manifest.name !== this.consoleExtension)
30893
+ throw new TypeError(`Conflicting global 'console': replacement is authorized only for '${this.consoleExtension}'.`);
30881
30894
  if (globals.has(name)) throw new TypeError(`Conflicting global '${name}'.`);
30882
30895
  globals.add(name);
30883
30896
  }
@@ -30912,6 +30925,7 @@ var RealmState = class {
30912
30925
  bridge;
30913
30926
  limits;
30914
30927
  extensions;
30928
+ consoleExtension;
30915
30929
  cleanups = [];
30916
30930
  callbacks = /* @__PURE__ */ new Map();
30917
30931
  pendingCallbacks = /* @__PURE__ */ new Set();
@@ -31342,6 +31356,8 @@ var RealmState = class {
31342
31356
  );
31343
31357
  const modules = readModules(exports.modules);
31344
31358
  assertNames(Object.keys(globals), extension.manifest.globals ?? [], "global");
31359
+ if (extension.manifest.name === this.consoleExtension && !this.hostObjects.has(globals.console))
31360
+ throw new TypeError("Console replacement must be a host object created in this realm.");
31345
31361
  assertNames(Object.keys(modules), Object.keys(extension.manifest.modules ?? {}), "module");
31346
31362
  for (const [name, values] of Object.entries(modules)) {
31347
31363
  assertNames(Object.keys(values), extension.manifest.modules?.[name] ?? [], "module export");
@@ -31587,6 +31603,7 @@ function readRealmOptions(value, oneShot = false) {
31587
31603
  "bindings",
31588
31604
  "modules",
31589
31605
  "extensions",
31606
+ "builtinOverrides",
31590
31607
  "grants",
31591
31608
  "budget",
31592
31609
  "signal",
@@ -32170,7 +32187,8 @@ var UnhandledRejectionError = class extends Error {
32170
32187
  };
32171
32188
  var DEFAULT_MAX_CALL_DEPTH = 1e3;
32172
32189
  function run(source, options = {}) {
32173
- if (options.extensions !== void 0) return runWithExtensions(source, options);
32190
+ if (options.extensions !== void 0 || options.builtinOverrides !== void 0)
32191
+ return runWithExtensions(source, options);
32174
32192
  const lifecycle = {
32175
32193
  hostCallbackDepth: 0,
32176
32194
  hostCallbackContext: new AsyncLocalStorage7()
@@ -32743,4 +32761,4 @@ export {
32743
32761
  FileSnapshotBackend,
32744
32762
  run
32745
32763
  };
32746
- //# sourceMappingURL=chunk-BW4SYRDC.js.map
32764
+ //# sourceMappingURL=chunk-CZNLD2Z3.js.map