@mappa-ai/mappa-node 1.0.0 → 1.2.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.
package/dist/index.cjs CHANGED
@@ -1 +1,5 @@
1
- let e=require(`@paralleldrive/cuid2`);var t=class extends Error{name=`MappaError`;requestId;code;constructor(e,t){super(e),this.requestId=t?.requestId,this.code=t?.code,this.cause=t?.cause}},n=class extends t{name=`ApiError`;status;details;constructor(e,t){super(e,{requestId:t.requestId,code:t.code}),this.status=t.status,this.details=t.details}},r=class extends n{name=`RateLimitError`;retryAfterMs},i=class extends n{name=`AuthError`},a=class extends n{name=`ValidationError`},o=class extends t{name=`JobFailedError`;jobId;constructor(e,t,n){super(t,n),this.jobId=e}},s=class extends t{name=`JobCanceledError`;jobId;constructor(e,t,n){super(t,n),this.jobId=e}},c=class{constructor(e){this.transport=e}async getBalance(e){return(await this.transport.request({method:`GET`,path:`/v1/credits/balance`,requestId:e?.requestId,signal:e?.signal,retryable:!0})).data}async listTransactions(e){let t={};return e?.limit!==void 0&&(t.limit=String(e.limit)),e?.offset!==void 0&&(t.offset=String(e.offset)),(await this.transport.request({method:`GET`,path:`/v1/credits/transactions`,query:t,requestId:e?.requestId,signal:e?.signal,retryable:!0})).data}async*listAllTransactions(e){let t=0,n=e?.limit??50;for(;;){let r=await this.listTransactions({...e,limit:n,offset:t});for(let e of r.transactions)yield e;if(t+=r.transactions.length,t>=r.pagination.total)break}}async getJobUsage(e,n){if(!e)throw new t(`jobId is required`);return(await this.transport.request({method:`GET`,path:`/v1/credits/usage/${encodeURIComponent(e)}`,requestId:n?.requestId,signal:n?.signal,retryable:!0})).data}async hasEnough(e,t){return(await this.getBalance(t)).available>=e}async getAvailable(e){return(await this.getBalance(e)).available}},l=class{constructor(e){this.transport=e}async create(e){if(!!e.reportId==!!e.jobId)throw new t(`Provide exactly one of reportId or jobId`);return(await this.transport.request({method:`POST`,path:`/v1/feedback`,body:e,idempotencyKey:e.idempotencyKey,requestId:e.requestId,signal:e.signal,retryable:!0})).data}},u=class{constructor(e){this.transport=e}async upload(e){if(typeof FormData>`u`)throw new t(`FormData is not available in this runtime; cannot perform multipart upload`);let n=d(e.file,e.filename),r=e.contentType??n;if(!r)throw new t(`contentType is required when it cannot be inferred from file.type or filename`);let i=e.filename??f(e.file)??`upload`,a=await m(e.file,r),o=new FormData;return o.append(`file`,a,i),o.append(`contentType`,r),e.filename&&o.append(`filename`,e.filename),(await this.transport.request({method:`POST`,path:`/v1/files`,body:o,idempotencyKey:e.idempotencyKey,requestId:e.requestId,signal:e.signal,retryable:!0})).data}async get(e,n){if(!e)throw new t(`mediaId is required`);return(await this.transport.request({method:`GET`,path:`/v1/files/${encodeURIComponent(e)}`,requestId:n?.requestId,signal:n?.signal,retryable:!0})).data}async list(e){let t={};return e?.limit!==void 0&&(t.limit=String(e.limit)),e?.cursor&&(t.cursor=e.cursor),e?.includeDeleted!==void 0&&(t.includeDeleted=String(e.includeDeleted)),(await this.transport.request({method:`GET`,path:`/v1/files`,query:t,requestId:e?.requestId,signal:e?.signal,retryable:!0})).data}async*listAll(e){let t,n=!0;for(;n;){let r=await this.list({...e,cursor:t});for(let e of r.files)yield e;t=r.cursor,n=r.hasMore}}async setRetentionLock(e,n,r){if(!e)throw new t(`mediaId is required`);return(await this.transport.request({method:`PATCH`,path:`/v1/files/${encodeURIComponent(e)}/retention`,body:{lock:n},requestId:r?.requestId,signal:r?.signal,retryable:!0})).data}async delete(e,n){if(!e)throw new t(`mediaId is required`);return(await this.transport.request({method:`DELETE`,path:`/v1/files/${encodeURIComponent(e)}`,idempotencyKey:n?.idempotencyKey,requestId:n?.requestId,signal:n?.signal,retryable:!0})).data}};function d(e,t){if(typeof Blob<`u`&&e instanceof Blob&&e.type)return e.type;if(t)return p(t)}function f(e){if(typeof Blob<`u`&&e instanceof Blob){let t=e;if(typeof t.name==`string`&&t.name)return t.name}}function p(e){let t=e.lastIndexOf(`.`);if(!(t<0))switch(e.slice(t+1).toLowerCase()){case`mp4`:return`video/mp4`;case`mov`:return`video/quicktime`;case`webm`:return`video/webm`;case`mp3`:return`audio/mpeg`;case`wav`:return`audio/wav`;case`m4a`:return`audio/mp4`;case`png`:return`image/png`;case`jpg`:case`jpeg`:return`image/jpeg`;case`gif`:return`image/gif`;case`webp`:return`image/webp`;case`pdf`:return`application/pdf`;case`json`:return`application/json`;case`txt`:return`text/plain`;default:return}}async function m(e,n){if(typeof Blob<`u`&&e instanceof Blob){if(e.type===n)return e;let t=e;return typeof t.slice==`function`?t.slice(0,e.size,n):e}if(e instanceof ArrayBuffer||e instanceof Uint8Array)return new Blob([e],{type:n});if(typeof ReadableStream<`u`&&e instanceof ReadableStream){if(typeof Response>`u`)throw new t(`ReadableStream upload requires Response to convert stream to Blob`);let r=await new Response(e).blob();return r.slice(0,r.size,n)}throw new t(`Unsupported file type for upload()`)}var h=class{constructor(e){this.transport=e}async ping(){return(await this.transport.request({method:`GET`,path:`/v1/health/ping`,retryable:!0})).data}};const g=(0,e.init)({length:32});function _(e,t){let n=e.get(t);return n===null?void 0:n}function v(e){let t=.8+Math.random()*.4;return Math.floor(e*t)}function y(e,t,n){let r=t*2**Math.max(0,e-1);return Math.min(r,n)}function b(){return Date.now()}function x(e){return!!e&&typeof e.aborted==`boolean`}function S(){let e=Error(`The operation was aborted`);return e.name=`AbortError`,e}function C(e=`req`){return`${e}_${g()}`}var w=class{constructor(e){this.transport=e}async get(e,t){return(await this.transport.request({method:`GET`,path:`/v1/jobs/${encodeURIComponent(e)}`,requestId:t?.requestId,signal:t?.signal,retryable:!0})).data}async cancel(e,t){return(await this.transport.request({method:`POST`,path:`/v1/jobs/${encodeURIComponent(e)}/cancel`,idempotencyKey:t?.idempotencyKey,requestId:t?.requestId,signal:t?.signal,retryable:!0})).data}async wait(e,t){let n=t?.timeoutMs??5*6e4,r=t?.pollIntervalMs??1e3,i=t?.maxPollIntervalMs??1e4,a=b(),c=0,l,u;for(;;){if(t?.signal?.aborted)throw S();let d=await this.get(e,{signal:t?.signal});if(d.status!==u&&(u=d.status,t?.onEvent?.({type:`status`,job:d})),d.stage&&d.stage!==l&&(l=d.stage,t?.onEvent?.({type:`stage`,stage:d.stage,progress:d.progress,job:d})),d.status===`succeeded`)return t?.onEvent?.({type:`terminal`,job:d}),d;if(d.status===`failed`)throw t?.onEvent?.({type:`terminal`,job:d}),new o(e,d.error?.message??`Job failed`,{requestId:d.requestId,code:d.error?.code,cause:d.error});if(d.status===`canceled`)throw t?.onEvent?.({type:`terminal`,job:d}),new s(e,`Job canceled`,{requestId:d.requestId,cause:d.error});if(b()-a>n)throw new o(e,`Timed out waiting for job ${e} after ${n}ms`,{cause:{jobId:e,timeoutMs:n}});c+=1;let f=v(y(c,r,i));await new Promise(e=>setTimeout(e,f))}}async*stream(e,t){let n,r;for(;;){if(t?.signal?.aborted)return;let i=await this.get(e,{signal:t?.signal});if(i.status!==r){r=i.status;let e={type:`status`,job:i};t?.onEvent?.(e),yield e}if(i.stage&&i.stage!==n){n=i.stage;let e={type:`stage`,stage:i.stage,progress:i.progress,job:i};t?.onEvent?.(e),yield e}if(i.status===`succeeded`||i.status===`failed`||i.status===`canceled`){let e={type:`terminal`,job:i};t?.onEvent?.(e),yield e;return}await new Promise(e=>setTimeout(e,1e3))}}};function T(e){let n=e;if(!(e=>typeof e==`object`&&!!e)(n))throw new t(`media must be an object`);if(n.url!==void 0)throw new t(`media.url is not supported; pass { mediaId } or use createJobFromUrl()`);let r=n.mediaId;if(typeof r!=`string`||!r)throw new t(`media.mediaId must be a non-empty string`)}var E=class{constructor(e,t,n,r){this.transport=e,this.jobs=t,this.files=n,this.fetchImpl=r}async createJob(e){T(e.media);let t=e.idempotencyKey??this.defaultIdempotencyKey(e),n=await this.transport.request({method:`POST`,path:`/v1/reports/jobs`,body:this.normalizeJobRequest(e),idempotencyKey:t,requestId:e.requestId,retryable:!0}),r={...n.data,requestId:n.requestId??n.data.requestId};return r.handle=this.makeHandle(r.jobId),r}async createJobFromFile(e){let{file:t,contentType:n,filename:r,idempotencyKey:i,requestId:a,signal:o,...s}=e,c=await this.files.upload({file:t,contentType:n,filename:r,idempotencyKey:i,requestId:a,signal:o});return this.createJob({...s,media:{mediaId:c.mediaId},idempotencyKey:i,requestId:a})}async createJobFromUrl(e){let{url:n,contentType:r,filename:i,idempotencyKey:a,requestId:o,signal:s,...c}=e,l;try{l=new URL(n)}catch{throw new t(`url must be a valid URL`)}if(l.protocol!==`http:`&&l.protocol!==`https:`)throw new t(`url must use http: or https:`);let u=await this.fetchImpl(l.toString(),{signal:s});if(!u.ok)throw new t(`Failed to download url (status ${u.status})`);let d=u.headers.get(`content-type`)??void 0,f=r??d;if(!f)throw new t(`contentType is required when it cannot be inferred from the download response`);if(typeof Blob>`u`)throw new t(`Blob is not available in this runtime; cannot download and upload from url`);let p=await u.blob(),m=await this.files.upload({file:p,contentType:f,filename:i,idempotencyKey:a,requestId:o,signal:s});return this.createJob({...c,media:{mediaId:m.mediaId},idempotencyKey:a,requestId:o})}async get(e,t){return(await this.transport.request({method:`GET`,path:`/v1/reports/${encodeURIComponent(e)}`,requestId:t?.requestId,signal:t?.signal,retryable:!0})).data}async getByJob(e,t){return(await this.transport.request({method:`GET`,path:`/v1/reports/by-job/${encodeURIComponent(e)}`,requestId:t?.requestId,signal:t?.signal,retryable:!0})).data}async generate(e,n){let r=await this.createJob(e);if(!r.handle)throw new t(`Job receipt is missing handle`);return r.handle.wait(n?.wait)}async generateFromFile(e,n){let r=await this.createJobFromFile(e);if(!r.handle)throw new t(`Job receipt is missing handle`);return r.handle.wait(n?.wait)}async generateFromUrl(e,n){let r=await this.createJobFromUrl(e);if(!r.handle)throw new t(`Job receipt is missing handle`);return r.handle.wait(n?.wait)}makeHandle(e){let n=this;return{jobId:e,stream:t=>n.jobs.stream(e,t),async wait(r){let i=await n.jobs.wait(e,r);if(!i.reportId)throw new t(`Job ${e} succeeded but no reportId was returned`);return n.get(i.reportId)},cancel:()=>n.jobs.cancel(e),job:()=>n.jobs.get(e),report:()=>n.getByJob(e)}}defaultIdempotencyKey(e){return C(`idem`)}normalizeJobRequest(e){let t=e.target;if(!t)return e;let n={strategy:t.strategy};switch(t.onMiss&&(n.on_miss=t.onMiss),t.strategy){case`dominant`:return{...e,target:n};case`timerange`:{let r=t.timeRange??{};return{...e,target:{...n,timerange:{start_seconds:r.startSeconds??null,end_seconds:r.endSeconds??null}}}}case`entity_id`:return{...e,target:{...n,entity_id:t.entityId}};case`magic_hint`:return{...e,target:{...n,hint:t.hint}};default:return e}}};function D(e,t,n){let r=new URL(t.replace(/^\//,``),e.endsWith(`/`)?e:`${e}/`);if(n)for(let[e,t]of Object.entries(n))t!==void 0&&r.searchParams.set(e,String(t));return r.toString()}async function O(e){let t=await e.text();if(!t)return{parsed:null,text:``};try{return{parsed:JSON.parse(t),text:t}}catch{return{parsed:t,text:t}}}function k(e,t){let o=e.headers.get(`x-request-id`)??void 0,s,c=`Request failed with status ${e.status}`,l=t;if(typeof t==`string`)c=t;else if(t&&typeof t==`object`){let e=t,n=e.error??e;if(n&&typeof n==`object`){let e=n;typeof e.message==`string`&&(c=e.message),typeof e.code==`string`&&(s=e.code),`details`in e&&(l=e.details)}}if(e.status===401||e.status===403)return new i(c,{status:e.status,requestId:o,code:s,details:l});if(e.status===422)return new a(c,{status:e.status,requestId:o,code:s,details:l});if(e.status===429){let t=new r(c,{status:e.status,requestId:o,code:s,details:l}),n=e.headers.get(`retry-after`);if(n){let e=Number(n);Number.isFinite(e)&&e>=0&&(t.retryAfterMs=e*1e3)}return t}return new n(c,{status:e.status,requestId:o,code:s,details:l})}function A(e,t){return e.retryable?t instanceof r?{retry:!0,retryAfterMs:t.retryAfterMs}:t instanceof n?{retry:t.status>=500&&t.status<=599}:t instanceof TypeError?{retry:!0}:{retry:!1}:{retry:!1}}var j=class{fetchImpl;constructor(e){this.opts=e,this.fetchImpl=e.fetch??fetch}async request(e){let t=D(this.opts.baseUrl,e.path,e.query),n=e.requestId??C(`req`),r={"Mappa-Api-Key":this.opts.apiKey,"X-Request-Id":n,...this.opts.userAgent?{"User-Agent":this.opts.userAgent}:{},...this.opts.defaultHeaders??{}};if(e.idempotencyKey&&(r[`Idempotency-Key`]=e.idempotencyKey),e.headers)for(let[t,n]of Object.entries(e.headers))n!==void 0&&(r[t]=n);let i=typeof FormData<`u`&&e.body instanceof FormData;e.body!==void 0&&!i&&(r[`Content-Type`]=`application/json`);let a=e.body===void 0?void 0:i?e.body:JSON.stringify(e.body),o=Math.max(0,this.opts.maxRetries),s=Date.now();for(let i=1;i<=1+o;i++){let c=new AbortController,l=setTimeout(()=>c.abort(S()),this.opts.timeoutMs);if(x(e.signal)){let t=e.signal;if(!t)throw clearTimeout(l),Error(`Unexpected: abort signal missing`);if(t.aborted)throw clearTimeout(l),S();t.addEventListener(`abort`,()=>c.abort(S()),{once:!0})}this.opts.telemetry?.onRequest?.({method:e.method,url:t,requestId:n});try{let l=await this.fetchImpl(t,{method:e.method,headers:r,body:a,signal:c.signal}),u=Date.now()-s,d=_(l.headers,`x-request-id`)??n;if(!l.ok){let{parsed:n}=await O(l),r=k(l,n);this.opts.telemetry?.onError?.({url:t,requestId:d,error:r});let a=A(e,r);if(i<=o+1&&a.retry&&i<=o){let e=a.retryAfterMs??v(y(i,500,4e3));await new Promise(t=>setTimeout(t,e));continue}throw r}let f=l.headers.get(`content-type`)??``,p;return p=f.includes(`application/json`)?await l.json():await l.text(),this.opts.telemetry?.onResponse?.({status:l.status,url:t,requestId:d,durationMs:u}),{data:p,status:l.status,requestId:d,headers:l.headers}}catch(r){this.opts.telemetry?.onError?.({url:t,requestId:n,error:r});let a=A(e,r);if(i<=o&&a.retry){let e=a.retryAfterMs??v(y(i,500,4e3));await new Promise(t=>setTimeout(t,e));continue}throw r}finally{clearTimeout(l)}}throw Error(`Unexpected transport exit`)}};function M(e){return typeof e==`object`&&!!e}var N=class{async verifySignature(e){let t=e.toleranceSec??300,n=P(e.headers,`mappa-signature`);if(!n)throw Error(`Missing mappa-signature header`);let r=F(n),i=Number(r.t);if(!Number.isFinite(i))throw Error(`Invalid signature timestamp`);let a=Math.floor(Date.now()/1e3);if(Math.abs(a-i)>t)throw Error(`Signature timestamp outside tolerance`);let o=`${r.t}.${e.payload}`;if(!R(await I(e.secret,o),r.v1))throw Error(`Invalid signature`);return{ok:!0}}parseEvent(e){let t=JSON.parse(e);if(!M(t))throw Error(`Invalid webhook payload: not an object`);let n=t,r=n.id,i=n.type,a=n.createdAt;if(typeof r!=`string`)throw Error(`Invalid webhook payload: id must be a string`);if(typeof i!=`string`)throw Error(`Invalid webhook payload: type must be a string`);if(typeof a!=`string`)throw Error(`Invalid webhook payload: createdAt must be a string`);return{id:r,type:i,createdAt:a,data:`data`in n?n.data:void 0}}};function P(e,t){let n=Object.keys(e).find(e=>e.toLowerCase()===t.toLowerCase()),r=n?e[n]:void 0;if(r)return Array.isArray(r)?r[0]:r}function F(e){let t={};for(let n of e.split(`,`)){let[e,r]=n.split(`=`);e&&r&&(t[e.trim()]=r.trim())}if(!t.t||!t.v1)throw Error(`Invalid signature format`);return{t:t.t,v1:t.v1}}async function I(e,t){let n=new TextEncoder,r=await crypto.subtle.importKey(`raw`,n.encode(e),{name:`HMAC`,hash:`SHA-256`},!1,[`sign`]);return L(await crypto.subtle.sign(`HMAC`,r,n.encode(t)))}function L(e){let t=new Uint8Array(e),n=``;for(let e of t)n+=e.toString(16).padStart(2,`0`);return n}function R(e,t){if(e.length!==t.length)return!1;let n=0;for(let r=0;r<e.length;r++)n|=e.charCodeAt(r)^t.charCodeAt(r);return n===0}var z=class e{files;jobs;reports;feedback;credits;webhooks;health;transport;opts;constructor(e){if(!e.apiKey)throw new t(`apiKey is required`);let n=e.baseUrl??`https://api.mappa.ai`,r=e.timeoutMs??3e4,i=e.maxRetries??2;this.opts={...e,apiKey:e.apiKey,baseUrl:n,timeoutMs:r,maxRetries:i},this.transport=new j({apiKey:e.apiKey,baseUrl:n,timeoutMs:r,maxRetries:i,defaultHeaders:e.defaultHeaders,fetch:e.fetch,telemetry:e.telemetry,userAgent:e.userAgent}),this.files=new u(this.transport),this.jobs=new w(this.transport),this.reports=new E(this.transport,this.jobs,this.files,this.opts.fetch??fetch),this.feedback=new l(this.transport),this.credits=new c(this.transport),this.webhooks=new N,this.health=new h(this.transport)}withOptions(t){return new e({...this.opts,...t,apiKey:t.apiKey??this.opts.apiKey})}close(){}};function B(e){return e.output.type===`markdown`}function V(e){return e.output.type===`json`}function H(e){return e.output.type===`pdf`}function U(e){return e.output.type===`url`}function W(e){return e instanceof t}exports.ApiError=n,exports.AuthError=i,exports.JobCanceledError=s,exports.JobFailedError=o,exports.Mappa=z,exports.MappaError=t,exports.RateLimitError=r,exports.ValidationError=a,exports.isJsonReport=V,exports.isMappaError=W,exports.isMarkdownReport=B,exports.isPdfReport=H,exports.isUrlReport=U;
1
+ let e=require(`@paralleldrive/cuid2`);var t=class extends Error{name=`MappaError`;requestId;code;constructor(e,t){super(e),this.requestId=t?.requestId,this.code=t?.code,this.cause=t?.cause}},n=class extends t{name=`ApiError`;status;details;constructor(e,t){super(e,{requestId:t.requestId,code:t.code}),this.status=t.status,this.details=t.details}},r=class extends n{name=`RateLimitError`;retryAfterMs},i=class extends n{name=`AuthError`},a=class extends n{name=`ValidationError`},o=class extends t{name=`JobFailedError`;jobId;constructor(e,t,n){super(t,n),this.jobId=e}},s=class extends t{name=`JobCanceledError`;jobId;constructor(e,t,n){super(t,n),this.jobId=e}},c=class{constructor(e){this.transport=e}async getBalance(e){return(await this.transport.request({method:`GET`,path:`/v1/credits/balance`,requestId:e?.requestId,signal:e?.signal,retryable:!0})).data}async listTransactions(e){let t={};return e?.limit!==void 0&&(t.limit=String(e.limit)),e?.offset!==void 0&&(t.offset=String(e.offset)),(await this.transport.request({method:`GET`,path:`/v1/credits/transactions`,query:t,requestId:e?.requestId,signal:e?.signal,retryable:!0})).data}async*listAllTransactions(e){let t=0,n=e?.limit??50;for(;;){let r=await this.listTransactions({...e,limit:n,offset:t});for(let e of r.transactions)yield e;if(t+=r.transactions.length,t>=r.pagination.total)break}}async getJobUsage(e,n){if(!e)throw new t(`jobId is required`);return(await this.transport.request({method:`GET`,path:`/v1/credits/usage/${encodeURIComponent(e)}`,requestId:n?.requestId,signal:n?.signal,retryable:!0})).data}async hasEnough(e,t){return(await this.getBalance(t)).available>=e}async getAvailable(e){return(await this.getBalance(e)).available}};const l=/^[a-zA-Z0-9_-]{1,64}$/,u=10;function d(e){if(typeof e!=`string`)throw new t(`Tags must be strings`);if(!l.test(e))throw new t(`Invalid tag "${e}": must be 1-64 characters, alphanumeric with underscores and hyphens only`)}function f(e){if(!Array.isArray(e))throw new t(`tags must be an array`);if(e.length>10)throw new t(`Too many tags: maximum 10 per request`);for(let t of e)d(t)}var p=class{constructor(e){this.transport=e}async get(e,n){if(!e||typeof e!=`string`)throw new t(`entityId must be a non-empty string`);return(await this.transport.request({method:`GET`,path:`/v1/entities/${encodeURIComponent(e)}`,requestId:n?.requestId,signal:n?.signal,retryable:!0})).data}async list(e){let t={};return e?.tags&&(f(e.tags),t.tags=e.tags.join(`,`)),e?.cursor&&(t.cursor=e.cursor),e?.limit!==void 0&&(t.limit=String(e.limit)),(await this.transport.request({method:`GET`,path:`/v1/entities`,query:t,requestId:e?.requestId,signal:e?.signal,retryable:!0})).data}async*listAll(e){let t,n=!0;for(;n;){let r=await this.list({...e,cursor:t});for(let e of r.entities)yield e;t=r.cursor,n=r.hasMore}}async getByTag(e,t){return d(e),this.list({...t,tags:[e]})}async addTags(e,n,r){if(!e||typeof e!=`string`)throw new t(`entityId must be a non-empty string`);if(n.length===0)throw new t(`At least one tag is required`);return f(n),(await this.transport.request({method:`POST`,path:`/v1/entities/${encodeURIComponent(e)}/tags`,body:{tags:n},requestId:r?.requestId,signal:r?.signal,retryable:!0})).data}async removeTags(e,n,r){if(!e||typeof e!=`string`)throw new t(`entityId must be a non-empty string`);if(n.length===0)throw new t(`At least one tag is required`);return f(n),(await this.transport.request({method:`DELETE`,path:`/v1/entities/${encodeURIComponent(e)}/tags`,body:{tags:n},requestId:r?.requestId,signal:r?.signal,retryable:!0})).data}async setTags(e,n,r){if(!e||typeof e!=`string`)throw new t(`entityId must be a non-empty string`);return f(n),(await this.transport.request({method:`PUT`,path:`/v1/entities/${encodeURIComponent(e)}/tags`,body:{tags:n},requestId:r?.requestId,signal:r?.signal,retryable:!0})).data}},m=class{constructor(e){this.transport=e}async create(e){if(!!e.reportId==!!e.jobId)throw new t(`Provide exactly one of reportId or jobId`);return(await this.transport.request({method:`POST`,path:`/v1/feedback`,body:e,idempotencyKey:e.idempotencyKey,requestId:e.requestId,signal:e.signal,retryable:!0})).data}},h=class{constructor(e){this.transport=e}async upload(e){if(typeof FormData>`u`)throw new t(`FormData is not available in this runtime; cannot perform multipart upload`);let n=g(e.file,e.filename),r=e.contentType??n;if(!r)throw new t(`contentType is required when it cannot be inferred from file.type or filename`);let i=e.filename??_(e.file)??`upload`,a=await y(e.file,r),o=new FormData;return o.append(`file`,a,i),o.append(`contentType`,r),e.filename&&o.append(`filename`,e.filename),(await this.transport.request({method:`POST`,path:`/v1/files`,body:o,idempotencyKey:e.idempotencyKey,requestId:e.requestId,signal:e.signal,retryable:!0})).data}async get(e,n){if(!e)throw new t(`mediaId is required`);return(await this.transport.request({method:`GET`,path:`/v1/files/${encodeURIComponent(e)}`,requestId:n?.requestId,signal:n?.signal,retryable:!0})).data}async list(e){let t={};return e?.limit!==void 0&&(t.limit=String(e.limit)),e?.cursor&&(t.cursor=e.cursor),e?.includeDeleted!==void 0&&(t.includeDeleted=String(e.includeDeleted)),(await this.transport.request({method:`GET`,path:`/v1/files`,query:t,requestId:e?.requestId,signal:e?.signal,retryable:!0})).data}async*listAll(e){let t,n=!0;for(;n;){let r=await this.list({...e,cursor:t});for(let e of r.files)yield e;t=r.cursor,n=r.hasMore}}async setRetentionLock(e,n,r){if(!e)throw new t(`mediaId is required`);return(await this.transport.request({method:`PATCH`,path:`/v1/files/${encodeURIComponent(e)}/retention`,body:{lock:n},requestId:r?.requestId,signal:r?.signal,retryable:!0})).data}async delete(e,n){if(!e)throw new t(`mediaId is required`);return(await this.transport.request({method:`DELETE`,path:`/v1/files/${encodeURIComponent(e)}`,idempotencyKey:n?.idempotencyKey,requestId:n?.requestId,signal:n?.signal,retryable:!0})).data}};function g(e,t){if(typeof Blob<`u`&&e instanceof Blob&&e.type)return e.type;if(t)return v(t)}function _(e){if(typeof Blob<`u`&&e instanceof Blob){let t=e;if(typeof t.name==`string`&&t.name)return t.name}}function v(e){let t=e.lastIndexOf(`.`);if(!(t<0))switch(e.slice(t+1).toLowerCase()){case`mp4`:return`video/mp4`;case`mov`:return`video/quicktime`;case`webm`:return`video/webm`;case`mp3`:return`audio/mpeg`;case`wav`:return`audio/wav`;case`m4a`:return`audio/mp4`;case`png`:return`image/png`;case`jpg`:case`jpeg`:return`image/jpeg`;case`gif`:return`image/gif`;case`webp`:return`image/webp`;case`pdf`:return`application/pdf`;case`json`:return`application/json`;case`txt`:return`text/plain`;default:return}}async function y(e,n){if(typeof Blob<`u`&&e instanceof Blob){if(e.type===n)return e;let t=e;return typeof t.slice==`function`?t.slice(0,e.size,n):e}if(e instanceof ArrayBuffer||e instanceof Uint8Array)return new Blob([e],{type:n});if(typeof ReadableStream<`u`&&e instanceof ReadableStream){if(typeof Response>`u`)throw new t(`ReadableStream upload requires Response to convert stream to Blob`);let r=await new Response(e).blob();return r.slice(0,r.size,n)}throw new t(`Unsupported file type for upload()`)}var b=class{constructor(e){this.transport=e}async ping(){return(await this.transport.request({method:`GET`,path:`/v1/health/ping`,retryable:!0})).data}};const x=(0,e.init)({length:32});function S(e,t){let n=e.get(t);return n===null?void 0:n}function C(e){let t=.8+Math.random()*.4;return Math.floor(e*t)}function w(e,t,n){let r=t*2**Math.max(0,e-1);return Math.min(r,n)}function T(e){return!!e&&typeof e.aborted==`boolean`}function E(){let e=Error(`The operation was aborted`);return e.name=`AbortError`,e}function D(e=`req`){return`${e}_${x()}`}var O=class{constructor(e){this.transport=e}async get(e,t){return(await this.transport.request({method:`GET`,path:`/v1/jobs/${encodeURIComponent(e)}`,requestId:t?.requestId,signal:t?.signal,retryable:!0})).data}async cancel(e,t){return(await this.transport.request({method:`POST`,path:`/v1/jobs/${encodeURIComponent(e)}/cancel`,idempotencyKey:t?.idempotencyKey,requestId:t?.requestId,signal:t?.signal,retryable:!0})).data}async wait(e,t){let n=t?.timeoutMs??5*6e4,r=new AbortController,i=setTimeout(()=>r.abort(),n);if(t?.signal){if(t.signal.aborted)throw clearTimeout(i),E();t.signal.addEventListener(`abort`,()=>r.abort(),{once:!0})}try{for await(let n of this.stream(e,{signal:r.signal,onEvent:t?.onEvent}))if(n.type===`terminal`){let t=n.job;if(t.status===`succeeded`)return t;if(t.status===`failed`)throw new o(e,t.error?.message??`Job failed`,{requestId:t.requestId,code:t.error?.code,cause:t.error});if(t.status===`canceled`)throw new s(e,`Job canceled`,{requestId:t.requestId,cause:t.error})}throw new o(e,`Timed out waiting for job ${e} after ${n}ms`,{cause:{jobId:e,timeoutMs:n}})}finally{clearTimeout(i)}}async*stream(e,n){let r,i=0;for(;i<3;)try{let a=this.transport.streamSSE(`/v1/jobs/${encodeURIComponent(e)}/stream`,{signal:n?.signal,lastEventId:r});for await(let e of a){if(r=e.id,e.event===`error`){let n=e.data;throw new t(n.error?.message??`Unknown SSE error`,{code:n.error?.code})}if(e.event===`heartbeat`)continue;let a=this.mapSSEToJobEvent(e);if(a&&(n?.onEvent?.(a),yield a,e.event===`terminal`))return;i=0}i++,i<3&&await this.backoff(i)}catch(e){if(n?.signal?.aborted||(i++,i>=3))throw e;await this.backoff(i)}throw new t(`Failed to get status for job ${e} after 3 retries`)}mapSSEToJobEvent(e){let t=e.data;switch(e.event){case`status`:return{type:`status`,job:t.job};case`stage`:return{type:`stage`,stage:t.stage,progress:t.progress,job:t.job};case`terminal`:return{type:`terminal`,job:t.job};default:return{type:`status`,job:t.job}}}async backoff(e){let t=Math.min(1e3*2**e,1e4),n=t*.5*Math.random();await new Promise(e=>setTimeout(e,t+n))}};function k(e){let n=e;if(!(e=>typeof e==`object`&&!!e)(n))throw new t(`media must be an object`);if(n.url!==void 0)throw new t(`media.url is not supported; pass { mediaId } or use createJobFromUrl()`);let r=n.mediaId;if(typeof r!=`string`||!r)throw new t(`media.mediaId must be a non-empty string`)}var A=class{constructor(e,t,n,r){this.transport=e,this.jobs=t,this.files=n,this.fetchImpl=r}async createJob(e){k(e.media);let t=e.idempotencyKey??this.defaultIdempotencyKey(e),n=await this.transport.request({method:`POST`,path:`/v1/reports/jobs`,body:this.normalizeJobRequest(e),idempotencyKey:t,requestId:e.requestId,retryable:!0}),r={...n.data,requestId:n.requestId??n.data.requestId};return r.handle=this.makeHandle(r.jobId),r}async createJobFromFile(e){let{file:t,contentType:n,filename:r,idempotencyKey:i,requestId:a,signal:o,...s}=e,c=await this.files.upload({file:t,contentType:n,filename:r,idempotencyKey:i,requestId:a,signal:o});return this.createJob({...s,media:{mediaId:c.mediaId},idempotencyKey:i,requestId:a})}async createJobFromUrl(e){let{url:n,contentType:r,filename:i,idempotencyKey:a,requestId:o,signal:s,...c}=e,l;try{l=new URL(n)}catch{throw new t(`url must be a valid URL`)}if(l.protocol!==`http:`&&l.protocol!==`https:`)throw new t(`url must use http: or https:`);let u=await this.fetchImpl(l.toString(),{signal:s});if(!u.ok)throw new t(`Failed to download url (status ${u.status})`);let d=u.headers.get(`content-type`)??void 0,f=r??d;if(!f)throw new t(`contentType is required when it cannot be inferred from the download response`);if(typeof Blob>`u`)throw new t(`Blob is not available in this runtime; cannot download and upload from url`);let p=await u.blob(),m=await this.files.upload({file:p,contentType:f,filename:i,idempotencyKey:a,requestId:o,signal:s});return this.createJob({...c,media:{mediaId:m.mediaId},idempotencyKey:a,requestId:o})}async get(e,t){return(await this.transport.request({method:`GET`,path:`/v1/reports/${encodeURIComponent(e)}`,requestId:t?.requestId,signal:t?.signal,retryable:!0})).data}async getByJob(e,t){return(await this.transport.request({method:`GET`,path:`/v1/reports/by-job/${encodeURIComponent(e)}`,requestId:t?.requestId,signal:t?.signal,retryable:!0})).data}async generate(e,n){let r=await this.createJob(e);if(!r.handle)throw new t(`Job receipt is missing handle`);return r.handle.wait(n?.wait)}async generateFromFile(e,n){let r=await this.createJobFromFile(e);if(!r.handle)throw new t(`Job receipt is missing handle`);return r.handle.wait(n?.wait)}async generateFromUrl(e,n){let r=await this.createJobFromUrl(e);if(!r.handle)throw new t(`Job receipt is missing handle`);return r.handle.wait(n?.wait)}makeHandle(e){let n=this;return{jobId:e,stream:t=>n.jobs.stream(e,t),async wait(r){let i=await n.jobs.wait(e,r);if(!i.reportId)throw new t(`Job ${e} succeeded but no reportId was returned`);return n.get(i.reportId)},cancel:()=>n.jobs.cancel(e),job:()=>n.jobs.get(e),report:()=>n.getByJob(e)}}defaultIdempotencyKey(e){return D(`idem`)}normalizeJobRequest(e){let t=e.target;if(!t)return e;let n={strategy:t.strategy};switch(t.onMiss&&(n.on_miss=t.onMiss),t.tags&&t.tags.length>0&&(n.tags=t.tags),t.excludeTags&&t.excludeTags.length>0&&(n.exclude_tags=t.excludeTags),t.strategy){case`dominant`:return{...e,target:n};case`timerange`:{let r=t.timeRange??{};return{...e,target:{...n,timerange:{start_seconds:r.startSeconds??null,end_seconds:r.endSeconds??null}}}}case`entity_id`:return{...e,target:{...n,entity_id:t.entityId}};case`magic_hint`:return{...e,target:{...n,hint:t.hint}};default:return e}}};function j(e,t,n){let r=new URL(t.replace(/^\//,``),e.endsWith(`/`)?e:`${e}/`);if(n)for(let[e,t]of Object.entries(n))t!==void 0&&r.searchParams.set(e,String(t));return r.toString()}async function M(e){let t=await e.text();if(!t)return{parsed:null,text:``};try{return{parsed:JSON.parse(t),text:t}}catch{return{parsed:t,text:t}}}function N(e,t){let o=e.headers.get(`x-request-id`)??void 0,s,c=`Request failed with status ${e.status}`,l=t;if(typeof t==`string`)c=t;else if(t&&typeof t==`object`){let e=t,n=e.error??e;if(n&&typeof n==`object`){let e=n;typeof e.message==`string`&&(c=e.message),typeof e.code==`string`&&(s=e.code),`details`in e&&(l=e.details)}}if(e.status===401||e.status===403)return new i(c,{status:e.status,requestId:o,code:s,details:l});if(e.status===422)return new a(c,{status:e.status,requestId:o,code:s,details:l});if(e.status===429){let t=new r(c,{status:e.status,requestId:o,code:s,details:l}),n=e.headers.get(`retry-after`);if(n){let e=Number(n);Number.isFinite(e)&&e>=0&&(t.retryAfterMs=e*1e3)}return t}return new n(c,{status:e.status,requestId:o,code:s,details:l})}function P(e,t){return e.retryable?t instanceof r?{retry:!0,retryAfterMs:t.retryAfterMs}:t instanceof n?{retry:t.status>=500&&t.status<=599}:t instanceof TypeError?{retry:!0}:{retry:!1}:{retry:!1}}var F=class{fetchImpl;constructor(e){this.opts=e,this.fetchImpl=e.fetch??fetch}async*streamSSE(e,n){let r=j(this.opts.baseUrl,e),i=D(`req`),a={Accept:`text/event-stream`,"Cache-Control":`no-cache`,"Mappa-Api-Key":this.opts.apiKey,"X-Request-Id":i,...this.opts.userAgent?{"User-Agent":this.opts.userAgent}:{},...this.opts.defaultHeaders??{}};n?.lastEventId&&(a[`Last-Event-ID`]=n.lastEventId);let o=new AbortController,s=setTimeout(()=>o.abort(E()),this.opts.timeoutMs);if(T(n?.signal)){let e=n?.signal;if(e?.aborted)throw clearTimeout(s),E();e?.addEventListener(`abort`,()=>o.abort(E()),{once:!0})}this.opts.telemetry?.onRequest?.({method:`GET`,url:r,requestId:i});let c;try{c=await this.fetchImpl(r,{method:`GET`,headers:a,signal:o.signal})}catch(e){throw clearTimeout(s),this.opts.telemetry?.onError?.({url:r,requestId:i,error:e}),e}if(!c.ok){clearTimeout(s);let{parsed:e}=await M(c),t=N(c,e);throw this.opts.telemetry?.onError?.({url:r,requestId:i,error:t}),t}if(!c.body)throw clearTimeout(s),new t(`SSE response has no body`);try{yield*this.parseSSEStream(c.body)}finally{clearTimeout(s)}}async*parseSSEStream(e){let t=new TextDecoder,n=e.getReader(),r=``;try{for(;;){let{done:e,value:i}=await n.read();if(e)break;r+=t.decode(i,{stream:!0});let a=r.split(`
2
+
3
+ `);r=a.pop()??``;for(let e of a){if(!e.trim())continue;let t=this.parseSSEEvent(e);t&&(yield t)}}if(r.trim()){let e=this.parseSSEEvent(r);e&&(yield e)}}finally{n.releaseLock()}}parseSSEEvent(e){let t=e.split(`
4
+ `),n,r=`message`,i=``;for(let e of t)e.startsWith(`id:`)?n=e.slice(3).trim():e.startsWith(`event:`)?r=e.slice(6).trim():e.startsWith(`data:`)&&(i&&(i+=`
5
+ `),i+=e.slice(5).trim());if(!i)return null;let a;try{a=JSON.parse(i)}catch{a=i}return{id:n,event:r,data:a}}async request(e){let t=j(this.opts.baseUrl,e.path,e.query),n=e.requestId??D(`req`),r={"Mappa-Api-Key":this.opts.apiKey,"X-Request-Id":n,...this.opts.userAgent?{"User-Agent":this.opts.userAgent}:{},...this.opts.defaultHeaders??{}};if(e.idempotencyKey&&(r[`Idempotency-Key`]=e.idempotencyKey),e.headers)for(let[t,n]of Object.entries(e.headers))n!==void 0&&(r[t]=n);let i=typeof FormData<`u`&&e.body instanceof FormData;e.body!==void 0&&!i&&(r[`Content-Type`]=`application/json`);let a=e.body===void 0?void 0:i?e.body:JSON.stringify(e.body),o=Math.max(0,this.opts.maxRetries),s=Date.now();for(let i=1;i<=1+o;i++){let c=new AbortController,l=setTimeout(()=>c.abort(E()),this.opts.timeoutMs);if(T(e.signal)){let t=e.signal;if(!t)throw clearTimeout(l),Error(`Unexpected: abort signal missing`);if(t.aborted)throw clearTimeout(l),E();t.addEventListener(`abort`,()=>c.abort(E()),{once:!0})}this.opts.telemetry?.onRequest?.({method:e.method,url:t,requestId:n});try{let l=await this.fetchImpl(t,{method:e.method,headers:r,body:a,signal:c.signal}),u=Date.now()-s,d=S(l.headers,`x-request-id`)??n;if(!l.ok){let{parsed:n}=await M(l),r=N(l,n);this.opts.telemetry?.onError?.({url:t,requestId:d,error:r});let a=P(e,r);if(i<=o+1&&a.retry&&i<=o){let e=a.retryAfterMs??C(w(i,500,4e3));await new Promise(t=>setTimeout(t,e));continue}throw r}let f=l.headers.get(`content-type`)??``,p;return p=f.includes(`application/json`)?await l.json():await l.text(),this.opts.telemetry?.onResponse?.({status:l.status,url:t,requestId:d,durationMs:u}),{data:p,status:l.status,requestId:d,headers:l.headers}}catch(r){this.opts.telemetry?.onError?.({url:t,requestId:n,error:r});let a=P(e,r);if(i<=o&&a.retry){let e=a.retryAfterMs??C(w(i,500,4e3));await new Promise(t=>setTimeout(t,e));continue}throw r}finally{clearTimeout(l)}}throw Error(`Unexpected transport exit`)}};function I(e){return typeof e==`object`&&!!e}var L=class{async verifySignature(e){let t=e.toleranceSec??300,n=R(e.headers,`mappa-signature`);if(!n)throw Error(`Missing mappa-signature header`);let r=z(n),i=Number(r.t);if(!Number.isFinite(i))throw Error(`Invalid signature timestamp`);let a=Math.floor(Date.now()/1e3);if(Math.abs(a-i)>t)throw Error(`Signature timestamp outside tolerance`);let o=`${r.t}.${e.payload}`;if(!H(await B(e.secret,o),r.v1))throw Error(`Invalid signature`);return{ok:!0}}parseEvent(e){let t=JSON.parse(e);if(!I(t))throw Error(`Invalid webhook payload: not an object`);let n=t,r=n.id,i=n.type,a=n.createdAt;if(typeof r!=`string`)throw Error(`Invalid webhook payload: id must be a string`);if(typeof i!=`string`)throw Error(`Invalid webhook payload: type must be a string`);if(typeof a!=`string`)throw Error(`Invalid webhook payload: createdAt must be a string`);return{id:r,type:i,createdAt:a,data:`data`in n?n.data:void 0}}};function R(e,t){let n=Object.keys(e).find(e=>e.toLowerCase()===t.toLowerCase()),r=n?e[n]:void 0;if(r)return Array.isArray(r)?r[0]:r}function z(e){let t={};for(let n of e.split(`,`)){let[e,r]=n.split(`=`);e&&r&&(t[e.trim()]=r.trim())}if(!t.t||!t.v1)throw Error(`Invalid signature format`);return{t:t.t,v1:t.v1}}async function B(e,t){let n=new TextEncoder,r=await crypto.subtle.importKey(`raw`,n.encode(e),{name:`HMAC`,hash:`SHA-256`},!1,[`sign`]);return V(await crypto.subtle.sign(`HMAC`,r,n.encode(t)))}function V(e){let t=new Uint8Array(e),n=``;for(let e of t)n+=e.toString(16).padStart(2,`0`);return n}function H(e,t){if(e.length!==t.length)return!1;let n=0;for(let r=0;r<e.length;r++)n|=e.charCodeAt(r)^t.charCodeAt(r);return n===0}var U=class e{files;jobs;reports;feedback;credits;entities;webhooks;health;transport;opts;constructor(e){if(!e.apiKey)throw new t(`apiKey is required`);let n=e.baseUrl??`https://api.mappa.ai`,r=e.timeoutMs??3e4,i=e.maxRetries??2;this.opts={...e,apiKey:e.apiKey,baseUrl:n,timeoutMs:r,maxRetries:i},this.transport=new F({apiKey:e.apiKey,baseUrl:n,timeoutMs:r,maxRetries:i,defaultHeaders:e.defaultHeaders,fetch:e.fetch,telemetry:e.telemetry,userAgent:e.userAgent}),this.files=new h(this.transport),this.jobs=new O(this.transport),this.reports=new A(this.transport,this.jobs,this.files,this.opts.fetch??fetch),this.feedback=new m(this.transport),this.credits=new c(this.transport),this.entities=new p(this.transport),this.webhooks=new L,this.health=new b(this.transport)}withOptions(t){return new e({...this.opts,...t,apiKey:t.apiKey??this.opts.apiKey})}close(){}};function W(e){return e.output.type===`markdown`}function G(e){return e.output.type===`json`}function K(e){return e.output.type===`pdf`}function q(e){return e.output.type===`url`}function J(e){return e.entity!==void 0&&e.entity!==null}function Y(e){return e instanceof t}exports.ApiError=n,exports.AuthError=i,exports.JobCanceledError=s,exports.JobFailedError=o,exports.Mappa=U,exports.MappaError=t,exports.RateLimitError=r,exports.ValidationError=a,exports.hasEntity=J,exports.isJsonReport=G,exports.isMappaError=Y,exports.isMarkdownReport=W,exports.isPdfReport=K,exports.isUrlReport=q;
package/dist/index.d.cts CHANGED
@@ -76,6 +76,21 @@ declare class JobCanceledError extends MappaError {
76
76
  }
77
77
  //#endregion
78
78
  //#region src/resources/transport.d.ts
79
+ /**
80
+ * Options for SSE streaming.
81
+ */
82
+ type SSEStreamOptions = {
83
+ signal?: AbortSignal;
84
+ lastEventId?: string;
85
+ };
86
+ /**
87
+ * A parsed SSE event.
88
+ */
89
+ type SSEEvent<T = unknown> = {
90
+ id?: string;
91
+ event: string;
92
+ data: T;
93
+ };
79
94
  type Telemetry = {
80
95
  onRequest?: (ctx: {
81
96
  method: string;
@@ -125,6 +140,30 @@ declare class Transport {
125
140
  private readonly opts;
126
141
  private readonly fetchImpl;
127
142
  constructor(opts: TransportOptions);
143
+ /**
144
+ * Stream SSE events from a given path.
145
+ *
146
+ * Uses native `fetch` with streaming response body (not browser-only `EventSource`).
147
+ * Parses SSE format manually from the `ReadableStream`.
148
+ */
149
+ streamSSE<T>(path: string, opts?: SSEStreamOptions): AsyncGenerator<SSEEvent<T>>;
150
+ /**
151
+ * Parse SSE events from a ReadableStream.
152
+ *
153
+ * SSE format:
154
+ * ```
155
+ * id: <id>
156
+ * event: <type>
157
+ * data: <json>
158
+ *
159
+ * ```
160
+ * Each event is terminated by an empty line.
161
+ */
162
+ private parseSSEStream;
163
+ /**
164
+ * Parse a single SSE event from text.
165
+ */
166
+ private parseSSEEvent;
128
167
  request<T>(req: RequestOptions): Promise<TransportResponse<T>>;
129
168
  }
130
169
  //#endregion
@@ -195,6 +234,23 @@ type TargetBase = {
195
234
  * Behavior when the entity is not found.
196
235
  */
197
236
  onMiss?: TargetOnMiss;
237
+ /**
238
+ * Tags to apply to the selected entity after job completion.
239
+ *
240
+ * Tags must be 1-64 characters, alphanumeric with underscores and hyphens only.
241
+ * Maximum 10 tags per request.
242
+ *
243
+ * @example ["interviewer", "sales-rep", "round-1"]
244
+ */
245
+ tags?: string[];
246
+ /**
247
+ * Exclude speakers whose entities have ANY of these tags from selection.
248
+ *
249
+ * Useful for filtering out known interviewers, hosts, etc.
250
+ *
251
+ * @example ["interviewer", "host"]
252
+ */
253
+ excludeTags?: string[];
198
254
  };
199
255
  type TargetDominant = TargetBase & {
200
256
  strategy: "dominant";
@@ -320,6 +376,10 @@ type ReportBase = {
320
376
  url?: string;
321
377
  mediaId?: string;
322
378
  };
379
+ entity: {
380
+ id: string;
381
+ tags: string[];
382
+ };
323
383
  usage?: Usage;
324
384
  metrics?: Record<string, JsonValue>;
325
385
  raw?: JsonValue;
@@ -468,7 +528,7 @@ type CreditUsage = {
468
528
  modelVersion?: string;
469
529
  };
470
530
  /**
471
- * Options for long-polling job completion.
531
+ * Options for waiting on job completion.
472
532
  */
473
533
  type WaitOptions = {
474
534
  /**
@@ -477,18 +537,6 @@ type WaitOptions = {
477
537
  * @defaultValue 300000
478
538
  */
479
539
  timeoutMs?: number;
480
- /**
481
- * Initial polling interval.
482
- *
483
- * @defaultValue 1000
484
- */
485
- pollIntervalMs?: number;
486
- /**
487
- * Maximum polling interval used with exponential backoff.
488
- *
489
- * @defaultValue 10000
490
- */
491
- maxPollIntervalMs?: number;
492
540
  /**
493
541
  * Optional callback invoked on meaningful job state transitions.
494
542
  */
@@ -525,6 +573,39 @@ declare function isPdfReport(report: Report): report is PdfReport;
525
573
  * Type guard for UrlReport.
526
574
  */
527
575
  declare function isUrlReport(report: Report): report is UrlReport;
576
+ type Entity = {
577
+ id: string;
578
+ tags: string[];
579
+ createdAt: string;
580
+ mediaCount: number;
581
+ lastSeenAt: string | null;
582
+ };
583
+ type EntityTagsResult = {
584
+ entityId: string;
585
+ tags: string[];
586
+ };
587
+ type ListEntitiesOptions = CursorPaginationParams & {
588
+ /**
589
+ * Filter entities by tags.
590
+ * Entities must have ALL specified tags (AND logic).
591
+ */
592
+ tags?: string[];
593
+ };
594
+ type ListEntitiesResponse = {
595
+ entities: Entity[];
596
+ cursor?: string;
597
+ hasMore: boolean;
598
+ };
599
+ /**
600
+ * Type guard to check if a report has entity information.
601
+ * Always returns true since entity is always present in reports.
602
+ */
603
+ declare function hasEntity(report: Report): report is Report & {
604
+ entity: {
605
+ id: string;
606
+ tags: string[];
607
+ };
608
+ };
528
609
  //#endregion
529
610
  //#region src/resources/credits.d.ts
530
611
  type ListTransactionsOptions = {
@@ -616,6 +697,178 @@ declare class CreditsResource {
616
697
  }): Promise<number>;
617
698
  }
618
699
  //#endregion
700
+ //#region src/resources/entities.d.ts
701
+ /**
702
+ * Entities API resource.
703
+ *
704
+ * Responsibilities:
705
+ * - List entities with optional tag filtering (`GET /v1/entities`)
706
+ * - Get single entity details (`GET /v1/entities/:entityId`)
707
+ * - Add tags to entities (`POST /v1/entities/:entityId/tags`)
708
+ * - Remove tags from entities (`DELETE /v1/entities/:entityId/tags`)
709
+ * - Replace all entity tags (`PUT /v1/entities/:entityId/tags`)
710
+ *
711
+ * Entities represent analyzed speakers identified by voice fingerprints.
712
+ * Tags allow you to label entities (e.g., "interviewer", "sales-rep") for easier
713
+ * filtering and identification across multiple reports.
714
+ */
715
+ declare class EntitiesResource {
716
+ private readonly transport;
717
+ constructor(transport: Transport);
718
+ /**
719
+ * Get a single entity by ID.
720
+ *
721
+ * Returns entity metadata including tags, creation time, and usage statistics.
722
+ *
723
+ * @param entityId - The entity ID to retrieve
724
+ * @param opts - Optional request options (requestId, signal)
725
+ * @returns Entity details with tags and metadata
726
+ *
727
+ * @example
728
+ * ```typescript
729
+ * const entity = await mappa.entities.get("entity_abc123");
730
+ * console.log(entity.tags); // ["interviewer", "john"]
731
+ * ```
732
+ */
733
+ get(entityId: string, opts?: {
734
+ requestId?: string;
735
+ signal?: AbortSignal;
736
+ }): Promise<Entity>;
737
+ /**
738
+ * List entities with optional tag filtering.
739
+ *
740
+ * Supports cursor-based pagination. Use the returned `cursor` for fetching
741
+ * subsequent pages, or use {@link listAll} for automatic pagination.
742
+ *
743
+ * When `tags` is provided, only entities with ALL specified tags are returned (AND logic).
744
+ *
745
+ * @param opts - List options: tags filter, cursor, limit
746
+ * @returns Paginated list of entities
747
+ *
748
+ * @example
749
+ * ```typescript
750
+ * // List all entities
751
+ * const page1 = await mappa.entities.list({ limit: 20 });
752
+ *
753
+ * // Filter by tags (must have both "interviewer" AND "sales")
754
+ * const filtered = await mappa.entities.list({
755
+ * tags: ["interviewer", "sales"],
756
+ * limit: 50
757
+ * });
758
+ *
759
+ * // Pagination
760
+ * const page2 = await mappa.entities.list({
761
+ * cursor: page1.cursor,
762
+ * limit: 20
763
+ * });
764
+ * ```
765
+ */
766
+ list(opts?: ListEntitiesOptions & {
767
+ requestId?: string;
768
+ signal?: AbortSignal;
769
+ }): Promise<ListEntitiesResponse>;
770
+ /**
771
+ * Async iterator that automatically paginates through all entities.
772
+ *
773
+ * Useful for processing large entity sets without manual pagination management.
774
+ *
775
+ * @param opts - List options: tags filter, limit per page
776
+ * @yields Individual entities
777
+ *
778
+ * @example
779
+ * ```typescript
780
+ * // Process all entities with "interviewer" tag
781
+ * for await (const entity of mappa.entities.listAll({ tags: ["interviewer"] })) {
782
+ * console.log(`${entity.id}: ${entity.tags.join(", ")}`);
783
+ * }
784
+ * ```
785
+ */
786
+ listAll(opts?: Omit<ListEntitiesOptions, "cursor"> & {
787
+ requestId?: string;
788
+ signal?: AbortSignal;
789
+ }): AsyncIterable<Entity>;
790
+ /**
791
+ * Get all entities with a specific tag.
792
+ *
793
+ * Convenience wrapper around {@link list} for single-tag filtering.
794
+ *
795
+ * @param tag - The tag to filter by
796
+ * @param opts - Optional pagination and request options
797
+ * @returns Paginated list of entities with the specified tag
798
+ *
799
+ * @example
800
+ * ```typescript
801
+ * const interviewers = await mappa.entities.getByTag("interviewer");
802
+ * ```
803
+ */
804
+ getByTag(tag: string, opts?: Omit<ListEntitiesOptions, "tags"> & {
805
+ requestId?: string;
806
+ signal?: AbortSignal;
807
+ }): Promise<ListEntitiesResponse>;
808
+ /**
809
+ * Add tags to an entity.
810
+ *
811
+ * Idempotent: existing tags are preserved, duplicates are ignored.
812
+ *
813
+ * @param entityId - The entity ID to tag
814
+ * @param tags - Array of tags to add (1-10 tags, each 1-64 chars)
815
+ * @param opts - Optional request options
816
+ * @returns Updated tags for the entity
817
+ *
818
+ * @example
819
+ * ```typescript
820
+ * await mappa.entities.addTags("entity_abc123", ["interviewer", "john"]);
821
+ * ```
822
+ */
823
+ addTags(entityId: string, tags: string[], opts?: {
824
+ requestId?: string;
825
+ signal?: AbortSignal;
826
+ }): Promise<EntityTagsResult>;
827
+ /**
828
+ * Remove tags from an entity.
829
+ *
830
+ * Idempotent: missing tags are silently ignored.
831
+ *
832
+ * @param entityId - The entity ID to update
833
+ * @param tags - Array of tags to remove
834
+ * @param opts - Optional request options
835
+ * @returns Updated tags for the entity
836
+ *
837
+ * @example
838
+ * ```typescript
839
+ * await mappa.entities.removeTags("entity_abc123", ["interviewer"]);
840
+ * ```
841
+ */
842
+ removeTags(entityId: string, tags: string[], opts?: {
843
+ requestId?: string;
844
+ signal?: AbortSignal;
845
+ }): Promise<EntityTagsResult>;
846
+ /**
847
+ * Replace all tags on an entity.
848
+ *
849
+ * Sets the complete tag list, removing any tags not in the provided array.
850
+ * Pass an empty array to remove all tags.
851
+ *
852
+ * @param entityId - The entity ID to update
853
+ * @param tags - New complete tag list (0-10 tags)
854
+ * @param opts - Optional request options
855
+ * @returns Updated tags for the entity
856
+ *
857
+ * @example
858
+ * ```typescript
859
+ * // Replace all tags
860
+ * await mappa.entities.setTags("entity_abc123", ["sales-rep", "john"]);
861
+ *
862
+ * // Remove all tags
863
+ * await mappa.entities.setTags("entity_abc123", []);
864
+ * ```
865
+ */
866
+ setTags(entityId: string, tags: string[], opts?: {
867
+ requestId?: string;
868
+ signal?: AbortSignal;
869
+ }): Promise<EntityTagsResult>;
870
+ }
871
+ //#endregion
619
872
  //#region src/resources/feedback.d.ts
620
873
  type FeedbackCreateRequest = {
621
874
  reportId?: string;
@@ -758,16 +1011,30 @@ declare class JobsResource {
758
1011
  requestId?: string;
759
1012
  signal?: AbortSignal;
760
1013
  }): Promise<Job>;
1014
+ /**
1015
+ * Wait for a job to reach a terminal state.
1016
+ *
1017
+ * Uses SSE streaming internally for efficient real-time updates.
1018
+ */
761
1019
  wait(jobId: string, opts?: WaitOptions): Promise<Job>;
762
1020
  /**
763
- * Public stream API.
764
- * If you add SSE later, keep this signature and switch implementation internally.
765
- * For now, it yields events based on polling. Use `AbortSignal` to cancel streaming.
1021
+ * Stream job events via SSE.
1022
+ *
1023
+ * Yields events as they arrive from the server. Use `AbortSignal` to cancel streaming.
1024
+ * Automatically handles reconnection with `Last-Event-ID` for up to 3 retries.
766
1025
  */
767
1026
  stream(jobId: string, opts?: {
768
1027
  signal?: AbortSignal;
769
1028
  onEvent?: (e: JobEvent) => void;
770
1029
  }): AsyncIterable<JobEvent>;
1030
+ /**
1031
+ * Map an SSE event to a JobEvent.
1032
+ */
1033
+ private mapSSEToJobEvent;
1034
+ /**
1035
+ * Exponential backoff with jitter for reconnection.
1036
+ */
1037
+ private backoff;
771
1038
  }
772
1039
  //#endregion
773
1040
  //#region src/resources/reports.d.ts
@@ -1003,6 +1270,7 @@ declare class Mappa {
1003
1270
  readonly reports: ReportsResource;
1004
1271
  readonly feedback: FeedbackResource;
1005
1272
  readonly credits: CreditsResource;
1273
+ readonly entities: EntitiesResource;
1006
1274
  readonly webhooks: WebhooksResource;
1007
1275
  readonly health: HealthResource;
1008
1276
  private readonly transport;
@@ -1018,4 +1286,4 @@ declare class Mappa {
1018
1286
  */
1019
1287
  declare function isMappaError(err: unknown): err is MappaError;
1020
1288
  //#endregion
1021
- export { ApiError, AuthError, CreditBalance, CreditTransaction, CreditTransactionType, CreditUsage, CursorPage, CursorPaginationParams, FeedbackReceipt, FileDeleteReceipt, Job, JobCanceledError, JobCreditReservation, JobEvent, JobFailedError, JobStage, JobStatus, JsonReport, JsonValue, Mappa, MappaError, MarkdownReport, MediaFile, MediaIdRef, MediaObject, MediaProcessingStatus, MediaRef, MediaRetention, OffsetPage, OffsetPaginationParams, PdfReport, RateLimitError, Report, ReportBase, ReportCreateJobRequest, ReportJobReceipt, ReportOutput, ReportOutputType, ReportRunHandle, ReportTemplateId, ReportTemplateParamsMap, RetentionLockResult, Subject, TargetDominant, TargetEntityId, TargetFor, TargetMagicHint, TargetOnMiss, TargetSelector, TargetStrategy, TargetStrategyMap, TargetTimeRange, TargetTimeRangeStrategy, UrlReport, Usage, ValidationError, WaitOptions, WebhookConfig, isJsonReport, isMappaError, isMarkdownReport, isPdfReport, isUrlReport };
1289
+ export { ApiError, AuthError, CreditBalance, CreditTransaction, CreditTransactionType, CreditUsage, CursorPage, CursorPaginationParams, Entity, EntityTagsResult, FeedbackReceipt, FileDeleteReceipt, Job, JobCanceledError, JobCreditReservation, JobEvent, JobFailedError, JobStage, JobStatus, JsonReport, JsonValue, ListEntitiesOptions, ListEntitiesResponse, Mappa, MappaError, MarkdownReport, MediaFile, MediaIdRef, MediaObject, MediaProcessingStatus, MediaRef, MediaRetention, OffsetPage, OffsetPaginationParams, PdfReport, RateLimitError, Report, ReportBase, ReportCreateJobRequest, ReportJobReceipt, ReportOutput, ReportOutputType, ReportRunHandle, ReportTemplateId, ReportTemplateParamsMap, RetentionLockResult, Subject, TargetDominant, TargetEntityId, TargetFor, TargetMagicHint, TargetOnMiss, TargetSelector, TargetStrategy, TargetStrategyMap, TargetTimeRange, TargetTimeRangeStrategy, UrlReport, Usage, ValidationError, WaitOptions, WebhookConfig, hasEntity, isJsonReport, isMappaError, isMarkdownReport, isPdfReport, isUrlReport };
package/dist/index.d.mts CHANGED
@@ -76,6 +76,21 @@ declare class JobCanceledError extends MappaError {
76
76
  }
77
77
  //#endregion
78
78
  //#region src/resources/transport.d.ts
79
+ /**
80
+ * Options for SSE streaming.
81
+ */
82
+ type SSEStreamOptions = {
83
+ signal?: AbortSignal;
84
+ lastEventId?: string;
85
+ };
86
+ /**
87
+ * A parsed SSE event.
88
+ */
89
+ type SSEEvent<T = unknown> = {
90
+ id?: string;
91
+ event: string;
92
+ data: T;
93
+ };
79
94
  type Telemetry = {
80
95
  onRequest?: (ctx: {
81
96
  method: string;
@@ -125,6 +140,30 @@ declare class Transport {
125
140
  private readonly opts;
126
141
  private readonly fetchImpl;
127
142
  constructor(opts: TransportOptions);
143
+ /**
144
+ * Stream SSE events from a given path.
145
+ *
146
+ * Uses native `fetch` with streaming response body (not browser-only `EventSource`).
147
+ * Parses SSE format manually from the `ReadableStream`.
148
+ */
149
+ streamSSE<T>(path: string, opts?: SSEStreamOptions): AsyncGenerator<SSEEvent<T>>;
150
+ /**
151
+ * Parse SSE events from a ReadableStream.
152
+ *
153
+ * SSE format:
154
+ * ```
155
+ * id: <id>
156
+ * event: <type>
157
+ * data: <json>
158
+ *
159
+ * ```
160
+ * Each event is terminated by an empty line.
161
+ */
162
+ private parseSSEStream;
163
+ /**
164
+ * Parse a single SSE event from text.
165
+ */
166
+ private parseSSEEvent;
128
167
  request<T>(req: RequestOptions): Promise<TransportResponse<T>>;
129
168
  }
130
169
  //#endregion
@@ -195,6 +234,23 @@ type TargetBase = {
195
234
  * Behavior when the entity is not found.
196
235
  */
197
236
  onMiss?: TargetOnMiss;
237
+ /**
238
+ * Tags to apply to the selected entity after job completion.
239
+ *
240
+ * Tags must be 1-64 characters, alphanumeric with underscores and hyphens only.
241
+ * Maximum 10 tags per request.
242
+ *
243
+ * @example ["interviewer", "sales-rep", "round-1"]
244
+ */
245
+ tags?: string[];
246
+ /**
247
+ * Exclude speakers whose entities have ANY of these tags from selection.
248
+ *
249
+ * Useful for filtering out known interviewers, hosts, etc.
250
+ *
251
+ * @example ["interviewer", "host"]
252
+ */
253
+ excludeTags?: string[];
198
254
  };
199
255
  type TargetDominant = TargetBase & {
200
256
  strategy: "dominant";
@@ -320,6 +376,10 @@ type ReportBase = {
320
376
  url?: string;
321
377
  mediaId?: string;
322
378
  };
379
+ entity: {
380
+ id: string;
381
+ tags: string[];
382
+ };
323
383
  usage?: Usage;
324
384
  metrics?: Record<string, JsonValue>;
325
385
  raw?: JsonValue;
@@ -468,7 +528,7 @@ type CreditUsage = {
468
528
  modelVersion?: string;
469
529
  };
470
530
  /**
471
- * Options for long-polling job completion.
531
+ * Options for waiting on job completion.
472
532
  */
473
533
  type WaitOptions = {
474
534
  /**
@@ -477,18 +537,6 @@ type WaitOptions = {
477
537
  * @defaultValue 300000
478
538
  */
479
539
  timeoutMs?: number;
480
- /**
481
- * Initial polling interval.
482
- *
483
- * @defaultValue 1000
484
- */
485
- pollIntervalMs?: number;
486
- /**
487
- * Maximum polling interval used with exponential backoff.
488
- *
489
- * @defaultValue 10000
490
- */
491
- maxPollIntervalMs?: number;
492
540
  /**
493
541
  * Optional callback invoked on meaningful job state transitions.
494
542
  */
@@ -525,6 +573,39 @@ declare function isPdfReport(report: Report): report is PdfReport;
525
573
  * Type guard for UrlReport.
526
574
  */
527
575
  declare function isUrlReport(report: Report): report is UrlReport;
576
+ type Entity = {
577
+ id: string;
578
+ tags: string[];
579
+ createdAt: string;
580
+ mediaCount: number;
581
+ lastSeenAt: string | null;
582
+ };
583
+ type EntityTagsResult = {
584
+ entityId: string;
585
+ tags: string[];
586
+ };
587
+ type ListEntitiesOptions = CursorPaginationParams & {
588
+ /**
589
+ * Filter entities by tags.
590
+ * Entities must have ALL specified tags (AND logic).
591
+ */
592
+ tags?: string[];
593
+ };
594
+ type ListEntitiesResponse = {
595
+ entities: Entity[];
596
+ cursor?: string;
597
+ hasMore: boolean;
598
+ };
599
+ /**
600
+ * Type guard to check if a report has entity information.
601
+ * Always returns true since entity is always present in reports.
602
+ */
603
+ declare function hasEntity(report: Report): report is Report & {
604
+ entity: {
605
+ id: string;
606
+ tags: string[];
607
+ };
608
+ };
528
609
  //#endregion
529
610
  //#region src/resources/credits.d.ts
530
611
  type ListTransactionsOptions = {
@@ -616,6 +697,178 @@ declare class CreditsResource {
616
697
  }): Promise<number>;
617
698
  }
618
699
  //#endregion
700
+ //#region src/resources/entities.d.ts
701
+ /**
702
+ * Entities API resource.
703
+ *
704
+ * Responsibilities:
705
+ * - List entities with optional tag filtering (`GET /v1/entities`)
706
+ * - Get single entity details (`GET /v1/entities/:entityId`)
707
+ * - Add tags to entities (`POST /v1/entities/:entityId/tags`)
708
+ * - Remove tags from entities (`DELETE /v1/entities/:entityId/tags`)
709
+ * - Replace all entity tags (`PUT /v1/entities/:entityId/tags`)
710
+ *
711
+ * Entities represent analyzed speakers identified by voice fingerprints.
712
+ * Tags allow you to label entities (e.g., "interviewer", "sales-rep") for easier
713
+ * filtering and identification across multiple reports.
714
+ */
715
+ declare class EntitiesResource {
716
+ private readonly transport;
717
+ constructor(transport: Transport);
718
+ /**
719
+ * Get a single entity by ID.
720
+ *
721
+ * Returns entity metadata including tags, creation time, and usage statistics.
722
+ *
723
+ * @param entityId - The entity ID to retrieve
724
+ * @param opts - Optional request options (requestId, signal)
725
+ * @returns Entity details with tags and metadata
726
+ *
727
+ * @example
728
+ * ```typescript
729
+ * const entity = await mappa.entities.get("entity_abc123");
730
+ * console.log(entity.tags); // ["interviewer", "john"]
731
+ * ```
732
+ */
733
+ get(entityId: string, opts?: {
734
+ requestId?: string;
735
+ signal?: AbortSignal;
736
+ }): Promise<Entity>;
737
+ /**
738
+ * List entities with optional tag filtering.
739
+ *
740
+ * Supports cursor-based pagination. Use the returned `cursor` for fetching
741
+ * subsequent pages, or use {@link listAll} for automatic pagination.
742
+ *
743
+ * When `tags` is provided, only entities with ALL specified tags are returned (AND logic).
744
+ *
745
+ * @param opts - List options: tags filter, cursor, limit
746
+ * @returns Paginated list of entities
747
+ *
748
+ * @example
749
+ * ```typescript
750
+ * // List all entities
751
+ * const page1 = await mappa.entities.list({ limit: 20 });
752
+ *
753
+ * // Filter by tags (must have both "interviewer" AND "sales")
754
+ * const filtered = await mappa.entities.list({
755
+ * tags: ["interviewer", "sales"],
756
+ * limit: 50
757
+ * });
758
+ *
759
+ * // Pagination
760
+ * const page2 = await mappa.entities.list({
761
+ * cursor: page1.cursor,
762
+ * limit: 20
763
+ * });
764
+ * ```
765
+ */
766
+ list(opts?: ListEntitiesOptions & {
767
+ requestId?: string;
768
+ signal?: AbortSignal;
769
+ }): Promise<ListEntitiesResponse>;
770
+ /**
771
+ * Async iterator that automatically paginates through all entities.
772
+ *
773
+ * Useful for processing large entity sets without manual pagination management.
774
+ *
775
+ * @param opts - List options: tags filter, limit per page
776
+ * @yields Individual entities
777
+ *
778
+ * @example
779
+ * ```typescript
780
+ * // Process all entities with "interviewer" tag
781
+ * for await (const entity of mappa.entities.listAll({ tags: ["interviewer"] })) {
782
+ * console.log(`${entity.id}: ${entity.tags.join(", ")}`);
783
+ * }
784
+ * ```
785
+ */
786
+ listAll(opts?: Omit<ListEntitiesOptions, "cursor"> & {
787
+ requestId?: string;
788
+ signal?: AbortSignal;
789
+ }): AsyncIterable<Entity>;
790
+ /**
791
+ * Get all entities with a specific tag.
792
+ *
793
+ * Convenience wrapper around {@link list} for single-tag filtering.
794
+ *
795
+ * @param tag - The tag to filter by
796
+ * @param opts - Optional pagination and request options
797
+ * @returns Paginated list of entities with the specified tag
798
+ *
799
+ * @example
800
+ * ```typescript
801
+ * const interviewers = await mappa.entities.getByTag("interviewer");
802
+ * ```
803
+ */
804
+ getByTag(tag: string, opts?: Omit<ListEntitiesOptions, "tags"> & {
805
+ requestId?: string;
806
+ signal?: AbortSignal;
807
+ }): Promise<ListEntitiesResponse>;
808
+ /**
809
+ * Add tags to an entity.
810
+ *
811
+ * Idempotent: existing tags are preserved, duplicates are ignored.
812
+ *
813
+ * @param entityId - The entity ID to tag
814
+ * @param tags - Array of tags to add (1-10 tags, each 1-64 chars)
815
+ * @param opts - Optional request options
816
+ * @returns Updated tags for the entity
817
+ *
818
+ * @example
819
+ * ```typescript
820
+ * await mappa.entities.addTags("entity_abc123", ["interviewer", "john"]);
821
+ * ```
822
+ */
823
+ addTags(entityId: string, tags: string[], opts?: {
824
+ requestId?: string;
825
+ signal?: AbortSignal;
826
+ }): Promise<EntityTagsResult>;
827
+ /**
828
+ * Remove tags from an entity.
829
+ *
830
+ * Idempotent: missing tags are silently ignored.
831
+ *
832
+ * @param entityId - The entity ID to update
833
+ * @param tags - Array of tags to remove
834
+ * @param opts - Optional request options
835
+ * @returns Updated tags for the entity
836
+ *
837
+ * @example
838
+ * ```typescript
839
+ * await mappa.entities.removeTags("entity_abc123", ["interviewer"]);
840
+ * ```
841
+ */
842
+ removeTags(entityId: string, tags: string[], opts?: {
843
+ requestId?: string;
844
+ signal?: AbortSignal;
845
+ }): Promise<EntityTagsResult>;
846
+ /**
847
+ * Replace all tags on an entity.
848
+ *
849
+ * Sets the complete tag list, removing any tags not in the provided array.
850
+ * Pass an empty array to remove all tags.
851
+ *
852
+ * @param entityId - The entity ID to update
853
+ * @param tags - New complete tag list (0-10 tags)
854
+ * @param opts - Optional request options
855
+ * @returns Updated tags for the entity
856
+ *
857
+ * @example
858
+ * ```typescript
859
+ * // Replace all tags
860
+ * await mappa.entities.setTags("entity_abc123", ["sales-rep", "john"]);
861
+ *
862
+ * // Remove all tags
863
+ * await mappa.entities.setTags("entity_abc123", []);
864
+ * ```
865
+ */
866
+ setTags(entityId: string, tags: string[], opts?: {
867
+ requestId?: string;
868
+ signal?: AbortSignal;
869
+ }): Promise<EntityTagsResult>;
870
+ }
871
+ //#endregion
619
872
  //#region src/resources/feedback.d.ts
620
873
  type FeedbackCreateRequest = {
621
874
  reportId?: string;
@@ -758,16 +1011,30 @@ declare class JobsResource {
758
1011
  requestId?: string;
759
1012
  signal?: AbortSignal;
760
1013
  }): Promise<Job>;
1014
+ /**
1015
+ * Wait for a job to reach a terminal state.
1016
+ *
1017
+ * Uses SSE streaming internally for efficient real-time updates.
1018
+ */
761
1019
  wait(jobId: string, opts?: WaitOptions): Promise<Job>;
762
1020
  /**
763
- * Public stream API.
764
- * If you add SSE later, keep this signature and switch implementation internally.
765
- * For now, it yields events based on polling. Use `AbortSignal` to cancel streaming.
1021
+ * Stream job events via SSE.
1022
+ *
1023
+ * Yields events as they arrive from the server. Use `AbortSignal` to cancel streaming.
1024
+ * Automatically handles reconnection with `Last-Event-ID` for up to 3 retries.
766
1025
  */
767
1026
  stream(jobId: string, opts?: {
768
1027
  signal?: AbortSignal;
769
1028
  onEvent?: (e: JobEvent) => void;
770
1029
  }): AsyncIterable<JobEvent>;
1030
+ /**
1031
+ * Map an SSE event to a JobEvent.
1032
+ */
1033
+ private mapSSEToJobEvent;
1034
+ /**
1035
+ * Exponential backoff with jitter for reconnection.
1036
+ */
1037
+ private backoff;
771
1038
  }
772
1039
  //#endregion
773
1040
  //#region src/resources/reports.d.ts
@@ -1003,6 +1270,7 @@ declare class Mappa {
1003
1270
  readonly reports: ReportsResource;
1004
1271
  readonly feedback: FeedbackResource;
1005
1272
  readonly credits: CreditsResource;
1273
+ readonly entities: EntitiesResource;
1006
1274
  readonly webhooks: WebhooksResource;
1007
1275
  readonly health: HealthResource;
1008
1276
  private readonly transport;
@@ -1018,4 +1286,4 @@ declare class Mappa {
1018
1286
  */
1019
1287
  declare function isMappaError(err: unknown): err is MappaError;
1020
1288
  //#endregion
1021
- export { ApiError, AuthError, CreditBalance, CreditTransaction, CreditTransactionType, CreditUsage, CursorPage, CursorPaginationParams, FeedbackReceipt, FileDeleteReceipt, Job, JobCanceledError, JobCreditReservation, JobEvent, JobFailedError, JobStage, JobStatus, JsonReport, JsonValue, Mappa, MappaError, MarkdownReport, MediaFile, MediaIdRef, MediaObject, MediaProcessingStatus, MediaRef, MediaRetention, OffsetPage, OffsetPaginationParams, PdfReport, RateLimitError, Report, ReportBase, ReportCreateJobRequest, ReportJobReceipt, ReportOutput, ReportOutputType, ReportRunHandle, ReportTemplateId, ReportTemplateParamsMap, RetentionLockResult, Subject, TargetDominant, TargetEntityId, TargetFor, TargetMagicHint, TargetOnMiss, TargetSelector, TargetStrategy, TargetStrategyMap, TargetTimeRange, TargetTimeRangeStrategy, UrlReport, Usage, ValidationError, WaitOptions, WebhookConfig, isJsonReport, isMappaError, isMarkdownReport, isPdfReport, isUrlReport };
1289
+ export { ApiError, AuthError, CreditBalance, CreditTransaction, CreditTransactionType, CreditUsage, CursorPage, CursorPaginationParams, Entity, EntityTagsResult, FeedbackReceipt, FileDeleteReceipt, Job, JobCanceledError, JobCreditReservation, JobEvent, JobFailedError, JobStage, JobStatus, JsonReport, JsonValue, ListEntitiesOptions, ListEntitiesResponse, Mappa, MappaError, MarkdownReport, MediaFile, MediaIdRef, MediaObject, MediaProcessingStatus, MediaRef, MediaRetention, OffsetPage, OffsetPaginationParams, PdfReport, RateLimitError, Report, ReportBase, ReportCreateJobRequest, ReportJobReceipt, ReportOutput, ReportOutputType, ReportRunHandle, ReportTemplateId, ReportTemplateParamsMap, RetentionLockResult, Subject, TargetDominant, TargetEntityId, TargetFor, TargetMagicHint, TargetOnMiss, TargetSelector, TargetStrategy, TargetStrategyMap, TargetTimeRange, TargetTimeRangeStrategy, UrlReport, Usage, ValidationError, WaitOptions, WebhookConfig, hasEntity, isJsonReport, isMappaError, isMarkdownReport, isPdfReport, isUrlReport };
package/dist/index.mjs CHANGED
@@ -1 +1,5 @@
1
- import{init as e}from"@paralleldrive/cuid2";var t=class extends Error{name=`MappaError`;requestId;code;constructor(e,t){super(e),this.requestId=t?.requestId,this.code=t?.code,this.cause=t?.cause}},n=class extends t{name=`ApiError`;status;details;constructor(e,t){super(e,{requestId:t.requestId,code:t.code}),this.status=t.status,this.details=t.details}},r=class extends n{name=`RateLimitError`;retryAfterMs},i=class extends n{name=`AuthError`},a=class extends n{name=`ValidationError`},o=class extends t{name=`JobFailedError`;jobId;constructor(e,t,n){super(t,n),this.jobId=e}},s=class extends t{name=`JobCanceledError`;jobId;constructor(e,t,n){super(t,n),this.jobId=e}},c=class{constructor(e){this.transport=e}async getBalance(e){return(await this.transport.request({method:`GET`,path:`/v1/credits/balance`,requestId:e?.requestId,signal:e?.signal,retryable:!0})).data}async listTransactions(e){let t={};return e?.limit!==void 0&&(t.limit=String(e.limit)),e?.offset!==void 0&&(t.offset=String(e.offset)),(await this.transport.request({method:`GET`,path:`/v1/credits/transactions`,query:t,requestId:e?.requestId,signal:e?.signal,retryable:!0})).data}async*listAllTransactions(e){let t=0,n=e?.limit??50;for(;;){let r=await this.listTransactions({...e,limit:n,offset:t});for(let e of r.transactions)yield e;if(t+=r.transactions.length,t>=r.pagination.total)break}}async getJobUsage(e,n){if(!e)throw new t(`jobId is required`);return(await this.transport.request({method:`GET`,path:`/v1/credits/usage/${encodeURIComponent(e)}`,requestId:n?.requestId,signal:n?.signal,retryable:!0})).data}async hasEnough(e,t){return(await this.getBalance(t)).available>=e}async getAvailable(e){return(await this.getBalance(e)).available}},l=class{constructor(e){this.transport=e}async create(e){if(!!e.reportId==!!e.jobId)throw new t(`Provide exactly one of reportId or jobId`);return(await this.transport.request({method:`POST`,path:`/v1/feedback`,body:e,idempotencyKey:e.idempotencyKey,requestId:e.requestId,signal:e.signal,retryable:!0})).data}},u=class{constructor(e){this.transport=e}async upload(e){if(typeof FormData>`u`)throw new t(`FormData is not available in this runtime; cannot perform multipart upload`);let n=d(e.file,e.filename),r=e.contentType??n;if(!r)throw new t(`contentType is required when it cannot be inferred from file.type or filename`);let i=e.filename??f(e.file)??`upload`,a=await m(e.file,r),o=new FormData;return o.append(`file`,a,i),o.append(`contentType`,r),e.filename&&o.append(`filename`,e.filename),(await this.transport.request({method:`POST`,path:`/v1/files`,body:o,idempotencyKey:e.idempotencyKey,requestId:e.requestId,signal:e.signal,retryable:!0})).data}async get(e,n){if(!e)throw new t(`mediaId is required`);return(await this.transport.request({method:`GET`,path:`/v1/files/${encodeURIComponent(e)}`,requestId:n?.requestId,signal:n?.signal,retryable:!0})).data}async list(e){let t={};return e?.limit!==void 0&&(t.limit=String(e.limit)),e?.cursor&&(t.cursor=e.cursor),e?.includeDeleted!==void 0&&(t.includeDeleted=String(e.includeDeleted)),(await this.transport.request({method:`GET`,path:`/v1/files`,query:t,requestId:e?.requestId,signal:e?.signal,retryable:!0})).data}async*listAll(e){let t,n=!0;for(;n;){let r=await this.list({...e,cursor:t});for(let e of r.files)yield e;t=r.cursor,n=r.hasMore}}async setRetentionLock(e,n,r){if(!e)throw new t(`mediaId is required`);return(await this.transport.request({method:`PATCH`,path:`/v1/files/${encodeURIComponent(e)}/retention`,body:{lock:n},requestId:r?.requestId,signal:r?.signal,retryable:!0})).data}async delete(e,n){if(!e)throw new t(`mediaId is required`);return(await this.transport.request({method:`DELETE`,path:`/v1/files/${encodeURIComponent(e)}`,idempotencyKey:n?.idempotencyKey,requestId:n?.requestId,signal:n?.signal,retryable:!0})).data}};function d(e,t){if(typeof Blob<`u`&&e instanceof Blob&&e.type)return e.type;if(t)return p(t)}function f(e){if(typeof Blob<`u`&&e instanceof Blob){let t=e;if(typeof t.name==`string`&&t.name)return t.name}}function p(e){let t=e.lastIndexOf(`.`);if(!(t<0))switch(e.slice(t+1).toLowerCase()){case`mp4`:return`video/mp4`;case`mov`:return`video/quicktime`;case`webm`:return`video/webm`;case`mp3`:return`audio/mpeg`;case`wav`:return`audio/wav`;case`m4a`:return`audio/mp4`;case`png`:return`image/png`;case`jpg`:case`jpeg`:return`image/jpeg`;case`gif`:return`image/gif`;case`webp`:return`image/webp`;case`pdf`:return`application/pdf`;case`json`:return`application/json`;case`txt`:return`text/plain`;default:return}}async function m(e,n){if(typeof Blob<`u`&&e instanceof Blob){if(e.type===n)return e;let t=e;return typeof t.slice==`function`?t.slice(0,e.size,n):e}if(e instanceof ArrayBuffer||e instanceof Uint8Array)return new Blob([e],{type:n});if(typeof ReadableStream<`u`&&e instanceof ReadableStream){if(typeof Response>`u`)throw new t(`ReadableStream upload requires Response to convert stream to Blob`);let r=await new Response(e).blob();return r.slice(0,r.size,n)}throw new t(`Unsupported file type for upload()`)}var h=class{constructor(e){this.transport=e}async ping(){return(await this.transport.request({method:`GET`,path:`/v1/health/ping`,retryable:!0})).data}};const g=e({length:32});function _(e,t){let n=e.get(t);return n===null?void 0:n}function v(e){let t=.8+Math.random()*.4;return Math.floor(e*t)}function y(e,t,n){let r=t*2**Math.max(0,e-1);return Math.min(r,n)}function b(){return Date.now()}function x(e){return!!e&&typeof e.aborted==`boolean`}function S(){let e=Error(`The operation was aborted`);return e.name=`AbortError`,e}function C(e=`req`){return`${e}_${g()}`}var w=class{constructor(e){this.transport=e}async get(e,t){return(await this.transport.request({method:`GET`,path:`/v1/jobs/${encodeURIComponent(e)}`,requestId:t?.requestId,signal:t?.signal,retryable:!0})).data}async cancel(e,t){return(await this.transport.request({method:`POST`,path:`/v1/jobs/${encodeURIComponent(e)}/cancel`,idempotencyKey:t?.idempotencyKey,requestId:t?.requestId,signal:t?.signal,retryable:!0})).data}async wait(e,t){let n=t?.timeoutMs??5*6e4,r=t?.pollIntervalMs??1e3,i=t?.maxPollIntervalMs??1e4,a=b(),c=0,l,u;for(;;){if(t?.signal?.aborted)throw S();let d=await this.get(e,{signal:t?.signal});if(d.status!==u&&(u=d.status,t?.onEvent?.({type:`status`,job:d})),d.stage&&d.stage!==l&&(l=d.stage,t?.onEvent?.({type:`stage`,stage:d.stage,progress:d.progress,job:d})),d.status===`succeeded`)return t?.onEvent?.({type:`terminal`,job:d}),d;if(d.status===`failed`)throw t?.onEvent?.({type:`terminal`,job:d}),new o(e,d.error?.message??`Job failed`,{requestId:d.requestId,code:d.error?.code,cause:d.error});if(d.status===`canceled`)throw t?.onEvent?.({type:`terminal`,job:d}),new s(e,`Job canceled`,{requestId:d.requestId,cause:d.error});if(b()-a>n)throw new o(e,`Timed out waiting for job ${e} after ${n}ms`,{cause:{jobId:e,timeoutMs:n}});c+=1;let f=v(y(c,r,i));await new Promise(e=>setTimeout(e,f))}}async*stream(e,t){let n,r;for(;;){if(t?.signal?.aborted)return;let i=await this.get(e,{signal:t?.signal});if(i.status!==r){r=i.status;let e={type:`status`,job:i};t?.onEvent?.(e),yield e}if(i.stage&&i.stage!==n){n=i.stage;let e={type:`stage`,stage:i.stage,progress:i.progress,job:i};t?.onEvent?.(e),yield e}if(i.status===`succeeded`||i.status===`failed`||i.status===`canceled`){let e={type:`terminal`,job:i};t?.onEvent?.(e),yield e;return}await new Promise(e=>setTimeout(e,1e3))}}};function T(e){let n=e;if(!(e=>typeof e==`object`&&!!e)(n))throw new t(`media must be an object`);if(n.url!==void 0)throw new t(`media.url is not supported; pass { mediaId } or use createJobFromUrl()`);let r=n.mediaId;if(typeof r!=`string`||!r)throw new t(`media.mediaId must be a non-empty string`)}var E=class{constructor(e,t,n,r){this.transport=e,this.jobs=t,this.files=n,this.fetchImpl=r}async createJob(e){T(e.media);let t=e.idempotencyKey??this.defaultIdempotencyKey(e),n=await this.transport.request({method:`POST`,path:`/v1/reports/jobs`,body:this.normalizeJobRequest(e),idempotencyKey:t,requestId:e.requestId,retryable:!0}),r={...n.data,requestId:n.requestId??n.data.requestId};return r.handle=this.makeHandle(r.jobId),r}async createJobFromFile(e){let{file:t,contentType:n,filename:r,idempotencyKey:i,requestId:a,signal:o,...s}=e,c=await this.files.upload({file:t,contentType:n,filename:r,idempotencyKey:i,requestId:a,signal:o});return this.createJob({...s,media:{mediaId:c.mediaId},idempotencyKey:i,requestId:a})}async createJobFromUrl(e){let{url:n,contentType:r,filename:i,idempotencyKey:a,requestId:o,signal:s,...c}=e,l;try{l=new URL(n)}catch{throw new t(`url must be a valid URL`)}if(l.protocol!==`http:`&&l.protocol!==`https:`)throw new t(`url must use http: or https:`);let u=await this.fetchImpl(l.toString(),{signal:s});if(!u.ok)throw new t(`Failed to download url (status ${u.status})`);let d=u.headers.get(`content-type`)??void 0,f=r??d;if(!f)throw new t(`contentType is required when it cannot be inferred from the download response`);if(typeof Blob>`u`)throw new t(`Blob is not available in this runtime; cannot download and upload from url`);let p=await u.blob(),m=await this.files.upload({file:p,contentType:f,filename:i,idempotencyKey:a,requestId:o,signal:s});return this.createJob({...c,media:{mediaId:m.mediaId},idempotencyKey:a,requestId:o})}async get(e,t){return(await this.transport.request({method:`GET`,path:`/v1/reports/${encodeURIComponent(e)}`,requestId:t?.requestId,signal:t?.signal,retryable:!0})).data}async getByJob(e,t){return(await this.transport.request({method:`GET`,path:`/v1/reports/by-job/${encodeURIComponent(e)}`,requestId:t?.requestId,signal:t?.signal,retryable:!0})).data}async generate(e,n){let r=await this.createJob(e);if(!r.handle)throw new t(`Job receipt is missing handle`);return r.handle.wait(n?.wait)}async generateFromFile(e,n){let r=await this.createJobFromFile(e);if(!r.handle)throw new t(`Job receipt is missing handle`);return r.handle.wait(n?.wait)}async generateFromUrl(e,n){let r=await this.createJobFromUrl(e);if(!r.handle)throw new t(`Job receipt is missing handle`);return r.handle.wait(n?.wait)}makeHandle(e){let n=this;return{jobId:e,stream:t=>n.jobs.stream(e,t),async wait(r){let i=await n.jobs.wait(e,r);if(!i.reportId)throw new t(`Job ${e} succeeded but no reportId was returned`);return n.get(i.reportId)},cancel:()=>n.jobs.cancel(e),job:()=>n.jobs.get(e),report:()=>n.getByJob(e)}}defaultIdempotencyKey(e){return C(`idem`)}normalizeJobRequest(e){let t=e.target;if(!t)return e;let n={strategy:t.strategy};switch(t.onMiss&&(n.on_miss=t.onMiss),t.strategy){case`dominant`:return{...e,target:n};case`timerange`:{let r=t.timeRange??{};return{...e,target:{...n,timerange:{start_seconds:r.startSeconds??null,end_seconds:r.endSeconds??null}}}}case`entity_id`:return{...e,target:{...n,entity_id:t.entityId}};case`magic_hint`:return{...e,target:{...n,hint:t.hint}};default:return e}}};function D(e,t,n){let r=new URL(t.replace(/^\//,``),e.endsWith(`/`)?e:`${e}/`);if(n)for(let[e,t]of Object.entries(n))t!==void 0&&r.searchParams.set(e,String(t));return r.toString()}async function O(e){let t=await e.text();if(!t)return{parsed:null,text:``};try{return{parsed:JSON.parse(t),text:t}}catch{return{parsed:t,text:t}}}function k(e,t){let o=e.headers.get(`x-request-id`)??void 0,s,c=`Request failed with status ${e.status}`,l=t;if(typeof t==`string`)c=t;else if(t&&typeof t==`object`){let e=t,n=e.error??e;if(n&&typeof n==`object`){let e=n;typeof e.message==`string`&&(c=e.message),typeof e.code==`string`&&(s=e.code),`details`in e&&(l=e.details)}}if(e.status===401||e.status===403)return new i(c,{status:e.status,requestId:o,code:s,details:l});if(e.status===422)return new a(c,{status:e.status,requestId:o,code:s,details:l});if(e.status===429){let t=new r(c,{status:e.status,requestId:o,code:s,details:l}),n=e.headers.get(`retry-after`);if(n){let e=Number(n);Number.isFinite(e)&&e>=0&&(t.retryAfterMs=e*1e3)}return t}return new n(c,{status:e.status,requestId:o,code:s,details:l})}function A(e,t){return e.retryable?t instanceof r?{retry:!0,retryAfterMs:t.retryAfterMs}:t instanceof n?{retry:t.status>=500&&t.status<=599}:t instanceof TypeError?{retry:!0}:{retry:!1}:{retry:!1}}var j=class{fetchImpl;constructor(e){this.opts=e,this.fetchImpl=e.fetch??fetch}async request(e){let t=D(this.opts.baseUrl,e.path,e.query),n=e.requestId??C(`req`),r={"Mappa-Api-Key":this.opts.apiKey,"X-Request-Id":n,...this.opts.userAgent?{"User-Agent":this.opts.userAgent}:{},...this.opts.defaultHeaders??{}};if(e.idempotencyKey&&(r[`Idempotency-Key`]=e.idempotencyKey),e.headers)for(let[t,n]of Object.entries(e.headers))n!==void 0&&(r[t]=n);let i=typeof FormData<`u`&&e.body instanceof FormData;e.body!==void 0&&!i&&(r[`Content-Type`]=`application/json`);let a=e.body===void 0?void 0:i?e.body:JSON.stringify(e.body),o=Math.max(0,this.opts.maxRetries),s=Date.now();for(let i=1;i<=1+o;i++){let c=new AbortController,l=setTimeout(()=>c.abort(S()),this.opts.timeoutMs);if(x(e.signal)){let t=e.signal;if(!t)throw clearTimeout(l),Error(`Unexpected: abort signal missing`);if(t.aborted)throw clearTimeout(l),S();t.addEventListener(`abort`,()=>c.abort(S()),{once:!0})}this.opts.telemetry?.onRequest?.({method:e.method,url:t,requestId:n});try{let l=await this.fetchImpl(t,{method:e.method,headers:r,body:a,signal:c.signal}),u=Date.now()-s,d=_(l.headers,`x-request-id`)??n;if(!l.ok){let{parsed:n}=await O(l),r=k(l,n);this.opts.telemetry?.onError?.({url:t,requestId:d,error:r});let a=A(e,r);if(i<=o+1&&a.retry&&i<=o){let e=a.retryAfterMs??v(y(i,500,4e3));await new Promise(t=>setTimeout(t,e));continue}throw r}let f=l.headers.get(`content-type`)??``,p;return p=f.includes(`application/json`)?await l.json():await l.text(),this.opts.telemetry?.onResponse?.({status:l.status,url:t,requestId:d,durationMs:u}),{data:p,status:l.status,requestId:d,headers:l.headers}}catch(r){this.opts.telemetry?.onError?.({url:t,requestId:n,error:r});let a=A(e,r);if(i<=o&&a.retry){let e=a.retryAfterMs??v(y(i,500,4e3));await new Promise(t=>setTimeout(t,e));continue}throw r}finally{clearTimeout(l)}}throw Error(`Unexpected transport exit`)}};function M(e){return typeof e==`object`&&!!e}var N=class{async verifySignature(e){let t=e.toleranceSec??300,n=P(e.headers,`mappa-signature`);if(!n)throw Error(`Missing mappa-signature header`);let r=F(n),i=Number(r.t);if(!Number.isFinite(i))throw Error(`Invalid signature timestamp`);let a=Math.floor(Date.now()/1e3);if(Math.abs(a-i)>t)throw Error(`Signature timestamp outside tolerance`);let o=`${r.t}.${e.payload}`;if(!R(await I(e.secret,o),r.v1))throw Error(`Invalid signature`);return{ok:!0}}parseEvent(e){let t=JSON.parse(e);if(!M(t))throw Error(`Invalid webhook payload: not an object`);let n=t,r=n.id,i=n.type,a=n.createdAt;if(typeof r!=`string`)throw Error(`Invalid webhook payload: id must be a string`);if(typeof i!=`string`)throw Error(`Invalid webhook payload: type must be a string`);if(typeof a!=`string`)throw Error(`Invalid webhook payload: createdAt must be a string`);return{id:r,type:i,createdAt:a,data:`data`in n?n.data:void 0}}};function P(e,t){let n=Object.keys(e).find(e=>e.toLowerCase()===t.toLowerCase()),r=n?e[n]:void 0;if(r)return Array.isArray(r)?r[0]:r}function F(e){let t={};for(let n of e.split(`,`)){let[e,r]=n.split(`=`);e&&r&&(t[e.trim()]=r.trim())}if(!t.t||!t.v1)throw Error(`Invalid signature format`);return{t:t.t,v1:t.v1}}async function I(e,t){let n=new TextEncoder,r=await crypto.subtle.importKey(`raw`,n.encode(e),{name:`HMAC`,hash:`SHA-256`},!1,[`sign`]);return L(await crypto.subtle.sign(`HMAC`,r,n.encode(t)))}function L(e){let t=new Uint8Array(e),n=``;for(let e of t)n+=e.toString(16).padStart(2,`0`);return n}function R(e,t){if(e.length!==t.length)return!1;let n=0;for(let r=0;r<e.length;r++)n|=e.charCodeAt(r)^t.charCodeAt(r);return n===0}var z=class e{files;jobs;reports;feedback;credits;webhooks;health;transport;opts;constructor(e){if(!e.apiKey)throw new t(`apiKey is required`);let n=e.baseUrl??`https://api.mappa.ai`,r=e.timeoutMs??3e4,i=e.maxRetries??2;this.opts={...e,apiKey:e.apiKey,baseUrl:n,timeoutMs:r,maxRetries:i},this.transport=new j({apiKey:e.apiKey,baseUrl:n,timeoutMs:r,maxRetries:i,defaultHeaders:e.defaultHeaders,fetch:e.fetch,telemetry:e.telemetry,userAgent:e.userAgent}),this.files=new u(this.transport),this.jobs=new w(this.transport),this.reports=new E(this.transport,this.jobs,this.files,this.opts.fetch??fetch),this.feedback=new l(this.transport),this.credits=new c(this.transport),this.webhooks=new N,this.health=new h(this.transport)}withOptions(t){return new e({...this.opts,...t,apiKey:t.apiKey??this.opts.apiKey})}close(){}};function B(e){return e.output.type===`markdown`}function V(e){return e.output.type===`json`}function H(e){return e.output.type===`pdf`}function U(e){return e.output.type===`url`}function W(e){return e instanceof t}export{n as ApiError,i as AuthError,s as JobCanceledError,o as JobFailedError,z as Mappa,t as MappaError,r as RateLimitError,a as ValidationError,V as isJsonReport,W as isMappaError,B as isMarkdownReport,H as isPdfReport,U as isUrlReport};
1
+ import{init as e}from"@paralleldrive/cuid2";var t=class extends Error{name=`MappaError`;requestId;code;constructor(e,t){super(e),this.requestId=t?.requestId,this.code=t?.code,this.cause=t?.cause}},n=class extends t{name=`ApiError`;status;details;constructor(e,t){super(e,{requestId:t.requestId,code:t.code}),this.status=t.status,this.details=t.details}},r=class extends n{name=`RateLimitError`;retryAfterMs},i=class extends n{name=`AuthError`},a=class extends n{name=`ValidationError`},o=class extends t{name=`JobFailedError`;jobId;constructor(e,t,n){super(t,n),this.jobId=e}},s=class extends t{name=`JobCanceledError`;jobId;constructor(e,t,n){super(t,n),this.jobId=e}},c=class{constructor(e){this.transport=e}async getBalance(e){return(await this.transport.request({method:`GET`,path:`/v1/credits/balance`,requestId:e?.requestId,signal:e?.signal,retryable:!0})).data}async listTransactions(e){let t={};return e?.limit!==void 0&&(t.limit=String(e.limit)),e?.offset!==void 0&&(t.offset=String(e.offset)),(await this.transport.request({method:`GET`,path:`/v1/credits/transactions`,query:t,requestId:e?.requestId,signal:e?.signal,retryable:!0})).data}async*listAllTransactions(e){let t=0,n=e?.limit??50;for(;;){let r=await this.listTransactions({...e,limit:n,offset:t});for(let e of r.transactions)yield e;if(t+=r.transactions.length,t>=r.pagination.total)break}}async getJobUsage(e,n){if(!e)throw new t(`jobId is required`);return(await this.transport.request({method:`GET`,path:`/v1/credits/usage/${encodeURIComponent(e)}`,requestId:n?.requestId,signal:n?.signal,retryable:!0})).data}async hasEnough(e,t){return(await this.getBalance(t)).available>=e}async getAvailable(e){return(await this.getBalance(e)).available}};const l=/^[a-zA-Z0-9_-]{1,64}$/;function u(e){if(typeof e!=`string`)throw new t(`Tags must be strings`);if(!l.test(e))throw new t(`Invalid tag "${e}": must be 1-64 characters, alphanumeric with underscores and hyphens only`)}function d(e){if(!Array.isArray(e))throw new t(`tags must be an array`);if(e.length>10)throw new t(`Too many tags: maximum 10 per request`);for(let t of e)u(t)}var f=class{constructor(e){this.transport=e}async get(e,n){if(!e||typeof e!=`string`)throw new t(`entityId must be a non-empty string`);return(await this.transport.request({method:`GET`,path:`/v1/entities/${encodeURIComponent(e)}`,requestId:n?.requestId,signal:n?.signal,retryable:!0})).data}async list(e){let t={};return e?.tags&&(d(e.tags),t.tags=e.tags.join(`,`)),e?.cursor&&(t.cursor=e.cursor),e?.limit!==void 0&&(t.limit=String(e.limit)),(await this.transport.request({method:`GET`,path:`/v1/entities`,query:t,requestId:e?.requestId,signal:e?.signal,retryable:!0})).data}async*listAll(e){let t,n=!0;for(;n;){let r=await this.list({...e,cursor:t});for(let e of r.entities)yield e;t=r.cursor,n=r.hasMore}}async getByTag(e,t){return u(e),this.list({...t,tags:[e]})}async addTags(e,n,r){if(!e||typeof e!=`string`)throw new t(`entityId must be a non-empty string`);if(n.length===0)throw new t(`At least one tag is required`);return d(n),(await this.transport.request({method:`POST`,path:`/v1/entities/${encodeURIComponent(e)}/tags`,body:{tags:n},requestId:r?.requestId,signal:r?.signal,retryable:!0})).data}async removeTags(e,n,r){if(!e||typeof e!=`string`)throw new t(`entityId must be a non-empty string`);if(n.length===0)throw new t(`At least one tag is required`);return d(n),(await this.transport.request({method:`DELETE`,path:`/v1/entities/${encodeURIComponent(e)}/tags`,body:{tags:n},requestId:r?.requestId,signal:r?.signal,retryable:!0})).data}async setTags(e,n,r){if(!e||typeof e!=`string`)throw new t(`entityId must be a non-empty string`);return d(n),(await this.transport.request({method:`PUT`,path:`/v1/entities/${encodeURIComponent(e)}/tags`,body:{tags:n},requestId:r?.requestId,signal:r?.signal,retryable:!0})).data}},p=class{constructor(e){this.transport=e}async create(e){if(!!e.reportId==!!e.jobId)throw new t(`Provide exactly one of reportId or jobId`);return(await this.transport.request({method:`POST`,path:`/v1/feedback`,body:e,idempotencyKey:e.idempotencyKey,requestId:e.requestId,signal:e.signal,retryable:!0})).data}},m=class{constructor(e){this.transport=e}async upload(e){if(typeof FormData>`u`)throw new t(`FormData is not available in this runtime; cannot perform multipart upload`);let n=h(e.file,e.filename),r=e.contentType??n;if(!r)throw new t(`contentType is required when it cannot be inferred from file.type or filename`);let i=e.filename??g(e.file)??`upload`,a=await v(e.file,r),o=new FormData;return o.append(`file`,a,i),o.append(`contentType`,r),e.filename&&o.append(`filename`,e.filename),(await this.transport.request({method:`POST`,path:`/v1/files`,body:o,idempotencyKey:e.idempotencyKey,requestId:e.requestId,signal:e.signal,retryable:!0})).data}async get(e,n){if(!e)throw new t(`mediaId is required`);return(await this.transport.request({method:`GET`,path:`/v1/files/${encodeURIComponent(e)}`,requestId:n?.requestId,signal:n?.signal,retryable:!0})).data}async list(e){let t={};return e?.limit!==void 0&&(t.limit=String(e.limit)),e?.cursor&&(t.cursor=e.cursor),e?.includeDeleted!==void 0&&(t.includeDeleted=String(e.includeDeleted)),(await this.transport.request({method:`GET`,path:`/v1/files`,query:t,requestId:e?.requestId,signal:e?.signal,retryable:!0})).data}async*listAll(e){let t,n=!0;for(;n;){let r=await this.list({...e,cursor:t});for(let e of r.files)yield e;t=r.cursor,n=r.hasMore}}async setRetentionLock(e,n,r){if(!e)throw new t(`mediaId is required`);return(await this.transport.request({method:`PATCH`,path:`/v1/files/${encodeURIComponent(e)}/retention`,body:{lock:n},requestId:r?.requestId,signal:r?.signal,retryable:!0})).data}async delete(e,n){if(!e)throw new t(`mediaId is required`);return(await this.transport.request({method:`DELETE`,path:`/v1/files/${encodeURIComponent(e)}`,idempotencyKey:n?.idempotencyKey,requestId:n?.requestId,signal:n?.signal,retryable:!0})).data}};function h(e,t){if(typeof Blob<`u`&&e instanceof Blob&&e.type)return e.type;if(t)return _(t)}function g(e){if(typeof Blob<`u`&&e instanceof Blob){let t=e;if(typeof t.name==`string`&&t.name)return t.name}}function _(e){let t=e.lastIndexOf(`.`);if(!(t<0))switch(e.slice(t+1).toLowerCase()){case`mp4`:return`video/mp4`;case`mov`:return`video/quicktime`;case`webm`:return`video/webm`;case`mp3`:return`audio/mpeg`;case`wav`:return`audio/wav`;case`m4a`:return`audio/mp4`;case`png`:return`image/png`;case`jpg`:case`jpeg`:return`image/jpeg`;case`gif`:return`image/gif`;case`webp`:return`image/webp`;case`pdf`:return`application/pdf`;case`json`:return`application/json`;case`txt`:return`text/plain`;default:return}}async function v(e,n){if(typeof Blob<`u`&&e instanceof Blob){if(e.type===n)return e;let t=e;return typeof t.slice==`function`?t.slice(0,e.size,n):e}if(e instanceof ArrayBuffer||e instanceof Uint8Array)return new Blob([e],{type:n});if(typeof ReadableStream<`u`&&e instanceof ReadableStream){if(typeof Response>`u`)throw new t(`ReadableStream upload requires Response to convert stream to Blob`);let r=await new Response(e).blob();return r.slice(0,r.size,n)}throw new t(`Unsupported file type for upload()`)}var y=class{constructor(e){this.transport=e}async ping(){return(await this.transport.request({method:`GET`,path:`/v1/health/ping`,retryable:!0})).data}};const b=e({length:32});function x(e,t){let n=e.get(t);return n===null?void 0:n}function S(e){let t=.8+Math.random()*.4;return Math.floor(e*t)}function C(e,t,n){let r=t*2**Math.max(0,e-1);return Math.min(r,n)}function w(e){return!!e&&typeof e.aborted==`boolean`}function T(){let e=Error(`The operation was aborted`);return e.name=`AbortError`,e}function E(e=`req`){return`${e}_${b()}`}var D=class{constructor(e){this.transport=e}async get(e,t){return(await this.transport.request({method:`GET`,path:`/v1/jobs/${encodeURIComponent(e)}`,requestId:t?.requestId,signal:t?.signal,retryable:!0})).data}async cancel(e,t){return(await this.transport.request({method:`POST`,path:`/v1/jobs/${encodeURIComponent(e)}/cancel`,idempotencyKey:t?.idempotencyKey,requestId:t?.requestId,signal:t?.signal,retryable:!0})).data}async wait(e,t){let n=t?.timeoutMs??5*6e4,r=new AbortController,i=setTimeout(()=>r.abort(),n);if(t?.signal){if(t.signal.aborted)throw clearTimeout(i),T();t.signal.addEventListener(`abort`,()=>r.abort(),{once:!0})}try{for await(let n of this.stream(e,{signal:r.signal,onEvent:t?.onEvent}))if(n.type===`terminal`){let t=n.job;if(t.status===`succeeded`)return t;if(t.status===`failed`)throw new o(e,t.error?.message??`Job failed`,{requestId:t.requestId,code:t.error?.code,cause:t.error});if(t.status===`canceled`)throw new s(e,`Job canceled`,{requestId:t.requestId,cause:t.error})}throw new o(e,`Timed out waiting for job ${e} after ${n}ms`,{cause:{jobId:e,timeoutMs:n}})}finally{clearTimeout(i)}}async*stream(e,n){let r,i=0;for(;i<3;)try{let a=this.transport.streamSSE(`/v1/jobs/${encodeURIComponent(e)}/stream`,{signal:n?.signal,lastEventId:r});for await(let e of a){if(r=e.id,e.event===`error`){let n=e.data;throw new t(n.error?.message??`Unknown SSE error`,{code:n.error?.code})}if(e.event===`heartbeat`)continue;let a=this.mapSSEToJobEvent(e);if(a&&(n?.onEvent?.(a),yield a,e.event===`terminal`))return;i=0}i++,i<3&&await this.backoff(i)}catch(e){if(n?.signal?.aborted||(i++,i>=3))throw e;await this.backoff(i)}throw new t(`Failed to get status for job ${e} after 3 retries`)}mapSSEToJobEvent(e){let t=e.data;switch(e.event){case`status`:return{type:`status`,job:t.job};case`stage`:return{type:`stage`,stage:t.stage,progress:t.progress,job:t.job};case`terminal`:return{type:`terminal`,job:t.job};default:return{type:`status`,job:t.job}}}async backoff(e){let t=Math.min(1e3*2**e,1e4),n=t*.5*Math.random();await new Promise(e=>setTimeout(e,t+n))}};function O(e){let n=e;if(!(e=>typeof e==`object`&&!!e)(n))throw new t(`media must be an object`);if(n.url!==void 0)throw new t(`media.url is not supported; pass { mediaId } or use createJobFromUrl()`);let r=n.mediaId;if(typeof r!=`string`||!r)throw new t(`media.mediaId must be a non-empty string`)}var k=class{constructor(e,t,n,r){this.transport=e,this.jobs=t,this.files=n,this.fetchImpl=r}async createJob(e){O(e.media);let t=e.idempotencyKey??this.defaultIdempotencyKey(e),n=await this.transport.request({method:`POST`,path:`/v1/reports/jobs`,body:this.normalizeJobRequest(e),idempotencyKey:t,requestId:e.requestId,retryable:!0}),r={...n.data,requestId:n.requestId??n.data.requestId};return r.handle=this.makeHandle(r.jobId),r}async createJobFromFile(e){let{file:t,contentType:n,filename:r,idempotencyKey:i,requestId:a,signal:o,...s}=e,c=await this.files.upload({file:t,contentType:n,filename:r,idempotencyKey:i,requestId:a,signal:o});return this.createJob({...s,media:{mediaId:c.mediaId},idempotencyKey:i,requestId:a})}async createJobFromUrl(e){let{url:n,contentType:r,filename:i,idempotencyKey:a,requestId:o,signal:s,...c}=e,l;try{l=new URL(n)}catch{throw new t(`url must be a valid URL`)}if(l.protocol!==`http:`&&l.protocol!==`https:`)throw new t(`url must use http: or https:`);let u=await this.fetchImpl(l.toString(),{signal:s});if(!u.ok)throw new t(`Failed to download url (status ${u.status})`);let d=u.headers.get(`content-type`)??void 0,f=r??d;if(!f)throw new t(`contentType is required when it cannot be inferred from the download response`);if(typeof Blob>`u`)throw new t(`Blob is not available in this runtime; cannot download and upload from url`);let p=await u.blob(),m=await this.files.upload({file:p,contentType:f,filename:i,idempotencyKey:a,requestId:o,signal:s});return this.createJob({...c,media:{mediaId:m.mediaId},idempotencyKey:a,requestId:o})}async get(e,t){return(await this.transport.request({method:`GET`,path:`/v1/reports/${encodeURIComponent(e)}`,requestId:t?.requestId,signal:t?.signal,retryable:!0})).data}async getByJob(e,t){return(await this.transport.request({method:`GET`,path:`/v1/reports/by-job/${encodeURIComponent(e)}`,requestId:t?.requestId,signal:t?.signal,retryable:!0})).data}async generate(e,n){let r=await this.createJob(e);if(!r.handle)throw new t(`Job receipt is missing handle`);return r.handle.wait(n?.wait)}async generateFromFile(e,n){let r=await this.createJobFromFile(e);if(!r.handle)throw new t(`Job receipt is missing handle`);return r.handle.wait(n?.wait)}async generateFromUrl(e,n){let r=await this.createJobFromUrl(e);if(!r.handle)throw new t(`Job receipt is missing handle`);return r.handle.wait(n?.wait)}makeHandle(e){let n=this;return{jobId:e,stream:t=>n.jobs.stream(e,t),async wait(r){let i=await n.jobs.wait(e,r);if(!i.reportId)throw new t(`Job ${e} succeeded but no reportId was returned`);return n.get(i.reportId)},cancel:()=>n.jobs.cancel(e),job:()=>n.jobs.get(e),report:()=>n.getByJob(e)}}defaultIdempotencyKey(e){return E(`idem`)}normalizeJobRequest(e){let t=e.target;if(!t)return e;let n={strategy:t.strategy};switch(t.onMiss&&(n.on_miss=t.onMiss),t.tags&&t.tags.length>0&&(n.tags=t.tags),t.excludeTags&&t.excludeTags.length>0&&(n.exclude_tags=t.excludeTags),t.strategy){case`dominant`:return{...e,target:n};case`timerange`:{let r=t.timeRange??{};return{...e,target:{...n,timerange:{start_seconds:r.startSeconds??null,end_seconds:r.endSeconds??null}}}}case`entity_id`:return{...e,target:{...n,entity_id:t.entityId}};case`magic_hint`:return{...e,target:{...n,hint:t.hint}};default:return e}}};function A(e,t,n){let r=new URL(t.replace(/^\//,``),e.endsWith(`/`)?e:`${e}/`);if(n)for(let[e,t]of Object.entries(n))t!==void 0&&r.searchParams.set(e,String(t));return r.toString()}async function j(e){let t=await e.text();if(!t)return{parsed:null,text:``};try{return{parsed:JSON.parse(t),text:t}}catch{return{parsed:t,text:t}}}function M(e,t){let o=e.headers.get(`x-request-id`)??void 0,s,c=`Request failed with status ${e.status}`,l=t;if(typeof t==`string`)c=t;else if(t&&typeof t==`object`){let e=t,n=e.error??e;if(n&&typeof n==`object`){let e=n;typeof e.message==`string`&&(c=e.message),typeof e.code==`string`&&(s=e.code),`details`in e&&(l=e.details)}}if(e.status===401||e.status===403)return new i(c,{status:e.status,requestId:o,code:s,details:l});if(e.status===422)return new a(c,{status:e.status,requestId:o,code:s,details:l});if(e.status===429){let t=new r(c,{status:e.status,requestId:o,code:s,details:l}),n=e.headers.get(`retry-after`);if(n){let e=Number(n);Number.isFinite(e)&&e>=0&&(t.retryAfterMs=e*1e3)}return t}return new n(c,{status:e.status,requestId:o,code:s,details:l})}function N(e,t){return e.retryable?t instanceof r?{retry:!0,retryAfterMs:t.retryAfterMs}:t instanceof n?{retry:t.status>=500&&t.status<=599}:t instanceof TypeError?{retry:!0}:{retry:!1}:{retry:!1}}var P=class{fetchImpl;constructor(e){this.opts=e,this.fetchImpl=e.fetch??fetch}async*streamSSE(e,n){let r=A(this.opts.baseUrl,e),i=E(`req`),a={Accept:`text/event-stream`,"Cache-Control":`no-cache`,"Mappa-Api-Key":this.opts.apiKey,"X-Request-Id":i,...this.opts.userAgent?{"User-Agent":this.opts.userAgent}:{},...this.opts.defaultHeaders??{}};n?.lastEventId&&(a[`Last-Event-ID`]=n.lastEventId);let o=new AbortController,s=setTimeout(()=>o.abort(T()),this.opts.timeoutMs);if(w(n?.signal)){let e=n?.signal;if(e?.aborted)throw clearTimeout(s),T();e?.addEventListener(`abort`,()=>o.abort(T()),{once:!0})}this.opts.telemetry?.onRequest?.({method:`GET`,url:r,requestId:i});let c;try{c=await this.fetchImpl(r,{method:`GET`,headers:a,signal:o.signal})}catch(e){throw clearTimeout(s),this.opts.telemetry?.onError?.({url:r,requestId:i,error:e}),e}if(!c.ok){clearTimeout(s);let{parsed:e}=await j(c),t=M(c,e);throw this.opts.telemetry?.onError?.({url:r,requestId:i,error:t}),t}if(!c.body)throw clearTimeout(s),new t(`SSE response has no body`);try{yield*this.parseSSEStream(c.body)}finally{clearTimeout(s)}}async*parseSSEStream(e){let t=new TextDecoder,n=e.getReader(),r=``;try{for(;;){let{done:e,value:i}=await n.read();if(e)break;r+=t.decode(i,{stream:!0});let a=r.split(`
2
+
3
+ `);r=a.pop()??``;for(let e of a){if(!e.trim())continue;let t=this.parseSSEEvent(e);t&&(yield t)}}if(r.trim()){let e=this.parseSSEEvent(r);e&&(yield e)}}finally{n.releaseLock()}}parseSSEEvent(e){let t=e.split(`
4
+ `),n,r=`message`,i=``;for(let e of t)e.startsWith(`id:`)?n=e.slice(3).trim():e.startsWith(`event:`)?r=e.slice(6).trim():e.startsWith(`data:`)&&(i&&(i+=`
5
+ `),i+=e.slice(5).trim());if(!i)return null;let a;try{a=JSON.parse(i)}catch{a=i}return{id:n,event:r,data:a}}async request(e){let t=A(this.opts.baseUrl,e.path,e.query),n=e.requestId??E(`req`),r={"Mappa-Api-Key":this.opts.apiKey,"X-Request-Id":n,...this.opts.userAgent?{"User-Agent":this.opts.userAgent}:{},...this.opts.defaultHeaders??{}};if(e.idempotencyKey&&(r[`Idempotency-Key`]=e.idempotencyKey),e.headers)for(let[t,n]of Object.entries(e.headers))n!==void 0&&(r[t]=n);let i=typeof FormData<`u`&&e.body instanceof FormData;e.body!==void 0&&!i&&(r[`Content-Type`]=`application/json`);let a=e.body===void 0?void 0:i?e.body:JSON.stringify(e.body),o=Math.max(0,this.opts.maxRetries),s=Date.now();for(let i=1;i<=1+o;i++){let c=new AbortController,l=setTimeout(()=>c.abort(T()),this.opts.timeoutMs);if(w(e.signal)){let t=e.signal;if(!t)throw clearTimeout(l),Error(`Unexpected: abort signal missing`);if(t.aborted)throw clearTimeout(l),T();t.addEventListener(`abort`,()=>c.abort(T()),{once:!0})}this.opts.telemetry?.onRequest?.({method:e.method,url:t,requestId:n});try{let l=await this.fetchImpl(t,{method:e.method,headers:r,body:a,signal:c.signal}),u=Date.now()-s,d=x(l.headers,`x-request-id`)??n;if(!l.ok){let{parsed:n}=await j(l),r=M(l,n);this.opts.telemetry?.onError?.({url:t,requestId:d,error:r});let a=N(e,r);if(i<=o+1&&a.retry&&i<=o){let e=a.retryAfterMs??S(C(i,500,4e3));await new Promise(t=>setTimeout(t,e));continue}throw r}let f=l.headers.get(`content-type`)??``,p;return p=f.includes(`application/json`)?await l.json():await l.text(),this.opts.telemetry?.onResponse?.({status:l.status,url:t,requestId:d,durationMs:u}),{data:p,status:l.status,requestId:d,headers:l.headers}}catch(r){this.opts.telemetry?.onError?.({url:t,requestId:n,error:r});let a=N(e,r);if(i<=o&&a.retry){let e=a.retryAfterMs??S(C(i,500,4e3));await new Promise(t=>setTimeout(t,e));continue}throw r}finally{clearTimeout(l)}}throw Error(`Unexpected transport exit`)}};function F(e){return typeof e==`object`&&!!e}var I=class{async verifySignature(e){let t=e.toleranceSec??300,n=L(e.headers,`mappa-signature`);if(!n)throw Error(`Missing mappa-signature header`);let r=R(n),i=Number(r.t);if(!Number.isFinite(i))throw Error(`Invalid signature timestamp`);let a=Math.floor(Date.now()/1e3);if(Math.abs(a-i)>t)throw Error(`Signature timestamp outside tolerance`);let o=`${r.t}.${e.payload}`;if(!V(await z(e.secret,o),r.v1))throw Error(`Invalid signature`);return{ok:!0}}parseEvent(e){let t=JSON.parse(e);if(!F(t))throw Error(`Invalid webhook payload: not an object`);let n=t,r=n.id,i=n.type,a=n.createdAt;if(typeof r!=`string`)throw Error(`Invalid webhook payload: id must be a string`);if(typeof i!=`string`)throw Error(`Invalid webhook payload: type must be a string`);if(typeof a!=`string`)throw Error(`Invalid webhook payload: createdAt must be a string`);return{id:r,type:i,createdAt:a,data:`data`in n?n.data:void 0}}};function L(e,t){let n=Object.keys(e).find(e=>e.toLowerCase()===t.toLowerCase()),r=n?e[n]:void 0;if(r)return Array.isArray(r)?r[0]:r}function R(e){let t={};for(let n of e.split(`,`)){let[e,r]=n.split(`=`);e&&r&&(t[e.trim()]=r.trim())}if(!t.t||!t.v1)throw Error(`Invalid signature format`);return{t:t.t,v1:t.v1}}async function z(e,t){let n=new TextEncoder,r=await crypto.subtle.importKey(`raw`,n.encode(e),{name:`HMAC`,hash:`SHA-256`},!1,[`sign`]);return B(await crypto.subtle.sign(`HMAC`,r,n.encode(t)))}function B(e){let t=new Uint8Array(e),n=``;for(let e of t)n+=e.toString(16).padStart(2,`0`);return n}function V(e,t){if(e.length!==t.length)return!1;let n=0;for(let r=0;r<e.length;r++)n|=e.charCodeAt(r)^t.charCodeAt(r);return n===0}var H=class e{files;jobs;reports;feedback;credits;entities;webhooks;health;transport;opts;constructor(e){if(!e.apiKey)throw new t(`apiKey is required`);let n=e.baseUrl??`https://api.mappa.ai`,r=e.timeoutMs??3e4,i=e.maxRetries??2;this.opts={...e,apiKey:e.apiKey,baseUrl:n,timeoutMs:r,maxRetries:i},this.transport=new P({apiKey:e.apiKey,baseUrl:n,timeoutMs:r,maxRetries:i,defaultHeaders:e.defaultHeaders,fetch:e.fetch,telemetry:e.telemetry,userAgent:e.userAgent}),this.files=new m(this.transport),this.jobs=new D(this.transport),this.reports=new k(this.transport,this.jobs,this.files,this.opts.fetch??fetch),this.feedback=new p(this.transport),this.credits=new c(this.transport),this.entities=new f(this.transport),this.webhooks=new I,this.health=new y(this.transport)}withOptions(t){return new e({...this.opts,...t,apiKey:t.apiKey??this.opts.apiKey})}close(){}};function U(e){return e.output.type===`markdown`}function W(e){return e.output.type===`json`}function G(e){return e.output.type===`pdf`}function K(e){return e.output.type===`url`}function q(e){return e.entity!==void 0&&e.entity!==null}function J(e){return e instanceof t}export{n as ApiError,i as AuthError,s as JobCanceledError,o as JobFailedError,H as Mappa,t as MappaError,r as RateLimitError,a as ValidationError,q as hasEntity,W as isJsonReport,J as isMappaError,U as isMarkdownReport,G as isPdfReport,K as isUrlReport};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@mappa-ai/mappa-node",
3
3
  "module": "src/index.ts",
4
4
  "type": "module",
5
- "version": "1.0.0",
5
+ "version": "1.2.0",
6
6
  "license": "MIT",
7
7
  "files": [
8
8
  "dist/**"