@lostgradient/weft 0.15.0 → 0.16.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 (56) hide show
  1. package/README.md +1 -1
  2. package/dist/alerting/alert-manager.d.ts +2 -0
  3. package/dist/alerting/alert-manager.js +3 -0
  4. package/dist/cli/api.js +5 -19
  5. package/dist/cli/console.d.ts +12 -0
  6. package/dist/cli/console.js +62 -0
  7. package/dist/cli/generated/operation-catalog.snapshot.json +71 -0
  8. package/dist/cli/generated/operation-client.generated.d.ts +15 -2
  9. package/dist/cli/generated/operation-client.generated.js +2 -0
  10. package/dist/cli/help-text.d.ts +1 -1
  11. package/dist/cli/help-text.js +1 -0
  12. package/dist/cli/index.d.ts +1 -0
  13. package/dist/cli/index.js +1 -0
  14. package/dist/cli/json-input.d.ts +20 -0
  15. package/dist/cli/json-input.js +33 -0
  16. package/dist/cli/parse-arguments.js +2 -0
  17. package/dist/cli/schedule.js +7 -9
  18. package/dist/cli/types.d.ts +1 -0
  19. package/dist/cli/workflow-commands.js +5 -25
  20. package/dist/cli-main.js +4 -2
  21. package/dist/core/engine/index.d.ts +3 -0
  22. package/dist/core/engine/index.js +3 -0
  23. package/dist/diagnostics/recommendations.js +1 -9
  24. package/dist/http.js +2 -2
  25. package/dist/indexeddb.js +1 -1
  26. package/dist/server/authorization-scope.d.ts +1 -1
  27. package/dist/server/authorization-scope.js +0 -2
  28. package/dist/server/dashboard-assets.d.ts +26 -0
  29. package/dist/server/dashboard-assets.js +95 -0
  30. package/dist/server/index.d.ts +10 -1
  31. package/dist/server/index.js +11 -2
  32. package/dist/server/interactive-operations.js +1 -0
  33. package/dist/server/operations/create-schedule.js +19 -46
  34. package/dist/server/operations/list-alerts.d.ts +26 -0
  35. package/dist/server/operations/list-alerts.js +54 -0
  36. package/dist/server/operations/schedule-faults.d.ts +9 -1
  37. package/dist/server/operations/schedule-faults.js +51 -0
  38. package/dist/server/operations/update-schedule.js +7 -54
  39. package/dist/server/rest-bindings.js +3 -0
  40. package/dist/service-worker/index.d.ts +4 -1
  41. package/dist/service-worker/index.js +2 -2
  42. package/dist/service-worker/setup.d.ts +20 -0
  43. package/dist/service-worker/setup.js +5 -5
  44. package/dist/storage/bun-sql.js +13 -31
  45. package/dist/storage/lmdb.js +1 -1
  46. package/dist/storage/memory.js +1 -1
  47. package/dist/storage/neon.js +2 -2
  48. package/dist/storage/postgres.js +2 -2
  49. package/dist/storage/resolve.js +1 -1
  50. package/dist/storage/scoped-storage.js +1 -1
  51. package/dist/storage/testing.js +1 -1
  52. package/dist/storage/turso.js +2 -2
  53. package/dist/version.d.ts +1 -1
  54. package/dist/version.js +1 -1
  55. package/dist/web-extension.js +1 -1
  56. package/package.json +5 -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 M(G,J){if(J>A)throw new E(G,J)}function j(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(M("conditionalBatch conditions",J.length),M("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:j(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.reverse!==void 0)J.reverse=G.reverse;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={};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}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.#Z(J)))yield[this.#Y(Q),X]}async batch(G){M("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.#$(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.#Z(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 K(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)},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=j(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,$=j(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(M("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){M("conditionalBatch conditions",G.length),M("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,$=j(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=j(G),Y=J.transaction(U,"readonly").objectStore(U);return K(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 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};
@@ -16,7 +16,7 @@
16
16
  * the exact scope they require. Scope-implication semantics (if any) are a
17
17
  * future track.
18
18
  */
19
- 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", "budget:read", "budget:write", "storage:read", "storage:write", "storage:admin", "workers:write", "system:read", "system:admin"];
19
+ 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
20
  /** String-literal union of every authorization scope. */
21
21
  export type AuthorizationScope = (typeof AUTHORIZATION_SCOPES)[number];
22
22
  /** Runtime type guard for the `AuthorizationScope` union. */
@@ -14,8 +14,6 @@ export const AUTHORIZATION_SCOPES = [
14
14
  "tags:write",
15
15
  "streams:read",
16
16
  "events:read",
17
- "budget:read",
18
- "budget:write",
19
17
  "storage:read",
20
18
  "storage:write",
21
19
  "storage:admin",
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Static files belonging to an externally supplied dashboard.
3
+ *
4
+ * `prefix` is mounted as `${prefix}/*`; `directory` must exist before
5
+ * `serve()` is called.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import type { DashboardAssets } from '@lostgradient/weft/server';
10
+ *
11
+ * const dashboardAssets: DashboardAssets = {
12
+ * prefix: '/assets',
13
+ * directory: './dist/assets',
14
+ * };
15
+ * ```
16
+ */
17
+ export interface DashboardAssets {
18
+ prefix: string;
19
+ directory: string;
20
+ }
21
+ export interface ResolvedDashboardAssets {
22
+ prefix: string;
23
+ directory: string;
24
+ }
25
+ export declare function resolveDashboardAssets(value: unknown, pageRoutes: readonly string[]): ResolvedDashboardAssets;
26
+ export declare function createDashboardAssetRoute(assets: ResolvedDashboardAssets): Partial<Record<'GET' | 'HEAD', (request: Request) => Response>>;
@@ -0,0 +1,95 @@
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
+ }
12
+ function overlapsDashboardPageRoute(prefix, route) {
13
+ if (route.endsWith("/*")) {
14
+ const base = route.slice(0, -2);
15
+ return prefix === base || prefix.startsWith(`${base}/`);
16
+ }
17
+ return prefix === route;
18
+ }
19
+ function hasInvalidPrefixShape(prefix) {
20
+ return prefix.length === 0 || prefix === "/" || !prefix.startsWith("/") || prefix.endsWith("/") || ["?", "#", "%", "*", ":", "\\"].some((character) => prefix.includes(character));
21
+ }
22
+ function validateAssetPrefixShape(prefix) {
23
+ if (hasInvalidPrefixShape(prefix))
24
+ throw Error("dashboardAssets.prefix must be an absolute path prefix without a trailing slash, wildcard, parameter, query, fragment, percent escape, or backslash");
25
+ if (prefix.split("/").slice(1).some((segment) => segment.length === 0 || segment === "." || segment === ".."))
26
+ throw Error("dashboardAssets.prefix must contain only concrete, non-empty path segments");
27
+ }
28
+ 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");
31
+ if (pageRoutes.some((route) => overlapsDashboardPageRoute(prefix, route)))
32
+ 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
+ }
36
+ function validateAssetPrefix(prefix, pageRoutes) {
37
+ validateAssetPrefixShape(prefix);
38
+ validateAssetPrefixReservations(prefix, pageRoutes);
39
+ }
40
+ export function resolveDashboardAssets(value, pageRoutes) {
41
+ if (typeof value !== "object" || value === null || Array.isArray(value))
42
+ throw Error("dashboardAssets must be an object with prefix and directory strings");
43
+ if (!("prefix" in value) || !("directory" in value))
44
+ throw Error("dashboardAssets.prefix and dashboardAssets.directory must be strings");
45
+ const { prefix, directory } = value;
46
+ if (typeof prefix !== "string" || typeof directory !== "string")
47
+ throw Error("dashboardAssets.prefix and dashboardAssets.directory must be strings");
48
+ validateAssetPrefix(prefix, pageRoutes);
49
+ const resolvedDirectory = resolve(directory);
50
+ let directoryStats;
51
+ try {
52
+ directoryStats = statSync(resolvedDirectory);
53
+ } catch {
54
+ throw Error(`dashboardAssets.directory does not exist: ${directory}`);
55
+ }
56
+ if (!directoryStats.isDirectory())
57
+ throw Error(`dashboardAssets.directory must be a directory: ${directory}`);
58
+ return { prefix, directory: realpathSync(resolvedDirectory) };
59
+ }
60
+ function isWithinDirectory(directory, path) {
61
+ const pathRelativeToDirectory = relative(directory, path);
62
+ return pathRelativeToDirectory.length > 0 && pathRelativeToDirectory !== ".." && !pathRelativeToDirectory.startsWith(`..${sep}`) && !isAbsolute(pathRelativeToDirectory);
63
+ }
64
+ function assetResponse(directory, prefix, request) {
65
+ const rawWildcardPath = new URL(request.url).pathname.slice(prefix.length + 1);
66
+ let wildcardPath;
67
+ try {
68
+ wildcardPath = decodeURIComponent(rawWildcardPath);
69
+ } catch {
70
+ return new Response("Not Found", { status: 404 });
71
+ }
72
+ if (wildcardPath.length === 0)
73
+ return new Response("Not Found", { status: 404 });
74
+ const pathSegments = wildcardPath.split(/[\\/]/);
75
+ if (pathSegments.some((segment) => segment === ".." || segment.length === 0))
76
+ return new Response("Not Found", { status: 404 });
77
+ const assetPath = resolve(join(directory, ...pathSegments));
78
+ if (!isWithinDirectory(directory, assetPath))
79
+ return new Response("Not Found", { status: 404 });
80
+ let realAssetPath;
81
+ try {
82
+ realAssetPath = realpathSync(assetPath);
83
+ if (!statSync(realAssetPath).isFile())
84
+ return new Response("Not Found", { status: 404 });
85
+ } catch {
86
+ return new Response("Not Found", { status: 404 });
87
+ }
88
+ if (!isWithinDirectory(directory, realAssetPath))
89
+ return new Response("Not Found", { status: 404 });
90
+ return new Response(Bun.file(realAssetPath));
91
+ }
92
+ export function createDashboardAssetRoute(assets) {
93
+ const handler = (request) => assetResponse(assets.directory, assets.prefix, request);
94
+ return { GET: handler, HEAD: handler };
95
+ }
@@ -10,11 +10,13 @@ import type { PrometheusExporter } from '../observability/metrics.ts';
10
10
  import type { RoutingPolicy } from '../worker/registry.ts';
11
11
  import { WorkerRegistry } from '../worker/registry.ts';
12
12
  import type { AuthConfig, RateLimitConfig } from './authentication.ts';
13
+ import { type DashboardAssets } from './dashboard-assets.ts';
13
14
  import type { DiscoveryInfo } from './discovery-info.ts';
14
15
  import type { CorsOptions } from './runtime/cors.ts';
15
16
  import type { SchedulingPolicy } from './task-queue-types.ts';
16
17
  import { TaskQueue } from './task-queue.ts';
17
18
  export { wireEventBroadcasting, type EventBroadcastingHandle, } from './runtime/event-broadcasting.ts';
19
+ export type { DashboardAssets } from './dashboard-assets.ts';
18
20
  export type { CorsOptions } from './runtime/cors.ts';
19
21
  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';
20
22
  export type { RetryPolicy } from '../core/types.ts';
@@ -32,6 +34,8 @@ export { TaskQueue } from './task-queue.ts';
32
34
  * They are intentionally specific (no blanket `/*`) so they cannot shadow the
33
35
  * API served under the `/api` prefix or the root-stable discovery endpoints —
34
36
  * those fall through to the `fetch` handler.
37
+ * The supported page routes are `/`, `/workflows`, `/workflows/*`, `/reviews`,
38
+ * `/workers`, `/schedules`, `/storage`, and `/system`.
35
39
  *
36
40
  * @example
37
41
  * ```ts
@@ -42,7 +46,7 @@ export { TaskQueue } from './task-queue.ts';
42
46
  * console.log(DASHBOARD_PAGE_ROUTES.includes('/workflows')); // true
43
47
  * ```
44
48
  */
45
- export declare const DASHBOARD_PAGE_ROUTES: readonly ["/", "/workflows", "/workflows/*", "/reviews", "/workers"];
49
+ export declare const DASHBOARD_PAGE_ROUTES: readonly ["/", "/workflows", "/workflows/*", "/reviews", "/workers", "/schedules", "/storage", "/system"];
46
50
  /**
47
51
  * Route pattern owned by an externally supplied dashboard shell.
48
52
  *
@@ -129,6 +133,11 @@ export interface ServeOptions {
129
133
  development?: boolean;
130
134
  /** Optional external dashboard shell served at {@link DASHBOARD_PAGE_ROUTES}. */
131
135
  dashboard?: DashboardRouteTarget;
136
+ /**
137
+ * Static files served below an explicit prefix for the supplied dashboard.
138
+ * The directory and prefix are validated synchronously before the port binds.
139
+ */
140
+ dashboardAssets?: DashboardAssets;
132
141
  /** Authentication configuration. When provided, all non-public endpoints require valid credentials. */
133
142
  auth?: AuthConfig;
134
143
  /**
@@ -1,4 +1,8 @@
1
1
  import { WorkerRegistry } from "../worker/registry.js";
2
+ import {
3
+ createDashboardAssetRoute,
4
+ resolveDashboardAssets
5
+ } from "./dashboard-assets.js";
2
6
  import { createServerWebSocketHandlers } from "./runtime/authentication-bridge.js";
3
7
  import {
4
8
  wireEventBroadcasting
@@ -39,13 +43,18 @@ export const DASHBOARD_PAGE_ROUTES = [
39
43
  "/workflows",
40
44
  "/workflows/*",
41
45
  "/reviews",
42
- "/workers"
46
+ "/workers",
47
+ "/schedules",
48
+ "/storage",
49
+ "/system"
43
50
  ];
44
51
  export function serve(options) {
45
- const { port, hostname, development, tlsOptions, serverOptions, serverMetricsCollector } = resolveNetworkConfig(options), context = buildServerContext(serverOptions, serverMetricsCollector), boundCleanup = (operationId) => cleanupWorkflowIndex(context, operationId), routes = {};
52
+ const dashboardAssets = options.dashboardAssets === void 0 ? void 0 : resolveDashboardAssets(options.dashboardAssets, DASHBOARD_PAGE_ROUTES), { port, hostname, development, tlsOptions, serverOptions, serverMetricsCollector } = resolveNetworkConfig(options), context = buildServerContext(serverOptions, serverMetricsCollector), boundCleanup = (operationId) => cleanupWorkflowIndex(context, operationId), routes = {};
46
53
  if (options.dashboard != null)
47
54
  for (const path of DASHBOARD_PAGE_ROUTES)
48
55
  routes[path] = options.dashboard;
56
+ if (dashboardAssets !== void 0)
57
+ routes[`${dashboardAssets.prefix}/*`] = createDashboardAssetRoute(dashboardAssets);
49
58
  const serverHolder = { current: null }, server = Bun.serve(buildBunServeConfig(port, hostname, development, routes, tlsOptions, buildFetchHandler(serverHolder, context, serverOptions), createServerWebSocketHandlers(context, serverOptions, boundCleanup)));
50
59
  serverHolder.current = server;
51
60
  const stack = new AsyncDisposableStack;
@@ -1,4 +1,5 @@
1
1
  export const INTERACTIVE_OPERATION_NAMES = [
2
+ "weft.alerts.list",
2
3
  "weft.workflows.start",
3
4
  "weft.workflows.get",
4
5
  "weft.workflows.scheduleprovenance.get",
@@ -1,17 +1,16 @@
1
1
  import { z } from "zod";
2
2
  import { defineOperation } from "../operation-registry.js";
3
3
  import { invalidParamsFault, shapeRestFault } from "./operation-helpers.js";
4
- import { mapScheduleErrorToFault, validateScheduleInputCadence } from "./schedule-faults.js";
4
+ import {
5
+ mapScheduleErrorToFault,
6
+ validateScheduleInputCadence,
7
+ validateScheduleMutableOptions
8
+ } from "./schedule-faults.js";
5
9
  import {
6
10
  extractSharedScheduleRestFields,
7
11
  parseScheduleRestBodyRequestRecord
8
12
  } from "./schedule-rest-body.js";
9
- const VALID_SCHEDULE_OVERLAP_POLICIES = new Set([
10
- "skip",
11
- "queue",
12
- "cancel-running",
13
- "allow"
14
- ]), createScheduleInput = z.object({
13
+ const createScheduleInput = z.object({
15
14
  type: z.unknown().describe("Workflow type name. Runtime validation requires a non-empty string."),
16
15
  cronExpression: z.unknown().optional().describe("Cron expression. Supply exactly one of cronExpression or every; runtime validation requires a non-empty string."),
17
16
  every: z.unknown().optional().describe("Interval period (duration string or milliseconds). Supply exactly one of cronExpression or every."),
@@ -29,43 +28,21 @@ function validateRequiredScheduleFields(input) {
29
28
  throw invalidParamsFault("Missing required field: type");
30
29
  return { type: input.type, spec: validateScheduleInputCadence(input) };
31
30
  }
31
+ function validateScheduleId(value) {
32
+ if (value === void 0)
33
+ return;
34
+ if (typeof value !== "string" || value.length === 0)
35
+ throw invalidParamsFault('Field "id" must be a non-empty string');
36
+ return value;
37
+ }
32
38
  function validateOptionalScheduleFields(input) {
33
- let validatedId;
34
- if (input.id !== void 0) {
35
- if (typeof input.id !== "string" || input.id.length === 0)
36
- throw invalidParamsFault('Field "id" must be a non-empty string');
37
- validatedId = input.id;
38
- }
39
- let validatedDescription;
40
- if (input.description !== void 0) {
41
- if (typeof input.description !== "string")
42
- throw invalidParamsFault('Field "description" must be a string');
43
- validatedDescription = input.description;
44
- }
45
- let validatedOverlap;
46
- if (input.overlap !== void 0) {
47
- if (typeof input.overlap !== "string" || !isScheduleOverlapPolicy(input.overlap))
48
- throw invalidParamsFault('Field "overlap" must be one of skip, queue, cancel-running, allow');
49
- validatedOverlap = input.overlap;
50
- }
51
- let validatedBackfill;
52
- if (input.backfill !== void 0) {
53
- if (typeof input.backfill !== "boolean")
54
- throw invalidParamsFault('Field "backfill" must be a boolean');
55
- validatedBackfill = input.backfill;
56
- }
57
- let validatedJitter;
58
- if (input.jitter !== void 0) {
59
- if (typeof input.jitter !== "string" && typeof input.jitter !== "number")
60
- throw invalidParamsFault('Field "jitter" must be a duration string or a number of milliseconds');
61
- validatedJitter = input.jitter;
62
- }
39
+ const validatedId = validateScheduleId(input.id), validatedOptions = validateScheduleMutableOptions(input);
63
40
  return {
64
41
  id: validatedId,
65
- description: validatedDescription,
66
- overlap: validatedOverlap,
67
- backfill: validatedBackfill,
68
- jitter: validatedJitter
42
+ description: validatedOptions.description,
43
+ overlap: validatedOptions.overlap,
44
+ backfill: validatedOptions.backfill,
45
+ jitter: validatedOptions.jitter
69
46
  };
70
47
  }
71
48
  function validateCreateScheduleInput(input) {
@@ -99,11 +76,7 @@ export const createScheduleOperation = defineOperation({
99
76
  throw mapScheduleErrorToFault(validated.id ?? "", error);
100
77
  }
101
78
  }
102
- });
103
- function isScheduleOverlapPolicy(value) {
104
- return VALID_SCHEDULE_OVERLAP_POLICIES.has(value);
105
- }
106
- export const createScheduleRestBinding = {
79
+ }), createScheduleRestBinding = {
107
80
  method: "POST",
108
81
  path: "/v1/schedules",
109
82
  pathParamNames: [],
@@ -0,0 +1,26 @@
1
+ /**
2
+ * `weft.alerts.list` operation + REST binding.
3
+ *
4
+ * Reports the alert rules that are currently firing. The response deliberately
5
+ * omits rule actions and webhook destinations: this is an operator diagnostic
6
+ * surface, not a configuration export.
7
+ */
8
+ import { z } from 'zod';
9
+ import type { AlertMetric } from '../../alerting/types.ts';
10
+ import type { UnknownRestBinding } from '../rest-bindings.ts';
11
+ declare const listAlertsInput: z.ZodObject<{}, z.core.$strip>;
12
+ declare const activeAlertSchema: z.ZodObject<{
13
+ metric: z.ZodType<AlertMetric, unknown, z.core.$ZodTypeInternals<AlertMetric, unknown>>;
14
+ threshold: z.ZodNumber;
15
+ currentValue: z.ZodNumber;
16
+ window: z.ZodNullable<z.ZodString>;
17
+ firedAt: z.ZodNullable<z.ZodNumber>;
18
+ }, z.core.$strip>;
19
+ export type ListAlertsInput = z.infer<typeof listAlertsInput>;
20
+ export type ActiveAlert = z.infer<typeof activeAlertSchema>;
21
+ export type ListAlertsOutput = {
22
+ items: ActiveAlert[];
23
+ };
24
+ export declare const listAlertsOperation: import("../operation-catalog.ts").OperationDefinition<Record<string, never>, ListAlertsOutput, unknown>;
25
+ export declare const listAlertsRestBinding: UnknownRestBinding;
26
+ export {};
@@ -0,0 +1,54 @@
1
+ import { z } from "zod";
2
+ import { shapeOperationFaultAsJson } from "../operation-fault.js";
3
+ import { defineOperation } from "../operation-registry.js";
4
+ const alertMetricSchema = z.enum([
5
+ "workflow.failure_rate",
6
+ "activity.p99_duration",
7
+ "storage.size"
8
+ ]), listAlertsInput = z.object({}), activeAlertSchema = z.object({
9
+ metric: alertMetricSchema,
10
+ threshold: z.number(),
11
+ currentValue: z.number(),
12
+ window: z.string().nullable(),
13
+ firedAt: z.number().nullable()
14
+ }), listAlertsOutput = z.object({
15
+ items: z.array(activeAlertSchema)
16
+ });
17
+ export const listAlertsOperation = defineOperation({
18
+ name: "weft.alerts.list",
19
+ mcpExposable: !1,
20
+ summary: "List currently firing alerts",
21
+ description: "List the alert rules that are currently firing, including their metric, threshold, current value, configured window, and firing timestamp. Read-only and limited to active in-memory alert state; resolved alerts are not returned.",
22
+ destructive: !1,
23
+ tags: ["Observability"],
24
+ inputSchema: listAlertsInput,
25
+ outputSchema: listAlertsOutput,
26
+ access: { kind: "scoped", scopes: { kind: "anyOf", scopes: ["system:read"] } },
27
+ discoverable: !0,
28
+ transports: { http: !0, jsonRpcHttp: !0, jsonRpcWebSocket: !0, jsonRpcStdio: !0 },
29
+ unknownKeyPolicy: { http: "strip", jsonRpc: "reject" },
30
+ invoke: async ({ engine }) => {
31
+ return {
32
+ items: engine.getActiveAlerts().map((state) => ({
33
+ metric: state.rule.metric,
34
+ threshold: state.rule.threshold,
35
+ currentValue: state.currentValue,
36
+ window: state.rule.window ?? null,
37
+ firedAt: state.lastFiredAt ?? null
38
+ }))
39
+ };
40
+ }
41
+ }), listAlertsRestBinding = {
42
+ method: "GET",
43
+ path: "/v1/alerts",
44
+ pathParamNames: [],
45
+ operationName: "weft.alerts.list",
46
+ inputSources: {},
47
+ extractInput: async () => ({}),
48
+ success: { kind: "json", status: 200 },
49
+ shapeSuccess: (output) => new Response(JSON.stringify(output), {
50
+ status: 200,
51
+ headers: { "Content-Type": "application/json" }
52
+ }),
53
+ shapeFault: shapeOperationFaultAsJson
54
+ };
@@ -1,6 +1,14 @@
1
- import type { ScheduleSpec } from '../../core/types.ts';
1
+ import type { ScheduleSpec, ScheduleUpdateOptions } from '../../core/types.ts';
2
2
  import type { OperationFault } from '../operation-fault.ts';
3
3
  export { isOperationFault } from './operation-helpers.ts';
4
+ export type ScheduleMutableOptionsInput = {
5
+ description?: unknown;
6
+ overlap?: unknown;
7
+ backfill?: unknown;
8
+ jitter?: unknown;
9
+ };
10
+ /** Validate the mutable schedule options shared by create and update. */
11
+ export declare function validateScheduleMutableOptions(input: ScheduleMutableOptionsInput): ScheduleUpdateOptions;
4
12
  export declare function mapScheduleErrorToFault(scheduleId: string, error: unknown): OperationFault;
5
13
  /**
6
14
  * Validate the mutually exclusive schedule cadence fields (`cronExpression` or
@@ -1,5 +1,56 @@
1
+ import {
2
+ isValidScheduleOverlapPolicy,
3
+ normalizeScheduleUpdateOptions
4
+ } from "../../core/engine/validation/schedule.js";
1
5
  import { invalidParamsFault } from "./operation-helpers.js";
2
6
  export { isOperationFault } from "./operation-helpers.js";
7
+ export function validateScheduleMutableOptions(input) {
8
+ const description = validateScheduleDescription(input.description), overlap = validateScheduleOverlap(input.overlap), backfill = validateScheduleBackfill(input.backfill), jitter = validateScheduleJitter(input.jitter);
9
+ return {
10
+ ...description !== void 0 ? { description } : {},
11
+ ...overlap !== void 0 ? { overlap } : {},
12
+ ...backfill !== void 0 ? { backfill } : {},
13
+ ...jitter !== void 0 ? { jitter } : {}
14
+ };
15
+ }
16
+ function validateScheduleDescription(value) {
17
+ if (value === void 0)
18
+ return;
19
+ if (typeof value !== "string")
20
+ throw invalidParamsFault('Field "description" must be a string');
21
+ return value;
22
+ }
23
+ function validateScheduleOverlap(value) {
24
+ if (value === void 0)
25
+ return;
26
+ if (!isValidScheduleOverlapPolicy(value))
27
+ throw invalidParamsFault('Field "overlap" must be one of skip, queue, cancel-running, allow');
28
+ return value;
29
+ }
30
+ function validateScheduleBackfill(value) {
31
+ if (value === void 0)
32
+ return;
33
+ if (typeof value !== "boolean")
34
+ throw invalidParamsFault('Field "backfill" must be a boolean');
35
+ return value;
36
+ }
37
+ function validateScheduleJitter(value) {
38
+ if (value === void 0)
39
+ return;
40
+ if (typeof value !== "string" && typeof value !== "number")
41
+ throw invalidParamsFault('Field "jitter" must be a duration string or a number of milliseconds');
42
+ try {
43
+ normalizeScheduleUpdateOptions({ jitter: value });
44
+ } catch (error) {
45
+ const message = error instanceof Error ? error.message : String(error);
46
+ throw invalidParamsFault(formatJitterValidationMessage(message));
47
+ }
48
+ return value;
49
+ }
50
+ function formatJitterValidationMessage(message) {
51
+ const enginePrefix = "Invalid options.jitter: ", hasEnginePrefix = message.startsWith(enginePrefix), wireDetail = (hasEnginePrefix ? message.slice(enginePrefix.length) : message).replaceAll("options.jitter", 'Field "jitter"');
52
+ return hasEnginePrefix ? `Field "jitter" is invalid: ${wireDetail}` : wireDetail;
53
+ }
3
54
  export function mapScheduleErrorToFault(scheduleId, error) {
4
55
  const message = error instanceof Error ? error.message : String(error), normalizedMessage = message.toLowerCase();
5
56
  if (normalizedMessage.includes("not found"))
@@ -1,8 +1,11 @@
1
1
  import { z } from "zod";
2
- import { normalizeScheduleUpdateOptions } from "../../core/engine/validation/schedule.js";
3
2
  import { defineOperation } from "../operation-registry.js";
4
- import { invalidParamsFault, shapeRestFault } from "./operation-helpers.js";
5
- import { mapScheduleErrorToFault, validateScheduleInputCadence } from "./schedule-faults.js";
3
+ import { shapeRestFault } from "./operation-helpers.js";
4
+ import {
5
+ mapScheduleErrorToFault,
6
+ validateScheduleInputCadence,
7
+ validateScheduleMutableOptions
8
+ } from "./schedule-faults.js";
6
9
  import {
7
10
  extractSharedScheduleRestFields,
8
11
  parseScheduleRestBodyRequestRecord
@@ -16,56 +19,6 @@ const updateScheduleInput = z.object({
16
19
  backfill: z.unknown().optional(),
17
20
  jitter: z.unknown().optional()
18
21
  });
19
- function isScheduleOverlapPolicy(value) {
20
- return value === "skip" || value === "queue" || value === "cancel-running" || value === "allow";
21
- }
22
- function validateDescription(value) {
23
- if (value === void 0)
24
- return;
25
- if (typeof value !== "string")
26
- throw invalidParamsFault('Field "description" must be a string');
27
- return value;
28
- }
29
- function validateOverlap(value) {
30
- if (value === void 0)
31
- return;
32
- if (typeof value !== "string" || !isScheduleOverlapPolicy(value))
33
- throw invalidParamsFault('Field "overlap" must be one of skip, queue, cancel-running, allow');
34
- return value;
35
- }
36
- function validateBackfill(value) {
37
- if (value === void 0)
38
- return;
39
- if (typeof value !== "boolean")
40
- throw invalidParamsFault('Field "backfill" must be a boolean');
41
- return value;
42
- }
43
- function formatJitterValidationMessage(message) {
44
- const enginePrefix = "Invalid options.jitter: ", hasEnginePrefix = message.startsWith(enginePrefix), wireDetail = (hasEnginePrefix ? message.slice(enginePrefix.length) : message).replaceAll("options.jitter", 'Field "jitter"');
45
- return hasEnginePrefix ? `Field "jitter" is invalid: ${wireDetail}` : wireDetail;
46
- }
47
- function validateJitter(value) {
48
- if (value === void 0)
49
- return;
50
- if (typeof value !== "string" && typeof value !== "number")
51
- throw invalidParamsFault('Field "jitter" must be a duration string or a number of milliseconds');
52
- try {
53
- normalizeScheduleUpdateOptions({ jitter: value });
54
- } catch (error) {
55
- const message = error instanceof Error ? error.message : String(error);
56
- throw invalidParamsFault(formatJitterValidationMessage(message));
57
- }
58
- return value;
59
- }
60
- function validateScheduleUpdateOptions(input) {
61
- const description = validateDescription(input.description), overlap = validateOverlap(input.overlap), backfill = validateBackfill(input.backfill), jitter = validateJitter(input.jitter);
62
- return {
63
- ...description !== void 0 ? { description } : {},
64
- ...overlap !== void 0 ? { overlap } : {},
65
- ...backfill !== void 0 ? { backfill } : {},
66
- ...jitter !== void 0 ? { jitter } : {}
67
- };
68
- }
69
22
  export const updateScheduleOperation = defineOperation({
70
23
  name: "weft.schedules.update",
71
24
  mcpExposable: !1,
@@ -80,7 +33,7 @@ export const updateScheduleOperation = defineOperation({
80
33
  transports: { http: !0, jsonRpcHttp: !0, jsonRpcWebSocket: !0, jsonRpcStdio: !0 },
81
34
  unknownKeyPolicy: { http: "strip", jsonRpc: "reject" },
82
35
  invoke: async ({ input, engine }) => {
83
- const typedEngine = engine, spec = validateScheduleInputCadence(input), options = validateScheduleUpdateOptions(input);
36
+ const typedEngine = engine, spec = validateScheduleInputCadence(input), options = validateScheduleMutableOptions(input);
84
37
  try {
85
38
  await typedEngine.updateSchedule(input.scheduleId, spec, options);
86
39
  return null;
@@ -98,6 +98,7 @@ import {
98
98
  getWorkflowTimelineRestBinding
99
99
  } from "./operations/get-workflow-timeline.js";
100
100
  import { getWorkflowOperation, getWorkflowRestBinding } from "./operations/get-workflow.js";
101
+ import { listAlertsOperation, listAlertsRestBinding } from "./operations/list-alerts.js";
101
102
  import {
102
103
  listCheckpointsOperation,
103
104
  listCheckpointsRestBinding
@@ -214,6 +215,7 @@ import {
214
215
  } from "./operations/workflow-events-sse.js";
215
216
  import { workflowEventsSubscriptionOperation } from "./operations/workflow-events-subscription.js";
216
217
  export const REST_BINDINGS = [
218
+ listAlertsRestBinding,
217
219
  startWorkflowRestBinding,
218
220
  startOrSignalWorkflowRestBinding,
219
221
  recoverAllRestBinding,
@@ -401,6 +403,7 @@ export function createLiveOperationRegistry(options) {
401
403
  buildTaskDiagnosticsOperationForRegistry(resolved),
402
404
  clearTaskDeadLetterOperation,
403
405
  listSchedulesOperation,
406
+ listAlertsOperation,
404
407
  getScheduleOperation,
405
408
  replayWorkflowOperation,
406
409
  storageCapabilities.storageCapabilitiesOperation,
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import type { RegistryAgnosticEngine } from '../core/engine';
10
10
  import type { ServiceWorkerScheduler } from './scheduler.ts';
11
+ import type { ServiceWorkerHandlerOptions } from './setup.ts';
11
12
  import type { MinimalExtendableEvent, MinimalFetchEvent, MinimalPeriodicSyncEvent } from './shared.ts';
12
13
  export { ServiceWorkerScheduler } from './scheduler.ts';
13
14
  export type { ServiceWorkerSchedulerOptions } from './scheduler.ts';
@@ -44,6 +45,8 @@ export interface ServiceWorkerOptions {
44
45
  */
45
46
  engine: RegistryAgnosticEngine;
46
47
  pathPrefix?: string;
48
+ /** Handler options supported by the Service Worker runtime. */
49
+ handlerOptions?: ServiceWorkerHandlerOptions;
47
50
  }
48
51
  /**
49
52
  * Create a fetch event handler that intercepts requests matching the given
@@ -127,4 +130,4 @@ export declare function createLifecycleHandlers(): {
127
130
  install: (event: MinimalExtendableEvent) => void;
128
131
  activate: (event: MinimalExtendableEvent) => void;
129
132
  };
130
- export { setupServiceWorker, type SetupServiceWorkerOptions, type SetupServiceWorkerResult, } from './setup.ts';
133
+ export { setupServiceWorker, type ServiceWorkerHandlerOptions, type SetupServiceWorkerOptions, type SetupServiceWorkerResult, } from './setup.ts';