@lostgradient/weft 0.11.0 → 0.12.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 (83) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/generated/operation-catalog.snapshot.json +4671 -0
  3. package/dist/cli/generated/operation-client.generated.d.ts +1 -0
  4. package/dist/cli/index.js +1 -0
  5. package/dist/cli-main.js +227 -401
  6. package/dist/client/http-operations.js +7 -1
  7. package/dist/client/http-request.d.ts +23 -1
  8. package/dist/client/http-request.js +12 -3
  9. package/dist/client/index.d.ts +2 -0
  10. package/dist/client/index.js +7 -0
  11. package/dist/connection.d.ts +8 -0
  12. package/dist/connection.js +41 -17
  13. package/dist/core/engine/callback-creators-core.js +2 -1
  14. package/dist/core/engine/index.d.ts +43 -0
  15. package/dist/core/engine/lifecycle/shared.d.ts +25 -0
  16. package/dist/core/engine/lifecycle/transition.js +19 -9
  17. package/dist/core/types/definition-schema-to-json.d.ts +11 -0
  18. package/dist/core/types/definition-schema-to-json.js +11 -2
  19. package/dist/http.js +2 -0
  20. package/dist/indexeddb.js +1 -0
  21. package/dist/json-schema.js +3 -3
  22. package/dist/mcp/cli.js +63 -61
  23. package/dist/mcp/http.d.ts +7 -2
  24. package/dist/mcp/session.d.ts +3 -3
  25. package/dist/mcp/session.js +3 -2
  26. package/dist/mcp/stdio.d.ts +7 -2
  27. package/dist/observability/index.js +2 -2
  28. package/dist/runtime/portable.d.ts +33 -0
  29. package/dist/runtime/portable.js +17 -6
  30. package/dist/server/engine-event-feed-backend.d.ts +22 -3
  31. package/dist/server/fleet-event-feed.d.ts +65 -0
  32. package/dist/server/handler/index.d.ts +11 -2
  33. package/dist/server/handler/index.js +7 -0
  34. package/dist/server/handler.js +1 -56
  35. package/dist/server/index.d.ts +12 -1
  36. package/dist/server/index.js +87 -64
  37. package/dist/server/operations/submit-review-decision.d.ts +2 -0
  38. package/dist/server/operations/submit-review-decision.js +19 -2
  39. package/dist/server/replay-live-feed-internals.d.ts +19 -0
  40. package/dist/server/replay-live-feed-internals.js +76 -0
  41. package/dist/server/runtime/authentication-bridge.d.ts +2 -2
  42. package/dist/server/serve-internals.js +3 -3
  43. package/dist/server/workflow-event-feed.d.ts +100 -11
  44. package/dist/server/workflow-event-feed.js +1 -140
  45. package/dist/service-worker/index.d.ts +8 -2
  46. package/dist/service-worker/index.js +37 -56
  47. package/dist/service-worker/setup.d.ts +6 -2
  48. package/dist/storage/auto.js +1 -1
  49. package/dist/storage/bun-sql.js +3 -3
  50. package/dist/storage/cloudflare-durable-object-sql.d.ts +75 -0
  51. package/dist/storage/cloudflare-durable-object-sql.js +0 -0
  52. package/dist/storage/cloudflare-value-codec.d.ts +65 -0
  53. package/dist/storage/cloudflare-value-codec.js +46 -0
  54. package/dist/storage/cloudflare.d.ts +140 -0
  55. package/dist/storage/cloudflare.js +127 -0
  56. package/dist/storage/compressed-storage.js +1 -1
  57. package/dist/storage/http.js +200 -2
  58. package/dist/storage/index.d.ts +1 -0
  59. package/dist/storage/indexeddb.js +311 -1
  60. package/dist/storage/interface.js +1 -1
  61. package/dist/storage/lmdb.js +1 -1
  62. package/dist/storage/memory.js +1 -1
  63. package/dist/storage/neon.js +4 -4
  64. package/dist/storage/node-sqlite.js +3 -3
  65. package/dist/storage/postgres-key-value-queries.d.ts +6 -0
  66. package/dist/storage/postgres-key-value-queries.js +2 -3
  67. package/dist/storage/postgres.js +4 -4
  68. package/dist/storage/resolve.js +1 -1
  69. package/dist/storage/scoped-storage.js +1 -1
  70. package/dist/storage/sql-identifier.d.ts +35 -0
  71. package/dist/storage/sql-identifier.js +5 -0
  72. package/dist/storage/sqlite-key-value-queries.d.ts +11 -6
  73. package/dist/storage/sqlite-key-value-queries.js +3 -3
  74. package/dist/storage/testing.js +1 -1
  75. package/dist/storage/turso.js +2 -2
  76. package/dist/storage/typed-storage.js +138 -2
  77. package/dist/storage/web-extension.js +298 -1
  78. package/dist/testing/index.js +21 -60
  79. package/dist/version.d.ts +1 -1
  80. package/dist/version.js +1 -1
  81. package/dist/web-extension.js +1 -0
  82. package/dist/worker/protocol.js +1 -1
  83. package/package.json +8 -3
@@ -1 +1,298 @@
1
- var r=Object.defineProperty;var i=(F)=>F;function t(F,J){this[F]=i.bind(null,J)}var qF=(F,J)=>{for(var X in J)r(F,X,{get:J[X],enumerable:!0,configurable:!0,set:t.bind(J,X)})};function b(F){let J=[];for(let X=0;X<F.length;X+=512)J.push(String.fromCharCode(...F.subarray(X,X+512)));return btoa(J.join(""))}function x(F){let J=atob(F),X=new Uint8Array(J.length);for(let $=0;$<J.length;$+=1)X[$]=J.charCodeAt($);return X}function Y(F){return typeof F==="object"&&F!==null&&!Array.isArray(F)}async function j(F,J){return await F.get(J)!==null}async function*G(F,J,X){for await(let[$]of F.scan(J,X))yield $}async function C(F,J){let X=0;for await(let $ of G(F,J))X++;return X}async function T(F,J){let X=[];for await(let $ of G(F,J))X.push({type:"delete",key:$});if(X.length===0)return 0;return await F.batch(X),X.length}async function W(F,J,X){let $=[];for await(let H of G(F,J,X))$.push({type:"delete",key:H});if($.length===0)return 0;return await F.batch($),$.length}function B(F,J,X){if(!F.capabilities()[J])throw Error(`Feature "${X}" requires storage capability "${J}", but this storage backend does not provide it.`)}var v="default";var A=1e4,RF=1e4;class I extends Error{code="StorageBatchOperationLimitExceededError";cap=A;count;target;constructor(F,J){super(`${F} count ${J} exceeds MAX_BATCH_OPERATIONS (${A}).`);this.name="StorageBatchOperationLimitExceededError",this.target=F,this.count=J}}function _(F,J){if(J>A)throw new I(F,J)}function g(F){return F.length>0?F.slice(0,-1)+String.fromCharCode(F.charCodeAt(F.length-1)+1):"ÿ"}function c(F,J={}){if(J.gt!==void 0&&F<=J.gt)return!1;if(J.gte!==void 0&&F<J.gte)return!1;if(J.lt!==void 0&&F>=J.lt)return!1;if(J.lte!==void 0&&F>J.lte)return!1;return!0}function OF(F,J){if(F===null||J===null)return F===J;if(F.byteLength!==J.byteLength)return!1;for(let X=0;X<F.byteLength;X++)if(F[X]!==J[X])return!1;return!0}async function u(F,J){if(F.has)return F.has(J);return j(F,J)}function y(F,J,X){if(F.keys)return F.keys(J,X);return G(F,J,X)}async function w(F,J){if(F.count)return F.count(J);return C(F,J)}async function f(F,J){if(F.deletePrefix)return F.deletePrefix(J);return T(F,J)}async function PF(F,J){_("batch operations",J.length),await F.batch(J)}async function k(F,J,X){if(_("conditionalBatch conditions",J.length),_("conditionalBatch operations",X.length),B(F,"conditionalBatch","storageConditionalBatch"),!F.conditionalBatch)throw Error("This storage backend reports conditionalBatch capability but does not implement the conditionalBatch() method.");return F.conditionalBatch(J,X)}function Z(F){return encodeURIComponent(F)}function SF(F){return decodeURIComponent(F)}function hF(F){try{return decodeURIComponent(F)}catch{return null}}var M=(F)=>String(F).padStart(16,"0"),o="0",e="1",m=(F,J,X,$)=>`sig:${Z(F)}:${Z(J)}:${$}:${Z(X)}`,KF={workflow:(F)=>`wf:${Z(F)}`,checkpoint:(F)=>`wf:${Z(F)}:ckpt`,checkpointHistory:(F,J)=>`wf:${Z(F)}:ckpt:${String(J).padStart(10,"0")}`,timelinePrefix:(F)=>`wf:${Z(F)}:timeline:`,timeline:(F,J)=>`wf:${Z(F)}:timeline:${String(J).padStart(10,"0")}`,schedule:(F)=>`schedule:${Z(F)}`,scheduleTick:(F,J)=>`schedule-due:${String(F).padStart(16,"0")}:${Z(J)}`,scheduleRun:(F)=>`schedule-run:${Z(F)}`,operation:(F,J,X)=>`op:${F}:${M(J)}:${X}`,operationInflight:(F)=>`op:inflight:${F}`,operationQueued:(F)=>`op:queued:${F}`,operationResolved:(F)=>`op:resolved:${F}`,operationDeadLetterPrefix:()=>"op:dead-letter:",operationDeadLetter:(F)=>`op:dead-letter:${F}`,bulkOperationAuditPrefix:()=>"audit:bulk:",bulkOperationAudit:(F,J,X)=>`audit:bulk:${M(F)}:${Z(J)}:${Z(X)}`,operationResolvedByTimePrefix:()=>"op:resolved-by-time:",operationResolvedByTime:(F,J)=>`op:resolved-by-time:${M(F)}:${Z(J)}`,asyncActivity:(F,J)=>`async-act:v1:${Z(F)}:${Z(J)}`,asyncActivityResolution:(F,J)=>`async-act:v1:${Z(F)}:${Z(J)}:resolution`,activityReconciliationPrefix:(F)=>`actrec:v1:${Z(F)}:`,activityReconciliation:(F,J,X)=>`actrec:v1:${Z(F)}:${Z(J)}:${X}`,eventPrefix:(F)=>`ev:${Z(F)}:`,event:(F,J)=>`ev:${Z(F)}:${String(J).padStart(10,"0")}`,eventHead:(F)=>`ev:${Z(F)}:head`,eventWatermark:(F)=>`ev:${Z(F)}:watermark`,fleetEventPrefix:()=>"fleet-event:",fleetEvent:(F)=>`fleet-event:${String(F).padStart(10,"0")}`,fleetEventTail:()=>"fleet-event-tail",fleetEventByWorkflowPrefix:(F)=>`fleet-event-by-workflow:${Z(F)}:`,fleetEventByWorkflow:(F,J)=>`fleet-event-by-workflow:${Z(F)}:${String(J).padStart(10,"0")}`,signal:(F,J,X)=>m(F,J,X,e),startSignal:(F,J,X)=>m(F,J,X,o),signalSequence:(F)=>`sigseq:v1:${Z(F)}`,signalAcceptedResponsePrefix:(F)=>`sigres:v1:${Z(F)}:`,signalAcceptedResponse:(F,J,X)=>`sigres:v1:${Z(F)}:${Z(J)}:${Z(X)}`,deadline:(F,J)=>`wf-deadline:${M(F)}:${Z(J)}`,terminalCleanup:(F,J)=>`wf-cleanup:${M(F)}:${Z(J)}`,teardownTimer:(F,J)=>`wf-teardown:${M(F)}:${Z(J)}`,delayedStart:(F,J)=>`wf-delayed:${M(F)}:${Z(J)}`,terminalWorkflowPrefix:()=>"wf-terminal:",terminalWorkflow:(F,J)=>`wf-terminal:${M(F)}:${Z(J)}`,attribute:(F)=>`attr:${Z(F)}`,attributeIndex:(F,J,X)=>`idx:${F}:${J}:${Z(X)}`,tagIndex:(F,J)=>`tag:${Z(F)}:${Z(J)}`,updatePrefix:(F)=>`upd:${Z(F)}:`,update:(F,J)=>`upd:${Z(F)}:${J}`,updateResponse:(F)=>`upr:${F}`,updateIdempotency:(F,J)=>`upk:${Z(F)}:${J}`,startIdempotency:(F)=>`start-idem:${Z(F)}`,startIdempotencySignalId:(F)=>`start-idem:${F}`,livenessPrefix:()=>"liveness:",liveness:(F)=>`liveness:${Z(F)}`,leasePrefix:()=>"lease:",leaseEpoch:()=>"lease:epoch",leaseHolder:()=>"lease:holder",budget:(F,J,X)=>`budget:${F}:${J}:${X}`,review:(F,J)=>`review:${Z(F)}:${J}`,workflowHeaders:(F)=>`wf-headers:${Z(F)}`,childCancellationPrefix:(F)=>`child-cancel:${Z(F)}:`,childCancellation:(F,J)=>`child-cancel:${Z(F)}:${Z(J)}`,terminalCleanupNeeded:(F)=>`wf-cleanup-needed:${Z(F)}`,workflowConcurrency:(F,J)=>`wf-concurrency:${Z(F)}:${Z(J)}`,workflowConcurrencyHolder:(F)=>`wf-concurrency-holder:${Z(F)}`,workflowHasServices:(F)=>`wf-has-services:${Z(F)}`,finalizerState:(F)=>`wf-finalizer-state:${Z(F)}`,teardownOwed:(F)=>`wf-teardown-needed:${Z(F)}`,teardownDeadLetter:(F)=>`wf-teardown-deadletter:${Z(F)}`,offload:(F,J)=>`offload:${Z(F)}:${J}`,archive:(F,J)=>`archive:${Z(F)}:${J}`,stateExecution:(F,J)=>`state:execution:${Z(F)}:${Z(J)}`,stateWorkflow:(F,J)=>`state:workflow-scope:${v}:${Z(F)}:${Z(J)}`,streamChunkPrefix:(F,J)=>`blob:${Z(F)}:${J}:chunk:`,streamChunk:(F,J,X)=>`blob:${Z(F)}:${J}:chunk:${String(X).padStart(10,"0")}`,streamTail:(F,J)=>`blob:${Z(F)}:${J}:tail`,streamMetadata:(F,J)=>`blob:${Z(F)}:${J}:meta`,budgetCharged:(F)=>`budget-charged:${F}`,toolEffect:(F,J,X)=>`tool-effect:${Z(F)}:${J}:${X}`,workflowVisibilityStatus:(F,J)=>`wf-idx-status:${Z(F)}:${Z(J)}`,workflowVisibilityType:(F,J)=>`wf-idx-type:${Z(F)}:${Z(J)}`,workflowVisibilityCreated:(F,J)=>`wf-idx-created:${M(F)}:${Z(J)}`,workflowVisibilityUpdated:(F,J)=>`wf-idx-updated:${M(F)}:${Z(J)}`,workflowVisibilityDeadline:(F,J)=>`wf-idx-deadline:${M(F)}:${Z(J)}`,workflowVisibilityManifest:(F)=>`wf-idx-manifest:${Z(F)}`,workflowVisibilityMetaVersion:()=>"wf-idx-meta:version",workflowVisibilityMetaBuiltAt:()=>"wf-idx-meta:built-at",workflowVisibilityMetaCursor:()=>"wf-idx-meta:cursor"};function FF(F){if(F===void 0)return;if(typeof F!=="number"||!Number.isInteger(F)||F<0)throw Error("deleteRange limit must be a finite non-negative integer");return F===0?0:F}function N(F){let J={},X=!1;for(let H of["gt","gte","lt","lte"]){let U=F[H];if(U===void 0)continue;if(typeof U!=="string")throw Error("deleteRange bounds must be strings");J[H]=U,X=!0}if(!X)throw Error("deleteRange requires at least one of gt/gte/lt/lte; use deletePrefix to delete a whole prefix");let $=FF(F.limit);if($!==void 0)J.limit=$;return J}async function d(F,J,X){let $=N(X);if(F.deleteRange)return F.deleteRange(J,$);return W(F,J,$)}function JF(F,J){let X={key:F,open:!1};if(J.gte!==void 0&&J.gte>X.key)X.key=J.gte,X.open=!1;if(J.gt!==void 0&&J.gt>=X.key)X.key=J.gt,X.open=!0;return X}function XF(F,J){let X={key:g(F),open:!0};if(J.lt!==void 0&&J.lt<=X.key)X.key=J.lt,X.open=!0;if(J.lte!==void 0&&J.lte<X.key)X.key=J.lte,X.open=!1;return X}function IF(F,J){let X=JF(F,J),$=XF(F,J);if(X.key>$.key||X.key===$.key&&(X.open||$.open))return null;return{lower:X,upper:$}}function E(F){return F.replaceAll(/:+$/g,"")}function ZF(F,J){let X=E(F),$=E(J);if(X.length===0)return $;if($.length===0)return X;return`${X}:${$}`}class R{#J;#X;constructor(F,J){this.#J=F,this.#X=E(J)}#F(F){if(this.#X.length===0)return F;return F.length===0?`${this.#X}:`:`${this.#X}:${F}`}#Z(F){if(this.#X.length===0)return F;return F.slice(this.#X.length+1)}#$(F={}){let J={};if(F.limit!==void 0)J.limit=F.limit;if(F.reverse!==void 0)J.reverse=F.reverse;if(F.gt!==void 0)J.gt=this.#F(F.gt);if(F.gte!==void 0)J.gte=this.#F(F.gte);if(F.lt!==void 0)J.lt=this.#F(F.lt);if(F.lte!==void 0)J.lte=this.#F(F.lte);return J}#U(F){let J={};if(F.limit!==void 0)J.limit=F.limit;if(F.gt!==void 0)J.gt=this.#F(F.gt);if(F.gte!==void 0)J.gte=this.#F(F.gte);if(F.lt!==void 0)J.lt=this.#F(F.lt);if(F.lte!==void 0)J.lte=this.#F(F.lte);return J}capabilities(){return this.#J.capabilities()}scoped(F){return new R(this.#J,ZF(this.#X,F))}async get(F){return this.#J.get(this.#F(F))}async put(F,J){await this.#J.put(this.#F(F),J)}async delete(F){await this.#J.delete(this.#F(F))}async*scan(F,J){for await(let[X,$]of this.#J.scan(this.#F(F),this.#$(J)))yield[this.#Z(X),$]}async batch(F){_("batch operations",F.length),await this.#J.batch(F.map((J)=>{if(J.type==="put")return{type:"put",key:this.#F(J.key),value:J.value};return{type:"delete",key:this.#F(J.key)}}))}async conditionalBatch(F,J){return k(this.#J,F.map((X)=>({key:this.#F(X.key),expectedValue:X.expectedValue})),J.map((X)=>{if(X.type==="put")return{type:"put",key:this.#F(X.key),value:X.value};return{type:"delete",key:this.#F(X.key)}}))}async has(F){return u(this.#J,this.#F(F))}async deletePrefix(F){return f(this.#J,this.#F(F))}async deleteRange(F,J){let X=this.#U(N(J));return d(this.#J,this.#F(F),X)}async*keys(F,J){for await(let X of y(this.#J,this.#F(F),this.#$(J)))yield this.#Z(X)}async count(F){return w(this.#J,this.#F(F))}[Symbol.dispose](){this.#J[Symbol.dispose]()}}function l(F,J){return new R(F,J)}var s=1,q="__weftStorageKeyspace",$F=102400,UF=8192;function HF(F){return typeof F==="object"&&F!==null&&"then"in F&&typeof F.then==="function"}function S(F){if(!Y(F))return!1;if(F.__weftStorage!==s)return!1;return typeof F.value==="string"}function MF(F){if(!Y(F))return!1;return Object.values(F).every(S)}function z(F){if(F!==q)return;throw Error(`WebExtensionStorage key "${q}" is reserved for adapter metadata.`)}function VF(F){for(let J of F)z(J.key)}function p(F){return{__weftStorage:s,value:b(F)}}function n(F){if(!S(F))return null;return x(F.value)}function GF(F){return S(F)}function O(F){return new TextEncoder().encode(JSON.stringify(F)).byteLength}function _F(F){if(!Y(F))return!1;if(F.runtime!==void 0&&!Y(F.runtime))return!1;return F.storage===void 0||Y(F.storage)}function YF(F=void 0){if(F!==void 0){if(!_F(F))throw Error("WebExtensionStorage injected namespace must be an object.");return F}let J=globalThis,X=J.browser??J.chrome;if(X?.storage===void 0)throw Error("WebExtensionStorage requires globalThis.browser.storage or globalThis.chrome.storage.");return X}function LF(F,J){let X=F.storage?.[J];if(X===void 0)throw Error(`WebExtensionStorage area "${J}" is not available.`);return X}function NF(F){if(F==="session")return"ephemeral";if(F==="sync"||F==="managed")return"remote";return"local"}function QF(F){let J=F.runtime?.lastError?.message;return J===void 0?null:Error(J)}async function P(F,J,X){return new Promise(($,H)=>{let U=!1,L=(V)=>{if(U)return;let K=QF(F);if(U=!0,K!==null)H(K);else $(V)},h=(V)=>{if(U)return;U=!0,H(V)},a=(V)=>L(V),D;try{D=J(a)}catch{try{D=X()}catch(V){h(V);return}}if(HF(D))D.then(L,h)})}class Q{static#J=new WeakMap;#X;#F;#Z;#$;#U;constructor(F={},J=void 0){this.#Z=F.area??"local",this.#$=NF(this.#Z),this.#X=YF(J),this.#F=LF(this.#X,this.#Z),this.#U=()=>{},this.#X.storage?.onChanged?.addListener?.(this.#U)}capabilities(){return{persistence:this.#$,readAfterWrite:"session",scanConsistency:"best-effort",atomicBatch:!1,conditionalBatch:!1,boundedRangeDelete:!1}}async#_(F){return P(this.#X,(J)=>this.#F.get(F,J),()=>this.#F.get(F))}async#L(F){await P(this.#X,(J)=>this.#F.set(F,J),()=>this.#F.set(F))}async#Y(F){if(this.#F.getBytesInUse===void 0)return null;return P(this.#X,(J)=>this.#F.getBytesInUse?.(F,J),()=>this.#F.getBytesInUse?.(F))}async#H(){let J=(await this.#_(q))[q];if(J===void 0)return{};if(!MF(J))throw Error("WebExtensionStorage keyspace is not a valid storage envelope map.");return{...J}}async#V(F){let J={[q]:F};await this.#q(J),await this.#L(J)}async#G(F){let J=Q.#J.get(this.#F)??Promise.resolve(),X={},$=new Promise((U)=>{X.release=U}),H=J.catch(()=>{return}).then(()=>$);Q.#J.set(this.#F,H),await J.catch(()=>{return});try{return await F()}finally{if(X.release?.(),Q.#J.get(this.#F)===H)Q.#J.delete(this.#F)}}#M(){if(this.#Z==="managed")throw Error('WebExtensionStorage area "managed" is read-only.')}#N(F){let J=this.#F.QUOTA_BYTES_PER_ITEM??UF;for(let[X,$]of Object.entries(F)){let H=O({[X]:$});if(H>J)throw Error(`WebExtensionStorage sync item quota exceeded for "${X}": ${String(H)} > ${String(J)}.`)}}async#Q(F){let J=Object.keys(F),X=await this.#Y(null),$=await this.#Y(J);if(X!==null&&$!==null)return X-$+O(F);let H=await this.#_(null);return O({...H,...F})}async#q(F){if(this.#Z!=="sync")return;this.#N(F);let J=this.#F.QUOTA_BYTES??$F,X=await this.#Q(F);if(X>J)throw Error(`WebExtensionStorage sync total quota exceeded: ${String(X)} > ${String(J)}.`)}async get(F){z(F);let J=await this.#H();return n(J[F])}async put(F,J){this.#M(),z(F),await this.#G(async()=>{let X=await this.#H();X[F]=p(J),await this.#V(X)})}async delete(F){this.#M(),z(F),await this.#G(async()=>{let J=await this.#H();if(!(F in J))return;delete J[F],await this.#V(J)})}async*scan(F,J={}){let X=await this.#H(),$=Object.keys(X).filter((U)=>U.startsWith(F)).filter((U)=>c(U,J)).filter((U)=>GF(X[U])).toSorted();if(J.reverse)$=$.toReversed();let H=0;for(let U of $){if(J.limit!==void 0&&H>=J.limit)break;let L=n(X[U]);if(L===null)continue;yield[U,L],H+=1}}async batch(F){if(_("batch operations",F.length),this.#M(),F.length===0)return;VF(F),await this.#G(async()=>{let J=await this.#H();for(let X of F)if(X.type==="put")J[X.key]=p(X.value);else delete J[X.key];await this.#V(J)})}has(F){return j(this,F)}async*keys(F,J){yield*G(this,F,J)}count(F){return C(this,F)}async deletePrefix(F){return this.#M(),T(this,F)}async deleteRange(F,J){let X=N(J);return this.#M(),W(this,F,X)}scoped(F){return l(this,F)}[Symbol.dispose](){this.#X.storage?.onChanged?.removeListener?.(this.#U)}}export{Q as WebExtensionStorage};
1
+ import { decodeBase64ToBytes, encodeBytesToBase64, isRecord } from "./byte-encoding.js";
2
+ import { normalizeDeleteRangeOptions } from "./delete-range.js";
3
+ import {
4
+ storageCountCore,
5
+ storageDeletePrefixCore,
6
+ storageDeleteRangeCore,
7
+ storageHasCore,
8
+ storageKeysCore
9
+ } from "./derived-operations.js";
10
+ import {
11
+ assertStorageBatchOperationCount,
12
+ matchesScanOptions
13
+ } from "./interface.js";
14
+ import { scopedStorage } from "./scoped-storage.js";
15
+ const ENVELOPE_MARKER = 1, KEYSPACE_STORAGE_KEY = "__weftStorageKeyspace", DEFAULT_SYNC_QUOTA_BYTES = 102400, DEFAULT_SYNC_QUOTA_BYTES_PER_ITEM = 8192;
16
+ function isPromiseLike(value) {
17
+ return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
18
+ }
19
+ function isStorageEnvelope(value) {
20
+ if (!isRecord(value))
21
+ return !1;
22
+ if (value.__weftStorage !== ENVELOPE_MARKER)
23
+ return !1;
24
+ return typeof value.value === "string";
25
+ }
26
+ function isStorageKeyspace(value) {
27
+ if (!isRecord(value))
28
+ return !1;
29
+ return Object.values(value).every(isStorageEnvelope);
30
+ }
31
+ function assertUserStorageKey(key) {
32
+ if (key !== KEYSPACE_STORAGE_KEY)
33
+ return;
34
+ throw Error(`WebExtensionStorage key "${KEYSPACE_STORAGE_KEY}" is reserved for adapter metadata.`);
35
+ }
36
+ function assertBatchUserStorageKeys(operations) {
37
+ for (const operation of operations)
38
+ assertUserStorageKey(operation.key);
39
+ }
40
+ function createPutEnvelope(value) {
41
+ return { __weftStorage: ENVELOPE_MARKER, value: encodeBytesToBase64(value) };
42
+ }
43
+ function decodeEnvelope(value) {
44
+ if (!isStorageEnvelope(value))
45
+ return null;
46
+ return decodeBase64ToBytes(value.value);
47
+ }
48
+ function envelopeIsVisible(value) {
49
+ return isStorageEnvelope(value);
50
+ }
51
+ function serializedByteLength(value) {
52
+ return new TextEncoder().encode(JSON.stringify(value)).byteLength;
53
+ }
54
+ function isWebExtensionNamespace(value) {
55
+ if (!isRecord(value))
56
+ return !1;
57
+ if (value.runtime !== void 0 && !isRecord(value.runtime))
58
+ return !1;
59
+ return value.storage === void 0 || isRecord(value.storage);
60
+ }
61
+ function resolveNamespace(namespace = void 0) {
62
+ if (namespace !== void 0) {
63
+ if (!isWebExtensionNamespace(namespace))
64
+ throw Error("WebExtensionStorage injected namespace must be an object.");
65
+ return namespace;
66
+ }
67
+ const globalObject = globalThis, globalNamespace = globalObject.browser ?? globalObject.chrome;
68
+ if (globalNamespace?.storage === void 0)
69
+ throw Error("WebExtensionStorage requires globalThis.browser.storage or globalThis.chrome.storage.");
70
+ return globalNamespace;
71
+ }
72
+ function resolveStorageArea(namespace, area) {
73
+ const driver = namespace.storage?.[area];
74
+ if (driver === void 0)
75
+ throw Error(`WebExtensionStorage area "${area}" is not available.`);
76
+ return driver;
77
+ }
78
+ function webExtensionAreaPersistence(area) {
79
+ if (area === "session")
80
+ return "ephemeral";
81
+ if (area === "sync" || area === "managed")
82
+ return "remote";
83
+ return "local";
84
+ }
85
+ function lastRuntimeError(namespace) {
86
+ const message = namespace.runtime?.lastError?.message;
87
+ return message === void 0 ? null : Error(message);
88
+ }
89
+ async function invokeWebExtensionMethod(namespace, invoke, invokeWithoutCallback) {
90
+ return new Promise((resolve, reject) => {
91
+ let settled = !1;
92
+ const settleResolve = (value) => {
93
+ if (settled)
94
+ return;
95
+ const error = lastRuntimeError(namespace);
96
+ settled = !0;
97
+ if (error !== null)
98
+ reject(error);
99
+ else
100
+ resolve(value);
101
+ }, settleReject = (error) => {
102
+ if (settled)
103
+ return;
104
+ settled = !0;
105
+ reject(error);
106
+ }, callback = (value) => settleResolve(value);
107
+ let result;
108
+ try {
109
+ result = invoke(callback);
110
+ } catch {
111
+ try {
112
+ result = invokeWithoutCallback();
113
+ } catch (error) {
114
+ settleReject(error);
115
+ return;
116
+ }
117
+ }
118
+ if (isPromiseLike(result))
119
+ result.then(settleResolve, settleReject);
120
+ });
121
+ }
122
+
123
+ export class WebExtensionStorage {
124
+ static #mutationQueues = new WeakMap;
125
+ #namespace;
126
+ #driver;
127
+ #area;
128
+ #persistence;
129
+ #changeListener;
130
+ constructor(options = {}, namespace = void 0) {
131
+ this.#area = options.area ?? "local";
132
+ this.#persistence = webExtensionAreaPersistence(this.#area);
133
+ this.#namespace = resolveNamespace(namespace);
134
+ this.#driver = resolveStorageArea(this.#namespace, this.#area);
135
+ this.#changeListener = () => {};
136
+ this.#namespace.storage?.onChanged?.addListener?.(this.#changeListener);
137
+ }
138
+ capabilities() {
139
+ return {
140
+ persistence: this.#persistence,
141
+ readAfterWrite: "session",
142
+ scanConsistency: "best-effort",
143
+ atomicBatch: !1,
144
+ conditionalBatch: !1,
145
+ boundedRangeDelete: !1
146
+ };
147
+ }
148
+ async#getItems(keys) {
149
+ return invokeWebExtensionMethod(this.#namespace, (callback) => this.#driver.get(keys, callback), () => this.#driver.get(keys));
150
+ }
151
+ async#setItems(items) {
152
+ await invokeWebExtensionMethod(this.#namespace, (callback) => this.#driver.set(items, callback), () => this.#driver.set(items));
153
+ }
154
+ async#getBytesInUse(keys) {
155
+ if (this.#driver.getBytesInUse === void 0)
156
+ return null;
157
+ return invokeWebExtensionMethod(this.#namespace, (callback) => this.#driver.getBytesInUse?.(keys, callback), () => this.#driver.getBytesInUse?.(keys));
158
+ }
159
+ async#getKeyspace() {
160
+ const keyspace = (await this.#getItems(KEYSPACE_STORAGE_KEY))[KEYSPACE_STORAGE_KEY];
161
+ if (keyspace === void 0)
162
+ return {};
163
+ if (!isStorageKeyspace(keyspace))
164
+ throw Error("WebExtensionStorage keyspace is not a valid storage envelope map.");
165
+ return { ...keyspace };
166
+ }
167
+ async#writeKeyspace(keyspace) {
168
+ const items = { [KEYSPACE_STORAGE_KEY]: keyspace };
169
+ await this.#assertSyncQuota(items);
170
+ await this.#setItems(items);
171
+ }
172
+ async#withMutationLock(operation) {
173
+ const previous = WebExtensionStorage.#mutationQueues.get(this.#driver) ?? Promise.resolve(), gate = {}, current = new Promise((resolve) => {
174
+ gate.release = resolve;
175
+ }), queued = previous.catch(() => {
176
+ return;
177
+ }).then(() => current);
178
+ WebExtensionStorage.#mutationQueues.set(this.#driver, queued);
179
+ await previous.catch(() => {
180
+ return;
181
+ });
182
+ try {
183
+ return await operation();
184
+ } finally {
185
+ gate.release?.();
186
+ if (WebExtensionStorage.#mutationQueues.get(this.#driver) === queued)
187
+ WebExtensionStorage.#mutationQueues.delete(this.#driver);
188
+ }
189
+ }
190
+ #assertWritable() {
191
+ if (this.#area === "managed")
192
+ throw Error('WebExtensionStorage area "managed" is read-only.');
193
+ }
194
+ #assertSyncItemQuota(items) {
195
+ const itemQuota = this.#driver.QUOTA_BYTES_PER_ITEM ?? DEFAULT_SYNC_QUOTA_BYTES_PER_ITEM;
196
+ for (const [key, value] of Object.entries(items)) {
197
+ const bytes = serializedByteLength({ [key]: value });
198
+ if (bytes > itemQuota)
199
+ throw Error(`WebExtensionStorage sync item quota exceeded for "${key}": ${String(bytes)} > ${String(itemQuota)}.`);
200
+ }
201
+ }
202
+ async#projectSyncTotalBytes(items) {
203
+ const replacementKeys = Object.keys(items), currentTotalBytes = await this.#getBytesInUse(null), currentReplacementBytes = await this.#getBytesInUse(replacementKeys);
204
+ if (currentTotalBytes !== null && currentReplacementBytes !== null)
205
+ return currentTotalBytes - currentReplacementBytes + serializedByteLength(items);
206
+ const current = await this.#getItems(null);
207
+ return serializedByteLength({ ...current, ...items });
208
+ }
209
+ async#assertSyncQuota(items) {
210
+ if (this.#area !== "sync")
211
+ return;
212
+ this.#assertSyncItemQuota(items);
213
+ const totalQuota = this.#driver.QUOTA_BYTES ?? DEFAULT_SYNC_QUOTA_BYTES, projectedTotalBytes = await this.#projectSyncTotalBytes(items);
214
+ if (projectedTotalBytes > totalQuota)
215
+ throw Error(`WebExtensionStorage sync total quota exceeded: ${String(projectedTotalBytes)} > ${String(totalQuota)}.`);
216
+ }
217
+ async get(key) {
218
+ assertUserStorageKey(key);
219
+ const keyspace = await this.#getKeyspace();
220
+ return decodeEnvelope(keyspace[key]);
221
+ }
222
+ async put(key, value) {
223
+ this.#assertWritable();
224
+ assertUserStorageKey(key);
225
+ await this.#withMutationLock(async () => {
226
+ const keyspace = await this.#getKeyspace();
227
+ keyspace[key] = createPutEnvelope(value);
228
+ await this.#writeKeyspace(keyspace);
229
+ });
230
+ }
231
+ async delete(key) {
232
+ this.#assertWritable();
233
+ assertUserStorageKey(key);
234
+ await this.#withMutationLock(async () => {
235
+ const keyspace = await this.#getKeyspace();
236
+ if (!(key in keyspace))
237
+ return;
238
+ delete keyspace[key];
239
+ await this.#writeKeyspace(keyspace);
240
+ });
241
+ }
242
+ async* scan(prefix, options = {}) {
243
+ const keyspace = await this.#getKeyspace();
244
+ let keys = Object.keys(keyspace).filter((key) => key.startsWith(prefix)).filter((key) => matchesScanOptions(key, options)).filter((key) => envelopeIsVisible(keyspace[key])).toSorted();
245
+ if (options.reverse)
246
+ keys = keys.toReversed();
247
+ let count = 0;
248
+ for (const key of keys) {
249
+ if (options.limit !== void 0 && count >= options.limit)
250
+ break;
251
+ const value = decodeEnvelope(keyspace[key]);
252
+ if (value === null)
253
+ continue;
254
+ yield [key, value];
255
+ count += 1;
256
+ }
257
+ }
258
+ async batch(operations) {
259
+ assertStorageBatchOperationCount("batch operations", operations.length);
260
+ this.#assertWritable();
261
+ if (operations.length === 0)
262
+ return;
263
+ assertBatchUserStorageKeys(operations);
264
+ await this.#withMutationLock(async () => {
265
+ const keyspace = await this.#getKeyspace();
266
+ for (const operation of operations)
267
+ if (operation.type === "put")
268
+ keyspace[operation.key] = createPutEnvelope(operation.value);
269
+ else
270
+ delete keyspace[operation.key];
271
+ await this.#writeKeyspace(keyspace);
272
+ });
273
+ }
274
+ has(key) {
275
+ return storageHasCore(this, key);
276
+ }
277
+ async* keys(prefix, options) {
278
+ yield* storageKeysCore(this, prefix, options);
279
+ }
280
+ count(prefix) {
281
+ return storageCountCore(this, prefix);
282
+ }
283
+ async deletePrefix(prefix) {
284
+ this.#assertWritable();
285
+ return storageDeletePrefixCore(this, prefix);
286
+ }
287
+ async deleteRange(prefix, options) {
288
+ const normalized = normalizeDeleteRangeOptions(options);
289
+ this.#assertWritable();
290
+ return storageDeleteRangeCore(this, prefix, normalized);
291
+ }
292
+ scoped(prefix) {
293
+ return scopedStorage(this, prefix);
294
+ }
295
+ [Symbol.dispose]() {
296
+ this.#namespace.storage?.onChanged?.removeListener?.(this.#changeListener);
297
+ }
298
+ }