@cube-nl/dmp-sdk 0.0.0-stage → 1.0.0-rc.3
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/LICENSE +21 -0
- package/README.md +690 -2
- package/dist/app.d.ts +87 -0
- package/dist/app.js +245 -0
- package/dist/auth.d.ts +117 -0
- package/dist/auth.js +321 -0
- package/dist/browser.d.ts +1 -0
- package/dist/browser.js +3 -0
- package/dist/client.d.ts +102 -0
- package/dist/client.js +220 -0
- package/dist/cube-sdk.js +2 -0
- package/dist/errors.d.ts +48 -0
- package/dist/errors.js +68 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +6 -0
- package/dist/preview.d.ts +69 -0
- package/dist/preview.js +676 -0
- package/dist/query.d.ts +9 -0
- package/dist/query.js +121 -0
- package/dist/types.d.ts +166 -0
- package/dist/types.js +6 -0
- package/dist/version.d.ts +4 -0
- package/dist/version.js +4 -0
- package/package.json +38 -4
package/dist/cube-sdk.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! @cube/dmp-sdk 1.0.0-rc.3 cube-sdk.js (window.CubeApp) | MIT */
|
|
2
|
+
"use strict";(()=>{var be=Object.defineProperty;var ke=(e,n,t)=>n in e?be(e,n,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[n]=t;var k=(e,n,t)=>ke(e,typeof n!="symbol"?n+"":n,t);var R=class extends Error{constructor(t,r,s){let i=s!==null&&typeof s=="object"?s:{},o=A(i.detail)??A(i.error_description);super(o??`HTTP ${t}`);k(this,"status");k(this,"code");k(this,"reason");k(this,"type");k(this,"title");k(this,"detail");k(this,"errors");k(this,"retryAfter");k(this,"recordVersion");k(this,"allow");k(this,"headers");k(this,"body");this.name="DmpError",this.status=t,this.code=A(i.code)??A(i.error),this.reason=A(i.reason),this.type=A(i.type),this.title=A(i.title),this.detail=o,this.errors=Re(i.errors)?i.errors:null,this.retryAfter=Te(r.get("Retry-After")),this.recordVersion=r.get("X-Dmp-Record-Version"),this.allow=r.has("Allow")?(r.get("Allow")??"").split(",").map(a=>a.trim()).filter(a=>a!==""):null,this.headers=r,this.body=s}get isProblem(){return this.code!==null}};function A(e){return typeof e=="string"?e:null}function Re(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function Te(e){if(e===null||e.trim()==="")return null;let n=Number(e);if(Number.isFinite(n))return n;let t=Date.parse(e);return Number.isNaN(t)?null:Math.max(0,Math.ceil((t-Date.now())/1e3))}function Y(e){return{async getAccessToken(){let n=typeof e=="function"?await e():e;return n===void 0||n===""?null:n}}}var $="cube-dmp-sdk.pending-login";function Z(e){let n=e.fetch??globalThis.fetch.bind(globalThis),t=e.tokenStore??M(),r=e.pendingStore??ve(),s=(e.refreshLeewaySeconds??30)*1e3,i=typeof e.scopes=="string"?e.scopes:e.scopes.join(" "),o=e.metadata??{},a=Se(o)?Promise.resolve({...o,issuer:o.issuer??e.issuer}):null,l=null,c=()=>(a??(a=_e(e.issuer,n).then(p=>({...p,...o}),p=>{throw a=null,p})),a),g=async(p,d)=>{let{token_endpoint:u}=await c(),f=new URLSearchParams(p);e.clientSecret!==void 0&&f.set("client_secret",e.clientSecret);let m=await n(u,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/x-www-form-urlencoded"},body:f.toString()}),y=await ee(m);if(!m.ok||y===null||typeof y!="object"||typeof y.access_token!="string")throw new R(m.status,m.headers,y);let T={accessToken:y.access_token,refreshToken:typeof y.refresh_token=="string"?y.refresh_token:null,expiresAt:typeof y.expires_in=="number"?Date.now()+y.expires_in*1e3:null,scope:typeof y.scope=="string"?y.scope:d};return await t.set(T),T},b=()=>(l??(l=(async()=>{let p=await t.get();if(p?.refreshToken==null)throw new TypeError("No refresh token: sign in again.");try{return await g({grant_type:"refresh_token",refresh_token:p.refreshToken,client_id:e.clientId},p.scope)}catch(d){throw d instanceof R&&d.status>=400&&d.status<500&&await t.set(null),d}})().finally(()=>{l=null})),l),w=async(p={})=>{let d=await c();if(!(d.code_challenge_methods_supported??[]).includes("S256"))throw new TypeError("The authorization server does not offer PKCE S256; this SDK will not sign in without it.");let u=B(64),f=p.state??B(32),m=ne(await crypto.subtle.digest("SHA-256",new TextEncoder().encode(u))),y={verifier:u,state:f,redirectUri:e.redirectUri,clientId:e.clientId};r.setItem($,JSON.stringify(y));let T=new URL(d.authorization_endpoint);return T.search=new URLSearchParams({...p.extraParams,response_type:"code",client_id:e.clientId,redirect_uri:e.redirectUri,scope:i,state:f,code_challenge:m,code_challenge_method:"S256"}).toString(),T.toString()};return{discover:c,authorizationUrl:w,async signIn(p={}){let d=X();if(d===null)throw new TypeError("signIn() needs a browser; use authorizationUrl() and redirect yourself.");d.assign(await w(p))},async handleCallback(p){let d=p??X()?.href;if(d===void 0)throw new TypeError("handleCallback() needs the callback URL outside a browser.");let u=new URL(d),f=new URLSearchParams(u.search);if(!Oe(u,e.redirectUri)||!f.has("code")&&!f.has("error"))return null;if(p===void 0){for(let y of["code","state","error","error_description","iss"])u.searchParams.delete(y);globalThis.history?.replaceState(null,"",u.pathname+u.search+u.hash)}let m=De(r.getItem($));if(m===null||f.get("state")!==m.state)throw new TypeError("The state does not match: this callback does not belong to a sign-in started here.");if(r.removeItem($),f.has("error"))throw new R(400,new Headers,{error:f.get("error")??"access_denied",error_description:f.get("error_description")??void 0});return g({grant_type:"authorization_code",code:f.get("code")??"",redirect_uri:m.redirectUri,client_id:m.clientId,code_verifier:m.verifier},i)},refresh:b,async getTokens(){return t.get()},async setTokens(p){await t.set(p)},async signOut(){await t.set(null),r.removeItem($)},async getAccessToken(){let p=await t.get();if(p===null)return null;if(p.expiresAt!==null&&p.expiresAt-s<=Date.now()&&p.refreshToken!==null)try{return(await b()).accessToken}catch{return(await t.get())?.accessToken??null}return p.accessToken},async onUnauthorized(){if((await t.get())?.refreshToken==null)return!1;try{return await b(),!0}catch{return!1}}}}function Se(e){return typeof e.authorization_endpoint=="string"&&typeof e.token_endpoint=="string"}async function _e(e,n=globalThis.fetch.bind(globalThis)){let t=`${e.replace(/\/+$/,"")}/.well-known/oauth-authorization-server`,r=await n(t,{headers:{Accept:"application/json"}}),s=await ee(r);if(!r.ok||s===null||typeof s!="object")throw new R(r.status,r.headers,s);return s}function M(e=null){let n=e;return{get:()=>n,set:t=>{n=t}}}function Ae(){let e=new Map;return{getItem:n=>e.get(n)??null,setItem:(n,t)=>{e.set(n,t)},removeItem:n=>{e.delete(n)}}}function ve(){try{let e=globalThis.sessionStorage;if(e!==void 0)return e}catch{}return Ae()}function X(){let e=globalThis.location;return e!==void 0&&typeof e.assign=="function"?e:null}function Oe(e,n){let t=new URL(n),r=s=>s.replace(/\/+$/,"")||"/";return e.origin===t.origin&&r(e.pathname)===r(t.pathname)}function De(e){if(e===null)return null;try{let n=JSON.parse(e);return typeof n.verifier=="string"&&typeof n.state=="string"&&typeof n.redirectUri=="string"&&typeof n.clientId=="string"?n:null}catch{return null}}async function ee(e){let n=await e.text();if(n==="")return null;try{return JSON.parse(n)}catch{return n}}function B(e){return ne(crypto.getRandomValues(new Uint8Array(e)))}function ne(e){let n=e instanceof Uint8Array?e:new Uint8Array(e),t="";for(let r of n)t+=String.fromCharCode(r);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}var Pe=new Set(["in","between"]);function v(e,n={}){let t=new URLSearchParams;xe(t,e,n.fields),$e(t,n.filter),P(t,"sort",n.sort),P(t,"include",n.include),P(t,"with_count",n.withCount),Ue(t,n.has),P(t,"fn",n.fn),P(t,"group",n.group),n.page?.size!==void 0&&t.append("page[size]",String(n.page.size)),n.page?.number!==void 0&&t.append("page[number]",String(n.page.number));for(let[r,s]of Object.entries(n.params??{}))t.append(r,s);return t.toString().replace(/%5B/gi,"[").replace(/%5D/gi,"]").replace(/%2C/gi,",").replace(/%28/gi,"(").replace(/%29/gi,")")}function xe(e,n,t){if(t===void 0)return;let r=Ee(t)?{[n]:t}:t;for(let[s,i]of Object.entries(r))i.length>0&&e.append(`fields[${s}]`,i.join(","))}function $e(e,n){for(let[t,r]of Object.entries(n??{})){if(Array.isArray(r))throw new TypeError(`filter.${t}: a bare list names no operator and cube/api refuses it. Write { in: [...] } (or { between: [from, to] }).`);if(r==null)throw new TypeError(`filter.${t}: write { is: 'null' } or { is: 'not_null' } to filter on null.`);if(typeof r!="object"){e.append(`filter[${t}]`,F(r));continue}for(let[s,i]of Object.entries(r))i!==void 0&&e.append(`filter[${t}][${s}]`,Me(t,s,i))}}function Me(e,n,t){if(t===null){if(n!=="is")throw new TypeError(`filter.${e}.${n}: null is only meaningful under 'is'.`);return"null"}if(Array.isArray(t)){if(!Pe.has(n))throw new TypeError(`filter.${e}.${n}: takes one value; only 'in' and 'between' take a list.`);return t.map(F).join(",")}return F(t)}function Ue(e,n){for(let[t,r]of Object.entries(n??{}))r===!0?e.append(`has[${t}]`,"1"):typeof r=="number"?e.append(`has[${t}][min]`,String(r)):(r.min!==void 0&&e.append(`has[${t}][min]`,String(r.min)),r.max!==void 0&&e.append(`has[${t}][max]`,String(r.max)))}function P(e,n,t){if(t===void 0)return;let r=typeof t=="string"?t:t.filter(s=>s!=="").join(",");r!==""&&e.append(n,r)}function F(e){return typeof e=="boolean"?e?"1":"0":String(e)}function Ee(e){return Array.isArray(e)}var Ce=/^[a-z][a-z0-9_]*$/;function re(e){let n=e.baseUrl.replace(/\/+$/,""),t=e.fetch??globalThis.fetch.bind(globalThis),r=e.auth===void 0?null:typeof e.auth=="string"||typeof e.auth=="function"?Y(e.auth):e.auth,s=e.cache??(typeof document<"u"?"no-cache":void 0),i=async(a,l,{query:c,body:g,headers:b={},signal:w}={})=>{let p=`${n}${l.startsWith("/")?l:`/${l}`}${c?`?${c}`:""}`,d=async()=>{let y=r===null?null:await r.getAccessToken(),T={method:a,headers:{Accept:"application/json",...e.headers,...y!==null?{Authorization:`Bearer ${y}`}:{},...g!==void 0?{"Content-Type":"application/json"}:{},...b}};g!==void 0&&(T.body=JSON.stringify(g)),s!==void 0&&(T.cache=s),w!==void 0&&(T.signal=w);let we=Date.now(),H=await t(p,T);return e.onResponse?.({method:a,url:p,status:H.status,ms:Date.now()-we}),H},u=await d();u.status===401&&r?.onUnauthorized!==void 0&&await r.onUnauthorized()&&(await u.body?.cancel().catch(()=>{}),u=await d());let f=await Ie(u);if(!u.ok)throw new R(u.status,u.headers,f);let m=Le(f)?f:null;return{status:u.status,data:m!==null?m.data:f,meta:m?.meta??null,headers:u.headers,etag:u.headers.get("ETag"),recordVersion:u.headers.get("X-Dmp-Record-Version"),location:u.headers.get("Location"),replayed:u.headers.get("Idempotency-Replayed")==="true",rateLimit:Ne(u.headers)}};return{async capabilities(a={}){let l=(await i("GET","/_capabilities",E({},a))).data;return{...l,resources:l.resources.map(je)}},resources:a=>{if(!Ce.test(a))throw new TypeError(`'${a}' is not a resource name: cube/api names match [a-z][a-z0-9_]*.`);let l=`/${a}`,c=d=>{let u=String(d);if(u==="")throw new TypeError(`${a}: an empty id would address the collection, not a record.`);if(ie(u))throw new TypeError(`${a}: '${u}' is not an id: it would address another path, not a record.`);return`${l}/${encodeURIComponent(u)}`},g=d=>d===void 0?"":v(a,{fields:d}),b=async(d={},u={})=>await i("GET",l,E({query:v(a,d)},u)),w=async function*(d={},u={}){let f=d.page?.number??1;for(;;){let m=await b({...d,page:{...d.page,number:f}},u);yield m;let y=m.meta.page;if(y==null||y.number>=y.pages||m.data.length===0)return;f=y.number+1}},p=async(d,u,f)=>{let m=await i("POST",d,{query:g(f.fields),body:u??{},headers:U(f)});return{...m,data:m.status===204?null:m.data,accepted:m.status===202}};return{name:a,list:b,pages:w,async*all(d={},u={}){for await(let f of w(d,u))yield*f.data},get:(d,u={},f={})=>i("GET",c(d),E({query:v(a,u)},f)),aggregate:(d,u={})=>i("GET",`${l}/_aggregate`,E({query:v(a,d)},u)),create:(d,u={})=>i("POST",l,{query:g(u.fields),body:d,headers:U(u)}),update:(d,u,f={})=>i("PATCH",c(d),{query:g(f.fields),body:u,headers:U(f)}),delete:async(d,u={})=>({...await i("DELETE",c(d),{headers:U(u)}),data:null}),operation:(d,u,f,m={})=>p(`${c(d)}/${encodeURIComponent(te(u))}`,f,m),collectionOperation:(d,u,f={})=>p(`${l}/_operations/${encodeURIComponent(te(d))}`,u,f)}},request:i}}function je(e){return{...e,operations:(e.operations??[]).map(n=>({...n,input:q(n.input)})),aggregatable:q(e.aggregatable),relations:q(e.relations)}}function q(e){return Array.isArray(e)||e===void 0||e===null?{}:e}function te(e){if(e===""||e.startsWith("_"))throw new TypeError(`'${e}' is not an operation name: the '_' namespace is reserved by the API.`);if(ie(e))throw new TypeError(`'${e}' is not an operation name: it would address another path.`);return e}function ie(e){return e==="."||e===".."}function U(e){let n={};if(e.idempotencyKey!==void 0&&(n["Idempotency-Key"]=e.idempotencyKey),e.ifMatch!==void 0){if(e.ifMatch.startsWith("W/"))throw new TypeError("ifMatch takes recordVersion (X-Dmp-Record-Version), not the weak ETag.");n["If-Match"]=e.ifMatch}return n}function E(e,n){return n.signal===void 0?e:{...e,signal:n.signal}}async function Ie(e){let n=await e.text();if(n==="")return null;try{return JSON.parse(n)}catch{return n}}function Le(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)&&"data"in e}function Ne(e){let n=t=>{let r=e.get(t);return r===null||r===""||!Number.isFinite(Number(r))?null:Number(r)};return{limit:n("X-RateLimit-Limit"),remaining:n("X-RateLimit-Remaining"),reset:n("X-RateLimit-Reset")}}var se=/^[a-z][a-z0-9_]*$/,oe=["eq","neq","gt","gte","lt","lte","in","like","ilike","is","between"],ze=[[">=","gte"],["<=","lte"],["<>","neq"],[">","gt"],["<","lt"],["=","eq"]],Q=20,V=200,Fe=50,K=200,qe="1.0",C=class{constructor(n,t=null,r=()=>{}){this.manifest=t;this.guard=r;k(this,"tables",new Map);for(let[s,i]of Object.entries(Qe(n))){if(typeof i!="object"||i===null||Array.isArray(i))continue;let o=typeof i.schema?.name=="string"&&i.schema.name!==""?i.schema.name:s;se.test(o)&&this.tables.set(o,Ve(o,i))}}resourceNames(){return[...this.tables.keys()]}resource(n){if(!se.test(n))throw new TypeError(`'${n}' is not a resource name: cube/api names match [a-z][a-z0-9_]*.`);let t=(i,o)=>Promise.resolve().then(()=>o(this.authorize(n,i))),r=(i={})=>t("list",o=>Ge(o,i)),s=async function*(i={}){let o=i.page?.number??1;for(;;){let a=await r({...i,page:{...i.page,number:o}});if(yield a,a.meta.page.number>=a.meta.page.pages||a.data.length===0)return;o=a.meta.page.number+1}};return{name:n,list:r,pages:s,async*all(i={}){for await(let o of s(i))yield*o.data},get:(i,o={})=>t("get",a=>Je(a,i,o)),aggregate:i=>t("list",o=>Ze(o,i)),create:(i,o={})=>t("create",a=>tn(a,i,o)),update:(i,o,a={})=>t("update",l=>rn(l,i,o,a)),delete:(i,o={})=>t("delete",a=>sn(a,i,o)),operation:(i,o,a,l)=>t("get",c=>{throw I(c,i),le(o)}),collectionOperation:i=>t("list",()=>{throw le(i)})}}authorize(n,t){this.guard();let r=this.tables.get(n);if(r===void 0)throw h(404,"not_found",`Unknown API resource [${n}].`);let s=this.manifest?.uses;if(s!==void 0&&!(s[n]??[]).includes(t))throw h(403,"client_scope_exceeded",`This app may not ${t} '${n}': add it to "uses" in cube-app.json.`);return r}};function Qe(e){let n=e.resources;return Object.keys(e).length===1&&fe(n)&&!("rows"in n)&&!("schema"in n)?n:e}function Ve(e,n){let t=n.schema??{},r=(n.rows??[]).map(c=>J(c)),s=new Map,i=Array.isArray(t.fields)?t.fields.map(c=>[c.name,c]):Object.entries(t.fields??{});for(let[c,g]of i)s.set(c,Ke(c,g));for(let c of s.size===0?r:[])for(let g of Object.keys(c))s.has(g)||s.set(g,{name:g,types:[],nullable:!0});let o=[...s.keys()],a=new Set(["id",...t.readonly??[],...i.filter(([,c])=>c.readonly===!0).map(([c])=>c)]),l=c=>c===void 0?null:c.filter(g=>s.has(g));return{name:e,fields:s,filterable:l(t.filters)??o,writable:new Set(o.filter(c=>!a.has(c))),summary:(l(t.summary_fields)??[]).length>0?l(t.summary_fields):o,rows:r,versions:new Map(r.map(c=>[_(c.id),1])),replays:new Map}}function Ke(e,n){let t=n.type,r=typeof t=="string"?t.split("|"):Array.isArray(t)?[...t]:[];return{name:e,types:r.filter(s=>s!=="null"&&s!==""),nullable:n.nullable===!0||r.includes("null")}}function x(e,n){return new URLSearchParams(v(e.name,n))}function Ge(e,n){let t=x(e,n),r=j(e,t),s=ce(e,t),i=Be(e,t),{size:o,number:a}=Ye(t),l=ue(e,t),c=e.rows.filter(p=>s.every(d=>de(e,p,d)));i.length>0&&(c=[...c].sort((p,d)=>{for(let{field:u,descending:f}of i){let m=S(p[u],d[u]);if(m!==0)return f?-m:m}return 0}));let g=c.length,b=c.slice((a-1)*o,a*o).map(p=>L(p,r)),w={size:o,number:a,total:g,pages:Math.max(1,Math.ceil(g/o))};return{...D(200,b,O(e,w,l)),meta:{...O(e,w,l),page:w}}}function Je(e,n,t){let r=x(e,t),s=j(e,r),i=ue(e,r),o=I(e,n);return D(200,L(o,s),O(e,null,i),N(e,o))}function j(e,n){let t=`fields[${e.name}]`,r=n.get(t);if(r===null||r==="")return e.summary;let s=r.split(",").map(o=>o.trim()),i=s.filter(o=>!e.fields.has(o));if(i.length>0)throw h(422,"query_invalid",`Unknown ${t}: ${i.join(", ")}.`,{[t]:[`Allowed values: ${[...e.fields.keys()].join(", ")}.`]});return s}function ue(e,n){let t=r=>(n.get(r)??"").split(",").filter(s=>s!=="");return[...new Set([...t("include"),...t("with_count")])]}function ce(e,n){let t=[],r=[];n.forEach((s,i)=>r.push([i,s]));for(let[s,i]of r){let o=/^filter\[([^\]]+)\](?:\[([^\]]+)\])?$/.exec(s);if(o===null)continue;let a=o[1];if(!e.filterable.includes(a))throw h(422,"query_invalid",`Unknown filter: ${a}.`,{filter:[`Allowed values: ${e.filterable.join(", ")}.`]},"not_filterable");t.push(o[2]===void 0?We(a,i):He(a,o[2],i))}if(t.length>Q)throw h(422,"query_invalid",`A query takes at most ${Q} filters.`,{filter:[`Sent ${t.length}; the limit is ${Q}.`]});return t}function We(e,n){for(let[t,r]of ze)if(n.startsWith(t))return{field:e,operator:r,value:n.slice(t.length)};return{field:e,operator:"eq",value:n}}function He(e,n,t){if(!oe.includes(n))throw h(422,"query_invalid",`Unknown filter operator: ${n}.`,{filter:[`Allowed operators: ${oe.join(", ")}.`]});if(n==="between"&&t.split(",").length!==2)throw h(422,"query_invalid",`filter[${e}][between] takes exactly 2 values.`,{filter:[`Write filter[${e}][between]=low,high. Received ${t.split(",").length} value(s).`]});if(n==="in"&&t.split(",").length>V)throw h(422,"query_invalid",`filter[${e}][in] takes at most ${V} values.`,{filter:[`Sent ${t.split(",").length}; the limit is ${V}.`]});if(n==="is"&&t!=="null"&&t!=="not_null")throw h(422,"query_invalid",`filter[${e}][is] takes null or not_null.`,{filter:[`Received: ${t}.`]});return{field:e,operator:n,value:t}}function de(e,n,t){let r=n[t.field],s=i=>ln(e.fields.get(t.field),i.trim());switch(t.operator){case"eq":return G(r,s(t.value));case"neq":return r!=null&&!G(r,s(t.value));case"gt":return r!=null&&S(r,s(t.value))>0;case"gte":return r!=null&&S(r,s(t.value))>=0;case"lt":return r!=null&&S(r,s(t.value))<0;case"lte":return r!=null&&S(r,s(t.value))<=0;case"in":return t.value.split(",").some(i=>G(r,s(i)));case"between":{let[i,o]=t.value.split(",").map(s);return r!=null&&S(r,i)>=0&&S(r,o)<=0}case"like":case"ilike":return typeof r=="string"&&Xe(r,t.value);case"is":return t.value==="null"?r==null:r!=null;default:return!1}}function Xe(e,n){let t=n.split("%").map(r=>r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join(".*");return new RegExp(t,"iu").test(e)}function Be(e,n){let t=n.get("sort");if(t===null||t==="")return[];let r=t.split(",").map(i=>i.trim()).filter(i=>i!=="").map(i=>({field:i.replace(/^[-+]/,""),descending:i.startsWith("-")})),s=r.map(i=>i.field).filter(i=>!e.fields.has(i));if(s.length>0)throw h(422,"query_invalid",`Unknown sort: ${s.join(", ")}.`,{sort:[`Allowed values: ${[...e.fields.keys()].join(", ")}.`]},"not_sortable");return r}function Ye(e){let n=r=>{let s=e.get(r);if(s===null||s==="")return null;if(!/^\d+$/.test(s))throw h(422,"query_invalid",`${r} must be a positive integer.`,{[r]:["Expected a positive integer."]});return Number(s)},t=n("page[size]")??Fe;if(t>K)throw h(422,"cardinality_exceeded",`page[size] may not exceed ${K}.`,{"page.size":[`The maximum page size is ${K}.`]});return{size:Math.max(1,t),number:Math.max(1,n("page[number]")??1)}}function Ze(e,n){let t=x(e,n),r=ce(e,t),s=(t.get("fn")??"").split(",").filter(g=>g!==""),i=(t.get("group")??"").split(",").filter(g=>g!=="");if(s.length===0)throw h(422,"query_invalid","fn is required.",{fn:["Name at least one function, e.g. fn=count."]});let o=s.map(g=>en(e,g)),a=i.map(g=>nn(e,g)),l=new Map;for(let g of e.rows.filter(b=>r.every(w=>de(e,b,w)))){let b={};for(let[d,u]of i.entries())b[u]=a[d](g);let w=JSON.stringify(Object.values(b)),p=l.get(w)??{key:b,rows:[]};p.rows.push(g),l.set(w,p)}i.length===0&&l.size===0&&l.set("[]",{key:{},rows:[]});let c=[...l.values()].map(({key:g,rows:b})=>{let w={...g};for(let[p,d]of s.entries())w[d]=o[p](b);return w});return D(200,c,{...O(e,null,[]),groups:i.length===0?null:c.length})}function en(e,n){if(n==="count")return i=>i.length;let t=/^(count|sum|avg|min|max)\(([^)]+)\)$/.exec(n);if(t===null)throw h(422,"query_invalid",`Unknown function: ${n}.`,{fn:["Allowed: count, count(field), sum(field), avg(field), min(field), max(field)."]},"unknown_function");let[,r,s]=t;if(!e.fields.has(s))throw h(422,"query_invalid",`Unknown fn: ${s}.`,{fn:[`Allowed values: ${[...e.fields.keys()].join(", ")}.`]},"not_aggregatable");return i=>{let o=i.map(l=>l[s]).filter(l=>l!=null),a=o.map(Number).filter(l=>Number.isFinite(l));switch(r){case"count":return o.length;case"sum":return a.reduce((l,c)=>l+c,0);case"avg":return a.length===0?null:a.reduce((l,c)=>l+c,0)/a.length;case"min":return o.length===0?null:o.reduce((l,c)=>S(c,l)<0?c:l);default:return o.length===0?null:o.reduce((l,c)=>S(c,l)>0?c:l)}}}function nn(e,n){let[t,r]=n.split(".");if(!e.fields.has(t))throw h(422,"query_invalid",`Unknown group: ${t}.`,{group:[`Allowed values: ${[...e.fields.keys()].join(", ")}.`]},"not_groupable");let s={year:4,month:7,day:10};if(r===void 0)return o=>o[t]??null;let i=s[r];if(i===void 0)throw h(422,"query_invalid",`Unknown truncation: ${r}.`,{group:["Allowed truncations in the preview: year, month, day."]},"unknown_truncation");return o=>typeof o[t]=="string"?o[t].slice(0,i):null}function tn(e,n,t){let r=t.fields===void 0?e.summary:j(e,x(e,{fields:t.fields})),s=JSON.stringify([n,t.fields??null]);if(t.idempotencyKey!==void 0){let l=e.replays.get(t.idempotencyKey);if(l!==void 0){if(l.body!==s)throw h(422,"validation_failed","This Idempotency-Key was already used with a different request body. Use a new key, or resend the original body.");return{...l.response,replayed:!0}}}let i=pe(e,n),o={id:an(e),...i};for(let l of e.fields.keys())l in o||(o[l]=null);e.rows.push(o),e.versions.set(_(o.id),1);let a=D(201,L(o,r),O(e,null,[]),N(e,o));return t.idempotencyKey!==void 0&&e.replays.set(t.idempotencyKey,{body:s,response:a}),a}function rn(e,n,t,r){let s=r.fields===void 0?e.summary:j(e,x(e,{fields:r.fields})),i=I(e,n);return ge(e,i,r.ifMatch),Object.assign(i,pe(e,t)),e.versions.set(_(i.id),(e.versions.get(_(i.id))??1)+1),D(200,L(i,s),O(e,null,[]),N(e,i))}function sn(e,n,t){let r=I(e,n);return ge(e,r,t.ifMatch),e.rows.splice(e.rows.indexOf(r),1),e.versions.delete(_(r.id)),D(204,null,null)}function pe(e,n){let t=[...e.writable],r=t.length===0?"This resource declares no writable fields.":`Writable fields: ${t.join(", ")}.`;if(!fe(n)||Object.keys(n).length===0)throw h(422,"validation_failed","The request body must be a JSON object naming at least one writable field.",{body:[r]});let s=Object.keys(n).filter(o=>!e.writable.has(o));if(s.length>0)throw h(422,"query_invalid",`Unknown or read-only fields: ${s.join(", ")}.`,Object.fromEntries(s.map(o=>[o,[r]])));let i={};for(let[o,a]of Object.entries(n)){let l=on(e.fields.get(o),a);l!==null&&(i[o]=[l])}if(Object.keys(i).length>0)throw h(422,"validation_failed","The request body failed validation.",i);return J(n)}function on(e,n){return e===void 0||e.types.length!==1?null:n===null?e.nullable?null:ae(e):(()=>{switch(e.types[0]){case"string":return typeof n=="string";case"integer":return typeof n=="number"&&Number.isInteger(n)||typeof n=="string"&&/^[+-]?\d+$/.test(n.trim());case"number":return typeof n=="number"&&Number.isFinite(n)||typeof n=="string"&&n.trim()!==""&&Number.isFinite(Number(n));case"boolean":return[!0,!1,0,1,"0","1"].includes(n);case"array":case"object":return typeof n=="object";default:return!0}})()?null:ae(e)}function ae(e){return`The ${e.name.replace(/_/g," ")} field must be ${{string:"a string",integer:"an integer",number:"a number",boolean:"true or false",array:"an array",object:"an array"}[e.types[0]]??"valid"}.`}function ge(e,n,t){if(t===void 0)return;if(t.startsWith("W/"))throw new TypeError("ifMatch takes recordVersion (X-Dmp-Record-Version), not the weak ETag.");let r=N(e,n);if(t!==r)throw h(409,"conflict","The record changed since the version you sent.",void 0,void 0,{"X-Dmp-Record-Version":r})}function an(e){let n=e.rows.map(t=>t.id);return n.length>0&&n.every(t=>typeof t=="string")&&n.some(t=>!/^\d+$/.test(t))?crypto.randomUUID():n.reduce((t,r)=>Math.max(t,Number(r)||0),0)+1}function I(e,n){let t=_(n);if(t===""||t==="."||t==="..")throw new TypeError(`${e.name}: '${t}' is not an id.`);let r=e.rows.find(s=>_(s.id)===t);if(r===void 0)throw h(404,"not_found",`No such ${e.name} record.`);return r}function le(e){return h(404,"not_found",`No such operation [${e}]. The preview runs no custom operations.`)}function L(e,n){let t={};for(let r of n)t[r]=r in e?J(e[r]):null;return t}function N(e,n){return`"preview-${_(n.id)}-${e.versions.get(_(n.id))??1}"`}function O(e,n,t){return{resource:e.name,contract:qe,page:n,groups:null,omitted:t,pruned:[],forbidden_fields:[]}}function D(e,n,t,r=null){let s=new Headers({"X-Dmp-Preview":"true"});return r!==null&&s.set("X-Dmp-Record-Version",r),{status:e,data:n,meta:t,headers:s,etag:null,recordVersion:r,location:null,replayed:!1,rateLimit:{limit:null,remaining:null,reset:null}}}function h(e,n,t,r,s,i={}){return new R(e,new Headers({"Content-Type":"application/problem+json",...i}),{type:`https://docs.cube.nl/dmp-api/problems/${n}`,title:n.replace(/_/g," "),status:e,code:n,...s!==void 0?{reason:s}:{},detail:t,...r!==void 0?{errors:r}:{}})}function ln(e,n){let t=e?.types[0];if((t==="integer"||t==="number")&&n!==""&&Number.isFinite(Number(n)))return Number(n);if(t==="boolean"){if(n==="1"||n==="true")return!0;if(n==="0"||n==="false")return!1}return n}function G(e,n){return e==null?!1:typeof e=="boolean"&&typeof n!="boolean"?(e?"1":"0")===String(n)||String(e)===String(n):typeof e=="number"&&typeof n=="string"?String(e)===n:e===n||typeof e!="object"&&String(e)===String(n)}function S(e,n){if(e==null||n==null)return e==null?n==null?0:-1:1;if(typeof e=="number"&&typeof n=="number")return e-n;if(typeof e=="boolean"||typeof n=="boolean")return+(e===!0||e===1||e==="1")-+(n===!0||n===1||n==="1");let t=String(e),r=String(n);return t<r?-1:t>r?1:0}function _(e){return e==null?"":String(e)}function fe(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function J(e){return e===void 0?e:JSON.parse(JSON.stringify(e))}var me="1.0.0-rc.3";var cn="./cube-app.config.json",dn="./cube-mock.json",pn="./cube-app.json",gn={id:"preview",name:"Voorbeeldgebruiker",scopes:[]},ye=null;async function fn(e={}){let n=e.fetch??globalThis.fetch.bind(globalThis),t=e.pageUrl??Tn(),r=await W(n,z(cn,t));return r===null?mn(e,n,t):yn(bn(r),e,n,t)}async function mn(e,n,t){let r=e.mock??ye??await W(n,z(dn,t))??{},s=e.manifest===!1?null:e.manifest??await W(n,z(pn,t)),i=!0,o=new C(r,s,()=>{if(!i)throw new R(401,new Headers({"Content-Type":"application/problem+json"}),{type:"https://docs.cube.nl/dmp-api/problems/token_invalid",title:"token invalid",status:401,code:"token_invalid",detail:"Unauthenticated."})});return{mode:"preview",signInError:null,user:()=>i?{...gn}:null,async signIn(){i=!0},async signOut(){i=!1},resource:a=>o.resource(a)}}async function yn(e,n,t,r){let s=e.dmpUrl.replace(/\/+$/,""),i=null,o=kn(n.tokenStorage==="memory"?M():Rn(`cube-app.tokens.${e.clientId}`),g=>{i=g}),a=Z({issuer:e.issuer??s,clientId:e.clientId,redirectUri:z(e.redirectUri,r),scopes:e.scopes,fetch:t,tokenStore:o,...e.metadata!==void 0?{metadata:e.metadata}:{}}),l=re({baseUrl:e.apiUrl??`${s}/apps/api/v1`,auth:a,fetch:t}),c=null;i=await o.get();try{await a.handleCallback()}catch(g){c=g instanceof Error?g:new Error(String(g))}return i===null&&c===null&&n.autoSignIn===!0&&await a.signIn(),{mode:"live",get signInError(){return c},user:()=>i===null?null:hn(i),async signIn(){c=null,await a.signIn()},async signOut(){await a.signOut()},resource:g=>l.resources(g)}}function hn(e){let n=wn(e.accessToken),t=n?.sub,r=Array.isArray(n?.scopes)?n.scopes.map(String):null;return{id:typeof t=="string"||typeof t=="number"?String(t):null,name:null,scopes:r??(e.scope??"").split(" ").filter(s=>s!=="")}}function wn(e){let n=e.split(".")[1];if(n===void 0)return null;try{let t=n.replace(/-/g,"+").replace(/_/g,"/"),r=atob(t.padEnd(t.length+(4-t.length%4)%4,"=")),s=new TextDecoder().decode(Uint8Array.from(r,o=>o.charCodeAt(0))),i=JSON.parse(s);return i!==null&&typeof i=="object"?i:null}catch{return null}}function bn(e){let n=["dmpUrl","clientId","redirectUri"].filter(r=>typeof e[r]!="string"||e[r]===""),t=e.scopes;if(typeof t=="string"||Array.isArray(t)&&t.every(r=>typeof r=="string")||n.push("scopes"),n.length>0)throw new TypeError(`cube-app.config.json is incomplete: ${n.join(", ")} missing or not a string.`);return e}async function W(e,n){let t;try{t=await e(n,{headers:{Accept:"application/json"},...typeof document<"u"?{cache:"no-store"}:{}})}catch{return null}if(t.status===404||t.status===410)return null;if(!t.ok)throw new Error(`${n} answered ${t.status}.`);try{let r=JSON.parse(await t.text());return r!==null&&typeof r=="object"&&!Array.isArray(r)?r:null}catch{return null}}function kn(e,n){return{async get(){let t=await e.get();return n(t),t},async set(t){await e.set(t),n(t)}}}function Rn(e){let n=(()=>{try{return globalThis.sessionStorage??null}catch{return null}})();return n===null?M():{get(){try{let t=n.getItem(e);return t===null?null:JSON.parse(t)}catch{return null}},set(t){try{t===null?n.removeItem(e):n.setItem(e,JSON.stringify(t))}catch{}}}}function Tn(){let e=globalThis;return e.document?.baseURI??e.location?.href??"http://localhost/"}function z(e,n){return new URL(e,n).href}function Sn(e){ye=e}var he=Object.freeze({connect:fn,mock:Sn,version:me,sdk:"1"});globalThis.CubeApp=he;})();
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The codes cube/api promises (README, "Errors"). The list is closed on the API side; `code` on a
|
|
3
|
+
* DmpError is one of these, an OAuth `error` from the token endpoint, or null for a response that
|
|
4
|
+
* was not a problem at all (a proxy's 502 page, say). Decide on `code`, never on `detail`.
|
|
5
|
+
*/
|
|
6
|
+
export type ErrorCode = 'token_invalid' | 'resource_forbidden' | 'client_scope_exceeded' | 'client_inactive' | 'client_without_user' | 'act_as_forbidden' | 'act_as_unknown' | 'not_found' | 'unsupported_action' | 'conflict' | 'query_invalid' | 'validation_failed' | 'cardinality_exceeded' | 'field_forbidden' | 'rate_limited' | (string & {});
|
|
7
|
+
/** The finer classification under `code`, when the engine gives one. Absent means "none finer". */
|
|
8
|
+
export type ErrorReason = 'resource_forbidden' | 'field_forbidden' | 'unknown_resource' | 'too_many_groups' | 'too_many_dimensions' | 'too_many_relations' | 'limit_too_high' | 'unknown_field' | 'unknown_operator' | 'not_filterable' | 'not_sortable' | 'unknown_sort_direction' | 'not_groupable' | 'not_aggregatable' | 'unknown_function' | 'unknown_truncation' | 'unknown_relation' | 'include_too_deep' | 'cursor_invalid' | 'not_countable' | 'empty_range' | 'value_shape_invalid' | 'contract_unsupported' | (string & {});
|
|
9
|
+
/** An RFC 9457 problem document as cube/api writes it, or an RFC 6749 OAuth error. */
|
|
10
|
+
export interface ProblemBody {
|
|
11
|
+
type?: string;
|
|
12
|
+
title?: string;
|
|
13
|
+
status?: number;
|
|
14
|
+
code?: string;
|
|
15
|
+
reason?: string;
|
|
16
|
+
detail?: string;
|
|
17
|
+
errors?: Record<string, string[]>;
|
|
18
|
+
error?: string;
|
|
19
|
+
error_description?: string;
|
|
20
|
+
[extension: string]: unknown;
|
|
21
|
+
}
|
|
22
|
+
/** A refusal. Thrown for every non-2xx answer (and for a 304 nobody asked for). */
|
|
23
|
+
export declare class DmpError extends Error {
|
|
24
|
+
readonly status: number;
|
|
25
|
+
readonly code: ErrorCode | null;
|
|
26
|
+
readonly reason: ErrorReason | null;
|
|
27
|
+
readonly type: string | null;
|
|
28
|
+
readonly title: string | null;
|
|
29
|
+
readonly detail: string | null;
|
|
30
|
+
/** Per field or parameter: `errors.sort`, `errors['Idempotency-Key']`. */
|
|
31
|
+
readonly errors: Record<string, string[]> | null;
|
|
32
|
+
/** Seconds, from `Retry-After` on a 429. */
|
|
33
|
+
readonly retryAfter: number | null;
|
|
34
|
+
/** The record's current version on a 409 `conflict`: fetch-free retry material. */
|
|
35
|
+
readonly recordVersion: string | null;
|
|
36
|
+
/**
|
|
37
|
+
* `Allow` on a 405. cube/api lists the resource's actions here (`list, get, create`), not HTTP
|
|
38
|
+
* methods. Null in a browser on another origin: the API does not expose `Allow` through CORS.
|
|
39
|
+
*/
|
|
40
|
+
readonly allow: string[] | null;
|
|
41
|
+
readonly headers: Headers;
|
|
42
|
+
/** The parsed body, or the raw text when it was not JSON. */
|
|
43
|
+
readonly body: unknown;
|
|
44
|
+
constructor(status: number, headers: Headers, body: unknown);
|
|
45
|
+
/** True when the body was an actual problem document with a `code`, not just an HTTP status. */
|
|
46
|
+
get isProblem(): boolean;
|
|
47
|
+
}
|
|
48
|
+
export declare function isDmpError(value: unknown): value is DmpError;
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** A refusal. Thrown for every non-2xx answer (and for a 304 nobody asked for). */
|
|
2
|
+
export class DmpError extends Error {
|
|
3
|
+
status;
|
|
4
|
+
code;
|
|
5
|
+
reason;
|
|
6
|
+
type;
|
|
7
|
+
title;
|
|
8
|
+
detail;
|
|
9
|
+
/** Per field or parameter: `errors.sort`, `errors['Idempotency-Key']`. */
|
|
10
|
+
errors;
|
|
11
|
+
/** Seconds, from `Retry-After` on a 429. */
|
|
12
|
+
retryAfter;
|
|
13
|
+
/** The record's current version on a 409 `conflict`: fetch-free retry material. */
|
|
14
|
+
recordVersion;
|
|
15
|
+
/**
|
|
16
|
+
* `Allow` on a 405. cube/api lists the resource's actions here (`list, get, create`), not HTTP
|
|
17
|
+
* methods. Null in a browser on another origin: the API does not expose `Allow` through CORS.
|
|
18
|
+
*/
|
|
19
|
+
allow;
|
|
20
|
+
headers;
|
|
21
|
+
/** The parsed body, or the raw text when it was not JSON. */
|
|
22
|
+
body;
|
|
23
|
+
constructor(status, headers, body) {
|
|
24
|
+
const problem = body !== null && typeof body === 'object' ? body : {};
|
|
25
|
+
const detail = str(problem.detail) ?? str(problem.error_description);
|
|
26
|
+
super(detail ?? `HTTP ${status}`);
|
|
27
|
+
this.name = 'DmpError';
|
|
28
|
+
this.status = status;
|
|
29
|
+
this.code = str(problem.code) ?? str(problem.error);
|
|
30
|
+
this.reason = str(problem.reason);
|
|
31
|
+
this.type = str(problem.type);
|
|
32
|
+
this.title = str(problem.title);
|
|
33
|
+
this.detail = detail;
|
|
34
|
+
this.errors = isErrors(problem.errors) ? problem.errors : null;
|
|
35
|
+
this.retryAfter = seconds(headers.get('Retry-After'));
|
|
36
|
+
this.recordVersion = headers.get('X-Dmp-Record-Version');
|
|
37
|
+
this.allow = headers.has('Allow')
|
|
38
|
+
? (headers.get('Allow') ?? '').split(',').map((method) => method.trim()).filter((method) => method !== '')
|
|
39
|
+
: null;
|
|
40
|
+
this.headers = headers;
|
|
41
|
+
this.body = body;
|
|
42
|
+
}
|
|
43
|
+
/** True when the body was an actual problem document with a `code`, not just an HTTP status. */
|
|
44
|
+
get isProblem() {
|
|
45
|
+
return this.code !== null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function isDmpError(value) {
|
|
49
|
+
return value instanceof DmpError;
|
|
50
|
+
}
|
|
51
|
+
function str(value) {
|
|
52
|
+
return typeof value === 'string' ? value : null;
|
|
53
|
+
}
|
|
54
|
+
function isErrors(value) {
|
|
55
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
56
|
+
}
|
|
57
|
+
function seconds(value) {
|
|
58
|
+
if (value === null || value.trim() === '') {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const asNumber = Number(value);
|
|
62
|
+
if (Number.isFinite(asNumber)) {
|
|
63
|
+
return asNumber;
|
|
64
|
+
}
|
|
65
|
+
// RFC 9110 also allows an HTTP date. cube/api sends seconds, a proxy in front of it may not.
|
|
66
|
+
const date = Date.parse(value);
|
|
67
|
+
return Number.isNaN(date) ? null : Math.max(0, Math.ceil((date - Date.now()) / 1000));
|
|
68
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { createClient, newIdempotencyKey, type ClientOptions, type CreateOptions, type DmpClient, type OperationOptions, type RequestOptions, type ResourceClient, type WriteOptions, } from './client.js';
|
|
2
|
+
export { bearer, createOAuth, discoverMetadata, memoryKeyValueStore, memoryTokenStore, registerClient, type AuthStrategy, type AuthorizationServerMetadata, type KeyValueStore, type OAuthOptions, type OAuthSession, type RegistrationRequest, type TokenSource, type TokenStore, type Tokens, } from './auth.js';
|
|
3
|
+
export { DmpError, isDmpError, type ErrorCode, type ErrorReason, type ProblemBody } from './errors.js';
|
|
4
|
+
export { buildQuery } from './query.js';
|
|
5
|
+
export type * from './types.js';
|
|
6
|
+
export { CubeApp, type ConnectOptions, type CubeAppConfig, type CubeAppSession, type CubeAppStatic, type CubeAppUser, } from './app.js';
|
|
7
|
+
export type { AppManifest, MockData, MockField, MockResource, MockSchema } from './preview.js';
|
|
8
|
+
export { VERSION } from './version.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createClient, newIdempotencyKey, } from './client.js';
|
|
2
|
+
export { bearer, createOAuth, discoverMetadata, memoryKeyValueStore, memoryTokenStore, registerClient, } from './auth.js';
|
|
3
|
+
export { DmpError, isDmpError } from './errors.js';
|
|
4
|
+
export { buildQuery } from './query.js';
|
|
5
|
+
export { CubeApp, } from './app.js';
|
|
6
|
+
export { VERSION } from './version.js';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { ResourceClient } from './client.js';
|
|
2
|
+
import type { DmpRecord } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* One field as `dmp.describe` (and so `cube.mock_data`) writes it: a JSON Schema. `type` is a
|
|
5
|
+
* name, or a list of names where `'null'` makes it nullable. `_capabilities`' own field shape
|
|
6
|
+
* (`{ name, type: 'string|integer', nullable, readonly }`) is read too.
|
|
7
|
+
*/
|
|
8
|
+
export interface MockField {
|
|
9
|
+
type?: string | readonly string[];
|
|
10
|
+
format?: string;
|
|
11
|
+
enum?: readonly unknown[];
|
|
12
|
+
description?: string | null;
|
|
13
|
+
nullable?: boolean;
|
|
14
|
+
readonly?: boolean;
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
/** The `schema` half of one resource in `cube.mock_data`'s output: the `dmp.describe` payload. */
|
|
18
|
+
export interface MockSchema {
|
|
19
|
+
name?: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
actions?: readonly string[];
|
|
22
|
+
/** The filterable fields. Absent: every field. */
|
|
23
|
+
filters?: readonly string[];
|
|
24
|
+
/** Fields a write may not set. `id` never is writable, listed or not. */
|
|
25
|
+
readonly?: readonly string[];
|
|
26
|
+
/** What a row carries when the query names no `fields`, as on the REST face. Absent: every field. */
|
|
27
|
+
summary_fields?: readonly string[];
|
|
28
|
+
max_results?: number;
|
|
29
|
+
fields?: Record<string, MockField> | ReadonlyArray<MockField & {
|
|
30
|
+
name: string;
|
|
31
|
+
}>;
|
|
32
|
+
sample_row?: DmpRecord;
|
|
33
|
+
[key: string]: unknown;
|
|
34
|
+
}
|
|
35
|
+
export interface MockResource {
|
|
36
|
+
schema?: MockSchema;
|
|
37
|
+
rows?: readonly DmpRecord[];
|
|
38
|
+
}
|
|
39
|
+
/** `cube.mock_data`'s output as is: resource name => `{ schema, rows }`. */
|
|
40
|
+
export type MockData = Record<string, MockResource>;
|
|
41
|
+
/** The part of `cube-app.json` the preview reads: what the app declared it uses. */
|
|
42
|
+
export interface AppManifest {
|
|
43
|
+
name?: string;
|
|
44
|
+
description?: string;
|
|
45
|
+
uses?: Record<string, readonly string[]>;
|
|
46
|
+
sdk?: string;
|
|
47
|
+
[key: string]: unknown;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The preview's data: every resource of the mock data, in memory, changed by create, update and
|
|
51
|
+
* delete until the page is reloaded. It answers as the REST face does — the same envelope, the same
|
|
52
|
+
* paging, the same refusals as `DmpError` — and never touches the network.
|
|
53
|
+
*/
|
|
54
|
+
export declare class PreviewStore {
|
|
55
|
+
private readonly manifest;
|
|
56
|
+
/** Runs before every call; throw to refuse it (the preview's signed-out state). */
|
|
57
|
+
private readonly guard;
|
|
58
|
+
private readonly tables;
|
|
59
|
+
constructor(data: MockData, manifest?: AppManifest | null,
|
|
60
|
+
/** Runs before every call; throw to refuse it (the preview's signed-out state). */
|
|
61
|
+
guard?: () => void);
|
|
62
|
+
resourceNames(): string[];
|
|
63
|
+
resource<T extends DmpRecord = DmpRecord>(name: string): ResourceClient<T>;
|
|
64
|
+
/**
|
|
65
|
+
* Unknown resource first (404, as the REST face answers it), then the manifest: live, the app's
|
|
66
|
+
* OAuth client is capped to its `uses`, so the preview refuses what the live app would.
|
|
67
|
+
*/
|
|
68
|
+
private authorize;
|
|
69
|
+
}
|