@lostgradient/weft 0.16.0 → 0.18.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 (119) hide show
  1. package/README.md +1 -1
  2. package/dist/alerting/alert-manager.d.ts +5 -5
  3. package/dist/alerting/alert-manager.js +24 -6
  4. package/dist/alerting/index.d.ts +1 -1
  5. package/dist/alerting/types.d.ts +17 -13
  6. package/dist/alerting/types.js +5 -0
  7. package/dist/cli/generated/operation-catalog.snapshot.json +73 -0
  8. package/dist/cli/generated/operation-client.generated.d.ts +11 -2
  9. package/dist/cli/generated/operation-client.generated.js +2 -0
  10. package/dist/core/engine/disposal.js +7 -0
  11. package/dist/core/engine/engine-runtime-helpers.d.ts +3 -1
  12. package/dist/core/engine/engine-runtime-helpers.js +2 -2
  13. package/dist/core/engine/errors.d.ts +20 -0
  14. package/dist/core/engine/errors.js +8 -0
  15. package/dist/core/engine/index.d.ts +39 -17
  16. package/dist/core/engine/index.js +81 -9
  17. package/dist/core/engine/inline-launch-queue.d.ts +6 -2
  18. package/dist/core/engine/inline-launch-queue.js +41 -6
  19. package/dist/core/engine/internals.d.ts +2 -0
  20. package/dist/core/engine/lease-manager.d.ts +3 -2
  21. package/dist/core/engine/lease-manager.js +7 -3
  22. package/dist/core/engine/operations-time.js +6 -0
  23. package/dist/core/execution-strategy.d.ts +8 -0
  24. package/dist/core/inline-execution-strategy.d.ts +1 -0
  25. package/dist/core/inline-execution-strategy.js +33 -20
  26. package/dist/core/weft-error.d.ts +1 -1
  27. package/dist/core/weft-error.js +1 -0
  28. package/dist/core/worker-execution-strategy.d.ts +2 -0
  29. package/dist/core/worker-execution-strategy.js +7 -2
  30. package/dist/core/worker-turn-watchdog.d.ts +4 -1
  31. package/dist/core/worker-turn-watchdog.js +9 -4
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.js +1 -0
  34. package/dist/indexeddb.js +1 -1
  35. package/dist/json-schema.js +1 -1
  36. package/dist/mcp/tools.js +1 -3
  37. package/dist/server/authorization-scope.d.ts +32 -2
  38. package/dist/server/dashboard-assets.d.ts +12 -1
  39. package/dist/server/dashboard-assets.js +145 -31
  40. package/dist/server/index.d.ts +2 -0
  41. package/dist/server/index.js +4 -0
  42. package/dist/server/operations/aggregate-workflows.js +0 -7
  43. package/dist/server/operations/async-activity.js +5 -24
  44. package/dist/server/operations/bulk-cancel-workflows.js +3 -8
  45. package/dist/server/operations/bulk-delete-workflows.js +3 -8
  46. package/dist/server/operations/bulk-mutate-workflow-tags.js +3 -8
  47. package/dist/server/operations/bulk-operation-helpers.d.ts +2 -10
  48. package/dist/server/operations/bulk-operation-helpers.js +0 -6
  49. package/dist/server/operations/bulk-retry-failed-workflows.js +3 -8
  50. package/dist/server/operations/bulk-signal-workflows.js +3 -8
  51. package/dist/server/operations/cancel-schedule.js +1 -3
  52. package/dist/server/operations/cancel-workflow.js +1 -3
  53. package/dist/server/operations/create-schedule.js +2 -3
  54. package/dist/server/operations/fork-workflow.js +2 -3
  55. package/dist/server/operations/get-checkpoint-at.js +1 -6
  56. package/dist/server/operations/get-principal.d.ts +75 -0
  57. package/dist/server/operations/get-principal.js +41 -0
  58. package/dist/server/operations/get-registry.js +1 -7
  59. package/dist/server/operations/get-retention-overview.js +2 -15
  60. package/dist/server/operations/get-review.js +2 -15
  61. package/dist/server/operations/get-schedule.js +2 -8
  62. package/dist/server/operations/get-stream-chunks.js +2 -6
  63. package/dist/server/operations/get-system-lease.js +1 -7
  64. package/dist/server/operations/get-system-metrics.js +0 -4
  65. package/dist/server/operations/get-task-diagnostics.js +2 -13
  66. package/dist/server/operations/get-update-result.js +1 -6
  67. package/dist/server/operations/get-workflow-attributes.js +2 -15
  68. package/dist/server/operations/get-workflow-events.js +2 -15
  69. package/dist/server/operations/get-workflow-observability.js +2 -5
  70. package/dist/server/operations/get-workflow-result.js +0 -7
  71. package/dist/server/operations/get-workflow-timeline.js +1 -6
  72. package/dist/server/operations/get-workflow.d.ts +3 -6
  73. package/dist/server/operations/get-workflow.js +2 -12
  74. package/dist/server/operations/list-alerts.d.ts +5 -2
  75. package/dist/server/operations/list-alerts.js +2 -9
  76. package/dist/server/operations/list-checkpoints.js +1 -6
  77. package/dist/server/operations/list-reviews.js +0 -7
  78. package/dist/server/operations/list-schedules.js +3 -8
  79. package/dist/server/operations/list-task-queues.js +0 -4
  80. package/dist/server/operations/list-workers.js +0 -4
  81. package/dist/server/operations/list-workflows.js +0 -7
  82. package/dist/server/operations/pause-schedule.js +1 -3
  83. package/dist/server/operations/purge-workflows.js +2 -5
  84. package/dist/server/operations/query-workflow.js +4 -15
  85. package/dist/server/operations/replay-workflow.js +2 -6
  86. package/dist/server/operations/resume-schedule.js +1 -3
  87. package/dist/server/operations/resume-workflow.js +1 -3
  88. package/dist/server/operations/set-workflow-attributes.js +3 -15
  89. package/dist/server/operations/signal-workflow.js +3 -12
  90. package/dist/server/operations/single-workflow-tag-mutation.js +0 -7
  91. package/dist/server/operations/start-or-signal-workflow.js +2 -3
  92. package/dist/server/operations/start-workflow.js +2 -3
  93. package/dist/server/operations/static-registrations.d.ts +28 -0
  94. package/dist/server/operations/static-registrations.js +263 -0
  95. package/dist/server/operations/storage-capabilities.js +1 -3
  96. package/dist/server/operations/storage.js +7 -13
  97. package/dist/server/operations/submit-review-decision.js +3 -12
  98. package/dist/server/operations/suspend-workflow.js +1 -3
  99. package/dist/server/operations/timeout-workflow.js +1 -3
  100. package/dist/server/operations/update-schedule.js +1 -3
  101. package/dist/server/operations/update-workflow.js +2 -3
  102. package/dist/server/operations/worker-drain.js +0 -8
  103. package/dist/server/rest-binding.d.ts +3 -2
  104. package/dist/server/rest-bindings.d.ts +5 -0
  105. package/dist/server/rest-bindings.js +4 -312
  106. package/dist/server/route-model.d.ts +2 -0
  107. package/dist/server/route-model.js +1 -1
  108. package/dist/server/runtime/task-reconciliation.d.ts +10 -1
  109. package/dist/server/runtime/task-reconciliation.js +26 -2
  110. package/dist/server/serve-internals.js +12 -6
  111. package/dist/storage/indexeddb.js +28 -48
  112. package/dist/storage/testing.d.ts +6 -3
  113. package/dist/storage/testing.js +1 -1
  114. package/dist/version.d.ts +1 -1
  115. package/dist/version.js +1 -1
  116. package/dist/worker/protocol-schemas.d.ts +2 -2
  117. package/dist/worker/protocol-schemas.js +2 -2
  118. package/dist/worker/protocol.js +1 -1
  119. package/package.json +1 -1
package/dist/indexeddb.js CHANGED
@@ -1 +1 @@
1
- async function q(G,J){return await G.get(J)!==null}async function*N(G,J,Q){for await(let[X]of G.scan(J,Q))yield X}async function O(G,J){let Q=0;for await(let X of N(G,J))Q++;return Q}async function R(G,J){let Q=[];for await(let X of N(G,J))Q.push({type:"delete",key:X});if(Q.length===0)return 0;return await G.batch(Q),Q.length}async function h(G,J,Q){let X=[];for await(let Y of N(G,J,Q))X.push({type:"delete",key:Y});if(X.length===0)return 0;return await G.batch(X),X.length}function v(G,J,Q){if(!G.capabilities()[J])throw Error(`Feature "${Q}" requires storage capability "${J}", but this storage backend does not provide it.`)}var A=1e4;class E extends Error{code="StorageBatchOperationLimitExceededError";cap=A;count;target;constructor(G,J){super(`${G} count ${J} exceeds MAX_BATCH_OPERATIONS (${A}).`);this.name="StorageBatchOperationLimitExceededError",this.target=G,this.count=J}}function K(G,J){if(J>A)throw new E(G,J)}function M(G){return G.length>0?G.slice(0,-1)+String.fromCharCode(G.charCodeAt(G.length-1)+1):"ÿ"}function S(G,J={}){if(J.gt!==void 0&&G<=J.gt)return!1;if(J.gte!==void 0&&G<J.gte)return!1;if(J.lt!==void 0&&G>=J.lt)return!1;if(J.lte!==void 0&&G>J.lte)return!1;return!0}function b(G,J){if(G===null||J===null)return G===J;if(G.byteLength!==J.byteLength)return!1;for(let Q=0;Q<G.byteLength;Q++)if(G[Q]!==J[Q])return!1;return!0}async function m(G,J){if(G.has)return G.has(J);return q(G,J)}function x(G,J,Q){if(G.keys)return G.keys(J,Q);return N(G,J,Q)}async function y(G,J){if(G.count)return G.count(J);return O(G,J)}async function I(G,J){if(G.deletePrefix)return G.deletePrefix(J);return R(G,J)}async function g(G,J,Q){if(K("conditionalBatch conditions",J.length),K("conditionalBatch operations",Q.length),v(G,"conditionalBatch","storageConditionalBatch"),!G.conditionalBatch)throw Error("This storage backend reports conditionalBatch capability but does not implement the conditionalBatch() method.");return G.conditionalBatch(J,Q)}function l(G){if(G===void 0)return;if(typeof G!=="number"||!Number.isInteger(G)||G<0)throw Error("deleteRange limit must be a finite non-negative integer");return G===0?0:G}function T(G){let J={},Q=!1;for(let Y of["gt","gte","lt","lte"]){let $=G[Y];if($===void 0)continue;if(typeof $!=="string")throw Error("deleteRange bounds must be strings");J[Y]=$,Q=!0}if(!Q)throw Error("deleteRange requires at least one of gt/gte/lt/lte; use deletePrefix to delete a whole prefix");let X=l(G.limit);if(X!==void 0)J.limit=X;return J}async function k(G,J,Q){let X=T(Q);if(G.deleteRange)return G.deleteRange(J,X);return h(G,J,X)}function p(G,J){let Q={key:G,open:!1};if(J.gte!==void 0&&J.gte>Q.key)Q.key=J.gte,Q.open=!1;if(J.gt!==void 0&&J.gt>=Q.key)Q.key=J.gt,Q.open=!0;return Q}function d(G,J){let Q={key:M(G),open:!0};if(J.lt!==void 0&&J.lt<=Q.key)Q.key=J.lt,Q.open=!0;if(J.lte!==void 0&&J.lte<Q.key)Q.key=J.lte,Q.open=!1;return Q}function c(G,J){let Q=p(G,J),X=d(G,J);if(Q.key>X.key||Q.key===X.key&&(Q.open||X.open))return null;return{lower:Q,upper:X}}function P(G){return G.replaceAll(/:+$/g,"")}function a(G,J){let Q=P(G),X=P(J);if(Q.length===0)return X;if(X.length===0)return Q;return`${Q}:${X}`}class C{#J;#Q;constructor(G,J){this.#J=G,this.#Q=P(J)}#G(G){if(this.#Q.length===0)return G;return G.length===0?`${this.#Q}:`:`${this.#Q}:${G}`}#X(G){return G.map((J)=>{if(J.type==="put")return{type:"put",key:this.#G(J.key),value:J.value};return{type:"delete",key:this.#G(J.key)}})}#Y(G){if(this.#Q.length===0)return G;return G.slice(this.#Q.length+1)}#Z(G){let J={};if(G.limit!==void 0)J.limit=G.limit;if(G.gt!==void 0)J.gt=this.#G(G.gt);if(G.gte!==void 0)J.gte=this.#G(G.gte);if(G.lt!==void 0)J.lt=this.#G(G.lt);if(G.lte!==void 0)J.lte=this.#G(G.lte);return J}#$(G={}){let J=this.#Z(G);if(G.reverse!==void 0)J.reverse=G.reverse;return J}#U(G){return this.#Z(G)}capabilities(){return this.#J.capabilities()}scoped(G){return new C(this.#J,a(this.#Q,G))}async get(G){return this.#J.get(this.#G(G))}async put(G,J){await this.#J.put(this.#G(G),J)}async delete(G){await this.#J.delete(this.#G(G))}async*scan(G,J){for await(let[Q,X]of this.#J.scan(this.#G(G),this.#$(J)))yield[this.#Y(Q),X]}async batch(G){K("batch operations",G.length),await this.#J.batch(this.#X(G))}async conditionalBatch(G,J){return g(this.#J,G.map((Q)=>({key:this.#G(Q.key),expectedValue:Q.expectedValue})),this.#X(J))}async has(G){return m(this.#J,this.#G(G))}async deletePrefix(G){return I(this.#J,this.#G(G))}async deleteRange(G,J){let Q=this.#U(T(J));return k(this.#J,this.#G(G),Q)}async*keys(G,J){for await(let Q of x(this.#J,this.#G(G),this.#$(J)))yield this.#Y(Q)}async count(G){return y(this.#J,this.#G(G))}[Symbol.dispose](){this.#J[Symbol.dispose]()}}function u(G,J){return new C(G,J)}var U="kv";function s(G=globalThis){let{indexedDB:J,IDBKeyRange:Q}=G;if(J===void 0||Q===void 0)throw Error("IndexedDBStorage requires both indexedDB and IDBKeyRange runtime globals.");return{indexedDB:J,IDBKeyRange:Q}}function j(G){return new Promise((J,Q)=>{G.onsuccess=()=>J(G.result),G.onerror=()=>Q(G.error)})}function w(G,J){let Q=null,X=null,Y=null,$=(Z,V)=>{let H=Z??Error(V);if(X){let B=X;Q=null,X=null,B(H);return}Y=H};return G.onsuccess=()=>{if(!Q)return;let Z=Q;Q=null,X=null,Z(G.result)},G.onerror=()=>{$(G.error,"IndexedDB cursor request failed.")},J.onerror=()=>{$(J.error,"IndexedDB transaction failed.")},J.onabort=()=>{$(J.error,"IndexedDB transaction aborted.")},()=>{return new Promise((Z,V)=>{if(Y){let H=Y;Y=null,V(H);return}if(Q=Z,X=V,G.readyState==="done"){let H=Q;Q=null,X=null,H?.(G.result)}})}}class n{#J;#Q=null;#G;#X;constructor(G="weft",J=s()){this.#J=G,this.#X=J,this.#G=this.#Y()}capabilities(){return{persistence:"local",readAfterWrite:"linearizable",scanConsistency:"best-effort",atomicBatch:!0,conditionalBatch:!0,boundedRangeDelete:!0}}#Y(){let G=this.#X.indexedDB.open(this.#J,1);return G.onupgradeneeded=()=>{let J=G.result;if(!J.objectStoreNames.contains(U))J.createObjectStore(U)},j(G).then((J)=>{return this.#Q=J,J})}async get(G){let X=(await this.#G).transaction(U,"readonly").objectStore(U),Y=await j(X.get(G));return Y===void 0?null:new Uint8Array(Y)}async put(G,J){let Y=(await this.#G).transaction(U,"readwrite").objectStore(U);await j(Y.put(J,G))}async delete(G){let X=(await this.#G).transaction(U,"readwrite").objectStore(U);await j(X.delete(G))}async has(G){let X=(await this.#G).transaction(U,"readonly").objectStore(U);return await j(X.count(G))>0}async deletePrefix(G){let J=await this.#G,Q=M(G),X=this.#X.IDBKeyRange.bound(G,Q,!1,!0);return new Promise((Y,$)=>{let Z=J.transaction(U,"readwrite"),V=Z.objectStore(U),H=0,B=V.count(X);B.onsuccess=()=>{H=B.result,V.delete(X)},Z.oncomplete=()=>Y(H),Z.onerror=()=>$(Z.error)})}async deleteRange(G,J){let Q=T(J),X=c(G,Q);if(X===null)return 0;let Y=this.#X.IDBKeyRange.bound(X.lower.key,X.upper.key,X.lower.open,X.upper.open),$=await this.#G,{limit:Z}=Q;return new Promise((V,H)=>{let B=$.transaction(U,"readwrite"),z=B.objectStore(U),D=0;if(Z===void 0){let F=z.count(Y);F.onsuccess=()=>{D=F.result,z.delete(Y)}}else{let F=z.openCursor(Y,"next");F.onsuccess=()=>{let W=F.result;if(W===null||D>=Z)return;if(W.delete(),D++,D<Z)W.continue()}}B.oncomplete=()=>V(D),B.onerror=()=>H(B.error)})}async*scan(G,J={}){let{limit:Q,reverse:X}=J,Y=await this.#G,$=M(G),Z=this.#X.IDBKeyRange.bound(G,$,!1,!0),V=X?"prev":"next",H=Y.transaction(U,"readonly"),z=H.objectStore(U).openCursor(Z,V),D=0,F=w(z,H),W=!1;try{let L=await F();while(L){if(Q!==void 0&&D>=Q)break;let _=L.key;if(S(_,J))yield[_,new Uint8Array(L.value)],D++;L.continue(),L=await F()}W=!0}finally{if(!W)try{H.abort()}catch{}}}async batch(G){if(K("batch operations",G.length),G.length===0)return;let J=await this.#G;return new Promise((Q,X)=>{let Y=J.transaction(U,"readwrite"),$=Y.objectStore(U);for(let Z of G)if(Z.type==="put")$.put(Z.value,Z.key);else $.delete(Z.key);Y.oncomplete=()=>Q(),Y.onerror=()=>X(Y.error)})}async conditionalBatch(G,J){K("conditionalBatch conditions",G.length),K("conditionalBatch operations",J.length);let Q=await this.#G;return new Promise((X,Y)=>{let $=Q.transaction(U,"readwrite"),Z=$.objectStore(U),V=!1,H=!1,B=(F,W)=>{if(V)return;V=!0,Y(F??Error(W))};$.oncomplete=()=>{if(V)return;V=!0,X(!0)},$.onerror=()=>{B($.error,"IndexedDB conditionalBatch transaction failed.")},$.onabort=()=>{if(V)return;if(V=!0,H){X(!1);return}Y($.error??Error("IndexedDB conditionalBatch transaction aborted."))};let z=()=>{for(let F of J)if(F.type==="put")Z.put(F.value,F.key);else Z.delete(F.key)},D=(F)=>{if(F>=G.length){z();return}let W=G[F],L=Z.get(W.key);L.onsuccess=()=>{let _=L.result,f=_===void 0?null:new Uint8Array(_);if(!b(f,W.expectedValue)){H=!0,$.abort();return}D(F+1)},L.onerror=()=>{B(L.error,"IndexedDB conditionalBatch condition check failed.")}};D(0)})}async*keys(G,J={}){let{limit:Q,reverse:X}=J,Y=await this.#G,$=M(G),Z=this.#X.IDBKeyRange.bound(G,$,!1,!0),V=X?"prev":"next",H=Y.transaction(U,"readonly"),z=H.objectStore(U).openKeyCursor(Z,V),D=0,F=w(z,H),W=!1;try{let L=await F();while(L){if(Q!==void 0&&D>=Q)break;let _=L.key;if(S(_,J))yield _,D++;L.continue(),L=await F()}W=!0}finally{if(!W)try{H.abort()}catch{}}}async count(G){let J=await this.#G,Q=M(G),Y=J.transaction(U,"readonly").objectStore(U);return j(Y.count(this.#X.IDBKeyRange.bound(G,Q,!1,!0)))}scoped(G){return u(this,G)}[Symbol.dispose](){if(this.#Q)this.#Q.close(),this.#Q=null}}export{n as IndexedDBStorage};
1
+ async function O(G,J){return await G.get(J)!==null}async function*T(G,J,Q){for await(let[X]of G.scan(J,Q))yield X}async function R(G,J){let Q=0;for await(let X of T(G,J))Q++;return Q}async function h(G,J){let Q=[];for await(let X of T(G,J))Q.push({type:"delete",key:X});if(Q.length===0)return 0;return await G.batch(Q),Q.length}async function v(G,J,Q){let X=[];for await(let Y of T(G,J,Q))X.push({type:"delete",key:Y});if(X.length===0)return 0;return await G.batch(X),X.length}function E(G,J,Q){if(!G.capabilities()[J])throw Error(`Feature "${Q}" requires storage capability "${J}", but this storage backend does not provide it.`)}var P=1e4;class b extends Error{code="StorageBatchOperationLimitExceededError";cap=P;count;target;constructor(G,J){super(`${G} count ${J} exceeds MAX_BATCH_OPERATIONS (${P}).`);this.name="StorageBatchOperationLimitExceededError",this.target=G,this.count=J}}function W(G,J){if(J>P)throw new b(G,J)}function M(G){return G.length>0?G.slice(0,-1)+String.fromCharCode(G.charCodeAt(G.length-1)+1):"ÿ"}function m(G,J={}){if(J.gt!==void 0&&G<=J.gt)return!1;if(J.gte!==void 0&&G<J.gte)return!1;if(J.lt!==void 0&&G>=J.lt)return!1;if(J.lte!==void 0&&G>J.lte)return!1;return!0}function x(G,J){if(G===null||J===null)return G===J;if(G.byteLength!==J.byteLength)return!1;for(let Q=0;Q<G.byteLength;Q++)if(G[Q]!==J[Q])return!1;return!0}async function y(G,J){if(G.has)return G.has(J);return O(G,J)}function I(G,J,Q){if(G.keys)return G.keys(J,Q);return T(G,J,Q)}async function g(G,J){if(G.count)return G.count(J);return R(G,J)}async function k(G,J){if(G.deletePrefix)return G.deletePrefix(J);return h(G,J)}async function c(G,J,Q){if(W("conditionalBatch conditions",J.length),W("conditionalBatch operations",Q.length),E(G,"conditionalBatch","storageConditionalBatch"),!G.conditionalBatch)throw Error("This storage backend reports conditionalBatch capability but does not implement the conditionalBatch() method.");return G.conditionalBatch(J,Q)}function d(G){if(G===void 0)return;if(typeof G!=="number"||!Number.isInteger(G)||G<0)throw Error("deleteRange limit must be a finite non-negative integer");return G===0?0:G}function A(G){let J={},Q=!1;for(let Y of["gt","gte","lt","lte"]){let Z=G[Y];if(Z===void 0)continue;if(typeof Z!=="string")throw Error("deleteRange bounds must be strings");J[Y]=Z,Q=!0}if(!Q)throw Error("deleteRange requires at least one of gt/gte/lt/lte; use deletePrefix to delete a whole prefix");let X=d(G.limit);if(X!==void 0)J.limit=X;return J}async function u(G,J,Q){let X=A(Q);if(G.deleteRange)return G.deleteRange(J,X);return v(G,J,X)}function a(G,J){let Q={key:G,open:!1};if(J.gte!==void 0&&J.gte>Q.key)Q.key=J.gte,Q.open=!1;if(J.gt!==void 0&&J.gt>=Q.key)Q.key=J.gt,Q.open=!0;return Q}function s(G,J){let Q={key:M(G),open:!0};if(J.lt!==void 0&&J.lt<=Q.key)Q.key=J.lt,Q.open=!0;if(J.lte!==void 0&&J.lte<Q.key)Q.key=J.lte,Q.open=!1;return Q}function w(G,J){let Q=a(G,J),X=s(G,J);if(Q.key>X.key||Q.key===X.key&&(Q.open||X.open))return null;return{lower:Q,upper:X}}function q(G){return G.replaceAll(/:+$/g,"")}function n(G,J){let Q=q(G),X=q(J);if(Q.length===0)return X;if(X.length===0)return Q;return`${Q}:${X}`}class C{#J;#Q;constructor(G,J){this.#J=G,this.#Q=q(J)}#G(G){if(this.#Q.length===0)return G;return G.length===0?`${this.#Q}:`:`${this.#Q}:${G}`}#X(G){return G.map((J)=>{if(J.type==="put")return{type:"put",key:this.#G(J.key),value:J.value};return{type:"delete",key:this.#G(J.key)}})}#Y(G){if(this.#Q.length===0)return G;return G.slice(this.#Q.length+1)}#Z(G){let J={};if(G.limit!==void 0)J.limit=G.limit;if(G.gt!==void 0)J.gt=this.#G(G.gt);if(G.gte!==void 0)J.gte=this.#G(G.gte);if(G.lt!==void 0)J.lt=this.#G(G.lt);if(G.lte!==void 0)J.lte=this.#G(G.lte);return J}#$(G={}){let J=this.#Z(G);if(G.reverse!==void 0)J.reverse=G.reverse;return J}#U(G){return this.#Z(G)}capabilities(){return this.#J.capabilities()}scoped(G){return new C(this.#J,n(this.#Q,G))}async get(G){return this.#J.get(this.#G(G))}async put(G,J){await this.#J.put(this.#G(G),J)}async delete(G){await this.#J.delete(this.#G(G))}async*scan(G,J){for await(let[Q,X]of this.#J.scan(this.#G(G),this.#$(J)))yield[this.#Y(Q),X]}async batch(G){W("batch operations",G.length),await this.#J.batch(this.#X(G))}async conditionalBatch(G,J){return c(this.#J,G.map((Q)=>({key:this.#G(Q.key),expectedValue:Q.expectedValue})),this.#X(J))}async has(G){return y(this.#J,this.#G(G))}async deletePrefix(G){return k(this.#J,this.#G(G))}async deleteRange(G,J){let Q=this.#U(A(J));return u(this.#J,this.#G(G),Q)}async*keys(G,J){for await(let Q of I(this.#J,this.#G(G),this.#$(J)))yield this.#Y(Q)}async count(G){return g(this.#J,this.#G(G))}[Symbol.dispose](){this.#J[Symbol.dispose]()}}function f(G,J){return new C(G,J)}var U="kv";function i(G=globalThis){let{indexedDB:J,IDBKeyRange:Q}=G;if(J===void 0||Q===void 0)throw Error("IndexedDBStorage requires both indexedDB and IDBKeyRange runtime globals.");return{indexedDB:J,IDBKeyRange:Q}}function K(G){return new Promise((J,Q)=>{G.onsuccess=()=>J(G.result),G.onerror=()=>Q(G.error)})}function t(G,J){let Q=null,X=null,Y=null,Z=($,H)=>{let F=$??Error(H);if(X){let L=X;Q=null,X=null,L(F);return}Y=F};return G.onsuccess=()=>{if(!Q)return;let $=Q;Q=null,X=null,$(G.result)},G.onerror=()=>{Z(G.error,"IndexedDB cursor request failed.")},J.onerror=()=>{Z(J.error,"IndexedDB transaction failed.")},J.onabort=()=>{Z(J.error,"IndexedDB transaction aborted.")},()=>{return new Promise(($,H)=>{if(Y){let F=Y;Y=null,H(F);return}if(Q=$,X=H,G.readyState==="done"){let F=Q;Q=null,X=null,F?.(G.result)}})}}async function*l(G,J,Q,X,Y,Z){let{limit:$,reverse:H}=X,F=M(Q),L=J.bound(Q,F,!1,!0),z=H?"prev":"next",B=G.transaction(U,"readonly"),V=B.objectStore(U),D=Y(V,L,z),_=t(D,B),j=0,S=!1;try{let N=await _();while(N){if($!==void 0&&j>=$)break;let p=N.key;if(m(p,X))yield Z(N),j++;N.continue(),N=await _()}S=!0}finally{if(!S)try{B.abort()}catch{}}}class r{#J;#Q=null;#G;#X;constructor(G="weft",J=i()){this.#J=G,this.#X=J,this.#G=this.#Y()}capabilities(){return{persistence:"local",readAfterWrite:"linearizable",scanConsistency:"best-effort",atomicBatch:!0,conditionalBatch:!0,boundedRangeDelete:!0}}#Y(){let G=this.#X.indexedDB.open(this.#J,1);return G.onupgradeneeded=()=>{let J=G.result;if(!J.objectStoreNames.contains(U))J.createObjectStore(U)},K(G).then((J)=>{return this.#Q=J,J})}async get(G){let X=(await this.#G).transaction(U,"readonly").objectStore(U),Y=await K(X.get(G));return Y===void 0?null:new Uint8Array(Y)}async put(G,J){let Y=(await this.#G).transaction(U,"readwrite").objectStore(U);await K(Y.put(J,G))}async delete(G){let X=(await this.#G).transaction(U,"readwrite").objectStore(U);await K(X.delete(G))}async has(G){let X=(await this.#G).transaction(U,"readonly").objectStore(U);return await K(X.count(G))>0}async deletePrefix(G){let J=await this.#G,Q=M(G),X=this.#X.IDBKeyRange.bound(G,Q,!1,!0);return new Promise((Y,Z)=>{let $=J.transaction(U,"readwrite"),H=$.objectStore(U),F=0,L=H.count(X);L.onsuccess=()=>{F=L.result,H.delete(X)},$.oncomplete=()=>Y(F),$.onerror=()=>Z($.error)})}async deleteRange(G,J){let Q=A(J),X=w(G,Q);if(X===null)return 0;let Y=this.#X.IDBKeyRange.bound(X.lower.key,X.upper.key,X.lower.open,X.upper.open),Z=await this.#G,{limit:$}=Q;return new Promise((H,F)=>{let L=Z.transaction(U,"readwrite"),z=L.objectStore(U),B=0;if($===void 0){let V=z.count(Y);V.onsuccess=()=>{B=V.result,z.delete(Y)}}else{let V=z.openCursor(Y,"next");V.onsuccess=()=>{let D=V.result;if(D===null||B>=$)return;if(D.delete(),B++,B<$)D.continue()}}L.oncomplete=()=>H(B),L.onerror=()=>F(L.error)})}async*scan(G,J={}){let Q=await this.#G;yield*l(Q,this.#X.IDBKeyRange,G,J,(X,Y,Z)=>X.openCursor(Y,Z),(X)=>[X.key,new Uint8Array(X.value)])}async batch(G){if(W("batch operations",G.length),G.length===0)return;let J=await this.#G;return new Promise((Q,X)=>{let Y=J.transaction(U,"readwrite"),Z=Y.objectStore(U);for(let $ of G)if($.type==="put")Z.put($.value,$.key);else Z.delete($.key);Y.oncomplete=()=>Q(),Y.onerror=()=>X(Y.error)})}async conditionalBatch(G,J){W("conditionalBatch conditions",G.length),W("conditionalBatch operations",J.length);let Q=await this.#G;return new Promise((X,Y)=>{let Z=Q.transaction(U,"readwrite"),$=Z.objectStore(U),H=!1,F=!1,L=(V,D)=>{if(H)return;H=!0,Y(V??Error(D))};Z.oncomplete=()=>{if(H)return;H=!0,X(!0)},Z.onerror=()=>{L(Z.error,"IndexedDB conditionalBatch transaction failed.")},Z.onabort=()=>{if(H)return;if(H=!0,F){X(!1);return}Y(Z.error??Error("IndexedDB conditionalBatch transaction aborted."))};let z=()=>{for(let V of J)if(V.type==="put")$.put(V.value,V.key);else $.delete(V.key)},B=(V)=>{if(V>=G.length){z();return}let D=G[V],_=$.get(D.key);_.onsuccess=()=>{let j=_.result,S=j===void 0?null:new Uint8Array(j);if(!x(S,D.expectedValue)){F=!0,Z.abort();return}B(V+1)},_.onerror=()=>{L(_.error,"IndexedDB conditionalBatch condition check failed.")}};B(0)})}async*keys(G,J={}){let Q=await this.#G;yield*l(Q,this.#X.IDBKeyRange,G,J,(X,Y,Z)=>X.openKeyCursor(Y,Z),(X)=>X.key)}async count(G){let J=await this.#G,Q=M(G),Y=J.transaction(U,"readonly").objectStore(U);return K(Y.count(this.#X.IDBKeyRange.bound(G,Q,!1,!0)))}scoped(G){return f(this,G)}[Symbol.dispose](){if(this.#Q)this.#Q.close(),this.#Q=null}}export{r as IndexedDBStorage};
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- var O=import.meta.require,T=(j,q,z)=>{if(q!=null){if(typeof q!=="object"&&typeof q!=="function")throw TypeError('Object expected to be assigned to "using" declaration');let A;if(z)A=q[Symbol.asyncDispose];if(A===void 0)A=q[Symbol.dispose];if(typeof A!=="function")throw TypeError("Object not disposable");j.push([z,A,q])}else if(z)j.push([z]);return q},y=(j,q,z)=>{let A=(G)=>q=z?new SuppressedError(G,q,"An error was suppressed during disposal"):(z=!0,G),H=(G)=>{while(G=j.pop())try{var $=G[1]&&G[1].call(G[2]);if(G[0])return Promise.resolve($).then(H,(Q)=>(A(Q),H()))}catch(Q){A(Q)}if(z)throw q};return H()};class X extends Error{code;constructor(j,q,z){super(q,z);this.code=j,this.name=j}}var B={WorkflowAlreadyExistsError:!0,BulkDeleteRequiresTerminalWorkflowsError:!0,BulkOperationConfirmationError:!0,WorkflowTypeNotRegisteredForRecoveryError:!0,EngineCreateNameMismatchError:!0,EngineDisposedError:!0,WorkflowNotFoundError:!0,WorkflowNotRegisteredError:!0,WorkflowConcurrencyLimitExceededError:!0,WorkflowSuspendNotSupportedError:!0,ActivityResolutionError:!0,BranchTopologyChangedError:!0,PersistedDataIncompatibleError:!0,WorkflowTimeoutError:!0,HttpClientError:!0,WorkerProtocolIncompatibleError:!0,UpdateTimeoutError:!0,UpdateValidationError:!0,WorkflowTerminalError:!0,WorkflowBuilderError:!0,VersionMismatchError:!0,EffectReplayConflictError:!0,ReviewTimeoutError:!0,AtomicStateConflictError:!0,StandardSchemaValidationError:!0,ActivityReconciliationCapabilityError:!0,ActivityReconciliationConflictError:!0,ActivityReconciliationIndeterminateError:!0,DurableActivityScopeError:!0,DurableActivityUnsupportedError:!0,AsyncActivityTokenNotFoundError:!0,ActivityScheduleToCloseTimeoutError:!0,ActivityPerAttemptTimeoutError:!0,PayloadSizeExceededError:!0,StartOrSignalConflictError:!0,WorkflowTeardownPendingError:!0,IdempotencyKeyPurgedError:!0},R=new Set(Object.keys(B));class K extends X{fieldName;operation;issues;constructor(j){super("StandardSchemaValidationError",M(j.fieldName,j.operation,j.issues));this.fieldName=j.fieldName,this.operation=j.operation,this.issues=j.issues}}async function Z(j,q,z){let A=j["~standard"];if(!D(A))throw TypeError(`Schema for ${z.fieldName} does not provide runtime validation. Attach a Standard Schema validator (Zod, Valibot, or another vendor) or supply a runtime-validating schema at this boundary.`);let H=await A.validate(q);if(H.issues===void 0)return H.value;throw new K({fieldName:z.fieldName,operation:z.operation,issues:H.issues.map(U)})}function Y(j){return j.map((q)=>q.path===""?q.message:`${q.path}: ${q.message}`).join(`
2
+ var O=import.meta.require,T=(j,q,z)=>{if(q!=null){if(typeof q!=="object"&&typeof q!=="function")throw TypeError('Object expected to be assigned to "using" declaration');let A;if(z)A=q[Symbol.asyncDispose];if(A===void 0)A=q[Symbol.dispose];if(typeof A!=="function")throw TypeError("Object not disposable");j.push([z,A,q])}else if(z)j.push([z]);return q},y=(j,q,z)=>{let A=(G)=>q=z?new SuppressedError(G,q,"An error was suppressed during disposal"):(z=!0,G),H=(G)=>{while(G=j.pop())try{var $=G[1]&&G[1].call(G[2]);if(G[0])return Promise.resolve($).then(H,(Q)=>(A(Q),H()))}catch(Q){A(Q)}if(z)throw q};return H()};class X extends Error{code;constructor(j,q,z){super(q,z);this.code=j,this.name=j}}var B={WorkflowAlreadyExistsError:!0,BulkDeleteRequiresTerminalWorkflowsError:!0,BulkOperationConfirmationError:!0,WorkflowTypeNotRegisteredForRecoveryError:!0,EngineCreateNameMismatchError:!0,EngineDisposedError:!0,EngineDisposalError:!0,WorkflowNotFoundError:!0,WorkflowNotRegisteredError:!0,WorkflowConcurrencyLimitExceededError:!0,WorkflowSuspendNotSupportedError:!0,ActivityResolutionError:!0,BranchTopologyChangedError:!0,PersistedDataIncompatibleError:!0,WorkflowTimeoutError:!0,HttpClientError:!0,WorkerProtocolIncompatibleError:!0,UpdateTimeoutError:!0,UpdateValidationError:!0,WorkflowTerminalError:!0,WorkflowBuilderError:!0,VersionMismatchError:!0,EffectReplayConflictError:!0,ReviewTimeoutError:!0,AtomicStateConflictError:!0,StandardSchemaValidationError:!0,ActivityReconciliationCapabilityError:!0,ActivityReconciliationConflictError:!0,ActivityReconciliationIndeterminateError:!0,DurableActivityScopeError:!0,DurableActivityUnsupportedError:!0,AsyncActivityTokenNotFoundError:!0,ActivityScheduleToCloseTimeoutError:!0,ActivityPerAttemptTimeoutError:!0,PayloadSizeExceededError:!0,StartOrSignalConflictError:!0,WorkflowTeardownPendingError:!0,IdempotencyKeyPurgedError:!0},R=new Set(Object.keys(B));class K extends X{fieldName;operation;issues;constructor(j){super("StandardSchemaValidationError",M(j.fieldName,j.operation,j.issues));this.fieldName=j.fieldName,this.operation=j.operation,this.issues=j.issues}}async function Z(j,q,z){let A=j["~standard"];if(!D(A))throw TypeError(`Schema for ${z.fieldName} does not provide runtime validation. Attach a Standard Schema validator (Zod, Valibot, or another vendor) or supply a runtime-validating schema at this boundary.`);let H=await A.validate(q);if(H.issues===void 0)return H.value;throw new K({fieldName:z.fieldName,operation:z.operation,issues:H.issues.map(U)})}function Y(j){return j.map((q)=>q.path===""?q.message:`${q.path}: ${q.message}`).join(`
3
3
  `)}function D(j){return typeof j.validate==="function"}function U(j){return{message:j.message,path:b(j.path)}}function b(j){if(j===void 0||j.length===0)return"";let q="";for(let z of j)q+="/",q+=w(z);return q}function w(j){let q=F(j)?j.key:j;return J(String(q))}function F(j){return j!==null&&typeof j==="object"&&"key"in j}function J(j){return j.replace(/~/g,"~0").replace(/\//g,"~1")}function M(j,q,z){return`Validation failed for ${q===void 0?j:`${q} ${j}`}:
4
4
  ${Y(z)}`}var V=Y,k=K,P=Z;export{P as validateStandardSchema,V as formatStandardSchemaIssues,k as StandardSchemaValidationError};
package/dist/mcp/tools.js CHANGED
@@ -113,9 +113,7 @@ function withWorkflowToolTimeoutParameter(schema) {
113
113
  };
114
114
  }
115
115
  function parseWorkflowToolArguments(argumentsValue) {
116
- if (argumentsValue === null || typeof argumentsValue !== "object" || Array.isArray(argumentsValue))
117
- return { input: argumentsValue, timeoutMs: DEFAULT_WORKFLOW_TOOL_TIMEOUT_MS };
118
- const record = argumentsValue, timeoutMs = parseWorkflowToolTimeoutMs(record.timeoutMs);
116
+ const record = requireObject(argumentsValue), timeoutMs = parseWorkflowToolTimeoutMs(record.timeoutMs);
119
117
  return { input: record.input, timeoutMs };
120
118
  }
121
119
  function parseWorkflowToolTimeoutMs(value) {
@@ -15,11 +15,41 @@
15
15
  * imply `workflows:read` or `workflows:write`. Operation authors must declare
16
16
  * the exact scope they require. Scope-implication semantics (if any) are a
17
17
  * future track.
18
+ *
19
+ * Public export so dashboards and API-key provisioning tooling can consume
20
+ * the canonical vocabulary instead of hand-copying it.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * import { AUTHORIZATION_SCOPES } from '@lostgradient/weft/server';
25
+ *
26
+ * console.log(AUTHORIZATION_SCOPES.includes('workflows:read'));
27
+ * ```
18
28
  */
19
29
  export declare const AUTHORIZATION_SCOPES: readonly ["workflows:read", "workflows:write", "workflows:admin", "schedules:read", "schedules:write", "signals:write", "updates:write", "queries:read", "reviews:read", "reviews:write", "attributes:read", "attributes:write", "tags:write", "streams:read", "events:read", "storage:read", "storage:write", "storage:admin", "workers:write", "system:read", "system:admin"];
20
- /** String-literal union of every authorization scope. */
30
+ /**
31
+ * String-literal union of every authorization scope.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * import { type AuthorizationScope } from '@lostgradient/weft/server';
36
+ *
37
+ * const scope: AuthorizationScope = 'workflows:read';
38
+ * console.log(scope);
39
+ * ```
40
+ */
21
41
  export type AuthorizationScope = (typeof AUTHORIZATION_SCOPES)[number];
22
- /** Runtime type guard for the `AuthorizationScope` union. */
42
+ /**
43
+ * Runtime type guard for the `AuthorizationScope` union.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * import { isAuthorizationScope } from '@lostgradient/weft/server';
48
+ *
49
+ * console.log(isAuthorizationScope('workflows:read'));
50
+ * console.log(isAuthorizationScope('not-a-scope'));
51
+ * ```
52
+ */
23
53
  export declare function isAuthorizationScope(value: string): value is AuthorizationScope;
24
54
  /**
25
55
  * Extract the full set of scopes granted by a JWT payload by *merging* every
@@ -1,3 +1,13 @@
1
+ import * as fileSystem from 'node:fs';
2
+ type DashboardAssetFileSystem = {
3
+ constants: Pick<typeof fileSystem.constants, 'O_RDONLY' | 'O_NOFOLLOW' | 'O_NONBLOCK'>;
4
+ realpathSync: (path: string) => string;
5
+ statSync: (path: string) => fileSystem.Stats;
6
+ openSync: (path: string, flags: Parameters<typeof fileSystem.openSync>[1], mode?: Parameters<typeof fileSystem.openSync>[2]) => number;
7
+ fstatSync: (descriptor: number) => fileSystem.Stats;
8
+ read: (descriptor: number, buffer: NodeJS.ArrayBufferView, offset: number, length: number, position: number | null) => Promise<number>;
9
+ closeSync: (descriptor: number) => void;
10
+ };
1
11
  /**
2
12
  * Static files belonging to an externally supplied dashboard.
3
13
  *
@@ -23,4 +33,5 @@ export interface ResolvedDashboardAssets {
23
33
  directory: string;
24
34
  }
25
35
  export declare function resolveDashboardAssets(value: unknown, pageRoutes: readonly string[]): ResolvedDashboardAssets;
26
- export declare function createDashboardAssetRoute(assets: ResolvedDashboardAssets): Partial<Record<'GET' | 'HEAD', (request: Request) => Response>>;
36
+ export declare function createDashboardAssetRoute(assets: ResolvedDashboardAssets, assetFileSystem?: DashboardAssetFileSystem): Partial<Record<'GET' | 'HEAD', (request: Request) => Response>>;
37
+ export {};
@@ -1,14 +1,6 @@
1
- import { realpathSync, statSync } from "node:fs";
2
- import { isAbsolute, join, relative, resolve, sep } from "node:path";
3
- const ROOT_API_PREFIXES = ["/api", "/v1"], DISCOVERY_DOCUMENTS = [
4
- "/openapi.json",
5
- "/openrpc.json",
6
- "/asyncapi.json",
7
- "/.well-known/mcp.json"
8
- ];
9
- function hasPathPrefix(path, prefix) {
10
- return path === prefix || path.startsWith(`${prefix}/`);
11
- }
1
+ import * as fileSystem from "node:fs";
2
+ import { isAbsolute, join, relative, resolve as resolvePath, sep } from "node:path";
3
+ import { API_PREFIX, DIRECT_HTTP_ROUTES, ROOT_API_PREFIX } from "./route-model.js";
12
4
  function overlapsDashboardPageRoute(prefix, route) {
13
5
  if (route.endsWith("/*")) {
14
6
  const base = route.slice(0, -2);
@@ -16,6 +8,9 @@ function overlapsDashboardPageRoute(prefix, route) {
16
8
  }
17
9
  return prefix === route;
18
10
  }
11
+ function overlapsWildcardRoute(prefix, route) {
12
+ return prefix === route || prefix.startsWith(`${route}/`) || route.startsWith(`${prefix}/`);
13
+ }
19
14
  function hasInvalidPrefixShape(prefix) {
20
15
  return prefix.length === 0 || prefix === "/" || !prefix.startsWith("/") || prefix.endsWith("/") || ["?", "#", "%", "*", ":", "\\"].some((character) => prefix.includes(character));
21
16
  }
@@ -24,14 +19,19 @@ function validateAssetPrefixShape(prefix) {
24
19
  throw Error("dashboardAssets.prefix must be an absolute path prefix without a trailing slash, wildcard, parameter, query, fragment, percent escape, or backslash");
25
20
  if (prefix.split("/").slice(1).some((segment) => segment.length === 0 || segment === "." || segment === ".."))
26
21
  throw Error("dashboardAssets.prefix must contain only concrete, non-empty path segments");
22
+ const serializedPathname = new URL(prefix, "http://weft.invalid").pathname;
23
+ if (serializedPathname !== prefix)
24
+ throw Error(`dashboardAssets.prefix must round-trip through URL serialization unchanged; ${prefix} serializes as ${serializedPathname}`);
27
25
  }
28
26
  function validateAssetPrefixReservations(prefix, pageRoutes) {
29
- if (ROOT_API_PREFIXES.some((reserved) => hasPathPrefix(prefix, reserved)))
30
- throw Error("dashboardAssets.prefix must not overlap the /api or /v1 route spaces");
27
+ if ([
28
+ API_PREFIX,
29
+ ROOT_API_PREFIX,
30
+ ...DIRECT_HTTP_ROUTES.map(({ path }) => path)
31
+ ].some((reserved) => overlapsWildcardRoute(prefix, reserved)))
32
+ throw Error("dashboardAssets.prefix must not overlap the /api, /v1, or direct HTTP route spaces");
31
33
  if (pageRoutes.some((route) => overlapsDashboardPageRoute(prefix, route)))
32
34
  throw Error("dashboardAssets.prefix must not overlap dashboard page routes");
33
- if (DISCOVERY_DOCUMENTS.some((document) => document === prefix))
34
- throw Error("dashboardAssets.prefix must not overlap a discovery document route");
35
35
  }
36
36
  function validateAssetPrefix(prefix, pageRoutes) {
37
37
  validateAssetPrefixShape(prefix);
@@ -46,50 +46,164 @@ export function resolveDashboardAssets(value, pageRoutes) {
46
46
  if (typeof prefix !== "string" || typeof directory !== "string")
47
47
  throw Error("dashboardAssets.prefix and dashboardAssets.directory must be strings");
48
48
  validateAssetPrefix(prefix, pageRoutes);
49
- const resolvedDirectory = resolve(directory);
49
+ const resolvedDirectory = resolvePath(directory);
50
50
  let directoryStats;
51
51
  try {
52
- directoryStats = statSync(resolvedDirectory);
52
+ directoryStats = fileSystem.statSync(resolvedDirectory);
53
53
  } catch {
54
54
  throw Error(`dashboardAssets.directory does not exist: ${directory}`);
55
55
  }
56
56
  if (!directoryStats.isDirectory())
57
57
  throw Error(`dashboardAssets.directory must be a directory: ${directory}`);
58
- return { prefix, directory: realpathSync(resolvedDirectory) };
58
+ return { prefix, directory: fileSystem.realpathSync(resolvedDirectory) };
59
59
  }
60
60
  function isWithinDirectory(directory, path) {
61
61
  const pathRelativeToDirectory = relative(directory, path);
62
62
  return pathRelativeToDirectory.length > 0 && pathRelativeToDirectory !== ".." && !pathRelativeToDirectory.startsWith(`..${sep}`) && !isAbsolute(pathRelativeToDirectory);
63
63
  }
64
- function assetResponse(directory, prefix, request) {
64
+ function hasSameFileIdentity(left, right) {
65
+ return left.dev === right.dev && left.ino === right.ino;
66
+ }
67
+ function resolveAssetPath(directory, prefix, request) {
68
+ if (request.signal.aborted)
69
+ return;
65
70
  const rawWildcardPath = new URL(request.url).pathname.slice(prefix.length + 1);
66
71
  let wildcardPath;
67
72
  try {
68
73
  wildcardPath = decodeURIComponent(rawWildcardPath);
69
74
  } catch {
70
- return new Response("Not Found", { status: 404 });
75
+ return;
71
76
  }
72
77
  if (wildcardPath.length === 0)
73
- return new Response("Not Found", { status: 404 });
78
+ return;
74
79
  const pathSegments = wildcardPath.split(/[\\/]/);
75
80
  if (pathSegments.some((segment) => segment === ".." || segment.length === 0))
76
- return new Response("Not Found", { status: 404 });
77
- const assetPath = resolve(join(directory, ...pathSegments));
81
+ return;
82
+ const assetPath = resolvePath(join(directory, ...pathSegments));
78
83
  if (!isWithinDirectory(directory, assetPath))
84
+ return;
85
+ return assetPath;
86
+ }
87
+ function isVerifiedAssetDescriptor(request, directory, canonicalStats, openedStats, postOpenStats, postOpenAssetPath, openedAssetPath) {
88
+ return !request.signal.aborted && isWithinDirectory(directory, postOpenAssetPath) && (openedAssetPath === void 0 || isWithinDirectory(directory, openedAssetPath)) && canonicalStats.isFile() && openedStats.isFile() && postOpenStats.isFile() && hasSameFileIdentity(openedStats, canonicalStats) && hasSameFileIdentity(openedStats, postOpenStats);
89
+ }
90
+ function resolveOpenedDescriptorPath(descriptor, assetFileSystem) {
91
+ for (const descriptorPath of [`/proc/self/fd/${descriptor}`, `/dev/fd/${descriptor}`])
92
+ try {
93
+ const resolvedPath = assetFileSystem.realpathSync(descriptorPath);
94
+ if (resolvedPath.startsWith("/proc/self/fd/") || resolvedPath.startsWith("/dev/fd/"))
95
+ continue;
96
+ return resolvedPath;
97
+ } catch {}
98
+ return;
99
+ }
100
+ function createAssetStream(descriptor, verifiedSize, assetFileSystem) {
101
+ let closed = !1, cancelled = !1, position = 0, remainingBytes = verifiedSize, inFlightRead;
102
+ const close = () => {
103
+ if (!closed) {
104
+ closed = !0;
105
+ assetFileSystem.closeSync(descriptor);
106
+ }
107
+ };
108
+ return { stream: new ReadableStream({
109
+ async pull(controller) {
110
+ if (cancelled)
111
+ return;
112
+ if (remainingBytes === 0) {
113
+ close();
114
+ controller.close();
115
+ return;
116
+ }
117
+ const buffer = Buffer.allocUnsafe(Math.min(65536, remainingBytes)), read = assetFileSystem.read(descriptor, buffer, 0, buffer.byteLength, position);
118
+ inFlightRead = read;
119
+ try {
120
+ const bytesRead = await read;
121
+ if (cancelled) {
122
+ close();
123
+ return;
124
+ }
125
+ if (bytesRead === 0) {
126
+ close();
127
+ controller.error(Error("Dashboard asset ended before its verified content length was read."));
128
+ return;
129
+ }
130
+ const boundedBytesRead = Math.min(bytesRead, remainingBytes);
131
+ position += boundedBytesRead;
132
+ remainingBytes -= boundedBytesRead;
133
+ controller.enqueue(buffer.subarray(0, boundedBytesRead));
134
+ if (remainingBytes === 0) {
135
+ close();
136
+ controller.close();
137
+ }
138
+ } catch (error) {
139
+ close();
140
+ if (!cancelled)
141
+ controller.error(error);
142
+ } finally {
143
+ if (inFlightRead === read)
144
+ inFlightRead = void 0;
145
+ }
146
+ },
147
+ async cancel() {
148
+ cancelled = !0;
149
+ try {
150
+ await inFlightRead;
151
+ } catch {}
152
+ close();
153
+ }
154
+ }), close };
155
+ }
156
+ function readAssetDescriptor(descriptor, buffer, offset, length, position) {
157
+ return new Promise((_resolve, reject) => {
158
+ fileSystem.read(descriptor, buffer, offset, length, position, (error, bytesRead) => {
159
+ if (error)
160
+ reject(error);
161
+ else
162
+ _resolve(bytesRead);
163
+ });
164
+ });
165
+ }
166
+ function assetResponse(directory, prefix, request, assetFileSystem) {
167
+ const assetPath = resolveAssetPath(directory, prefix, request);
168
+ if (assetPath === void 0)
79
169
  return new Response("Not Found", { status: 404 });
80
- let realAssetPath;
170
+ let descriptor;
81
171
  try {
82
- realAssetPath = realpathSync(assetPath);
83
- if (!statSync(realAssetPath).isFile())
172
+ const realAssetPath = assetFileSystem.realpathSync(assetPath);
173
+ if (!isWithinDirectory(directory, realAssetPath))
84
174
  return new Response("Not Found", { status: 404 });
175
+ const canonicalStats = assetFileSystem.statSync(realAssetPath);
176
+ descriptor = assetFileSystem.openSync(realAssetPath, assetFileSystem.constants.O_RDONLY | assetFileSystem.constants.O_NOFOLLOW | assetFileSystem.constants.O_NONBLOCK);
177
+ const fileStats = assetFileSystem.fstatSync(descriptor), postOpenAssetPath = assetFileSystem.realpathSync(realAssetPath), postOpenStats = assetFileSystem.statSync(postOpenAssetPath), openedAssetPath = resolveOpenedDescriptorPath(descriptor, assetFileSystem);
178
+ if (!isVerifiedAssetDescriptor(request, directory, canonicalStats, fileStats, postOpenStats, postOpenAssetPath, openedAssetPath))
179
+ return new Response("Not Found", { status: 404 });
180
+ const headers = {
181
+ "content-length": String(fileStats.size),
182
+ "content-type": Bun.file(realAssetPath).type
183
+ };
184
+ if (request.method === "HEAD")
185
+ return new Response(null, { headers });
186
+ const assetStream = createAssetStream(descriptor, fileStats.size, assetFileSystem);
187
+ try {
188
+ const response = new Response(assetStream.stream, { headers });
189
+ descriptor = void 0;
190
+ return response;
191
+ } catch {
192
+ assetStream.close();
193
+ descriptor = void 0;
194
+ return new Response("Not Found", { status: 404 });
195
+ }
85
196
  } catch {
86
197
  return new Response("Not Found", { status: 404 });
198
+ } finally {
199
+ if (descriptor !== void 0)
200
+ assetFileSystem.closeSync(descriptor);
87
201
  }
88
- if (!isWithinDirectory(directory, realAssetPath))
89
- return new Response("Not Found", { status: 404 });
90
- return new Response(Bun.file(realAssetPath));
91
202
  }
92
- export function createDashboardAssetRoute(assets) {
93
- const handler = (request) => assetResponse(assets.directory, assets.prefix, request);
203
+ export function createDashboardAssetRoute(assets, assetFileSystem) {
204
+ const fileSystemForAsset = assetFileSystem ?? {
205
+ ...fileSystem,
206
+ read: readAssetDescriptor
207
+ }, handler = (request) => assetResponse(assets.directory, assets.prefix, request, fileSystemForAsset);
94
208
  return { GET: handler, HEAD: handler };
95
209
  }
@@ -18,6 +18,8 @@ import { TaskQueue } from './task-queue.ts';
18
18
  export { wireEventBroadcasting, type EventBroadcastingHandle, } from './runtime/event-broadcasting.ts';
19
19
  export type { DashboardAssets } from './dashboard-assets.ts';
20
20
  export type { CorsOptions } from './runtime/cors.ts';
21
+ export { AUTHORIZATION_SCOPES, isAuthorizationScope, type AuthorizationScope, } from './authorization-scope.ts';
22
+ export type { GetPrincipalOutput } from './operations/get-principal.ts';
21
23
  export { createRateLimiter, createRotatingApiKeyStore, defaultAuthAuditSink, isSensitiveHeader, redactCredential, redactHeaders, validateRateLimitConfig, type ApiKeyRegistration, type AuthAuditEvent, type AuthAuditSink, type AuthConfig, type RateLimitConfig, type RateLimitDecision, type RateLimiter, type RotatingApiKeyStore, } from './authentication.ts';
22
24
  export type { RetryPolicy } from '../core/types.ts';
23
25
  export type { PrometheusExporter } from '../observability/metrics.ts';
@@ -27,6 +27,10 @@ import { TaskQueue } from "./task-queue.js";
27
27
  export {
28
28
  wireEventBroadcasting
29
29
  } from "./runtime/event-broadcasting.js";
30
+ export {
31
+ AUTHORIZATION_SCOPES,
32
+ isAuthorizationScope
33
+ } from "./authorization-scope.js";
30
34
  export {
31
35
  createRateLimiter,
32
36
  createRotatingApiKeyStore,
@@ -79,12 +79,6 @@ function extractAggregateWorkflowsInput(request) {
79
79
  ...limitValue !== void 0 && Number.isFinite(limitValue) && limitValue >= 1 ? { limit: limitValue } : {}
80
80
  };
81
81
  }
82
- function shapeAggregateWorkflowsSuccess(result) {
83
- return new Response(JSON.stringify(result), {
84
- status: 200,
85
- headers: { "Content-Type": "application/json" }
86
- });
87
- }
88
82
  function shapeAggregateWorkflowsFault(fault) {
89
83
  if (fault.code === "Unprocessable")
90
84
  return shapeRestFault(fault, { status: 400 });
@@ -106,6 +100,5 @@ export const aggregateWorkflowsRestBinding = {
106
100
  },
107
101
  extractInput: async (request) => extractAggregateWorkflowsInput(request),
108
102
  success: { kind: "json", status: 200 },
109
- shapeSuccess: (output) => shapeAggregateWorkflowsSuccess(output),
110
103
  shapeFault: shapeAggregateWorkflowsFault
111
104
  };
@@ -11,7 +11,7 @@ import { PayloadSizeExceededError } from "../../core/payload-size.js";
11
11
  import { raiseFault } from "../operation-catalog.js";
12
12
  import { defineOperation } from "../operation-registry.js";
13
13
  import { readRestJsonBody } from "../rest-body.js";
14
- import { invalidParamsFault, isOperationFault, shapeRestFault } from "./operation-helpers.js";
14
+ import { invalidParamsFault, isOperationFault } from "./operation-helpers.js";
15
15
  const asyncActivityReadAccess = {
16
16
  kind: "scoped",
17
17
  scopes: { kind: "anyOf", scopes: ["workflows:read"] }
@@ -137,20 +137,7 @@ export const completeAsyncActivityOperation = defineOperation({
137
137
  }
138
138
  return { ok: !0 };
139
139
  }
140
- });
141
- function shapeAsyncActivitySuccess(output) {
142
- return new Response(JSON.stringify(output), {
143
- status: 200,
144
- headers: { "Content-Type": "application/json" }
145
- });
146
- }
147
- function shapePendingAsyncActivitiesSuccess(output) {
148
- return new Response(JSON.stringify(output), {
149
- status: 200,
150
- headers: { "Content-Type": "application/json" }
151
- });
152
- }
153
- export const listPendingAsyncActivitiesRestBinding = {
140
+ }), listPendingAsyncActivitiesRestBinding = {
154
141
  method: "GET",
155
142
  path: "/v1/workflows/:id/pending-async-activities",
156
143
  pathParamNames: ["id"],
@@ -168,9 +155,7 @@ export const listPendingAsyncActivitiesRestBinding = {
168
155
  ...cursor === null ? {} : { cursor }
169
156
  };
170
157
  },
171
- success: { kind: "json", status: 200 },
172
- shapeSuccess: (output) => shapePendingAsyncActivitiesSuccess(output),
173
- shapeFault: shapeRestFault
158
+ success: { kind: "json", status: 200 }
174
159
  };
175
160
  async function readJsonObjectBody(request, context) {
176
161
  const body = await readRestJsonBody(request, context).catch((error) => {
@@ -198,9 +183,7 @@ export const completeAsyncActivityRestBinding = {
198
183
  ..."result" in body ? { result: body.result } : {}
199
184
  };
200
185
  },
201
- success: { kind: "json", status: 200 },
202
- shapeSuccess: (output) => shapeAsyncActivitySuccess(output),
203
- shapeFault: shapeRestFault
186
+ success: { kind: "json", status: 200 }
204
187
  }, failAsyncActivityRestBinding = {
205
188
  method: "POST",
206
189
  path: "/v1/activities/fail",
@@ -217,7 +200,5 @@ export const completeAsyncActivityRestBinding = {
217
200
  error: body.error
218
201
  };
219
202
  },
220
- success: { kind: "json", status: 200 },
221
- shapeSuccess: (output) => shapeAsyncActivitySuccess(output),
222
- shapeFault: shapeRestFault
203
+ success: { kind: "json", status: 200 }
223
204
  };
@@ -12,11 +12,8 @@ import {
12
12
  parseBulkOperationControlFromBody,
13
13
  readOptionalJsonBody
14
14
  } from "./bulk-filter-helpers.js";
15
- import {
16
- shapeBulkJsonSuccess,
17
- validatedListFilterFromBulkInput
18
- } from "./bulk-operation-helpers.js";
19
- import { invalidParamsFault, shapeRestFault } from "./operation-helpers.js";
15
+ import { validatedListFilterFromBulkInput } from "./bulk-operation-helpers.js";
16
+ import { invalidParamsFault } from "./operation-helpers.js";
20
17
  const bulkCancelWorkflowsInput = bulkListFilterInputSchema.merge(bulkOperationControlInputSchema), bulkCancelWorkflowsOutput = z.unknown();
21
18
  export const bulkCancelWorkflowsOperation = defineOperation({
22
19
  name: "weft.workflows.bulk.cancel",
@@ -58,7 +55,5 @@ export const bulkCancelWorkflowsOperation = defineOperation({
58
55
  throw invalidParamsFault(faultMessage(error));
59
56
  }
60
57
  },
61
- success: { kind: "json", status: 200 },
62
- shapeSuccess: (output) => shapeBulkJsonSuccess(output),
63
- shapeFault: shapeRestFault
58
+ success: { kind: "json", status: 200 }
64
59
  };
@@ -16,11 +16,8 @@ import {
16
16
  readOptionalJsonBody,
17
17
  unprocessableFault
18
18
  } from "./bulk-filter-helpers.js";
19
- import {
20
- shapeBulkJsonSuccess,
21
- validatedListFilterFromBulkInput
22
- } from "./bulk-operation-helpers.js";
23
- import { invalidParamsFault, shapeRestFault } from "./operation-helpers.js";
19
+ import { validatedListFilterFromBulkInput } from "./bulk-operation-helpers.js";
20
+ import { invalidParamsFault } from "./operation-helpers.js";
24
21
  const bulkDeleteWorkflowsInput = bulkListFilterInputSchema.merge(bulkOperationControlInputSchema), bulkDeleteWorkflowsOutput = z.unknown();
25
22
  export const bulkDeleteWorkflowsOperation = defineOperation({
26
23
  name: "weft.workflows.bulk.delete",
@@ -65,7 +62,5 @@ export const bulkDeleteWorkflowsOperation = defineOperation({
65
62
  throw invalidParamsFault(faultMessage(error));
66
63
  }
67
64
  },
68
- success: { kind: "json", status: 200 },
69
- shapeSuccess: (output) => shapeBulkJsonSuccess(output),
70
- shapeFault: shapeRestFault
65
+ success: { kind: "json", status: 200 }
71
66
  };
@@ -13,11 +13,8 @@ import {
13
13
  parseBulkOperationControlFromBody,
14
14
  readOptionalJsonBody
15
15
  } from "./bulk-filter-helpers.js";
16
- import {
17
- shapeBulkJsonSuccess,
18
- validatedListFilterFromBulkInput
19
- } from "./bulk-operation-helpers.js";
20
- import { invalidParamsFault, shapeRestFault } from "./operation-helpers.js";
16
+ import { validatedListFilterFromBulkInput } from "./bulk-operation-helpers.js";
17
+ import { invalidParamsFault } from "./operation-helpers.js";
21
18
  const bulkMutateWorkflowTagsInput = z.object({
22
19
  filter: bulkListFilterInputSchema.optional(),
23
20
  tags: z.array(z.string()),
@@ -94,7 +91,5 @@ export const bulkMutateWorkflowTagsRestBinding = {
94
91
  ...parseBulkOperationControlFromBody(body)
95
92
  };
96
93
  },
97
- success: { kind: "json", status: 200 },
98
- shapeSuccess: (output) => shapeBulkJsonSuccess(output),
99
- shapeFault: shapeRestFault
94
+ success: { kind: "json", status: 200 }
100
95
  };
@@ -1,4 +1,4 @@
1
- import type { BulkCancelResult, BulkDeleteResult, BulkOperationDryRunResult, BulkRetryFailedResult, BulkSignalResult, BulkTagResult, ListFilter, PurgeResult } from '../../core/types.ts';
1
+ import type { ListFilter } from '../../core/types.ts';
2
2
  import { type BulkListFilterInput } from './bulk-filter-helpers.ts';
3
3
  /**
4
4
  * Coerces filter tags (label `Field "filter.tags"`), builds a `ListFilter`
@@ -13,15 +13,7 @@ import { type BulkListFilterInput } from './bulk-filter-helpers.ts';
13
13
  * runs.
14
14
  *
15
15
  * Not used by `purge-workflows.ts`: purge allows empty/unscoped filters and
16
- * shapes faults through `shapeRestFault` (sanitized) rather than passing
16
+ * uses the canonical REST fault fallback (sanitized) rather than passing
17
17
  * raw engine messages through unmasked.
18
18
  */
19
19
  export declare function validatedListFilterFromBulkInput(input: BulkListFilterInput): ListFilter;
20
- export type BulkOperationSuccessResult = BulkCancelResult | BulkDeleteResult | BulkRetryFailedResult | BulkSignalResult | BulkTagResult | BulkOperationDryRunResult | PurgeResult;
21
- /**
22
- * Standard JSON 200 response shape for bulk-workflow operations. Used by
23
- * every bulk operation's REST `shapeSuccess`; the result types are unioned
24
- * rather than left as `unknown` so the type system catches accidental
25
- * `undefined` / `Response` / `Error` arguments at the call site.
26
- */
27
- export declare function shapeBulkJsonSuccess(result: BulkOperationSuccessResult): Response;
@@ -21,9 +21,3 @@ export function validatedListFilterFromBulkInput(input) {
21
21
  }
22
22
  return filter;
23
23
  }
24
- export function shapeBulkJsonSuccess(result) {
25
- return new Response(JSON.stringify(result), {
26
- status: 200,
27
- headers: { "Content-Type": "application/json" }
28
- });
29
- }
@@ -12,11 +12,8 @@ import {
12
12
  parseBulkOperationControlFromBody,
13
13
  readOptionalJsonBody
14
14
  } from "./bulk-filter-helpers.js";
15
- import {
16
- shapeBulkJsonSuccess,
17
- validatedListFilterFromBulkInput
18
- } from "./bulk-operation-helpers.js";
19
- import { invalidParamsFault, shapeRestFault } from "./operation-helpers.js";
15
+ import { validatedListFilterFromBulkInput } from "./bulk-operation-helpers.js";
16
+ import { invalidParamsFault } from "./operation-helpers.js";
20
17
  const bulkRetryFailedWorkflowsInput = bulkListFilterInputSchema.merge(bulkOperationControlInputSchema), bulkRetryFailedWorkflowsOutput = z.unknown();
21
18
  export const bulkRetryFailedWorkflowsOperation = defineOperation({
22
19
  name: "weft.workflows.bulk.retryfailed",
@@ -58,7 +55,5 @@ export const bulkRetryFailedWorkflowsOperation = defineOperation({
58
55
  throw invalidParamsFault(faultMessage(error));
59
56
  }
60
57
  },
61
- success: { kind: "json", status: 200 },
62
- shapeSuccess: (output) => shapeBulkJsonSuccess(output),
63
- shapeFault: shapeRestFault
58
+ success: { kind: "json", status: 200 }
64
59
  };
@@ -12,11 +12,8 @@ import {
12
12
  parseBulkOperationControlFromBody,
13
13
  readOptionalJsonBody
14
14
  } from "./bulk-filter-helpers.js";
15
- import {
16
- shapeBulkJsonSuccess,
17
- validatedListFilterFromBulkInput
18
- } from "./bulk-operation-helpers.js";
19
- import { invalidParamsFault, shapeRestFault } from "./operation-helpers.js";
15
+ import { validatedListFilterFromBulkInput } from "./bulk-operation-helpers.js";
16
+ import { invalidParamsFault } from "./operation-helpers.js";
20
17
  const bulkSignalWorkflowsInput = bulkListFilterInputSchema.extend({
21
18
  name: z.string().min(1),
22
19
  payload: z.unknown().optional()
@@ -74,7 +71,5 @@ export const bulkSignalWorkflowsOperation = defineOperation({
74
71
  ...parseBulkOperationControlFromBody(body)
75
72
  };
76
73
  },
77
- success: { kind: "json", status: 200 },
78
- shapeSuccess: (output) => shapeBulkJsonSuccess(output),
79
- shapeFault: shapeRestFault
74
+ success: { kind: "json", status: 200 }
80
75
  };