@elaraai/e3-ui-components 1.0.13 → 1.0.15

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.cjs CHANGED
@@ -2106,9 +2106,6 @@ function datasetCacheKey(workspace, path2) {
2106
2106
  const pathStr = datasetPathToString(path2);
2107
2107
  return pathStr ? `${workspace}.${pathStr}` : workspace;
2108
2108
  }
2109
- function toTreePath(path2) {
2110
- return path2;
2111
- }
2112
2109
  class ReactiveDatasetCache {
2113
2110
  constructor(config2, api3, clock2 = realClock) {
2114
2111
  __publicField(this, "destroyed", false);
@@ -2230,7 +2227,7 @@ class ReactiveDatasetCache {
2230
2227
  const prevTail = this.writeChains.get(key) ?? Promise.resolve();
2231
2228
  const run = prevTail.then(async () => {
2232
2229
  try {
2233
- await this.api.set(workspace, toTreePath(path2), value);
2230
+ await this.api.set(workspace, path2, value);
2234
2231
  if (this.destroyed) return;
2235
2232
  this.writeBaselines.set(key, {
2236
2233
  value,
@@ -2285,6 +2282,14 @@ class ReactiveDatasetCache {
2285
2282
  if (this.destroyed) return;
2286
2283
  await this.api.launchDataflow(workspace);
2287
2284
  }
2285
+ /**
2286
+ * Launch a workspace dataflow run without writing first. The standalone
2287
+ * half of {@link writeAndStart}; see the interface doc for semantics.
2288
+ */
2289
+ async launchDataflow(workspace) {
2290
+ if (this.destroyed) return;
2291
+ await this.api.launchDataflow(workspace);
2292
+ }
2288
2293
  /**
2289
2294
  * Preload a dataset into cache. Concurrent preloads of the same key
2290
2295
  * share one fetch (query-core dedup); a write racing the fetch cancels
@@ -2300,7 +2305,7 @@ class ReactiveDatasetCache {
2300
2305
  await this.client.fetchQuery({
2301
2306
  queryKey,
2302
2307
  queryFn: async () => {
2303
- const result = await this.fetchDataset(workspace, path2);
2308
+ const result = await this.api.get(workspace, path2);
2304
2309
  fetchedHash = result.hash;
2305
2310
  return result.data;
2306
2311
  }
@@ -2316,14 +2321,11 @@ class ReactiveDatasetCache {
2316
2321
  this.knownHashes.set(key, fetchedHash);
2317
2322
  this.notifyChange(key);
2318
2323
  }
2319
- fetchDataset(workspace, path2) {
2320
- return this.api.get(workspace, toTreePath(path2));
2321
- }
2322
2324
  /**
2323
2325
  * List fields at a path. Empty path lists workspace root.
2324
2326
  */
2325
2327
  list(workspace, path2) {
2326
- return path2.length === 0 ? this.api.listRoot(workspace) : this.api.listAt(workspace, toTreePath(path2));
2328
+ return path2.length === 0 ? this.api.listRoot(workspace) : this.api.listAt(workspace, path2);
2327
2329
  }
2328
2330
  /**
2329
2331
  * Set refetch interval for a dataset (polling).
@@ -2379,6 +2381,11 @@ class ReactiveDatasetCache {
2379
2381
  this.workspacePollers.delete(workspace);
2380
2382
  }
2381
2383
  }
2384
+ refresh(workspace) {
2385
+ if (this.destroyed) return Promise.resolve();
2386
+ if (!this.workspacePollers.has(workspace)) return Promise.resolve();
2387
+ return this.pollWorkspaceStatus(workspace);
2388
+ }
2382
2389
  /**
2383
2390
  * Poll workspace status and reconcile the cache with the server.
2384
2391
  * Concurrent calls for the same workspace dedupe to a single fetch.
@@ -2443,7 +2450,7 @@ class ReactiveDatasetCache {
2443
2450
  if (pending.length === 0) return;
2444
2451
  const fetched = await Promise.allSettled(
2445
2452
  pending.map(
2446
- (p2) => p2.kind === "fetch" ? this.fetchDataset(workspace, p2.path).then((result) => ({ p: p2, result })) : Promise.resolve({ p: p2, result: null })
2453
+ (p2) => p2.kind === "fetch" ? this.api.get(workspace, p2.path).then((result) => ({ p: p2, result })) : Promise.resolve({ p: p2, result: null })
2447
2454
  )
2448
2455
  );
2449
2456
  if (this.destroyed) return;
@@ -2626,6 +2633,9 @@ class IndexedDBStagedAdapter {
2626
2633
  req.onerror = () => reject2(req.error);
2627
2634
  req.onblocked = () => reject2(new Error("IndexedDB open blocked"));
2628
2635
  });
2636
+ void this.dbPromise.catch(() => {
2637
+ this.dbPromise = null;
2638
+ });
2629
2639
  }
2630
2640
  return this.dbPromise;
2631
2641
  }
@@ -2701,6 +2711,11 @@ class StagedStore {
2701
2711
  __publicField(this, "adapter");
2702
2712
  __publicField(this, "hydrated");
2703
2713
  __publicField(this, "inFlight", /* @__PURE__ */ new Set());
2714
+ // Per-key persistence chain — serializes save/remove for a key so they
2715
+ // land in IndexedDB in call order (last write wins) instead of racing.
2716
+ __publicField(this, "saveChains", /* @__PURE__ */ new Map());
2717
+ // Listeners notified when a persistence op fails durably.
2718
+ __publicField(this, "errorListeners", /* @__PURE__ */ new Set());
2704
2719
  this.adapter = adapter;
2705
2720
  this.hydrated = this.hydrate();
2706
2721
  }
@@ -2737,14 +2752,14 @@ class StagedStore {
2737
2752
  snapshot: next2.snapshot,
2738
2753
  buffered: next2.buffered
2739
2754
  };
2740
- this.track(this.adapter.save(key, persisted));
2755
+ this.persist(key, () => this.adapter.save(key, persisted));
2741
2756
  }
2742
2757
  discard(workspace, path2) {
2743
2758
  const key = datasetCacheKey(workspace, path2);
2744
2759
  if (!this.entries.has(key)) return false;
2745
2760
  this.entries.delete(key);
2746
2761
  this.notify(key);
2747
- this.track(this.adapter.remove(key));
2762
+ this.persist(key, () => this.adapter.remove(key));
2748
2763
  return true;
2749
2764
  }
2750
2765
  listKeys() {
@@ -2765,11 +2780,18 @@ class StagedStore {
2765
2780
  getKeyVersion(key) {
2766
2781
  return this.versions.get(key) ?? 0;
2767
2782
  }
2783
+ onPersistError(cb) {
2784
+ this.errorListeners.add(cb);
2785
+ return () => {
2786
+ this.errorListeners.delete(cb);
2787
+ };
2788
+ }
2768
2789
  /** Test-only: clear in-memory + persisted state. */
2769
2790
  async clear() {
2770
2791
  const keys = [...this.entries.keys()];
2771
2792
  this.entries.clear();
2772
2793
  for (const key of keys) this.notify(key);
2794
+ await this.flushPending();
2773
2795
  await this.adapter.clear();
2774
2796
  }
2775
2797
  // ----- internals -------------------------------------------------------
@@ -2778,20 +2800,45 @@ class StagedStore {
2778
2800
  const subs = this.subscribers.get(key);
2779
2801
  if (subs) for (const cb of subs) cb();
2780
2802
  }
2781
- track(p2) {
2782
- const wrapped = p2.catch((err) => {
2783
- console.warn("[StagedStore] persistence operation failed:", err);
2784
- }).then(() => {
2785
- this.inFlight.delete(wrapped);
2803
+ /**
2804
+ * Serialize a persistence op behind any prior op for the same key, so they
2805
+ * land in IndexedDB in call order (last write wins) rather than racing.
2806
+ * Tracks the op for {@link flushPending} and routes failures to
2807
+ * {@link onPersistError} listeners (falling back to a console warning).
2808
+ */
2809
+ persist(key, op) {
2810
+ const prev2 = this.saveChains.get(key) ?? Promise.resolve();
2811
+ const settled = prev2.then(op, op).then(
2812
+ () => void 0,
2813
+ (err) => {
2814
+ this.emitPersistError(key, err);
2815
+ }
2816
+ ).then(() => {
2817
+ if (this.saveChains.get(key) === settled) this.saveChains.delete(key);
2818
+ this.inFlight.delete(settled);
2786
2819
  });
2787
- this.inFlight.add(wrapped);
2820
+ this.saveChains.set(key, settled);
2821
+ this.inFlight.add(settled);
2822
+ }
2823
+ emitPersistError(key, err) {
2824
+ if (this.errorListeners.size === 0) {
2825
+ console.warn("[StagedStore] persistence operation failed:", err);
2826
+ return;
2827
+ }
2828
+ for (const cb of [...this.errorListeners]) {
2829
+ try {
2830
+ cb(key, err);
2831
+ } catch (listenerErr) {
2832
+ console.warn("[StagedStore] persist-error listener threw:", listenerErr);
2833
+ }
2834
+ }
2788
2835
  }
2789
2836
  async hydrate() {
2790
2837
  let persisted;
2791
2838
  try {
2792
2839
  persisted = await this.adapter.loadAll();
2793
2840
  } catch (err) {
2794
- console.warn("[StagedStore] hydrate failed:", err);
2841
+ this.emitPersistError("", err);
2795
2842
  return;
2796
2843
  }
2797
2844
  for (const [key, p2] of persisted) {
@@ -2827,7 +2874,7 @@ function clearStagedStoreSingleton() {
2827
2874
  }
2828
2875
  const UNCHANGED_VARIANT = east.variant("unchanged", null);
2829
2876
  const blobEqual = east.equalFor(east.BlobType);
2830
- const eastTypeEqual$1 = east.equalFor(internal$1.EastTypeType);
2877
+ const eastTypeEqual$2 = east.equalFor(internal$1.EastTypeType);
2831
2878
  const helpersCache = new east.SortedMap(
2832
2879
  void 0,
2833
2880
  east.compareFor(internal$1.EastTypeType)
@@ -2880,6 +2927,7 @@ class BindRuntime {
2880
2927
  // List cache helpers.
2881
2928
  __publicField(this, "listCache", /* @__PURE__ */ new Map());
2882
2929
  this.staged = staged;
2930
+ this.staged.onPersistError((_key, err) => this.emitWriteError(err));
2883
2931
  }
2884
2932
  // ----- cache singleton ------------------------------------------------
2885
2933
  /** Read the active cache. Throws if none has been initialized. */
@@ -2917,6 +2965,16 @@ class BindRuntime {
2917
2965
  this.writeErrorListeners.delete(cb);
2918
2966
  };
2919
2967
  }
2968
+ /** Fan a write/persistence error out to every registered listener. */
2969
+ emitWriteError(error3) {
2970
+ for (const cb of this.writeErrorListeners) {
2971
+ try {
2972
+ cb(error3);
2973
+ } catch (cbErr) {
2974
+ console.error("Data.bind write-error listener threw:", cbErr);
2975
+ }
2976
+ }
2977
+ }
2920
2978
  /** Resolve once every currently-queued write has finished
2921
2979
  * (success or failure). Errors are NOT thrown — subscribe via
2922
2980
  * {@link onWriteError} for those. */
@@ -2954,13 +3012,7 @@ class BindRuntime {
2954
3012
  await writeFn();
2955
3013
  } catch (error3) {
2956
3014
  console.error("Data.bind write failed:", error3);
2957
- for (const cb of this.writeErrorListeners) {
2958
- try {
2959
- cb(error3);
2960
- } catch (cbErr) {
2961
- console.error("Data.bind write-error listener threw:", cbErr);
2962
- }
2963
- }
3015
+ this.emitWriteError(error3);
2964
3016
  }
2965
3017
  }
2966
3018
  this.isProcessingWrites = false;
@@ -3017,7 +3069,7 @@ class BindRuntime {
3017
3069
  return;
3018
3070
  }
3019
3071
  const shapeChanged = existing.mode !== info.mode || existing.hasPatchDataset !== info.hasPatchDataset;
3020
- const typeChanged = !eastTypeEqual$1(existing.sourceType, info.sourceType);
3072
+ const typeChanged = !eastTypeEqual$2(existing.sourceType, info.sourceType);
3021
3073
  if (!shapeChanged && !typeChanged) {
3022
3074
  this.bindingRegistry.set(key, info);
3023
3075
  return;
@@ -3120,6 +3172,10 @@ class BindRuntime {
3120
3172
  }
3121
3173
  return null;
3122
3174
  };
3175
+ const startLaunch = () => {
3176
+ this.queueWrite(() => cache3.launchDataflow(ws));
3177
+ return null;
3178
+ };
3123
3179
  if (mode === "direct" && !patchPath) {
3124
3180
  return {
3125
3181
  read: () => {
@@ -3134,6 +3190,7 @@ class BindRuntime {
3134
3190
  this.queueWrite(() => cache3.writeAndStart(ws, sourcePath, encodeT(value)));
3135
3191
  return null;
3136
3192
  },
3193
+ start: startLaunch,
3137
3194
  source: () => {
3138
3195
  this.trackPath(ws, sourcePath);
3139
3196
  return readSourceValue();
@@ -3167,6 +3224,7 @@ class BindRuntime {
3167
3224
  this.queueWrite(() => cache3.writeAndStart(ws, pPath2, encodePatch(next2)));
3168
3225
  return null;
3169
3226
  },
3227
+ start: startLaunch,
3170
3228
  source: () => {
3171
3229
  this.trackPath(ws, sourcePath);
3172
3230
  return readSourceValue();
@@ -3180,12 +3238,8 @@ class BindRuntime {
3180
3238
  const patchVal = readPatchValue();
3181
3239
  this.queueWrite(async () => {
3182
3240
  const next2 = apply2(sourceVal, patchVal);
3183
- const writes = [];
3184
- cache3.batch(() => {
3185
- writes.push(cache3.write(ws, sourcePath, encodeT(next2)));
3186
- writes.push(cache3.write(ws, pPath2, encodePatch(UNCHANGED_VARIANT)));
3187
- });
3188
- await Promise.all(writes);
3241
+ await cache3.write(ws, sourcePath, encodeT(next2));
3242
+ await cache3.write(ws, pPath2, encodePatch(UNCHANGED_VARIANT));
3189
3243
  });
3190
3244
  return null;
3191
3245
  },
@@ -3211,6 +3265,7 @@ class BindRuntime {
3211
3265
  },
3212
3266
  write: writeStaged,
3213
3267
  writeAndStart: writeStaged,
3268
+ start: startLaunch,
3214
3269
  source: () => {
3215
3270
  this.trackPath(ws, sourcePath);
3216
3271
  return readSourceValue();
@@ -3255,6 +3310,7 @@ class BindRuntime {
3255
3310
  },
3256
3311
  write: writeStaged,
3257
3312
  writeAndStart: writeStaged,
3313
+ start: startLaunch,
3258
3314
  source: () => {
3259
3315
  this.trackPath(ws, sourcePath);
3260
3316
  return readSourceValue();
@@ -3403,6 +3459,110 @@ platform.registerReactiveTracker({
3403
3459
  }
3404
3460
  });
3405
3461
  platform.registerPlatformImplementation(BindPlatform);
3462
+ class TrackedChannelStore {
3463
+ constructor() {
3464
+ __publicField(this, "entries", /* @__PURE__ */ new Map());
3465
+ // Per-key reactive subscription shim.
3466
+ __publicField(this, "keySubscribers", /* @__PURE__ */ new Map());
3467
+ __publicField(this, "keyVersions", /* @__PURE__ */ new Map());
3468
+ // Reactive tracking — read closures push their channel keys here while a
3469
+ // Reactive.Root render is being tracked.
3470
+ __publicField(this, "trackingContext", null);
3471
+ }
3472
+ // ----- reactive tracking ----------------------------------------------
3473
+ enableTracking() {
3474
+ this.trackingContext = /* @__PURE__ */ new Set();
3475
+ return this.trackingContext;
3476
+ }
3477
+ disableTracking() {
3478
+ const keys = this.trackingContext ? [...this.trackingContext] : [];
3479
+ this.trackingContext = null;
3480
+ return keys;
3481
+ }
3482
+ isTracking() {
3483
+ return this.trackingContext !== null;
3484
+ }
3485
+ /** Record a channel-key dependency for the current render (no-op when not
3486
+ * tracking). */
3487
+ track(key) {
3488
+ var _a2;
3489
+ (_a2 = this.trackingContext) == null ? void 0 : _a2.add(key);
3490
+ }
3491
+ // ----- subscription shim ------------------------------------------------
3492
+ subscribe(key, callback) {
3493
+ let subs = this.keySubscribers.get(key);
3494
+ if (!subs) {
3495
+ subs = /* @__PURE__ */ new Set();
3496
+ this.keySubscribers.set(key, subs);
3497
+ }
3498
+ subs.add(callback);
3499
+ return () => {
3500
+ subs.delete(callback);
3501
+ if (subs.size === 0 && this.keySubscribers.get(key) === subs) {
3502
+ this.keySubscribers.delete(key);
3503
+ }
3504
+ };
3505
+ }
3506
+ getKeyVersion(key) {
3507
+ return this.keyVersions.get(key) ?? 0;
3508
+ }
3509
+ notify(key) {
3510
+ this.keyVersions.set(key, (this.keyVersions.get(key) ?? 0) + 1);
3511
+ const subs = this.keySubscribers.get(key);
3512
+ if (subs) for (const cb of [...subs]) cb();
3513
+ }
3514
+ // ----- channel registry + latest-wins latch ----------------------------
3515
+ entry(key) {
3516
+ let entry = this.entries.get(key);
3517
+ if (!entry) {
3518
+ entry = this.createEntry();
3519
+ this.entries.set(key, entry);
3520
+ }
3521
+ return entry;
3522
+ }
3523
+ /**
3524
+ * Open a latest-wins launch on a channel: bump the seq, mark the entry
3525
+ * `running`, optionally `reset` it (e.g. clear a prior error), notify, and
3526
+ * return a `settle` that applies a terminal mutation only if this launch is
3527
+ * still the current one (a superseded / cancelled launch settles to a
3528
+ * no-op).
3529
+ */
3530
+ beginLaunch(key, reset2) {
3531
+ const entry = this.entry(key);
3532
+ entry.launchSeq += 1;
3533
+ const mySeq = entry.launchSeq;
3534
+ entry.status = "running";
3535
+ reset2 == null ? void 0 : reset2(entry);
3536
+ this.notify(key);
3537
+ const settle = (mutate) => {
3538
+ const current = this.entries.get(key);
3539
+ if (!current || current.launchSeq !== mySeq) return;
3540
+ mutate(current);
3541
+ this.notify(key);
3542
+ };
3543
+ return { entry, settle };
3544
+ }
3545
+ /**
3546
+ * Client-side cancel: if the channel is running, orphan the in-flight wait
3547
+ * (bump the seq) and mark it `cancelled`. `after` runs before the notify,
3548
+ * for subclass-specific cleanup (e.g. dropping an in-flight handle).
3549
+ */
3550
+ cancelChannel(key, after) {
3551
+ const entry = this.entries.get(key);
3552
+ if (entry && entry.status === "running") {
3553
+ entry.launchSeq += 1;
3554
+ entry.status = "cancelled";
3555
+ after == null ? void 0 : after(entry);
3556
+ this.notify(key);
3557
+ }
3558
+ }
3559
+ /** Drop all channel state (entries + subscriptions + versions). */
3560
+ clearChannels() {
3561
+ this.entries.clear();
3562
+ this.keySubscribers.clear();
3563
+ this.keyVersions.clear();
3564
+ }
3565
+ }
3406
3566
  function createDefaultFunctionApi(apiUrl, repo, getToken) {
3407
3567
  const opts = () => ({ token: getToken() });
3408
3568
  return {
@@ -3418,8 +3578,8 @@ function createDefaultFunctionApi(apiUrl, repo, getToken) {
3418
3578
  }
3419
3579
  };
3420
3580
  }
3421
- const eastTypeEqual = east.equalFor(internal$1.EastTypeType);
3422
- function signatureOfHandleType(handleType) {
3581
+ const eastTypeEqual$1 = east.equalFor(internal$1.EastTypeType);
3582
+ function signatureOfFuncHandleType(handleType) {
3423
3583
  var _a2, _b2;
3424
3584
  if (handleType.type !== "Struct") {
3425
3585
  throw new Error(`Func.bind: handle type must be a Struct; got ${handleType.type}`);
@@ -3478,10 +3638,10 @@ function errorOfExecuteResult(result) {
3478
3638
  stderr: result.stderr
3479
3639
  };
3480
3640
  default:
3481
- return transportError(`unexpected outcome "${outcome.type}"`);
3641
+ return transportError$1(`unexpected outcome "${outcome.type}"`);
3482
3642
  }
3483
3643
  }
3484
- function transportError(message) {
3644
+ function transportError$1(message) {
3485
3645
  return {
3486
3646
  kind: east.variant("transport", { message }),
3487
3647
  message,
@@ -3492,20 +3652,16 @@ function transportError(message) {
3492
3652
  function funcChannelKey(workspace, name) {
3493
3653
  return `func:${workspace}:${name}`;
3494
3654
  }
3495
- class FuncRuntime {
3655
+ class FuncRuntime extends TrackedChannelStore {
3496
3656
  constructor() {
3657
+ super(...arguments);
3497
3658
  __publicField(this, "api", null);
3498
3659
  __publicField(this, "workspace", null);
3499
- __publicField(this, "entries", /* @__PURE__ */ new Map());
3500
3660
  // Signature lists are fetched once per workspace and cached.
3501
3661
  __publicField(this, "signatureLists", /* @__PURE__ */ new Map());
3502
- // Subscription shim — same contract (and the same stale-disposer
3503
- // guard) as the dataset cache's.
3504
- __publicField(this, "keySubscribers", /* @__PURE__ */ new Map());
3505
- __publicField(this, "keyVersions", /* @__PURE__ */ new Map());
3506
- // Reactive tracking — read closures push their channel keys here while
3507
- // a Reactive.Root render is being tracked.
3508
- __publicField(this, "trackingContext", null);
3662
+ }
3663
+ createEntry() {
3664
+ return { status: "idle", launchSeq: 0 };
3509
3665
  }
3510
3666
  // ----- wiring ----------------------------------------------------------
3511
3667
  /** Install the API adapter + workspace — called by the React provider
@@ -3518,59 +3674,8 @@ class FuncRuntime {
3518
3674
  clear() {
3519
3675
  this.api = null;
3520
3676
  this.workspace = null;
3521
- this.entries.clear();
3677
+ this.clearChannels();
3522
3678
  this.signatureLists.clear();
3523
- this.keySubscribers.clear();
3524
- this.keyVersions.clear();
3525
- }
3526
- // ----- reactive tracking ----------------------------------------------
3527
- enableTracking() {
3528
- this.trackingContext = /* @__PURE__ */ new Set();
3529
- return this.trackingContext;
3530
- }
3531
- disableTracking() {
3532
- const keys = this.trackingContext ? [...this.trackingContext] : [];
3533
- this.trackingContext = null;
3534
- return keys;
3535
- }
3536
- isTracking() {
3537
- return this.trackingContext !== null;
3538
- }
3539
- track(key) {
3540
- var _a2;
3541
- (_a2 = this.trackingContext) == null ? void 0 : _a2.add(key);
3542
- }
3543
- // ----- subscription shim ------------------------------------------------
3544
- subscribe(key, callback) {
3545
- let subs = this.keySubscribers.get(key);
3546
- if (!subs) {
3547
- subs = /* @__PURE__ */ new Set();
3548
- this.keySubscribers.set(key, subs);
3549
- }
3550
- subs.add(callback);
3551
- return () => {
3552
- subs.delete(callback);
3553
- if (subs.size === 0 && this.keySubscribers.get(key) === subs) {
3554
- this.keySubscribers.delete(key);
3555
- }
3556
- };
3557
- }
3558
- getKeyVersion(key) {
3559
- return this.keyVersions.get(key) ?? 0;
3560
- }
3561
- notify(key) {
3562
- this.keyVersions.set(key, (this.keyVersions.get(key) ?? 0) + 1);
3563
- const subs = this.keySubscribers.get(key);
3564
- if (subs) for (const cb of [...subs]) cb();
3565
- }
3566
- // ----- registry ----------------------------------------------------------
3567
- entry(key) {
3568
- let entry = this.entries.get(key);
3569
- if (!entry) {
3570
- entry = { status: "idle", launchSeq: 0 };
3571
- this.entries.set(key, entry);
3572
- }
3573
- return entry;
3574
3679
  }
3575
3680
  /** The deployed signature list for a workspace (fetched once). */
3576
3681
  signatures(workspace) {
@@ -3593,7 +3698,7 @@ class FuncRuntime {
3593
3698
  try {
3594
3699
  list = await this.signatures(workspace);
3595
3700
  } catch (err) {
3596
- return transportError(`failed to list workspace functions: ${err instanceof Error ? err.message : String(err)}`);
3701
+ return transportError$1(`failed to list workspace functions: ${err instanceof Error ? err.message : String(err)}`);
3597
3702
  }
3598
3703
  const deployed = list.find((f2) => f2.name === name);
3599
3704
  if (!deployed) {
@@ -3604,8 +3709,8 @@ class FuncRuntime {
3604
3709
  stderr: ""
3605
3710
  };
3606
3711
  }
3607
- const inputsMatch = deployed.inputTypes.length === sig.inputs.length && deployed.inputTypes.every((t4, i) => eastTypeEqual(t4, sig.inputs[i]));
3608
- const outputMatches = eastTypeEqual(deployed.outputType, sig.output);
3712
+ const inputsMatch = deployed.inputTypes.length === sig.inputs.length && deployed.inputTypes.every((t4, i) => eastTypeEqual$1(t4, sig.inputs[i]));
3713
+ const outputMatches = eastTypeEqual$1(deployed.outputType, sig.output);
3609
3714
  if (!inputsMatch || !outputMatches) {
3610
3715
  const message = `signature mismatch for "${name}": bound (${sig.inputs.length} inputs) disagrees with the deployed package`;
3611
3716
  return {
@@ -3620,17 +3725,7 @@ class FuncRuntime {
3620
3725
  // ----- closure semantics -------------------------------------------------
3621
3726
  launch(workspace, name, sig, args) {
3622
3727
  const key = funcChannelKey(workspace, name);
3623
- const entry = this.entry(key);
3624
- entry.launchSeq += 1;
3625
- const mySeq = entry.launchSeq;
3626
- entry.status = "running";
3627
- this.notify(key);
3628
- const settle = (mutate) => {
3629
- const current = this.entries.get(key);
3630
- if (!current || current.launchSeq !== mySeq) return;
3631
- mutate(current);
3632
- this.notify(key);
3633
- };
3728
+ const { settle } = this.beginLaunch(key);
3634
3729
  void (async () => {
3635
3730
  const invalid = await this.validate(workspace, name, sig);
3636
3731
  if (invalid) {
@@ -3644,7 +3739,7 @@ class FuncRuntime {
3644
3739
  if (!api3) {
3645
3740
  settle((e3) => {
3646
3741
  e3.status = "failed";
3647
- e3.error = transportError("no FunctionApi installed");
3742
+ e3.error = transportError$1("no FunctionApi installed");
3648
3743
  });
3649
3744
  return;
3650
3745
  }
@@ -3655,7 +3750,7 @@ class FuncRuntime {
3655
3750
  } catch (err) {
3656
3751
  settle((e3) => {
3657
3752
  e3.status = "failed";
3658
- e3.error = transportError(err instanceof Error ? err.message : String(err));
3753
+ e3.error = transportError$1(err instanceof Error ? err.message : String(err));
3659
3754
  });
3660
3755
  return;
3661
3756
  }
@@ -3666,7 +3761,7 @@ class FuncRuntime {
3666
3761
  } catch (err) {
3667
3762
  settle((e3) => {
3668
3763
  e3.status = "failed";
3669
- e3.error = transportError(`failed to decode result: ${err instanceof Error ? err.message : String(err)}`);
3764
+ e3.error = transportError$1(`failed to decode result: ${err instanceof Error ? err.message : String(err)}`);
3670
3765
  });
3671
3766
  return;
3672
3767
  }
@@ -3686,7 +3781,7 @@ class FuncRuntime {
3686
3781
  }
3687
3782
  /** Build the handle value for one `Func.bind` platform evaluation. */
3688
3783
  buildHandle(handleType, name) {
3689
- const sig = signatureOfHandleType(handleType);
3784
+ const sig = signatureOfFuncHandleType(handleType);
3690
3785
  const runtime = this;
3691
3786
  const resolveWorkspace = () => {
3692
3787
  if (!runtime.workspace) {
@@ -3724,12 +3819,8 @@ class FuncRuntime {
3724
3819
  return entry.status === "running";
3725
3820
  },
3726
3821
  cancel: () => {
3727
- const { key, entry } = channel();
3728
- if (entry.status === "running") {
3729
- entry.launchSeq += 1;
3730
- entry.status = "cancelled";
3731
- runtime.notify(key);
3732
- }
3822
+ const { key } = channel();
3823
+ runtime.cancelChannel(key);
3733
3824
  return null;
3734
3825
  },
3735
3826
  binding: { name }
@@ -7469,6 +7560,430 @@ var faCircleInfo = {
7469
7560
  iconName: "circle-info",
7470
7561
  icon: [512, 512, ["info-circle"], "f05a", "M256 512a256 256 0 1 0 0-512 256 256 0 1 0 0 512zM224 160a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zm-8 64l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24z"]
7471
7562
  };
7563
+ function createDefaultRecordApi(apiUrl, repo, getToken) {
7564
+ const opts = () => ({ token: getToken() });
7565
+ return {
7566
+ async describe(workspace, record) {
7567
+ return e3ApiClient.workspaceRecordDescribe(apiUrl, repo, workspace, record, opts());
7568
+ },
7569
+ async mutate(workspace, record, mutation, req) {
7570
+ return e3ApiClient.workspaceRecordMutate(apiUrl, repo, workspace, record, mutation, {
7571
+ args: req.args,
7572
+ actor: east.none,
7573
+ limits: east.none
7574
+ }, opts());
7575
+ },
7576
+ async history(workspace, record, limit2, from2) {
7577
+ return e3ApiClient.workspaceRecordHistory(apiUrl, repo, workspace, record, limit2, opts(), from2);
7578
+ }
7579
+ };
7580
+ }
7581
+ const eastTypeEqual = east.equalFor(internal$1.EastTypeType);
7582
+ const RESERVED_MUTATE_FIELDS = /* @__PURE__ */ new Set(["pending", "status", "error", "cancel"]);
7583
+ function signatureOfRecordHandleType(handleType) {
7584
+ var _a2, _b2;
7585
+ if (handleType.type !== "Struct") {
7586
+ throw new Error(`Record.bind: handle type must be a Struct; got ${handleType.type}`);
7587
+ }
7588
+ const fields = handleType.value;
7589
+ const read = (_a2 = fields.find((f2) => f2.name === "read")) == null ? void 0 : _a2.type;
7590
+ const mutate = (_b2 = fields.find((f2) => f2.name === "mutate")) == null ? void 0 : _b2.type;
7591
+ if ((read == null ? void 0 : read.type) !== "Function") {
7592
+ throw new Error("Record.bind: handle type is missing its read closure");
7593
+ }
7594
+ if ((mutate == null ? void 0 : mutate.type) !== "Struct") {
7595
+ throw new Error("Record.bind: handle type is missing its mutate struct");
7596
+ }
7597
+ const stateType = read.value.output;
7598
+ const mutations = /* @__PURE__ */ new Map();
7599
+ for (const field of mutate.value) {
7600
+ if (RESERVED_MUTATE_FIELDS.has(field.name)) continue;
7601
+ if (field.type.type !== "Function") {
7602
+ throw new Error(`Record.bind: mutate field "${field.name}" must be a closure`);
7603
+ }
7604
+ mutations.set(field.name, field.type.value.inputs);
7605
+ }
7606
+ return { stateType, mutations };
7607
+ }
7608
+ function errorOfMutationResult(result) {
7609
+ const outcome = result.outcome;
7610
+ switch (outcome.type) {
7611
+ case "invalid":
7612
+ return { kind: east.variant("invalid", { message: outcome.value.message }), message: outcome.value.message, stderr: "" };
7613
+ case "failed":
7614
+ return {
7615
+ kind: east.variant("failed", { exitCode: outcome.value.exitCode }),
7616
+ message: `reducer exited with code ${outcome.value.exitCode}`,
7617
+ stderr: outcome.value.stderr
7618
+ };
7619
+ case "too_large":
7620
+ return {
7621
+ kind: east.variant("too_large", { bytes: outcome.value.bytes, limit: outcome.value.limit }),
7622
+ message: `new state too large (${outcome.value.bytes} bytes, limit ${outcome.value.limit})`,
7623
+ stderr: outcome.value.stderr
7624
+ };
7625
+ case "timed_out":
7626
+ return {
7627
+ kind: east.variant("timed_out", { ms: outcome.value.ms }),
7628
+ message: `mutation timed out after ${outcome.value.ms}ms (server deadline)`,
7629
+ stderr: outcome.value.stderr
7630
+ };
7631
+ case "conflict":
7632
+ return {
7633
+ kind: east.variant("conflict", { attempts: outcome.value.attempts }),
7634
+ message: `compare-and-swap conflicted after ${outcome.value.attempts} attempts; try again`,
7635
+ stderr: ""
7636
+ };
7637
+ default:
7638
+ return transportError(`unexpected outcome "${outcome.type}"`);
7639
+ }
7640
+ }
7641
+ function transportError(message) {
7642
+ return { kind: east.variant("transport", { message }), message, stderr: "" };
7643
+ }
7644
+ function recordPath(name) {
7645
+ return [east.variant("field", "records"), east.variant("field", name)];
7646
+ }
7647
+ function recordChannelKey(workspace, name) {
7648
+ return `record:${workspace}:${name}`;
7649
+ }
7650
+ class RecordRuntime extends TrackedChannelStore {
7651
+ constructor() {
7652
+ super(...arguments);
7653
+ __publicField(this, "api", null);
7654
+ __publicField(this, "cache", null);
7655
+ __publicField(this, "workspace", null);
7656
+ // Describe signatures, fetched once per (workspace, record), failures evicted.
7657
+ __publicField(this, "signatureCache", /* @__PURE__ */ new Map());
7658
+ // History per channel, fetched once + refreshed on commit.
7659
+ __publicField(this, "histories", /* @__PURE__ */ new Map());
7660
+ __publicField(this, "historyInFlight", /* @__PURE__ */ new Set());
7661
+ // Channels whose last history fetch failed. Guards `history()` from
7662
+ // re-issuing the request on every render (a failing endpoint would
7663
+ // otherwise spin); cleared on the next commit / on clear() so a later
7664
+ // mutation retries.
7665
+ __publicField(this, "historyFailed", /* @__PURE__ */ new Set());
7666
+ }
7667
+ createEntry() {
7668
+ return { status: "idle", launchSeq: 0 };
7669
+ }
7670
+ // ----- wiring ----------------------------------------------------------
7671
+ /** Install the API adapter + dataset cache + workspace — called by the
7672
+ * React provider (or a test/showcase harness) before any handle is used. */
7673
+ initialize(api3, cache3, workspace) {
7674
+ this.api = api3;
7675
+ this.cache = cache3;
7676
+ this.workspace = workspace;
7677
+ }
7678
+ /** Tear down the adapter and all record state. */
7679
+ clear() {
7680
+ this.api = null;
7681
+ this.cache = null;
7682
+ this.workspace = null;
7683
+ this.clearChannels();
7684
+ this.signatureCache.clear();
7685
+ this.histories.clear();
7686
+ this.historyInFlight.clear();
7687
+ this.historyFailed.clear();
7688
+ }
7689
+ resolveWorkspace() {
7690
+ if (!this.workspace) {
7691
+ throw new Error("Record.bind: no workspace configured — mount a provider (or call initializeRecordApi) first");
7692
+ }
7693
+ return this.workspace;
7694
+ }
7695
+ /** The deployed describe signature for a record (fetched once, evicted on failure). */
7696
+ signature(workspace, record) {
7697
+ const sigKey = `${workspace}:${record}`;
7698
+ let promise4 = this.signatureCache.get(sigKey);
7699
+ if (!promise4) {
7700
+ const api3 = this.api;
7701
+ if (!api3) return Promise.reject(new Error("Record.bind: no RecordApi installed"));
7702
+ promise4 = api3.describe(workspace, record).catch((err) => {
7703
+ this.signatureCache.delete(sigKey);
7704
+ throw err;
7705
+ });
7706
+ this.signatureCache.set(sigKey, promise4);
7707
+ }
7708
+ return promise4;
7709
+ }
7710
+ /** Validate a mutation's declared arg types against the deployed signature. */
7711
+ async validate(workspace, record, mutation, argTypes) {
7712
+ let sig;
7713
+ try {
7714
+ sig = await this.signature(workspace, record);
7715
+ } catch (err) {
7716
+ return transportError(`failed to describe record "${record}": ${err instanceof Error ? err.message : String(err)}`);
7717
+ }
7718
+ const deployed = sig.mutations.find((m2) => m2.name === mutation);
7719
+ if (!deployed) {
7720
+ const message = `no mutation "${mutation}" on record "${record}" in the deployed package`;
7721
+ return { kind: east.variant("invalid", { message }), message, stderr: "" };
7722
+ }
7723
+ const argsMatch = deployed.argTypes.length === argTypes.length && deployed.argTypes.every((t4, i) => eastTypeEqual(t4, argTypes[i]));
7724
+ if (!argsMatch) {
7725
+ const message = `signature mismatch for mutation "${mutation}": bound (${argTypes.length} args) disagrees with the deployed package`;
7726
+ return { kind: east.variant("invalid", { message }), message, stderr: "" };
7727
+ }
7728
+ return null;
7729
+ }
7730
+ /** Fetch a record's history once per channel (deduped); notify on settle. */
7731
+ fetchHistory(workspace, record, key) {
7732
+ if (this.historyInFlight.has(key)) return;
7733
+ this.historyInFlight.add(key);
7734
+ void (async () => {
7735
+ const api3 = this.api;
7736
+ try {
7737
+ if (!api3) throw new Error("no RecordApi installed");
7738
+ const result = await api3.history(workspace, record, void 0);
7739
+ this.histories.set(key, result.commits);
7740
+ this.historyFailed.delete(key);
7741
+ } catch {
7742
+ this.historyFailed.add(key);
7743
+ } finally {
7744
+ this.historyInFlight.delete(key);
7745
+ this.notify(key);
7746
+ }
7747
+ })();
7748
+ }
7749
+ // ----- closure semantics -------------------------------------------------
7750
+ launchMutation(workspace, record, mutation, argTypes, args) {
7751
+ const key = recordChannelKey(workspace, record);
7752
+ const { entry, settle } = this.beginLaunch(key, (e3) => {
7753
+ delete e3.error;
7754
+ });
7755
+ const run = (async () => {
7756
+ var _a2;
7757
+ const invalid = await this.validate(workspace, record, mutation, argTypes);
7758
+ if (invalid) {
7759
+ settle((e3) => {
7760
+ e3.status = "failed";
7761
+ e3.error = invalid;
7762
+ });
7763
+ return;
7764
+ }
7765
+ const api3 = this.api;
7766
+ if (!api3) {
7767
+ settle((e3) => {
7768
+ e3.status = "failed";
7769
+ e3.error = transportError("no RecordApi installed");
7770
+ });
7771
+ return;
7772
+ }
7773
+ let result;
7774
+ try {
7775
+ const encoded = args.map((arg, i) => east.encodeBeast2For(argTypes[i])(arg));
7776
+ result = await api3.mutate(workspace, record, mutation, { args: encoded });
7777
+ } catch (err) {
7778
+ settle((e3) => {
7779
+ e3.status = "failed";
7780
+ e3.error = transportError(err instanceof Error ? err.message : String(err));
7781
+ });
7782
+ return;
7783
+ }
7784
+ if (result.outcome.type === "committed") {
7785
+ settle((e3) => {
7786
+ e3.status = "committed";
7787
+ delete e3.error;
7788
+ });
7789
+ void ((_a2 = this.cache) == null ? void 0 : _a2.refresh(workspace));
7790
+ this.histories.delete(key);
7791
+ this.historyFailed.delete(key);
7792
+ this.notify(key);
7793
+ } else {
7794
+ settle((e3) => {
7795
+ e3.status = "failed";
7796
+ e3.error = errorOfMutationResult(result);
7797
+ });
7798
+ }
7799
+ })();
7800
+ entry.inflight = run;
7801
+ void run.finally(() => {
7802
+ const current = this.entries.get(key);
7803
+ if (current && current.inflight === run) delete current.inflight;
7804
+ });
7805
+ }
7806
+ /** Build the handle value for one `Record.bind` platform evaluation. */
7807
+ buildHandle(handleType, name) {
7808
+ const sig = signatureOfRecordHandleType(handleType);
7809
+ const decodeState = east.decodeBeast2For(sig.stateType);
7810
+ const runtime = this;
7811
+ const path2 = recordPath(name);
7812
+ const requireCache = () => {
7813
+ if (!runtime.cache) {
7814
+ throw new Error("Record.bind: no dataset cache configured — mount a provider first");
7815
+ }
7816
+ return runtime.cache;
7817
+ };
7818
+ const mutate = {
7819
+ pending: () => {
7820
+ const ws = runtime.resolveWorkspace();
7821
+ const key = recordChannelKey(ws, name);
7822
+ runtime.track(key);
7823
+ return runtime.entry(key).status === "running";
7824
+ },
7825
+ status: () => {
7826
+ const ws = runtime.resolveWorkspace();
7827
+ const key = recordChannelKey(ws, name);
7828
+ runtime.track(key);
7829
+ return east.variant(runtime.entry(key).status, null);
7830
+ },
7831
+ error: () => {
7832
+ const ws = runtime.resolveWorkspace();
7833
+ const key = recordChannelKey(ws, name);
7834
+ runtime.track(key);
7835
+ const entry = runtime.entry(key);
7836
+ return entry.status === "failed" && entry.error !== void 0 ? east.some(entry.error) : east.none;
7837
+ },
7838
+ cancel: () => {
7839
+ const ws = runtime.resolveWorkspace();
7840
+ runtime.cancelChannel(recordChannelKey(ws, name), (e3) => {
7841
+ delete e3.error;
7842
+ delete e3.inflight;
7843
+ });
7844
+ return null;
7845
+ }
7846
+ };
7847
+ for (const [mutationName, argTypes] of sig.mutations) {
7848
+ mutate[mutationName] = (...args) => {
7849
+ runtime.launchMutation(runtime.resolveWorkspace(), name, mutationName, argTypes, args);
7850
+ return null;
7851
+ };
7852
+ }
7853
+ return {
7854
+ read: () => {
7855
+ const ws = runtime.resolveWorkspace();
7856
+ trackDatasetPath(ws, path2);
7857
+ const bytes = requireCache().read(ws, path2);
7858
+ if (bytes === void 0) {
7859
+ throw new Error(`Record.bind: record state not loaded: ${datasetCacheKey(ws, path2)} (it should be preloaded via the UI task manifest)`);
7860
+ }
7861
+ return decodeState(bytes);
7862
+ },
7863
+ status: () => {
7864
+ const ws = runtime.resolveWorkspace();
7865
+ trackDatasetPath(ws, path2);
7866
+ return requireCache().getStatus(ws, path2);
7867
+ },
7868
+ history: () => {
7869
+ const ws = runtime.resolveWorkspace();
7870
+ const key = recordChannelKey(ws, name);
7871
+ runtime.track(key);
7872
+ const cached = runtime.histories.get(key);
7873
+ if (cached === void 0) {
7874
+ if (!runtime.historyFailed.has(key)) runtime.fetchHistory(ws, name, key);
7875
+ return east.none;
7876
+ }
7877
+ return east.some(cached);
7878
+ },
7879
+ mutate,
7880
+ start: () => {
7881
+ const ws = runtime.resolveWorkspace();
7882
+ const cache3 = requireCache();
7883
+ const inflight = runtime.entry(recordChannelKey(ws, name)).inflight;
7884
+ void Promise.resolve(inflight).catch(() => void 0).then(() => cache3.launchDataflow(ws)).catch(() => void 0);
7885
+ return null;
7886
+ },
7887
+ binding: { name, mutations: [...sig.mutations.keys()] }
7888
+ };
7889
+ }
7890
+ // ----- platform building -------------------------------------------------
7891
+ /** Build a `Record.bind` PlatformFunction bound to this runtime. Pass
7892
+ * `allowed=null` for an unscoped impl; pass a Set of record names for
7893
+ * manifest scoping. */
7894
+ buildPlatform(allowed) {
7895
+ return internal.recordBindPlatformFn.implement(
7896
+ (handleType) => (nameArg) => {
7897
+ const name = nameArg;
7898
+ if (allowed && !allowed.has(name)) {
7899
+ throw new Error(
7900
+ `Record.bind: record "${name}" is not in this UI task's manifest — bind it in the task body so derivation records it`
7901
+ );
7902
+ }
7903
+ return this.buildHandle(handleType, name);
7904
+ }
7905
+ );
7906
+ }
7907
+ }
7908
+ const defaultRecordRuntime = new RecordRuntime();
7909
+ function initializeRecordApi(api3, cache3, workspace) {
7910
+ defaultRecordRuntime.initialize(api3, cache3, workspace);
7911
+ }
7912
+ function clearRecordApi() {
7913
+ defaultRecordRuntime.clear();
7914
+ }
7915
+ const RecordPlatform = [defaultRecordRuntime.buildPlatform(null)];
7916
+ function createScopedRecordPlatform(records) {
7917
+ return [defaultRecordRuntime.buildPlatform(new Set(records))];
7918
+ }
7919
+ function createInMemoryRecordApi(cache3, workspace, defs2) {
7920
+ const compiled = /* @__PURE__ */ new Map();
7921
+ for (const def of defs2) {
7922
+ const stateType = east.toEastTypeValue(def.stateType);
7923
+ const mutations = /* @__PURE__ */ new Map();
7924
+ for (const m2 of def.mutations) {
7925
+ mutations.set(m2.name, { argTypes: m2.argTypes.map((t4) => east.toEastTypeValue(t4)), reduce: m2.reduce });
7926
+ }
7927
+ const genesis = {
7928
+ hash: `${def.name}-0`.padEnd(64, "0"),
7929
+ parent: east.none,
7930
+ state: `${def.name}-state-0`.padEnd(64, "0"),
7931
+ mutation: "$init",
7932
+ actor: "memory",
7933
+ at: /* @__PURE__ */ new Date(0)
7934
+ };
7935
+ compiled.set(def.name, { stateType, mutations, commits: [genesis], seq: 0 });
7936
+ void cache3.write(workspace, recordPath(def.name), east.encodeBeast2For(stateType)(def.initial));
7937
+ }
7938
+ return {
7939
+ async describe(_ws, record) {
7940
+ const c2 = compiled.get(record);
7941
+ if (!c2) throw new Error(`no in-memory record "${record}"`);
7942
+ return {
7943
+ name: record,
7944
+ mutations: [...c2.mutations].map(([name, m2]) => ({ name, argTypes: m2.argTypes }))
7945
+ };
7946
+ },
7947
+ async mutate(ws, record, mutation, req) {
7948
+ const c2 = compiled.get(record);
7949
+ if (!c2) throw new Error(`no in-memory record "${record}"`);
7950
+ const m2 = c2.mutations.get(mutation);
7951
+ if (!m2) return { outcome: east.variant("invalid", { message: `no mutation "${mutation}"` }) };
7952
+ const current = cache3.read(ws, recordPath(record));
7953
+ const state = current !== void 0 ? east.decodeBeast2For(c2.stateType)(current) : void 0;
7954
+ const args = req.args.map((bytes, i) => east.decodeBeast2For(m2.argTypes[i])(bytes));
7955
+ const next2 = m2.reduce(state, ...args);
7956
+ await cache3.write(ws, recordPath(record), east.encodeBeast2For(c2.stateType)(next2));
7957
+ c2.seq += 1;
7958
+ const hash2 = `${record}-${c2.seq}`.padEnd(64, "0");
7959
+ c2.commits.unshift({
7960
+ hash: hash2,
7961
+ parent: east.some(c2.commits[0].hash),
7962
+ state: `${record}-state-${c2.seq}`.padEnd(64, "0"),
7963
+ mutation,
7964
+ actor: "memory",
7965
+ at: /* @__PURE__ */ new Date(0)
7966
+ });
7967
+ return { outcome: east.variant("committed", { commitHash: hash2, stateHash: `${record}-state-${c2.seq}`.padEnd(64, "0") }) };
7968
+ },
7969
+ async history(_ws, record, limit2) {
7970
+ const c2 = compiled.get(record);
7971
+ if (!c2) throw new Error(`no in-memory record "${record}"`);
7972
+ const commits = limit2 !== void 0 ? c2.commits.slice(0, limit2) : c2.commits;
7973
+ return { commits };
7974
+ }
7975
+ };
7976
+ }
7977
+ platform.registerReactiveTracker({
7978
+ id: "record-bind",
7979
+ enableTracking: () => defaultRecordRuntime.enableTracking(),
7980
+ disableTracking: () => defaultRecordRuntime.disableTracking(),
7981
+ getStore: () => ({
7982
+ subscribe: (key, cb) => defaultRecordRuntime.subscribe(key, cb),
7983
+ getKeyVersion: (key) => defaultRecordRuntime.getKeyVersion(key)
7984
+ })
7985
+ });
7986
+ platform.registerPlatformImplementation(RecordPlatform);
7472
7987
  const E3ConfigContext = React.createContext(null);
7473
7988
  function E3Provider({ children: children2, config: config2, queryClient: externalClient }) {
7474
7989
  const client = React.useMemo(
@@ -7519,11 +8034,21 @@ function ReactiveDatasetProvider({
7519
8034
  );
7520
8035
  }
7521
8036
  }, [e3.apiUrl, e3.repo, e3.workspace]);
8037
+ React.useMemo(() => {
8038
+ if (e3.workspace !== void 0) {
8039
+ initializeRecordApi(
8040
+ createDefaultRecordApi(e3.apiUrl, e3.repo ?? "default", () => tokenRef.current),
8041
+ cache3,
8042
+ e3.workspace
8043
+ );
8044
+ }
8045
+ }, [e3.apiUrl, e3.repo, e3.workspace, cache3]);
7522
8046
  React.useEffect(() => {
7523
8047
  return () => {
7524
8048
  const ws = cache3.getConfig().workspace;
7525
8049
  clearReactiveDatasetCache();
7526
8050
  clearFunctionApi();
8051
+ clearRecordApi();
7527
8052
  cache3.destroy();
7528
8053
  if (ws) clearBindingRegistry(ws);
7529
8054
  else clearBindingRegistry();
@@ -60731,30 +61256,37 @@ function RefusalZone({ refusal, overlap, naiveValue, outcome }) {
60731
61256
  ] });
60732
61257
  }
60733
61258
  function ValidatePanel({ vm, barList }) {
60734
- const treatedPct = Math.round(vm.primary.treatedShare * 100);
61259
+ const stat = react.useSlotRecipe({ key: "stat" })({ size: "lg" });
61260
+ const meter = react.useSlotRecipe({ key: "segmentedMeter" })({});
60735
61261
  return /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { p: "4.5", children: [
60736
61262
  /* @__PURE__ */ jsxRuntime.jsx(Cap, { help: "tab_validate", children: vm.headline }),
60737
61263
  /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { display: "flex", alignItems: "flex-end", gap: "5", flexWrap: "wrap", children: [
60738
- /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { display: "flex", flexDirection: "column", gap: "0.5", children: [
60739
- /* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "mono.sm", color: "fg.muted", children: /* @__PURE__ */ jsxRuntime.jsx(Help, { id: "validate_size", children: vm.holdback ? "to hold back from" : "to run" }) }),
60740
- vm.faint ? /* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "body.sm", fontWeight: "semibold", color: "fg.warning", maxW: "220px", children: "Effect too faint to size — set a materiality threshold to size a trial." }) : /* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "mono-kpi", fontFamily: "heading", fontSize: "32px", color: "brand.solid", children: vm.primary.nTotal.toLocaleString() })
61264
+ /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { css: stat.root, children: [
61265
+ /* @__PURE__ */ jsxRuntime.jsx(react.Text, { css: stat.label, children: /* @__PURE__ */ jsxRuntime.jsx(Help, { id: "validate_size", children: vm.holdback ? "to hold back from" : "to run" }) }),
61266
+ vm.faint ? /* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "body.sm", fontWeight: "semibold", color: "fg.warning", maxW: "220px", children: "Effect too faint to size — set a materiality threshold to size a trial." }) : /* @__PURE__ */ jsxRuntime.jsx(react.Text, { css: stat.valueText, color: "brand.solid", children: vm.primary.nTotal.toLocaleString() })
60741
61267
  ] }),
60742
- /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { flex: "1", minW: "220px", children: [
60743
- /* @__PURE__ */ jsxRuntime.jsx(Cap, { help: "validate_split", children: vm.holdback ? "Hold-back split" : "Split" }),
60744
- /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { display: "flex", h: "10px", borderRadius: "full", overflow: "hidden", borderWidth: "1px", borderColor: "border.subtle", children: [
60745
- /* @__PURE__ */ jsxRuntime.jsx(react.Box, { bg: "brand.solid", width: `${treatedPct}%` }),
60746
- /* @__PURE__ */ jsxRuntime.jsx(react.Box, { bg: "bg.emphasized", flex: "1" })
61268
+ /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { css: meter.root, flex: "1", minW: "220px", children: [
61269
+ /* @__PURE__ */ jsxRuntime.jsx(react.Text, { css: meter.label, children: /* @__PURE__ */ jsxRuntime.jsx(Help, { id: "validate_split", children: vm.holdback ? "Hold-back split" : "Split" }) }),
61270
+ /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { css: meter.track, children: [
61271
+ /* @__PURE__ */ jsxRuntime.jsx(react.Box, { css: meter.segment, flex: vm.primary.treatedShare, bg: "brand.solid" }),
61272
+ /* @__PURE__ */ jsxRuntime.jsx(react.Box, { css: meter.segment, flex: 1 - vm.primary.treatedShare, bg: "bg.emphasized" })
60747
61273
  ] }),
60748
- /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { display: "flex", justifyContent: "space-between", mt: "1.5", children: [
60749
- /* @__PURE__ */ jsxRuntime.jsxs(react.Text, { textStyle: "caption", children: [
60750
- /* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", color: "brand.fg", fontWeight: "semibold", children: vm.primary.nTreated.toLocaleString() }),
60751
- " ",
60752
- vm.holdback ? "treated" : "get it"
61274
+ /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { css: meter.keyRow, justifyContent: "space-between", children: [
61275
+ /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { css: meter.keyItem, children: [
61276
+ /* @__PURE__ */ jsxRuntime.jsx(react.Box, { css: meter.keyDot, bg: "brand.solid" }),
61277
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
61278
+ /* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", css: meter.valueText, children: vm.primary.nTreated.toLocaleString() }),
61279
+ " ",
61280
+ vm.holdback ? "treated" : "get it"
61281
+ ] })
60753
61282
  ] }),
60754
- /* @__PURE__ */ jsxRuntime.jsxs(react.Text, { textStyle: "caption", children: [
60755
- /* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", color: "fg.default", fontWeight: "semibold", children: vm.primary.nControl.toLocaleString() }),
60756
- " ",
60757
- vm.holdback ? "held back" : "left alone"
61283
+ /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { css: meter.keyItem, children: [
61284
+ /* @__PURE__ */ jsxRuntime.jsx(react.Box, { css: meter.keyDot, bg: "bg.emphasized" }),
61285
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
61286
+ /* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", css: meter.valueText, children: vm.primary.nControl.toLocaleString() }),
61287
+ " ",
61288
+ vm.holdback ? "held back" : "left alone"
61289
+ ] })
60758
61290
  ] })
60759
61291
  ] })
60760
61292
  ] })
@@ -63046,7 +63578,7 @@ const UITaskPreview = React.memo(function UITaskPreview2({
63046
63578
  const manifest = React.useMemo(() => {
63047
63579
  if (!details || !isUI) return null;
63048
63580
  const meta3 = getTaskMetadata(details);
63049
- return meta3 ? internal.decodeManifest(meta3) : { paths: [], functions: [] };
63581
+ return meta3 ? internal.decodeManifest(meta3) : { paths: [], functions: [], records: [] };
63050
63582
  }, [details, isUI]);
63051
63583
  const outputPath = details ? treePathToString$1(details.output) : null;
63052
63584
  const preloads = React.useMemo(
@@ -63059,6 +63591,7 @@ const UITaskPreview = React.memo(function UITaskPreview2({
63059
63591
  ...eastUiComponents.StateImpl,
63060
63592
  ...createScopedBindPlatform(manifest),
63061
63593
  ...createScopedFuncPlatform(manifest.functions),
63594
+ ...createScopedRecordPlatform(manifest.records),
63062
63595
  ...eastUiComponents.OverlayImpl
63063
63596
  ] : void 0,
63064
63597
  [manifest]
@@ -63656,6 +64189,8 @@ exports.MemoryStagedAdapter = MemoryStagedAdapter;
63656
64189
  exports.ReactiveDatasetCache = ReactiveDatasetCache;
63657
64190
  exports.ReactiveDatasetLoader = ReactiveDatasetLoader;
63658
64191
  exports.ReactiveDatasetProvider = ReactiveDatasetProvider;
64192
+ exports.RecordPlatform = RecordPlatform;
64193
+ exports.RecordRuntime = RecordRuntime;
63659
64194
  exports.StagedStore = StagedStore;
63660
64195
  exports.StatusDisplay = StatusDisplay;
63661
64196
  exports.TaskPreview = TaskPreview;
@@ -63667,17 +64202,22 @@ exports.clearFunctionApi = clearFunctionApi;
63667
64202
  exports.clearPendingWrites = clearPendingWrites;
63668
64203
  exports.clearReactiveDatasetCache = clearReactiveDatasetCache;
63669
64204
  exports.clearReactiveDatasetListCache = clearReactiveDatasetListCache;
64205
+ exports.clearRecordApi = clearRecordApi;
63670
64206
  exports.clearStagedStoreSingleton = clearStagedStoreSingleton;
63671
64207
  exports.createDefaultDatasetApi = createDefaultDatasetApi;
63672
64208
  exports.createDefaultFunctionApi = createDefaultFunctionApi;
64209
+ exports.createDefaultRecordApi = createDefaultRecordApi;
63673
64210
  exports.createInMemoryFunctionApi = createInMemoryFunctionApi;
64211
+ exports.createInMemoryRecordApi = createInMemoryRecordApi;
63674
64212
  exports.createReactiveDatasetCache = createReactiveDatasetCache;
63675
64213
  exports.createScopedBindPlatform = createScopedBindPlatform;
63676
64214
  exports.createScopedFuncPlatform = createScopedFuncPlatform;
64215
+ exports.createScopedRecordPlatform = createScopedRecordPlatform;
63677
64216
  exports.datasetCacheKey = datasetCacheKey;
63678
64217
  exports.datasetPathToString = datasetPathToString;
63679
64218
  exports.defaultBindRuntime = defaultBindRuntime;
63680
64219
  exports.defaultFuncRuntime = defaultFuncRuntime;
64220
+ exports.defaultRecordRuntime = defaultRecordRuntime;
63681
64221
  exports.disableBindingTracking = disableBindingTracking;
63682
64222
  exports.enableBindingTracking = enableBindingTracking;
63683
64223
  exports.formatApiError = formatApiError;
@@ -63690,12 +64230,14 @@ exports.getTaskKind = getTaskKind;
63690
64230
  exports.getTaskMetadata = getTaskMetadata;
63691
64231
  exports.initializeFunctionApi = initializeFunctionApi;
63692
64232
  exports.initializeReactiveDatasetCache = initializeReactiveDatasetCache;
64233
+ exports.initializeRecordApi = initializeRecordApi;
63693
64234
  exports.initializeStagedStore = initializeStagedStore;
63694
64235
  exports.isBindingTracking = isBindingTracking;
63695
64236
  exports.onWriteError = onWriteError;
63696
64237
  exports.preloadReactiveDatasetList = preloadReactiveDatasetList;
63697
64238
  exports.realClock = realClock;
63698
- exports.signatureOfHandleType = signatureOfHandleType;
64239
+ exports.recordChannelKey = recordChannelKey;
64240
+ exports.signatureOfFuncHandleType = signatureOfFuncHandleType;
63699
64241
  exports.trackDatasetPath = trackDatasetPath;
63700
64242
  exports.useDataflowCancel = useDataflowCancel;
63701
64243
  exports.useDataflowExecute = useDataflowExecute;