@elaraai/e3-ui-components 1.0.25 → 1.0.27

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/dist/index.js CHANGED
@@ -18,16 +18,17 @@ var __privateWrapper = (obj, member, setter, getter) => ({
18
18
  }
19
19
  });
20
20
  var _focused, _cleanup, _setup, _a, _provider, _providerCalled, _b, _online, _cleanup2, _setup2, _c, _gcTimeout, _d, _initialState, _revertState, _cache, _client, _retryer, _defaultOptions, _abortSignalConsumed, _Query_instances, isInitialPausedFetch_fn, dispatch_fn, _e, _client2, _observers, _mutationCache, _retryer2, _Mutation_instances, dispatch_fn2, _f, _mutations, _scopes, _mutationId, _g, _queries, _h, _queryCache, _mutationCache2, _defaultOptions2, _queryDefaults, _mutationDefaults, _mountCount, _unsubscribeFocus, _unsubscribeOnline, _i, _j;
21
- import { variant, EastError, ConflictError, equalFor, SortedMap, toEastTypeValue, PatchType, diffFor, applyFor, encodeBeast2For, decodeBeast2For, BlobType, compareFor, none as none$1, some, walkPatch, pathToString, pathDisplay, printFor, isVariant, prunePatchFor, detectConflictsFor, mergeWithResolutionsFor, validatePatchFor, fromEastTypeValue, OptionType, StringType } from "@elaraai/east";
21
+ import { variant, SortedMap, compareFor, EastError, fromEastTypeValue, East, none as none$1, some, OptionType, BooleanType, NullType, ConflictError, equalFor, toEastTypeValue, PatchType, diffFor, applyFor, encodeBeast2For, decodeBeast2For, BlobType, StructType, StringType, walkPatch, pathToString, pathDisplay, printFor, isVariant, prunePatchFor, detectConflictsFor, mergeWithResolutionsFor, validatePatchFor, ArrayType } from "@elaraai/east";
22
22
  import { EastTypeType } from "@elaraai/east/internal";
23
- import { bindPlatformFn, funcBindPlatformFn, recordBindPlatformFn, Diff, Ontology, FuncBindHandleType, Experiment, decisionBindPlatformFn, DecisionQueue, DecisionJournal, decodeManifest } from "@elaraai/e3-ui/internal";
24
- import { registerReactiveTracker, registerPlatformImplementation } from "@elaraai/east-ui-components/platform";
25
- import { workspaceStatus, datasetListAt, datasetList, dataflowExecuteLaunch, datasetSet, datasetGet, workspaceFunctionCall, workspaceFunctionList, workspaceRecordHistory, workspaceRecordMutate, workspaceRecordDescribe, ApiError, repoCreate, repoGc, repoGcStart, repoGcStatus, repoList, repoRemove, repoStatus, packageExport, packageGet, packageImport, packageList, packageRemove, workspaceCreate, workspaceDeploy, workspaceExport, workspaceGet, workspaceList, workspaceRemove, datasetGetStatus, datasetListRecursive, datasetListRecursivePaths, datasetListWithStatus, taskExecutionList, taskGet, taskList, dataflowCancel, dataflowExecute, dataflowExecutePoll, dataflowGraph, taskLogs } from "@elaraai/e3-api-client";
23
+ import { DataBindModeType, DataBindPrimitives, bindPlatformFn, FuncBindPrimitives, FuncErrorType, FuncStatusType, funcBindPlatformFn, RecordBindPrimitives, recordBindPlatformFn, Diff, Ontology, FuncBindHandleType, Experiment, decisionBindPlatformFn, DecisionBindPrimitives, DecisionConstraintType, DiffBindingType, CommitStateType, VerdictType, AnswerType, judgementInputType, DecisionType, DecisionQueue, DecisionJournal, decodeManifest } from "@elaraai/e3-ui/internal";
24
+ import { registerReactiveTracker, registerPlatformImplementation, getSomeorUndefined as getSomeorUndefined$1, StateRuntime, buildSliceHandle, DEFAULT_SLICE_STATE, getRegisteredPlatformImplementations } from "@elaraai/east-ui-components/platform";
25
+ import { TreePathType, DatasetStatusType } from "@elaraai/e3-types";
26
+ import { workspaceStatus, datasetListAt, datasetList, dataflowExecuteLaunch, datasetSet, datasetGet, ApiError, workspaceFunctionCall, workspaceFunctionList, workspaceRecordHistory, workspaceRecordMutate, workspaceRecordDescribe, repoCreate, repoGc, repoGcStart, repoGcStatus, repoList, repoRemove, repoStatus, packageExport, packageGet, packageImport, packageList, packageRemove, workspaceCreate, workspaceDeploy, workspaceExport, workspaceGet, workspaceList, workspaceRemove, datasetGetStatus, datasetListRecursive, datasetListRecursivePaths, datasetListWithStatus, taskExecutionList, taskGet, taskList, dataflowCancel, dataflowExecute, dataflowExecutePoll, dataflowGraph, taskLogs } from "@elaraai/e3-api-client";
26
27
  import { jsx as jsx$1, Fragment, jsxs } from "react/jsx-runtime";
27
28
  import * as React from "react";
28
29
  import React__default, { useId, useMemo, useContext, createContext, useRef, useEffect, useState, useCallback, useSyncExternalStore, memo, forwardRef, useLayoutEffect, Fragment as Fragment$1, Component } from "react";
29
30
  import { Box, Flex, Text, HStack, Input, useSlotRecipe, useRecipe, VStack, useToken, Drawer, Portal, Field, NativeSelect, Textarea, chakra, Tooltip, SegmentGroup, Menu, HoverCard, useChakraContext, Spinner, Code, Clipboard, IconButton, Badge, Button, Tabs, useTabs } from "@chakra-ui/react";
30
- import { implementUIComponent, getSomeorUndefined, formatPredicate, SliceEditPopover, SlicePredicateBuilder, StateRuntime, buildSliceHandle, DEFAULT_SLICE_STATE, registerPlatformImplementation as registerPlatformImplementation$1, EastChakraSelect, EastChakraStringInput, EastChakraDateTimeInput, EastChakraFloatInput, EastChakraIntegerInput, formatTick, useSliceReactivity, SliceRailCluster, EastChakraComponent, ClauseChip, StateImpl, OverlayImpl, createUIStore, UIStoreProvider } from "@elaraai/east-ui-components";
31
+ import { implementUIComponent, getSomeorUndefined, formatPredicate, SliceEditPopover, SlicePredicateBuilder, EastChakraSelect, EastChakraStringInput, EastChakraDateTimeInput, EastChakraFloatInput, EastChakraIntegerInput, formatTick, useSliceReactivity, SliceRailCluster, EastChakraComponent, ClauseChip, StateImpl, NavImpl, SliceImpl, SliceApplyImpl, OverlayImpl, ClipboardImpl, DownloadImpl, ShareImpl, createUIStore, UIStoreProvider } from "@elaraai/east-ui-components";
31
32
  import { createPortal } from "react-dom";
32
33
  import { sliceMatches } from "@elaraai/east-ui/internal";
33
34
  import { QueryClient as QueryClient$1, QueryClientProvider, useMutation, useQuery } from "@tanstack/react-query";
@@ -2294,6 +2295,11 @@ class ReactiveDatasetCache {
2294
2295
  });
2295
2296
  } catch (error3) {
2296
2297
  if (isCancelledError(error3)) return;
2298
+ if (isDatasetUnassignedError(error3)) {
2299
+ this.statuses.set(key, variant("unset", null));
2300
+ this.notifyChange(key);
2301
+ return;
2302
+ }
2297
2303
  throw error3;
2298
2304
  }
2299
2305
  if (this.destroyed) {
@@ -2591,6 +2597,9 @@ class ReactiveDatasetCache {
2591
2597
  function isCancelledError(error3) {
2592
2598
  return error3 instanceof CancelledError;
2593
2599
  }
2600
+ function isDatasetUnassignedError(error3) {
2601
+ return error3 instanceof ApiError && error3.code === "dataset_unassigned";
2602
+ }
2594
2603
  function createReactiveDatasetCache(config2, api3, clock2) {
2595
2604
  return new ReactiveDatasetCache(config2, api3, clock2);
2596
2605
  }
@@ -2908,6 +2917,17 @@ class BindRuntime {
2908
2917
  __publicField(this, "fallbackWarningEmitted", /* @__PURE__ */ new Set());
2909
2918
  // List cache helpers.
2910
2919
  __publicField(this, "listCache", /* @__PURE__ */ new Map());
2920
+ // Compiled-handle cache (issue #106 perf). The bind impl compiles N
2921
+ // East.function methods per bind, and binds re-run on every reactive frame.
2922
+ // The compiled IR is a pure function of (sourceType, sourcePath, patch, mode)
2923
+ // — same key ⇒ same methods — and the methods resolve cache/workspace LIVE,
2924
+ // so a cached handle still re-binds correctly across renders / cache swaps.
2925
+ // Keyed *structurally* (each bind builds a fresh EastTypeValue from IR, so a
2926
+ // by-identity cache would miss every render — same reason as helpersCache).
2927
+ __publicField(this, "handleCache", new SortedMap(
2928
+ void 0,
2929
+ compareFor(EastTypeType)
2930
+ ));
2911
2931
  this.staged = staged;
2912
2932
  this.staged.onPersistError((_key, err) => this.emitWriteError(err));
2913
2933
  }
@@ -2938,6 +2958,7 @@ class BindRuntime {
2938
2958
  clearCache() {
2939
2959
  this.cache = null;
2940
2960
  this.clearPendingWrites();
2961
+ this.handleCache.clear();
2941
2962
  }
2942
2963
  // ----- pending writes queue -------------------------------------------
2943
2964
  /** Subscribe to write-queue errors. Returns an unsubscribe fn. */
@@ -3064,13 +3085,14 @@ class BindRuntime {
3064
3085
  }
3065
3086
  // ----- bind-handle construction --------------------------------------
3066
3087
  /**
3067
- * Build a bind handle for a single `(sourcePath, mode, patch?)`
3068
- * triple. Public so tests can call this directly without going
3069
- * through East's compile/dispatch pipeline.
3088
+ * Build a `Data.bind` handle. Its methods are thin IR-bearing
3089
+ * `East.function`s over the `data_*` primitives, capturing only the
3090
+ * plain-data `{source, patch, mode}` descriptor — so the handle is ordinary
3091
+ * serializable East data (issue #106) and re-binds to the decoder's cache.
3092
+ * Public so tests can call it directly without East's compile/dispatch.
3070
3093
  *
3071
- * @throws `EastError` if the cache is uninitialized, the
3072
- * workspace is not configured, or `allowed` is non-null and any
3073
- * referenced path is not in it.
3094
+ * @throws `EastError` if the cache is uninitialized, the workspace is not
3095
+ * configured, or `allowed` is non-null and any referenced path is not in it.
3074
3096
  */
3075
3097
  buildBindHandle(sourceType, sourcePath, patchPath, mode, allowed) {
3076
3098
  const cache3 = this.requireCache();
@@ -3094,6 +3116,106 @@ class BindRuntime {
3094
3116
  }
3095
3117
  }
3096
3118
  }
3119
+ const { patchType } = getBindingHelpers(sourceType);
3120
+ this.registerBindingTypes(ws, sourcePath, {
3121
+ workspace: ws,
3122
+ sourceType,
3123
+ patchType,
3124
+ mode,
3125
+ hasPatchDataset: patchPath !== void 0
3126
+ });
3127
+ const descKey = `${datasetPathToString(sourcePath)}\0${patchPath !== void 0 ? datasetPathToString(patchPath) : ""}\0${mode}`;
3128
+ let byDesc = this.handleCache.get(sourceType);
3129
+ if (byDesc) {
3130
+ const hit = byDesc.get(descKey);
3131
+ if (hit) return hit;
3132
+ } else {
3133
+ byDesc = /* @__PURE__ */ new Map();
3134
+ this.handleCache.set(sourceType, byDesc);
3135
+ }
3136
+ const T2 = fromEastTypeValue(sourceType);
3137
+ const srcExpr = East.value(sourcePath, TreePathType);
3138
+ const patchExpr = East.value(patchPath !== void 0 ? some(patchPath) : none$1, OptionType(TreePathType));
3139
+ const modeExpr = East.value(variant(mode, null), DataBindModeType);
3140
+ const platform = this.buildPrimitives();
3141
+ const P2 = DataBindPrimitives;
3142
+ const handle = {
3143
+ read: East.compile(East.function([], T2, ($3) => {
3144
+ $3.return(P2.read([T2], srcExpr, patchExpr, modeExpr));
3145
+ }), platform),
3146
+ write: East.compile(East.function([T2], NullType, ($3, v3) => {
3147
+ $3.return(P2.write([T2], srcExpr, patchExpr, modeExpr, v3));
3148
+ }), platform),
3149
+ writeAndStart: East.compile(East.function([T2], NullType, ($3, v3) => {
3150
+ $3.return(P2.writeAndStart([T2], srcExpr, patchExpr, modeExpr, v3));
3151
+ }), platform),
3152
+ start: East.compile(East.function([], NullType, ($3) => {
3153
+ $3.return(P2.start([T2], srcExpr, patchExpr, modeExpr));
3154
+ }), platform),
3155
+ source: East.compile(East.function([], T2, ($3) => {
3156
+ $3.return(P2.source([T2], srcExpr, patchExpr, modeExpr));
3157
+ }), platform),
3158
+ pending: East.compile(East.function([], BooleanType, ($3) => {
3159
+ $3.return(P2.pending([T2], srcExpr, patchExpr, modeExpr));
3160
+ }), platform),
3161
+ commit: East.compile(East.function([], NullType, ($3) => {
3162
+ $3.return(P2.commit([T2], srcExpr, patchExpr, modeExpr));
3163
+ }), platform),
3164
+ discard: East.compile(East.function([], NullType, ($3) => {
3165
+ $3.return(P2.discard([T2], srcExpr, patchExpr, modeExpr));
3166
+ }), platform),
3167
+ has: East.compile(East.function([], BooleanType, ($3) => {
3168
+ $3.return(P2.has([T2], srcExpr, patchExpr, modeExpr));
3169
+ }), platform),
3170
+ status: East.compile(East.function([], DatasetStatusType, ($3) => {
3171
+ $3.return(P2.status([T2], srcExpr, patchExpr, modeExpr));
3172
+ }), platform),
3173
+ binding: {
3174
+ source: sourcePath,
3175
+ patch: patchPath !== void 0 ? some(patchPath) : none$1,
3176
+ mode: variant(mode, null)
3177
+ }
3178
+ };
3179
+ byDesc.set(descKey, handle);
3180
+ return handle;
3181
+ }
3182
+ /**
3183
+ * The low-level primitives backing handle methods, bound to THIS runtime.
3184
+ * Registered globally (extension registry) and included by the scoped
3185
+ * platform (e3 `ui()` tasks) so a decoded handle re-binds here. Each impl
3186
+ * recovers the `{source, patch, mode}` descriptor from its args and delegates
3187
+ * to {@link buildRawHandle}, reusing the full mode×patch logic verbatim.
3188
+ */
3189
+ buildPrimitives() {
3190
+ const raw = (sourceType, sourceArg, patchOpt, modeVariant) => {
3191
+ const { patchPath, mode } = resolveOptions(patchOpt, modeVariant);
3192
+ return this.buildRawHandle(sourceType, sourceArg, patchPath, mode);
3193
+ };
3194
+ const P2 = DataBindPrimitives;
3195
+ return [
3196
+ P2.read.implement((t4) => (s2, p2, m2) => raw(t4, s2, p2, m2).read()),
3197
+ P2.source.implement((t4) => (s2, p2, m2) => raw(t4, s2, p2, m2).source()),
3198
+ P2.write.implement((t4) => (s2, p2, m2, v3) => raw(t4, s2, p2, m2).write(v3)),
3199
+ P2.writeAndStart.implement((t4) => (s2, p2, m2, v3) => raw(t4, s2, p2, m2).writeAndStart(v3)),
3200
+ P2.start.implement((t4) => (s2, p2, m2) => raw(t4, s2, p2, m2).start()),
3201
+ P2.pending.implement((t4) => (s2, p2, m2) => raw(t4, s2, p2, m2).pending()),
3202
+ P2.commit.implement((t4) => (s2, p2, m2) => raw(t4, s2, p2, m2).commit()),
3203
+ P2.discard.implement((t4) => (s2, p2, m2) => raw(t4, s2, p2, m2).discard()),
3204
+ P2.has.implement((t4) => (s2, p2, m2) => raw(t4, s2, p2, m2).has()),
3205
+ P2.status.implement((t4) => (s2, p2, m2) => raw(t4, s2, p2, m2).status())
3206
+ ];
3207
+ }
3208
+ /**
3209
+ * Build the raw (host-closure) bind handle for a single
3210
+ * `(sourcePath, mode, patch?)` triple. The mode×patch matrix lives here; the
3211
+ * serializable {@link buildBindHandle} wraps each method as an `East.function`
3212
+ * over a primitive that delegates back here. Re-registers binding types on
3213
+ * every call (decode self-heal). Public so tests can drive the raw closures.
3214
+ */
3215
+ buildRawHandle(sourceType, sourcePath, patchPath, mode) {
3216
+ const cache3 = this.requireCache();
3217
+ const ws = cache3.getConfig().workspace;
3218
+ if (!ws) throw new Error("ReactiveDatasetCache workspace not configured");
3097
3219
  const { patchType, decodeT, encodeT, decodePatch, encodePatch, apply: apply2, diff: diff2 } = getBindingHelpers(sourceType);
3098
3220
  this.registerBindingTypes(ws, sourcePath, {
3099
3221
  workspace: ws,
@@ -3408,10 +3530,16 @@ function awaitPendingWrites() {
3408
3530
  function clearPendingWrites() {
3409
3531
  defaultBindRuntime.clearPendingWrites();
3410
3532
  }
3411
- const BindPlatform = [defaultBindRuntime.buildPlatform(null)];
3533
+ const BindPlatform = [
3534
+ defaultBindRuntime.buildPlatform(null),
3535
+ ...defaultBindRuntime.buildPrimitives()
3536
+ ];
3412
3537
  function createScopedBindPlatform(manifest) {
3413
3538
  const allowed = new Set(manifest.paths.map((p2) => datasetPathToString(p2)));
3414
- return [defaultBindRuntime.buildPlatform(allowed)];
3539
+ return [
3540
+ defaultBindRuntime.buildPlatform(allowed),
3541
+ ...defaultBindRuntime.buildPrimitives()
3542
+ ];
3415
3543
  }
3416
3544
  async function preloadReactiveDatasetList(workspace, path2) {
3417
3545
  return defaultBindRuntime.preloadList(workspace, path2);
@@ -3641,6 +3769,11 @@ class FuncRuntime extends TrackedChannelStore {
3641
3769
  __publicField(this, "workspace", null);
3642
3770
  // Signature lists are fetched once per workspace and cached.
3643
3771
  __publicField(this, "signatureLists", /* @__PURE__ */ new Map());
3772
+ // Compiled-handle cache (issue #106 perf): buildHandle compiles 6 East.functions
3773
+ // per bind, and binds re-run every reactive frame. The method IR is a pure
3774
+ // function of the function name (which fixes the handle type), and the methods
3775
+ // resolve workspace/api LIVE, so a cached handle still re-binds. Cleared on clear().
3776
+ __publicField(this, "handleCache", /* @__PURE__ */ new Map());
3644
3777
  }
3645
3778
  createEntry() {
3646
3779
  return { status: "idle", launchSeq: 0 };
@@ -3658,6 +3791,7 @@ class FuncRuntime extends TrackedChannelStore {
3658
3791
  this.workspace = null;
3659
3792
  this.clearChannels();
3660
3793
  this.signatureLists.clear();
3794
+ this.handleCache.clear();
3661
3795
  }
3662
3796
  /** The deployed signature list for a workspace (fetched once). */
3663
3797
  signatures(workspace) {
@@ -3761,9 +3895,15 @@ class FuncRuntime extends TrackedChannelStore {
3761
3895
  }
3762
3896
  })();
3763
3897
  }
3764
- /** Build the handle value for one `Func.bind` platform evaluation. */
3765
- buildHandle(handleType, name) {
3766
- const sig = signatureOfFuncHandleType(handleType);
3898
+ /**
3899
+ * The 6 low-level primitives backing handle methods, bound to THIS runtime.
3900
+ * Registered globally (extension registry) and included by the scoped
3901
+ * platform (e3 `ui()` tasks) so a decoded handle re-binds to whatever runtime
3902
+ * resolves the primitives on the decode side. The host effects (launch /
3903
+ * channel reads + reactive tracking) live here, keyed only on the plain-data
3904
+ * function name; the output type rides as a type-arg.
3905
+ */
3906
+ buildPrimitives() {
3767
3907
  const runtime = this;
3768
3908
  const resolveWorkspace = () => {
3769
3909
  if (!runtime.workspace) {
@@ -3771,42 +3911,91 @@ class FuncRuntime extends TrackedChannelStore {
3771
3911
  }
3772
3912
  return runtime.workspace;
3773
3913
  };
3774
- const channel = () => {
3914
+ const channel = (name) => {
3775
3915
  const key = funcChannelKey(resolveWorkspace(), name);
3776
3916
  return { key, entry: runtime.entry(key) };
3777
3917
  };
3778
- return {
3779
- call: (...args) => {
3780
- runtime.launch(resolveWorkspace(), name, sig, args);
3918
+ return [
3919
+ // call: recover per-arg input types from the ArgsStruct type-arg + the
3920
+ // values from the passed struct (field order), and the output type from O.
3921
+ FuncBindPrimitives.call.implement((argsStructType, outputType) => (nameArg, argsStruct) => {
3922
+ const fields = argsStructType.value;
3923
+ const obj = argsStruct;
3924
+ const inputs = fields.map((f2) => f2.type);
3925
+ const args = fields.map((f2) => obj[f2.name]);
3926
+ runtime.launch(resolveWorkspace(), nameArg, { inputs, output: outputType }, args);
3781
3927
  return null;
3782
- },
3783
- read: () => {
3784
- const { key, entry } = channel();
3928
+ }),
3929
+ FuncBindPrimitives.read.implement((_outputType) => (nameArg) => {
3930
+ const { key, entry } = channel(nameArg);
3785
3931
  runtime.track(key);
3786
3932
  return entry.result !== void 0 ? variant("some", entry.result) : variant("none", null);
3787
- },
3788
- status: () => {
3789
- const { key, entry } = channel();
3933
+ }),
3934
+ FuncBindPrimitives.status.implement((nameArg) => {
3935
+ const { key, entry } = channel(nameArg);
3790
3936
  runtime.track(key);
3791
3937
  return variant(entry.status, null);
3792
- },
3793
- error: () => {
3794
- const { key, entry } = channel();
3938
+ }),
3939
+ FuncBindPrimitives.error.implement((nameArg) => {
3940
+ const { key, entry } = channel(nameArg);
3795
3941
  runtime.track(key);
3796
3942
  return entry.status === "failed" && entry.error !== void 0 ? variant("some", entry.error) : variant("none", null);
3797
- },
3798
- pending: () => {
3799
- const { key, entry } = channel();
3943
+ }),
3944
+ FuncBindPrimitives.pending.implement((nameArg) => {
3945
+ const { key, entry } = channel(nameArg);
3800
3946
  runtime.track(key);
3801
3947
  return entry.status === "running";
3802
- },
3803
- cancel: () => {
3804
- const { key } = channel();
3948
+ }),
3949
+ FuncBindPrimitives.cancel.implement((nameArg) => {
3950
+ const { key } = channel(nameArg);
3805
3951
  runtime.cancelChannel(key);
3806
3952
  return null;
3807
- },
3953
+ })
3954
+ ];
3955
+ }
3956
+ /**
3957
+ * Build the handle value for one `Func.bind` evaluation. The methods are
3958
+ * thin IR-bearing `East.function`s over {@link buildPrimitives}, capturing
3959
+ * only the function name (and the signature types via type-args) — so the
3960
+ * handle is ordinary serializable East data (issue #106).
3961
+ */
3962
+ buildHandle(handleType, name) {
3963
+ const cached = this.handleCache.get(name);
3964
+ if (cached) return cached;
3965
+ const sig = signatureOfFuncHandleType(handleType);
3966
+ const inputsEast = sig.inputs.map((t4) => fromEastTypeValue(t4));
3967
+ const outputEast = fromEastTypeValue(sig.output);
3968
+ const ArgsStruct = StructType(Object.fromEntries(inputsEast.map((t4, i) => [`arg${i}`, t4])));
3969
+ const nameExpr = East.value(name, StringType);
3970
+ const platform = this.buildPrimitives();
3971
+ const { call, read, status, error: error3, pending, cancel } = FuncBindPrimitives;
3972
+ const handle = {
3973
+ call: East.compile(East.function(inputsEast, NullType, ($3, ...args) => {
3974
+ const obj = {};
3975
+ args.forEach((a2, i) => {
3976
+ obj[`arg${i}`] = a2;
3977
+ });
3978
+ $3.return(call([ArgsStruct, outputEast], nameExpr, East.value(obj, ArgsStruct)));
3979
+ }), platform),
3980
+ read: East.compile(East.function([], OptionType(outputEast), ($3) => {
3981
+ $3.return(read([outputEast], nameExpr));
3982
+ }), platform),
3983
+ status: East.compile(East.function([], FuncStatusType, ($3) => {
3984
+ $3.return(status(nameExpr));
3985
+ }), platform),
3986
+ error: East.compile(East.function([], OptionType(FuncErrorType), ($3) => {
3987
+ $3.return(error3(nameExpr));
3988
+ }), platform),
3989
+ pending: East.compile(East.function([], BooleanType, ($3) => {
3990
+ $3.return(pending(nameExpr));
3991
+ }), platform),
3992
+ cancel: East.compile(East.function([], NullType, ($3) => {
3993
+ $3.return(cancel(nameExpr));
3994
+ }), platform),
3808
3995
  binding: { name }
3809
3996
  };
3997
+ this.handleCache.set(name, handle);
3998
+ return handle;
3810
3999
  }
3811
4000
  // ----- platform building -------------------------------------------------
3812
4001
  /** Build a `Func.bind` PlatformFunction bound to this runtime. Pass
@@ -3833,9 +4022,15 @@ function initializeFunctionApi(api3, workspace) {
3833
4022
  function clearFunctionApi() {
3834
4023
  defaultFuncRuntime.clear();
3835
4024
  }
3836
- const FuncPlatform = [defaultFuncRuntime.buildPlatform(null)];
4025
+ const FuncPlatform = [
4026
+ defaultFuncRuntime.buildPlatform(null),
4027
+ ...defaultFuncRuntime.buildPrimitives()
4028
+ ];
3837
4029
  function createScopedFuncPlatform(functions2) {
3838
- return [defaultFuncRuntime.buildPlatform(new Set(functions2))];
4030
+ return [
4031
+ defaultFuncRuntime.buildPlatform(new Set(functions2)),
4032
+ ...defaultFuncRuntime.buildPrimitives()
4033
+ ];
3839
4034
  }
3840
4035
  function createInMemoryFunctionApi(functions2) {
3841
4036
  const defs2 = functions2.map((def) => ({
@@ -7645,6 +7840,12 @@ class RecordRuntime extends TrackedChannelStore {
7645
7840
  // otherwise spin); cleared on the next commit / on clear() so a later
7646
7841
  // mutation retries.
7647
7842
  __publicField(this, "historyFailed", /* @__PURE__ */ new Set());
7843
+ // Compiled-handle cache (issue #106 perf): buildHandle compiles 8 + (one per
7844
+ // mutation) East.functions per bind, and binds re-run every reactive frame.
7845
+ // The method IR is a pure function of the record name (which fixes the handle
7846
+ // type), and the methods resolve cache/api/ws LIVE, so a cached handle still
7847
+ // re-binds. Cleared on clear().
7848
+ __publicField(this, "handleCache", /* @__PURE__ */ new Map());
7648
7849
  }
7649
7850
  createEntry() {
7650
7851
  return { status: "idle", launchSeq: 0 };
@@ -7667,6 +7868,7 @@ class RecordRuntime extends TrackedChannelStore {
7667
7868
  this.histories.clear();
7668
7869
  this.historyInFlight.clear();
7669
7870
  this.historyFailed.clear();
7871
+ this.handleCache.clear();
7670
7872
  }
7671
7873
  resolveWorkspace() {
7672
7874
  if (!this.workspace) {
@@ -7785,89 +7987,163 @@ class RecordRuntime extends TrackedChannelStore {
7785
7987
  if (current && current.inflight === run) delete current.inflight;
7786
7988
  });
7787
7989
  }
7788
- /** Build the handle value for one `Record.bind` platform evaluation. */
7789
- buildHandle(handleType, name) {
7790
- const sig = signatureOfRecordHandleType(handleType);
7791
- const decodeState = decodeBeast2For(sig.stateType);
7990
+ /**
7991
+ * The low-level primitives backing handle methods, bound to THIS runtime.
7992
+ * Registered globally (extension registry) and included by the scoped
7993
+ * platform (e3 `ui()` tasks) so a decoded handle re-binds here. The host
7994
+ * effects (dataset-cache reads + reactive tracking + mutation launch) live
7995
+ * here, keyed only on the plain-data record name; the state type rides as a
7996
+ * type-arg on `record_read`, and per-mutation arg types come from the
7997
+ * `record_mutate` ArgsStruct type-arg.
7998
+ */
7999
+ buildPrimitives() {
7792
8000
  const runtime = this;
7793
- const path2 = recordPath(name);
7794
8001
  const requireCache = () => {
7795
8002
  if (!runtime.cache) {
7796
8003
  throw new Error("Record.bind: no dataset cache configured — mount a provider first");
7797
8004
  }
7798
8005
  return runtime.cache;
7799
8006
  };
7800
- const mutate = {
7801
- pending: () => {
8007
+ return [
8008
+ RecordBindPrimitives.read.implement((stateType) => {
8009
+ const decode = decodeBeast2For(stateType);
8010
+ return (nameArg) => {
8011
+ const ws = runtime.resolveWorkspace();
8012
+ const path2 = recordPath(nameArg);
8013
+ trackDatasetPath(ws, path2);
8014
+ const bytes = requireCache().read(ws, path2);
8015
+ if (bytes === void 0) {
8016
+ throw new Error(`Record.bind: record state not loaded: ${datasetCacheKey(ws, path2)} (it should be preloaded via the UI task manifest)`);
8017
+ }
8018
+ return decode(bytes);
8019
+ };
8020
+ }),
8021
+ RecordBindPrimitives.status.implement((nameArg) => {
8022
+ const ws = runtime.resolveWorkspace();
8023
+ const path2 = recordPath(nameArg);
8024
+ trackDatasetPath(ws, path2);
8025
+ return requireCache().getStatus(ws, path2);
8026
+ }),
8027
+ RecordBindPrimitives.history.implement((nameArg) => {
8028
+ const name = nameArg;
7802
8029
  const ws = runtime.resolveWorkspace();
7803
8030
  const key = recordChannelKey(ws, name);
7804
8031
  runtime.track(key);
8032
+ const cached = runtime.histories.get(key);
8033
+ if (cached === void 0) {
8034
+ if (!runtime.historyFailed.has(key)) runtime.fetchHistory(ws, name, key);
8035
+ return none$1;
8036
+ }
8037
+ return some(cached);
8038
+ }),
8039
+ RecordBindPrimitives.start.implement((nameArg) => {
8040
+ const ws = runtime.resolveWorkspace();
8041
+ const cache3 = requireCache();
8042
+ const inflight = runtime.entry(recordChannelKey(ws, nameArg)).inflight;
8043
+ void Promise.resolve(inflight).catch(() => void 0).then(() => cache3.launchDataflow(ws)).catch(() => void 0);
8044
+ return null;
8045
+ }),
8046
+ RecordBindPrimitives.mutatePending.implement((nameArg) => {
8047
+ const ws = runtime.resolveWorkspace();
8048
+ const key = recordChannelKey(ws, nameArg);
8049
+ runtime.track(key);
7805
8050
  return runtime.entry(key).status === "running";
7806
- },
7807
- status: () => {
8051
+ }),
8052
+ RecordBindPrimitives.mutateStatus.implement((nameArg) => {
7808
8053
  const ws = runtime.resolveWorkspace();
7809
- const key = recordChannelKey(ws, name);
8054
+ const key = recordChannelKey(ws, nameArg);
7810
8055
  runtime.track(key);
7811
8056
  return variant(runtime.entry(key).status, null);
7812
- },
7813
- error: () => {
8057
+ }),
8058
+ RecordBindPrimitives.mutateError.implement((nameArg) => {
7814
8059
  const ws = runtime.resolveWorkspace();
7815
- const key = recordChannelKey(ws, name);
8060
+ const key = recordChannelKey(ws, nameArg);
7816
8061
  runtime.track(key);
7817
8062
  const entry = runtime.entry(key);
7818
8063
  return entry.status === "failed" && entry.error !== void 0 ? some(entry.error) : none$1;
7819
- },
7820
- cancel: () => {
8064
+ }),
8065
+ RecordBindPrimitives.mutateCancel.implement((nameArg) => {
7821
8066
  const ws = runtime.resolveWorkspace();
7822
- runtime.cancelChannel(recordChannelKey(ws, name), (e3) => {
8067
+ runtime.cancelChannel(recordChannelKey(ws, nameArg), (e3) => {
7823
8068
  delete e3.error;
7824
8069
  delete e3.inflight;
7825
8070
  });
7826
8071
  return null;
7827
- }
8072
+ }),
8073
+ RecordBindPrimitives.mutate.implement((argsStructType) => (recordNameArg, mutationNameArg, argsStruct) => {
8074
+ const fields = argsStructType.value;
8075
+ const obj = argsStruct;
8076
+ const argTypes = fields.map((f2) => f2.type);
8077
+ const args = fields.map((f2) => obj[f2.name]);
8078
+ runtime.launchMutation(runtime.resolveWorkspace(), recordNameArg, mutationNameArg, argTypes, args);
8079
+ return null;
8080
+ })
8081
+ ];
8082
+ }
8083
+ /**
8084
+ * Build the handle value for one `Record.bind` evaluation. Every method —
8085
+ * top-level AND nested `mutate.*` (incl. the dynamic per-mutation closures) —
8086
+ * is a thin IR-bearing `East.function` over {@link buildPrimitives}, capturing
8087
+ * only the record name (+ per-mutation name), so the handle is ordinary
8088
+ * serializable East data (issue #106).
8089
+ */
8090
+ buildHandle(handleType, name) {
8091
+ const cached = this.handleCache.get(name);
8092
+ if (cached) return cached;
8093
+ const sig = signatureOfRecordHandleType(handleType);
8094
+ const stateTypeEast = fromEastTypeValue(sig.stateType);
8095
+ const fields = handleType.value;
8096
+ const fnOut = (list, n2) => fromEastTypeValue(list.find((f2) => f2.name === n2).type.value.output);
8097
+ const statusRet = fnOut(fields, "status");
8098
+ const historyRet = fnOut(fields, "history");
8099
+ const mfields = fields.find((f2) => f2.name === "mutate").type.value;
8100
+ const nameExpr = East.value(name, StringType);
8101
+ const platform = this.buildPrimitives();
8102
+ const P2 = RecordBindPrimitives;
8103
+ const mutate = {
8104
+ pending: East.compile(East.function([], fnOut(mfields, "pending"), ($3) => {
8105
+ $3.return(P2.mutatePending(nameExpr));
8106
+ }), platform),
8107
+ status: East.compile(East.function([], fnOut(mfields, "status"), ($3) => {
8108
+ $3.return(P2.mutateStatus(nameExpr));
8109
+ }), platform),
8110
+ error: East.compile(East.function([], fnOut(mfields, "error"), ($3) => {
8111
+ $3.return(P2.mutateError(nameExpr));
8112
+ }), platform),
8113
+ cancel: East.compile(East.function([], NullType, ($3) => {
8114
+ $3.return(P2.mutateCancel(nameExpr));
8115
+ }), platform)
7828
8116
  };
7829
8117
  for (const [mutationName, argTypes] of sig.mutations) {
7830
- mutate[mutationName] = (...args) => {
7831
- runtime.launchMutation(runtime.resolveWorkspace(), name, mutationName, argTypes, args);
7832
- return null;
7833
- };
7834
- }
7835
- return {
7836
- read: () => {
7837
- const ws = runtime.resolveWorkspace();
7838
- trackDatasetPath(ws, path2);
7839
- const bytes = requireCache().read(ws, path2);
7840
- if (bytes === void 0) {
7841
- throw new Error(`Record.bind: record state not loaded: ${datasetCacheKey(ws, path2)} (it should be preloaded via the UI task manifest)`);
7842
- }
7843
- return decodeState(bytes);
7844
- },
7845
- status: () => {
7846
- const ws = runtime.resolveWorkspace();
7847
- trackDatasetPath(ws, path2);
7848
- return requireCache().getStatus(ws, path2);
7849
- },
7850
- history: () => {
7851
- const ws = runtime.resolveWorkspace();
7852
- const key = recordChannelKey(ws, name);
7853
- runtime.track(key);
7854
- const cached = runtime.histories.get(key);
7855
- if (cached === void 0) {
7856
- if (!runtime.historyFailed.has(key)) runtime.fetchHistory(ws, name, key);
7857
- return none$1;
7858
- }
7859
- return some(cached);
7860
- },
8118
+ const argTypesEast = argTypes.map((t4) => fromEastTypeValue(t4));
8119
+ const ArgsStruct = StructType(Object.fromEntries(argTypesEast.map((t4, i) => [`arg${i}`, t4])));
8120
+ const mutationNameExpr = East.value(mutationName, StringType);
8121
+ mutate[mutationName] = East.compile(East.function(argTypesEast, NullType, ($3, ...args) => {
8122
+ const obj = {};
8123
+ args.forEach((a2, i) => {
8124
+ obj[`arg${i}`] = a2;
8125
+ });
8126
+ $3.return(P2.mutate([ArgsStruct], nameExpr, mutationNameExpr, East.value(obj, ArgsStruct)));
8127
+ }), platform);
8128
+ }
8129
+ const handle = {
8130
+ read: East.compile(East.function([], stateTypeEast, ($3) => {
8131
+ $3.return(P2.read([stateTypeEast], nameExpr));
8132
+ }), platform),
8133
+ status: East.compile(East.function([], statusRet, ($3) => {
8134
+ $3.return(P2.status(nameExpr));
8135
+ }), platform),
8136
+ history: East.compile(East.function([], historyRet, ($3) => {
8137
+ $3.return(P2.history(nameExpr));
8138
+ }), platform),
7861
8139
  mutate,
7862
- start: () => {
7863
- const ws = runtime.resolveWorkspace();
7864
- const cache3 = requireCache();
7865
- const inflight = runtime.entry(recordChannelKey(ws, name)).inflight;
7866
- void Promise.resolve(inflight).catch(() => void 0).then(() => cache3.launchDataflow(ws)).catch(() => void 0);
7867
- return null;
7868
- },
8140
+ start: East.compile(East.function([], NullType, ($3) => {
8141
+ $3.return(P2.start(nameExpr));
8142
+ }), platform),
7869
8143
  binding: { name, mutations: [...sig.mutations.keys()] }
7870
8144
  };
8145
+ this.handleCache.set(name, handle);
8146
+ return handle;
7871
8147
  }
7872
8148
  // ----- platform building -------------------------------------------------
7873
8149
  /** Build a `Record.bind` PlatformFunction bound to this runtime. Pass
@@ -7894,9 +8170,15 @@ function initializeRecordApi(api3, cache3, workspace) {
7894
8170
  function clearRecordApi() {
7895
8171
  defaultRecordRuntime.clear();
7896
8172
  }
7897
- const RecordPlatform = [defaultRecordRuntime.buildPlatform(null)];
8173
+ const RecordPlatform = [
8174
+ defaultRecordRuntime.buildPlatform(null),
8175
+ ...defaultRecordRuntime.buildPrimitives()
8176
+ ];
7898
8177
  function createScopedRecordPlatform(records) {
7899
- return [defaultRecordRuntime.buildPlatform(new Set(records))];
8178
+ return [
8179
+ defaultRecordRuntime.buildPlatform(new Set(records)),
8180
+ ...defaultRecordRuntime.buildPrimitives()
8181
+ ];
7900
8182
  }
7901
8183
  function createInMemoryRecordApi(cache3, workspace, defs2) {
7902
8184
  const compiled = /* @__PURE__ */ new Map();
@@ -61604,23 +61886,23 @@ function hashString2(s2) {
61604
61886
  for (let i = 0; i < s2.length; i++) h2 = (h2 << 5) + h2 + s2.charCodeAt(i) | 0;
61605
61887
  return (h2 >>> 0).toString(36);
61606
61888
  }
61607
- function buildDecisionHandle(decisions, judgements, sliceInit) {
61608
- const selectionKey = deriveSelectionKey(decisions);
61609
- const readSelection = () => {
61610
- StateRuntime.trackKey(selectionKey);
61611
- const bytes = StateRuntime.getStore().read(selectionKey);
61612
- return bytes === void 0 ? none$1 : decodeSelection(bytes);
61613
- };
61614
- const writeSelection = (value) => {
61615
- StateRuntime.getStore().write(selectionKey, encodeSelection(value));
61616
- return null;
61617
- };
61618
- const queue = () => decisions.flatMap((b2) => viewFor(b2).read());
61619
- const readJudgements = () => viewFor(judgements).read();
61620
- const writeJudgements = (next2) => {
61621
- viewFor(judgements).write(next2);
61622
- };
61623
- const judgementFor = (caseId) => readJudgements().get(caseId) ?? {
61889
+ function readSelectionAt(selectionKey) {
61890
+ StateRuntime.trackKey(selectionKey);
61891
+ const bytes = StateRuntime.getStore().read(selectionKey);
61892
+ return bytes === void 0 ? none$1 : decodeSelection(bytes);
61893
+ }
61894
+ function writeSelectionAt(selectionKey, value) {
61895
+ StateRuntime.getStore().write(selectionKey, encodeSelection(value));
61896
+ return null;
61897
+ }
61898
+ function hostQueue(decisions) {
61899
+ return decisions.flatMap((b2) => viewFor(b2).read());
61900
+ }
61901
+ function readJudgementsAt(judgements) {
61902
+ return viewFor(judgements).read();
61903
+ }
61904
+ function judgementForAt(judgements, caseId) {
61905
+ return readJudgementsAt(judgements).get(caseId) ?? {
61624
61906
  caseId,
61625
61907
  answers: /* @__PURE__ */ new Map(),
61626
61908
  knowledge: none$1,
@@ -61628,101 +61910,190 @@ function buildDecisionHandle(decisions, judgements, sliceInit) {
61628
61910
  verdict: none$1,
61629
61911
  resolvedAt: none$1
61630
61912
  };
61631
- const stageJudgement = (caseId, change) => {
61632
- const dict = new Map(readJudgements());
61633
- dict.set(caseId, { ...judgementFor(caseId), ...change });
61634
- writeJudgements(dict);
61635
- return null;
61636
- };
61637
- const removeFromOwningView = (caseId) => {
61638
- for (const binding of decisions) {
61639
- const view = viewFor(binding);
61640
- const rows = view.read();
61641
- if (rows.some((d2) => d2.id === caseId)) {
61642
- view.write(rows.filter((d2) => d2.id !== caseId));
61643
- }
61913
+ }
61914
+ function stageJudgementAt(judgements, caseId, change) {
61915
+ const dict = new Map(readJudgementsAt(judgements));
61916
+ dict.set(caseId, { ...judgementForAt(judgements, caseId), ...change });
61917
+ viewFor(judgements).write(dict);
61918
+ return null;
61919
+ }
61920
+ function removeFromOwningView(decisions, caseId) {
61921
+ for (const binding of decisions) {
61922
+ const view = viewFor(binding);
61923
+ const rows = view.read();
61924
+ if (rows.some((d2) => d2.id === caseId)) {
61925
+ view.write(rows.filter((d2) => d2.id !== caseId));
61644
61926
  }
61645
- };
61927
+ }
61928
+ }
61929
+ const decisionHandleCache = /* @__PURE__ */ new Map();
61930
+ function typeToken(tv) {
61931
+ const seen = /* @__PURE__ */ new WeakSet();
61932
+ return JSON.stringify(tv, (_k, v3) => {
61933
+ if (typeof v3 === "object" && v3 !== null) {
61934
+ if (seen.has(v3)) return "<cyc>";
61935
+ seen.add(v3);
61936
+ }
61937
+ return typeof v3 === "bigint" ? `${v3}n` : v3;
61938
+ });
61939
+ }
61940
+ function compileDecisionMethods(constraintType, decisions, judgements, selectionKey) {
61941
+ const platform = getRegisteredPlatformImplementations();
61942
+ const c2 = fromEastTypeValue(constraintType);
61943
+ const decisionsExpr = East.value(decisions, ArrayType(DiffBindingType));
61944
+ const judgementsExpr = East.value(judgements, DiffBindingType);
61945
+ const selKeyExpr = East.value(selectionKey, StringType);
61946
+ const {
61947
+ queue,
61948
+ selected: selected2,
61949
+ select: select2,
61950
+ clearSelection,
61951
+ decision,
61952
+ update: update2,
61953
+ judgement,
61954
+ answer,
61955
+ addKnowledge,
61956
+ inject,
61957
+ resolve: resolve2,
61958
+ commitState
61959
+ } = DecisionBindPrimitives;
61960
+ return {
61961
+ queue: East.compile(East.function([], ArrayType(DecisionType), ($3) => {
61962
+ $3.return(queue(decisionsExpr));
61963
+ }), platform),
61964
+ selected: East.compile(East.function([], OptionType(StringType), ($3) => {
61965
+ $3.return(selected2(selKeyExpr));
61966
+ }), platform),
61967
+ select: East.compile(East.function([StringType], NullType, ($3, id3) => {
61968
+ $3.return(select2(selKeyExpr, id3));
61969
+ }), platform),
61970
+ clearSelection: East.compile(East.function([], NullType, ($3) => {
61971
+ $3.return(clearSelection(selKeyExpr));
61972
+ }), platform),
61973
+ decision: East.compile(East.function([], OptionType(DecisionType), ($3) => {
61974
+ $3.return(decision(decisionsExpr, selKeyExpr));
61975
+ }), platform),
61976
+ update: East.compile(East.function([DecisionType], NullType, ($3, edited) => {
61977
+ $3.return(update2(decisionsExpr, edited));
61978
+ }), platform),
61979
+ judgement: East.compile(East.function([StringType], judgementInputType(c2), ($3, id3) => {
61980
+ $3.return(judgement([c2], judgementsExpr, id3));
61981
+ }), platform),
61982
+ answer: East.compile(East.function([StringType, StringType, AnswerType], NullType, ($3, id3, prompt, ans) => {
61983
+ $3.return(answer(judgementsExpr, id3, prompt, ans));
61984
+ }), platform),
61985
+ addKnowledge: East.compile(East.function([StringType, StringType], NullType, ($3, id3, text) => {
61986
+ $3.return(addKnowledge(judgementsExpr, id3, text));
61987
+ }), platform),
61988
+ inject: East.compile(East.function([StringType, c2], NullType, ($3, id3, con) => {
61989
+ $3.return(inject([c2], judgementsExpr, id3, con));
61990
+ }), platform),
61991
+ resolve: East.compile(East.function([StringType, VerdictType], NullType, ($3, id3, verdict) => {
61992
+ $3.return(resolve2(decisionsExpr, judgementsExpr, selKeyExpr, id3, verdict));
61993
+ }), platform),
61994
+ commitState: East.compile(East.function([StringType], CommitStateType, ($3, id3) => {
61995
+ $3.return(commitState(decisionsExpr, judgementsExpr, id3));
61996
+ }), platform)
61997
+ };
61998
+ }
61999
+ function buildDecisionHandle(decisions, judgements, sliceInit, constraintType) {
62000
+ const cType = constraintType ?? toEastTypeValue(DecisionConstraintType);
62001
+ const selectionKey = deriveSelectionKey(decisions);
61646
62002
  const slice3 = buildSliceHandle(
61647
62003
  deriveSliceKey(decisions, sliceInit),
61648
62004
  DECISION_SLICE_CONFIG,
61649
62005
  sliceInit ?? DEFAULT_SLICE_STATE,
61650
- () => queue(),
62006
+ () => hostQueue(decisions),
61651
62007
  none$1
61652
62008
  );
62009
+ const journal = () => {
62010
+ const entries = [...readJudgementsAt(judgements).values()].filter((j2) => j2.verdict.type === "some");
62011
+ const at = (j2) => j2.resolvedAt.type === "some" ? j2.resolvedAt.value.getTime() : 0;
62012
+ return entries.sort((a2, b2) => at(b2) - at(a2));
62013
+ };
62014
+ const cacheKey = `${hashString2(stableStringify({ decisions, judgements }))}|${typeToken(cType)}`;
62015
+ let methods = decisionHandleCache.get(cacheKey);
62016
+ if (methods === void 0) {
62017
+ methods = compileDecisionMethods(cType, decisions, judgements, selectionKey);
62018
+ decisionHandleCache.set(cacheKey, methods);
62019
+ }
61653
62020
  return {
61654
62021
  decisions,
61655
62022
  judgements,
61656
62023
  sliceInit: sliceInit !== void 0 ? some(sliceInit) : none$1,
61657
62024
  slice: slice3,
61658
- queue,
61659
- selected: readSelection,
61660
- select: (caseId) => writeSelection(some(caseId)),
61661
- clearSelection: () => writeSelection(none$1),
61662
- decision: () => {
61663
- const selection2 = readSelection();
61664
- if (selection2.type === "none") return none$1;
61665
- const found = queue().find((d2) => d2.id === selection2.value);
61666
- return found === void 0 ? none$1 : some(found);
61667
- },
61668
- update: (edited) => {
61669
- for (const binding of decisions) {
61670
- const view = viewFor(binding);
61671
- const rows = view.read();
61672
- if (rows.some((d2) => d2.id === edited.id)) {
61673
- view.write(rows.map((d2) => d2.id === edited.id ? edited : d2));
61674
- }
61675
- }
61676
- return null;
61677
- },
61678
- judgement: judgementFor,
61679
- answer: (caseId, prompt, response) => {
61680
- const answers = new Map(judgementFor(caseId).answers);
61681
- answers.set(prompt, response);
61682
- return stageJudgement(caseId, { answers });
61683
- },
61684
- addKnowledge: (caseId, text) => stageJudgement(caseId, { knowledge: some(text) }),
61685
- inject: (caseId, constraint) => {
61686
- const constraints = judgementFor(caseId).constraints.filter((c2) => c2.type !== constraint.type).concat([constraint]);
61687
- return stageJudgement(caseId, { constraints });
61688
- },
61689
- resolve: (caseId, verdict) => {
61690
- stageJudgement(caseId, { verdict: some(verdict), resolvedAt: some(/* @__PURE__ */ new Date()) });
61691
- removeFromOwningView(caseId);
61692
- writeSelection(none$1);
61693
- return null;
61694
- },
61695
- journal: () => {
61696
- const entries = [...readJudgements().values()].filter((j2) => j2.verdict.type === "some");
61697
- const at = (j2) => j2.resolvedAt.type === "some" ? j2.resolvedAt.value.getTime() : 0;
61698
- return entries.sort((a2, b2) => at(b2) - at(a2));
61699
- },
61700
- commitState: (caseId) => {
61701
- const decision = queue().find((d2) => d2.id === caseId);
61702
- const prompts = (decision == null ? void 0 : decision.prompts) ?? [];
61703
- const answers = judgementFor(caseId).answers;
61704
- const responses = prompts.map((p2) => {
61705
- var _a2;
61706
- return (_a2 = answers.get(p2.id)) == null ? void 0 : _a2.type;
61707
- });
61708
- if (responses.some((r2) => r2 === "no")) return variant("blocked", null);
61709
- if (responses.some((r2) => r2 === "unknown")) return variant("handoff", null);
61710
- const unanswered = responses.filter((r2) => r2 === void 0).length;
61711
- if (unanswered > 0) return variant("gated", BigInt(unanswered));
61712
- return variant("ready", null);
61713
- }
62025
+ journal,
62026
+ ...methods
61714
62027
  };
61715
62028
  }
61716
62029
  const DecisionBindPlatform = [
61717
- // Generic over the constraint contract — the type argument resolver
61718
- // receives the contract type value; the JS impl is type-agnostic.
61719
- decisionBindPlatformFn.implement((_constraintType) => (decisionsArg, judgementsArg, sliceInitArg) => buildDecisionHandle(
62030
+ // Generic over the constraint contract — the resolver receives the contract
62031
+ // type value and threads it into the IR-bearing judgement / inject methods.
62032
+ decisionBindPlatformFn.implement((constraintType) => (decisionsArg, judgementsArg, sliceInitArg) => buildDecisionHandle(
61720
62033
  decisionsArg,
61721
62034
  judgementsArg,
61722
- getSomeorUndefined(sliceInitArg)
61723
- ))
62035
+ getSomeorUndefined$1(sliceInitArg),
62036
+ constraintType
62037
+ )),
62038
+ // ── issue #106 primitives — host I/O backing the IR-bearing handle methods.
62039
+ // Each is a faithful lift of the original closure body, taking the plain-data
62040
+ // descriptor(s) + the selection key as arguments. ──
62041
+ DecisionBindPrimitives.queue.implement((decisions) => hostQueue(decisions)),
62042
+ DecisionBindPrimitives.selected.implement((selectionKey) => readSelectionAt(selectionKey)),
62043
+ DecisionBindPrimitives.select.implement((selectionKey, caseId) => writeSelectionAt(selectionKey, some(caseId))),
62044
+ DecisionBindPrimitives.clearSelection.implement((selectionKey) => writeSelectionAt(selectionKey, none$1)),
62045
+ DecisionBindPrimitives.decision.implement((decisions, selectionKey) => {
62046
+ const sel = readSelectionAt(selectionKey);
62047
+ if (sel.type === "none") return none$1;
62048
+ const found = hostQueue(decisions).find((d2) => d2.id === sel.value);
62049
+ return found === void 0 ? none$1 : some(found);
62050
+ }),
62051
+ DecisionBindPrimitives.update.implement((decisions, edited) => {
62052
+ const e3 = edited;
62053
+ for (const binding of decisions) {
62054
+ const view = viewFor(binding);
62055
+ const rows = view.read();
62056
+ if (rows.some((d2) => d2.id === e3.id)) {
62057
+ view.write(rows.map((d2) => d2.id === e3.id ? e3 : d2));
62058
+ }
62059
+ }
62060
+ return null;
62061
+ }),
62062
+ DecisionBindPrimitives.judgement.implement((_c2) => (judgements, caseId) => judgementForAt(judgements, caseId)),
62063
+ DecisionBindPrimitives.answer.implement((judgements, caseId, prompt, response) => {
62064
+ const answers = new Map(judgementForAt(judgements, caseId).answers);
62065
+ answers.set(prompt, response);
62066
+ return stageJudgementAt(judgements, caseId, { answers });
62067
+ }),
62068
+ DecisionBindPrimitives.addKnowledge.implement((judgements, caseId, text) => stageJudgementAt(judgements, caseId, { knowledge: some(text) })),
62069
+ DecisionBindPrimitives.inject.implement((_c2) => (judgements, caseId, constraint) => {
62070
+ const con = constraint;
62071
+ const constraints = judgementForAt(judgements, caseId).constraints.filter((x2) => x2.type !== con.type).concat([con]);
62072
+ return stageJudgementAt(judgements, caseId, { constraints });
62073
+ }),
62074
+ DecisionBindPrimitives.resolve.implement((decisions, judgements, selectionKey, caseId, verdict) => {
62075
+ stageJudgementAt(judgements, caseId, { verdict: some(verdict), resolvedAt: some(/* @__PURE__ */ new Date()) });
62076
+ removeFromOwningView(decisions, caseId);
62077
+ writeSelectionAt(selectionKey, none$1);
62078
+ return null;
62079
+ }),
62080
+ DecisionBindPrimitives.commitState.implement((decisions, judgements, caseId) => {
62081
+ const cid = caseId;
62082
+ const decision = hostQueue(decisions).find((d2) => d2.id === cid);
62083
+ const prompts = (decision == null ? void 0 : decision.prompts) ?? [];
62084
+ const answers = judgementForAt(judgements, cid).answers;
62085
+ const responses = prompts.map((p2) => {
62086
+ var _a2;
62087
+ return (_a2 = answers.get(p2.id)) == null ? void 0 : _a2.type;
62088
+ });
62089
+ if (responses.some((r2) => r2 === "no")) return variant("blocked", null);
62090
+ if (responses.some((r2) => r2 === "unknown")) return variant("handoff", null);
62091
+ const unanswered = responses.filter((r2) => r2 === void 0).length;
62092
+ if (unanswered > 0) return variant("gated", BigInt(unanswered));
62093
+ return variant("ready", null);
62094
+ })
61724
62095
  ];
61725
- registerPlatformImplementation$1(DecisionBindPlatform);
62096
+ registerPlatformImplementation(DecisionBindPlatform);
61726
62097
  function useHandleVersion(handle) {
61727
62098
  const staged = getStagedStore();
61728
62099
  const cache3 = getReactiveDatasetCache();
@@ -61758,7 +62129,7 @@ function useDecisionHandle(ref) {
61758
62129
  () => ref ? buildDecisionHandle(
61759
62130
  [...ref.decisions],
61760
62131
  ref.judgements,
61761
- getSomeorUndefined(ref.slice)
62132
+ getSomeorUndefined$1(ref.slice)
61762
62133
  ) : null,
61763
62134
  [ref]
61764
62135
  );
@@ -62108,6 +62479,9 @@ const OptionsFacet = memo(function OptionsFacet2({ decision, narrow }) {
62108
62479
  const options2 = rankOptions(decision);
62109
62480
  const maxUp = Math.max(...options2.map((o2) => Math.max(o2.value, 0)), 1e-9);
62110
62481
  const maxDown = Math.max(...options2.map((o2) => Math.abs(o2.downside ?? 0)), 1e-9);
62482
+ const valueAxis = getSomeorUndefined(decision.valueAxis);
62483
+ const upLabel = (valueAxis == null ? void 0 : valueAxis.label) ?? "Uplift";
62484
+ const signed2 = (valueAxis == null ? void 0 : valueAxis.signed) ?? true;
62111
62485
  if (narrow) {
62112
62486
  const meter = (label, frac, display, tone) => /* @__PURE__ */ jsxs(Box, { display: "grid", gridTemplateColumns: "80px 1fr 64px", alignItems: "center", gap: "8px", children: [
62113
62487
  /* @__PURE__ */ jsx$1(Text, { ...caption, whiteSpace: "nowrap", children: label }),
@@ -62126,7 +62500,7 @@ const OptionsFacet = memo(function OptionsFacet2({ decision, narrow }) {
62126
62500
  " ",
62127
62501
  /* @__PURE__ */ jsx$1(Text, { as: "span", fontWeight: o2.recommended ? "semibold" : "normal", children: o2.label })
62128
62502
  ] }),
62129
- meter("Uplift", o2.value / maxUp, fmt(decision, o2.value, true), "success"),
62503
+ meter(upLabel, o2.value / maxUp, fmt(decision, o2.value, signed2), signed2 ? "success" : "neutral"),
62130
62504
  meter("If wrong", (o2.downside ?? 0) / maxDown, o2.downside !== void 0 ? fmt(decision, o2.downside, true) : "—", "danger")
62131
62505
  ] }, o2.rank))
62132
62506
  ] });
@@ -62139,7 +62513,7 @@ const OptionsFacet = memo(function OptionsFacet2({ decision, narrow }) {
62139
62513
  " evaluated"
62140
62514
  ] }),
62141
62515
  /* @__PURE__ */ jsx$1(Text, { ...caption, textAlign: "center", children: "If wrong" }),
62142
- /* @__PURE__ */ jsx$1(Text, { ...caption, textAlign: "right", children: "Uplift" })
62516
+ /* @__PURE__ */ jsx$1(Text, { ...caption, textAlign: "right", children: upLabel })
62143
62517
  ] }),
62144
62518
  options2.map((o2) => {
62145
62519
  const downFrac = Math.min(Math.abs(o2.downside ?? 0) / maxDown, 1);
@@ -62164,8 +62538,8 @@ const OptionsFacet = memo(function OptionsFacet2({ decision, narrow }) {
62164
62538
  /* @__PURE__ */ jsx$1(Box, { height: "12px", width: `${Math.round(downFrac * 100)}%`, bg: "fg.danger", opacity: 0.55 })
62165
62539
  ] }) }),
62166
62540
  /* @__PURE__ */ jsx$1(Box, { display: "flex", alignItems: "center", gap: "6px", height: "16px", borderLeftWidth: "1px", borderColor: "border.strong", children: o2.value > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
62167
- /* @__PURE__ */ jsx$1(Box, { height: "12px", width: `${Math.round(upFrac * 100)}%`, bg: "fg.success", opacity: 0.6 }),
62168
- /* @__PURE__ */ jsx$1(Text, { fontSize: "10.5px", fontFamily: "mono", color: "fg.success", flexShrink: 0, children: fmt(decision, o2.value, true) })
62541
+ /* @__PURE__ */ jsx$1(Box, { height: "12px", width: `${Math.round(upFrac * 100)}%`, bg: signed2 ? "fg.success" : "fg.subtle", opacity: 0.6 }),
62542
+ /* @__PURE__ */ jsx$1(Text, { fontSize: "10.5px", fontFamily: "mono", color: signed2 ? "fg.success" : "fg.muted", flexShrink: 0, children: fmt(decision, o2.value, signed2) })
62169
62543
  ] }) })
62170
62544
  ]
62171
62545
  },
@@ -62350,7 +62724,8 @@ function useLeverPayloads(ref) {
62350
62724
  return (constraint == null ? void 0 : constraint.type) === "Variant" ? constraint.cases ?? {} : {};
62351
62725
  }, [ref, workspace]);
62352
62726
  }
62353
- const Row = memo(function Row2({ decision, handle, selected: selected2, narrow, leverPayloads, modify, detail, defaultFacet, apply: apply2, reject: reject2, leaving, storageKey }) {
62727
+ const Row = memo(function Row2({ decision, handle, selected: selected2, narrow, leverPayloads, modify, evidence, defaultFacet, facetInclude, apply: apply2, reject: reject2, leaving, storageKey }) {
62728
+ var _a2, _b2;
62354
62729
  const dq = useSlotRecipe({ key: "decisionQueue" });
62355
62730
  const status = useSlotRecipe({ key: "status" });
62356
62731
  const tabs = useSlotRecipe({ key: "facetTabs" });
@@ -62358,10 +62733,15 @@ const Row = memo(function Row2({ decision, handle, selected: selected2, narrow,
62358
62733
  const rs = dq({});
62359
62734
  const ts = tabs({});
62360
62735
  const st = status({ status: URGENCY_TONE[decision.urgency.type], size: "md" });
62361
- const [facet, setFacet] = useState(defaultFacet);
62736
+ const visibleFacets = useMemo(
62737
+ () => FACETS.filter((f2) => f2.key === "modify" ? modify !== void 0 : facetInclude === null || facetInclude.has(f2.key)),
62738
+ [modify, facetInclude]
62739
+ );
62740
+ const effectiveDefault = visibleFacets.some((f2) => f2.key === defaultFacet) ? defaultFacet : ((_a2 = visibleFacets[0]) == null ? void 0 : _a2.key) ?? defaultFacet;
62741
+ const [facet, setFacet] = useState(effectiveDefault);
62362
62742
  useEffect(() => {
62363
- if (!selected2) setFacet(defaultFacet);
62364
- }, [selected2, defaultFacet]);
62743
+ if (!selected2) setFacet(effectiveDefault);
62744
+ }, [selected2, effectiveDefault]);
62365
62745
  const gate = handle.commitStateFor(decision.id);
62366
62746
  const applyEnabled = gate === null || gate.type === "ready";
62367
62747
  const gatePulse = !applyEnabled && decision.prompts.length > 0;
@@ -62380,10 +62760,10 @@ const Row = memo(function Row2({ decision, handle, selected: selected2, narrow,
62380
62760
  return modify(decision, handle.update);
62381
62761
  }, [selected2, facet, modify, decision, handle.update]);
62382
62762
  const canvas = useMemo(() => {
62383
- if (!selected2 || facet !== "evidence" || !detail) return null;
62384
- return detail(decision);
62385
- }, [selected2, facet, detail, decision]);
62386
- const facetTabs = /* @__PURE__ */ jsx$1(Box, { css: ts.root, ...narrow ? { display: "flex", width: "100%" } : {}, children: FACETS.filter((f2) => f2.key !== "modify" || modify !== void 0).map((f2) => /* @__PURE__ */ jsx$1(
62763
+ if (!selected2 || facet !== "evidence" || !evidence) return null;
62764
+ return evidence(decision);
62765
+ }, [selected2, facet, evidence, decision]);
62766
+ const facetTabs = /* @__PURE__ */ jsx$1(Box, { css: ts.root, ...narrow ? { display: "flex", width: "100%" } : {}, children: visibleFacets.map((f2) => /* @__PURE__ */ jsx$1(
62387
62767
  Box,
62388
62768
  {
62389
62769
  as: "button",
@@ -62429,7 +62809,8 @@ const Row = memo(function Row2({ decision, handle, selected: selected2, narrow,
62429
62809
  deadlineSuffix(decision)
62430
62810
  ] })
62431
62811
  ] });
62432
- const valueText = /* @__PURE__ */ jsx$1(Text, { fontFamily: "mono", fontWeight: "semibold", textAlign: "right", ...decision.value >= 0 && selected2 ? { color: "fg.success" } : {}, children: decisionValue(decision, decision.value, selected2) });
62812
+ const valueSigned = ((_b2 = getSomeorUndefined(decision.valueAxis)) == null ? void 0 : _b2.signed) ?? true;
62813
+ const valueText = /* @__PURE__ */ jsx$1(Text, { fontFamily: "mono", fontWeight: "semibold", textAlign: "right", ...valueSigned && decision.value >= 0 && selected2 ? { color: "fg.success" } : {}, children: decisionValue(decision, decision.value, valueSigned && selected2) });
62433
62814
  return /* @__PURE__ */ jsxs(Box, { css: rs.rowShell, ...leaving ? { "data-leaving": leaving } : {}, children: [
62434
62815
  narrow ? /* @__PURE__ */ jsxs(Box, { px: "14px", py: "10px", children: [
62435
62816
  /* @__PURE__ */ jsxs(Box, { display: "flex", justifyContent: "space-between", alignItems: "baseline", children: [
@@ -62451,7 +62832,7 @@ const Row = memo(function Row2({ decision, handle, selected: selected2, narrow,
62451
62832
  ] })
62452
62833
  ] }),
62453
62834
  selected2 && /* @__PURE__ */ jsxs(Box, { px: narrow ? "14px" : "18px", py: "12px", borderTopWidth: "1px", borderColor: "border.subtle", bg: "bg.canvas", children: [
62454
- facet === "evidence" && /* @__PURE__ */ jsx$1(EvidenceFacet, { decision, children: canvas != null && /* @__PURE__ */ jsx$1(EastChakraComponent, { value: canvas, storageKey: `${storageKey}-detail-${decision.id}` }) }),
62835
+ facet === "evidence" && /* @__PURE__ */ jsx$1(EvidenceFacet, { decision, children: canvas != null && /* @__PURE__ */ jsx$1(EastChakraComponent, { value: canvas, storageKey: `${storageKey}-evidence-${decision.id}` }) }),
62455
62836
  facet === "options" && /* @__PURE__ */ jsx$1(OptionsFacet, { decision, narrow }),
62456
62837
  facet === "judgement" && /* @__PURE__ */ jsx$1(JudgementFacet, { decision, handle, leverPayloads }),
62457
62838
  facet === "modify" && probe != null && /* @__PURE__ */ jsx$1(EastChakraComponent, { value: probe, storageKey: `${storageKey}-modify-${decision.id}` })
@@ -62559,11 +62940,15 @@ const EastChakraDecisionQueue = memo(function EastChakraDecisionQueue2({ value,
62559
62940
  () => getSomeorUndefined(value.modify),
62560
62941
  [value.modify]
62561
62942
  );
62562
- const detail = useMemo(
62563
- () => getSomeorUndefined(value.detail),
62564
- [value.detail]
62943
+ const evidence = useMemo(
62944
+ () => getSomeorUndefined(value.evidence),
62945
+ [value.evidence]
62565
62946
  );
62566
62947
  const defaultFacet = ((_a2 = getSomeorUndefined(value.defaultFacet)) == null ? void 0 : _a2.type) ?? "evidence";
62948
+ const facetInclude = useMemo(() => {
62949
+ const facets = getSomeorUndefined(value.facets);
62950
+ return facets === void 0 ? null : new Set(facets.map((f2) => f2.type));
62951
+ }, [value.facets]);
62567
62952
  const defaultExpanded = getSomeorUndefined(value.defaultExpanded);
62568
62953
  const selectedId = handle.selected ?? (defaultExpanded == null ? void 0 : defaultExpanded.id) ?? null;
62569
62954
  const resolve2 = useCallback((ds, hook2, reason) => {
@@ -62650,8 +63035,9 @@ const EastChakraDecisionQueue = memo(function EastChakraDecisionQueue2({ value,
62650
63035
  narrow,
62651
63036
  leverPayloads,
62652
63037
  modify,
62653
- detail,
63038
+ evidence,
62654
63039
  defaultFacet,
63040
+ facetInclude,
62655
63041
  apply: apply2,
62656
63042
  reject: reject2,
62657
63043
  leaving: exitingReasons.get(d2.id),
@@ -63057,7 +63443,20 @@ function useDatasetStatus(apiUrl, repo, workspace, datasetPath, options2) {
63057
63443
  ...queryOverrides
63058
63444
  });
63059
63445
  }
63060
- const defaultPlatformImplementations = [...StateImpl, ...BindPlatform, ...OverlayImpl];
63446
+ const defaultPlatformImplementations = [
63447
+ ...StateImpl,
63448
+ ...NavImpl,
63449
+ ...SliceImpl,
63450
+ ...SliceApplyImpl,
63451
+ ...OverlayImpl,
63452
+ ...ClipboardImpl,
63453
+ ...DownloadImpl,
63454
+ ...ShareImpl,
63455
+ ...DecisionBindPlatform,
63456
+ ...BindPlatform,
63457
+ ...FuncPlatform,
63458
+ ...RecordPlatform
63459
+ ];
63061
63460
  function useDatasetValue(apiUrl, repo, workspace, datasetPath, options2) {
63062
63461
  const { requestOptions, queryOverrides, enabled = true, hash: hash2, platforms, type } = options2;
63063
63462
  const platformImpls = platforms ?? defaultPlatformImplementations;
@@ -63158,6 +63557,7 @@ function StatusDisplay({ variant: variant2, title, message, details }) {
63158
63557
  return /* @__PURE__ */ jsx$1(
63159
63558
  Box,
63160
63559
  {
63560
+ "data-status": variant2,
63161
63561
  height: "100%",
63162
63562
  width: "100%",
63163
63563
  display: "flex",
@@ -63191,7 +63591,7 @@ function StatusDisplay({ variant: variant2, title, message, details }) {
63191
63591
  }
63192
63592
  );
63193
63593
  }
63194
- return /* @__PURE__ */ jsxs(Box, { p: 6, bg: styles2.bg, height: "100%", width: "100%", children: [
63594
+ return /* @__PURE__ */ jsxs(Box, { "data-status": variant2, p: 6, bg: styles2.bg, height: "100%", width: "100%", children: [
63195
63595
  /* @__PURE__ */ jsx$1(Text, { fontSize: "lg", fontWeight: "bold", color: styles2.titleColor, mb: 4, children: title }),
63196
63596
  message && /* @__PURE__ */ jsx$1(Text, { color: styles2.messageColor, mb: details ? 4 : 0, children: message }),
63197
63597
  details && /* @__PURE__ */ jsxs(Box, { position: "relative", children: [
@@ -63705,11 +64105,22 @@ const UITaskPreview = memo(function UITaskPreview2({
63705
64105
  const { loading: preloading, error: preloadError } = usePreloadReactiveDatasets(preloads);
63706
64106
  const scopedPlatforms = useMemo(
63707
64107
  () => manifest ? [
64108
+ // EVERY browser-local platform impl (unscoped — no manifest paths) from east-ui-components +
64109
+ // e3-ui-components MUST be listed here, or a ui() task that uses it throws "Platform function
64110
+ // '<x>_bind' is not available" — and only inside an e3 ui() task, so component tests miss it
64111
+ // (see east-contribute "Common traps"). The manifest-SCOPED data/func/record binds follow.
63708
64112
  ...StateImpl,
64113
+ ...NavImpl,
64114
+ ...SliceImpl,
64115
+ ...SliceApplyImpl,
64116
+ ...OverlayImpl,
64117
+ ...ClipboardImpl,
64118
+ ...DownloadImpl,
64119
+ ...ShareImpl,
64120
+ ...DecisionBindPlatform,
63709
64121
  ...createScopedBindPlatform(manifest),
63710
64122
  ...createScopedFuncPlatform(manifest.functions),
63711
- ...createScopedRecordPlatform(manifest.records),
63712
- ...OverlayImpl
64123
+ ...createScopedRecordPlatform(manifest.records)
63713
64124
  ] : void 0,
63714
64125
  [manifest]
63715
64126
  );