yaml-flow 6.0.0 → 7.1.0

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.
Files changed (166) hide show
  1. package/board-live-cards-cli.js +4 -4
  2. package/browser/asset-integrity.json +3 -3
  3. package/browser/board-livecards-client.js +2 -0
  4. package/browser/board-livecards-client.js.map +1 -0
  5. package/browser/board-livecards-localstorage.js +10 -0
  6. package/browser/board-livecards-localstorage.js.map +1 -0
  7. package/browser/board-livegraph-engine.js +2 -2
  8. package/browser/board-livegraph-engine.js.map +1 -1
  9. package/browser/card-compute.js +28 -28
  10. package/browser/compute-jsonata.js +5 -0
  11. package/browser/compute-jsonata.js.map +1 -0
  12. package/browser/live-cards.js +264 -151
  13. package/card-store.js +4 -4
  14. package/dist/{board-live-cards-public-CltXYgaY.d.cts → board-live-cards-public-5n1-syA3.d.cts} +8 -5
  15. package/dist/{board-live-cards-public-f-E-FAyp.d.ts → board-live-cards-public-CK_J8uv0.d.ts} +8 -5
  16. package/dist/board-livegraph-runtime/index.cjs +2 -2
  17. package/dist/board-livegraph-runtime/index.cjs.map +1 -1
  18. package/dist/board-livegraph-runtime/index.d.cts +11 -9
  19. package/dist/board-livegraph-runtime/index.d.ts +11 -9
  20. package/dist/board-livegraph-runtime/index.js +2 -2
  21. package/dist/board-livegraph-runtime/index.js.map +1 -1
  22. package/dist/board-livegraph-runtime/jsonata-sync.cjs +37 -1
  23. package/dist/card-compute/index.cjs +4 -4
  24. package/dist/card-compute/index.cjs.map +1 -1
  25. package/dist/card-compute/index.d.cts +5 -1
  26. package/dist/card-compute/index.d.ts +5 -1
  27. package/dist/card-compute/index.js +4 -4
  28. package/dist/card-compute/index.js.map +1 -1
  29. package/dist/card-compute/jsonata-sync.cjs +37 -1
  30. package/dist/cli/browser-api/board-live-cards-browser-adapter.cjs +2 -1
  31. package/dist/cli/browser-api/board-live-cards-browser-adapter.cjs.map +1 -1
  32. package/dist/cli/browser-api/board-live-cards-browser-adapter.d.cts +27 -14
  33. package/dist/cli/browser-api/board-live-cards-browser-adapter.d.ts +27 -14
  34. package/dist/cli/browser-api/board-live-cards-browser-adapter.js +2 -1
  35. package/dist/cli/browser-api/board-live-cards-browser-adapter.js.map +1 -1
  36. package/dist/cli/browser-api/card-store-browser-api.cjs +1 -1
  37. package/dist/cli/browser-api/card-store-browser-api.cjs.map +1 -1
  38. package/dist/cli/browser-api/card-store-browser-api.js +1 -1
  39. package/dist/cli/browser-api/card-store-browser-api.js.map +1 -1
  40. package/dist/cli/browser-api/jsonata-sync.cjs +37 -1
  41. package/dist/cli/node/artifacts-store-cli.cjs +8 -8
  42. package/dist/cli/node/artifacts-store-cli.cjs.map +1 -1
  43. package/dist/cli/node/artifacts-store-cli.js +8 -8
  44. package/dist/cli/node/artifacts-store-cli.js.map +1 -1
  45. package/dist/cli/node/board-live-cards-cli.cjs +7 -7
  46. package/dist/cli/node/board-live-cards-cli.cjs.map +1 -1
  47. package/dist/cli/node/board-live-cards-cli.js +7 -7
  48. package/dist/cli/node/board-live-cards-cli.js.map +1 -1
  49. package/dist/cli/node/card-store-cli.cjs +5 -5
  50. package/dist/cli/node/card-store-cli.cjs.map +1 -1
  51. package/dist/cli/node/card-store-cli.js +5 -5
  52. package/dist/cli/node/card-store-cli.js.map +1 -1
  53. package/dist/cli/node/execution-adapter.cjs +3 -0
  54. package/dist/cli/node/execution-adapter.cjs.map +1 -0
  55. package/dist/cli/node/execution-adapter.d.cts +174 -0
  56. package/dist/cli/node/execution-adapter.d.ts +174 -0
  57. package/dist/cli/node/execution-adapter.js +3 -0
  58. package/dist/cli/node/execution-adapter.js.map +1 -0
  59. package/dist/cli/node/fs-board-adapter.cjs +7 -7
  60. package/dist/cli/node/fs-board-adapter.cjs.map +1 -1
  61. package/dist/cli/node/fs-board-adapter.d.cts +2 -2
  62. package/dist/cli/node/fs-board-adapter.d.ts +2 -2
  63. package/dist/cli/node/fs-board-adapter.js +7 -7
  64. package/dist/cli/node/fs-board-adapter.js.map +1 -1
  65. package/dist/cli/node/jsonata-sync.cjs +37 -1
  66. package/dist/cli/node/source-cli-task-executor.cjs +4 -4
  67. package/dist/cli/node/source-cli-task-executor.cjs.map +1 -1
  68. package/dist/cli/node/source-cli-task-executor.js +4 -4
  69. package/dist/cli/node/source-cli-task-executor.js.map +1 -1
  70. package/dist/continuous-event-graph/index.cjs +2 -2
  71. package/dist/continuous-event-graph/index.cjs.map +1 -1
  72. package/dist/continuous-event-graph/index.js +2 -2
  73. package/dist/continuous-event-graph/index.js.map +1 -1
  74. package/dist/continuous-event-graph/jsonata-sync.cjs +37 -1
  75. package/dist/execution-refs.cjs +2 -1
  76. package/dist/execution-refs.cjs.map +1 -1
  77. package/dist/execution-refs.d.cts +55 -12
  78. package/dist/execution-refs.d.ts +55 -12
  79. package/dist/execution-refs.js +2 -1
  80. package/dist/execution-refs.js.map +1 -1
  81. package/dist/index.cjs +10 -10
  82. package/dist/index.cjs.map +1 -1
  83. package/dist/index.js +10 -10
  84. package/dist/index.js.map +1 -1
  85. package/dist/jsonata-sync.cjs +37 -1
  86. package/dist/server-runtime/index.cjs +9 -0
  87. package/dist/server-runtime/index.cjs.map +1 -0
  88. package/dist/server-runtime/index.d.cts +31 -0
  89. package/dist/server-runtime/index.d.ts +31 -0
  90. package/dist/server-runtime/index.js +9 -0
  91. package/dist/server-runtime/index.js.map +1 -0
  92. package/dist/server-runtime/jsonata-sync.cjs +7623 -0
  93. package/dist/step-machine-public/index.cjs +3 -0
  94. package/dist/step-machine-public/index.cjs.map +1 -0
  95. package/dist/step-machine-public/index.d.cts +166 -0
  96. package/dist/step-machine-public/index.d.ts +166 -0
  97. package/dist/step-machine-public/index.js +3 -0
  98. package/dist/step-machine-public/index.js.map +1 -0
  99. package/dist/step-machine-public/jsonata-sync.cjs +7623 -0
  100. package/dist/storage-refs.cjs +2 -2
  101. package/dist/storage-refs.cjs.map +1 -1
  102. package/dist/storage-refs.d.cts +6 -6
  103. package/dist/storage-refs.d.ts +6 -6
  104. package/dist/storage-refs.js +2 -2
  105. package/dist/storage-refs.js.map +1 -1
  106. package/dist/types-CU3DjTKL.d.cts +147 -0
  107. package/dist/types-HGDTWIun.d.ts +147 -0
  108. package/examples/browser/boards/portfolio-tracker/portfolio-t4.js +9 -10
  109. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-http-test.js +370 -0
  110. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-http-test.py +398 -0
  111. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-public.js +9 -10
  112. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-server.js +300 -0
  113. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-server.py +617 -0
  114. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-sse-worker.js +48 -0
  115. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.py +11 -10
  116. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +19 -4
  117. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +4 -8
  118. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +6 -10
  119. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/poll-status-cli.js +8 -16
  120. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +2 -6
  121. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +4 -8
  122. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/status-cli.js +3 -7
  123. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +4 -8
  124. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +7 -16
  125. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +2 -6
  126. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/_board_pycli.py +13 -3
  127. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/add-cards.py +2 -1
  128. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/init-board.py +2 -1
  129. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/poll-status.py +2 -1
  130. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +20 -24
  131. package/examples/cli/step-machine-cli/portfolio-tracker/run-inline-python-demo-pycli.py +0 -3
  132. package/examples/cli/step-machine-demo/jsonata-init-board-cli.js +8 -13
  133. package/examples/cli/step-machine-demo/jsonata-init-board.flow.yaml +33 -9
  134. package/examples/cli/step-machine-demo/one-step-cli-only.flow.yaml +3 -1
  135. package/examples/cli/step-machine-demo/step2-double-cli.js +6 -12
  136. package/examples/cli/step-machine-demo/two-step-math.flow.yaml +66 -4
  137. package/examples/cli/step-machine-demo/two-step-mixed.flow.yaml +13 -5
  138. package/examples/example-board/agent-instructions.md +1 -1
  139. package/examples/example-board/cards/card-my-identity.json +30 -6
  140. package/examples/example-board/cards/card-portfolio-action.json +24 -6
  141. package/examples/example-board/cards/card-portfolio-intelligence.json +97 -0
  142. package/examples/example-board/cards/card-portfolio-risks.json +24 -6
  143. package/examples/example-board/cards/card-rebalance-impact.json +22 -6
  144. package/examples/example-board/cards/card-rebalance-sim.json +66 -15
  145. package/examples/example-board/cards/cardT-market-prices.json +80 -0
  146. package/examples/example-board/cards/{card-portfolio-value.json → cardT-portfolio-value.json} +38 -10
  147. package/examples/example-board/cards/cardT-portfolio.json +78 -0
  148. package/examples/example-board/demo-server-config.json +1 -1
  149. package/examples/example-board/demo-server.js +383 -69
  150. package/examples/example-board/demo-shell-localstorage.html +774 -0
  151. package/examples/example-board/demo-shell-with-server.html +18 -36
  152. package/examples/example-board/demo-shell.html +5 -4
  153. package/examples/example-board/demo-task-executor.js +213 -265
  154. package/package.json +15 -13
  155. package/step-machine-cli.js +43 -310
  156. package/board-livecards-server-runtime.js +0 -1513
  157. package/browser/board-livecards-runtime-client.js +0 -263
  158. package/dist/pycli/quickjs-board-runtime.global.js +0 -9
  159. package/dist/pycli/quickjs-board-runtime.global.js.map +0 -1
  160. package/dist/pycli/quickjs-step-machine-runtime.global.js +0 -5
  161. package/dist/pycli/quickjs-step-machine-runtime.global.js.map +0 -1
  162. package/examples/cli/step-machine-demo/two-step-math-handlers.js +0 -32
  163. package/examples/cli/step-machine-demo/two-step-mixed-handlers.js +0 -24
  164. package/examples/example-board/cards/card-market-prices.json +0 -56
  165. package/examples/example-board/cards/card-portfolio.json +0 -44
  166. package/examples/example-board/demo-shell-browser.html +0 -675
@@ -1,4 +1,4 @@
1
- (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.jsonata = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
1
+ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.jsonataSync = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
2
2
  /**
3
3
  * © Copyright IBM Corp. 2018 All Rights Reserved
4
4
  * Project name: JSONata
@@ -2839,6 +2839,10 @@ const functions = (() => {
2839
2839
  }
2840
2840
 
2841
2841
  var result = createSequence();
2842
+ // PATCH: $map must always return an array (never collapse to scalar),
2843
+ // even when the input has exactly 1 item. keepSingleton prevents the
2844
+ // length===1 → result[0] collapse in evaluate().
2845
+ result.keepSingleton = true;
2842
2846
  // do the map - iterate over the arrays, and invoke func
2843
2847
  for (var i = 0; i < arr.length; i++) {
2844
2848
  var func_args = hofFuncArgs(func, arr[i], i, arr);
@@ -2865,6 +2869,8 @@ const functions = (() => {
2865
2869
  }
2866
2870
 
2867
2871
  var result = createSequence();
2872
+ // PATCH: $filter must always return an array (never scalar)
2873
+ result.keepSingleton = true;
2868
2874
 
2869
2875
  for (var i = 0; i < arr.length; i++) {
2870
2876
  var entry = arr[i];
@@ -3022,6 +3028,8 @@ const functions = (() => {
3022
3028
  } else if (arg !== null && typeof arg === 'object' && !isFunction(arg)) {
3023
3029
  Object.keys(arg).forEach(key => result.push(key));
3024
3030
  }
3031
+ // PATCH: $keys must always return an array (never scalar)
3032
+ result.keepSingleton = true;
3025
3033
  return result;
3026
3034
  }
3027
3035
 
@@ -3096,6 +3104,8 @@ const functions = (() => {
3096
3104
  */
3097
3105
  function spread(arg) {
3098
3106
  var result = createSequence();
3107
+ // PATCH: $spread must always return an array (never scalar)
3108
+ result.keepSingleton = true;
3099
3109
 
3100
3110
  if (Array.isArray(arg)) {
3101
3111
  // spread all of the items in the array
@@ -3168,6 +3178,8 @@ const functions = (() => {
3168
3178
  */
3169
3179
  function each(obj, func) {
3170
3180
  var result = createSequence();
3181
+ // PATCH: $each must always return an array (never scalar)
3182
+ result.keepSingleton = true;
3171
3183
 
3172
3184
  for (var key in obj) {
3173
3185
  var func_args = hofFuncArgs(func, obj[key], key, obj);
@@ -3600,6 +3612,7 @@ var jsonata = (function() {
3600
3612
  var resultSequence;
3601
3613
  var isTupleStream = false;
3602
3614
  var tupleBindings = undefined;
3615
+ var traversedArray = false; // PATCH: track if any step iterated over an array
3603
3616
 
3604
3617
  // evaluate each step in turn
3605
3618
  for(var ii = 0; ii < expr.steps.length; ii++) {
@@ -3624,6 +3637,11 @@ var jsonata = (function() {
3624
3637
  break;
3625
3638
  }
3626
3639
 
3640
+ // PATCH: detect array traversal from the step result
3641
+ if (!isTupleStream && Array.isArray(resultSequence) && resultSequence._fromArrayField) {
3642
+ traversedArray = true;
3643
+ }
3644
+
3627
3645
  if(typeof step.focus === 'undefined') {
3628
3646
  inputSequence = resultSequence;
3629
3647
  }
@@ -3650,6 +3668,12 @@ var jsonata = (function() {
3650
3668
  resultSequence.keepSingleton = true;
3651
3669
  }
3652
3670
 
3671
+ // PATCH: Always preserve array results from path expressions that traversed
3672
+ // through an array. Ensures predictable array output regardless of input cardinality.
3673
+ if (traversedArray && Array.isArray(resultSequence) && resultSequence.sequence) {
3674
+ Object.defineProperty(resultSequence, 'keepSingleton', { value: true, enumerable: false, configurable: true });
3675
+ }
3676
+
3653
3677
  if (expr.hasOwnProperty('group')) {
3654
3678
  resultSequence = evaluateGroupExpression(expr.group, isTupleStream ? tupleBindings : resultSequence, environment)
3655
3679
  }
@@ -3698,8 +3722,10 @@ var jsonata = (function() {
3698
3722
  }
3699
3723
 
3700
3724
  var resultSequence = createSequence();
3725
+ var _flattenedArray = false; // PATCH: track if we flattened a real array
3701
3726
  if(lastStep && result.length === 1 && Array.isArray(result[0]) && !isSequence(result[0])) {
3702
3727
  resultSequence = result[0];
3728
+ _flattenedArray = true;
3703
3729
  } else {
3704
3730
  // flatten the sequence
3705
3731
  result.forEach(function(res) {
@@ -3709,10 +3735,20 @@ var jsonata = (function() {
3709
3735
  } else {
3710
3736
  // res is a sequence - flatten it into the parent sequence
3711
3737
  res.forEach(val => resultSequence.push(val));
3738
+ // PATCH: only mark as flattened array if res is a genuine data array,
3739
+ // not an intermediate sequence (from filter results, etc.)
3740
+ if (!isSequence(res)) {
3741
+ _flattenedArray = true;
3742
+ }
3712
3743
  }
3713
3744
  });
3714
3745
  }
3715
3746
 
3747
+ // PATCH: Mark sequences that came from flattening array fields
3748
+ if (_flattenedArray && Array.isArray(resultSequence)) {
3749
+ Object.defineProperty(resultSequence, '_fromArrayField', { value: true, enumerable: false, configurable: true });
3750
+ }
3751
+
3716
3752
  return resultSequence;
3717
3753
  }
3718
3754
 
@@ -1,2 +1,3 @@
1
- 'use strict';function h(r){return `::${r.kind}::${r.value}`}function g(r){if(!r.startsWith("::"))throw new Error(`Invalid ref format (expected ::kind::value): ${r}`);let e=r.slice(2),a=e.indexOf("::");if(a===-1)throw new Error(`Invalid ref format (expected ::kind::value): ${r}`);return {kind:e.slice(0,a),value:e.slice(a+2)}}function f(r){if(r==null||typeof r!="object")return JSON.stringify(r);if(Array.isArray(r))return `[${r.map(f).join(",")}]`;let e=r;return `{${Object.keys(e).sort().map(i=>`${JSON.stringify(i)}:${f(e[i])}`).join(",")}}`}function d(r,e){let a=e>>>0;for(let i=0;i<r.length;i++)a^=r.charCodeAt(i),a=Math.imul(a,16777619)>>>0;return a}function w(r){let e=f(r),a=d(e,2166136261),i=d(e,3735928559),o=d(e,19088743),n=d(e,4277009102);return [a,i,o,n].map(t=>t.toString(16).padStart(8,"0")).join("")}function y(r){function e(n){return `${r}:blob:${n}`}let a=new TextEncoder;function i(n){if(typeof btoa=="function"){let t="";for(let s=0;s<n.length;s++)t+=String.fromCharCode(n[s]);return btoa(t)}return ""}function o(n){if(typeof atob=="function"){let t=atob(n),s=new Uint8Array(t.length);for(let l=0;l<t.length;l++)s[l]=t.charCodeAt(l);return s}return new Uint8Array}return {read(n){return globalThis.localStorage.getItem(e(n))},write(n,t){globalThis.localStorage.setItem(e(n),t);},exists(n){return globalThis.localStorage.getItem(e(n))!==null},remove(n){globalThis.localStorage.removeItem(e(n));},readBytes(n){let t=globalThis.localStorage.getItem(e(n));if(t===null)return null;try{let s=JSON.parse(t);if(s&&s.__kind==="bytes-b64"&&typeof s.data=="string")return o(s.data)}catch{}return a.encode(t)},writeBytes(n,t){let s=JSON.stringify({__kind:"bytes-b64",data:i(t)});globalThis.localStorage.setItem(e(n),s);},listKeys(n){let t=e(n??""),s=[];for(let l=0;l<globalThis.localStorage.length;l++){let u=globalThis.localStorage.key(l);u&&u.startsWith(t)&&s.push(u.slice(e("").length));}return s.sort()},stat(n){let t=globalThis.localStorage.getItem(e(n));if(t===null)return null;let s=a.encode(t).byteLength;try{let l=JSON.parse(t);l&&l.__kind==="bytes-b64"&&typeof l.data=="string"&&(s=o(l.data).byteLength);}catch{}return {key:n,size:s}}}}function p(r){function e(a){return `${r}:kv:${a}`}return {read(a){let i=globalThis.localStorage.getItem(e(a));if(i===null)return null;try{return JSON.parse(i)}catch{return null}},write(a,i){globalThis.localStorage.setItem(e(a),JSON.stringify(i));},delete(a){globalThis.localStorage.removeItem(e(a));},listKeys(a){let i=e(a??""),o=[];for(let n=0;n<globalThis.localStorage.length;n++){let t=globalThis.localStorage.key(n);t!==null&&t.startsWith(i)&&o.push(t.slice(e("").length));}return o}}}function k(r){function e(){let i=globalThis.localStorage.getItem(r);if(!i)return [];try{return JSON.parse(i)}catch{return []}}function a(i){globalThis.localStorage.setItem(r,JSON.stringify(i));}return {readAllEntries(){return e()},appendEntry(i){let o=e();o.push(i),a(o);},generateId(){return globalThis.crypto.randomUUID()}}}function S(){let r=false;return {tryAcquire(){return r?null:(r=true,()=>{r=false;})}}}function A(r,e){let a=e?.callbackBaseUrl?{meta:"board-live-cards",howToRun:"http:post",whatToRun:e.callbackBaseUrl}:{meta:"board-live-cards",howToRun:"built-in",whatToRun:"::built-in::board-live-cards-browser"},i=S();return {kvStorage:o=>p(`${r}:${o}`),blobStorage:o=>y(o?`${r}:${o}`:r),journalAdapter:()=>k(`${r}:journal`),lock:i,selfRef:a,async dispatchExecution(o,n){if(o.howToRun==="http:post")try{let t=o.whatToRun.startsWith("::")?g(o.whatToRun).value:o.whatToRun,s=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)});return s.ok?{dispatched:!0}:{dispatched:!1,error:`HTTP ${s.status}: ${s.statusText}`}}catch(t){return {dispatched:false,error:t instanceof Error?t.message:String(t)}}if(o.howToRun==="http:get")try{let t=o.whatToRun.startsWith("::")?g(o.whatToRun).value:o.whatToRun,s=new URLSearchParams(Object.entries(n).filter(([,c])=>c!=null).map(([c,b])=>[c,String(b)])),l=`${t}?${s.toString()}`,u=await fetch(l);return u.ok?{dispatched:!0}:{dispatched:!1,error:`HTTP ${u.status}: ${u.statusText}`}}catch(t){return {dispatched:false,error:t instanceof Error?t.message:String(t)}}return {dispatched:false,error:`Browser adapter: only http:post and http:get dispatch are supported (got: ${o.howToRun})`}},resolveBlob(o){let t=y(r).read(o.value);if(t===null)throw new Error(`resolveBlob: blob not found: ${h(o)}`);return t},hashFn:w,genId:()=>globalThis.crypto.randomUUID().replace(/-/g,""),kvStorageForRef:o=>p(g(o).value),onWarn:e?.onWarn}}exports.createBrowserBoardPlatformAdapter=A;//# sourceMappingURL=board-live-cards-browser-adapter.cjs.map
1
+ 'use strict';var y="b64:";function B(e){let t=new TextEncoder().encode(e),o=globalThis.Buffer,n;if(o)n=o.from(t).toString("base64");else if(typeof btoa=="function"){let l="";for(let i of t)l+=String.fromCharCode(i);n=btoa(l);}else throw new Error("No base64 encoder available in this runtime");return n.replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/g,"")}function T(e){let t=e.replace(/-/g,"+").replace(/_/g,"/")+"=".repeat((4-e.length%4)%4),o=globalThis.Buffer;if(o)return o.from(t,"base64").toString("utf8");if(typeof atob=="function"){let n=atob(t),l=new Uint8Array(n.length);for(let i=0;i<n.length;i+=1)l[i]=n.charCodeAt(i);return new TextDecoder().decode(l)}throw new Error("No base64 decoder available in this runtime")}function g(e){return `${y}${B(JSON.stringify(e))}`}function d(e){if(!e.startsWith(y))throw new Error(`Invalid ref format (expected ${y}<base64url(json)>): ${e}`);let t;try{t=JSON.parse(T(e.slice(y.length)));}catch{throw new Error(`Invalid ref format (malformed base64url/json): ${e}`)}if(!t||typeof t!="object")throw new Error(`Invalid ref format (expected object payload): ${e}`);let o=t;if(typeof o.kind!="string"||typeof o.value!="string")throw new Error(`Invalid ref format (payload must contain string kind/value): ${e}`);return {kind:o.kind,value:o.value}}function h(e){if(e==null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return `[${e.map(h).join(",")}]`;let t=e;return `{${Object.keys(t).sort().map(n=>`${JSON.stringify(n)}:${h(t[n])}`).join(",")}}`}function p(e,t){let o=t>>>0;for(let n=0;n<e.length;n++)o^=e.charCodeAt(n),o=Math.imul(o,16777619)>>>0;return o}function k(e){let t=h(e),o=p(t,2166136261),n=p(t,3735928559),l=p(t,19088743),i=p(t,4277009102);return [o,n,l,i].map(r=>r.toString(16).padStart(8,"0")).join("")}function w(e){function t(i){return `${e}:blob:${i}`}let o=new TextEncoder;function n(i){if(typeof btoa=="function"){let r="";for(let s=0;s<i.length;s++)r+=String.fromCharCode(i[s]);return btoa(r)}return ""}function l(i){if(typeof atob=="function"){let r=atob(i),s=new Uint8Array(r.length);for(let a=0;a<r.length;a++)s[a]=r.charCodeAt(a);return s}return new Uint8Array}return {read(i){return globalThis.localStorage.getItem(t(i))},write(i,r){globalThis.localStorage.setItem(t(i),r);},exists(i){return globalThis.localStorage.getItem(t(i))!==null},remove(i){globalThis.localStorage.removeItem(t(i));},readBytes(i){let r=globalThis.localStorage.getItem(t(i));if(r===null)return null;try{let s=JSON.parse(r);if(s&&s.__kind==="bytes-b64"&&typeof s.data=="string")return l(s.data)}catch{}return o.encode(r)},writeBytes(i,r){let s=JSON.stringify({__kind:"bytes-b64",data:n(r)});globalThis.localStorage.setItem(t(i),s);},listKeys(i){let r=t(i??""),s=[];for(let a=0;a<globalThis.localStorage.length;a++){let u=globalThis.localStorage.key(a);u&&u.startsWith(r)&&s.push(u.slice(t("").length));}return s.sort()},stat(i){let r=globalThis.localStorage.getItem(t(i));if(r===null)return null;let s=o.encode(r).byteLength;try{let a=JSON.parse(r);a&&a.__kind==="bytes-b64"&&typeof a.data=="string"&&(s=l(a.data).byteLength);}catch{}return {key:i,size:s}}}}function m(e){function t(o){return `${e}:kv:${o}`}return {read(o){let n=globalThis.localStorage.getItem(t(o));if(n===null)return null;try{return JSON.parse(n)}catch{return null}},write(o,n){globalThis.localStorage.setItem(t(o),JSON.stringify(n));},delete(o){globalThis.localStorage.removeItem(t(o));},listKeys(o){let n=t(o??""),l=[];for(let i=0;i<globalThis.localStorage.length;i++){let r=globalThis.localStorage.key(i);r!==null&&r.startsWith(n)&&l.push(r.slice(t("").length));}return l}}}function S(e){function t(){let n=globalThis.localStorage.getItem(e);if(!n)return [];try{return JSON.parse(n)}catch{return []}}function o(n){globalThis.localStorage.setItem(e,JSON.stringify(n));}return {readAllEntries(){return t()},appendEntry(n){let l=t();l.push(n),o(l);},generateId(){return globalThis.crypto.randomUUID()}}}function J(){let e=false;return {tryAcquire(){return e?null:(e=true,()=>{e=false;})}}}var v=new Map;function x(e){let t=v.get(e);if(!t){let o=new Set;t={publish(n){for(let l of o)l(n);},subscribe(n){return o.add(n),()=>{o.delete(n);}}},v.set(e,t);}return t}function C(){return {async subscribe(e,t){return e.kind!=="in-memory-bus"?(console.warn(`[in-memory-transport] unsupported kind: ${e.kind}`),()=>{}):x(e.value).subscribe(n=>{let l=n;if(l&&l.kind==="notification-batch"&&Array.isArray(l.notifications)){for(let i of l.notifications)t(i);return}t(n);})}}}function N(e,t){let o=t?.callbackBaseUrl?{meta:"board-live-cards",howToRun:"http:post",whatToRun:t.callbackBaseUrl}:{meta:"board-live-cards",howToRun:"in-browser",whatToRun:g({kind:"in-browser",value:e})},n=new Map,l=new Map,i=J();return {kvStorage:r=>m(`${e}:${r}`),blobStorage:r=>w(r?`${e}:${r}`:e),journalAdapter:()=>S(`${e}:journal`),lock:i,selfRef:o,async dispatchExecution(r,s){if(r.howToRun==="http:post")try{let a=r.whatToRun,u=typeof a=="object"?a.value:d(a).value,c=await fetch(u,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});return c.ok?{dispatched:!0}:{dispatched:!1,error:`HTTP ${c.status}: ${c.statusText}`}}catch(a){return {dispatched:false,error:a instanceof Error?a.message:String(a)}}if(r.howToRun==="http:get")try{let a=r.whatToRun,u=typeof a=="object"?a.value:d(a).value,c=new URLSearchParams(Object.entries(s).filter(([,f])=>f!=null).map(([f,A])=>[f,String(A)])),R=`${u}?${c.toString()}`,b=await fetch(R);return b.ok?{dispatched:!0}:{dispatched:!1,error:`HTTP ${b.status}: ${b.statusText}`}}catch(a){return {dispatched:false,error:a instanceof Error?a.message:String(a)}}if(r.howToRun==="in-browser"){let a=r.whatToRun,u=typeof a=="object"?a.value:d(a).value,c=n.get(u);return c?c(r,s):{dispatched:false,error:`No in-browser handler registered for: ${u}`}}return {dispatched:false,error:`Browser adapter: unsupported dispatch kind (got: ${r.howToRun})`}},resolveBlob(r){if(r.kind==="in-memory"){let u=l.get(r.value);if(u==null)throw new Error(`resolveBlob: in-memory blob not found: ${g(r)}`);return u}let a=w(e).read(r.value);if(a===null)throw new Error(`resolveBlob: blob not found: ${g(r)}`);return a},hashFn:k,genId:()=>globalThis.crypto.randomUUID().replace(/-/g,""),kvStorageForRef:r=>m(d(r).value),publishBoardChangeNotifications(r){if(!t?.notifyChannel||r.length===0)return;x(t.notifyChannel).publish({kind:"notification-batch",notifications:r});},onWarn:t?.onWarn,registerHandler(r,s){n.set(r,s);},writeMemoryBlob(r,s){return l.set(r,s),g({kind:"in-memory",value:r})}}}
2
+ exports.createBrowserBoardPlatformAdapter=N;exports.createInMemoryNotificationTransport=C;exports.getInMemoryNotificationBus=x;//# sourceMappingURL=board-live-cards-browser-adapter.cjs.map
2
3
  //# sourceMappingURL=board-live-cards-browser-adapter.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/cli/common/storage-interface.ts","../../../src/cli/browser-api/storage-localstorage-adapters.ts","../../../src/cli/browser-api/board-live-cards-browser-adapter.ts"],"names":["serializeRef","ref","parseRef","s","inner","idx","stableJson","value","obj","k","fnv32a","str","seed","h","computeStableJsonHashBrowser","b","c","d","n","createLocalStorageBlobStorage","prefix","key","textEncoder","encodeBytes","bytes","bin","i","decodeBytes","encoded","out","content","raw","parsed","envelope","prefix2","marker","size","createLocalStorageKvStorage","fullPrefix","result","lsKey","createLocalStorageJournalStorageAdapter","storageKey","load","save","entries","entry","createInMemoryRelayLock","held","createBrowserBoardPlatformAdapter","namespace","opts","selfRef","lock","ns","args","url","resp","e","baseUrl","params","v"],"mappings":"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","file":"board-live-cards-browser-adapter.cjs","sourcesContent":["/**\n * storage-interface.ts\n *\n * Three minimal storage primitives that together cover all persistence needs\n * of the board-live-cards system. Any backend (Node fs, CosmosDB, Azure Blob,\n * browser localStorage, in-memory test double) implements these three interfaces.\n *\n * The pure-logic stores in board-live-cards-all-stores.ts depend only on these\n * interfaces — never on Node built-ins.\n *\n * Blob — raw string content at a logical, backend-neutral key\n * Journal — append-only log with cursor-based reads\n * KV — key-value store with list/delete\n *\n * Mapping to existing storage adapters:\n *\n * CardStorageAdapter\n * inventory (cardId → { blobRef, checksum, fileMetadata? }) → KV\n * card JSON files → Blob\n * source output files → Blob\n *\n * JournalStorageAdapter → Journal (board-journal.jsonl)\n *\n * ExecutionRequestStore → KV (keyed by journalId, via createFsKvStorage)\n *\n * StateSnapshotStorageAdapter\n * board-graph.json (packed single JSON, written atomically) → Blob\n * per-card sidecars (cards/<id>/runtime, fetched-sources-manifest) → KV\n */\n\n// ============================================================================\n// Blob — raw content at an opaque key\n//\n// The key is backend-specific (file path, blob name, storage key).\n// Text helpers are always available. Binary helpers are optional so existing\n// backends can adopt incrementally.\n// ============================================================================\n\nexport interface BlobStat {\n key: string;\n size: number;\n updatedAt?: string;\n contentType?: string;\n}\n\nexport interface BlobStorage {\n /** Returns raw content string, or null if the blob does not exist. */\n read(key: string): string | null;\n\n /** Write content at key. Implementations should be atomic (write-rename). */\n write(key: string, content: string): void;\n\n /** Returns true if a blob exists at key. */\n exists(key: string): boolean;\n\n /** Delete the blob at key. No-op if it does not exist. */\n remove(key: string): void;\n\n /** Optional binary read for file-like artifacts. */\n readBytes?(key: string): Uint8Array | null;\n\n /** Optional binary write for file-like artifacts. */\n writeBytes?(key: string, content: Uint8Array): void;\n\n /** Optional key listing by prefix. */\n listKeys?(prefix?: string): string[];\n\n /** Optional metadata lookup. */\n stat?(key: string): BlobStat | null;\n}\n\n// ============================================================================\n// KindValueRef — backend-neutral typed reference\n//\n// A ref describes WHERE content lives without carrying the bytes.\n// Serialized on the CLI wire as: ::kind::value\n// kind = 'fs-path': value is an absolute file path\n// Additional kinds (e.g. 'cosmos') are added in public-storage-adapter.ts as new backends are supported.\n// ============================================================================\n\nexport interface KindValueRef {\n readonly kind: string;\n readonly value: string;\n}\n\n/** Serialize a KindValueRef to the wire format: ::kind::value */\nexport function serializeRef(ref: KindValueRef): string {\n return `::${ref.kind}::${ref.value}`;\n}\n\n/** Parse a wire-format ref string (::kind::value) into a KindValueRef. */\nexport function parseRef(s: string): KindValueRef {\n if (!s.startsWith('::')) throw new Error(`Invalid ref format (expected ::kind::value): ${s}`);\n const inner = s.slice(2);\n const idx = inner.indexOf('::');\n if (idx === -1) throw new Error(`Invalid ref format (expected ::kind::value): ${s}`);\n return { kind: inner.slice(0, idx), value: inner.slice(idx + 2) };\n}\n\n// ============================================================================\n// Journal — append-only log, cursor-based reads\n//\n// Each entry has a string id (UUID or monotonic token) and an opaque payload.\n// Cursors are entry ids — readAfter returns entries strictly after that id.\n// A null/empty cursor means \"read from the beginning\".\n// ============================================================================\n\nexport interface JournalEntry {\n id: string;\n payload: unknown;\n}\n\nexport interface JournalReadResult {\n entries: JournalEntry[];\n /** The id of the last entry returned, suitable for use as the next cursor. */\n newCursor: string | null;\n}\n\nexport interface JournalStorage {\n /** Append an entry. The storage layer assigns the id. */\n append(payload: unknown): JournalEntry;\n\n /** Read ALL entries (for index rebuilds, full replay). */\n readAll(): JournalEntry[];\n\n /**\n * Read entries appended after the given cursor id.\n * If cursor is null/empty, returns all entries from the beginning.\n */\n readAfter(cursor: string | null): JournalReadResult;\n}\n\n// ============================================================================\n// KV — key-value store with list and delete\n//\n// Values are opaque unknown — callers own serialisation.\n// Keys are scoped by the adapter factory (e.g. a boardDir prefix is closed\n// over in the adapter, not passed per-call).\n// ============================================================================\n\nexport interface KVStorage {\n /** Returns the stored value, or null if the key does not exist. */\n read(key: string): unknown | null;\n\n /** Write value at key. Overwrites any existing value. */\n write(key: string, value: unknown): void;\n\n /** Delete the key. No-op if it does not exist. */\n delete(key: string): void;\n\n /**\n * List all keys, optionally filtered to those starting with prefix.\n * Order is implementation-defined.\n */\n listKeys(prefix?: string): string[];\n}\n\n// ============================================================================\n// JSONStorage — KV store with JSON-aware merge operations\n//\n// Backed by KVStorage under the hood. Adds deepMerge and shallowMerge so\n// callers never need to read-modify-write manually for partial updates.\n// ============================================================================\n\nexport interface JSONStorage {\n /** Returns the stored JSON value, or null if the key does not exist. */\n read(key: string): unknown | null;\n\n /**\n * Read a nested value inside the stored object using a dot-notation path.\n * e.g. get('myKey', 'a.b.c') returns the value at { a: { b: { c: ... } } }.\n * Returns null if the key does not exist or the path cannot be traversed.\n */\n get(key: string, jsonPath: string): unknown | null;\n\n /** Write value at key. Overwrites any existing value. */\n write(key: string, value: unknown): void;\n\n /** Delete the key. No-op if it does not exist. */\n delete(key: string): void;\n\n /** List all keys, optionally filtered by prefix. */\n listKeys(prefix?: string): string[];\n\n /**\n * Shallow-merge patch into the existing object at key.\n * Equivalent to: write(key, { ...read(key), ...patch })\n * Creates the key if it does not exist.\n */\n shallowMerge(key: string, patch: Record<string, unknown>): void;\n\n /**\n * Deep-merge patch into the existing object at key.\n * Recursively merges nested plain objects; arrays and primitives are replaced.\n * Creates the key if it does not exist.\n */\n deepMerge(key: string, patch: Record<string, unknown>): void;\n\n /**\n * Set a nested value inside the stored object using a dot-notation path.\n * e.g. patch('myKey', 'a.b.c', 42) sets { a: { b: { c: 42 } } } into the stored object.\n * Intermediate objects are created if absent. Arrays are not traversed — use integer\n * segments to index into them (e.g. 'items.0.name').\n * Creates the top-level key if it does not exist.\n */\n patch(key: string, jsonPath: string, value: unknown): void;\n}\n\n// ============================================================================\n// StorageProvider — aggregate of all three primitives\n//\n// Adapter factories receive a StorageProvider and close over any scope (e.g.\n// boardDir) themselves. This is the single injection point for swapping\n// backends (Node fs → CosmosDB, browser localStorage, test doubles, etc.).\n// ============================================================================\n\nexport interface StorageProvider {\n blob: BlobStorage;\n journal: JournalStorage;\n kv: KVStorage;\n}\n\n// ============================================================================\n// AtomicRelayLock — non-blocking try-acquire lock with relay-on-busy semantics\n//\n// This interface serves TWO tightly coupled purposes which are intentionally\n// unified into a single primitive:\n//\n// 1. ATOMICITY — ensures that a read-mutate-save cycle is executed by at\n// most one actor at a time, preventing concurrent actors from racing on\n// stale state and writing conflicting snapshots.\n//\n// 2. RELAY SIGNAL — when tryAcquire() returns null, the caller knows the\n// cycle is already in progress. Because the holder always reads fresh\n// state upon entry, it will pick up every change appended by the skipping\n// caller before the lock was attempted. The caller can therefore safely\n// exit — its work will be completed by the holder. This is the\n// \"relay baton\" pattern: the lock being held IS the in-progress signal.\n//\n// These two purposes are not an accidental overload — they are the same\n// invariant expressed at different scopes. Any backend implementation\n// (FS lockfile, Cosmos document lease, Azure entity lock, in-memory flag)\n// that satisfies \"at most one holder at a time\" automatically satisfies both.\n//\n// Contract:\n// - tryAcquire() is non-blocking. It never waits.\n// - Returns a release function on success, or null if already held.\n// - The release function must be called exactly once (use try/finally).\n// - Behaviour after calling release() more than once is undefined.\n// ============================================================================\n\nexport interface AtomicRelayLock {\n /**\n * Attempt to acquire the lock without blocking.\n * Returns a `release` function if successful, or `null` if the lock is\n * already held by another actor (relay: that actor will complete the work).\n */\n tryAcquire(): (() => void) | null;\n}\n\n/**\n * Execute `work` under an `AtomicRelayLock`.\n *\n * - If the lock is busy, returns false immediately (relay: the holder will\n * complete the work on behalf of this caller).\n * - If acquired, runs `work` exclusively, releases the lock, then calls\n * `continuation` if provided — allowing the caller to schedule the next\n * cycle (e.g. spawn a detached process) after the lock is free.\n * - Returns true if work ran.\n */\nexport async function withRelayLock(\n lock: AtomicRelayLock,\n work: () => Promise<void>,\n continuation?: () => void,\n): Promise<boolean> {\n const release = lock.tryAcquire();\n if (!release) return false; // relay: holder is already doing the work\n try {\n await work();\n } finally {\n release(); // release before continuation so it can immediately re-acquire\n }\n continuation?.();\n return true;\n}\n","/**\n * storage-localstorage-adapters.ts\n *\n * Browser localStorage implementations of the board-live-cards storage primitives:\n * BlobStorage — localStorage keys prefixed with `${prefix}:blob:`\n * KVStorage — localStorage keys prefixed with `${prefix}:kv:`, values JSON-encoded\n * JournalStorageAdapter — single localStorage key holding a JSON array of entries\n * CardStorageAdapter — KV-backed, compatible with createCardStore()\n *\n * No Node imports. Requires globalThis.localStorage (browser / jsdom environment).\n */\n\nimport type { BlobStorage, KVStorage, JSONStorage } from '../common/storage-interface.js';\nimport type { JournalStorageAdapter, CardStorageAdapter, JournalEntry, LiveCard, CardIndex } from '../common/board-live-cards-lib.js';\n\n// ============================================================================\n// Stable JSON + sync hash\n// Used for card dedup and snapshot versioning. Not security-sensitive.\n// ============================================================================\n\nfunction stableJson(value: unknown): string {\n if (value === null || value === undefined || typeof value !== 'object') return JSON.stringify(value);\n if (Array.isArray(value)) return `[${(value as unknown[]).map(stableJson).join(',')}]`;\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj).sort();\n return `{${keys.map(k => `${JSON.stringify(k)}:${stableJson(obj[k])}`).join(',')}}`;\n}\n\nfunction fnv32a(str: string, seed: number): number {\n let h = seed >>> 0;\n for (let i = 0; i < str.length; i++) {\n h ^= str.charCodeAt(i);\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n return h;\n}\n\n/**\n * Synchronous stable content hash for browser environments.\n * Uses four FNV-1a 32-bit passes to produce 32 hex chars.\n * Deterministic and cross-session stable; NOT cryptographically secure.\n */\nexport function computeStableJsonHashBrowser(value: unknown): string {\n const str = stableJson(value);\n const a = fnv32a(str, 0x811c9dc5);\n const b = fnv32a(str, 0xdeadbeef);\n const c = fnv32a(str, 0x01234567);\n const d = fnv32a(str, 0xfeedface);\n return [a, b, c, d].map(n => n.toString(16).padStart(8, '0')).join('');\n}\n\n// ============================================================================\n// createLocalStorageBlobStorage\n// ============================================================================\n\nexport function createLocalStorageBlobStorage(prefix: string): BlobStorage {\n function key(k: string): string { return `${prefix}:blob:${k}`; }\n const textEncoder = new TextEncoder();\n\n function encodeBytes(bytes: Uint8Array): string {\n if (typeof btoa === 'function') {\n let bin = '';\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);\n return btoa(bin);\n }\n return '';\n }\n\n function decodeBytes(encoded: string): Uint8Array {\n if (typeof atob === 'function') {\n const bin = atob(encoded);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n }\n return new Uint8Array();\n }\n\n return {\n read(k: string): string | null {\n return globalThis.localStorage.getItem(key(k));\n },\n write(k: string, content: string): void {\n globalThis.localStorage.setItem(key(k), content);\n },\n exists(k: string): boolean {\n return globalThis.localStorage.getItem(key(k)) !== null;\n },\n remove(k: string): void {\n globalThis.localStorage.removeItem(key(k));\n },\n\n readBytes(k: string): Uint8Array | null {\n const raw = globalThis.localStorage.getItem(key(k));\n if (raw === null) return null;\n try {\n const parsed = JSON.parse(raw) as { __kind?: string; data?: string };\n if (parsed && parsed.__kind === 'bytes-b64' && typeof parsed.data === 'string') {\n return decodeBytes(parsed.data);\n }\n } catch {\n // fall through to plain text path\n }\n return textEncoder.encode(raw);\n },\n\n writeBytes(k: string, content: Uint8Array): void {\n // Store binary payloads as base64 envelope to avoid lossy UTF-8 coercion.\n const envelope = JSON.stringify({ __kind: 'bytes-b64', data: encodeBytes(content) });\n globalThis.localStorage.setItem(key(k), envelope);\n },\n\n listKeys(prefix2?: string): string[] {\n const marker = key(prefix2 ?? '');\n const out: string[] = [];\n for (let i = 0; i < globalThis.localStorage.length; i++) {\n const k = globalThis.localStorage.key(i);\n if (k && k.startsWith(marker)) out.push(k.slice(key('').length));\n }\n return out.sort();\n },\n\n stat(k: string) {\n const raw = globalThis.localStorage.getItem(key(k));\n if (raw === null) return null;\n let size = textEncoder.encode(raw).byteLength;\n try {\n const parsed = JSON.parse(raw) as { __kind?: string; data?: string };\n if (parsed && parsed.__kind === 'bytes-b64' && typeof parsed.data === 'string') {\n size = decodeBytes(parsed.data).byteLength;\n }\n } catch {\n // plain text path\n }\n return { key: k, size };\n },\n };\n}\n\n// ============================================================================\n// createLocalStorageKvStorage\n// ============================================================================\n\nexport function createLocalStorageKvStorage(prefix: string): KVStorage {\n function key(k: string): string { return `${prefix}:kv:${k}`; }\n\n return {\n read(k: string): unknown | null {\n const raw = globalThis.localStorage.getItem(key(k));\n if (raw === null) return null;\n try { return JSON.parse(raw); } catch { return null; }\n },\n write(k: string, value: unknown): void {\n globalThis.localStorage.setItem(key(k), JSON.stringify(value));\n },\n delete(k: string): void {\n globalThis.localStorage.removeItem(key(k));\n },\n listKeys(prefix2?: string): string[] {\n const fullPrefix = key(prefix2 ?? '');\n const result: string[] = [];\n for (let i = 0; i < globalThis.localStorage.length; i++) {\n const lsKey = globalThis.localStorage.key(i);\n if (lsKey !== null && lsKey.startsWith(fullPrefix)) {\n // Strip the outer prefix + ':kv:' to return the logical key\n result.push(lsKey.slice(key('').length));\n }\n }\n return result;\n },\n };\n}\n\nfunction deepMergeObjects(target: Record<string, unknown>, patch: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = { ...target };\n for (const [k, v] of Object.entries(patch)) {\n if (v !== null && typeof v === 'object' && !Array.isArray(v) &&\n result[k] !== null && typeof result[k] === 'object' && !Array.isArray(result[k])) {\n result[k] = deepMergeObjects(result[k] as Record<string, unknown>, v as Record<string, unknown>);\n } else {\n result[k] = v;\n }\n }\n return result;\n}\n\nfunction applyJsonPath(obj: Record<string, unknown>, segments: string[], value: unknown): Record<string, unknown> {\n if (segments.length === 0) return obj;\n const [head, ...tail] = segments;\n if (tail.length === 0) return { ...obj, [head]: value };\n const nested = (obj[head] !== null && typeof obj[head] === 'object' && !Array.isArray(obj[head]))\n ? (obj[head] as Record<string, unknown>)\n : {};\n return { ...obj, [head]: applyJsonPath(nested, tail, value) };\n}\n\nexport function createLocalStorageJsonStorage(prefix: string): JSONStorage {\n const kv = createLocalStorageKvStorage(prefix);\n return {\n read: (key) => kv.read(key),\n get(key, jsonPath) {\n const obj = kv.read(key);\n if (obj === null) return null;\n let current: unknown = obj;\n for (const segment of jsonPath.split('.').filter(Boolean)) {\n if (current === null || typeof current !== 'object' || Array.isArray(current)) return null;\n current = (current as Record<string, unknown>)[segment] ?? null;\n }\n return current ?? null;\n },\n write: (key, value) => kv.write(key, value),\n delete: (key) => kv.delete(key),\n listKeys: (prefix2?) => kv.listKeys(prefix2),\n shallowMerge(key, patch) {\n const existing = (kv.read(key) as Record<string, unknown> | null) ?? {};\n kv.write(key, { ...existing, ...patch });\n },\n deepMerge(key, patch) {\n const existing = (kv.read(key) as Record<string, unknown> | null) ?? {};\n kv.write(key, deepMergeObjects(existing, patch));\n },\n patch(key, jsonPath, value) {\n const existing = (kv.read(key) as Record<string, unknown> | null) ?? {};\n const segments = jsonPath.split('.').filter(Boolean);\n kv.write(key, applyJsonPath(existing, segments, value));\n },\n };\n}\n\n// ============================================================================\n// createLocalStorageJournalStorageAdapter\n// All entries stored as a JSON array under a single localStorage key.\n// ============================================================================\n\nexport function createLocalStorageJournalStorageAdapter(storageKey: string): JournalStorageAdapter {\n function load(): JournalEntry[] {\n const raw = globalThis.localStorage.getItem(storageKey);\n if (!raw) return [];\n try { return JSON.parse(raw) as JournalEntry[]; } catch { return []; }\n }\n\n function save(entries: JournalEntry[]): void {\n globalThis.localStorage.setItem(storageKey, JSON.stringify(entries));\n }\n\n return {\n readAllEntries(): JournalEntry[] {\n return load();\n },\n appendEntry(entry: JournalEntry): void {\n const entries = load();\n entries.push(entry);\n save(entries);\n },\n generateId(): string {\n return globalThis.crypto.randomUUID();\n },\n };\n}\n\n// ============================================================================\n// createLocalStorageCardStorageAdapter\n// Mirrors createFsCardStorageAdapter — KV-backed, cards keyed by cardId.\n// ============================================================================\n\nexport function createLocalStorageCardStorageAdapter(prefix: string): CardStorageAdapter {\n const json = createLocalStorageJsonStorage(prefix);\n\n return {\n readIndex(): CardIndex | null {\n return json.read('_index') as CardIndex | null;\n },\n writeIndex(index: CardIndex): void {\n json.write('_index', index);\n },\n readCard(id: string): LiveCard | null {\n return json.read(id) as LiveCard | null;\n },\n writeCard(id: string, card: LiveCard): string {\n json.write(id, card);\n return computeStableJsonHashBrowser(card);\n },\n cardExists(id: string): boolean {\n return json.read(id) !== null;\n },\n defaultCardKey(cardId: string): string {\n return cardId;\n },\n };\n}\n","/**\n * board-live-cards-browser-adapter.ts\n *\n * Browser implementation of BoardPlatformAdapter.\n * Uses localStorage for all persistence.\n *\n * Constraints vs Node/FS adapter:\n * - lock: in-memory no-op (browser is single-threaded; no cross-tab locking)\n * - dispatchExecution: supports 'http:post' and 'http:get' only\n * - requestProcessAccumulated: not applicable (caller drives via polling / setInterval)\n * - selfRef: 'built-in' kind — no spawnable CLI available\n */\n\nimport type { KindValueRef, AtomicRelayLock } from '../common/storage-interface.js';\nimport { serializeRef, parseRef } from '../common/storage-interface.js';\nimport type { BoardPlatformAdapter } from '../common/board-live-cards-public.js';\nimport {\n createLocalStorageBlobStorage,\n createLocalStorageKvStorage,\n createLocalStorageJournalStorageAdapter,\n computeStableJsonHashBrowser,\n} from './storage-localstorage-adapters.js';\n\n// ============================================================================\n// In-memory no-op AtomicRelayLock\n// Browser is single-threaded; no concurrent actors within one tab.\n// ============================================================================\n\nfunction createInMemoryRelayLock(): AtomicRelayLock {\n let held = false;\n return {\n tryAcquire(): (() => void) | null {\n if (held) return null;\n held = true;\n return () => { held = false; };\n },\n };\n}\n\n// ============================================================================\n// createBrowserBoardPlatformAdapter\n//\n// namespace — logical name for this board instance (e.g. 'my-board').\n// Used as the localStorage key prefix so multiple boards can coexist.\n// opts.callbackBaseUrl — if set, used as selfRef.whatToRun for http callbacks.\n// e.g. 'https://my-app.example.com/api/board'\n// ============================================================================\n\nexport function createBrowserBoardPlatformAdapter(\n namespace: string,\n opts?: {\n callbackBaseUrl?: string;\n onWarn?: (msg: string) => void;\n },\n): BoardPlatformAdapter {\n const selfRef = opts?.callbackBaseUrl\n ? {\n meta: 'board-live-cards',\n howToRun: 'http:post' as const,\n whatToRun: opts.callbackBaseUrl,\n }\n : {\n meta: 'board-live-cards',\n howToRun: 'built-in' as const,\n whatToRun: '::built-in::board-live-cards-browser',\n };\n\n const lock = createInMemoryRelayLock();\n\n return {\n kvStorage: (ns: string) =>\n createLocalStorageKvStorage(`${namespace}:${ns}`),\n\n blobStorage: (ns: string) =>\n createLocalStorageBlobStorage(ns ? `${namespace}:${ns}` : namespace),\n\n journalAdapter: () =>\n createLocalStorageJournalStorageAdapter(`${namespace}:journal`),\n\n lock,\n\n selfRef,\n\n async dispatchExecution(ref, args): Promise<{ dispatched: boolean; error?: string }> {\n if (ref.howToRun === 'http:post') {\n try {\n const url = ref.whatToRun.startsWith('::')\n ? parseRef(ref.whatToRun).value\n : ref.whatToRun;\n const resp = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(args),\n });\n if (!resp.ok) {\n return { dispatched: false, error: `HTTP ${resp.status}: ${resp.statusText}` };\n }\n return { dispatched: true };\n } catch (e) {\n return { dispatched: false, error: e instanceof Error ? e.message : String(e) };\n }\n }\n\n if (ref.howToRun === 'http:get') {\n try {\n const baseUrl = ref.whatToRun.startsWith('::')\n ? parseRef(ref.whatToRun).value\n : ref.whatToRun;\n const params = new URLSearchParams(\n Object.entries(args as Record<string, unknown>)\n .filter(([, v]) => v !== undefined && v !== null)\n .map(([k, v]) => [k, String(v)]),\n );\n const url = `${baseUrl}?${params.toString()}`;\n const resp = await fetch(url);\n if (!resp.ok) {\n return { dispatched: false, error: `HTTP ${resp.status}: ${resp.statusText}` };\n }\n return { dispatched: true };\n } catch (e) {\n return { dispatched: false, error: e instanceof Error ? e.message : String(e) };\n }\n }\n\n return {\n dispatched: false,\n error: `Browser adapter: only http:post and http:get dispatch are supported (got: ${ref.howToRun})`,\n };\n },\n\n resolveBlob(ref: KindValueRef): string {\n // In the browser, blobs are stored in localStorage under the board namespace.\n // The ref value is treated as a logical localStorage key.\n const storage = createLocalStorageBlobStorage(namespace);\n const content = storage.read(ref.value);\n if (content === null) {\n throw new Error(`resolveBlob: blob not found: ${serializeRef(ref)}`);\n }\n return content;\n },\n\n hashFn: computeStableJsonHashBrowser,\n\n genId: (): string => globalThis.crypto.randomUUID().replace(/-/g, ''),\n\n kvStorageForRef: (ref: string) => createLocalStorageKvStorage(parseRef(ref).value),\n\n // requestProcessAccumulated is intentionally absent — the browser caller\n // drives drain cycles via polling or setInterval.\n\n onWarn: opts?.onWarn,\n };\n}\n"]}
1
+ {"version":3,"sources":["../../../src/cli/common/storage-interface.ts","../../../src/cli/browser-api/storage-localstorage-adapters.ts","../../../src/cli/browser-api/board-live-cards-browser-adapter.ts"],"names":["REF_PREFIX","toBase64Url","raw","utf8","buf","base64","binary","byte","fromBase64Url","input","bytes","serializeRef","ref","parseRef","s","parsed","candidate","stableJson","value","obj","k","fnv32a","str","seed","h","i","computeStableJsonHashBrowser","a","b","c","d","n","createLocalStorageBlobStorage","prefix","key","textEncoder","encodeBytes","bin","decodeBytes","encoded","out","content","envelope","prefix2","marker","size","createLocalStorageKvStorage","fullPrefix","result","lsKey","createLocalStorageJournalStorageAdapter","storageKey","load","save","entries","entry","createInMemoryRelayLock","held","_busRegistry","getInMemoryNotificationBus","channel","bus","listeners","event","fn","onEvent","createInMemoryNotificationTransport","e","createBrowserBoardPlatformAdapter","namespace","opts","selfRef","handlerRegistry","memoryBlobs","lock","ns","args","url","resp","baseUrl","params","v","handlerKey","handler","notifications","name","data"],"mappings":"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","file":"board-live-cards-browser-adapter.cjs","sourcesContent":["/**\n * storage-interface.ts\n *\n * Three minimal storage primitives that together cover all persistence needs\n * of the board-live-cards system. Any backend (Node fs, CosmosDB, Azure Blob,\n * browser localStorage, in-memory test double) implements these three interfaces.\n *\n * The pure-logic stores in board-live-cards-all-stores.ts depend only on these\n * interfaces — never on Node built-ins.\n *\n * Blob — raw string content at a logical, backend-neutral key\n * Journal — append-only log with cursor-based reads\n * KV — key-value store with list/delete\n *\n * Mapping to existing storage adapters:\n *\n * CardStorageAdapter\n * inventory (cardId → { blobRef, checksum, fileMetadata? }) → KV\n * card JSON files → Blob\n * source output files → Blob\n *\n * JournalStorageAdapter → Journal (board-journal.jsonl)\n *\n * ExecutionRequestStore → KV (keyed by journalId, via createFsKvStorage)\n *\n * StateSnapshotStorageAdapter\n * board-graph.json (packed single JSON, written atomically) → Blob\n * per-card sidecars (cards/<id>/runtime, fetched-sources-manifest) → KV\n */\n\n// ============================================================================\n// Blob — raw content at an opaque key\n//\n// The key is backend-specific (file path, blob name, storage key).\n// Text helpers are always available. Binary helpers are optional so existing\n// backends can adopt incrementally.\n// ============================================================================\n\nexport interface BlobStat {\n key: string;\n size: number;\n updatedAt?: string;\n contentType?: string;\n}\n\nexport interface BlobStorage {\n /** Returns raw content string, or null if the blob does not exist. */\n read(key: string): string | null;\n\n /** Write content at key. Implementations should be atomic (write-rename). */\n write(key: string, content: string): void;\n\n /** Returns true if a blob exists at key. */\n exists(key: string): boolean;\n\n /** Delete the blob at key. No-op if it does not exist. */\n remove(key: string): void;\n\n /** Optional binary read for file-like artifacts. */\n readBytes?(key: string): Uint8Array | null;\n\n /** Optional binary write for file-like artifacts. */\n writeBytes?(key: string, content: Uint8Array): void;\n\n /** Optional key listing by prefix. */\n listKeys?(prefix?: string): string[];\n\n /** Optional metadata lookup. */\n stat?(key: string): BlobStat | null;\n}\n\n// ============================================================================\n// KindValueRef — backend-neutral typed reference\n//\n// A ref describes WHERE content lives without carrying the bytes.\n// Serialized on the CLI wire as: b64:<base64url({\"kind\":\"...\",\"value\":\"...\"})>\n// kind = 'fs-path': value is an absolute file path\n// Additional kinds (e.g. 'cosmos') are added in public-storage-adapter.ts as new backends are supported.\n// ============================================================================\n\nexport interface KindValueRef {\n readonly kind: string;\n readonly value: string;\n}\n\nconst REF_PREFIX = 'b64:';\n\nfunction toBase64Url(raw: string): string {\n const utf8 = new TextEncoder().encode(raw);\n const buf = (globalThis as { Buffer?: { from(data: Uint8Array): { toString(enc: string): string } } }).Buffer;\n let base64: string;\n if (buf) {\n base64 = buf.from(utf8).toString('base64');\n } else if (typeof btoa === 'function') {\n let binary = '';\n for (const byte of utf8) binary += String.fromCharCode(byte);\n base64 = btoa(binary);\n } else {\n throw new Error('No base64 encoder available in this runtime');\n }\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/g, '');\n}\n\nfunction fromBase64Url(input: string): string {\n const base64 = input.replace(/-/g, '+').replace(/_/g, '/')\n + '='.repeat((4 - (input.length % 4)) % 4);\n const buf = (globalThis as { Buffer?: { from(data: string, enc: string): { toString(enc: string): string } } }).Buffer;\n if (buf) return buf.from(base64, 'base64').toString('utf8');\n if (typeof atob === 'function') {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);\n return new TextDecoder().decode(bytes);\n }\n throw new Error('No base64 decoder available in this runtime');\n}\n\n/** Serialize a KindValueRef to the wire format: b64:<base64url(json)> */\nexport function serializeRef(ref: KindValueRef): string {\n return `${REF_PREFIX}${toBase64Url(JSON.stringify(ref))}`;\n}\n\n/** Parse a wire-format ref string (b64:<base64url(json)>) into a KindValueRef. */\nexport function parseRef(s: string): KindValueRef {\n if (!s.startsWith(REF_PREFIX)) throw new Error(`Invalid ref format (expected ${REF_PREFIX}<base64url(json)>): ${s}`);\n let parsed: unknown;\n try {\n parsed = JSON.parse(fromBase64Url(s.slice(REF_PREFIX.length)));\n } catch {\n throw new Error(`Invalid ref format (malformed base64url/json): ${s}`);\n }\n if (!parsed || typeof parsed !== 'object') {\n throw new Error(`Invalid ref format (expected object payload): ${s}`);\n }\n const candidate = parsed as { kind?: unknown; value?: unknown };\n if (typeof candidate.kind !== 'string' || typeof candidate.value !== 'string') {\n throw new Error(`Invalid ref format (payload must contain string kind/value): ${s}`);\n }\n return { kind: candidate.kind, value: candidate.value };\n}\n\n// ============================================================================\n// Journal — append-only log, cursor-based reads\n//\n// Each entry has a string id (UUID or monotonic token) and an opaque payload.\n// Cursors are entry ids — readAfter returns entries strictly after that id.\n// A null/empty cursor means \"read from the beginning\".\n// ============================================================================\n\nexport interface JournalEntry {\n id: string;\n payload: unknown;\n}\n\nexport interface JournalReadResult {\n entries: JournalEntry[];\n /** The id of the last entry returned, suitable for use as the next cursor. */\n newCursor: string | null;\n}\n\nexport interface JournalStorage {\n /** Append an entry. The storage layer assigns the id. */\n append(payload: unknown): JournalEntry;\n\n /** Read ALL entries (for index rebuilds, full replay). */\n readAll(): JournalEntry[];\n\n /**\n * Read entries appended after the given cursor id.\n * If cursor is null/empty, returns all entries from the beginning.\n */\n readAfter(cursor: string | null): JournalReadResult;\n}\n\n// ============================================================================\n// KV — key-value store with list and delete\n//\n// Values are opaque unknown — callers own serialisation.\n// Keys are scoped by the adapter factory (e.g. a boardDir prefix is closed\n// over in the adapter, not passed per-call).\n// ============================================================================\n\nexport interface KVStorage {\n /** Returns the stored value, or null if the key does not exist. */\n read(key: string): unknown | null;\n\n /** Write value at key. Overwrites any existing value. */\n write(key: string, value: unknown): void;\n\n /** Delete the key. No-op if it does not exist. */\n delete(key: string): void;\n\n /**\n * List all keys, optionally filtered to those starting with prefix.\n * Order is implementation-defined.\n */\n listKeys(prefix?: string): string[];\n}\n\n// ============================================================================\n// JSONStorage — KV store with JSON-aware merge operations\n//\n// Backed by KVStorage under the hood. Adds deepMerge and shallowMerge so\n// callers never need to read-modify-write manually for partial updates.\n// ============================================================================\n\nexport interface JSONStorage {\n /** Returns the stored JSON value, or null if the key does not exist. */\n read(key: string): unknown | null;\n\n /**\n * Read a nested value inside the stored object using a dot-notation path.\n * e.g. get('myKey', 'a.b.c') returns the value at { a: { b: { c: ... } } }.\n * Returns null if the key does not exist or the path cannot be traversed.\n */\n get(key: string, jsonPath: string): unknown | null;\n\n /** Write value at key. Overwrites any existing value. */\n write(key: string, value: unknown): void;\n\n /** Delete the key. No-op if it does not exist. */\n delete(key: string): void;\n\n /** List all keys, optionally filtered by prefix. */\n listKeys(prefix?: string): string[];\n\n /**\n * Shallow-merge patch into the existing object at key.\n * Equivalent to: write(key, { ...read(key), ...patch })\n * Creates the key if it does not exist.\n */\n shallowMerge(key: string, patch: Record<string, unknown>): void;\n\n /**\n * Deep-merge patch into the existing object at key.\n * Recursively merges nested plain objects; arrays and primitives are replaced.\n * Creates the key if it does not exist.\n */\n deepMerge(key: string, patch: Record<string, unknown>): void;\n\n /**\n * Set a nested value inside the stored object using a dot-notation path.\n * e.g. patch('myKey', 'a.b.c', 42) sets { a: { b: { c: 42 } } } into the stored object.\n * Intermediate objects are created if absent. Arrays are not traversed — use integer\n * segments to index into them (e.g. 'items.0.name').\n * Creates the top-level key if it does not exist.\n */\n patch(key: string, jsonPath: string, value: unknown): void;\n}\n\n// ============================================================================\n// StorageProvider — aggregate of all three primitives\n//\n// Adapter factories receive a StorageProvider and close over any scope (e.g.\n// boardDir) themselves. This is the single injection point for swapping\n// backends (Node fs → CosmosDB, browser localStorage, test doubles, etc.).\n// ============================================================================\n\nexport interface StorageProvider {\n blob: BlobStorage;\n journal: JournalStorage;\n kv: KVStorage;\n}\n\n// ============================================================================\n// AtomicRelayLock — non-blocking try-acquire lock with relay-on-busy semantics\n//\n// This interface serves TWO tightly coupled purposes which are intentionally\n// unified into a single primitive:\n//\n// 1. ATOMICITY — ensures that a read-mutate-save cycle is executed by at\n// most one actor at a time, preventing concurrent actors from racing on\n// stale state and writing conflicting snapshots.\n//\n// 2. RELAY SIGNAL — when tryAcquire() returns null, the caller knows the\n// cycle is already in progress. Because the holder always reads fresh\n// state upon entry, it will pick up every change appended by the skipping\n// caller before the lock was attempted. The caller can therefore safely\n// exit — its work will be completed by the holder. This is the\n// \"relay baton\" pattern: the lock being held IS the in-progress signal.\n//\n// These two purposes are not an accidental overload — they are the same\n// invariant expressed at different scopes. Any backend implementation\n// (FS lockfile, Cosmos document lease, Azure entity lock, in-memory flag)\n// that satisfies \"at most one holder at a time\" automatically satisfies both.\n//\n// Contract:\n// - tryAcquire() is non-blocking. It never waits.\n// - Returns a release function on success, or null if already held.\n// - The release function must be called exactly once (use try/finally).\n// - Behaviour after calling release() more than once is undefined.\n// ============================================================================\n\nexport interface AtomicRelayLock {\n /**\n * Attempt to acquire the lock without blocking.\n * Returns a `release` function if successful, or `null` if the lock is\n * already held by another actor (relay: that actor will complete the work).\n */\n tryAcquire(): (() => void) | null;\n}\n\n/**\n * Execute `work` under an `AtomicRelayLock`.\n *\n * - If the lock is busy, returns false immediately (relay: the holder will\n * complete the work on behalf of this caller).\n * - If acquired, runs `work` exclusively, releases the lock, then calls\n * `continuation` if provided — allowing the caller to schedule the next\n * cycle (e.g. spawn a detached process) after the lock is free.\n * - Returns true if work ran.\n */\nexport async function withRelayLock(\n lock: AtomicRelayLock,\n work: () => Promise<void>,\n continuation?: () => void,\n): Promise<boolean> {\n const release = lock.tryAcquire();\n if (!release) return false; // relay: holder is already doing the work\n try {\n await work();\n } finally {\n release(); // release before continuation so it can immediately re-acquire\n }\n continuation?.();\n return true;\n}\n","/**\n * storage-localstorage-adapters.ts\n *\n * Browser localStorage implementations of the board-live-cards storage primitives:\n * BlobStorage — localStorage keys prefixed with `${prefix}:blob:`\n * KVStorage — localStorage keys prefixed with `${prefix}:kv:`, values JSON-encoded\n * JournalStorageAdapter — single localStorage key holding a JSON array of entries\n * CardStorageAdapter — KV-backed, compatible with createCardStore()\n *\n * No Node imports. Requires globalThis.localStorage (browser / jsdom environment).\n */\n\nimport type { BlobStorage, KVStorage, JSONStorage } from '../common/storage-interface.js';\nimport type { JournalStorageAdapter, CardStorageAdapter, JournalEntry, LiveCard, CardIndex } from '../common/board-live-cards-lib.js';\n\n// ============================================================================\n// Stable JSON + sync hash\n// Used for card dedup and snapshot versioning. Not security-sensitive.\n// ============================================================================\n\nfunction stableJson(value: unknown): string {\n if (value === null || value === undefined || typeof value !== 'object') return JSON.stringify(value);\n if (Array.isArray(value)) return `[${(value as unknown[]).map(stableJson).join(',')}]`;\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj).sort();\n return `{${keys.map(k => `${JSON.stringify(k)}:${stableJson(obj[k])}`).join(',')}}`;\n}\n\nfunction fnv32a(str: string, seed: number): number {\n let h = seed >>> 0;\n for (let i = 0; i < str.length; i++) {\n h ^= str.charCodeAt(i);\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n return h;\n}\n\n/**\n * Synchronous stable content hash for browser environments.\n * Uses four FNV-1a 32-bit passes to produce 32 hex chars.\n * Deterministic and cross-session stable; NOT cryptographically secure.\n */\nexport function computeStableJsonHashBrowser(value: unknown): string {\n const str = stableJson(value);\n const a = fnv32a(str, 0x811c9dc5);\n const b = fnv32a(str, 0xdeadbeef);\n const c = fnv32a(str, 0x01234567);\n const d = fnv32a(str, 0xfeedface);\n return [a, b, c, d].map(n => n.toString(16).padStart(8, '0')).join('');\n}\n\n// ============================================================================\n// createLocalStorageBlobStorage\n// ============================================================================\n\nexport function createLocalStorageBlobStorage(prefix: string): BlobStorage {\n function key(k: string): string { return `${prefix}:blob:${k}`; }\n const textEncoder = new TextEncoder();\n\n function encodeBytes(bytes: Uint8Array): string {\n if (typeof btoa === 'function') {\n let bin = '';\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);\n return btoa(bin);\n }\n return '';\n }\n\n function decodeBytes(encoded: string): Uint8Array {\n if (typeof atob === 'function') {\n const bin = atob(encoded);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n }\n return new Uint8Array();\n }\n\n return {\n read(k: string): string | null {\n return globalThis.localStorage.getItem(key(k));\n },\n write(k: string, content: string): void {\n globalThis.localStorage.setItem(key(k), content);\n },\n exists(k: string): boolean {\n return globalThis.localStorage.getItem(key(k)) !== null;\n },\n remove(k: string): void {\n globalThis.localStorage.removeItem(key(k));\n },\n\n readBytes(k: string): Uint8Array | null {\n const raw = globalThis.localStorage.getItem(key(k));\n if (raw === null) return null;\n try {\n const parsed = JSON.parse(raw) as { __kind?: string; data?: string };\n if (parsed && parsed.__kind === 'bytes-b64' && typeof parsed.data === 'string') {\n return decodeBytes(parsed.data);\n }\n } catch {\n // fall through to plain text path\n }\n return textEncoder.encode(raw);\n },\n\n writeBytes(k: string, content: Uint8Array): void {\n // Store binary payloads as base64 envelope to avoid lossy UTF-8 coercion.\n const envelope = JSON.stringify({ __kind: 'bytes-b64', data: encodeBytes(content) });\n globalThis.localStorage.setItem(key(k), envelope);\n },\n\n listKeys(prefix2?: string): string[] {\n const marker = key(prefix2 ?? '');\n const out: string[] = [];\n for (let i = 0; i < globalThis.localStorage.length; i++) {\n const k = globalThis.localStorage.key(i);\n if (k && k.startsWith(marker)) out.push(k.slice(key('').length));\n }\n return out.sort();\n },\n\n stat(k: string) {\n const raw = globalThis.localStorage.getItem(key(k));\n if (raw === null) return null;\n let size = textEncoder.encode(raw).byteLength;\n try {\n const parsed = JSON.parse(raw) as { __kind?: string; data?: string };\n if (parsed && parsed.__kind === 'bytes-b64' && typeof parsed.data === 'string') {\n size = decodeBytes(parsed.data).byteLength;\n }\n } catch {\n // plain text path\n }\n return { key: k, size };\n },\n };\n}\n\n// ============================================================================\n// createLocalStorageKvStorage\n// ============================================================================\n\nexport function createLocalStorageKvStorage(prefix: string): KVStorage {\n function key(k: string): string { return `${prefix}:kv:${k}`; }\n\n return {\n read(k: string): unknown | null {\n const raw = globalThis.localStorage.getItem(key(k));\n if (raw === null) return null;\n try { return JSON.parse(raw); } catch { return null; }\n },\n write(k: string, value: unknown): void {\n globalThis.localStorage.setItem(key(k), JSON.stringify(value));\n },\n delete(k: string): void {\n globalThis.localStorage.removeItem(key(k));\n },\n listKeys(prefix2?: string): string[] {\n const fullPrefix = key(prefix2 ?? '');\n const result: string[] = [];\n for (let i = 0; i < globalThis.localStorage.length; i++) {\n const lsKey = globalThis.localStorage.key(i);\n if (lsKey !== null && lsKey.startsWith(fullPrefix)) {\n // Strip the outer prefix + ':kv:' to return the logical key\n result.push(lsKey.slice(key('').length));\n }\n }\n return result;\n },\n };\n}\n\nfunction deepMergeObjects(target: Record<string, unknown>, patch: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = { ...target };\n for (const [k, v] of Object.entries(patch)) {\n if (v !== null && typeof v === 'object' && !Array.isArray(v) &&\n result[k] !== null && typeof result[k] === 'object' && !Array.isArray(result[k])) {\n result[k] = deepMergeObjects(result[k] as Record<string, unknown>, v as Record<string, unknown>);\n } else {\n result[k] = v;\n }\n }\n return result;\n}\n\nfunction applyJsonPath(obj: Record<string, unknown>, segments: string[], value: unknown): Record<string, unknown> {\n if (segments.length === 0) return obj;\n const [head, ...tail] = segments;\n if (tail.length === 0) return { ...obj, [head]: value };\n const nested = (obj[head] !== null && typeof obj[head] === 'object' && !Array.isArray(obj[head]))\n ? (obj[head] as Record<string, unknown>)\n : {};\n return { ...obj, [head]: applyJsonPath(nested, tail, value) };\n}\n\nexport function createLocalStorageJsonStorage(prefix: string): JSONStorage {\n const kv = createLocalStorageKvStorage(prefix);\n return {\n read: (key) => kv.read(key),\n get(key, jsonPath) {\n const obj = kv.read(key);\n if (obj === null) return null;\n let current: unknown = obj;\n for (const segment of jsonPath.split('.').filter(Boolean)) {\n if (current === null || typeof current !== 'object' || Array.isArray(current)) return null;\n current = (current as Record<string, unknown>)[segment] ?? null;\n }\n return current ?? null;\n },\n write: (key, value) => kv.write(key, value),\n delete: (key) => kv.delete(key),\n listKeys: (prefix2?) => kv.listKeys(prefix2),\n shallowMerge(key, patch) {\n const existing = (kv.read(key) as Record<string, unknown> | null) ?? {};\n kv.write(key, { ...existing, ...patch });\n },\n deepMerge(key, patch) {\n const existing = (kv.read(key) as Record<string, unknown> | null) ?? {};\n kv.write(key, deepMergeObjects(existing, patch));\n },\n patch(key, jsonPath, value) {\n const existing = (kv.read(key) as Record<string, unknown> | null) ?? {};\n const segments = jsonPath.split('.').filter(Boolean);\n kv.write(key, applyJsonPath(existing, segments, value));\n },\n };\n}\n\n// ============================================================================\n// createLocalStorageJournalStorageAdapter\n// All entries stored as a JSON array under a single localStorage key.\n// ============================================================================\n\nexport function createLocalStorageJournalStorageAdapter(storageKey: string): JournalStorageAdapter {\n function load(): JournalEntry[] {\n const raw = globalThis.localStorage.getItem(storageKey);\n if (!raw) return [];\n try { return JSON.parse(raw) as JournalEntry[]; } catch { return []; }\n }\n\n function save(entries: JournalEntry[]): void {\n globalThis.localStorage.setItem(storageKey, JSON.stringify(entries));\n }\n\n return {\n readAllEntries(): JournalEntry[] {\n return load();\n },\n appendEntry(entry: JournalEntry): void {\n const entries = load();\n entries.push(entry);\n save(entries);\n },\n generateId(): string {\n return globalThis.crypto.randomUUID();\n },\n };\n}\n\n// ============================================================================\n// createLocalStorageCardStorageAdapter\n// Mirrors createFsCardStorageAdapter — KV-backed, cards keyed by cardId.\n// ============================================================================\n\nexport function createLocalStorageCardStorageAdapter(prefix: string): CardStorageAdapter {\n const json = createLocalStorageJsonStorage(prefix);\n\n return {\n readIndex(): CardIndex | null {\n return json.read('_index') as CardIndex | null;\n },\n writeIndex(index: CardIndex): void {\n json.write('_index', index);\n },\n readCard(id: string): LiveCard | null {\n return json.read(id) as LiveCard | null;\n },\n writeCard(id: string, card: LiveCard): string {\n json.write(id, card);\n return computeStableJsonHashBrowser(card);\n },\n cardExists(id: string): boolean {\n return json.read(id) !== null;\n },\n defaultCardKey(cardId: string): string {\n return cardId;\n },\n };\n}\n","/**\n * board-live-cards-browser-adapter.ts\n *\n * Browser implementation of BoardPlatformAdapter.\n * Uses localStorage for all persistence.\n *\n * Constraints vs Node/FS adapter:\n * - lock: in-memory no-op (browser is single-threaded; no cross-tab locking)\n * - dispatchExecution: supports 'in-browser', 'http:post' and 'http:get'\n * - requestProcessAccumulated: not applicable (caller drives via polling / setInterval)\n * - selfRef: 'in-browser' kind — routes to registered in-memory handlers\n */\n\nimport type { KindValueRef, AtomicRelayLock } from '../common/storage-interface.js';\nimport { serializeRef, parseRef } from '../common/storage-interface.js';\nimport type { BoardPlatformAdapter } from '../common/board-live-cards-public.js';\nimport {\n createLocalStorageBlobStorage,\n createLocalStorageKvStorage,\n createLocalStorageJournalStorageAdapter,\n computeStableJsonHashBrowser,\n} from './storage-localstorage-adapters.js';\n\n// ============================================================================\n// In-memory no-op AtomicRelayLock\n// Browser is single-threaded; no concurrent actors within one tab.\n// ============================================================================\n\nfunction createInMemoryRelayLock(): AtomicRelayLock {\n let held = false;\n return {\n tryAcquire(): (() => void) | null {\n if (held) return null;\n held = true;\n return () => { held = false; };\n },\n };\n}\n\n// ============================================================================\n// In-memory notification bus (keyed by channel name)\n//\n// Same role as named-pipe in Node.js: the adapter publishes to a channel,\n// and a NotificationTransport subscribes on a matching KindValueRef.\n// Kind: \"in-memory-bus\" — e.g. { kind: 'in-memory-bus', value: 'my-board:notify' }\n// ============================================================================\n\ninterface InMemoryBus {\n publish(event: unknown): void;\n subscribe(onEvent: (event: unknown) => void): () => void;\n}\n\nconst _busRegistry = new Map<string, InMemoryBus>();\n\nexport function getInMemoryNotificationBus(channel: string): InMemoryBus {\n let bus = _busRegistry.get(channel);\n if (!bus) {\n const listeners = new Set<(event: unknown) => void>();\n bus = {\n publish(event) { for (const fn of listeners) fn(event); },\n subscribe(onEvent) {\n listeners.add(onEvent);\n return () => { listeners.delete(onEvent); };\n },\n };\n _busRegistry.set(channel, bus);\n }\n return bus;\n}\n\n/**\n * In-memory NotificationTransport for the browser.\n * Subscribes to the same in-memory bus that the adapter publishes to.\n * Use with notifyRef: { kind: 'in-memory-bus', value: '<channel>' }\n */\nexport function createInMemoryNotificationTransport(): import('../../server-runtime/types.js').NotificationTransport {\n return {\n async subscribe(ref, onEvent) {\n if (ref.kind !== 'in-memory-bus') {\n console.warn(`[in-memory-transport] unsupported kind: ${ref.kind}`);\n return () => {};\n }\n const bus = getInMemoryNotificationBus(ref.value);\n return bus.subscribe((event) => {\n const e = event as { kind?: string; notifications?: unknown[] };\n if (e && e.kind === 'notification-batch' && Array.isArray(e.notifications)) {\n for (const n of e.notifications) onEvent(n);\n return;\n }\n onEvent(event);\n });\n },\n };\n}\n\n// ============================================================================\n// createBrowserBoardPlatformAdapter\n//\n// namespace — logical name for this board instance (e.g. 'my-board').\n// Used as the localStorage key prefix so multiple boards can coexist.\n// opts.callbackBaseUrl — if set, used as selfRef.whatToRun for http callbacks.\n// e.g. 'https://my-app.example.com/api/board'\n// opts.notifyChannel — in-memory notification channel name.\n// The adapter publishes to this channel; pair with notifyRef { kind: 'in-memory-bus', value: channel }.\n// ============================================================================\n\nimport type { ExecutionRef } from '../common/execution-interface.js';\n\n/**\n * Registry of in-browser execution handlers keyed by whatToRun value.\n * Consumers register handlers that will be invoked when the drain cycle\n * dispatches execution with howToRun === 'in-browser'.\n */\nexport type InBrowserHandler = (ref: ExecutionRef, args: Record<string, unknown>) => Promise<{ dispatched: boolean; error?: string }>;\n\nexport function createBrowserBoardPlatformAdapter(\n namespace: string,\n opts?: {\n callbackBaseUrl?: string;\n notifyChannel?: string;\n onWarn?: (msg: string) => void;\n },\n): BoardPlatformAdapter & {\n registerHandler(name: string, handler: InBrowserHandler): void;\n writeMemoryBlob(key: string, data: string): string;\n} {\n const selfRef = opts?.callbackBaseUrl\n ? {\n meta: 'board-live-cards',\n howToRun: 'http:post' as const,\n whatToRun: opts.callbackBaseUrl,\n }\n : {\n meta: 'board-live-cards',\n howToRun: 'in-browser' as const,\n whatToRun: serializeRef({ kind: 'in-browser', value: namespace }),\n };\n\n // In-browser handler registry: maps whatToRun → handler function\n const handlerRegistry = new Map<string, InBrowserHandler>();\n\n // In-memory blob store: ephemeral key→value map for blob refs (kind: 'in-memory')\n const memoryBlobs = new Map<string, string>();\n\n const lock = createInMemoryRelayLock();\n\n return {\n kvStorage: (ns: string) =>\n createLocalStorageKvStorage(`${namespace}:${ns}`),\n\n blobStorage: (ns: string) =>\n createLocalStorageBlobStorage(ns ? `${namespace}:${ns}` : namespace),\n\n journalAdapter: () =>\n createLocalStorageJournalStorageAdapter(`${namespace}:journal`),\n\n lock,\n\n selfRef,\n\n async dispatchExecution(ref, args): Promise<{ dispatched: boolean; error?: string }> {\n if (ref.howToRun === 'http:post') {\n try {\n const raw = ref.whatToRun;\n const url = typeof raw === 'object' ? raw.value : parseRef(raw).value;\n const resp = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(args),\n });\n if (!resp.ok) {\n return { dispatched: false, error: `HTTP ${resp.status}: ${resp.statusText}` };\n }\n return { dispatched: true };\n } catch (e) {\n return { dispatched: false, error: e instanceof Error ? e.message : String(e) };\n }\n }\n\n if (ref.howToRun === 'http:get') {\n try {\n const raw = ref.whatToRun;\n const baseUrl = typeof raw === 'object' ? raw.value : parseRef(raw).value;\n const params = new URLSearchParams(\n Object.entries(args as Record<string, unknown>)\n .filter(([, v]) => v !== undefined && v !== null)\n .map(([k, v]) => [k, String(v)]),\n );\n const url = `${baseUrl}?${params.toString()}`;\n const resp = await fetch(url);\n if (!resp.ok) {\n return { dispatched: false, error: `HTTP ${resp.status}: ${resp.statusText}` };\n }\n return { dispatched: true };\n } catch (e) {\n return { dispatched: false, error: e instanceof Error ? e.message : String(e) };\n }\n }\n\n if (ref.howToRun === 'in-browser') {\n const raw = ref.whatToRun;\n const handlerKey = typeof raw === 'object' ? raw.value : parseRef(raw).value;\n const handler = handlerRegistry.get(handlerKey);\n if (handler) return handler(ref, args);\n return { dispatched: false, error: `No in-browser handler registered for: ${handlerKey}` };\n }\n\n return {\n dispatched: false,\n error: `Browser adapter: unsupported dispatch kind (got: ${ref.howToRun})`,\n };\n },\n\n resolveBlob(ref: KindValueRef): string {\n // In-memory blobs: written by task executors, ephemeral (page-lifetime)\n if (ref.kind === 'in-memory') {\n const content = memoryBlobs.get(ref.value);\n if (content === null || content === undefined) {\n throw new Error(`resolveBlob: in-memory blob not found: ${serializeRef(ref)}`);\n }\n return content;\n }\n // localStorage blobs: persistent across page reloads\n const storage = createLocalStorageBlobStorage(namespace);\n const content = storage.read(ref.value);\n if (content === null) {\n throw new Error(`resolveBlob: blob not found: ${serializeRef(ref)}`);\n }\n return content;\n },\n\n hashFn: computeStableJsonHashBrowser,\n\n genId: (): string => globalThis.crypto.randomUUID().replace(/-/g, ''),\n\n kvStorageForRef: (ref: string) => createLocalStorageKvStorage(parseRef(ref).value),\n\n publishBoardChangeNotifications(notifications) {\n if (!opts?.notifyChannel || notifications.length === 0) return;\n const bus = getInMemoryNotificationBus(opts.notifyChannel);\n bus.publish({ kind: 'notification-batch', notifications });\n },\n\n // requestProcessAccumulated is intentionally absent — the browser caller\n // drives drain cycles via polling or setInterval.\n\n onWarn: opts?.onWarn,\n\n registerHandler(name: string, handler: InBrowserHandler) {\n handlerRegistry.set(name, handler);\n },\n\n writeMemoryBlob(key: string, data: string): string {\n memoryBlobs.set(key, data);\n return serializeRef({ kind: 'in-memory', value: key });\n },\n };\n}\n"]}
@@ -1,24 +1,37 @@
1
- import { B as BoardPlatformAdapter } from '../../board-live-cards-public-CltXYgaY.cjs';
2
- import '../../execution-refs.cjs';
1
+ import { N as NotificationTransport } from '../../types-CU3DjTKL.cjs';
2
+ import { B as BoardPlatformAdapter } from '../../board-live-cards-public-5n1-syA3.cjs';
3
+ import { ExecutionRef } from '../../execution-refs.cjs';
3
4
  import '../../board-live-cards-lib-Bg6EvCo5.cjs';
4
5
  import '../../types-BBhqYGhE.cjs';
5
6
 
7
+ interface InMemoryBus {
8
+ publish(event: unknown): void;
9
+ subscribe(onEvent: (event: unknown) => void): () => void;
10
+ }
11
+ declare function getInMemoryNotificationBus(channel: string): InMemoryBus;
6
12
  /**
7
- * board-live-cards-browser-adapter.ts
8
- *
9
- * Browser implementation of BoardPlatformAdapter.
10
- * Uses localStorage for all persistence.
11
- *
12
- * Constraints vs Node/FS adapter:
13
- * - lock: in-memory no-op (browser is single-threaded; no cross-tab locking)
14
- * - dispatchExecution: supports 'http:post' and 'http:get' only
15
- * - requestProcessAccumulated: not applicable (caller drives via polling / setInterval)
16
- * - selfRef: 'built-in' kind — no spawnable CLI available
13
+ * In-memory NotificationTransport for the browser.
14
+ * Subscribes to the same in-memory bus that the adapter publishes to.
15
+ * Use with notifyRef: { kind: 'in-memory-bus', value: '<channel>' }
17
16
  */
17
+ declare function createInMemoryNotificationTransport(): NotificationTransport;
18
18
 
19
+ /**
20
+ * Registry of in-browser execution handlers keyed by whatToRun value.
21
+ * Consumers register handlers that will be invoked when the drain cycle
22
+ * dispatches execution with howToRun === 'in-browser'.
23
+ */
24
+ type InBrowserHandler = (ref: ExecutionRef, args: Record<string, unknown>) => Promise<{
25
+ dispatched: boolean;
26
+ error?: string;
27
+ }>;
19
28
  declare function createBrowserBoardPlatformAdapter(namespace: string, opts?: {
20
29
  callbackBaseUrl?: string;
30
+ notifyChannel?: string;
21
31
  onWarn?: (msg: string) => void;
22
- }): BoardPlatformAdapter;
32
+ }): BoardPlatformAdapter & {
33
+ registerHandler(name: string, handler: InBrowserHandler): void;
34
+ writeMemoryBlob(key: string, data: string): string;
35
+ };
23
36
 
24
- export { createBrowserBoardPlatformAdapter };
37
+ export { type InBrowserHandler, createBrowserBoardPlatformAdapter, createInMemoryNotificationTransport, getInMemoryNotificationBus };
@@ -1,24 +1,37 @@
1
- import { B as BoardPlatformAdapter } from '../../board-live-cards-public-f-E-FAyp.js';
2
- import '../../execution-refs.js';
1
+ import { N as NotificationTransport } from '../../types-HGDTWIun.js';
2
+ import { B as BoardPlatformAdapter } from '../../board-live-cards-public-CK_J8uv0.js';
3
+ import { ExecutionRef } from '../../execution-refs.js';
3
4
  import '../../board-live-cards-lib-jM2uYG1v.js';
4
5
  import '../../types-BBhqYGhE.js';
5
6
 
7
+ interface InMemoryBus {
8
+ publish(event: unknown): void;
9
+ subscribe(onEvent: (event: unknown) => void): () => void;
10
+ }
11
+ declare function getInMemoryNotificationBus(channel: string): InMemoryBus;
6
12
  /**
7
- * board-live-cards-browser-adapter.ts
8
- *
9
- * Browser implementation of BoardPlatformAdapter.
10
- * Uses localStorage for all persistence.
11
- *
12
- * Constraints vs Node/FS adapter:
13
- * - lock: in-memory no-op (browser is single-threaded; no cross-tab locking)
14
- * - dispatchExecution: supports 'http:post' and 'http:get' only
15
- * - requestProcessAccumulated: not applicable (caller drives via polling / setInterval)
16
- * - selfRef: 'built-in' kind — no spawnable CLI available
13
+ * In-memory NotificationTransport for the browser.
14
+ * Subscribes to the same in-memory bus that the adapter publishes to.
15
+ * Use with notifyRef: { kind: 'in-memory-bus', value: '<channel>' }
17
16
  */
17
+ declare function createInMemoryNotificationTransport(): NotificationTransport;
18
18
 
19
+ /**
20
+ * Registry of in-browser execution handlers keyed by whatToRun value.
21
+ * Consumers register handlers that will be invoked when the drain cycle
22
+ * dispatches execution with howToRun === 'in-browser'.
23
+ */
24
+ type InBrowserHandler = (ref: ExecutionRef, args: Record<string, unknown>) => Promise<{
25
+ dispatched: boolean;
26
+ error?: string;
27
+ }>;
19
28
  declare function createBrowserBoardPlatformAdapter(namespace: string, opts?: {
20
29
  callbackBaseUrl?: string;
30
+ notifyChannel?: string;
21
31
  onWarn?: (msg: string) => void;
22
- }): BoardPlatformAdapter;
32
+ }): BoardPlatformAdapter & {
33
+ registerHandler(name: string, handler: InBrowserHandler): void;
34
+ writeMemoryBlob(key: string, data: string): string;
35
+ };
23
36
 
24
- export { createBrowserBoardPlatformAdapter };
37
+ export { type InBrowserHandler, createBrowserBoardPlatformAdapter, createInMemoryNotificationTransport, getInMemoryNotificationBus };
@@ -1,2 +1,3 @@
1
- function h(r){return `::${r.kind}::${r.value}`}function g(r){if(!r.startsWith("::"))throw new Error(`Invalid ref format (expected ::kind::value): ${r}`);let e=r.slice(2),a=e.indexOf("::");if(a===-1)throw new Error(`Invalid ref format (expected ::kind::value): ${r}`);return {kind:e.slice(0,a),value:e.slice(a+2)}}function f(r){if(r==null||typeof r!="object")return JSON.stringify(r);if(Array.isArray(r))return `[${r.map(f).join(",")}]`;let e=r;return `{${Object.keys(e).sort().map(i=>`${JSON.stringify(i)}:${f(e[i])}`).join(",")}}`}function d(r,e){let a=e>>>0;for(let i=0;i<r.length;i++)a^=r.charCodeAt(i),a=Math.imul(a,16777619)>>>0;return a}function w(r){let e=f(r),a=d(e,2166136261),i=d(e,3735928559),o=d(e,19088743),n=d(e,4277009102);return [a,i,o,n].map(t=>t.toString(16).padStart(8,"0")).join("")}function y(r){function e(n){return `${r}:blob:${n}`}let a=new TextEncoder;function i(n){if(typeof btoa=="function"){let t="";for(let s=0;s<n.length;s++)t+=String.fromCharCode(n[s]);return btoa(t)}return ""}function o(n){if(typeof atob=="function"){let t=atob(n),s=new Uint8Array(t.length);for(let l=0;l<t.length;l++)s[l]=t.charCodeAt(l);return s}return new Uint8Array}return {read(n){return globalThis.localStorage.getItem(e(n))},write(n,t){globalThis.localStorage.setItem(e(n),t);},exists(n){return globalThis.localStorage.getItem(e(n))!==null},remove(n){globalThis.localStorage.removeItem(e(n));},readBytes(n){let t=globalThis.localStorage.getItem(e(n));if(t===null)return null;try{let s=JSON.parse(t);if(s&&s.__kind==="bytes-b64"&&typeof s.data=="string")return o(s.data)}catch{}return a.encode(t)},writeBytes(n,t){let s=JSON.stringify({__kind:"bytes-b64",data:i(t)});globalThis.localStorage.setItem(e(n),s);},listKeys(n){let t=e(n??""),s=[];for(let l=0;l<globalThis.localStorage.length;l++){let u=globalThis.localStorage.key(l);u&&u.startsWith(t)&&s.push(u.slice(e("").length));}return s.sort()},stat(n){let t=globalThis.localStorage.getItem(e(n));if(t===null)return null;let s=a.encode(t).byteLength;try{let l=JSON.parse(t);l&&l.__kind==="bytes-b64"&&typeof l.data=="string"&&(s=o(l.data).byteLength);}catch{}return {key:n,size:s}}}}function p(r){function e(a){return `${r}:kv:${a}`}return {read(a){let i=globalThis.localStorage.getItem(e(a));if(i===null)return null;try{return JSON.parse(i)}catch{return null}},write(a,i){globalThis.localStorage.setItem(e(a),JSON.stringify(i));},delete(a){globalThis.localStorage.removeItem(e(a));},listKeys(a){let i=e(a??""),o=[];for(let n=0;n<globalThis.localStorage.length;n++){let t=globalThis.localStorage.key(n);t!==null&&t.startsWith(i)&&o.push(t.slice(e("").length));}return o}}}function k(r){function e(){let i=globalThis.localStorage.getItem(r);if(!i)return [];try{return JSON.parse(i)}catch{return []}}function a(i){globalThis.localStorage.setItem(r,JSON.stringify(i));}return {readAllEntries(){return e()},appendEntry(i){let o=e();o.push(i),a(o);},generateId(){return globalThis.crypto.randomUUID()}}}function S(){let r=false;return {tryAcquire(){return r?null:(r=true,()=>{r=false;})}}}function A(r,e){let a=e?.callbackBaseUrl?{meta:"board-live-cards",howToRun:"http:post",whatToRun:e.callbackBaseUrl}:{meta:"board-live-cards",howToRun:"built-in",whatToRun:"::built-in::board-live-cards-browser"},i=S();return {kvStorage:o=>p(`${r}:${o}`),blobStorage:o=>y(o?`${r}:${o}`:r),journalAdapter:()=>k(`${r}:journal`),lock:i,selfRef:a,async dispatchExecution(o,n){if(o.howToRun==="http:post")try{let t=o.whatToRun.startsWith("::")?g(o.whatToRun).value:o.whatToRun,s=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)});return s.ok?{dispatched:!0}:{dispatched:!1,error:`HTTP ${s.status}: ${s.statusText}`}}catch(t){return {dispatched:false,error:t instanceof Error?t.message:String(t)}}if(o.howToRun==="http:get")try{let t=o.whatToRun.startsWith("::")?g(o.whatToRun).value:o.whatToRun,s=new URLSearchParams(Object.entries(n).filter(([,c])=>c!=null).map(([c,b])=>[c,String(b)])),l=`${t}?${s.toString()}`,u=await fetch(l);return u.ok?{dispatched:!0}:{dispatched:!1,error:`HTTP ${u.status}: ${u.statusText}`}}catch(t){return {dispatched:false,error:t instanceof Error?t.message:String(t)}}return {dispatched:false,error:`Browser adapter: only http:post and http:get dispatch are supported (got: ${o.howToRun})`}},resolveBlob(o){let t=y(r).read(o.value);if(t===null)throw new Error(`resolveBlob: blob not found: ${h(o)}`);return t},hashFn:w,genId:()=>globalThis.crypto.randomUUID().replace(/-/g,""),kvStorageForRef:o=>p(g(o).value),onWarn:e?.onWarn}}export{A as createBrowserBoardPlatformAdapter};//# sourceMappingURL=board-live-cards-browser-adapter.js.map
1
+ var y="b64:";function B(e){let t=new TextEncoder().encode(e),o=globalThis.Buffer,n;if(o)n=o.from(t).toString("base64");else if(typeof btoa=="function"){let l="";for(let i of t)l+=String.fromCharCode(i);n=btoa(l);}else throw new Error("No base64 encoder available in this runtime");return n.replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/g,"")}function T(e){let t=e.replace(/-/g,"+").replace(/_/g,"/")+"=".repeat((4-e.length%4)%4),o=globalThis.Buffer;if(o)return o.from(t,"base64").toString("utf8");if(typeof atob=="function"){let n=atob(t),l=new Uint8Array(n.length);for(let i=0;i<n.length;i+=1)l[i]=n.charCodeAt(i);return new TextDecoder().decode(l)}throw new Error("No base64 decoder available in this runtime")}function g(e){return `${y}${B(JSON.stringify(e))}`}function d(e){if(!e.startsWith(y))throw new Error(`Invalid ref format (expected ${y}<base64url(json)>): ${e}`);let t;try{t=JSON.parse(T(e.slice(y.length)));}catch{throw new Error(`Invalid ref format (malformed base64url/json): ${e}`)}if(!t||typeof t!="object")throw new Error(`Invalid ref format (expected object payload): ${e}`);let o=t;if(typeof o.kind!="string"||typeof o.value!="string")throw new Error(`Invalid ref format (payload must contain string kind/value): ${e}`);return {kind:o.kind,value:o.value}}function h(e){if(e==null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return `[${e.map(h).join(",")}]`;let t=e;return `{${Object.keys(t).sort().map(n=>`${JSON.stringify(n)}:${h(t[n])}`).join(",")}}`}function p(e,t){let o=t>>>0;for(let n=0;n<e.length;n++)o^=e.charCodeAt(n),o=Math.imul(o,16777619)>>>0;return o}function k(e){let t=h(e),o=p(t,2166136261),n=p(t,3735928559),l=p(t,19088743),i=p(t,4277009102);return [o,n,l,i].map(r=>r.toString(16).padStart(8,"0")).join("")}function w(e){function t(i){return `${e}:blob:${i}`}let o=new TextEncoder;function n(i){if(typeof btoa=="function"){let r="";for(let s=0;s<i.length;s++)r+=String.fromCharCode(i[s]);return btoa(r)}return ""}function l(i){if(typeof atob=="function"){let r=atob(i),s=new Uint8Array(r.length);for(let a=0;a<r.length;a++)s[a]=r.charCodeAt(a);return s}return new Uint8Array}return {read(i){return globalThis.localStorage.getItem(t(i))},write(i,r){globalThis.localStorage.setItem(t(i),r);},exists(i){return globalThis.localStorage.getItem(t(i))!==null},remove(i){globalThis.localStorage.removeItem(t(i));},readBytes(i){let r=globalThis.localStorage.getItem(t(i));if(r===null)return null;try{let s=JSON.parse(r);if(s&&s.__kind==="bytes-b64"&&typeof s.data=="string")return l(s.data)}catch{}return o.encode(r)},writeBytes(i,r){let s=JSON.stringify({__kind:"bytes-b64",data:n(r)});globalThis.localStorage.setItem(t(i),s);},listKeys(i){let r=t(i??""),s=[];for(let a=0;a<globalThis.localStorage.length;a++){let u=globalThis.localStorage.key(a);u&&u.startsWith(r)&&s.push(u.slice(t("").length));}return s.sort()},stat(i){let r=globalThis.localStorage.getItem(t(i));if(r===null)return null;let s=o.encode(r).byteLength;try{let a=JSON.parse(r);a&&a.__kind==="bytes-b64"&&typeof a.data=="string"&&(s=l(a.data).byteLength);}catch{}return {key:i,size:s}}}}function m(e){function t(o){return `${e}:kv:${o}`}return {read(o){let n=globalThis.localStorage.getItem(t(o));if(n===null)return null;try{return JSON.parse(n)}catch{return null}},write(o,n){globalThis.localStorage.setItem(t(o),JSON.stringify(n));},delete(o){globalThis.localStorage.removeItem(t(o));},listKeys(o){let n=t(o??""),l=[];for(let i=0;i<globalThis.localStorage.length;i++){let r=globalThis.localStorage.key(i);r!==null&&r.startsWith(n)&&l.push(r.slice(t("").length));}return l}}}function S(e){function t(){let n=globalThis.localStorage.getItem(e);if(!n)return [];try{return JSON.parse(n)}catch{return []}}function o(n){globalThis.localStorage.setItem(e,JSON.stringify(n));}return {readAllEntries(){return t()},appendEntry(n){let l=t();l.push(n),o(l);},generateId(){return globalThis.crypto.randomUUID()}}}function J(){let e=false;return {tryAcquire(){return e?null:(e=true,()=>{e=false;})}}}var v=new Map;function x(e){let t=v.get(e);if(!t){let o=new Set;t={publish(n){for(let l of o)l(n);},subscribe(n){return o.add(n),()=>{o.delete(n);}}},v.set(e,t);}return t}function C(){return {async subscribe(e,t){return e.kind!=="in-memory-bus"?(console.warn(`[in-memory-transport] unsupported kind: ${e.kind}`),()=>{}):x(e.value).subscribe(n=>{let l=n;if(l&&l.kind==="notification-batch"&&Array.isArray(l.notifications)){for(let i of l.notifications)t(i);return}t(n);})}}}function N(e,t){let o=t?.callbackBaseUrl?{meta:"board-live-cards",howToRun:"http:post",whatToRun:t.callbackBaseUrl}:{meta:"board-live-cards",howToRun:"in-browser",whatToRun:g({kind:"in-browser",value:e})},n=new Map,l=new Map,i=J();return {kvStorage:r=>m(`${e}:${r}`),blobStorage:r=>w(r?`${e}:${r}`:e),journalAdapter:()=>S(`${e}:journal`),lock:i,selfRef:o,async dispatchExecution(r,s){if(r.howToRun==="http:post")try{let a=r.whatToRun,u=typeof a=="object"?a.value:d(a).value,c=await fetch(u,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});return c.ok?{dispatched:!0}:{dispatched:!1,error:`HTTP ${c.status}: ${c.statusText}`}}catch(a){return {dispatched:false,error:a instanceof Error?a.message:String(a)}}if(r.howToRun==="http:get")try{let a=r.whatToRun,u=typeof a=="object"?a.value:d(a).value,c=new URLSearchParams(Object.entries(s).filter(([,f])=>f!=null).map(([f,A])=>[f,String(A)])),R=`${u}?${c.toString()}`,b=await fetch(R);return b.ok?{dispatched:!0}:{dispatched:!1,error:`HTTP ${b.status}: ${b.statusText}`}}catch(a){return {dispatched:false,error:a instanceof Error?a.message:String(a)}}if(r.howToRun==="in-browser"){let a=r.whatToRun,u=typeof a=="object"?a.value:d(a).value,c=n.get(u);return c?c(r,s):{dispatched:false,error:`No in-browser handler registered for: ${u}`}}return {dispatched:false,error:`Browser adapter: unsupported dispatch kind (got: ${r.howToRun})`}},resolveBlob(r){if(r.kind==="in-memory"){let u=l.get(r.value);if(u==null)throw new Error(`resolveBlob: in-memory blob not found: ${g(r)}`);return u}let a=w(e).read(r.value);if(a===null)throw new Error(`resolveBlob: blob not found: ${g(r)}`);return a},hashFn:k,genId:()=>globalThis.crypto.randomUUID().replace(/-/g,""),kvStorageForRef:r=>m(d(r).value),publishBoardChangeNotifications(r){if(!t?.notifyChannel||r.length===0)return;x(t.notifyChannel).publish({kind:"notification-batch",notifications:r});},onWarn:t?.onWarn,registerHandler(r,s){n.set(r,s);},writeMemoryBlob(r,s){return l.set(r,s),g({kind:"in-memory",value:r})}}}
2
+ export{N as createBrowserBoardPlatformAdapter,C as createInMemoryNotificationTransport,x as getInMemoryNotificationBus};//# sourceMappingURL=board-live-cards-browser-adapter.js.map
2
3
  //# sourceMappingURL=board-live-cards-browser-adapter.js.map