ofplatform-web 0.1.0-alpha.1 → 0.1.0-alpha.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/dist/adapters/db/IndexedDbAdapter.d.ts +3 -0
- package/dist/adapters/socket/WebSocketAdapter.d.ts +1 -0
- package/dist/auth/WebServerAuthoritativeAuthHostSupport.d.ts +57 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +5 -1
|
@@ -24,9 +24,12 @@ export declare class IndexedDbAdapter implements DbAdapter {
|
|
|
24
24
|
private readonly tableNamePrefix;
|
|
25
25
|
private tableNameResolver?;
|
|
26
26
|
private dbPromise?;
|
|
27
|
+
private dbInstance?;
|
|
27
28
|
private transactionDepth;
|
|
28
29
|
constructor(options?: IndexedDbAdapterOptions);
|
|
29
30
|
setTableNameResolver(resolver: (logicalTableName: string) => string): void;
|
|
31
|
+
close(): Promise<void>;
|
|
32
|
+
closeSync(): void;
|
|
30
33
|
resolveTableName(tableName: string): string;
|
|
31
34
|
private openDatabase;
|
|
32
35
|
private getDb;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type AuthoritySessionEnvelope, type CachedIdentityEntry } from 'ofauth-shared-core';
|
|
2
|
+
import { type StorageBackend } from '../adapters/storage/WebStorageAdapter';
|
|
3
|
+
type CacheRecord = {
|
|
4
|
+
identity?: CachedIdentityEntry;
|
|
5
|
+
envelope?: AuthoritySessionEnvelope;
|
|
6
|
+
};
|
|
7
|
+
export type WebServerAuthoritativeAuthHostSupportOptions = {
|
|
8
|
+
serverBaseUrl?: string;
|
|
9
|
+
cacheKey: string;
|
|
10
|
+
targetTenantId?: string;
|
|
11
|
+
targetBranchId?: string;
|
|
12
|
+
targetDomainId?: string;
|
|
13
|
+
storage?: StorageBackend;
|
|
14
|
+
fetchImpl?: typeof fetch;
|
|
15
|
+
isOnline?: () => boolean;
|
|
16
|
+
verifyOfflineSecret: (params: {
|
|
17
|
+
principal: string;
|
|
18
|
+
secret: string;
|
|
19
|
+
}) => Promise<boolean>;
|
|
20
|
+
};
|
|
21
|
+
export type WebServerAuthoritativeLoginResult = {
|
|
22
|
+
status: 'authenticated';
|
|
23
|
+
mode?: 'online' | 'offline';
|
|
24
|
+
} | {
|
|
25
|
+
status: 'rejected';
|
|
26
|
+
reasonCode?: string;
|
|
27
|
+
};
|
|
28
|
+
export type RestoredAuthEnvelope = {
|
|
29
|
+
principal: string;
|
|
30
|
+
envelope: AuthoritySessionEnvelope;
|
|
31
|
+
};
|
|
32
|
+
export declare class WebServerAuthoritativeAuthHostSupport {
|
|
33
|
+
private readonly options;
|
|
34
|
+
private readonly serverBaseUrl;
|
|
35
|
+
private readonly cacheKey;
|
|
36
|
+
private readonly targetTenantId;
|
|
37
|
+
private readonly targetBranchId;
|
|
38
|
+
private readonly targetDomainId;
|
|
39
|
+
private readonly storage;
|
|
40
|
+
private readonly fetchImpl;
|
|
41
|
+
private readonly isOnlineResolver;
|
|
42
|
+
private readonly serverBridge;
|
|
43
|
+
constructor(options: WebServerAuthoritativeAuthHostSupportOptions);
|
|
44
|
+
get isServerConfigured(): boolean;
|
|
45
|
+
loadCacheMap(): Record<string, CacheRecord>;
|
|
46
|
+
saveCacheMap(next: Record<string, CacheRecord>): void;
|
|
47
|
+
readCacheRecord(principal: string): CacheRecord | null;
|
|
48
|
+
writeCacheRecord(principal: string, record: CacheRecord): void;
|
|
49
|
+
clearCachedSessionEnvelope(principal: string): void;
|
|
50
|
+
clearAllCachedSessionEnvelopes(): void;
|
|
51
|
+
login(principal: string, secret: string): Promise<WebServerAuthoritativeLoginResult>;
|
|
52
|
+
logout(principal: string): Promise<void>;
|
|
53
|
+
restoreCachedEnvelope(): Promise<RestoredAuthEnvelope | null>;
|
|
54
|
+
getStoredAccessToken(principal: string): string;
|
|
55
|
+
getStoredEnvelope(principal: string): AuthoritySessionEnvelope | null;
|
|
56
|
+
}
|
|
57
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './WebServerAuthoritativeAuthHostSupport';
|
package/dist/index.d.ts
CHANGED
package/dist/index.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var T="__ofcore_meta__",k="__schema_version__",R="__table_schemas__";function v(a){return typeof structuredClone=="function"?structuredClone(a):JSON.parse(JSON.stringify(a))}function M(a){return!!(a&&typeof a=="object"&&!Array.isArray(a)&&"and"in a&&Array.isArray(a.and))}function F(a){return!!(a&&typeof a=="object"&&!Array.isArray(a)&&"or"in a&&Array.isArray(a.or))}function g(a){return new Promise((e,t)=>{a.onsuccess=()=>e(a.result),a.onerror=()=>{var r;return t((r=a.error)!=null?r:new Error("IndexedDB request failed"))}})}function P(a){return new Promise((e,t)=>{a.oncomplete=()=>e(),a.onerror=()=>{var r;return t((r=a.error)!=null?r:new Error("IndexedDB transaction failed"))},a.onabort=()=>{var r;return t((r=a.error)!=null?r:new Error("IndexedDB transaction aborted"))}})}var D=class{constructor(e={}){this.transactionDepth=0;var t,r;this.dbName=(t=e.dbName)!=null?t:"ofcore-app",this.initialDbVersion=e.initialDbVersion,this.tableNamePrefix=(r=e.tableNamePrefix)!=null?r:"",this.tableNameResolver=e.tableNameMapper}setTableNameResolver(e){this.tableNameResolver=e}resolveTableName(e){return e===T?T:this.tableNameResolver?String(this.tableNameResolver(e)||"").trim()||e:!this.tableNamePrefix||e.startsWith(this.tableNamePrefix)?e:`${this.tableNamePrefix}${e}`}async openDatabase(e){let t=typeof e=="number"?indexedDB.open(this.dbName,e):indexedDB.open(this.dbName);return t.onupgradeneeded=()=>{let r=t.result;r.objectStoreNames.contains(T)||r.createObjectStore(T,{keyPath:"key"})},new Promise((r,s)=>{t.onsuccess=()=>{let n=t.result;n.onversionchange=()=>n.close(),r(n)},t.onerror=()=>{let n=t.error;if((n==null?void 0:n.name)==="VersionError"){s(new Error(`IndexedDB version mismatch for "${this.dbName}": requested version is lower than existing database version`));return}s(n!=null?n:new Error("Failed to open IndexedDB"))},t.onblocked=()=>s(new Error("IndexedDB open blocked by another tab/process"))})}async getDb(){return this.dbPromise||(this.dbPromise=(async()=>{try{return await this.openDatabase(this.initialDbVersion)}catch(e){if(this.initialDbVersion!==void 0&&e instanceof Error&&e.message.includes("version mismatch"))return this.openDatabase();throw e}})()),this.dbPromise}async reopenWithUpgrade(e){let t=await this.getDb(),r=t.version+1;t.close(),this.dbPromise=void 0;let s=indexedDB.open(this.dbName,r);s.onupgradeneeded=()=>{let i=s.result;i.objectStoreNames.contains(T)||i.createObjectStore(T,{keyPath:"key"}),e(i)};let n=await new Promise((i,o)=>{s.onsuccess=()=>{let c=s.result;c.onversionchange=()=>c.close(),i(c)},s.onerror=()=>{var c;return o((c=s.error)!=null?c:new Error("Failed to upgrade IndexedDB"))},s.onblocked=()=>o(new Error("IndexedDB upgrade blocked by another tab/process"))});return this.dbPromise=Promise.resolve(n),n}async withStore(e,t,r){let n=(await this.getDb()).transaction(e,t),i=n.objectStore(e),o=await r(i);return await P(n),o}async getMetaValue(e,t){return(await this.getDb()).objectStoreNames.contains(T)?this.withStore(T,"readonly",async s=>{var i;let n=await g(s.get(e));return(i=n==null?void 0:n.value)!=null?i:t}):t}async setMetaValue(e,t){await this.withStore(T,"readwrite",async r=>{await g(r.put({key:e,value:t}))})}async getTableSchemas(){return this.getMetaValue(R,{})}async setTableSchemas(e){await this.setMetaValue(R,e)}async tableExists(e){let t=this.resolveTableName(e);return(await this.getDb()).objectStoreNames.contains(t)}async ensureTable(e){let t=this.resolveTableName(e);await this.tableExists(e)||await this.reopenWithUpgrade(r=>{r.objectStoreNames.contains(t)||r.createObjectStore(t,{keyPath:"id"})})}async listRows(e){return await this.tableExists(e)?this.withStore(this.resolveTableName(e),"readonly",async r=>g(r.getAll())):[]}async putRows(e,t){await this.ensureTable(e);let r=this.resolveTableName(e),n=(await this.getDb()).transaction(r,"readwrite"),i=n.objectStore(r);await g(i.clear());for(let o of t)await g(i.put(o));await P(n)}normalizeForComparison(e){if(e instanceof Date)return e.getTime();if(typeof e=="boolean")return e?1:0;if(Array.isArray(e))return e.map(t=>this.normalizeForComparison(t));if(e&&typeof e=="object")try{return JSON.stringify(e)}catch{return String(e)}return e}evalValueExpr(e,t,r,s="t0"){if(r.type==="column"){let n=r.tableAlias||s,i=n===s?e:t[n]||{};return this.normalizeForComparison(i[r.field])}return this.normalizeForComparison(r.value)}buildFieldCondition(e,t){if("field"in e)return{left:{type:"column",tableAlias:e.alias||t,field:e.field},operator:e.operator||"=",right:{type:"literal",value:e.value},...e.operator==="BETWEEN"?{}:{likeMode:e.likeMode}};if("left"in e)return e;throw new Error("Invalid field condition")}matchFilterExpression(e,t,r,s="t0"){if(M(r))return r.and.every(n=>this.matchFilterExpression(e,t,n,s));if(F(r))return r.or.some(n=>this.matchFilterExpression(e,t,n,s));if(r!==null&&typeof r=="object"&&!Array.isArray(r)){if("left"in r||"field"in r){let n=this.buildFieldCondition(r,s),i=this.evalValueExpr(e,t,n.left,s),o=this.evalValueExpr(e,t,n.right,s);if(n.operator==="IS NULL")return i==null;if(n.operator==="IS NOT NULL")return i!=null;if(n.operator==="IN"||n.operator==="NOT IN"){if(!Array.isArray(o))throw new Error("IN/NOT IN requires array as right value");let c=o.includes(i);return n.operator==="IN"?c:!c}if(n.operator==="BETWEEN")return!Array.isArray(o)||o.length!==2?!1:i>=o[0]&&i<=o[1];switch(n.operator){case"=":return i===o;case"!=":return i!==o;case"<":return i<o;case"<=":return i<=o;case">":return i>o;case">=":return i>=o;case"LIKE":{if(typeof i!="string"||typeof o!="string")return!1;let c=o.replace(/%/g,".*").replace(/_/g,".");return new RegExp(`^${c}$`,n.likeMode==="case-insensitive"?"i":"").test(i)}default:throw new Error(`Unsupported operator: ${n.operator}`)}}return Object.entries(r).every(([n,i])=>this.normalizeForComparison(e[n])===this.normalizeForComparison(i))}throw new Error(`Invalid filter expression: ${JSON.stringify(r)}`)}async simulateJoins(e,t){if(t.length===0)return e.map(n=>({record:n,joined:{}}));let r={};for(let n of t)r[n.alias]=await this.listRows(n.table);let s=[];for(let n of e){let i={},o=!0;for(let c of t){let l=r[c.alias]||[],d=null;if(c.conditions.length===1){let u=c.conditions[0];if(u.operator==="="&&u.left.type==="column"&&u.right.type==="column"){let b=u.left.tableAlias||"t0",h=u.right.type==="column"?u.right.tableAlias:void 0;if(b==="t0"&&u.left.type==="column"&&h===c.alias&&u.right.type==="column"){let y=this.normalizeForComparison(n[u.left.field]),w=u.right.field;d=l.find(m=>this.normalizeForComparison(m[w])===y)}}}if(!d&&(c.type==="inner"||!c.type)){o=!1;break}i[c.alias]=d!=null?d:null}o&&s.push({record:n,joined:i})}return s}extractField(e,t,r,s="t0"){if(typeof r=="string")return this.normalizeForComparison(e[r]);let n=r.tableAlias||s,i=n===s?e:t[n]||{};return this.normalizeForComparison(i[r.field])}extractAggregateField(e,t,r,s="t0"){if(typeof r=="string")return this.normalizeForComparison(e[r]);let n=r.tableAlias||s,i=n===s?e:t[n]||{};return this.normalizeForComparison(i[r.field])}async getSchemaVersion(){return this.getMetaValue(k,0)}async setSchemaVersion(e){await this.setMetaValue(k,e)}async addTable(e){await this.ensureTable(e.name);let t=await this.getTableSchemas();t[e.name]=v(e),await this.setTableSchemas(t)}async addColumn(e,t){var n;let r=await this.getTableSchemas(),s=(n=r[e])!=null?n:{name:e,columns:[],skipMetadataColumns:!0};s.columns.find(i=>i.name===t.name)||s.columns.push(v(t)),r[e]=s,await this.setTableSchemas(r)}async get(e,t){var n;return await this.tableExists(e)?typeof t=="string"?this.withStore(this.resolveTableName(e),"readonly",async i=>{let o=await g(i.get(t));return o!=null?o:null}):(n=(await this.query(e,{...t,limit:1}))[0])!=null?n:null:null}async query(e,t={}){var d,u;if(!await this.tableExists(e))return[];let s=(d=t.mainAlias)!=null?d:"t0",n=await this.listRows(e),i=await this.simulateJoins(n,t.joins||[]);if(t.filters&&(i=i.filter(({record:b,joined:h})=>this.matchFilterExpression(b,h,t.filters,s))),t.aggregates&&t.aggregates.length>0){let b={};for(let h of t.aggregates){let y=i;h.filter&&(y=y.filter(({record:p,joined:S})=>this.matchFilterExpression(p,S,h.filter,s)));let w=y.map(({record:p,joined:S})=>this.extractAggregateField(p,S,h.field,s)),m=w.map(p=>Number(p)).filter(p=>Number.isFinite(p)),f=null;switch(h.function){case"COUNT":f=w.filter(p=>p!=null).length;break;case"SUM":f=m.reduce((p,S)=>p+S,0);break;case"AVG":f=m.length?m.reduce((p,S)=>p+S,0)/m.length:0;break;case"MIN":f=m.length?Math.min(...m):null;break;case"MAX":f=m.length?Math.max(...m):null;break;default:throw new Error(`Unsupported aggregate function: ${h.function}`)}b[h.as]=f}return[b]}t.sort&&t.sort.length>0&&i.sort((b,h)=>{for(let y of t.sort){let w=this.extractField(b.record,b.joined,y.field,s),m=this.extractField(h.record,h.joined,y.field,s);if(w===m)continue;if(w==null)return y.direction==="asc"?-1:1;if(m==null)return y.direction==="asc"?1:-1;let f=w>m?1:-1;return y.direction==="asc"?f:-f}return 0});let o=i.map(({record:b,joined:h})=>{var m;if(!t.fields)return b;if(((m=t.resultShape)!=null?m:"flat")==="nested"){let f={};for(let[p,S]of Object.entries(t.fields)){let x=p===s?b:h[p]||{},E={};for(let A of S)E[A]=x[A];f[p]=E}return f}let w={};for(let[f,p]of Object.entries(t.fields)){let S=f===s?b:h[f]||{};for(let x of p)w[`${f}_${x}`]=S[x]}return w}),c=(u=t.offset)!=null?u:0,l=t.limit!=null?c+t.limit:void 0;return o.slice(c,l)}async create(e,t){await this.ensureTable(e);let r=t.id;if(!r||typeof r!="string")throw new Error(`create(${e}) requires string id`);return this.withStore(this.resolveTableName(e),"readwrite",async s=>{if(await g(s.get(r)))throw new Error(`Record already exists: ${e}#${r}`);return await g(s.put(v(t))),v(t)})}async update(e,t,r){if(!await this.tableExists(e))throw new Error(`Table not found: ${e}`);return this.withStore(this.resolveTableName(e),"readwrite",async n=>{let i=await g(n.get(t));if(!i)throw new Error(`Record not found: ${e}#${t}`);let o={...i,...v(r),id:t};return await g(n.put(o)),v(o)})}async delete(e,t){await this.tableExists(e)&&await this.withStore(this.resolveTableName(e),"readwrite",async s=>{await g(s.delete(t))})}async bulkCreate(e,t){await this.ensureTable(e);let r=this.resolveTableName(e),n=(await this.getDb()).transaction(r,"readwrite"),i=n.objectStore(r),o=[];for(let c of t){if(!c.id||typeof c.id!="string")throw n.abort(),new Error(`bulkCreate(${e}) requires string id per row`);let l=c.id;if(typeof l!="string")throw n.abort(),new Error(`bulkCreate(${e}) requires string id per row`);if(await g(i.get(l)))throw n.abort(),new Error(`Record already exists: ${e}#${l}`);let u=v(c);await g(i.put(u)),o.push(u)}return await P(n),o}async bulkUpdate(e,t){if(!await this.tableExists(e))throw new Error(`Table not found: ${e}`);let s=this.resolveTableName(e),i=(await this.getDb()).transaction(s,"readwrite"),o=i.objectStore(s),c=[];for(let l of t){let d=await g(o.get(l.id));if(!d)throw i.abort(),new Error(`Record not found: ${e}#${l.id}`);let u={...d,...v(l.updates),id:l.id};await g(o.put(u)),c.push(v(u))}return await P(i),c}async dataStores(){let e=await this.getDb();return Array.from(e.objectStoreNames).filter(t=>t!==T)}async takeSnapshot(){let e=await this.dataStores(),t={};for(let r of e)t[r]=await this.listRows(r);return t}async restoreSnapshot(e){var r;let t=await this.dataStores();for(let s of t){let n=(r=e[s])!=null?r:[];await this.putRows(s,n)}}async transaction(e){let t=this.transactionDepth===0,r=null;t&&(r=await this.takeSnapshot()),this.transactionDepth+=1;try{let s=await e(this);return this.transactionDepth-=1,s}catch(s){throw this.transactionDepth-=1,t&&r&&await this.restoreSnapshot(r),s}}async exportBackupPayload(){let e=await this.getDb(),[t,r,s]=await Promise.all([this.getMetaValue(k,0),this.getTableSchemas(),this.takeSnapshot()]);return{schema:"ofplatform-web-indexeddb-backup-v1",dbName:this.dbName,dbVersion:e.version,exportedAt:new Date().toISOString(),schemaVersion:t,tableSchemas:r,tables:s}}async importBackupPayload(e,t={}){var n,i,o,c;if(e.schema!=="ofplatform-web-indexeddb-backup-v1")throw new Error("Invalid backup payload schema");let r=(n=t.clearExisting)!=null?n:!0,s=Object.keys((i=e.tables)!=null?i:{});for(let l of s)await this.ensureTable(l);if(r){let l=await this.dataStores();for(let d of l)s.includes(d)||await this.putRows(d,[])}for(let l of s){let d=Array.isArray(e.tables[l])?e.tables[l]:[];await this.putRows(l,d)}await this.setMetaValue(k,(o=e.schemaVersion)!=null?o:0),await this.setTableSchemas((c=e.tableSchemas)!=null?c:{})}};function W(a,e){if(!e||Object.keys(e).length===0)return a;let t=new URLSearchParams;for(let[s,n]of Object.entries(e))n!=null&&t.set(s,String(n));let r=t.toString();return r?a.includes("?")?`${a}&${r}`:`${a}?${r}`:a}var O=class{constructor(e={}){let t=e.fetch;if(t)this.fetchImpl=t;else{let r=globalThis.fetch;if(typeof r!="function")throw new Error("FetchHttpAdapter: globalThis.fetch is not available. Pass a fetch implementation via options.fetch.");this.fetchImpl=r.bind(globalThis)}}async request(e){var n,i;let t=W(e.url,e.query),r=typeof AbortController!="undefined"?new AbortController:null,s=r&&e.timeoutMs?setTimeout(()=>r.abort(),e.timeoutMs):null;try{let o=e.body!==void 0&&e.body!==null,c={...(n=e.headers)!=null?n:{}};o&&!c["content-type"]&&!c["Content-Type"]&&(c["content-type"]="application/json");let l=await this.fetchImpl(t,{method:e.method,headers:c,body:o?JSON.stringify(e.body):void 0,signal:r==null?void 0:r.signal}),d={};l.headers.forEach((h,y)=>{d[y.toLowerCase()]=h});let b=((i=d["content-type"])!=null?i:"").includes("application/json")?await l.json():await l.text();return{status:l.status,headers:d,data:b}}finally{s!==null&&clearTimeout(s)}}};var L=!0;var I=class{constructor(e={}){var t;if(this.env=(t=e.env)!=null?t:{},e.crypto)this.cryptoImpl=e.crypto;else{let r=globalThis.crypto;if(!(r!=null&&r.subtle))throw new Error("WebPlatformAdapter: globalThis.crypto.subtle is not available. Pass a crypto implementation via options.crypto.");this.cryptoImpl=r}}getEnvVar(e){return this.env[e]}isOnline(){let e=globalThis.navigator;return e&&typeof e.onLine!="undefined"?!!e.onLine:!0}async hashPin(e){return`sha256:${await this.sha256Hex(e)}`}async verifyPin(e,t){if(!t.startsWith("sha256:"))return!1;let r=t.slice(7);return/^[0-9a-f]{64}$/.test(r)?await this.sha256Hex(e)===r:r===e}async sha256Hex(e){let r=new TextEncoder().encode(e),s=await this.cryptoImpl.subtle.digest("SHA-256",r);return Array.from(new Uint8Array(s)).map(n=>n.toString(16).padStart(2,"0")).join("")}};var J=!0;function B(a){let e=a.query;if(!e||Object.keys(e).length===0)return a.url;let r=new URLSearchParams(e).toString();return a.url.includes("?")?`${a.url}&${r}`:`${a.url}?${r}`}var C=class{constructor(e={}){this.ws=null;this.connectionOptions=null;this.listeners=new Map;this.reconnectAttempts=0;this.reconnectTimeout=null;this.intentionalClose=!1;var r,s,n,i,o;this.reconnect=(r=e.reconnect)!=null?r:!0,this.maxReconnectAttempts=Math.max(0,(s=e.maxReconnectAttempts)!=null?s:5),this.reconnectDelayMs=Math.max(0,(n=e.reconnectDelayMs)!=null?n:500),this.maxReconnectDelayMs=Math.max(this.reconnectDelayMs,(i=e.maxReconnectDelayMs)!=null?i:3e4);let t=(o=e.webSocket)!=null?o:globalThis.WebSocket;if(typeof t!="function")throw new Error("WebSocketAdapter: globalThis.WebSocket is not available. Pass a WebSocket constructor via options.webSocket.");this.WebSocketCtor=t}connect(e){return this.connectionOptions=e,this.intentionalClose=!1,this.reconnectAttempts=0,this.openConnection(e)}async disconnect(){this.intentionalClose=!0,this.clearReconnectTimeout(),this.ws&&(this.ws.close(1e3,"client disconnect"),this.ws=null)}subscribe(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>{let r=this.listeners.get(e);r&&(r.delete(t),r.size===0&&this.listeners.delete(e))}}async publish(e,t){var s,n;if(!this.ws||this.ws.readyState!==this.ws.OPEN)throw new Error(`WebSocketAdapter: cannot publish "${e}" \u2014 not connected (readyState=${(n=(s=this.ws)==null?void 0:s.readyState)!=null?n:"null"}).`);let r={event:e,payload:t};this.ws.send(JSON.stringify(r))}isConnected(){return this.ws!==null&&this.ws.readyState===this.ws.OPEN}openConnection(e){return new Promise((t,r)=>{let s=B(e),n;try{n=new this.WebSocketCtor(s)}catch(i){r(i instanceof Error?i:new Error(String(i)));return}n.onopen=()=>{this.ws=n,this.reconnectAttempts=0,t()},n.onerror=i=>{this.ws||r(new Error("WebSocketAdapter: connection failed"))},n.onclose=i=>{this.ws===n&&(this.ws=null),!this.intentionalClose&&this.reconnect&&this.connectionOptions&&this.reconnectAttempts<this.maxReconnectAttempts&&this.scheduleReconnect()},n.onmessage=i=>{this.handleMessage(i.data)}})}handleMessage(e){if(typeof e!="string")return;let t;try{t=JSON.parse(e)}catch{return}if(typeof t!="object"||t===null||typeof t.event!="string")return;let{event:r,payload:s}=t,n=this.listeners.get(r);if(n)for(let i of n)try{i(s)}catch{}}scheduleReconnect(){this.clearReconnectTimeout();let e=++this.reconnectAttempts,t=Math.min(this.reconnectDelayMs*Math.pow(2,e-1),this.maxReconnectDelayMs);this.reconnectTimeout=setTimeout(()=>{!this.intentionalClose&&this.connectionOptions&&this.openConnection(this.connectionOptions).catch(()=>{})},t)}clearReconnectTimeout(){this.reconnectTimeout!==null&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null)}};var K=!0;var N=class{constructor(e={}){if(e.storage)this.store=e.storage;else{let t=globalThis.localStorage;if(!t)throw new Error("WebStorageAdapter: globalThis.localStorage is not available. Pass a storage implementation via options.storage.");this.store=t}}getItem(e){return this.store.getItem(e)}setItem(e,t){this.store.setItem(e,t)}removeItem(e){this.store.removeItem(e)}};var ee=!0;var ne={platform:"web",ready:!0,notes:"All adapter domains implemented: IndexedDbAdapter (db), FetchHttpAdapter (http), WebPlatformAdapter (platform), WebSocketAdapter (socket), WebStorageAdapter (storage)."};export{O as FetchHttpAdapter,D as IndexedDbAdapter,L as OFPLATFORM_WEB_HTTP_ADAPTERS_READY,J as OFPLATFORM_WEB_PLATFORM_ADAPTERS_READY,K as OFPLATFORM_WEB_SOCKET_ADAPTERS_READY,ne as OFPLATFORM_WEB_STATUS,ee as OFPLATFORM_WEB_STORAGE_ADAPTERS_READY,I as WebPlatformAdapter,C as WebSocketAdapter,N as WebStorageAdapter};
|
|
1
|
+
var S="__ofcore_meta__",x="__schema_version__",I="__table_schemas__";function T(l){return typeof structuredClone=="function"?structuredClone(l):JSON.parse(JSON.stringify(l))}function W(l){return!!(l&&typeof l=="object"&&!Array.isArray(l)&&"and"in l&&Array.isArray(l.and))}function _(l){return!!(l&&typeof l=="object"&&!Array.isArray(l)&&"or"in l&&Array.isArray(l.or))}function w(l){return new Promise((e,t)=>{l.onsuccess=()=>e(l.result),l.onerror=()=>{var r;return t((r=l.error)!=null?r:new Error("IndexedDB request failed"))}})}function R(l){return new Promise((e,t)=>{l.oncomplete=()=>e(),l.onerror=()=>{var r;return t((r=l.error)!=null?r:new Error("IndexedDB transaction failed"))},l.onabort=()=>{var r;return t((r=l.error)!=null?r:new Error("IndexedDB transaction aborted"))}})}var P=class{constructor(e={}){this.transactionDepth=0;var t,r;this.dbName=(t=e.dbName)!=null?t:"ofcore-app",this.initialDbVersion=e.initialDbVersion,this.tableNamePrefix=(r=e.tableNamePrefix)!=null?r:"",this.tableNameResolver=e.tableNameMapper}setTableNameResolver(e){this.tableNameResolver=e}async close(){this.closeSync();let e=this.dbPromise;if(this.dbPromise=void 0,!!e)try{(await e).close()}catch{}}closeSync(){var e;try{(e=this.dbInstance)==null||e.close()}catch{}this.dbInstance=void 0,this.dbPromise=void 0}resolveTableName(e){return e===S?S:this.tableNameResolver?String(this.tableNameResolver(e)||"").trim()||e:!this.tableNamePrefix||e.startsWith(this.tableNamePrefix)?e:`${this.tableNamePrefix}${e}`}async openDatabase(e){let t=typeof e=="number"?indexedDB.open(this.dbName,e):indexedDB.open(this.dbName);return t.onupgradeneeded=()=>{let r=t.result;r.objectStoreNames.contains(S)||r.createObjectStore(S,{keyPath:"key"})},new Promise((r,i)=>{t.onsuccess=()=>{let n=t.result;n.onversionchange=()=>n.close(),this.dbInstance=n,r(n)},t.onerror=()=>{let n=t.error;if((n==null?void 0:n.name)==="VersionError"){i(new Error(`IndexedDB version mismatch for "${this.dbName}": requested version is lower than existing database version`));return}i(n!=null?n:new Error("Failed to open IndexedDB"))},t.onblocked=()=>i(new Error("IndexedDB open blocked by another tab/process"))})}async getDb(){return this.dbPromise||(this.dbPromise=(async()=>{try{return await this.openDatabase(this.initialDbVersion)}catch(e){if(this.initialDbVersion!==void 0&&e instanceof Error&&e.message.includes("version mismatch"))return this.openDatabase();throw e}})()),this.dbPromise}async reopenWithUpgrade(e){let t=await this.getDb(),r=t.version+1;t.close(),this.dbPromise=void 0;let i=indexedDB.open(this.dbName,r);i.onupgradeneeded=()=>{let s=i.result;s.objectStoreNames.contains(S)||s.createObjectStore(S,{keyPath:"key"}),e(s)};let n=await new Promise((s,o)=>{i.onsuccess=()=>{let c=i.result;c.onversionchange=()=>c.close(),this.dbInstance=c,s(c)},i.onerror=()=>{var c;return o((c=i.error)!=null?c:new Error("Failed to upgrade IndexedDB"))},i.onblocked=()=>o(new Error("IndexedDB upgrade blocked by another tab/process"))});return this.dbPromise=Promise.resolve(n),n}async withStore(e,t,r){let n=(await this.getDb()).transaction(e,t),s=n.objectStore(e),o=await r(s);return await R(n),o}async getMetaValue(e,t){return(await this.getDb()).objectStoreNames.contains(S)?this.withStore(S,"readonly",async i=>{var s;let n=await w(i.get(e));return(s=n==null?void 0:n.value)!=null?s:t}):t}async setMetaValue(e,t){await this.withStore(S,"readwrite",async r=>{await w(r.put({key:e,value:t}))})}async getTableSchemas(){return this.getMetaValue(I,{})}async setTableSchemas(e){await this.setMetaValue(I,e)}async tableExists(e){let t=this.resolveTableName(e);return(await this.getDb()).objectStoreNames.contains(t)}async ensureTable(e){let t=this.resolveTableName(e);await this.tableExists(e)||await this.reopenWithUpgrade(r=>{r.objectStoreNames.contains(t)||r.createObjectStore(t,{keyPath:"id"})})}async listRows(e){return await this.tableExists(e)?this.withStore(this.resolveTableName(e),"readonly",async r=>w(r.getAll())):[]}async putRows(e,t){await this.ensureTable(e);let r=this.resolveTableName(e),n=(await this.getDb()).transaction(r,"readwrite"),s=n.objectStore(r);await w(s.clear());for(let o of t)await w(s.put(o));await R(n)}normalizeForComparison(e){if(e instanceof Date)return e.getTime();if(typeof e=="boolean")return e?1:0;if(Array.isArray(e))return e.map(t=>this.normalizeForComparison(t));if(e&&typeof e=="object")try{return JSON.stringify(e)}catch{return String(e)}return e}evalValueExpr(e,t,r,i="t0"){if(r.type==="column"){let n=r.tableAlias||i,s=n===i?e:t[n]||{};return this.normalizeForComparison(s[r.field])}return this.normalizeForComparison(r.value)}buildFieldCondition(e,t){if("field"in e)return{left:{type:"column",tableAlias:e.alias||t,field:e.field},operator:e.operator||"=",right:{type:"literal",value:e.value},...e.operator==="BETWEEN"?{}:{likeMode:e.likeMode}};if("left"in e)return e;throw new Error("Invalid field condition")}matchFilterExpression(e,t,r,i="t0"){if(W(r))return r.and.every(n=>this.matchFilterExpression(e,t,n,i));if(_(r))return r.or.some(n=>this.matchFilterExpression(e,t,n,i));if(r!==null&&typeof r=="object"&&!Array.isArray(r)){if("left"in r||"field"in r){let n=this.buildFieldCondition(r,i),s=this.evalValueExpr(e,t,n.left,i),o=this.evalValueExpr(e,t,n.right,i);if(n.operator==="IS NULL")return s==null;if(n.operator==="IS NOT NULL")return s!=null;if(n.operator==="IN"||n.operator==="NOT IN"){if(!Array.isArray(o))throw new Error("IN/NOT IN requires array as right value");let c=o.includes(s);return n.operator==="IN"?c:!c}if(n.operator==="BETWEEN")return!Array.isArray(o)||o.length!==2?!1:s>=o[0]&&s<=o[1];switch(n.operator){case"=":return s===o;case"!=":return s!==o;case"<":return s<o;case"<=":return s<=o;case">":return s>o;case">=":return s>=o;case"LIKE":{if(typeof s!="string"||typeof o!="string")return!1;let c=o.replace(/%/g,".*").replace(/_/g,".");return new RegExp(`^${c}$`,n.likeMode==="case-insensitive"?"i":"").test(s)}default:throw new Error(`Unsupported operator: ${n.operator}`)}}return Object.entries(r).every(([n,s])=>this.normalizeForComparison(e[n])===this.normalizeForComparison(s))}throw new Error(`Invalid filter expression: ${JSON.stringify(r)}`)}async simulateJoins(e,t){if(t.length===0)return e.map(n=>({record:n,joined:{}}));let r={};for(let n of t)r[n.alias]=await this.listRows(n.table);let i=[];for(let n of e){let s={},o=!0;for(let c of t){let a=r[c.alias]||[],d=null;if(c.conditions.length===1){let h=c.conditions[0];if(h.operator==="="&&h.left.type==="column"&&h.right.type==="column"){let u=h.left.tableAlias||"t0",p=h.right.type==="column"?h.right.tableAlias:void 0;if(u==="t0"&&h.left.type==="column"&&p===c.alias&&h.right.type==="column"){let g=this.normalizeForComparison(n[h.left.field]),b=h.right.field;d=a.find(f=>this.normalizeForComparison(f[b])===g)}}}if(!d&&(c.type==="inner"||!c.type)){o=!1;break}s[c.alias]=d!=null?d:null}o&&i.push({record:n,joined:s})}return i}extractField(e,t,r,i="t0"){if(typeof r=="string")return this.normalizeForComparison(e[r]);let n=r.tableAlias||i,s=n===i?e:t[n]||{};return this.normalizeForComparison(s[r.field])}extractAggregateField(e,t,r,i="t0"){if(typeof r=="string")return this.normalizeForComparison(e[r]);let n=r.tableAlias||i,s=n===i?e:t[n]||{};return this.normalizeForComparison(s[r.field])}async getSchemaVersion(){return this.getMetaValue(x,0)}async setSchemaVersion(e){await this.setMetaValue(x,e)}async addTable(e){await this.ensureTable(e.name);let t=await this.getTableSchemas();t[e.name]=T(e),await this.setTableSchemas(t)}async addColumn(e,t){var n;let r=await this.getTableSchemas(),i=(n=r[e])!=null?n:{name:e,columns:[],skipMetadataColumns:!0};i.columns.find(s=>s.name===t.name)||i.columns.push(T(t)),r[e]=i,await this.setTableSchemas(r)}async get(e,t){var n;return await this.tableExists(e)?typeof t=="string"?this.withStore(this.resolveTableName(e),"readonly",async s=>{let o=await w(s.get(t));return o!=null?o:null}):(n=(await this.query(e,{...t,limit:1}))[0])!=null?n:null:null}async query(e,t={}){var d,h;if(!await this.tableExists(e))return[];let i=(d=t.mainAlias)!=null?d:"t0",n=await this.listRows(e),s=await this.simulateJoins(n,t.joins||[]);if(t.filters&&(s=s.filter(({record:u,joined:p})=>this.matchFilterExpression(u,p,t.filters,i))),t.aggregates&&t.aggregates.length>0){let u={};for(let p of t.aggregates){let g=s;p.filter&&(g=g.filter(({record:m,joined:v})=>this.matchFilterExpression(m,v,p.filter,i)));let b=g.map(({record:m,joined:v})=>this.extractAggregateField(m,v,p.field,i)),f=b.map(m=>Number(m)).filter(m=>Number.isFinite(m)),y=null;switch(p.function){case"COUNT":y=b.filter(m=>m!=null).length;break;case"SUM":y=f.reduce((m,v)=>m+v,0);break;case"AVG":y=f.length?f.reduce((m,v)=>m+v,0)/f.length:0;break;case"MIN":y=f.length?Math.min(...f):null;break;case"MAX":y=f.length?Math.max(...f):null;break;default:throw new Error(`Unsupported aggregate function: ${p.function}`)}u[p.as]=y}return[u]}t.sort&&t.sort.length>0&&s.sort((u,p)=>{for(let g of t.sort){let b=this.extractField(u.record,u.joined,g.field,i),f=this.extractField(p.record,p.joined,g.field,i);if(b===f)continue;if(b==null)return g.direction==="asc"?-1:1;if(f==null)return g.direction==="asc"?1:-1;let y=b>f?1:-1;return g.direction==="asc"?y:-y}return 0});let o=s.map(({record:u,joined:p})=>{var f;if(!t.fields)return u;if(((f=t.resultShape)!=null?f:"flat")==="nested"){let y={};for(let[m,v]of Object.entries(t.fields)){let A=m===i?u:p[m]||{},C={};for(let E of v)C[E]=A[E];y[m]=C}return y}let b={};for(let[y,m]of Object.entries(t.fields)){let v=y===i?u:p[y]||{};for(let A of m)b[`${y}_${A}`]=v[A]}return b}),c=(h=t.offset)!=null?h:0,a=t.limit!=null?c+t.limit:void 0;return o.slice(c,a)}async create(e,t){await this.ensureTable(e);let r=t.id;if(!r||typeof r!="string")throw new Error(`create(${e}) requires string id`);return this.withStore(this.resolveTableName(e),"readwrite",async i=>{if(await w(i.get(r)))throw new Error(`Record already exists: ${e}#${r}`);return await w(i.put(T(t))),T(t)})}async update(e,t,r){if(!await this.tableExists(e))throw new Error(`Table not found: ${e}`);return this.withStore(this.resolveTableName(e),"readwrite",async n=>{let s=await w(n.get(t));if(!s)throw new Error(`Record not found: ${e}#${t}`);let o={...s,...T(r),id:t};return await w(n.put(o)),T(o)})}async delete(e,t){await this.tableExists(e)&&await this.withStore(this.resolveTableName(e),"readwrite",async i=>{await w(i.delete(t))})}async bulkCreate(e,t){await this.ensureTable(e);let r=this.resolveTableName(e),n=(await this.getDb()).transaction(r,"readwrite"),s=n.objectStore(r),o=[];for(let c of t){if(!c.id||typeof c.id!="string")throw n.abort(),new Error(`bulkCreate(${e}) requires string id per row`);let a=c.id;if(typeof a!="string")throw n.abort(),new Error(`bulkCreate(${e}) requires string id per row`);if(await w(s.get(a)))throw n.abort(),new Error(`Record already exists: ${e}#${a}`);let h=T(c);await w(s.put(h)),o.push(h)}return await R(n),o}async bulkUpdate(e,t){if(!await this.tableExists(e))throw new Error(`Table not found: ${e}`);let i=this.resolveTableName(e),s=(await this.getDb()).transaction(i,"readwrite"),o=s.objectStore(i),c=[];for(let a of t){let d=await w(o.get(a.id));if(!d)throw s.abort(),new Error(`Record not found: ${e}#${a.id}`);let h={...d,...T(a.updates),id:a.id};await w(o.put(h)),c.push(T(h))}return await R(s),c}async dataStores(){let e=await this.getDb();return Array.from(e.objectStoreNames).filter(t=>t!==S)}async takeSnapshot(){let e=await this.dataStores(),t={};for(let r of e)t[r]=await this.listRows(r);return t}async restoreSnapshot(e){var r;let t=await this.dataStores();for(let i of t){let n=(r=e[i])!=null?r:[];await this.putRows(i,n)}}async transaction(e){let t=this.transactionDepth===0,r=null;t&&(r=await this.takeSnapshot()),this.transactionDepth+=1;try{let i=await e(this);return this.transactionDepth-=1,i}catch(i){throw this.transactionDepth-=1,t&&r&&await this.restoreSnapshot(r),i}}async exportBackupPayload(){let e=await this.getDb(),[t,r,i]=await Promise.all([this.getMetaValue(x,0),this.getTableSchemas(),this.takeSnapshot()]);return{schema:"ofplatform-web-indexeddb-backup-v1",dbName:this.dbName,dbVersion:e.version,exportedAt:new Date().toISOString(),schemaVersion:t,tableSchemas:r,tables:i}}async importBackupPayload(e,t={}){var n,s,o,c;if(e.schema!=="ofplatform-web-indexeddb-backup-v1")throw new Error("Invalid backup payload schema");let r=(n=t.clearExisting)!=null?n:!0,i=Object.keys((s=e.tables)!=null?s:{});for(let a of i)await this.ensureTable(a);if(r){let a=await this.dataStores();for(let d of a)i.includes(d)||await this.putRows(d,[])}for(let a of i){let d=Array.isArray(e.tables[a])?e.tables[a]:[];await this.putRows(a,d)}await this.setMetaValue(x,(o=e.schemaVersion)!=null?o:0),await this.setTableSchemas((c=e.tableSchemas)!=null?c:{})}};function j(l,e){if(!e||Object.keys(e).length===0)return l;let t=new URLSearchParams;for(let[i,n]of Object.entries(e))n!=null&&t.set(i,String(n));let r=t.toString();return r?l.includes("?")?`${l}&${r}`:`${l}?${r}`:l}var D=class{constructor(e={}){let t=e.fetch;if(t)this.fetchImpl=t;else{let r=globalThis.fetch;if(typeof r!="function")throw new Error("FetchHttpAdapter: globalThis.fetch is not available. Pass a fetch implementation via options.fetch.");this.fetchImpl=r.bind(globalThis)}}async request(e){var n,s;let t=j(e.url,e.query),r=typeof AbortController!="undefined"?new AbortController:null,i=r&&e.timeoutMs?setTimeout(()=>r.abort(),e.timeoutMs):null;try{let o=e.body!==void 0&&e.body!==null,c={...(n=e.headers)!=null?n:{}};o&&!c["content-type"]&&!c["Content-Type"]&&(c["content-type"]="application/json");let a=await this.fetchImpl(t,{method:e.method,headers:c,body:o?JSON.stringify(e.body):void 0,signal:r==null?void 0:r.signal}),d={};a.headers.forEach((p,g)=>{d[g.toLowerCase()]=p});let u=((s=d["content-type"])!=null?s:"").includes("application/json")?await a.json():await a.text();return{status:a.status,headers:d,data:u}}finally{i!==null&&clearTimeout(i)}}};var q=!0;var O=class{constructor(e={}){var t;if(this.env=(t=e.env)!=null?t:{},e.crypto)this.cryptoImpl=e.crypto;else{let r=globalThis.crypto;if(!(r!=null&&r.subtle))throw new Error("WebPlatformAdapter: globalThis.crypto.subtle is not available. Pass a crypto implementation via options.crypto.");this.cryptoImpl=r}}getEnvVar(e){return this.env[e]}isOnline(){let e=globalThis.navigator;return e&&typeof e.onLine!="undefined"?!!e.onLine:!0}async hashPin(e){return`sha256:${await this.sha256Hex(e)}`}async verifyPin(e,t){if(!t.startsWith("sha256:"))return!1;let r=t.slice(7);return/^[0-9a-f]{64}$/.test(r)?await this.sha256Hex(e)===r:r===e}async sha256Hex(e){let r=new TextEncoder().encode(e),i=await this.cryptoImpl.subtle.digest("SHA-256",r);return Array.from(new Uint8Array(i)).map(n=>n.toString(16).padStart(2,"0")).join("")}};var G=!0;function V(l){let e=l.query;if(!e||Object.keys(e).length===0)return l.url;let r=new URLSearchParams(e).toString();return l.url.includes("?")?`${l.url}&${r}`:`${l.url}?${r}`}var B=class{constructor(e={}){this.ws=null;this.connectionOptions=null;this.listeners=new Map;this.activeConnectionId=0;this.reconnectAttempts=0;this.reconnectTimeout=null;this.intentionalClose=!1;var r,i,n,s,o;this.reconnect=(r=e.reconnect)!=null?r:!0,this.maxReconnectAttempts=Math.max(0,(i=e.maxReconnectAttempts)!=null?i:5),this.reconnectDelayMs=Math.max(0,(n=e.reconnectDelayMs)!=null?n:500),this.maxReconnectDelayMs=Math.max(this.reconnectDelayMs,(s=e.maxReconnectDelayMs)!=null?s:3e4);let t=(o=e.webSocket)!=null?o:globalThis.WebSocket;if(typeof t!="function")throw new Error("WebSocketAdapter: globalThis.WebSocket is not available. Pass a WebSocket constructor via options.webSocket.");this.WebSocketCtor=t}connect(e){if(this.clearReconnectTimeout(),this.intentionalClose=!0,this.ws)try{this.ws.close(1e3,"client reconnect")}catch{}finally{this.ws=null}this.connectionOptions=e,this.intentionalClose=!1,this.reconnectAttempts=0;let t=++this.activeConnectionId;return this.openConnection(e,t)}async disconnect(){this.intentionalClose=!0,this.clearReconnectTimeout(),this.activeConnectionId+=1,this.ws&&(this.ws.close(1e3,"client disconnect"),this.ws=null)}subscribe(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>{let r=this.listeners.get(e);r&&(r.delete(t),r.size===0&&this.listeners.delete(e))}}async publish(e,t){var i,n;if(!this.ws||this.ws.readyState!==this.ws.OPEN)throw new Error(`WebSocketAdapter: cannot publish "${e}" \u2014 not connected (readyState=${(n=(i=this.ws)==null?void 0:i.readyState)!=null?n:"null"}).`);let r={event:e,payload:t};this.ws.send(JSON.stringify(r))}isConnected(){return this.ws!==null&&this.ws.readyState===this.ws.OPEN}openConnection(e,t){return new Promise((r,i)=>{let n=V(e),s;try{s=new this.WebSocketCtor(n)}catch(o){i(o instanceof Error?o:new Error(String(o)));return}s.onopen=()=>{if(t!==this.activeConnectionId){try{s.close(1e3,"stale connection")}catch{}r();return}this.ws=s,this.reconnectAttempts=0,r()},s.onerror=o=>{t===this.activeConnectionId&&(this.ws||i(new Error("WebSocketAdapter: connection failed")))},s.onclose=o=>{if(t!==this.activeConnectionId)return;this.ws===s&&(this.ws=null),!this.intentionalClose&&this.reconnect&&this.connectionOptions&&this.reconnectAttempts<this.maxReconnectAttempts&&this.scheduleReconnect()},s.onmessage=o=>{this.handleMessage(o.data)}})}handleMessage(e){if(typeof e!="string")return;let t;try{t=JSON.parse(e)}catch{return}if(typeof t!="object"||t===null||typeof t.event!="string")return;let{event:r,payload:i}=t,n=this.listeners.get(r);if(n)for(let s of n)try{s(i)}catch{}}scheduleReconnect(){this.clearReconnectTimeout();let e=++this.reconnectAttempts,t=Math.min(this.reconnectDelayMs*Math.pow(2,e-1),this.maxReconnectDelayMs);this.reconnectTimeout=setTimeout(()=>{if(!this.intentionalClose&&this.connectionOptions){let r=this.activeConnectionId;this.openConnection(this.connectionOptions,r).catch(()=>{})}},t)}clearReconnectTimeout(){this.reconnectTimeout!==null&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null)}};var te=!0;var k=class{constructor(e={}){if(e.storage)this.store=e.storage;else{let t=globalThis.localStorage;if(!t)throw new Error("WebStorageAdapter: globalThis.localStorage is not available. Pass a storage implementation via options.storage.");this.store=t}}getItem(e){return this.store.getItem(e)}setItem(e,t){this.store.setItem(e,t)}removeItem(e){this.store.removeItem(e)}};var se=!0;import{AuthRejectionError as N,createServerAuthoritativeAuthBridge as L}from"ofauth-shared-core";var $=6e4;function M(l,e){var i,n,s,o,c;let t=new Date().toISOString(),r=l&&typeof l=="object"?l:{};return{sessionId:String((i=r.sessionId)!=null?i:""),identityId:String((n=r.identityId)!=null?n:""),assuranceLevel:String((s=r.assuranceLevel)!=null?s:"basic"),issuedAt:String((o=r.issuedAt)!=null?o:t),expiresAt:String((c=r.expiresAt)!=null?c:e)}}var F=class{constructor(e){this.options=e;var i,n,s,o,c;if(this.serverBaseUrl=((i=e.serverBaseUrl)!=null?i:"").trim().replace(/\/+$/,""),this.cacheKey=e.cacheKey.trim(),this.targetTenantId=((n=e.targetTenantId)==null?void 0:n.trim())||"",this.targetBranchId=((s=e.targetBranchId)==null?void 0:s.trim())||"",this.targetDomainId=((o=e.targetDomainId)==null?void 0:o.trim())||"",this.storage=new k(e.storage?{storage:e.storage}:{}),e.fetchImpl)this.fetchImpl=e.fetchImpl;else if(typeof globalThis.fetch=="function")this.fetchImpl=globalThis.fetch.bind(globalThis);else throw new Error("WebServerAuthoritativeAuthHostSupport requires a fetch implementation");if(this.isOnlineResolver=(c=e.isOnline)!=null?c:(()=>typeof navigator=="undefined"||typeof navigator.onLine=="undefined"?!0:!!navigator.onLine),!this.serverBaseUrl){this.serverBridge=null;return}let t={login:async({principal:a,secret:d})=>{var g,b,f,y;let h=await this.fetchImpl(`${this.serverBaseUrl}/v1/auth/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({principal:a,secret:d,...this.targetTenantId?{targetTenantId:this.targetTenantId}:{},...this.targetBranchId?{targetBranchId:this.targetBranchId}:{},...this.targetDomainId?{targetDomainId:this.targetDomainId}:{}})}),u=await h.json().catch(()=>({}));if(!h.ok)throw new N(String((g=u.code)!=null?g:"AUTH_UNAUTHORIZED"));let p=new Date(Date.now()+15*6e4).toISOString();return{accessToken:String((b=u.accessToken)!=null?b:""),refreshToken:String((f=u.refreshToken)!=null?f:""),session:M(u.session,p),activeContext:(y=u.activeContext)!=null?y:null,activeRoleRef:u.activeRoleRef==null?"":String(u.activeRoleRef)}},refresh:async({refreshToken:a})=>{var p,g,b,f;let d=await this.fetchImpl(`${this.serverBaseUrl}/v1/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:a})}),h=await d.json().catch(()=>({}));if(!d.ok)throw new N(String((p=h.code)!=null?p:"AUTH_UNAUTHORIZED"));let u=new Date(Date.now()+15*6e4).toISOString();return{accessToken:String((g=h.accessToken)!=null?g:""),refreshToken:String((b=h.refreshToken)!=null?b:""),session:M(h.session,u),activeContext:(f=h.activeContext)!=null?f:null}},logout:async({accessToken:a,refreshToken:d})=>{let h={"Content-Type":"application/json"};a&&(h.Authorization=`Bearer ${a}`),await this.fetchImpl(`${this.serverBaseUrl}/v1/auth/logout`,{method:"POST",headers:h,body:JSON.stringify({refreshToken:d})}).catch(()=>{})}},r={getCachedIdentity:async a=>{var d,h;return(h=(d=this.readCacheRecord(a))==null?void 0:d.identity)!=null?h:null},setCachedIdentity:async a=>{let d=this.readCacheRecord(a.principal);this.writeCacheRecord(a.principal,{...d,identity:a})},clearCachedIdentity:async a=>{let d=this.readCacheRecord(a);d&&this.writeCacheRecord(a,{...d,identity:void 0})},getSessionEnvelope:async a=>{var d,h;return(h=(d=this.readCacheRecord(a))==null?void 0:d.envelope)!=null?h:null},setSessionEnvelope:async(a,d)=>{let h=this.readCacheRecord(a);this.writeCacheRecord(a,{...h,envelope:d})},clearSessionEnvelope:async a=>{let d=this.readCacheRecord(a);d&&this.writeCacheRecord(a,{...d,envelope:void 0})}};this.serverBridge=L({authority:t,cache:r,isOnline:()=>this.isOnlineResolver(),verifyOfflineSecret:this.options.verifyOfflineSecret})}get isServerConfigured(){return this.serverBaseUrl.length>0}loadCacheMap(){try{let e=this.storage.getItem(this.cacheKey);if(!e)return{};let t=JSON.parse(e);return t&&typeof t=="object"?t:{}}catch{return{}}}saveCacheMap(e){try{this.storage.setItem(this.cacheKey,JSON.stringify(e))}catch{}}readCacheRecord(e){var r;return(r=this.loadCacheMap()[e])!=null?r:null}writeCacheRecord(e,t){let r=this.loadCacheMap(),i={};t.identity&&(i.identity=t.identity),t.envelope&&(i.envelope=t.envelope),!i.identity&&!i.envelope?delete r[e]:r[e]=i,this.saveCacheMap(r)}clearCachedSessionEnvelope(e){let t=this.readCacheRecord(e);t&&this.writeCacheRecord(e,{...t,envelope:void 0})}clearAllCachedSessionEnvelopes(){let e=this.loadCacheMap();for(let t of Object.keys(e)){let r=e[t];r&&(e[t]=r.identity?{identity:r.identity}:{},e[t].identity||delete e[t])}this.saveCacheMap(e)}async login(e,t){var i,n;if(!this.serverBridge)return{status:"rejected",reasonCode:"AUTH_BRIDGE_NOT_CONFIGURED"};let r=await this.serverBridge.login({principal:e,secret:t,verifyMethod:"pin"});return r.status==="authenticated"?{status:"authenticated",mode:(i=r.mode)!=null?i:"online"}:{status:"rejected",reasonCode:(n=r.reasonCode)!=null?n:"AUTH_UNAUTHORIZED"}}async logout(e){this.serverBridge&&await this.serverBridge.logout(e)}async restoreCachedEnvelope(){var t,r,i;let e=this.loadCacheMap();for(let n of Object.keys(e)){let s=(t=e[n])==null?void 0:t.envelope;if(!s)continue;let o=Date.parse(s.expiresAt),c=Number.isFinite(o),a=!c||o<=Date.now()+$;if(this.isOnlineResolver()&&a&&s.refreshToken&&this.serverBaseUrl)try{let d=await this.fetchImpl(`${this.serverBaseUrl}/v1/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:s.refreshToken})}),h=await d.json().catch(()=>({}));if(d.ok&&h.accessToken){let u={...s,accessToken:String(h.accessToken),refreshToken:h.refreshToken?String(h.refreshToken):s.refreshToken,activeContext:(i=(r=h.activeContext)!=null?r:s.activeContext)!=null?i:null};return this.writeCacheRecord(n,{...e[n],envelope:u}),{principal:n,envelope:u}}}catch{}if(c&&o>Date.now())return{principal:n,envelope:s}}return null}getStoredAccessToken(e){var t,r,i;return(i=(r=(t=this.readCacheRecord(e))==null?void 0:t.envelope)==null?void 0:r.accessToken)!=null?i:""}getStoredEnvelope(e){var t,r;return(r=(t=this.readCacheRecord(e))==null?void 0:t.envelope)!=null?r:null}};var pe={platform:"web",ready:!0,notes:"All adapter domains implemented: IndexedDbAdapter (db), FetchHttpAdapter (http), WebPlatformAdapter (platform), WebSocketAdapter (socket), WebStorageAdapter (storage)."};export{D as FetchHttpAdapter,P as IndexedDbAdapter,q as OFPLATFORM_WEB_HTTP_ADAPTERS_READY,G as OFPLATFORM_WEB_PLATFORM_ADAPTERS_READY,te as OFPLATFORM_WEB_SOCKET_ADAPTERS_READY,pe as OFPLATFORM_WEB_STATUS,se as OFPLATFORM_WEB_STORAGE_ADAPTERS_READY,O as WebPlatformAdapter,F as WebServerAuthoritativeAuthHostSupport,B as WebSocketAdapter,k as WebStorageAdapter};
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var E=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var B=Object.prototype.hasOwnProperty;var _=(o,e)=>{for(var t in e)E(o,t,{get:e[t],enumerable:!0})},V=(o,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of W(e))!B.call(o,s)&&s!==t&&E(o,s,{get:()=>e[s],enumerable:!(r=F(e,s))||r.enumerable});return o};var $=o=>V(E({},"__esModule",{value:!0}),o);var K={};_(K,{FetchHttpAdapter:()=>R,IndexedDbAdapter:()=>A,OFPLATFORM_WEB_HTTP_ADAPTERS_READY:()=>z,OFPLATFORM_WEB_PLATFORM_ADAPTERS_READY:()=>q,OFPLATFORM_WEB_SOCKET_ADAPTERS_READY:()=>U,OFPLATFORM_WEB_STATUS:()=>Y,OFPLATFORM_WEB_STORAGE_ADAPTERS_READY:()=>Q,WebPlatformAdapter:()=>D,WebSocketAdapter:()=>O,WebStorageAdapter:()=>I});module.exports=$(K);var T="__ofcore_meta__",k="__schema_version__",M="__table_schemas__";function v(o){return typeof structuredClone=="function"?structuredClone(o):JSON.parse(JSON.stringify(o))}function j(o){return!!(o&&typeof o=="object"&&!Array.isArray(o)&&"and"in o&&Array.isArray(o.and))}function L(o){return!!(o&&typeof o=="object"&&!Array.isArray(o)&&"or"in o&&Array.isArray(o.or))}function g(o){return new Promise((e,t)=>{o.onsuccess=()=>e(o.result),o.onerror=()=>{var r;return t((r=o.error)!=null?r:new Error("IndexedDB request failed"))}})}function P(o){return new Promise((e,t)=>{o.oncomplete=()=>e(),o.onerror=()=>{var r;return t((r=o.error)!=null?r:new Error("IndexedDB transaction failed"))},o.onabort=()=>{var r;return t((r=o.error)!=null?r:new Error("IndexedDB transaction aborted"))}})}var A=class{constructor(e={}){this.transactionDepth=0;var t,r;this.dbName=(t=e.dbName)!=null?t:"ofcore-app",this.initialDbVersion=e.initialDbVersion,this.tableNamePrefix=(r=e.tableNamePrefix)!=null?r:"",this.tableNameResolver=e.tableNameMapper}setTableNameResolver(e){this.tableNameResolver=e}resolveTableName(e){return e===T?T:this.tableNameResolver?String(this.tableNameResolver(e)||"").trim()||e:!this.tableNamePrefix||e.startsWith(this.tableNamePrefix)?e:`${this.tableNamePrefix}${e}`}async openDatabase(e){let t=typeof e=="number"?indexedDB.open(this.dbName,e):indexedDB.open(this.dbName);return t.onupgradeneeded=()=>{let r=t.result;r.objectStoreNames.contains(T)||r.createObjectStore(T,{keyPath:"key"})},new Promise((r,s)=>{t.onsuccess=()=>{let n=t.result;n.onversionchange=()=>n.close(),r(n)},t.onerror=()=>{let n=t.error;if((n==null?void 0:n.name)==="VersionError"){s(new Error(`IndexedDB version mismatch for "${this.dbName}": requested version is lower than existing database version`));return}s(n!=null?n:new Error("Failed to open IndexedDB"))},t.onblocked=()=>s(new Error("IndexedDB open blocked by another tab/process"))})}async getDb(){return this.dbPromise||(this.dbPromise=(async()=>{try{return await this.openDatabase(this.initialDbVersion)}catch(e){if(this.initialDbVersion!==void 0&&e instanceof Error&&e.message.includes("version mismatch"))return this.openDatabase();throw e}})()),this.dbPromise}async reopenWithUpgrade(e){let t=await this.getDb(),r=t.version+1;t.close(),this.dbPromise=void 0;let s=indexedDB.open(this.dbName,r);s.onupgradeneeded=()=>{let i=s.result;i.objectStoreNames.contains(T)||i.createObjectStore(T,{keyPath:"key"}),e(i)};let n=await new Promise((i,a)=>{s.onsuccess=()=>{let c=s.result;c.onversionchange=()=>c.close(),i(c)},s.onerror=()=>{var c;return a((c=s.error)!=null?c:new Error("Failed to upgrade IndexedDB"))},s.onblocked=()=>a(new Error("IndexedDB upgrade blocked by another tab/process"))});return this.dbPromise=Promise.resolve(n),n}async withStore(e,t,r){let n=(await this.getDb()).transaction(e,t),i=n.objectStore(e),a=await r(i);return await P(n),a}async getMetaValue(e,t){return(await this.getDb()).objectStoreNames.contains(T)?this.withStore(T,"readonly",async s=>{var i;let n=await g(s.get(e));return(i=n==null?void 0:n.value)!=null?i:t}):t}async setMetaValue(e,t){await this.withStore(T,"readwrite",async r=>{await g(r.put({key:e,value:t}))})}async getTableSchemas(){return this.getMetaValue(M,{})}async setTableSchemas(e){await this.setMetaValue(M,e)}async tableExists(e){let t=this.resolveTableName(e);return(await this.getDb()).objectStoreNames.contains(t)}async ensureTable(e){let t=this.resolveTableName(e);await this.tableExists(e)||await this.reopenWithUpgrade(r=>{r.objectStoreNames.contains(t)||r.createObjectStore(t,{keyPath:"id"})})}async listRows(e){return await this.tableExists(e)?this.withStore(this.resolveTableName(e),"readonly",async r=>g(r.getAll())):[]}async putRows(e,t){await this.ensureTable(e);let r=this.resolveTableName(e),n=(await this.getDb()).transaction(r,"readwrite"),i=n.objectStore(r);await g(i.clear());for(let a of t)await g(i.put(a));await P(n)}normalizeForComparison(e){if(e instanceof Date)return e.getTime();if(typeof e=="boolean")return e?1:0;if(Array.isArray(e))return e.map(t=>this.normalizeForComparison(t));if(e&&typeof e=="object")try{return JSON.stringify(e)}catch{return String(e)}return e}evalValueExpr(e,t,r,s="t0"){if(r.type==="column"){let n=r.tableAlias||s,i=n===s?e:t[n]||{};return this.normalizeForComparison(i[r.field])}return this.normalizeForComparison(r.value)}buildFieldCondition(e,t){if("field"in e)return{left:{type:"column",tableAlias:e.alias||t,field:e.field},operator:e.operator||"=",right:{type:"literal",value:e.value},...e.operator==="BETWEEN"?{}:{likeMode:e.likeMode}};if("left"in e)return e;throw new Error("Invalid field condition")}matchFilterExpression(e,t,r,s="t0"){if(j(r))return r.and.every(n=>this.matchFilterExpression(e,t,n,s));if(L(r))return r.or.some(n=>this.matchFilterExpression(e,t,n,s));if(r!==null&&typeof r=="object"&&!Array.isArray(r)){if("left"in r||"field"in r){let n=this.buildFieldCondition(r,s),i=this.evalValueExpr(e,t,n.left,s),a=this.evalValueExpr(e,t,n.right,s);if(n.operator==="IS NULL")return i==null;if(n.operator==="IS NOT NULL")return i!=null;if(n.operator==="IN"||n.operator==="NOT IN"){if(!Array.isArray(a))throw new Error("IN/NOT IN requires array as right value");let c=a.includes(i);return n.operator==="IN"?c:!c}if(n.operator==="BETWEEN")return!Array.isArray(a)||a.length!==2?!1:i>=a[0]&&i<=a[1];switch(n.operator){case"=":return i===a;case"!=":return i!==a;case"<":return i<a;case"<=":return i<=a;case">":return i>a;case">=":return i>=a;case"LIKE":{if(typeof i!="string"||typeof a!="string")return!1;let c=a.replace(/%/g,".*").replace(/_/g,".");return new RegExp(`^${c}$`,n.likeMode==="case-insensitive"?"i":"").test(i)}default:throw new Error(`Unsupported operator: ${n.operator}`)}}return Object.entries(r).every(([n,i])=>this.normalizeForComparison(e[n])===this.normalizeForComparison(i))}throw new Error(`Invalid filter expression: ${JSON.stringify(r)}`)}async simulateJoins(e,t){if(t.length===0)return e.map(n=>({record:n,joined:{}}));let r={};for(let n of t)r[n.alias]=await this.listRows(n.table);let s=[];for(let n of e){let i={},a=!0;for(let c of t){let l=r[c.alias]||[],d=null;if(c.conditions.length===1){let u=c.conditions[0];if(u.operator==="="&&u.left.type==="column"&&u.right.type==="column"){let b=u.left.tableAlias||"t0",h=u.right.type==="column"?u.right.tableAlias:void 0;if(b==="t0"&&u.left.type==="column"&&h===c.alias&&u.right.type==="column"){let y=this.normalizeForComparison(n[u.left.field]),w=u.right.field;d=l.find(m=>this.normalizeForComparison(m[w])===y)}}}if(!d&&(c.type==="inner"||!c.type)){a=!1;break}i[c.alias]=d!=null?d:null}a&&s.push({record:n,joined:i})}return s}extractField(e,t,r,s="t0"){if(typeof r=="string")return this.normalizeForComparison(e[r]);let n=r.tableAlias||s,i=n===s?e:t[n]||{};return this.normalizeForComparison(i[r.field])}extractAggregateField(e,t,r,s="t0"){if(typeof r=="string")return this.normalizeForComparison(e[r]);let n=r.tableAlias||s,i=n===s?e:t[n]||{};return this.normalizeForComparison(i[r.field])}async getSchemaVersion(){return this.getMetaValue(k,0)}async setSchemaVersion(e){await this.setMetaValue(k,e)}async addTable(e){await this.ensureTable(e.name);let t=await this.getTableSchemas();t[e.name]=v(e),await this.setTableSchemas(t)}async addColumn(e,t){var n;let r=await this.getTableSchemas(),s=(n=r[e])!=null?n:{name:e,columns:[],skipMetadataColumns:!0};s.columns.find(i=>i.name===t.name)||s.columns.push(v(t)),r[e]=s,await this.setTableSchemas(r)}async get(e,t){var n;return await this.tableExists(e)?typeof t=="string"?this.withStore(this.resolveTableName(e),"readonly",async i=>{let a=await g(i.get(t));return a!=null?a:null}):(n=(await this.query(e,{...t,limit:1}))[0])!=null?n:null:null}async query(e,t={}){var d,u;if(!await this.tableExists(e))return[];let s=(d=t.mainAlias)!=null?d:"t0",n=await this.listRows(e),i=await this.simulateJoins(n,t.joins||[]);if(t.filters&&(i=i.filter(({record:b,joined:h})=>this.matchFilterExpression(b,h,t.filters,s))),t.aggregates&&t.aggregates.length>0){let b={};for(let h of t.aggregates){let y=i;h.filter&&(y=y.filter(({record:p,joined:S})=>this.matchFilterExpression(p,S,h.filter,s)));let w=y.map(({record:p,joined:S})=>this.extractAggregateField(p,S,h.field,s)),m=w.map(p=>Number(p)).filter(p=>Number.isFinite(p)),f=null;switch(h.function){case"COUNT":f=w.filter(p=>p!=null).length;break;case"SUM":f=m.reduce((p,S)=>p+S,0);break;case"AVG":f=m.length?m.reduce((p,S)=>p+S,0)/m.length:0;break;case"MIN":f=m.length?Math.min(...m):null;break;case"MAX":f=m.length?Math.max(...m):null;break;default:throw new Error(`Unsupported aggregate function: ${h.function}`)}b[h.as]=f}return[b]}t.sort&&t.sort.length>0&&i.sort((b,h)=>{for(let y of t.sort){let w=this.extractField(b.record,b.joined,y.field,s),m=this.extractField(h.record,h.joined,y.field,s);if(w===m)continue;if(w==null)return y.direction==="asc"?-1:1;if(m==null)return y.direction==="asc"?1:-1;let f=w>m?1:-1;return y.direction==="asc"?f:-f}return 0});let a=i.map(({record:b,joined:h})=>{var m;if(!t.fields)return b;if(((m=t.resultShape)!=null?m:"flat")==="nested"){let f={};for(let[p,S]of Object.entries(t.fields)){let x=p===s?b:h[p]||{},C={};for(let N of S)C[N]=x[N];f[p]=C}return f}let w={};for(let[f,p]of Object.entries(t.fields)){let S=f===s?b:h[f]||{};for(let x of p)w[`${f}_${x}`]=S[x]}return w}),c=(u=t.offset)!=null?u:0,l=t.limit!=null?c+t.limit:void 0;return a.slice(c,l)}async create(e,t){await this.ensureTable(e);let r=t.id;if(!r||typeof r!="string")throw new Error(`create(${e}) requires string id`);return this.withStore(this.resolveTableName(e),"readwrite",async s=>{if(await g(s.get(r)))throw new Error(`Record already exists: ${e}#${r}`);return await g(s.put(v(t))),v(t)})}async update(e,t,r){if(!await this.tableExists(e))throw new Error(`Table not found: ${e}`);return this.withStore(this.resolveTableName(e),"readwrite",async n=>{let i=await g(n.get(t));if(!i)throw new Error(`Record not found: ${e}#${t}`);let a={...i,...v(r),id:t};return await g(n.put(a)),v(a)})}async delete(e,t){await this.tableExists(e)&&await this.withStore(this.resolveTableName(e),"readwrite",async s=>{await g(s.delete(t))})}async bulkCreate(e,t){await this.ensureTable(e);let r=this.resolveTableName(e),n=(await this.getDb()).transaction(r,"readwrite"),i=n.objectStore(r),a=[];for(let c of t){if(!c.id||typeof c.id!="string")throw n.abort(),new Error(`bulkCreate(${e}) requires string id per row`);let l=c.id;if(typeof l!="string")throw n.abort(),new Error(`bulkCreate(${e}) requires string id per row`);if(await g(i.get(l)))throw n.abort(),new Error(`Record already exists: ${e}#${l}`);let u=v(c);await g(i.put(u)),a.push(u)}return await P(n),a}async bulkUpdate(e,t){if(!await this.tableExists(e))throw new Error(`Table not found: ${e}`);let s=this.resolveTableName(e),i=(await this.getDb()).transaction(s,"readwrite"),a=i.objectStore(s),c=[];for(let l of t){let d=await g(a.get(l.id));if(!d)throw i.abort(),new Error(`Record not found: ${e}#${l.id}`);let u={...d,...v(l.updates),id:l.id};await g(a.put(u)),c.push(v(u))}return await P(i),c}async dataStores(){let e=await this.getDb();return Array.from(e.objectStoreNames).filter(t=>t!==T)}async takeSnapshot(){let e=await this.dataStores(),t={};for(let r of e)t[r]=await this.listRows(r);return t}async restoreSnapshot(e){var r;let t=await this.dataStores();for(let s of t){let n=(r=e[s])!=null?r:[];await this.putRows(s,n)}}async transaction(e){let t=this.transactionDepth===0,r=null;t&&(r=await this.takeSnapshot()),this.transactionDepth+=1;try{let s=await e(this);return this.transactionDepth-=1,s}catch(s){throw this.transactionDepth-=1,t&&r&&await this.restoreSnapshot(r),s}}async exportBackupPayload(){let e=await this.getDb(),[t,r,s]=await Promise.all([this.getMetaValue(k,0),this.getTableSchemas(),this.takeSnapshot()]);return{schema:"ofplatform-web-indexeddb-backup-v1",dbName:this.dbName,dbVersion:e.version,exportedAt:new Date().toISOString(),schemaVersion:t,tableSchemas:r,tables:s}}async importBackupPayload(e,t={}){var n,i,a,c;if(e.schema!=="ofplatform-web-indexeddb-backup-v1")throw new Error("Invalid backup payload schema");let r=(n=t.clearExisting)!=null?n:!0,s=Object.keys((i=e.tables)!=null?i:{});for(let l of s)await this.ensureTable(l);if(r){let l=await this.dataStores();for(let d of l)s.includes(d)||await this.putRows(d,[])}for(let l of s){let d=Array.isArray(e.tables[l])?e.tables[l]:[];await this.putRows(l,d)}await this.setMetaValue(k,(a=e.schemaVersion)!=null?a:0),await this.setTableSchemas((c=e.tableSchemas)!=null?c:{})}};function H(o,e){if(!e||Object.keys(e).length===0)return o;let t=new URLSearchParams;for(let[s,n]of Object.entries(e))n!=null&&t.set(s,String(n));let r=t.toString();return r?o.includes("?")?`${o}&${r}`:`${o}?${r}`:o}var R=class{constructor(e={}){let t=e.fetch;if(t)this.fetchImpl=t;else{let r=globalThis.fetch;if(typeof r!="function")throw new Error("FetchHttpAdapter: globalThis.fetch is not available. Pass a fetch implementation via options.fetch.");this.fetchImpl=r.bind(globalThis)}}async request(e){var n,i;let t=H(e.url,e.query),r=typeof AbortController!="undefined"?new AbortController:null,s=r&&e.timeoutMs?setTimeout(()=>r.abort(),e.timeoutMs):null;try{let a=e.body!==void 0&&e.body!==null,c={...(n=e.headers)!=null?n:{}};a&&!c["content-type"]&&!c["Content-Type"]&&(c["content-type"]="application/json");let l=await this.fetchImpl(t,{method:e.method,headers:c,body:a?JSON.stringify(e.body):void 0,signal:r==null?void 0:r.signal}),d={};l.headers.forEach((h,y)=>{d[y.toLowerCase()]=h});let b=((i=d["content-type"])!=null?i:"").includes("application/json")?await l.json():await l.text();return{status:l.status,headers:d,data:b}}finally{s!==null&&clearTimeout(s)}}};var z=!0;var D=class{constructor(e={}){var t;if(this.env=(t=e.env)!=null?t:{},e.crypto)this.cryptoImpl=e.crypto;else{let r=globalThis.crypto;if(!(r!=null&&r.subtle))throw new Error("WebPlatformAdapter: globalThis.crypto.subtle is not available. Pass a crypto implementation via options.crypto.");this.cryptoImpl=r}}getEnvVar(e){return this.env[e]}isOnline(){let e=globalThis.navigator;return e&&typeof e.onLine!="undefined"?!!e.onLine:!0}async hashPin(e){return`sha256:${await this.sha256Hex(e)}`}async verifyPin(e,t){if(!t.startsWith("sha256:"))return!1;let r=t.slice(7);return/^[0-9a-f]{64}$/.test(r)?await this.sha256Hex(e)===r:r===e}async sha256Hex(e){let r=new TextEncoder().encode(e),s=await this.cryptoImpl.subtle.digest("SHA-256",r);return Array.from(new Uint8Array(s)).map(n=>n.toString(16).padStart(2,"0")).join("")}};var q=!0;function J(o){let e=o.query;if(!e||Object.keys(e).length===0)return o.url;let r=new URLSearchParams(e).toString();return o.url.includes("?")?`${o.url}&${r}`:`${o.url}?${r}`}var O=class{constructor(e={}){this.ws=null;this.connectionOptions=null;this.listeners=new Map;this.reconnectAttempts=0;this.reconnectTimeout=null;this.intentionalClose=!1;var r,s,n,i,a;this.reconnect=(r=e.reconnect)!=null?r:!0,this.maxReconnectAttempts=Math.max(0,(s=e.maxReconnectAttempts)!=null?s:5),this.reconnectDelayMs=Math.max(0,(n=e.reconnectDelayMs)!=null?n:500),this.maxReconnectDelayMs=Math.max(this.reconnectDelayMs,(i=e.maxReconnectDelayMs)!=null?i:3e4);let t=(a=e.webSocket)!=null?a:globalThis.WebSocket;if(typeof t!="function")throw new Error("WebSocketAdapter: globalThis.WebSocket is not available. Pass a WebSocket constructor via options.webSocket.");this.WebSocketCtor=t}connect(e){return this.connectionOptions=e,this.intentionalClose=!1,this.reconnectAttempts=0,this.openConnection(e)}async disconnect(){this.intentionalClose=!0,this.clearReconnectTimeout(),this.ws&&(this.ws.close(1e3,"client disconnect"),this.ws=null)}subscribe(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>{let r=this.listeners.get(e);r&&(r.delete(t),r.size===0&&this.listeners.delete(e))}}async publish(e,t){var s,n;if(!this.ws||this.ws.readyState!==this.ws.OPEN)throw new Error(`WebSocketAdapter: cannot publish "${e}" \u2014 not connected (readyState=${(n=(s=this.ws)==null?void 0:s.readyState)!=null?n:"null"}).`);let r={event:e,payload:t};this.ws.send(JSON.stringify(r))}isConnected(){return this.ws!==null&&this.ws.readyState===this.ws.OPEN}openConnection(e){return new Promise((t,r)=>{let s=J(e),n;try{n=new this.WebSocketCtor(s)}catch(i){r(i instanceof Error?i:new Error(String(i)));return}n.onopen=()=>{this.ws=n,this.reconnectAttempts=0,t()},n.onerror=i=>{this.ws||r(new Error("WebSocketAdapter: connection failed"))},n.onclose=i=>{this.ws===n&&(this.ws=null),!this.intentionalClose&&this.reconnect&&this.connectionOptions&&this.reconnectAttempts<this.maxReconnectAttempts&&this.scheduleReconnect()},n.onmessage=i=>{this.handleMessage(i.data)}})}handleMessage(e){if(typeof e!="string")return;let t;try{t=JSON.parse(e)}catch{return}if(typeof t!="object"||t===null||typeof t.event!="string")return;let{event:r,payload:s}=t,n=this.listeners.get(r);if(n)for(let i of n)try{i(s)}catch{}}scheduleReconnect(){this.clearReconnectTimeout();let e=++this.reconnectAttempts,t=Math.min(this.reconnectDelayMs*Math.pow(2,e-1),this.maxReconnectDelayMs);this.reconnectTimeout=setTimeout(()=>{!this.intentionalClose&&this.connectionOptions&&this.openConnection(this.connectionOptions).catch(()=>{})},t)}clearReconnectTimeout(){this.reconnectTimeout!==null&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null)}};var U=!0;var I=class{constructor(e={}){if(e.storage)this.store=e.storage;else{let t=globalThis.localStorage;if(!t)throw new Error("WebStorageAdapter: globalThis.localStorage is not available. Pass a storage implementation via options.storage.");this.store=t}}getItem(e){return this.store.getItem(e)}setItem(e,t){this.store.setItem(e,t)}removeItem(e){this.store.removeItem(e)}};var Q=!0;var Y={platform:"web",ready:!0,notes:"All adapter domains implemented: IndexedDbAdapter (db), FetchHttpAdapter (http), WebPlatformAdapter (platform), WebSocketAdapter (socket), WebStorageAdapter (storage)."};
|
|
1
|
+
"use strict";var E=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var V=Object.prototype.hasOwnProperty;var L=(a,e)=>{for(var t in e)E(a,t,{get:e[t],enumerable:!0})},$=(a,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of j(e))!V.call(a,i)&&i!==t&&E(a,i,{get:()=>e[i],enumerable:!(r=_(e,i))||r.enumerable});return a};var U=a=>$(E({},"__esModule",{value:!0}),a);var ee={};L(ee,{FetchHttpAdapter:()=>P,IndexedDbAdapter:()=>I,OFPLATFORM_WEB_HTTP_ADAPTERS_READY:()=>q,OFPLATFORM_WEB_PLATFORM_ADAPTERS_READY:()=>K,OFPLATFORM_WEB_SOCKET_ADAPTERS_READY:()=>Q,OFPLATFORM_WEB_STATUS:()=>X,OFPLATFORM_WEB_STORAGE_ADAPTERS_READY:()=>G,WebPlatformAdapter:()=>D,WebServerAuthoritativeAuthHostSupport:()=>B,WebSocketAdapter:()=>O,WebStorageAdapter:()=>A});module.exports=U(ee);var S="__ofcore_meta__",k="__schema_version__",F="__table_schemas__";function T(a){return typeof structuredClone=="function"?structuredClone(a):JSON.parse(JSON.stringify(a))}function H(a){return!!(a&&typeof a=="object"&&!Array.isArray(a)&&"and"in a&&Array.isArray(a.and))}function J(a){return!!(a&&typeof a=="object"&&!Array.isArray(a)&&"or"in a&&Array.isArray(a.or))}function w(a){return new Promise((e,t)=>{a.onsuccess=()=>e(a.result),a.onerror=()=>{var r;return t((r=a.error)!=null?r:new Error("IndexedDB request failed"))}})}function C(a){return new Promise((e,t)=>{a.oncomplete=()=>e(),a.onerror=()=>{var r;return t((r=a.error)!=null?r:new Error("IndexedDB transaction failed"))},a.onabort=()=>{var r;return t((r=a.error)!=null?r:new Error("IndexedDB transaction aborted"))}})}var I=class{constructor(e={}){this.transactionDepth=0;var t,r;this.dbName=(t=e.dbName)!=null?t:"ofcore-app",this.initialDbVersion=e.initialDbVersion,this.tableNamePrefix=(r=e.tableNamePrefix)!=null?r:"",this.tableNameResolver=e.tableNameMapper}setTableNameResolver(e){this.tableNameResolver=e}async close(){this.closeSync();let e=this.dbPromise;if(this.dbPromise=void 0,!!e)try{(await e).close()}catch{}}closeSync(){var e;try{(e=this.dbInstance)==null||e.close()}catch{}this.dbInstance=void 0,this.dbPromise=void 0}resolveTableName(e){return e===S?S:this.tableNameResolver?String(this.tableNameResolver(e)||"").trim()||e:!this.tableNamePrefix||e.startsWith(this.tableNamePrefix)?e:`${this.tableNamePrefix}${e}`}async openDatabase(e){let t=typeof e=="number"?indexedDB.open(this.dbName,e):indexedDB.open(this.dbName);return t.onupgradeneeded=()=>{let r=t.result;r.objectStoreNames.contains(S)||r.createObjectStore(S,{keyPath:"key"})},new Promise((r,i)=>{t.onsuccess=()=>{let n=t.result;n.onversionchange=()=>n.close(),this.dbInstance=n,r(n)},t.onerror=()=>{let n=t.error;if((n==null?void 0:n.name)==="VersionError"){i(new Error(`IndexedDB version mismatch for "${this.dbName}": requested version is lower than existing database version`));return}i(n!=null?n:new Error("Failed to open IndexedDB"))},t.onblocked=()=>i(new Error("IndexedDB open blocked by another tab/process"))})}async getDb(){return this.dbPromise||(this.dbPromise=(async()=>{try{return await this.openDatabase(this.initialDbVersion)}catch(e){if(this.initialDbVersion!==void 0&&e instanceof Error&&e.message.includes("version mismatch"))return this.openDatabase();throw e}})()),this.dbPromise}async reopenWithUpgrade(e){let t=await this.getDb(),r=t.version+1;t.close(),this.dbPromise=void 0;let i=indexedDB.open(this.dbName,r);i.onupgradeneeded=()=>{let s=i.result;s.objectStoreNames.contains(S)||s.createObjectStore(S,{keyPath:"key"}),e(s)};let n=await new Promise((s,o)=>{i.onsuccess=()=>{let l=i.result;l.onversionchange=()=>l.close(),this.dbInstance=l,s(l)},i.onerror=()=>{var l;return o((l=i.error)!=null?l:new Error("Failed to upgrade IndexedDB"))},i.onblocked=()=>o(new Error("IndexedDB upgrade blocked by another tab/process"))});return this.dbPromise=Promise.resolve(n),n}async withStore(e,t,r){let n=(await this.getDb()).transaction(e,t),s=n.objectStore(e),o=await r(s);return await C(n),o}async getMetaValue(e,t){return(await this.getDb()).objectStoreNames.contains(S)?this.withStore(S,"readonly",async i=>{var s;let n=await w(i.get(e));return(s=n==null?void 0:n.value)!=null?s:t}):t}async setMetaValue(e,t){await this.withStore(S,"readwrite",async r=>{await w(r.put({key:e,value:t}))})}async getTableSchemas(){return this.getMetaValue(F,{})}async setTableSchemas(e){await this.setMetaValue(F,e)}async tableExists(e){let t=this.resolveTableName(e);return(await this.getDb()).objectStoreNames.contains(t)}async ensureTable(e){let t=this.resolveTableName(e);await this.tableExists(e)||await this.reopenWithUpgrade(r=>{r.objectStoreNames.contains(t)||r.createObjectStore(t,{keyPath:"id"})})}async listRows(e){return await this.tableExists(e)?this.withStore(this.resolveTableName(e),"readonly",async r=>w(r.getAll())):[]}async putRows(e,t){await this.ensureTable(e);let r=this.resolveTableName(e),n=(await this.getDb()).transaction(r,"readwrite"),s=n.objectStore(r);await w(s.clear());for(let o of t)await w(s.put(o));await C(n)}normalizeForComparison(e){if(e instanceof Date)return e.getTime();if(typeof e=="boolean")return e?1:0;if(Array.isArray(e))return e.map(t=>this.normalizeForComparison(t));if(e&&typeof e=="object")try{return JSON.stringify(e)}catch{return String(e)}return e}evalValueExpr(e,t,r,i="t0"){if(r.type==="column"){let n=r.tableAlias||i,s=n===i?e:t[n]||{};return this.normalizeForComparison(s[r.field])}return this.normalizeForComparison(r.value)}buildFieldCondition(e,t){if("field"in e)return{left:{type:"column",tableAlias:e.alias||t,field:e.field},operator:e.operator||"=",right:{type:"literal",value:e.value},...e.operator==="BETWEEN"?{}:{likeMode:e.likeMode}};if("left"in e)return e;throw new Error("Invalid field condition")}matchFilterExpression(e,t,r,i="t0"){if(H(r))return r.and.every(n=>this.matchFilterExpression(e,t,n,i));if(J(r))return r.or.some(n=>this.matchFilterExpression(e,t,n,i));if(r!==null&&typeof r=="object"&&!Array.isArray(r)){if("left"in r||"field"in r){let n=this.buildFieldCondition(r,i),s=this.evalValueExpr(e,t,n.left,i),o=this.evalValueExpr(e,t,n.right,i);if(n.operator==="IS NULL")return s==null;if(n.operator==="IS NOT NULL")return s!=null;if(n.operator==="IN"||n.operator==="NOT IN"){if(!Array.isArray(o))throw new Error("IN/NOT IN requires array as right value");let l=o.includes(s);return n.operator==="IN"?l:!l}if(n.operator==="BETWEEN")return!Array.isArray(o)||o.length!==2?!1:s>=o[0]&&s<=o[1];switch(n.operator){case"=":return s===o;case"!=":return s!==o;case"<":return s<o;case"<=":return s<=o;case">":return s>o;case">=":return s>=o;case"LIKE":{if(typeof s!="string"||typeof o!="string")return!1;let l=o.replace(/%/g,".*").replace(/_/g,".");return new RegExp(`^${l}$`,n.likeMode==="case-insensitive"?"i":"").test(s)}default:throw new Error(`Unsupported operator: ${n.operator}`)}}return Object.entries(r).every(([n,s])=>this.normalizeForComparison(e[n])===this.normalizeForComparison(s))}throw new Error(`Invalid filter expression: ${JSON.stringify(r)}`)}async simulateJoins(e,t){if(t.length===0)return e.map(n=>({record:n,joined:{}}));let r={};for(let n of t)r[n.alias]=await this.listRows(n.table);let i=[];for(let n of e){let s={},o=!0;for(let l of t){let c=r[l.alias]||[],d=null;if(l.conditions.length===1){let h=l.conditions[0];if(h.operator==="="&&h.left.type==="column"&&h.right.type==="column"){let u=h.left.tableAlias||"t0",p=h.right.type==="column"?h.right.tableAlias:void 0;if(u==="t0"&&h.left.type==="column"&&p===l.alias&&h.right.type==="column"){let g=this.normalizeForComparison(n[h.left.field]),b=h.right.field;d=c.find(f=>this.normalizeForComparison(f[b])===g)}}}if(!d&&(l.type==="inner"||!l.type)){o=!1;break}s[l.alias]=d!=null?d:null}o&&i.push({record:n,joined:s})}return i}extractField(e,t,r,i="t0"){if(typeof r=="string")return this.normalizeForComparison(e[r]);let n=r.tableAlias||i,s=n===i?e:t[n]||{};return this.normalizeForComparison(s[r.field])}extractAggregateField(e,t,r,i="t0"){if(typeof r=="string")return this.normalizeForComparison(e[r]);let n=r.tableAlias||i,s=n===i?e:t[n]||{};return this.normalizeForComparison(s[r.field])}async getSchemaVersion(){return this.getMetaValue(k,0)}async setSchemaVersion(e){await this.setMetaValue(k,e)}async addTable(e){await this.ensureTable(e.name);let t=await this.getTableSchemas();t[e.name]=T(e),await this.setTableSchemas(t)}async addColumn(e,t){var n;let r=await this.getTableSchemas(),i=(n=r[e])!=null?n:{name:e,columns:[],skipMetadataColumns:!0};i.columns.find(s=>s.name===t.name)||i.columns.push(T(t)),r[e]=i,await this.setTableSchemas(r)}async get(e,t){var n;return await this.tableExists(e)?typeof t=="string"?this.withStore(this.resolveTableName(e),"readonly",async s=>{let o=await w(s.get(t));return o!=null?o:null}):(n=(await this.query(e,{...t,limit:1}))[0])!=null?n:null:null}async query(e,t={}){var d,h;if(!await this.tableExists(e))return[];let i=(d=t.mainAlias)!=null?d:"t0",n=await this.listRows(e),s=await this.simulateJoins(n,t.joins||[]);if(t.filters&&(s=s.filter(({record:u,joined:p})=>this.matchFilterExpression(u,p,t.filters,i))),t.aggregates&&t.aggregates.length>0){let u={};for(let p of t.aggregates){let g=s;p.filter&&(g=g.filter(({record:m,joined:v})=>this.matchFilterExpression(m,v,p.filter,i)));let b=g.map(({record:m,joined:v})=>this.extractAggregateField(m,v,p.field,i)),f=b.map(m=>Number(m)).filter(m=>Number.isFinite(m)),y=null;switch(p.function){case"COUNT":y=b.filter(m=>m!=null).length;break;case"SUM":y=f.reduce((m,v)=>m+v,0);break;case"AVG":y=f.length?f.reduce((m,v)=>m+v,0)/f.length:0;break;case"MIN":y=f.length?Math.min(...f):null;break;case"MAX":y=f.length?Math.max(...f):null;break;default:throw new Error(`Unsupported aggregate function: ${p.function}`)}u[p.as]=y}return[u]}t.sort&&t.sort.length>0&&s.sort((u,p)=>{for(let g of t.sort){let b=this.extractField(u.record,u.joined,g.field,i),f=this.extractField(p.record,p.joined,g.field,i);if(b===f)continue;if(b==null)return g.direction==="asc"?-1:1;if(f==null)return g.direction==="asc"?1:-1;let y=b>f?1:-1;return g.direction==="asc"?y:-y}return 0});let o=s.map(({record:u,joined:p})=>{var f;if(!t.fields)return u;if(((f=t.resultShape)!=null?f:"flat")==="nested"){let y={};for(let[m,v]of Object.entries(t.fields)){let R=m===i?u:p[m]||{},N={};for(let M of v)N[M]=R[M];y[m]=N}return y}let b={};for(let[y,m]of Object.entries(t.fields)){let v=y===i?u:p[y]||{};for(let R of m)b[`${y}_${R}`]=v[R]}return b}),l=(h=t.offset)!=null?h:0,c=t.limit!=null?l+t.limit:void 0;return o.slice(l,c)}async create(e,t){await this.ensureTable(e);let r=t.id;if(!r||typeof r!="string")throw new Error(`create(${e}) requires string id`);return this.withStore(this.resolveTableName(e),"readwrite",async i=>{if(await w(i.get(r)))throw new Error(`Record already exists: ${e}#${r}`);return await w(i.put(T(t))),T(t)})}async update(e,t,r){if(!await this.tableExists(e))throw new Error(`Table not found: ${e}`);return this.withStore(this.resolveTableName(e),"readwrite",async n=>{let s=await w(n.get(t));if(!s)throw new Error(`Record not found: ${e}#${t}`);let o={...s,...T(r),id:t};return await w(n.put(o)),T(o)})}async delete(e,t){await this.tableExists(e)&&await this.withStore(this.resolveTableName(e),"readwrite",async i=>{await w(i.delete(t))})}async bulkCreate(e,t){await this.ensureTable(e);let r=this.resolveTableName(e),n=(await this.getDb()).transaction(r,"readwrite"),s=n.objectStore(r),o=[];for(let l of t){if(!l.id||typeof l.id!="string")throw n.abort(),new Error(`bulkCreate(${e}) requires string id per row`);let c=l.id;if(typeof c!="string")throw n.abort(),new Error(`bulkCreate(${e}) requires string id per row`);if(await w(s.get(c)))throw n.abort(),new Error(`Record already exists: ${e}#${c}`);let h=T(l);await w(s.put(h)),o.push(h)}return await C(n),o}async bulkUpdate(e,t){if(!await this.tableExists(e))throw new Error(`Table not found: ${e}`);let i=this.resolveTableName(e),s=(await this.getDb()).transaction(i,"readwrite"),o=s.objectStore(i),l=[];for(let c of t){let d=await w(o.get(c.id));if(!d)throw s.abort(),new Error(`Record not found: ${e}#${c.id}`);let h={...d,...T(c.updates),id:c.id};await w(o.put(h)),l.push(T(h))}return await C(s),l}async dataStores(){let e=await this.getDb();return Array.from(e.objectStoreNames).filter(t=>t!==S)}async takeSnapshot(){let e=await this.dataStores(),t={};for(let r of e)t[r]=await this.listRows(r);return t}async restoreSnapshot(e){var r;let t=await this.dataStores();for(let i of t){let n=(r=e[i])!=null?r:[];await this.putRows(i,n)}}async transaction(e){let t=this.transactionDepth===0,r=null;t&&(r=await this.takeSnapshot()),this.transactionDepth+=1;try{let i=await e(this);return this.transactionDepth-=1,i}catch(i){throw this.transactionDepth-=1,t&&r&&await this.restoreSnapshot(r),i}}async exportBackupPayload(){let e=await this.getDb(),[t,r,i]=await Promise.all([this.getMetaValue(k,0),this.getTableSchemas(),this.takeSnapshot()]);return{schema:"ofplatform-web-indexeddb-backup-v1",dbName:this.dbName,dbVersion:e.version,exportedAt:new Date().toISOString(),schemaVersion:t,tableSchemas:r,tables:i}}async importBackupPayload(e,t={}){var n,s,o,l;if(e.schema!=="ofplatform-web-indexeddb-backup-v1")throw new Error("Invalid backup payload schema");let r=(n=t.clearExisting)!=null?n:!0,i=Object.keys((s=e.tables)!=null?s:{});for(let c of i)await this.ensureTable(c);if(r){let c=await this.dataStores();for(let d of c)i.includes(d)||await this.putRows(d,[])}for(let c of i){let d=Array.isArray(e.tables[c])?e.tables[c]:[];await this.putRows(c,d)}await this.setMetaValue(k,(o=e.schemaVersion)!=null?o:0),await this.setTableSchemas((l=e.tableSchemas)!=null?l:{})}};function z(a,e){if(!e||Object.keys(e).length===0)return a;let t=new URLSearchParams;for(let[i,n]of Object.entries(e))n!=null&&t.set(i,String(n));let r=t.toString();return r?a.includes("?")?`${a}&${r}`:`${a}?${r}`:a}var P=class{constructor(e={}){let t=e.fetch;if(t)this.fetchImpl=t;else{let r=globalThis.fetch;if(typeof r!="function")throw new Error("FetchHttpAdapter: globalThis.fetch is not available. Pass a fetch implementation via options.fetch.");this.fetchImpl=r.bind(globalThis)}}async request(e){var n,s;let t=z(e.url,e.query),r=typeof AbortController!="undefined"?new AbortController:null,i=r&&e.timeoutMs?setTimeout(()=>r.abort(),e.timeoutMs):null;try{let o=e.body!==void 0&&e.body!==null,l={...(n=e.headers)!=null?n:{}};o&&!l["content-type"]&&!l["Content-Type"]&&(l["content-type"]="application/json");let c=await this.fetchImpl(t,{method:e.method,headers:l,body:o?JSON.stringify(e.body):void 0,signal:r==null?void 0:r.signal}),d={};c.headers.forEach((p,g)=>{d[g.toLowerCase()]=p});let u=((s=d["content-type"])!=null?s:"").includes("application/json")?await c.json():await c.text();return{status:c.status,headers:d,data:u}}finally{i!==null&&clearTimeout(i)}}};var q=!0;var D=class{constructor(e={}){var t;if(this.env=(t=e.env)!=null?t:{},e.crypto)this.cryptoImpl=e.crypto;else{let r=globalThis.crypto;if(!(r!=null&&r.subtle))throw new Error("WebPlatformAdapter: globalThis.crypto.subtle is not available. Pass a crypto implementation via options.crypto.");this.cryptoImpl=r}}getEnvVar(e){return this.env[e]}isOnline(){let e=globalThis.navigator;return e&&typeof e.onLine!="undefined"?!!e.onLine:!0}async hashPin(e){return`sha256:${await this.sha256Hex(e)}`}async verifyPin(e,t){if(!t.startsWith("sha256:"))return!1;let r=t.slice(7);return/^[0-9a-f]{64}$/.test(r)?await this.sha256Hex(e)===r:r===e}async sha256Hex(e){let r=new TextEncoder().encode(e),i=await this.cryptoImpl.subtle.digest("SHA-256",r);return Array.from(new Uint8Array(i)).map(n=>n.toString(16).padStart(2,"0")).join("")}};var K=!0;function Y(a){let e=a.query;if(!e||Object.keys(e).length===0)return a.url;let r=new URLSearchParams(e).toString();return a.url.includes("?")?`${a.url}&${r}`:`${a.url}?${r}`}var O=class{constructor(e={}){this.ws=null;this.connectionOptions=null;this.listeners=new Map;this.activeConnectionId=0;this.reconnectAttempts=0;this.reconnectTimeout=null;this.intentionalClose=!1;var r,i,n,s,o;this.reconnect=(r=e.reconnect)!=null?r:!0,this.maxReconnectAttempts=Math.max(0,(i=e.maxReconnectAttempts)!=null?i:5),this.reconnectDelayMs=Math.max(0,(n=e.reconnectDelayMs)!=null?n:500),this.maxReconnectDelayMs=Math.max(this.reconnectDelayMs,(s=e.maxReconnectDelayMs)!=null?s:3e4);let t=(o=e.webSocket)!=null?o:globalThis.WebSocket;if(typeof t!="function")throw new Error("WebSocketAdapter: globalThis.WebSocket is not available. Pass a WebSocket constructor via options.webSocket.");this.WebSocketCtor=t}connect(e){if(this.clearReconnectTimeout(),this.intentionalClose=!0,this.ws)try{this.ws.close(1e3,"client reconnect")}catch{}finally{this.ws=null}this.connectionOptions=e,this.intentionalClose=!1,this.reconnectAttempts=0;let t=++this.activeConnectionId;return this.openConnection(e,t)}async disconnect(){this.intentionalClose=!0,this.clearReconnectTimeout(),this.activeConnectionId+=1,this.ws&&(this.ws.close(1e3,"client disconnect"),this.ws=null)}subscribe(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>{let r=this.listeners.get(e);r&&(r.delete(t),r.size===0&&this.listeners.delete(e))}}async publish(e,t){var i,n;if(!this.ws||this.ws.readyState!==this.ws.OPEN)throw new Error(`WebSocketAdapter: cannot publish "${e}" \u2014 not connected (readyState=${(n=(i=this.ws)==null?void 0:i.readyState)!=null?n:"null"}).`);let r={event:e,payload:t};this.ws.send(JSON.stringify(r))}isConnected(){return this.ws!==null&&this.ws.readyState===this.ws.OPEN}openConnection(e,t){return new Promise((r,i)=>{let n=Y(e),s;try{s=new this.WebSocketCtor(n)}catch(o){i(o instanceof Error?o:new Error(String(o)));return}s.onopen=()=>{if(t!==this.activeConnectionId){try{s.close(1e3,"stale connection")}catch{}r();return}this.ws=s,this.reconnectAttempts=0,r()},s.onerror=o=>{t===this.activeConnectionId&&(this.ws||i(new Error("WebSocketAdapter: connection failed")))},s.onclose=o=>{if(t!==this.activeConnectionId)return;this.ws===s&&(this.ws=null),!this.intentionalClose&&this.reconnect&&this.connectionOptions&&this.reconnectAttempts<this.maxReconnectAttempts&&this.scheduleReconnect()},s.onmessage=o=>{this.handleMessage(o.data)}})}handleMessage(e){if(typeof e!="string")return;let t;try{t=JSON.parse(e)}catch{return}if(typeof t!="object"||t===null||typeof t.event!="string")return;let{event:r,payload:i}=t,n=this.listeners.get(r);if(n)for(let s of n)try{s(i)}catch{}}scheduleReconnect(){this.clearReconnectTimeout();let e=++this.reconnectAttempts,t=Math.min(this.reconnectDelayMs*Math.pow(2,e-1),this.maxReconnectDelayMs);this.reconnectTimeout=setTimeout(()=>{if(!this.intentionalClose&&this.connectionOptions){let r=this.activeConnectionId;this.openConnection(this.connectionOptions,r).catch(()=>{})}},t)}clearReconnectTimeout(){this.reconnectTimeout!==null&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null)}};var Q=!0;var A=class{constructor(e={}){if(e.storage)this.store=e.storage;else{let t=globalThis.localStorage;if(!t)throw new Error("WebStorageAdapter: globalThis.localStorage is not available. Pass a storage implementation via options.storage.");this.store=t}}getItem(e){return this.store.getItem(e)}setItem(e,t){this.store.setItem(e,t)}removeItem(e){this.store.removeItem(e)}};var G=!0;var x=require("ofauth-shared-core");var Z=6e4;function W(a,e){var i,n,s,o,l;let t=new Date().toISOString(),r=a&&typeof a=="object"?a:{};return{sessionId:String((i=r.sessionId)!=null?i:""),identityId:String((n=r.identityId)!=null?n:""),assuranceLevel:String((s=r.assuranceLevel)!=null?s:"basic"),issuedAt:String((o=r.issuedAt)!=null?o:t),expiresAt:String((l=r.expiresAt)!=null?l:e)}}var B=class{constructor(e){this.options=e;var i,n,s,o,l;if(this.serverBaseUrl=((i=e.serverBaseUrl)!=null?i:"").trim().replace(/\/+$/,""),this.cacheKey=e.cacheKey.trim(),this.targetTenantId=((n=e.targetTenantId)==null?void 0:n.trim())||"",this.targetBranchId=((s=e.targetBranchId)==null?void 0:s.trim())||"",this.targetDomainId=((o=e.targetDomainId)==null?void 0:o.trim())||"",this.storage=new A(e.storage?{storage:e.storage}:{}),e.fetchImpl)this.fetchImpl=e.fetchImpl;else if(typeof globalThis.fetch=="function")this.fetchImpl=globalThis.fetch.bind(globalThis);else throw new Error("WebServerAuthoritativeAuthHostSupport requires a fetch implementation");if(this.isOnlineResolver=(l=e.isOnline)!=null?l:(()=>typeof navigator=="undefined"||typeof navigator.onLine=="undefined"?!0:!!navigator.onLine),!this.serverBaseUrl){this.serverBridge=null;return}let t={login:async({principal:c,secret:d})=>{var g,b,f,y;let h=await this.fetchImpl(`${this.serverBaseUrl}/v1/auth/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({principal:c,secret:d,...this.targetTenantId?{targetTenantId:this.targetTenantId}:{},...this.targetBranchId?{targetBranchId:this.targetBranchId}:{},...this.targetDomainId?{targetDomainId:this.targetDomainId}:{}})}),u=await h.json().catch(()=>({}));if(!h.ok)throw new x.AuthRejectionError(String((g=u.code)!=null?g:"AUTH_UNAUTHORIZED"));let p=new Date(Date.now()+15*6e4).toISOString();return{accessToken:String((b=u.accessToken)!=null?b:""),refreshToken:String((f=u.refreshToken)!=null?f:""),session:W(u.session,p),activeContext:(y=u.activeContext)!=null?y:null,activeRoleRef:u.activeRoleRef==null?"":String(u.activeRoleRef)}},refresh:async({refreshToken:c})=>{var p,g,b,f;let d=await this.fetchImpl(`${this.serverBaseUrl}/v1/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:c})}),h=await d.json().catch(()=>({}));if(!d.ok)throw new x.AuthRejectionError(String((p=h.code)!=null?p:"AUTH_UNAUTHORIZED"));let u=new Date(Date.now()+15*6e4).toISOString();return{accessToken:String((g=h.accessToken)!=null?g:""),refreshToken:String((b=h.refreshToken)!=null?b:""),session:W(h.session,u),activeContext:(f=h.activeContext)!=null?f:null}},logout:async({accessToken:c,refreshToken:d})=>{let h={"Content-Type":"application/json"};c&&(h.Authorization=`Bearer ${c}`),await this.fetchImpl(`${this.serverBaseUrl}/v1/auth/logout`,{method:"POST",headers:h,body:JSON.stringify({refreshToken:d})}).catch(()=>{})}},r={getCachedIdentity:async c=>{var d,h;return(h=(d=this.readCacheRecord(c))==null?void 0:d.identity)!=null?h:null},setCachedIdentity:async c=>{let d=this.readCacheRecord(c.principal);this.writeCacheRecord(c.principal,{...d,identity:c})},clearCachedIdentity:async c=>{let d=this.readCacheRecord(c);d&&this.writeCacheRecord(c,{...d,identity:void 0})},getSessionEnvelope:async c=>{var d,h;return(h=(d=this.readCacheRecord(c))==null?void 0:d.envelope)!=null?h:null},setSessionEnvelope:async(c,d)=>{let h=this.readCacheRecord(c);this.writeCacheRecord(c,{...h,envelope:d})},clearSessionEnvelope:async c=>{let d=this.readCacheRecord(c);d&&this.writeCacheRecord(c,{...d,envelope:void 0})}};this.serverBridge=(0,x.createServerAuthoritativeAuthBridge)({authority:t,cache:r,isOnline:()=>this.isOnlineResolver(),verifyOfflineSecret:this.options.verifyOfflineSecret})}get isServerConfigured(){return this.serverBaseUrl.length>0}loadCacheMap(){try{let e=this.storage.getItem(this.cacheKey);if(!e)return{};let t=JSON.parse(e);return t&&typeof t=="object"?t:{}}catch{return{}}}saveCacheMap(e){try{this.storage.setItem(this.cacheKey,JSON.stringify(e))}catch{}}readCacheRecord(e){var r;return(r=this.loadCacheMap()[e])!=null?r:null}writeCacheRecord(e,t){let r=this.loadCacheMap(),i={};t.identity&&(i.identity=t.identity),t.envelope&&(i.envelope=t.envelope),!i.identity&&!i.envelope?delete r[e]:r[e]=i,this.saveCacheMap(r)}clearCachedSessionEnvelope(e){let t=this.readCacheRecord(e);t&&this.writeCacheRecord(e,{...t,envelope:void 0})}clearAllCachedSessionEnvelopes(){let e=this.loadCacheMap();for(let t of Object.keys(e)){let r=e[t];r&&(e[t]=r.identity?{identity:r.identity}:{},e[t].identity||delete e[t])}this.saveCacheMap(e)}async login(e,t){var i,n;if(!this.serverBridge)return{status:"rejected",reasonCode:"AUTH_BRIDGE_NOT_CONFIGURED"};let r=await this.serverBridge.login({principal:e,secret:t,verifyMethod:"pin"});return r.status==="authenticated"?{status:"authenticated",mode:(i=r.mode)!=null?i:"online"}:{status:"rejected",reasonCode:(n=r.reasonCode)!=null?n:"AUTH_UNAUTHORIZED"}}async logout(e){this.serverBridge&&await this.serverBridge.logout(e)}async restoreCachedEnvelope(){var t,r,i;let e=this.loadCacheMap();for(let n of Object.keys(e)){let s=(t=e[n])==null?void 0:t.envelope;if(!s)continue;let o=Date.parse(s.expiresAt),l=Number.isFinite(o),c=!l||o<=Date.now()+Z;if(this.isOnlineResolver()&&c&&s.refreshToken&&this.serverBaseUrl)try{let d=await this.fetchImpl(`${this.serverBaseUrl}/v1/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:s.refreshToken})}),h=await d.json().catch(()=>({}));if(d.ok&&h.accessToken){let u={...s,accessToken:String(h.accessToken),refreshToken:h.refreshToken?String(h.refreshToken):s.refreshToken,activeContext:(i=(r=h.activeContext)!=null?r:s.activeContext)!=null?i:null};return this.writeCacheRecord(n,{...e[n],envelope:u}),{principal:n,envelope:u}}}catch{}if(l&&o>Date.now())return{principal:n,envelope:s}}return null}getStoredAccessToken(e){var t,r,i;return(i=(r=(t=this.readCacheRecord(e))==null?void 0:t.envelope)==null?void 0:r.accessToken)!=null?i:""}getStoredEnvelope(e){var t,r;return(r=(t=this.readCacheRecord(e))==null?void 0:t.envelope)!=null?r:null}};var X={platform:"web",ready:!0,notes:"All adapter domains implemented: IndexedDbAdapter (db), FetchHttpAdapter (http), WebPlatformAdapter (platform), WebSocketAdapter (socket), WebStorageAdapter (storage)."};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofplatform-web",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Web platform adapters for browser-based of* host apps (IndexedDB, browser transport, and future web primitives).",
|
|
6
6
|
"author": {
|
|
@@ -33,9 +33,13 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"ofcore": "0.2.0-alpha.0"
|
|
35
35
|
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"ofauth-shared-core": "0.2.0-alpha.0"
|
|
38
|
+
},
|
|
36
39
|
"devDependencies": {
|
|
37
40
|
"esbuild": "^0.25.11",
|
|
38
41
|
"fake-indexeddb": "^6.2.5",
|
|
42
|
+
"ofauth-shared-core": "0.2.0-alpha.0",
|
|
39
43
|
"typescript": "^5.9.3"
|
|
40
44
|
},
|
|
41
45
|
"publishConfig": {
|