ofsync-shared-core 0.2.0-alpha.2 → 0.2.0-alpha.4
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.
|
@@ -6,6 +6,7 @@ export interface OfsyncHttpTransportOptions {
|
|
|
6
6
|
httpAdapter: HttpAdapter;
|
|
7
7
|
getAccessToken?: () => string | null | undefined | Promise<string | null | undefined>;
|
|
8
8
|
nowIso?: () => string;
|
|
9
|
+
hashText?: (input: string) => Promise<string>;
|
|
9
10
|
}
|
|
10
11
|
export declare class OfsyncTransportError extends Error {
|
|
11
12
|
readonly code: string;
|
package/dist/index.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var T={batchSize:200,maxRetry:5,scheduler:{mode:"manual",onlineTrigger:!0},retryBackoff:{strategy:"exponential-jitter",baseDelayMs:500,maxDelayMs:3e4}};var x=class{constructor(){this.rows=new Map}async enqueue(e){this.rows.set(e.outboxId,e)}async listPending(e){return[...this.rows.values()].filter(r=>r.status==="PENDING"||r.status==="FAILED").sort((r,n)=>r.createdAt.localeCompare(n.createdAt)).slice(0,Math.max(0,e))}async findByIdempotencyKey(e){for(let r of this.rows.values())if(r.idempotencyKey===e)return r;return null}async listAll(){return[...this.rows.values()].sort((e,r)=>e.createdAt.localeCompare(r.createdAt))}async update(e){this.rows.set(e.outboxId,e)}async remove(e){this.rows.delete(e)}};var _=class{constructor(){this.rows=new Map}async get(e){return this.rows.has(e)?this.rows.get(e):null}async set(e,r){this.rows.set(e,r)}};function j(t){return t.code==="VERSION_CONFLICT"?{conflictId:t.conflictId,action:"APPLY_REMOTE"}:t.code==="SCOPE_CONFLICT"?{conflictId:t.conflictId,action:"MANUAL_MERGE"}:{conflictId:t.conflictId,action:"KEEP_LOCAL"}}var St="phase1-runtime-skeleton";import{CoreRuntime as Z}from"ofcore";import{InMemoryDbAdapter as tt}from"ofcore";import{asReadonlyStore as K,createStore as Y}from"ofcore";function et(t={}){return{...T,...t,scheduler:{...T.scheduler,...t.scheduler||{}},retryBackoff:{...T.retryBackoff,...t.retryBackoff||{}}}}function rt(t){return`${t.tenantId||"__standalone__"}::${t.branchId||"__all__"}::${t.ledgerProfileId||"__default_ledger__"}::${t.sourceDomainId||"__default_source__"}::${t.deviceId||"__device__"}`}function nt(t,e){return`${rt(t)}::${e}`}function ot(t){if(typeof t!="string"||t.trim().length===0)return null;let e=Date.parse(t);return Number.isFinite(e)?new Date(e+5e3).toISOString():null}var E=class{constructor(e,r,n,o={}){this.runtime=e;this.runtimeStateStore=r;this.orchestratorStateStore=n;this.bridges=new Map;this.orchestratorScope=null;this.orchestratorSocketUnsubscribe=null;this.orchestratorSocketConnectedByCore=!1;this.orchestratorSyncPromise=null;this.runtimeStore=K(this.runtimeStateStore),this.orchestratorStore=K(this.orchestratorStateStore),this.policy=et(o.policy),this.transport=o.transport,this.outboxStore=o.outboxStore||new x,this.checkpointStore=o.checkpointStore||new _,this.conflictResolvers=o.conflictResolvers||[],this.projectionHook=o.projectionHook}static builder(){return new L}get registry(){return this.runtime.registry}getPolicy(){return this.policy}registerDomainBridge(e){let r=e.domain.trim();if(!r)throw new Error("DomainBridge.domain is required");if(this.bridges.has(r))throw new Error(`DomainBridge for domain "${r}" is already registered`);this.bridges.set(r,e)}listDomainBridges(){return[...this.bridges.keys()].sort()}async getOutboxState(){let e=await this.outboxStore.listAll(),r=e.filter(o=>o.status==="PENDING").length,n=e.filter(o=>o.status==="FAILED").length;return{pending:r,failed:n,total:e.length}}async enqueueLocalChange(e,r){let n=(r||`${e.domain}:${e.entity}:${e.recordId}:${e.type}:${e.id}`).trim();if(!n)throw new Error("idempotencyKey must not be empty");let o=await this.outboxStore.findByIdempotencyKey(n);if(o)return o;let i=new Date().toISOString(),c={outboxId:`outbox_${Date.now()}_${Math.random().toString(36).slice(2)}`,idempotencyKey:n,change:e,retryCount:0,status:"PENDING",createdAt:i,updatedAt:i};return await this.outboxStore.enqueue(c),c}async start(e={}){this.runtimeStateStore.setState({phase:"starting",started:!1,syncing:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.start(e),this.runtimeStateStore.setState(r=>({...r,phase:"started",started:!0,syncing:!1,startCount:r.startCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(r){throw this.runtimeStateStore.setState({phase:"error",started:!1,syncing:!1,lastError:r instanceof Error?r.message:String(r),lastTransitionAt:new Date().toISOString()}),r}}async stop(){await this.stopOrchestrator(),this.stopScheduler(),this.runtimeStateStore.setState({phase:"stopping",started:this.runtime.isStarted(),syncing:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.stop(),this.runtimeStateStore.setState(e=>({...e,phase:"stopped",started:!1,syncing:!1,stopCount:e.stopCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(e){throw this.runtimeStateStore.setState({phase:"error",started:this.runtime.isStarted(),syncing:!1,lastError:e instanceof Error?e.message:String(e),lastTransitionAt:new Date().toISOString()}),e}}async startSync(e){if(!this.runtime.isStarted())throw new Error("Cannot start sync before runtime is started");this.runtimeStateStore.setState({phase:"syncing",started:!0,syncing:!0,lastError:null,lastTransitionAt:new Date().toISOString()});try{if(this.transport){await this.collectBridgeLocalChanges(e),await this.processOutbox(e);let r=this.listDomainBridges();for(let n of r){let o=nt(e,n),i=await this.checkpointStore.get(o),c;try{c=await this.transport.pull({scope:e,cursor:i,limit:this.policy.batchSize,domain:n})}catch(a){let s=(a==null?void 0:a.code)||"",d=a==null?void 0:a.details,h=ot(d==null?void 0:d.cutoffTime);if(s!=="SYNC_RESYNC_REQUIRED"||!h)throw a;await this.checkpointStore.set(o,h),i=h,c=await this.transport.pull({scope:e,cursor:i,limit:this.policy.batchSize,domain:n})}let u=(c.changes||[]).map(a=>{let s=String(a.domain||"").trim().toLowerCase();if(!s)return{...a,domain:n};if(s!==n)throw new Error(`Sync pull domain mismatch: expected ${n}, received ${s}`);return a});await this.applyRemoteChanges(u,e),c.nextCursor!==void 0&&await this.checkpointStore.set(o,c.nextCursor)}}this.runtimeStateStore.setState(r=>({...r,phase:"started",started:!0,syncing:!1,syncCount:r.syncCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(r){throw this.runtimeStateStore.setState({phase:"error",started:this.runtime.isStarted(),syncing:!1,lastError:r instanceof Error?r.message:String(r),lastTransitionAt:new Date().toISOString()}),r}}async stopSync(){this.runtimeStateStore.setState(e=>({...e,phase:this.runtime.isStarted()?"started":"stopped",syncing:!1,lastTransitionAt:new Date().toISOString()}))}isStarted(){return this.runtime.isStarted()}startScheduler(e){if(this.policy.scheduler.mode!=="interval")return;let r=this.policy.scheduler.intervalMs||1e4;this.stopScheduler(),this.schedulerTimer=setInterval(()=>{this.startSync(e).catch(n=>{this.runtimeStateStore.setState(o=>({...o,phase:"error",syncing:!1,lastError:n instanceof Error?n.message:String(n),lastTransitionAt:new Date().toISOString()}))})},r)}stopScheduler(){this.schedulerTimer&&(clearInterval(this.schedulerTimer),this.schedulerTimer=void 0)}async notifyOnline(e){if(this.policy.scheduler.onlineTrigger!==!1){if(this.orchestratorStateStore.getState().running){this.orchestratorScope=e,await this.triggerSync("online");return}await this.startSync(e)}}getOrchestratorState(){return this.orchestratorStateStore.getState()}async startOrchestrator(e){let r=e.scope;this.orchestratorScope=r,this.orchestratorStateStore.setState({running:!0,syncing:!1,wsConnected:!1,lastSyncAt:null,lastTriggerReason:null,lastError:null,lastErrorCode:null}),this.runtime.isStarted()||await this.start(),await this.configureOrchestratorSocket(e.socket),this.configureOrchestratorInterval(e.auto)}async stopOrchestrator(){var e;this.orchestratorTimer&&(clearInterval(this.orchestratorTimer),this.orchestratorTimer=void 0),this.orchestratorSocketHealthTimer&&(clearInterval(this.orchestratorSocketHealthTimer),this.orchestratorSocketHealthTimer=void 0),this.orchestratorSocketUnsubscribe&&(this.orchestratorSocketUnsubscribe(),this.orchestratorSocketUnsubscribe=null),this.orchestratorSocketConnectedByCore&&((e=this.runtime.registry)!=null&&e.socketAdapter)&&await this.runtime.registry.socketAdapter.disconnect(),this.orchestratorSocketConnectedByCore=!1,this.orchestratorSyncPromise=null,this.orchestratorStateStore.setState(r=>({...r,running:!1,syncing:!1,wsConnected:!1,lastTriggerReason:null}))}async triggerSync(e="manual"){if(!this.orchestratorScope)throw new Error("Sync orchestrator scope is not initialized");return this.orchestratorSyncPromise?this.orchestratorSyncPromise:(this.runtime.isStarted()||await this.start(),this.orchestratorStateStore.setState(r=>({...r,syncing:!0,lastTriggerReason:e,lastError:null,lastErrorCode:null})),this.orchestratorSyncPromise=this.startSync(this.orchestratorScope).then(()=>{this.orchestratorStateStore.setState(r=>({...r,syncing:!1,lastSyncAt:new Date().toISOString(),lastError:null,lastErrorCode:null}))}).catch(r=>{var i;let n=r instanceof Error?r.message:String(r),o=String((i=r==null?void 0:r.code)!=null?i:"");throw this.orchestratorStateStore.setState(c=>({...c,syncing:!1,lastError:n,lastErrorCode:o||null})),r}).finally(()=>{this.orchestratorSyncPromise=null}),this.orchestratorSyncPromise)}async configureOrchestratorSocket(e){var c;let r=this.orchestratorSocketConnectedByCore;if(this.orchestratorSocketUnsubscribe&&(this.orchestratorSocketUnsubscribe(),this.orchestratorSocketUnsubscribe=null),!((e==null?void 0:e.enabled)!==!1)){this.orchestratorSocketConnectedByCore=!1;return}let o=(c=this.runtime.registry)==null?void 0:c.socketAdapter;if(!o){this.orchestratorSocketConnectedByCore=!1;return}if(r)try{await o.disconnect()}catch{}this.orchestratorSocketConnectedByCore=!1;let i=e!=null&&e.resolveConnectOptions?await e.resolveConnectOptions():(e==null?void 0:e.connectOptions)||null;if(i){try{await o.connect(i),this.orchestratorSocketConnectedByCore=!0;let u=(e==null?void 0:e.eventName)||"sync:notify";this.orchestratorSocketUnsubscribe=o.subscribe(u,()=>{this.triggerSync("notify").catch(a=>{var h;let s=a instanceof Error?a.message:String(a),d=String((h=a==null?void 0:a.code)!=null?h:"");this.orchestratorStateStore.setState(l=>({...l,lastError:s,lastErrorCode:d||null}))})})}catch{this.orchestratorSocketConnectedByCore=!1}this.orchestratorStateStore.setState(u=>({...u,wsConnected:this.readSocketConnectedState()})),this.startOrchestratorSocketHealthMonitor()}}configureOrchestratorInterval(e){var o;if(this.orchestratorTimer&&(clearInterval(this.orchestratorTimer),this.orchestratorTimer=void 0),!((o=e==null?void 0:e.enabled)!=null?o:this.policy.scheduler.mode==="interval"))return;let n=(e==null?void 0:e.intervalMs)||this.policy.scheduler.intervalMs||1e4;this.orchestratorTimer=setInterval(()=>{this.triggerSync("interval").catch(i=>{var a;let c=i instanceof Error?i.message:String(i),u=String((a=i==null?void 0:i.code)!=null?a:"");this.orchestratorStateStore.setState(s=>({...s,lastError:c,lastErrorCode:u||null}))})},n)}startOrchestratorSocketHealthMonitor(){this.orchestratorSocketHealthTimer&&(clearInterval(this.orchestratorSocketHealthTimer),this.orchestratorSocketHealthTimer=void 0),this.orchestratorSocketHealthTimer=setInterval(()=>{this.orchestratorStateStore.setState(e=>({...e,wsConnected:this.readSocketConnectedState()}))},2e3)}readSocketConnectedState(){var r;let e=(r=this.runtime.registry)==null?void 0:r.socketAdapter;if(e&&typeof e.isConnected=="function")try{return!!e.isConnected()}catch{return!1}return this.orchestratorSocketConnectedByCore}async processOutbox(e){var o;if(!this.transport)return;let r=await this.outboxStore.listPending(this.policy.batchSize);if(r.length===0)return;let n=new Map;for(let i of r){let c=String(i.change.domain||"").trim().toLowerCase()||"__unknown__";n.has(c)||n.set(c,[]),(o=n.get(c))==null||o.push(i)}for(let[i,c]of n.entries()){if(i==="__unknown__"){for(let h of c){let l=h.retryCount+1;await this.outboxStore.update({...h,retryCount:l,status:l>=this.policy.maxRetry?"FAILED":"PENDING",lastError:"DOMAIN_REQUIRED",updatedAt:new Date().toISOString()})}continue}let u=await this.transport.push({scope:e,outbox:c,domain:i}),a=new Set((u.applied||[]).map(h=>h.changeId)),s=new Map((u.failed||[]).map(h=>[h.changeId,h])),d=new Set((u.conflicts||[]).map(h=>h.changeId));await this.resolveConflicts(u.conflicts||[],e,i);for(let h of c){if(a.has(h.change.id)){await this.outboxStore.remove(h.outboxId);continue}let l=s.get(h.change.id);if(l||d.has(h.change.id)){let y=h.retryCount+1;await this.outboxStore.update({...h,retryCount:y,status:y>=this.policy.maxRetry?"FAILED":"PENDING",lastError:(l==null?void 0:l.message)||(d.has(h.change.id)?"SYNC_CONFLICT":h.lastError),updatedAt:new Date().toISOString()})}}}}async collectBridgeLocalChanges(e){let r=this.listDomainBridges();for(let n of r){let o=this.bridges.get(n);if(!(o!=null&&o.collectLocalChanges))continue;let i=await o.collectLocalChanges(e);if(!(!Array.isArray(i)||i.length===0))for(let c of i){let u=String(c.domain||"").trim()||n,a={...c,domain:u,scope:c.scope||e},s=`${a.domain}:${a.id}`;await this.enqueueLocalChange(a,s)}}}async applyRemoteChanges(e,r){if(!Array.isArray(e)||e.length===0)return;let n=[...e].sort((i,c)=>i.occurredAt.localeCompare(c.occurredAt)),o=new Map;for(let i of n){o.has(i.domain)||o.set(i.domain,[]);let c=o.get(i.domain);c&&c.push(i)}for(let[i,c]of o.entries()){let u=this.bridges.get(i);if(u!=null&&u.applyRemoteChanges&&(await u.applyRemoteChanges(c,r),this.projectionHook))for(let a of c)await this.projectionHook(a,r)}}async resolveConflicts(e,r,n){var i;if(!((i=this.transport)!=null&&i.resolveConflicts)||e.length===0)return;let o=[];for(let c of e){let u=[...this.conflictResolvers],a=null;for(let s of u){let d=await s(c,{scope:r});if(d){a=d;break}}a||(a=j(c)),o.push(a)}o.length>0&&await this.transport.resolveConflicts({scope:r,domain:n,resolutions:o})}},L=class{constructor(){this.runtimeBuilder=Z.builder();this.options={};this.runtimeBuilder.withDbAdapter(()=>new tt)}withPlatformAdapter(e){return this.runtimeBuilder.withPlatformAdapter(e),this}withDbAdapter(e){return this.runtimeBuilder.withDbAdapter(e),this}withHttpAdapter(e){return this.runtimeBuilder.withHttpAdapter(e),this}withSocketAdapter(e){return this.runtimeBuilder.withSocketAdapter(e),this}withLoggerAdapter(e){return this.runtimeBuilder.withLoggerAdapter(e),this}withActivitySink(e){return this.runtimeBuilder.withActivitySink(e),this}withExtension(e,r){return this.runtimeBuilder.withExtension(e,r),this}withPolicy(e){return this.options={...this.options,policy:e},this}withTransport(e){return this.options={...this.options,transport:e},this}withOutboxStore(e){return this.options={...this.options,outboxStore:e},this}withCheckpointStore(e){return this.options={...this.options,checkpointStore:e},this}withConflictResolvers(e){return this.options={...this.options,conflictResolvers:e},this}withProjectionHook(e){return this.options={...this.options,projectionHook:e},this}build(){let e=Y({phase:"idle",started:!1,syncing:!1,startCount:0,stopCount:0,syncCount:0,lastError:null,lastTransitionAt:new Date().toISOString()}),r=Y({running:!1,syncing:!1,wsConnected:!1,lastSyncAt:null,lastTriggerReason:null,lastError:null,lastErrorCode:null}),n=this.runtimeBuilder.build();return new E(n,e,r,this.options)}};function It(t={}){let e=E.builder();return t.policy&&e.withPolicy(t.policy),t.transport&&e.withTransport(t.transport),t.outboxStore&&e.withOutboxStore(t.outboxStore),t.checkpointStore&&e.withCheckpointStore(t.checkpointStore),t.conflictResolvers&&e.withConflictResolvers(t.conflictResolvers),t.projectionHook&&e.withProjectionHook(t.projectionHook),t.socketAdapterFactory&&e.withSocketAdapter(t.socketAdapterFactory),e.build()}function At(t,e){t.registerDomainBridge(e)}async function Et(t,e,r={}){t.isStarted()||await t.start(r),await t.startSync(e)}async function Pt(t){await t.stopSync()}function w(t){return{store:t.orchestratorStore,getState:()=>t.getOrchestratorState(),start:async e=>t.startOrchestrator(e),stop:async()=>t.stopOrchestrator(),trigger:async(e="manual")=>t.triggerSync(e)}}var P=class extends Error{constructor(e,r,n=!1,o,i){super(r),this.name="OfsyncTransportError",this.code=e,this.retryable=n,this.status=o,this.details=i}};async function it(t){let e=globalThis.crypto;if(!e||!e.subtle)throw new Error("WebCrypto API is not available");let r=new TextEncoder().encode(t),n=await e.subtle.digest("SHA-256",r);return[...new Uint8Array(n)].map(o=>o.toString(16).padStart(2,"0")).join("")}function st(t){let e=String(t.table||""),r=String(t.id||""),n=String(t.changeId||"");return[e,r,n].join("::")}function q(t){return{conflictId:st(t),changeId:String(t.changeId||""),domain:"unknown",entity:String(t.table||""),recordId:String(t.id||""),code:String(t.code||"UNKNOWN_CONFLICT"),message:String(t.message||"Conflict detected"),retryable:!!t.retryable,scope:{tenantId:typeof t.tenantId=="string"?t.tenantId:void 0,branchId:typeof t.branchId=="string"?t.branchId:void 0,ledgerProfileId:typeof t.ledgerProfileId=="string"?t.ledgerProfileId:typeof t.financeDomainId=="string"?t.financeDomainId:void 0,sourceDomainId:typeof t.sourceDomain=="string"?t.sourceDomain:typeof t.financeDomainId=="string"?t.financeDomainId:void 0},details:t}}function D(t){return!!(t&&typeof t=="object"&&!Array.isArray(t))}function ct(t){var r;let e=(r=t.branchId)==null?void 0:r.trim();if(!e)throw new P("BRANCH_SCOPE_REQUIRED","Sync scope requires branchId",!1);return e}function W(t){return typeof t=="string"?t.trim():""}function at(t,e){let r=W(t.productFamilyId);if(r)return r;if(!Array.isArray(e)||e.length===0)return"";let n=Array.from(new Set(e.map(o=>{var i;return W((i=o.change.scope)==null?void 0:i.productFamilyId)}).filter(Boolean)));return n.length===1?n[0]:""}var G=class{constructor(e){this.options=e}nowIso(){return this.options.nowIso?this.options.nowIso():new Date().toISOString()}defaultBootstrapSinceIso(){return new Date(Date.now()-2160*60*60*1e3).toISOString()}async buildHeaders(e,r){let n={"content-type":"application/json","x-branch-id":ct(e),"x-sync-protocol-version":"1"};e.tenantId&&(n["x-tenant-id"]=e.tenantId),e.deviceId&&(n["x-device-id"]=e.deviceId),e.ledgerProfileId&&(n["x-ledger-profile-id"]=e.ledgerProfileId),e.sourceDomainId&&(n["x-finance-source-domain-id"]=e.sourceDomainId);let o=at(e,r);if(o&&(n["x-product-family-id"]=o),this.options.getAccessToken){let i=await this.options.getAccessToken();i&&(n.authorization=`Bearer ${i}`)}return n}normalizeUrl(e){return`${this.options.baseUrl.replace(/\/+$/,"")}${e}`}normalizeDomainPath(e){let r=e.trim().toLowerCase().replace(/[^a-z0-9_-]/g,"");return r||null}resolveDomainPathOrThrow(e){let r=this.normalizeDomainPath(e||"");if(!r)throw new P("DOMAIN_REQUIRED","Sync transport requires explicit domain",!1);return r}parseFailure(e,r){var a,s,d;let n=r||{},o=((a=n.error)==null?void 0:a.code)||"SYNC_TRANSPORT_ERROR",i=((s=n.error)==null?void 0:s.message)||`Sync transport failed with status ${e}`,c=!!((d=n.error)!=null&&d.retryable),u=D(n.error)&&D(n.error.details)?n.error.details:void 0;throw new P(o,i,c,e,u)}async push(e){let r={changes:e.outbox.map(s=>{let d=D(s.change.payload)?s.change.payload:{},h=typeof d.table=="string"?d.table.trim():"",l=D(d.record)?d.record:null,y=Date.parse(s.change.occurredAt);return h&&l?{id:s.change.id,table:h,type:s.change.type,record:{...l,id:s.change.recordId||String(l.id||s.change.id)},updated_at:Number.isFinite(y)?y:s.change.occurredAt}:{id:s.change.id,entity:s.change.entity,type:s.change.type,data:{id:s.change.recordId,...d},timestamp:s.change.occurredAt}})},n=JSON.stringify(r),o=await it(n),i=await this.buildHeaders(e.scope,e.outbox);i["x-data-checksum"]=o;let c=this.resolveDomainPathOrThrow(e.domain),u=await this.options.httpAdapter.request({method:"POST",headers:i,body:r,url:this.normalizeUrl(`/sync/${c}/push`)});u.status>=400&&this.parseFailure(u.status,u.data);let a=u.data||{};return{applied:Array.isArray(a.applied)?a.applied:[],failed:Array.isArray(a.failed)?a.failed:[],conflicts:Array.isArray(a.conflicts)?a.conflicts.map(q):[]}}async pull(e){let r=await this.buildHeaders(e.scope),n=e.cursor||this.defaultBootstrapSinceIso(),o=this.resolveDomainPathOrThrow(e.domain),i=await this.options.httpAdapter.request({method:"GET",headers:r,query:{since:n},url:this.normalizeUrl(`/sync/${o}/pull`)});i.status>=400&&this.parseFailure(i.status,i.data);let c=i.data||{},u=Array.isArray(c.changes)?c.changes.map(s=>{var l,y,f,g;let d=typeof s.table=="string"?s.table:"",h=typeof s.domain=="string"&&s.domain.trim().length>0?s.domain:d.startsWith("auth_")?"ofauth":d.startsWith("offinance_")||d.startsWith("ofinance_")?"offinance":d.startsWith("ofcoop_")?"ofcoop":d.startsWith("ofpos_")?"ofpos":"unknown";return{id:String(s.changeId||s.id||""),domain:String(h),entity:String(s.entity||d||""),type:String(s.type||s.action||"UPDATE"),recordId:String(s.recordId||((l=s.record)==null?void 0:l.id)||((y=s.data)==null?void 0:y.id)||s.id||""),payload:typeof d=="string"&&d.length>0?{table:String(d),action:String(s.action||s.type||"UPDATE"),record:{...s.record||s.data||{},id:String(s.recordId||((f=s.record)==null?void 0:f.id)||((g=s.data)==null?void 0:g.id)||s.id||"")}}:s.record||s.data||{},occurredAt:String(s.timestamp||this.nowIso()),scope:e.scope}}):[],a=u.length>0?u[u.length-1].occurredAt:this.nowIso();return{changes:u,nextCursor:a,conflicts:[]}}async listConflicts(e){let r=await this.buildHeaders(e.scope),n=this.normalizeDomainPath(e.domain||""),o=n?this.normalizeUrl(`/sync/${n}/conflicts`):this.normalizeUrl("/sync/conflicts"),i=await this.options.httpAdapter.request({method:"GET",url:o,headers:r});i.status>=400&&this.parseFailure(i.status,i.data);let c=i.data||{};return Array.isArray(c.conflicts)?c.conflicts.map(q):[]}async resolveConflicts(e){var h;let r=await this.listConflicts({scope:e.scope,domain:e.domain}),n=new Map(r.map(l=>[l.conflictId,l])),o=e.resolutions.map(l=>n.get(l.conflictId)).filter(l=>!!l).map(l=>({table:l.entity,id:l.recordId})),i=await this.buildHeaders(e.scope),c={resolutions:o},u=this.normalizeDomainPath(e.domain||""),a=u?this.normalizeUrl(`/sync/${u}/conflicts/resolve`):this.normalizeUrl("/sync/conflicts/resolve"),s=await this.options.httpAdapter.request({method:"POST",url:a,headers:i,body:c});s.status>=400&&this.parseFailure(s.status,s.data);let d=Array.isArray((h=s.data)==null?void 0:h.remaining)?s.data.remaining.length:0;return{resolvedCount:Math.max(0,o.length-d)}}};function Q(t){return`Tidak dapat terhubung ke server sinkronisasi (${t.trim()||"VITE_OFSYNC_BASE_URL"}). Pastikan ofsync-server berjalan dan CORS origin browser diizinkan.`}function V(t){return t.trim().replace(/\/+$/,"")}function X(t){return t instanceof Error?t.message:String(t!=null?t:"")}function xt(t){let e=X(t).toLowerCase();return e.includes("server sinkronisasi")||e.includes("cors origin browser diizinkan")||e.includes("tidak dapat terhubung ke server sinkronisasi")}function lt(t,e=9e5){if(typeof t=="number"&&Number.isFinite(t)&&t>0)return Math.floor(t*1e3);if(typeof t!="string")return e;let r=t.trim();if(!r)return e;if(/^\d+$/.test(r))return Math.max(1,Number(r))*1e3;let n=r.match(/^(\d+)\s*([smhd])$/i);if(!n)return e;let o=Number(n[1]),i=n[2].toLowerCase(),c=i==="s"?1e3:i==="m"?6e4:i==="h"?36e5:864e5;return Math.max(1,o)*c}function R(t,e,r=Q){let n=X(t),o=n.toLowerCase();return o.includes("networkerror")||o.includes("failed to fetch")||o.includes("load failed")||o.includes("fetch failed")||o.includes("the network connection was lost")||o.includes("network request failed")?r(e):n}function k(t,e){let r=new Error(e);return r.code=t,r}function _t(t){var u,a;let e=t.accessToken.trim(),r=t.branchId.trim();if(!e||!r)return null;let n=V(t.baseUrl).replace(/^http:\/\//i,"ws://").replace(/^https:\/\//i,"wss://"),o={token:e,branchId:r},i=String((u=t.tenantId)!=null?u:"").trim(),c=String((a=t.domainId)!=null?a:"").trim();return i&&(o.tenantId=i),c&&(o.domainId=c),{url:`${n}/ws/sync-notify`,query:o}}async function Dt(t){var o,i,c,u;if(t.bearerToken.length>0)return t.bearerToken;let e=(o=t.nowMs)!=null?o:Date.now(),r=(i=t.refreshLeewayMs)!=null?i:1e4;if((c=t.session)!=null&&c.accessToken){if(t.session.expiresAtMs>e+r)return t.session.accessToken;let a=await t.refresh();if(a!=null&&a.accessToken)return a.accessToken}let n=await t.loginFromCredentials();return(u=n==null?void 0:n.accessToken)!=null?u:""}function Lt(t){var u,a,s;let e=(u=t.fetchImpl)!=null?u:fetch,r=V(t.baseUrl),n=(a=t.accessTokenFallbackTtlMs)!=null?a:9e5,o=(s=t.networkErrorMessage)!=null?s:Q,i=d=>(Array.isArray(d.scopes)?d.scopes:[]).filter(l=>!!(l&&typeof l=="object")).map(l=>{var y,f,g,S;return{tenantId:typeof l.tenantId=="string"&&l.tenantId.trim().length>0?l.tenantId:null,branchId:String((y=l.branchId)!=null?y:"").trim(),role:String((f=l.role)!=null?f:""),userId:String((g=l.userId)!=null?g:""),email:String((S=l.email)!=null?S:"")}}).filter(l=>l.branchId.length>0),c=(d,h)=>{var f,g,S;let l=String((g=(f=d.accessToken)!=null?f:d.token)!=null?g:""),y=String((S=d.refreshToken)!=null?S:"");if(!l||!y)throw k("SYNC_AUTH_TOKEN_INCOMPLETE","Sync auth gagal: token tidak lengkap.");return{accessToken:l,refreshToken:y,expiresAtMs:Date.now()+lt(d.expiresIn,n),principal:h}};return{async fetchScopeOptions(d,h){var l,y,f;if(!r)return[];try{let g=await e(`${r}/api/v1/auth/scope-options`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:d,password:h})}),S=await g.json().catch(()=>({}));if(!g.ok){let m=(l=S.error)!=null?l:{};throw k(String((y=m.code)!=null?y:`HTTP_${g.status}`),String((f=m.message)!=null?f:`Sync scope discovery gagal (${g.status})`))}return i(S)}catch(g){let S=g;throw k(typeof(S==null?void 0:S.code)=="string"?S.code:"SYNC_AUTH_SCOPE_DISCOVERY_FAILED",R(g,r,o))}},async login(d,h,l){var g,S,m,O,I;let y=String((g=l.branchId)!=null?g:"").trim(),f=String((S=l.tenantId)!=null?S:"").trim();try{let b=await e(`${r}/api/v1/auth/login`,{method:"POST",headers:{"Content-Type":"application/json","X-Branch-ID":y,...f?{"X-Tenant-ID":f}:{},"X-Device-ID":t.deviceId},body:JSON.stringify({username:d,password:h})}),C=await b.json().catch(()=>({}));if(!b.ok){let A=(m=C.error)!=null?m:{};throw k(String((O=A.code)!=null?O:`HTTP_${b.status}`),String((I=A.message)!=null?I:`Sync auth login gagal (${b.status})`))}return c(C,d)}catch(b){let C=b;throw k(typeof(C==null?void 0:C.code)=="string"?C.code:"SYNC_AUTH_FAILED",R(b,r,o))}},async refresh(d,h){var l,y,f;try{let g=await e(`${r}/api/v1/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json",...h?{"X-Tenant-ID":h}:{},"X-Device-ID":t.deviceId},body:JSON.stringify({refreshToken:d})}),S=await g.json().catch(()=>({}));if(!g.ok){let m=(l=S.error)!=null?l:{};throw k(String((y=m.code)!=null?y:`HTTP_${g.status}`),String((f=m.message)!=null?f:`Sync auth refresh gagal (${g.status})`))}return c(S,"")}catch(g){let S=g;throw k(typeof(S==null?void 0:S.code)=="string"?S.code:"SYNC_AUTH_REFRESH_FAILED",R(g,r,o))}}}}function Ut(){let t=new Set;return{track(e){let r=Promise.resolve(e).finally(()=>{t.delete(r)});return t.add(r),r},async waitForIdle(){for(;t.size>0;)await Promise.allSettled([...t])},size(){return t.size},clear(){t.clear()}}}async function Ft(t){if(!t)return 0;let e=await t.getOutboxState();return Number(e.pending||0)+Number(e.failed||0)}async function Ht(t){var i,c,u,a;let e=(i=t.triggerInitialPull)!=null?i:(async s=>{await w(s).trigger("manual")});if(!t.runtimeReady||!t.serverSyncEnabled||!t.isAuthenticated){if(t.lastStartSignatureRef.current="",(c=t.setWsConnected)==null||c.call(t,!1),(u=t.setSyncing)==null||u.call(t,!1),!t.runtime)return;await w(t.runtime).stop().catch(()=>{});return}let r=t.runtime;if(r&&w(r).getState().running&&t.lastStartSignatureRef.current===t.startSignature)return;let n=await t.ensureOrchestratorStarted();w(n).getState().running&&t.lastStartSignatureRef.current===t.startSignature||(await e(n),t.lastStartSignatureRef.current=t.startSignature,(a=t.setLastError)==null||a.call(t,""))}async function $t(t){var n,o,i,c,u,a,s,d,h,l,y,f,g,S,m,O,I,b,C,A,B,N,M,U,F,H,$,z;let e=(n=t.triggerManualSync)!=null?n:(async p=>{await w(p).trigger("manual")});if((o=t.setStage)==null||o.call(t,"check-syncing"),t.runtime.getOrchestratorState().syncing)return{ok:!0,message:(i=t.inProgressMessage)!=null?i:"Sinkronisasi sedang berjalan."};if((c=t.setStage)==null||c.call(t,"check-base-url"),t.serverSyncBaseUrl.length===0){let p=(u=t.missingBaseUrlMessage)!=null?u:"VITE_OFSYNC_BASE_URL belum diatur.";return t.silent||t.setLastError(p),{ok:!1,message:p}}if((a=t.setStage)==null||a.call(t,"resolve-access-token"),!(await t.resolveAccessToken()).trim()){let p=(s=t.missingAccessTokenMessage)!=null?s:"Sesi sinkronisasi tidak valid. Login ulang diperlukan.";return t.silent||t.setLastError(p),t.setAuthBlocked(!0),{ok:!1,message:p}}t.setLastError("");try{(d=t.setStage)==null||d.call(t,"ensure-orchestrator-started"),await t.ensureOrchestratorStarted(),(h=t.setStage)==null||h.call(t,"before-trigger"),await((l=t.beforeTrigger)==null?void 0:l.call(t)),(y=t.setStage)==null||y.call(t,"wait-pending-operations"),await((f=t.waitForPendingOperations)==null?void 0:f.call(t)),(g=t.setStage)==null||g.call(t,"refresh-queue-length:before-trigger"),await((S=t.refreshQueueLength)==null?void 0:S.call(t));try{(m=t.setStage)==null||m.call(t,"trigger-manual"),await e(t.runtime)}catch(p){if((p instanceof Error?p.message:String(p))!=="Cannot start sync before runtime is started")throw p;(O=t.setStage)==null||O.call(t,"ensure-orchestrator-started:retry"),await t.ensureOrchestratorStarted(),(I=t.setStage)==null||I.call(t,"before-trigger:retry"),await((b=t.beforeTrigger)==null?void 0:b.call(t)),(C=t.setStage)==null||C.call(t,"trigger-manual:retry"),await e(t.runtime)}return(A=t.setStage)==null||A.call(t,"refresh-queue-length:after-trigger"),await((B=t.refreshQueueLength)==null?void 0:B.call(t)),(N=t.setStage)==null||N.call(t,"refresh-projection-ui"),await((M=t.refreshProjectionUi)==null?void 0:M.call(t)),(U=t.setStage)==null||U.call(t,"on-success"),await((F=t.onSuccess)==null?void 0:F.call(t)),t.setAuthBlocked(!1),(H=t.setStage)==null||H.call(t,"completed"),{ok:!0,message:"Sinkronisasi selesai."}}catch(p){($=t.onRawError)==null||$.call(t,p instanceof Error?p.message:String(p));let v=R(p,t.serverSyncBaseUrl),J=v.toLowerCase().includes("unauthorized")||v.toLowerCase().includes("sesi sinkronisasi tidak valid")||v.includes("401");return t.silent&&t.isConnectivityError(v)?{ok:!1,message:v}:(t.setLastError(v),t.setAuthBlocked(J),(z=t.setRuntimeError)==null||z.call(t,v),{ok:!1,message:v})}}function zt(t){let e=t.store.getState().syncing;return t.store.subscribe(r=>{var c,u,a,s,d,h,l,y,f,g,S,m;let n=e;if(e=r.syncing,(c=t.setSyncing)==null||c.call(t,r.syncing),(u=t.setWsConnected)==null||u.call(t,r.wsConnected),!r.lastError){if(n&&!r.syncing){let O=(s=(a=t.getPullAppliedCount)==null?void 0:a.call(t))!=null?s:0;(d=t.resetPullAppliedCount)==null||d.call(t),(h=t.onSuccessfulIdle)==null||h.call(t,{appliedCount:O,state:r})}t.setLastError(""),t.setAuthBlocked(!1),(l=t.clearConnectivityError)==null||l.call(t);return}let o=((y=r.lastErrorCode)==null?void 0:y.startsWith("AUTH_"))||r.lastError.toLowerCase().includes("unauthorized")||r.lastError.includes("401");((g=(f=t.shouldSuppressConnectivityError)==null?void 0:f.call(t,r))!=null?g:r.lastTriggerReason!=="manual"&&t.isConnectivityError(r.lastError))||(r.syncing||(S=t.resetPullAppliedCount)==null||S.call(t),t.setLastError(r.lastError),t.setAuthBlocked(!!o),(m=t.onError)==null||m.call(t,{message:r.lastError,isAuthError:!!o,state:r}))})}export{T as DEFAULT_SYNC_POLICY,_ as InMemoryCheckpointStore,x as InMemoryOutboxStore,St as OFSYNC_CONTRACT_STAGE,E as OfsyncCore,L as OfsyncCoreBuilder,G as OfsyncHttpTransport,P as OfsyncTransportError,_t as buildSyncNotifySocketConnectOptions,Lt as createOfsyncAuthClient,Ut as createPendingSyncOperationTracker,k as createSyncAuthError,It as createSyncRuntime,j as defaultConflictResolver,Ht as ensureHostSyncOrchestratorLifecycle,w as getSyncOrchestrator,xt as isSyncConnectivityRuntimeError,R as normalizeSyncNetworkError,lt as parseSyncDurationMs,Ft as readSyncOutboxQueueLength,At as registerDomainBridge,Dt as resolveSyncAccessToken,$t as runManualSyncCycle,Et as startSync,Pt as stopSync,zt as subscribeToSyncOrchestratorLifecycle};
|
|
1
|
+
var T={batchSize:200,maxRetry:5,scheduler:{mode:"manual",onlineTrigger:!0},retryBackoff:{strategy:"exponential-jitter",baseDelayMs:500,maxDelayMs:3e4}};var x=class{constructor(){this.rows=new Map}async enqueue(e){this.rows.set(e.outboxId,e)}async listPending(e){return[...this.rows.values()].filter(r=>r.status==="PENDING"||r.status==="FAILED").sort((r,i)=>r.createdAt.localeCompare(i.createdAt)).slice(0,Math.max(0,e))}async findByIdempotencyKey(e){for(let r of this.rows.values())if(r.idempotencyKey===e)return r;return null}async listAll(){return[...this.rows.values()].sort((e,r)=>e.createdAt.localeCompare(r.createdAt))}async update(e){this.rows.set(e.outboxId,e)}async remove(e){this.rows.delete(e)}};var _=class{constructor(){this.rows=new Map}async get(e){return this.rows.has(e)?this.rows.get(e):null}async set(e,r){this.rows.set(e,r)}};function j(t){return t.code==="VERSION_CONFLICT"?{conflictId:t.conflictId,action:"APPLY_REMOTE"}:t.code==="SCOPE_CONFLICT"?{conflictId:t.conflictId,action:"MANUAL_MERGE"}:{conflictId:t.conflictId,action:"KEEP_LOCAL"}}var St="phase1-runtime-skeleton";import{CoreRuntime as Z}from"ofcore";import{InMemoryDbAdapter as tt}from"ofcore";import{asReadonlyStore as K,createStore as Y}from"ofcore";function et(t={}){return{...T,...t,scheduler:{...T.scheduler,...t.scheduler||{}},retryBackoff:{...T.retryBackoff,...t.retryBackoff||{}}}}function rt(t){return`${t.tenantId||"__standalone__"}::${t.branchId||"__all__"}::${t.ledgerProfileId||"__default_ledger__"}::${t.sourceDomainId||"__default_source__"}::${t.deviceId||"__device__"}`}function nt(t,e){return`${rt(t)}::${e}`}function ot(t){if(typeof t!="string"||t.trim().length===0)return null;let e=Date.parse(t);return Number.isFinite(e)?new Date(e+5e3).toISOString():null}var E=class{constructor(e,r,i,n={}){this.runtime=e;this.runtimeStateStore=r;this.orchestratorStateStore=i;this.bridges=new Map;this.orchestratorScope=null;this.orchestratorSocketUnsubscribe=null;this.orchestratorSocketConnectedByCore=!1;this.orchestratorSyncPromise=null;this.runtimeStore=K(this.runtimeStateStore),this.orchestratorStore=K(this.orchestratorStateStore),this.policy=et(n.policy),this.transport=n.transport,this.outboxStore=n.outboxStore||new x,this.checkpointStore=n.checkpointStore||new _,this.conflictResolvers=n.conflictResolvers||[],this.projectionHook=n.projectionHook}static builder(){return new L}get registry(){return this.runtime.registry}getPolicy(){return this.policy}registerDomainBridge(e){let r=e.domain.trim();if(!r)throw new Error("DomainBridge.domain is required");if(this.bridges.has(r))throw new Error(`DomainBridge for domain "${r}" is already registered`);this.bridges.set(r,e)}listDomainBridges(){return[...this.bridges.keys()].sort()}async getOutboxState(){let e=await this.outboxStore.listAll(),r=e.filter(n=>n.status==="PENDING").length,i=e.filter(n=>n.status==="FAILED").length;return{pending:r,failed:i,total:e.length}}async enqueueLocalChange(e,r){let i=(r||`${e.domain}:${e.entity}:${e.recordId}:${e.type}:${e.id}`).trim();if(!i)throw new Error("idempotencyKey must not be empty");let n=await this.outboxStore.findByIdempotencyKey(i);if(n)return n;let o=new Date().toISOString(),c={outboxId:`outbox_${Date.now()}_${Math.random().toString(36).slice(2)}`,idempotencyKey:i,change:e,retryCount:0,status:"PENDING",createdAt:o,updatedAt:o};return await this.outboxStore.enqueue(c),c}async start(e={}){this.runtimeStateStore.setState({phase:"starting",started:!1,syncing:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.start(e),this.runtimeStateStore.setState(r=>({...r,phase:"started",started:!0,syncing:!1,startCount:r.startCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(r){throw this.runtimeStateStore.setState({phase:"error",started:!1,syncing:!1,lastError:r instanceof Error?r.message:String(r),lastTransitionAt:new Date().toISOString()}),r}}async stop(){await this.stopOrchestrator(),this.stopScheduler(),this.runtimeStateStore.setState({phase:"stopping",started:this.runtime.isStarted(),syncing:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.stop(),this.runtimeStateStore.setState(e=>({...e,phase:"stopped",started:!1,syncing:!1,stopCount:e.stopCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(e){throw this.runtimeStateStore.setState({phase:"error",started:this.runtime.isStarted(),syncing:!1,lastError:e instanceof Error?e.message:String(e),lastTransitionAt:new Date().toISOString()}),e}}async startSync(e){if(!this.runtime.isStarted())throw new Error("Cannot start sync before runtime is started");this.runtimeStateStore.setState({phase:"syncing",started:!0,syncing:!0,lastError:null,lastTransitionAt:new Date().toISOString()});try{if(this.transport){await this.collectBridgeLocalChanges(e),await this.processOutbox(e);let r=this.listDomainBridges();for(let i of r){let n=nt(e,i),o=await this.checkpointStore.get(n),c;try{c=await this.transport.pull({scope:e,cursor:o,limit:this.policy.batchSize,domain:i})}catch(a){let s=(a==null?void 0:a.code)||"",d=a==null?void 0:a.details,h=ot(d==null?void 0:d.cutoffTime);if(s!=="SYNC_RESYNC_REQUIRED"||!h)throw a;await this.checkpointStore.set(n,h),o=h,c=await this.transport.pull({scope:e,cursor:o,limit:this.policy.batchSize,domain:i})}let u=(c.changes||[]).map(a=>{let s=String(a.domain||"").trim().toLowerCase();if(!s)return{...a,domain:i};if(s!==i)throw new Error(`Sync pull domain mismatch: expected ${i}, received ${s}`);return a});await this.applyRemoteChanges(u,e),c.nextCursor!==void 0&&await this.checkpointStore.set(n,c.nextCursor)}}this.runtimeStateStore.setState(r=>({...r,phase:"started",started:!0,syncing:!1,syncCount:r.syncCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(r){throw this.runtimeStateStore.setState({phase:"error",started:this.runtime.isStarted(),syncing:!1,lastError:r instanceof Error?r.message:String(r),lastTransitionAt:new Date().toISOString()}),r}}async stopSync(){this.runtimeStateStore.setState(e=>({...e,phase:this.runtime.isStarted()?"started":"stopped",syncing:!1,lastTransitionAt:new Date().toISOString()}))}isStarted(){return this.runtime.isStarted()}startScheduler(e){if(this.policy.scheduler.mode!=="interval")return;let r=this.policy.scheduler.intervalMs||1e4;this.stopScheduler(),this.schedulerTimer=setInterval(()=>{this.startSync(e).catch(i=>{this.runtimeStateStore.setState(n=>({...n,phase:"error",syncing:!1,lastError:i instanceof Error?i.message:String(i),lastTransitionAt:new Date().toISOString()}))})},r)}stopScheduler(){this.schedulerTimer&&(clearInterval(this.schedulerTimer),this.schedulerTimer=void 0)}async notifyOnline(e){if(this.policy.scheduler.onlineTrigger!==!1){if(this.orchestratorStateStore.getState().running){this.orchestratorScope=e,await this.triggerSync("online");return}await this.startSync(e)}}getOrchestratorState(){return this.orchestratorStateStore.getState()}async startOrchestrator(e){let r=e.scope;this.orchestratorScope=r,this.orchestratorStateStore.setState({running:!0,syncing:!1,wsConnected:!1,lastSyncAt:null,lastTriggerReason:null,lastError:null,lastErrorCode:null}),this.runtime.isStarted()||await this.start(),await this.configureOrchestratorSocket(e.socket),this.configureOrchestratorInterval(e.auto)}async stopOrchestrator(){var e;this.orchestratorTimer&&(clearInterval(this.orchestratorTimer),this.orchestratorTimer=void 0),this.orchestratorSocketHealthTimer&&(clearInterval(this.orchestratorSocketHealthTimer),this.orchestratorSocketHealthTimer=void 0),this.orchestratorSocketUnsubscribe&&(this.orchestratorSocketUnsubscribe(),this.orchestratorSocketUnsubscribe=null),this.orchestratorSocketConnectedByCore&&((e=this.runtime.registry)!=null&&e.socketAdapter)&&await this.runtime.registry.socketAdapter.disconnect(),this.orchestratorSocketConnectedByCore=!1,this.orchestratorSyncPromise=null,this.orchestratorStateStore.setState(r=>({...r,running:!1,syncing:!1,wsConnected:!1,lastTriggerReason:null}))}async triggerSync(e="manual"){if(!this.orchestratorScope)throw new Error("Sync orchestrator scope is not initialized");return this.orchestratorSyncPromise?this.orchestratorSyncPromise:(this.runtime.isStarted()||await this.start(),this.orchestratorStateStore.setState(r=>({...r,syncing:!0,lastTriggerReason:e,lastError:null,lastErrorCode:null})),this.orchestratorSyncPromise=this.startSync(this.orchestratorScope).then(()=>{this.orchestratorStateStore.setState(r=>({...r,syncing:!1,lastSyncAt:new Date().toISOString(),lastError:null,lastErrorCode:null}))}).catch(r=>{var o;let i=r instanceof Error?r.message:String(r),n=String((o=r==null?void 0:r.code)!=null?o:"");throw this.orchestratorStateStore.setState(c=>({...c,syncing:!1,lastError:i,lastErrorCode:n||null})),r}).finally(()=>{this.orchestratorSyncPromise=null}),this.orchestratorSyncPromise)}async configureOrchestratorSocket(e){var c;let r=this.orchestratorSocketConnectedByCore;if(this.orchestratorSocketUnsubscribe&&(this.orchestratorSocketUnsubscribe(),this.orchestratorSocketUnsubscribe=null),!((e==null?void 0:e.enabled)!==!1)){this.orchestratorSocketConnectedByCore=!1;return}let n=(c=this.runtime.registry)==null?void 0:c.socketAdapter;if(!n){this.orchestratorSocketConnectedByCore=!1;return}if(r)try{await n.disconnect()}catch{}this.orchestratorSocketConnectedByCore=!1;let o=e!=null&&e.resolveConnectOptions?await e.resolveConnectOptions():(e==null?void 0:e.connectOptions)||null;if(o){try{await n.connect(o),this.orchestratorSocketConnectedByCore=!0;let u=(e==null?void 0:e.eventName)||"sync:notify";this.orchestratorSocketUnsubscribe=n.subscribe(u,()=>{this.triggerSync("notify").catch(a=>{var h;let s=a instanceof Error?a.message:String(a),d=String((h=a==null?void 0:a.code)!=null?h:"");this.orchestratorStateStore.setState(l=>({...l,lastError:s,lastErrorCode:d||null}))})})}catch{this.orchestratorSocketConnectedByCore=!1}this.orchestratorStateStore.setState(u=>({...u,wsConnected:this.readSocketConnectedState()})),this.startOrchestratorSocketHealthMonitor()}}configureOrchestratorInterval(e){var n;if(this.orchestratorTimer&&(clearInterval(this.orchestratorTimer),this.orchestratorTimer=void 0),!((n=e==null?void 0:e.enabled)!=null?n:this.policy.scheduler.mode==="interval"))return;let i=(e==null?void 0:e.intervalMs)||this.policy.scheduler.intervalMs||1e4;this.orchestratorTimer=setInterval(()=>{this.triggerSync("interval").catch(o=>{var a;let c=o instanceof Error?o.message:String(o),u=String((a=o==null?void 0:o.code)!=null?a:"");this.orchestratorStateStore.setState(s=>({...s,lastError:c,lastErrorCode:u||null}))})},i)}startOrchestratorSocketHealthMonitor(){this.orchestratorSocketHealthTimer&&(clearInterval(this.orchestratorSocketHealthTimer),this.orchestratorSocketHealthTimer=void 0),this.orchestratorSocketHealthTimer=setInterval(()=>{this.orchestratorStateStore.setState(e=>({...e,wsConnected:this.readSocketConnectedState()}))},2e3)}readSocketConnectedState(){var r;let e=(r=this.runtime.registry)==null?void 0:r.socketAdapter;if(e&&typeof e.isConnected=="function")try{return!!e.isConnected()}catch{return!1}return this.orchestratorSocketConnectedByCore}async processOutbox(e){var n;if(!this.transport)return;let r=await this.outboxStore.listPending(this.policy.batchSize);if(r.length===0)return;let i=new Map;for(let o of r){let c=String(o.change.domain||"").trim().toLowerCase()||"__unknown__";i.has(c)||i.set(c,[]),(n=i.get(c))==null||n.push(o)}for(let[o,c]of i.entries()){if(o==="__unknown__"){for(let h of c){let l=h.retryCount+1;await this.outboxStore.update({...h,retryCount:l,status:l>=this.policy.maxRetry?"FAILED":"PENDING",lastError:"DOMAIN_REQUIRED",updatedAt:new Date().toISOString()})}continue}let u=await this.transport.push({scope:e,outbox:c,domain:o}),a=new Set((u.applied||[]).map(h=>h.changeId)),s=new Map((u.failed||[]).map(h=>[h.changeId,h])),d=new Set((u.conflicts||[]).map(h=>h.changeId));await this.resolveConflicts(u.conflicts||[],e,o);for(let h of c){if(a.has(h.change.id)){await this.outboxStore.remove(h.outboxId);continue}let l=s.get(h.change.id);if(l||d.has(h.change.id)){let y=h.retryCount+1;await this.outboxStore.update({...h,retryCount:y,status:y>=this.policy.maxRetry?"FAILED":"PENDING",lastError:(l==null?void 0:l.message)||(d.has(h.change.id)?"SYNC_CONFLICT":h.lastError),updatedAt:new Date().toISOString()})}}}}async collectBridgeLocalChanges(e){let r=this.listDomainBridges();for(let i of r){let n=this.bridges.get(i);if(!(n!=null&&n.collectLocalChanges))continue;let o=await n.collectLocalChanges(e);if(!(!Array.isArray(o)||o.length===0))for(let c of o){let u=String(c.domain||"").trim()||i,a={...c,domain:u,scope:c.scope||e},s=`${a.domain}:${a.id}`;await this.enqueueLocalChange(a,s)}}}async applyRemoteChanges(e,r){if(!Array.isArray(e)||e.length===0)return;let i=[...e].sort((o,c)=>o.occurredAt.localeCompare(c.occurredAt)),n=new Map;for(let o of i){n.has(o.domain)||n.set(o.domain,[]);let c=n.get(o.domain);c&&c.push(o)}for(let[o,c]of n.entries()){let u=this.bridges.get(o);if(u!=null&&u.applyRemoteChanges&&(await u.applyRemoteChanges(c,r),this.projectionHook))for(let a of c)await this.projectionHook(a,r)}}async resolveConflicts(e,r,i){var o;if(!((o=this.transport)!=null&&o.resolveConflicts)||e.length===0)return;let n=[];for(let c of e){let u=[...this.conflictResolvers],a=null;for(let s of u){let d=await s(c,{scope:r});if(d){a=d;break}}a||(a=j(c)),n.push(a)}n.length>0&&await this.transport.resolveConflicts({scope:r,domain:i,resolutions:n})}},L=class{constructor(){this.runtimeBuilder=Z.builder();this.options={};this.runtimeBuilder.withDbAdapter(()=>new tt)}withPlatformAdapter(e){return this.runtimeBuilder.withPlatformAdapter(e),this}withDbAdapter(e){return this.runtimeBuilder.withDbAdapter(e),this}withHttpAdapter(e){return this.runtimeBuilder.withHttpAdapter(e),this}withSocketAdapter(e){return this.runtimeBuilder.withSocketAdapter(e),this}withLoggerAdapter(e){return this.runtimeBuilder.withLoggerAdapter(e),this}withActivitySink(e){return this.runtimeBuilder.withActivitySink(e),this}withExtension(e,r){return this.runtimeBuilder.withExtension(e,r),this}withPolicy(e){return this.options={...this.options,policy:e},this}withTransport(e){return this.options={...this.options,transport:e},this}withOutboxStore(e){return this.options={...this.options,outboxStore:e},this}withCheckpointStore(e){return this.options={...this.options,checkpointStore:e},this}withConflictResolvers(e){return this.options={...this.options,conflictResolvers:e},this}withProjectionHook(e){return this.options={...this.options,projectionHook:e},this}build(){let e=Y({phase:"idle",started:!1,syncing:!1,startCount:0,stopCount:0,syncCount:0,lastError:null,lastTransitionAt:new Date().toISOString()}),r=Y({running:!1,syncing:!1,wsConnected:!1,lastSyncAt:null,lastTriggerReason:null,lastError:null,lastErrorCode:null}),i=this.runtimeBuilder.build();return new E(i,e,r,this.options)}};function It(t={}){let e=E.builder();return t.policy&&e.withPolicy(t.policy),t.transport&&e.withTransport(t.transport),t.outboxStore&&e.withOutboxStore(t.outboxStore),t.checkpointStore&&e.withCheckpointStore(t.checkpointStore),t.conflictResolvers&&e.withConflictResolvers(t.conflictResolvers),t.projectionHook&&e.withProjectionHook(t.projectionHook),t.socketAdapterFactory&&e.withSocketAdapter(t.socketAdapterFactory),e.build()}function At(t,e){t.registerDomainBridge(e)}async function Et(t,e,r={}){t.isStarted()||await t.start(r),await t.startSync(e)}async function Pt(t){await t.stopSync()}function k(t){return{store:t.orchestratorStore,getState:()=>t.getOrchestratorState(),start:async e=>t.startOrchestrator(e),stop:async()=>t.stopOrchestrator(),trigger:async(e="manual")=>t.triggerSync(e)}}var P=class extends Error{constructor(e,r,i=!1,n,o){super(r),this.name="OfsyncTransportError",this.code=e,this.retryable=i,this.status=n,this.details=o}};async function it(t,e){if(e)return e(t);let r=globalThis.crypto;if(!r||!r.subtle)throw new Error("WebCrypto API is not available");let i=new TextEncoder().encode(t),n=await r.subtle.digest("SHA-256",i);return[...new Uint8Array(n)].map(o=>o.toString(16).padStart(2,"0")).join("")}function st(t){let e=String(t.table||""),r=String(t.id||""),i=String(t.changeId||"");return[e,r,i].join("::")}function q(t){return{conflictId:st(t),changeId:String(t.changeId||""),domain:"unknown",entity:String(t.table||""),recordId:String(t.id||""),code:String(t.code||"UNKNOWN_CONFLICT"),message:String(t.message||"Conflict detected"),retryable:!!t.retryable,scope:{tenantId:typeof t.tenantId=="string"?t.tenantId:void 0,branchId:typeof t.branchId=="string"?t.branchId:void 0,ledgerProfileId:typeof t.ledgerProfileId=="string"?t.ledgerProfileId:typeof t.financeDomainId=="string"?t.financeDomainId:void 0,sourceDomainId:typeof t.sourceDomain=="string"?t.sourceDomain:typeof t.financeDomainId=="string"?t.financeDomainId:void 0},details:t}}function D(t){return!!(t&&typeof t=="object"&&!Array.isArray(t))}function ct(t){var r;let e=(r=t.branchId)==null?void 0:r.trim();if(!e)throw new P("BRANCH_SCOPE_REQUIRED","Sync scope requires branchId",!1);return e}function W(t){return typeof t=="string"?t.trim():""}function at(t,e){let r=W(t.productFamilyId);if(r)return r;if(!Array.isArray(e)||e.length===0)return"";let i=Array.from(new Set(e.map(n=>{var o;return W((o=n.change.scope)==null?void 0:o.productFamilyId)}).filter(Boolean)));return i.length===1?i[0]:""}var G=class{constructor(e){this.options=e}nowIso(){return this.options.nowIso?this.options.nowIso():new Date().toISOString()}defaultBootstrapSinceIso(){return new Date(Date.now()-2160*60*60*1e3).toISOString()}async buildHeaders(e,r,i){let n={"content-type":"application/json","x-branch-id":ct(e),"x-sync-protocol-version":"1"},o=this.normalizeDomainPath(i||"");o&&(n["x-domain-id"]=o),e.tenantId&&(n["x-tenant-id"]=e.tenantId),e.deviceId&&(n["x-device-id"]=e.deviceId),e.ledgerProfileId&&(n["x-ledger-profile-id"]=e.ledgerProfileId),e.sourceDomainId&&(n["x-finance-source-domain-id"]=e.sourceDomainId);let c=at(e,r);if(c&&(n["x-product-family-id"]=c),this.options.getAccessToken){let u=await this.options.getAccessToken();u&&(n.authorization=`Bearer ${u}`)}return n}normalizeUrl(e){return`${this.options.baseUrl.replace(/\/+$/,"")}${e}`}normalizeDomainPath(e){let r=e.trim().toLowerCase().replace(/[^a-z0-9_-]/g,"");return r||null}resolveDomainPathOrThrow(e){let r=this.normalizeDomainPath(e||"");if(!r)throw new P("DOMAIN_REQUIRED","Sync transport requires explicit domain",!1);return r}parseFailure(e,r){var a,s,d;let i=r||{},n=((a=i.error)==null?void 0:a.code)||"SYNC_TRANSPORT_ERROR",o=((s=i.error)==null?void 0:s.message)||`Sync transport failed with status ${e}`,c=!!((d=i.error)!=null&&d.retryable),u=D(i.error)&&D(i.error.details)?i.error.details:void 0;throw new P(n,o,c,e,u)}async push(e){let r={changes:e.outbox.map(s=>{let d=D(s.change.payload)?s.change.payload:{},h=typeof d.table=="string"?d.table.trim():"",l=D(d.record)?d.record:null,y=Date.parse(s.change.occurredAt);return h&&l?{id:s.change.id,table:h,type:s.change.type,record:{...l,id:s.change.recordId||String(l.id||s.change.id)},updated_at:Number.isFinite(y)?y:s.change.occurredAt}:{id:s.change.id,entity:s.change.entity,type:s.change.type,data:{id:s.change.recordId,...d},timestamp:s.change.occurredAt}})},i=JSON.stringify(r),n=await it(i,this.options.hashText),o=this.resolveDomainPathOrThrow(e.domain),c=await this.buildHeaders(e.scope,e.outbox,o);c["x-data-checksum"]=n;let u=await this.options.httpAdapter.request({method:"POST",headers:c,body:r,url:this.normalizeUrl(`/sync/${o}/push`)});u.status>=400&&this.parseFailure(u.status,u.data);let a=u.data||{};return{applied:Array.isArray(a.applied)?a.applied:[],failed:Array.isArray(a.failed)?a.failed:[],conflicts:Array.isArray(a.conflicts)?a.conflicts.map(q):[]}}async pull(e){let r=e.cursor||this.defaultBootstrapSinceIso(),i=this.resolveDomainPathOrThrow(e.domain),n=await this.buildHeaders(e.scope,void 0,i),o=await this.options.httpAdapter.request({method:"GET",headers:n,query:{since:r},url:this.normalizeUrl(`/sync/${i}/pull`)});o.status>=400&&this.parseFailure(o.status,o.data);let c=o.data||{},u=Array.isArray(c.changes)?c.changes.map(s=>{var l,y,f,g;let d=typeof s.table=="string"?s.table:"",h=typeof s.domain=="string"&&s.domain.trim().length>0?s.domain:d.startsWith("auth_")?"ofauth":d.startsWith("offinance_")||d.startsWith("ofinance_")?"offinance":d.startsWith("ofcoop_")?"ofcoop":d.startsWith("ofpos_")?"ofpos":"unknown";return{id:String(s.changeId||s.id||""),domain:String(h),entity:String(s.entity||d||""),type:String(s.type||s.action||"UPDATE"),recordId:String(s.recordId||((l=s.record)==null?void 0:l.id)||((y=s.data)==null?void 0:y.id)||s.id||""),payload:typeof d=="string"&&d.length>0?{table:String(d),action:String(s.action||s.type||"UPDATE"),record:{...s.record||s.data||{},id:String(s.recordId||((f=s.record)==null?void 0:f.id)||((g=s.data)==null?void 0:g.id)||s.id||"")}}:s.record||s.data||{},occurredAt:String(s.timestamp||this.nowIso()),scope:e.scope}}):[],a=u.length>0?u[u.length-1].occurredAt:this.nowIso();return{changes:u,nextCursor:a,conflicts:[]}}async listConflicts(e){let r=this.normalizeDomainPath(e.domain||""),i=await this.buildHeaders(e.scope,void 0,r||void 0),n=r?this.normalizeUrl(`/sync/${r}/conflicts`):this.normalizeUrl("/sync/conflicts"),o=await this.options.httpAdapter.request({method:"GET",url:n,headers:i});o.status>=400&&this.parseFailure(o.status,o.data);let c=o.data||{};return Array.isArray(c.conflicts)?c.conflicts.map(q):[]}async resolveConflicts(e){var h;let r=await this.listConflicts({scope:e.scope,domain:e.domain}),i=new Map(r.map(l=>[l.conflictId,l])),n=e.resolutions.map(l=>i.get(l.conflictId)).filter(l=>!!l).map(l=>({table:l.entity,id:l.recordId})),o={resolutions:n},c=this.normalizeDomainPath(e.domain||""),u=await this.buildHeaders(e.scope,void 0,c||void 0),a=c?this.normalizeUrl(`/sync/${c}/conflicts/resolve`):this.normalizeUrl("/sync/conflicts/resolve"),s=await this.options.httpAdapter.request({method:"POST",url:a,headers:u,body:o});s.status>=400&&this.parseFailure(s.status,s.data);let d=Array.isArray((h=s.data)==null?void 0:h.remaining)?s.data.remaining.length:0;return{resolvedCount:Math.max(0,n.length-d)}}};function Q(t){return`Tidak dapat terhubung ke server sinkronisasi (${t.trim()||"VITE_OFSYNC_BASE_URL"}). Pastikan ofsync-server berjalan dan CORS origin browser diizinkan.`}function V(t){return t.trim().replace(/\/+$/,"")}function X(t){return t instanceof Error?t.message:String(t!=null?t:"")}function xt(t){let e=X(t).toLowerCase();return e.includes("server sinkronisasi")||e.includes("cors origin browser diizinkan")||e.includes("tidak dapat terhubung ke server sinkronisasi")}function lt(t,e=9e5){if(typeof t=="number"&&Number.isFinite(t)&&t>0)return Math.floor(t*1e3);if(typeof t!="string")return e;let r=t.trim();if(!r)return e;if(/^\d+$/.test(r))return Math.max(1,Number(r))*1e3;let i=r.match(/^(\d+)\s*([smhd])$/i);if(!i)return e;let n=Number(i[1]),o=i[2].toLowerCase(),c=o==="s"?1e3:o==="m"?6e4:o==="h"?36e5:864e5;return Math.max(1,n)*c}function R(t,e,r=Q){let i=X(t),n=i.toLowerCase();return n.includes("networkerror")||n.includes("failed to fetch")||n.includes("load failed")||n.includes("fetch failed")||n.includes("the network connection was lost")||n.includes("network request failed")?r(e):i}function w(t,e){let r=new Error(e);return r.code=t,r}function _t(t){var u,a;let e=t.accessToken.trim(),r=t.branchId.trim();if(!e||!r)return null;let i=V(t.baseUrl).replace(/^http:\/\//i,"ws://").replace(/^https:\/\//i,"wss://"),n={token:e,branchId:r},o=String((u=t.tenantId)!=null?u:"").trim(),c=String((a=t.domainId)!=null?a:"").trim();return o&&(n.tenantId=o),c&&(n.domainId=c),{url:`${i}/ws/sync-notify`,query:n}}async function Dt(t){var n,o,c,u;if(t.bearerToken.length>0)return t.bearerToken;let e=(n=t.nowMs)!=null?n:Date.now(),r=(o=t.refreshLeewayMs)!=null?o:1e4;if((c=t.session)!=null&&c.accessToken){if(t.session.expiresAtMs>e+r)return t.session.accessToken;let a=await t.refresh();if(a!=null&&a.accessToken)return a.accessToken}let i=await t.loginFromCredentials();return(u=i==null?void 0:i.accessToken)!=null?u:""}function Lt(t){var u,a,s;let e=(u=t.fetchImpl)!=null?u:fetch,r=V(t.baseUrl),i=(a=t.accessTokenFallbackTtlMs)!=null?a:9e5,n=(s=t.networkErrorMessage)!=null?s:Q,o=d=>(Array.isArray(d.scopes)?d.scopes:[]).filter(l=>!!(l&&typeof l=="object")).map(l=>{var y,f,g,S;return{tenantId:typeof l.tenantId=="string"&&l.tenantId.trim().length>0?l.tenantId:null,branchId:String((y=l.branchId)!=null?y:"").trim(),role:String((f=l.role)!=null?f:""),userId:String((g=l.userId)!=null?g:""),email:String((S=l.email)!=null?S:"")}}).filter(l=>l.branchId.length>0),c=(d,h)=>{var f,g,S;let l=String((g=(f=d.accessToken)!=null?f:d.token)!=null?g:""),y=String((S=d.refreshToken)!=null?S:"");if(!l||!y)throw w("SYNC_AUTH_TOKEN_INCOMPLETE","Sync auth gagal: token tidak lengkap.");return{accessToken:l,refreshToken:y,expiresAtMs:Date.now()+lt(d.expiresIn,i),principal:h}};return{async fetchScopeOptions(d,h){var l,y,f;if(!r)return[];try{let g=await e(`${r}/api/v1/auth/scope-options`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:d,password:h})}),S=await g.json().catch(()=>({}));if(!g.ok){let m=(l=S.error)!=null?l:{};throw w(String((y=m.code)!=null?y:`HTTP_${g.status}`),String((f=m.message)!=null?f:`Sync scope discovery gagal (${g.status})`))}return o(S)}catch(g){let S=g;throw w(typeof(S==null?void 0:S.code)=="string"?S.code:"SYNC_AUTH_SCOPE_DISCOVERY_FAILED",R(g,r,n))}},async login(d,h,l){var g,S,m,O,I;let y=String((g=l.branchId)!=null?g:"").trim(),f=String((S=l.tenantId)!=null?S:"").trim();try{let b=await e(`${r}/api/v1/auth/login`,{method:"POST",headers:{"Content-Type":"application/json","X-Branch-ID":y,...f?{"X-Tenant-ID":f}:{},"X-Device-ID":t.deviceId},body:JSON.stringify({username:d,password:h})}),C=await b.json().catch(()=>({}));if(!b.ok){let A=(m=C.error)!=null?m:{};throw w(String((O=A.code)!=null?O:`HTTP_${b.status}`),String((I=A.message)!=null?I:`Sync auth login gagal (${b.status})`))}return c(C,d)}catch(b){let C=b;throw w(typeof(C==null?void 0:C.code)=="string"?C.code:"SYNC_AUTH_FAILED",R(b,r,n))}},async refresh(d,h){var l,y,f;try{let g=await e(`${r}/api/v1/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json",...h?{"X-Tenant-ID":h}:{},"X-Device-ID":t.deviceId},body:JSON.stringify({refreshToken:d})}),S=await g.json().catch(()=>({}));if(!g.ok){let m=(l=S.error)!=null?l:{};throw w(String((y=m.code)!=null?y:`HTTP_${g.status}`),String((f=m.message)!=null?f:`Sync auth refresh gagal (${g.status})`))}return c(S,"")}catch(g){let S=g;throw w(typeof(S==null?void 0:S.code)=="string"?S.code:"SYNC_AUTH_REFRESH_FAILED",R(g,r,n))}}}}function Ut(){let t=new Set;return{track(e){let r=Promise.resolve(e).finally(()=>{t.delete(r)});return t.add(r),r},async waitForIdle(){for(;t.size>0;)await Promise.allSettled([...t])},size(){return t.size},clear(){t.clear()}}}async function Ft(t){if(!t)return 0;let e=await t.getOutboxState();return Number(e.pending||0)+Number(e.failed||0)}async function Ht(t){var o,c,u,a;let e=(o=t.triggerInitialPull)!=null?o:(async s=>{await k(s).trigger("manual")});if(!t.runtimeReady||!t.serverSyncEnabled||!t.isAuthenticated){if(t.lastStartSignatureRef.current="",(c=t.setWsConnected)==null||c.call(t,!1),(u=t.setSyncing)==null||u.call(t,!1),!t.runtime)return;await k(t.runtime).stop().catch(()=>{});return}let r=t.runtime;if(r){let s=k(r).getState();if(s.running&&t.lastStartSignatureRef.current===t.startSignature)return;s.running&&t.lastStartSignatureRef.current!==t.startSignature&&await k(r).stop().catch(()=>{})}let i=await t.ensureOrchestratorStarted();k(i).getState().running&&t.lastStartSignatureRef.current===t.startSignature||(await e(i),t.lastStartSignatureRef.current=t.startSignature,(a=t.setLastError)==null||a.call(t,""))}async function $t(t){var i,n,o,c,u,a,s,d,h,l,y,f,g,S,m,O,I,b,C,A,B,N,M,U,F,H,$,z;let e=(i=t.triggerManualSync)!=null?i:(async p=>{await k(p).trigger("manual")});if((n=t.setStage)==null||n.call(t,"check-syncing"),t.runtime.getOrchestratorState().syncing)return{ok:!0,message:(o=t.inProgressMessage)!=null?o:"Sinkronisasi sedang berjalan."};if((c=t.setStage)==null||c.call(t,"check-base-url"),t.serverSyncBaseUrl.length===0){let p=(u=t.missingBaseUrlMessage)!=null?u:"VITE_OFSYNC_BASE_URL belum diatur.";return t.silent||t.setLastError(p),{ok:!1,message:p}}if((a=t.setStage)==null||a.call(t,"resolve-access-token"),!(await t.resolveAccessToken()).trim()){let p=(s=t.missingAccessTokenMessage)!=null?s:"Sesi sinkronisasi tidak valid. Login ulang diperlukan.";return t.silent||t.setLastError(p),t.setAuthBlocked(!0),{ok:!1,message:p}}t.setLastError("");try{(d=t.setStage)==null||d.call(t,"ensure-orchestrator-started"),await t.ensureOrchestratorStarted(),(h=t.setStage)==null||h.call(t,"before-trigger"),await((l=t.beforeTrigger)==null?void 0:l.call(t)),(y=t.setStage)==null||y.call(t,"wait-pending-operations"),await((f=t.waitForPendingOperations)==null?void 0:f.call(t)),(g=t.setStage)==null||g.call(t,"refresh-queue-length:before-trigger"),await((S=t.refreshQueueLength)==null?void 0:S.call(t));try{(m=t.setStage)==null||m.call(t,"trigger-manual"),await e(t.runtime)}catch(p){if((p instanceof Error?p.message:String(p))!=="Cannot start sync before runtime is started")throw p;(O=t.setStage)==null||O.call(t,"ensure-orchestrator-started:retry"),await t.ensureOrchestratorStarted(),(I=t.setStage)==null||I.call(t,"before-trigger:retry"),await((b=t.beforeTrigger)==null?void 0:b.call(t)),(C=t.setStage)==null||C.call(t,"trigger-manual:retry"),await e(t.runtime)}return(A=t.setStage)==null||A.call(t,"refresh-queue-length:after-trigger"),await((B=t.refreshQueueLength)==null?void 0:B.call(t)),(N=t.setStage)==null||N.call(t,"refresh-projection-ui"),await((M=t.refreshProjectionUi)==null?void 0:M.call(t)),(U=t.setStage)==null||U.call(t,"on-success"),await((F=t.onSuccess)==null?void 0:F.call(t)),t.setAuthBlocked(!1),(H=t.setStage)==null||H.call(t,"completed"),{ok:!0,message:"Sinkronisasi selesai."}}catch(p){($=t.onRawError)==null||$.call(t,p instanceof Error?p.message:String(p));let v=R(p,t.serverSyncBaseUrl),J=v.toLowerCase().includes("unauthorized")||v.toLowerCase().includes("sesi sinkronisasi tidak valid")||v.includes("401");return t.silent&&t.isConnectivityError(v)?{ok:!1,message:v}:(t.setLastError(v),t.setAuthBlocked(J),(z=t.setRuntimeError)==null||z.call(t,v),{ok:!1,message:v})}}function zt(t){let e=t.store.getState().syncing;return t.store.subscribe(r=>{var c,u,a,s,d,h,l,y,f,g,S,m;let i=e;if(e=r.syncing,(c=t.setSyncing)==null||c.call(t,r.syncing),(u=t.setWsConnected)==null||u.call(t,r.wsConnected),!r.lastError){if(i&&!r.syncing){let O=(s=(a=t.getPullAppliedCount)==null?void 0:a.call(t))!=null?s:0;(d=t.resetPullAppliedCount)==null||d.call(t),(h=t.onSuccessfulIdle)==null||h.call(t,{appliedCount:O,state:r})}t.setLastError(""),t.setAuthBlocked(!1),(l=t.clearConnectivityError)==null||l.call(t);return}let n=((y=r.lastErrorCode)==null?void 0:y.startsWith("AUTH_"))||r.lastError.toLowerCase().includes("unauthorized")||r.lastError.includes("401");((g=(f=t.shouldSuppressConnectivityError)==null?void 0:f.call(t,r))!=null?g:r.lastTriggerReason!=="manual"&&t.isConnectivityError(r.lastError))||(r.syncing||(S=t.resetPullAppliedCount)==null||S.call(t),t.setLastError(r.lastError),t.setAuthBlocked(!!n),(m=t.onError)==null||m.call(t,{message:r.lastError,isAuthError:!!n,state:r}))})}export{T as DEFAULT_SYNC_POLICY,_ as InMemoryCheckpointStore,x as InMemoryOutboxStore,St as OFSYNC_CONTRACT_STAGE,E as OfsyncCore,L as OfsyncCoreBuilder,G as OfsyncHttpTransport,P as OfsyncTransportError,_t as buildSyncNotifySocketConnectOptions,Lt as createOfsyncAuthClient,Ut as createPendingSyncOperationTracker,w as createSyncAuthError,It as createSyncRuntime,j as defaultConflictResolver,Ht as ensureHostSyncOrchestratorLifecycle,k as getSyncOrchestrator,xt as isSyncConnectivityRuntimeError,R as normalizeSyncNetworkError,lt as parseSyncDurationMs,Ft as readSyncOutboxQueueLength,At as registerDomainBridge,Dt as resolveSyncAccessToken,$t as runManualSyncCycle,Et as startSync,Pt as stopSync,zt as subscribeToSyncOrchestratorLifecycle};
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var N=Object.defineProperty;var rt=Object.getOwnPropertyDescriptor;var nt=Object.getOwnPropertyNames;var ot=Object.prototype.hasOwnProperty;var it=(t,e)=>{for(var r in e)N(t,r,{get:e[r],enumerable:!0})},st=(t,e,r,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of nt(e))!ot.call(t,n)&&n!==r&&N(t,n,{get:()=>e[n],enumerable:!(o=rt(e,n))||o.enumerable});return t};var ct=t=>st(N({},"__esModule",{value:!0}),t);var Tt={};it(Tt,{DEFAULT_SYNC_POLICY:()=>x,InMemoryCheckpointStore:()=>D,InMemoryOutboxStore:()=>_,OFSYNC_CONTRACT_STAGE:()=>at,OfsyncCore:()=>A,OfsyncCoreBuilder:()=>L,OfsyncHttpTransport:()=>U,OfsyncTransportError:()=>E,buildSyncNotifySocketConnectOptions:()=>Ot,createOfsyncAuthClient:()=>wt,createPendingSyncOperationTracker:()=>It,createSyncAuthError:()=>O,createSyncRuntime:()=>gt,defaultConflictResolver:()=>M,ensureHostSyncOrchestratorLifecycle:()=>Et,getSyncOrchestrator:()=>w,isSyncConnectivityRuntimeError:()=>vt,normalizeSyncNetworkError:()=>P,parseSyncDurationMs:()=>tt,readSyncOutboxQueueLength:()=>At,registerDomainBridge:()=>St,resolveSyncAccessToken:()=>kt,runManualSyncCycle:()=>Pt,startSync:()=>yt,stopSync:()=>ft,subscribeToSyncOrchestratorLifecycle:()=>Rt});module.exports=ct(Tt);var x={batchSize:200,maxRetry:5,scheduler:{mode:"manual",onlineTrigger:!0},retryBackoff:{strategy:"exponential-jitter",baseDelayMs:500,maxDelayMs:3e4}};var _=class{constructor(){this.rows=new Map}async enqueue(e){this.rows.set(e.outboxId,e)}async listPending(e){return[...this.rows.values()].filter(r=>r.status==="PENDING"||r.status==="FAILED").sort((r,o)=>r.createdAt.localeCompare(o.createdAt)).slice(0,Math.max(0,e))}async findByIdempotencyKey(e){for(let r of this.rows.values())if(r.idempotencyKey===e)return r;return null}async listAll(){return[...this.rows.values()].sort((e,r)=>e.createdAt.localeCompare(r.createdAt))}async update(e){this.rows.set(e.outboxId,e)}async remove(e){this.rows.delete(e)}};var D=class{constructor(){this.rows=new Map}async get(e){return this.rows.has(e)?this.rows.get(e):null}async set(e,r){this.rows.set(e,r)}};function M(t){return t.code==="VERSION_CONFLICT"?{conflictId:t.conflictId,action:"APPLY_REMOTE"}:t.code==="SCOPE_CONFLICT"?{conflictId:t.conflictId,action:"MANUAL_MERGE"}:{conflictId:t.conflictId,action:"KEEP_LOCAL"}}var at="phase1-runtime-skeleton";var W=require("ofcore"),G=require("ofcore"),I=require("ofcore");function lt(t={}){return{...x,...t,scheduler:{...x.scheduler,...t.scheduler||{}},retryBackoff:{...x.retryBackoff,...t.retryBackoff||{}}}}function ut(t){return`${t.tenantId||"__standalone__"}::${t.branchId||"__all__"}::${t.ledgerProfileId||"__default_ledger__"}::${t.sourceDomainId||"__default_source__"}::${t.deviceId||"__device__"}`}function dt(t,e){return`${ut(t)}::${e}`}function ht(t){if(typeof t!="string"||t.trim().length===0)return null;let e=Date.parse(t);return Number.isFinite(e)?new Date(e+5e3).toISOString():null}var A=class{constructor(e,r,o,n={}){this.runtime=e;this.runtimeStateStore=r;this.orchestratorStateStore=o;this.bridges=new Map;this.orchestratorScope=null;this.orchestratorSocketUnsubscribe=null;this.orchestratorSocketConnectedByCore=!1;this.orchestratorSyncPromise=null;this.runtimeStore=(0,I.asReadonlyStore)(this.runtimeStateStore),this.orchestratorStore=(0,I.asReadonlyStore)(this.orchestratorStateStore),this.policy=lt(n.policy),this.transport=n.transport,this.outboxStore=n.outboxStore||new _,this.checkpointStore=n.checkpointStore||new D,this.conflictResolvers=n.conflictResolvers||[],this.projectionHook=n.projectionHook}static builder(){return new L}get registry(){return this.runtime.registry}getPolicy(){return this.policy}registerDomainBridge(e){let r=e.domain.trim();if(!r)throw new Error("DomainBridge.domain is required");if(this.bridges.has(r))throw new Error(`DomainBridge for domain "${r}" is already registered`);this.bridges.set(r,e)}listDomainBridges(){return[...this.bridges.keys()].sort()}async getOutboxState(){let e=await this.outboxStore.listAll(),r=e.filter(n=>n.status==="PENDING").length,o=e.filter(n=>n.status==="FAILED").length;return{pending:r,failed:o,total:e.length}}async enqueueLocalChange(e,r){let o=(r||`${e.domain}:${e.entity}:${e.recordId}:${e.type}:${e.id}`).trim();if(!o)throw new Error("idempotencyKey must not be empty");let n=await this.outboxStore.findByIdempotencyKey(o);if(n)return n;let i=new Date().toISOString(),c={outboxId:`outbox_${Date.now()}_${Math.random().toString(36).slice(2)}`,idempotencyKey:o,change:e,retryCount:0,status:"PENDING",createdAt:i,updatedAt:i};return await this.outboxStore.enqueue(c),c}async start(e={}){this.runtimeStateStore.setState({phase:"starting",started:!1,syncing:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.start(e),this.runtimeStateStore.setState(r=>({...r,phase:"started",started:!0,syncing:!1,startCount:r.startCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(r){throw this.runtimeStateStore.setState({phase:"error",started:!1,syncing:!1,lastError:r instanceof Error?r.message:String(r),lastTransitionAt:new Date().toISOString()}),r}}async stop(){await this.stopOrchestrator(),this.stopScheduler(),this.runtimeStateStore.setState({phase:"stopping",started:this.runtime.isStarted(),syncing:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.stop(),this.runtimeStateStore.setState(e=>({...e,phase:"stopped",started:!1,syncing:!1,stopCount:e.stopCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(e){throw this.runtimeStateStore.setState({phase:"error",started:this.runtime.isStarted(),syncing:!1,lastError:e instanceof Error?e.message:String(e),lastTransitionAt:new Date().toISOString()}),e}}async startSync(e){if(!this.runtime.isStarted())throw new Error("Cannot start sync before runtime is started");this.runtimeStateStore.setState({phase:"syncing",started:!0,syncing:!0,lastError:null,lastTransitionAt:new Date().toISOString()});try{if(this.transport){await this.collectBridgeLocalChanges(e),await this.processOutbox(e);let r=this.listDomainBridges();for(let o of r){let n=dt(e,o),i=await this.checkpointStore.get(n),c;try{c=await this.transport.pull({scope:e,cursor:i,limit:this.policy.batchSize,domain:o})}catch(a){let s=(a==null?void 0:a.code)||"",d=a==null?void 0:a.details,h=ht(d==null?void 0:d.cutoffTime);if(s!=="SYNC_RESYNC_REQUIRED"||!h)throw a;await this.checkpointStore.set(n,h),i=h,c=await this.transport.pull({scope:e,cursor:i,limit:this.policy.batchSize,domain:o})}let u=(c.changes||[]).map(a=>{let s=String(a.domain||"").trim().toLowerCase();if(!s)return{...a,domain:o};if(s!==o)throw new Error(`Sync pull domain mismatch: expected ${o}, received ${s}`);return a});await this.applyRemoteChanges(u,e),c.nextCursor!==void 0&&await this.checkpointStore.set(n,c.nextCursor)}}this.runtimeStateStore.setState(r=>({...r,phase:"started",started:!0,syncing:!1,syncCount:r.syncCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(r){throw this.runtimeStateStore.setState({phase:"error",started:this.runtime.isStarted(),syncing:!1,lastError:r instanceof Error?r.message:String(r),lastTransitionAt:new Date().toISOString()}),r}}async stopSync(){this.runtimeStateStore.setState(e=>({...e,phase:this.runtime.isStarted()?"started":"stopped",syncing:!1,lastTransitionAt:new Date().toISOString()}))}isStarted(){return this.runtime.isStarted()}startScheduler(e){if(this.policy.scheduler.mode!=="interval")return;let r=this.policy.scheduler.intervalMs||1e4;this.stopScheduler(),this.schedulerTimer=setInterval(()=>{this.startSync(e).catch(o=>{this.runtimeStateStore.setState(n=>({...n,phase:"error",syncing:!1,lastError:o instanceof Error?o.message:String(o),lastTransitionAt:new Date().toISOString()}))})},r)}stopScheduler(){this.schedulerTimer&&(clearInterval(this.schedulerTimer),this.schedulerTimer=void 0)}async notifyOnline(e){if(this.policy.scheduler.onlineTrigger!==!1){if(this.orchestratorStateStore.getState().running){this.orchestratorScope=e,await this.triggerSync("online");return}await this.startSync(e)}}getOrchestratorState(){return this.orchestratorStateStore.getState()}async startOrchestrator(e){let r=e.scope;this.orchestratorScope=r,this.orchestratorStateStore.setState({running:!0,syncing:!1,wsConnected:!1,lastSyncAt:null,lastTriggerReason:null,lastError:null,lastErrorCode:null}),this.runtime.isStarted()||await this.start(),await this.configureOrchestratorSocket(e.socket),this.configureOrchestratorInterval(e.auto)}async stopOrchestrator(){var e;this.orchestratorTimer&&(clearInterval(this.orchestratorTimer),this.orchestratorTimer=void 0),this.orchestratorSocketHealthTimer&&(clearInterval(this.orchestratorSocketHealthTimer),this.orchestratorSocketHealthTimer=void 0),this.orchestratorSocketUnsubscribe&&(this.orchestratorSocketUnsubscribe(),this.orchestratorSocketUnsubscribe=null),this.orchestratorSocketConnectedByCore&&((e=this.runtime.registry)!=null&&e.socketAdapter)&&await this.runtime.registry.socketAdapter.disconnect(),this.orchestratorSocketConnectedByCore=!1,this.orchestratorSyncPromise=null,this.orchestratorStateStore.setState(r=>({...r,running:!1,syncing:!1,wsConnected:!1,lastTriggerReason:null}))}async triggerSync(e="manual"){if(!this.orchestratorScope)throw new Error("Sync orchestrator scope is not initialized");return this.orchestratorSyncPromise?this.orchestratorSyncPromise:(this.runtime.isStarted()||await this.start(),this.orchestratorStateStore.setState(r=>({...r,syncing:!0,lastTriggerReason:e,lastError:null,lastErrorCode:null})),this.orchestratorSyncPromise=this.startSync(this.orchestratorScope).then(()=>{this.orchestratorStateStore.setState(r=>({...r,syncing:!1,lastSyncAt:new Date().toISOString(),lastError:null,lastErrorCode:null}))}).catch(r=>{var i;let o=r instanceof Error?r.message:String(r),n=String((i=r==null?void 0:r.code)!=null?i:"");throw this.orchestratorStateStore.setState(c=>({...c,syncing:!1,lastError:o,lastErrorCode:n||null})),r}).finally(()=>{this.orchestratorSyncPromise=null}),this.orchestratorSyncPromise)}async configureOrchestratorSocket(e){var c;let r=this.orchestratorSocketConnectedByCore;if(this.orchestratorSocketUnsubscribe&&(this.orchestratorSocketUnsubscribe(),this.orchestratorSocketUnsubscribe=null),!((e==null?void 0:e.enabled)!==!1)){this.orchestratorSocketConnectedByCore=!1;return}let n=(c=this.runtime.registry)==null?void 0:c.socketAdapter;if(!n){this.orchestratorSocketConnectedByCore=!1;return}if(r)try{await n.disconnect()}catch{}this.orchestratorSocketConnectedByCore=!1;let i=e!=null&&e.resolveConnectOptions?await e.resolveConnectOptions():(e==null?void 0:e.connectOptions)||null;if(i){try{await n.connect(i),this.orchestratorSocketConnectedByCore=!0;let u=(e==null?void 0:e.eventName)||"sync:notify";this.orchestratorSocketUnsubscribe=n.subscribe(u,()=>{this.triggerSync("notify").catch(a=>{var h;let s=a instanceof Error?a.message:String(a),d=String((h=a==null?void 0:a.code)!=null?h:"");this.orchestratorStateStore.setState(l=>({...l,lastError:s,lastErrorCode:d||null}))})})}catch{this.orchestratorSocketConnectedByCore=!1}this.orchestratorStateStore.setState(u=>({...u,wsConnected:this.readSocketConnectedState()})),this.startOrchestratorSocketHealthMonitor()}}configureOrchestratorInterval(e){var n;if(this.orchestratorTimer&&(clearInterval(this.orchestratorTimer),this.orchestratorTimer=void 0),!((n=e==null?void 0:e.enabled)!=null?n:this.policy.scheduler.mode==="interval"))return;let o=(e==null?void 0:e.intervalMs)||this.policy.scheduler.intervalMs||1e4;this.orchestratorTimer=setInterval(()=>{this.triggerSync("interval").catch(i=>{var a;let c=i instanceof Error?i.message:String(i),u=String((a=i==null?void 0:i.code)!=null?a:"");this.orchestratorStateStore.setState(s=>({...s,lastError:c,lastErrorCode:u||null}))})},o)}startOrchestratorSocketHealthMonitor(){this.orchestratorSocketHealthTimer&&(clearInterval(this.orchestratorSocketHealthTimer),this.orchestratorSocketHealthTimer=void 0),this.orchestratorSocketHealthTimer=setInterval(()=>{this.orchestratorStateStore.setState(e=>({...e,wsConnected:this.readSocketConnectedState()}))},2e3)}readSocketConnectedState(){var r;let e=(r=this.runtime.registry)==null?void 0:r.socketAdapter;if(e&&typeof e.isConnected=="function")try{return!!e.isConnected()}catch{return!1}return this.orchestratorSocketConnectedByCore}async processOutbox(e){var n;if(!this.transport)return;let r=await this.outboxStore.listPending(this.policy.batchSize);if(r.length===0)return;let o=new Map;for(let i of r){let c=String(i.change.domain||"").trim().toLowerCase()||"__unknown__";o.has(c)||o.set(c,[]),(n=o.get(c))==null||n.push(i)}for(let[i,c]of o.entries()){if(i==="__unknown__"){for(let h of c){let l=h.retryCount+1;await this.outboxStore.update({...h,retryCount:l,status:l>=this.policy.maxRetry?"FAILED":"PENDING",lastError:"DOMAIN_REQUIRED",updatedAt:new Date().toISOString()})}continue}let u=await this.transport.push({scope:e,outbox:c,domain:i}),a=new Set((u.applied||[]).map(h=>h.changeId)),s=new Map((u.failed||[]).map(h=>[h.changeId,h])),d=new Set((u.conflicts||[]).map(h=>h.changeId));await this.resolveConflicts(u.conflicts||[],e,i);for(let h of c){if(a.has(h.change.id)){await this.outboxStore.remove(h.outboxId);continue}let l=s.get(h.change.id);if(l||d.has(h.change.id)){let y=h.retryCount+1;await this.outboxStore.update({...h,retryCount:y,status:y>=this.policy.maxRetry?"FAILED":"PENDING",lastError:(l==null?void 0:l.message)||(d.has(h.change.id)?"SYNC_CONFLICT":h.lastError),updatedAt:new Date().toISOString()})}}}}async collectBridgeLocalChanges(e){let r=this.listDomainBridges();for(let o of r){let n=this.bridges.get(o);if(!(n!=null&&n.collectLocalChanges))continue;let i=await n.collectLocalChanges(e);if(!(!Array.isArray(i)||i.length===0))for(let c of i){let u=String(c.domain||"").trim()||o,a={...c,domain:u,scope:c.scope||e},s=`${a.domain}:${a.id}`;await this.enqueueLocalChange(a,s)}}}async applyRemoteChanges(e,r){if(!Array.isArray(e)||e.length===0)return;let o=[...e].sort((i,c)=>i.occurredAt.localeCompare(c.occurredAt)),n=new Map;for(let i of o){n.has(i.domain)||n.set(i.domain,[]);let c=n.get(i.domain);c&&c.push(i)}for(let[i,c]of n.entries()){let u=this.bridges.get(i);if(u!=null&&u.applyRemoteChanges&&(await u.applyRemoteChanges(c,r),this.projectionHook))for(let a of c)await this.projectionHook(a,r)}}async resolveConflicts(e,r,o){var i;if(!((i=this.transport)!=null&&i.resolveConflicts)||e.length===0)return;let n=[];for(let c of e){let u=[...this.conflictResolvers],a=null;for(let s of u){let d=await s(c,{scope:r});if(d){a=d;break}}a||(a=M(c)),n.push(a)}n.length>0&&await this.transport.resolveConflicts({scope:r,domain:o,resolutions:n})}},L=class{constructor(){this.runtimeBuilder=W.CoreRuntime.builder();this.options={};this.runtimeBuilder.withDbAdapter(()=>new G.InMemoryDbAdapter)}withPlatformAdapter(e){return this.runtimeBuilder.withPlatformAdapter(e),this}withDbAdapter(e){return this.runtimeBuilder.withDbAdapter(e),this}withHttpAdapter(e){return this.runtimeBuilder.withHttpAdapter(e),this}withSocketAdapter(e){return this.runtimeBuilder.withSocketAdapter(e),this}withLoggerAdapter(e){return this.runtimeBuilder.withLoggerAdapter(e),this}withActivitySink(e){return this.runtimeBuilder.withActivitySink(e),this}withExtension(e,r){return this.runtimeBuilder.withExtension(e,r),this}withPolicy(e){return this.options={...this.options,policy:e},this}withTransport(e){return this.options={...this.options,transport:e},this}withOutboxStore(e){return this.options={...this.options,outboxStore:e},this}withCheckpointStore(e){return this.options={...this.options,checkpointStore:e},this}withConflictResolvers(e){return this.options={...this.options,conflictResolvers:e},this}withProjectionHook(e){return this.options={...this.options,projectionHook:e},this}build(){let e=(0,I.createStore)({phase:"idle",started:!1,syncing:!1,startCount:0,stopCount:0,syncCount:0,lastError:null,lastTransitionAt:new Date().toISOString()}),r=(0,I.createStore)({running:!1,syncing:!1,wsConnected:!1,lastSyncAt:null,lastTriggerReason:null,lastError:null,lastErrorCode:null}),o=this.runtimeBuilder.build();return new A(o,e,r,this.options)}};function gt(t={}){let e=A.builder();return t.policy&&e.withPolicy(t.policy),t.transport&&e.withTransport(t.transport),t.outboxStore&&e.withOutboxStore(t.outboxStore),t.checkpointStore&&e.withCheckpointStore(t.checkpointStore),t.conflictResolvers&&e.withConflictResolvers(t.conflictResolvers),t.projectionHook&&e.withProjectionHook(t.projectionHook),t.socketAdapterFactory&&e.withSocketAdapter(t.socketAdapterFactory),e.build()}function St(t,e){t.registerDomainBridge(e)}async function yt(t,e,r={}){t.isStarted()||await t.start(r),await t.startSync(e)}async function ft(t){await t.stopSync()}function w(t){return{store:t.orchestratorStore,getState:()=>t.getOrchestratorState(),start:async e=>t.startOrchestrator(e),stop:async()=>t.stopOrchestrator(),trigger:async(e="manual")=>t.triggerSync(e)}}var E=class extends Error{constructor(e,r,o=!1,n,i){super(r),this.name="OfsyncTransportError",this.code=e,this.retryable=o,this.status=n,this.details=i}};async function pt(t){let e=globalThis.crypto;if(!e||!e.subtle)throw new Error("WebCrypto API is not available");let r=new TextEncoder().encode(t),o=await e.subtle.digest("SHA-256",r);return[...new Uint8Array(o)].map(n=>n.toString(16).padStart(2,"0")).join("")}function mt(t){let e=String(t.table||""),r=String(t.id||""),o=String(t.changeId||"");return[e,r,o].join("::")}function Q(t){return{conflictId:mt(t),changeId:String(t.changeId||""),domain:"unknown",entity:String(t.table||""),recordId:String(t.id||""),code:String(t.code||"UNKNOWN_CONFLICT"),message:String(t.message||"Conflict detected"),retryable:!!t.retryable,scope:{tenantId:typeof t.tenantId=="string"?t.tenantId:void 0,branchId:typeof t.branchId=="string"?t.branchId:void 0,ledgerProfileId:typeof t.ledgerProfileId=="string"?t.ledgerProfileId:typeof t.financeDomainId=="string"?t.financeDomainId:void 0,sourceDomainId:typeof t.sourceDomain=="string"?t.sourceDomain:typeof t.financeDomainId=="string"?t.financeDomainId:void 0},details:t}}function B(t){return!!(t&&typeof t=="object"&&!Array.isArray(t))}function bt(t){var r;let e=(r=t.branchId)==null?void 0:r.trim();if(!e)throw new E("BRANCH_SCOPE_REQUIRED","Sync scope requires branchId",!1);return e}function V(t){return typeof t=="string"?t.trim():""}function Ct(t,e){let r=V(t.productFamilyId);if(r)return r;if(!Array.isArray(e)||e.length===0)return"";let o=Array.from(new Set(e.map(n=>{var i;return V((i=n.change.scope)==null?void 0:i.productFamilyId)}).filter(Boolean)));return o.length===1?o[0]:""}var U=class{constructor(e){this.options=e}nowIso(){return this.options.nowIso?this.options.nowIso():new Date().toISOString()}defaultBootstrapSinceIso(){return new Date(Date.now()-2160*60*60*1e3).toISOString()}async buildHeaders(e,r){let o={"content-type":"application/json","x-branch-id":bt(e),"x-sync-protocol-version":"1"};e.tenantId&&(o["x-tenant-id"]=e.tenantId),e.deviceId&&(o["x-device-id"]=e.deviceId),e.ledgerProfileId&&(o["x-ledger-profile-id"]=e.ledgerProfileId),e.sourceDomainId&&(o["x-finance-source-domain-id"]=e.sourceDomainId);let n=Ct(e,r);if(n&&(o["x-product-family-id"]=n),this.options.getAccessToken){let i=await this.options.getAccessToken();i&&(o.authorization=`Bearer ${i}`)}return o}normalizeUrl(e){return`${this.options.baseUrl.replace(/\/+$/,"")}${e}`}normalizeDomainPath(e){let r=e.trim().toLowerCase().replace(/[^a-z0-9_-]/g,"");return r||null}resolveDomainPathOrThrow(e){let r=this.normalizeDomainPath(e||"");if(!r)throw new E("DOMAIN_REQUIRED","Sync transport requires explicit domain",!1);return r}parseFailure(e,r){var a,s,d;let o=r||{},n=((a=o.error)==null?void 0:a.code)||"SYNC_TRANSPORT_ERROR",i=((s=o.error)==null?void 0:s.message)||`Sync transport failed with status ${e}`,c=!!((d=o.error)!=null&&d.retryable),u=B(o.error)&&B(o.error.details)?o.error.details:void 0;throw new E(n,i,c,e,u)}async push(e){let r={changes:e.outbox.map(s=>{let d=B(s.change.payload)?s.change.payload:{},h=typeof d.table=="string"?d.table.trim():"",l=B(d.record)?d.record:null,y=Date.parse(s.change.occurredAt);return h&&l?{id:s.change.id,table:h,type:s.change.type,record:{...l,id:s.change.recordId||String(l.id||s.change.id)},updated_at:Number.isFinite(y)?y:s.change.occurredAt}:{id:s.change.id,entity:s.change.entity,type:s.change.type,data:{id:s.change.recordId,...d},timestamp:s.change.occurredAt}})},o=JSON.stringify(r),n=await pt(o),i=await this.buildHeaders(e.scope,e.outbox);i["x-data-checksum"]=n;let c=this.resolveDomainPathOrThrow(e.domain),u=await this.options.httpAdapter.request({method:"POST",headers:i,body:r,url:this.normalizeUrl(`/sync/${c}/push`)});u.status>=400&&this.parseFailure(u.status,u.data);let a=u.data||{};return{applied:Array.isArray(a.applied)?a.applied:[],failed:Array.isArray(a.failed)?a.failed:[],conflicts:Array.isArray(a.conflicts)?a.conflicts.map(Q):[]}}async pull(e){let r=await this.buildHeaders(e.scope),o=e.cursor||this.defaultBootstrapSinceIso(),n=this.resolveDomainPathOrThrow(e.domain),i=await this.options.httpAdapter.request({method:"GET",headers:r,query:{since:o},url:this.normalizeUrl(`/sync/${n}/pull`)});i.status>=400&&this.parseFailure(i.status,i.data);let c=i.data||{},u=Array.isArray(c.changes)?c.changes.map(s=>{var l,y,f,g;let d=typeof s.table=="string"?s.table:"",h=typeof s.domain=="string"&&s.domain.trim().length>0?s.domain:d.startsWith("auth_")?"ofauth":d.startsWith("offinance_")||d.startsWith("ofinance_")?"offinance":d.startsWith("ofcoop_")?"ofcoop":d.startsWith("ofpos_")?"ofpos":"unknown";return{id:String(s.changeId||s.id||""),domain:String(h),entity:String(s.entity||d||""),type:String(s.type||s.action||"UPDATE"),recordId:String(s.recordId||((l=s.record)==null?void 0:l.id)||((y=s.data)==null?void 0:y.id)||s.id||""),payload:typeof d=="string"&&d.length>0?{table:String(d),action:String(s.action||s.type||"UPDATE"),record:{...s.record||s.data||{},id:String(s.recordId||((f=s.record)==null?void 0:f.id)||((g=s.data)==null?void 0:g.id)||s.id||"")}}:s.record||s.data||{},occurredAt:String(s.timestamp||this.nowIso()),scope:e.scope}}):[],a=u.length>0?u[u.length-1].occurredAt:this.nowIso();return{changes:u,nextCursor:a,conflicts:[]}}async listConflicts(e){let r=await this.buildHeaders(e.scope),o=this.normalizeDomainPath(e.domain||""),n=o?this.normalizeUrl(`/sync/${o}/conflicts`):this.normalizeUrl("/sync/conflicts"),i=await this.options.httpAdapter.request({method:"GET",url:n,headers:r});i.status>=400&&this.parseFailure(i.status,i.data);let c=i.data||{};return Array.isArray(c.conflicts)?c.conflicts.map(Q):[]}async resolveConflicts(e){var h;let r=await this.listConflicts({scope:e.scope,domain:e.domain}),o=new Map(r.map(l=>[l.conflictId,l])),n=e.resolutions.map(l=>o.get(l.conflictId)).filter(l=>!!l).map(l=>({table:l.entity,id:l.recordId})),i=await this.buildHeaders(e.scope),c={resolutions:n},u=this.normalizeDomainPath(e.domain||""),a=u?this.normalizeUrl(`/sync/${u}/conflicts/resolve`):this.normalizeUrl("/sync/conflicts/resolve"),s=await this.options.httpAdapter.request({method:"POST",url:a,headers:i,body:c});s.status>=400&&this.parseFailure(s.status,s.data);let d=Array.isArray((h=s.data)==null?void 0:h.remaining)?s.data.remaining.length:0;return{resolvedCount:Math.max(0,n.length-d)}}};function X(t){return`Tidak dapat terhubung ke server sinkronisasi (${t.trim()||"VITE_OFSYNC_BASE_URL"}). Pastikan ofsync-server berjalan dan CORS origin browser diizinkan.`}function J(t){return t.trim().replace(/\/+$/,"")}function Z(t){return t instanceof Error?t.message:String(t!=null?t:"")}function vt(t){let e=Z(t).toLowerCase();return e.includes("server sinkronisasi")||e.includes("cors origin browser diizinkan")||e.includes("tidak dapat terhubung ke server sinkronisasi")}function tt(t,e=9e5){if(typeof t=="number"&&Number.isFinite(t)&&t>0)return Math.floor(t*1e3);if(typeof t!="string")return e;let r=t.trim();if(!r)return e;if(/^\d+$/.test(r))return Math.max(1,Number(r))*1e3;let o=r.match(/^(\d+)\s*([smhd])$/i);if(!o)return e;let n=Number(o[1]),i=o[2].toLowerCase(),c=i==="s"?1e3:i==="m"?6e4:i==="h"?36e5:864e5;return Math.max(1,n)*c}function P(t,e,r=X){let o=Z(t),n=o.toLowerCase();return n.includes("networkerror")||n.includes("failed to fetch")||n.includes("load failed")||n.includes("fetch failed")||n.includes("the network connection was lost")||n.includes("network request failed")?r(e):o}function O(t,e){let r=new Error(e);return r.code=t,r}function Ot(t){var u,a;let e=t.accessToken.trim(),r=t.branchId.trim();if(!e||!r)return null;let o=J(t.baseUrl).replace(/^http:\/\//i,"ws://").replace(/^https:\/\//i,"wss://"),n={token:e,branchId:r},i=String((u=t.tenantId)!=null?u:"").trim(),c=String((a=t.domainId)!=null?a:"").trim();return i&&(n.tenantId=i),c&&(n.domainId=c),{url:`${o}/ws/sync-notify`,query:n}}async function kt(t){var n,i,c,u;if(t.bearerToken.length>0)return t.bearerToken;let e=(n=t.nowMs)!=null?n:Date.now(),r=(i=t.refreshLeewayMs)!=null?i:1e4;if((c=t.session)!=null&&c.accessToken){if(t.session.expiresAtMs>e+r)return t.session.accessToken;let a=await t.refresh();if(a!=null&&a.accessToken)return a.accessToken}let o=await t.loginFromCredentials();return(u=o==null?void 0:o.accessToken)!=null?u:""}function wt(t){var u,a,s;let e=(u=t.fetchImpl)!=null?u:fetch,r=J(t.baseUrl),o=(a=t.accessTokenFallbackTtlMs)!=null?a:9e5,n=(s=t.networkErrorMessage)!=null?s:X,i=d=>(Array.isArray(d.scopes)?d.scopes:[]).filter(l=>!!(l&&typeof l=="object")).map(l=>{var y,f,g,S;return{tenantId:typeof l.tenantId=="string"&&l.tenantId.trim().length>0?l.tenantId:null,branchId:String((y=l.branchId)!=null?y:"").trim(),role:String((f=l.role)!=null?f:""),userId:String((g=l.userId)!=null?g:""),email:String((S=l.email)!=null?S:"")}}).filter(l=>l.branchId.length>0),c=(d,h)=>{var f,g,S;let l=String((g=(f=d.accessToken)!=null?f:d.token)!=null?g:""),y=String((S=d.refreshToken)!=null?S:"");if(!l||!y)throw O("SYNC_AUTH_TOKEN_INCOMPLETE","Sync auth gagal: token tidak lengkap.");return{accessToken:l,refreshToken:y,expiresAtMs:Date.now()+tt(d.expiresIn,o),principal:h}};return{async fetchScopeOptions(d,h){var l,y,f;if(!r)return[];try{let g=await e(`${r}/api/v1/auth/scope-options`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:d,password:h})}),S=await g.json().catch(()=>({}));if(!g.ok){let m=(l=S.error)!=null?l:{};throw O(String((y=m.code)!=null?y:`HTTP_${g.status}`),String((f=m.message)!=null?f:`Sync scope discovery gagal (${g.status})`))}return i(S)}catch(g){let S=g;throw O(typeof(S==null?void 0:S.code)=="string"?S.code:"SYNC_AUTH_SCOPE_DISCOVERY_FAILED",P(g,r,n))}},async login(d,h,l){var g,S,m,k,R;let y=String((g=l.branchId)!=null?g:"").trim(),f=String((S=l.tenantId)!=null?S:"").trim();try{let b=await e(`${r}/api/v1/auth/login`,{method:"POST",headers:{"Content-Type":"application/json","X-Branch-ID":y,...f?{"X-Tenant-ID":f}:{},"X-Device-ID":t.deviceId},body:JSON.stringify({username:d,password:h})}),C=await b.json().catch(()=>({}));if(!b.ok){let T=(m=C.error)!=null?m:{};throw O(String((k=T.code)!=null?k:`HTTP_${b.status}`),String((R=T.message)!=null?R:`Sync auth login gagal (${b.status})`))}return c(C,d)}catch(b){let C=b;throw O(typeof(C==null?void 0:C.code)=="string"?C.code:"SYNC_AUTH_FAILED",P(b,r,n))}},async refresh(d,h){var l,y,f;try{let g=await e(`${r}/api/v1/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json",...h?{"X-Tenant-ID":h}:{},"X-Device-ID":t.deviceId},body:JSON.stringify({refreshToken:d})}),S=await g.json().catch(()=>({}));if(!g.ok){let m=(l=S.error)!=null?l:{};throw O(String((y=m.code)!=null?y:`HTTP_${g.status}`),String((f=m.message)!=null?f:`Sync auth refresh gagal (${g.status})`))}return c(S,"")}catch(g){let S=g;throw O(typeof(S==null?void 0:S.code)=="string"?S.code:"SYNC_AUTH_REFRESH_FAILED",P(g,r,n))}}}}function It(){let t=new Set;return{track(e){let r=Promise.resolve(e).finally(()=>{t.delete(r)});return t.add(r),r},async waitForIdle(){for(;t.size>0;)await Promise.allSettled([...t])},size(){return t.size},clear(){t.clear()}}}async function At(t){if(!t)return 0;let e=await t.getOutboxState();return Number(e.pending||0)+Number(e.failed||0)}async function Et(t){var i,c,u,a;let e=(i=t.triggerInitialPull)!=null?i:(async s=>{await w(s).trigger("manual")});if(!t.runtimeReady||!t.serverSyncEnabled||!t.isAuthenticated){if(t.lastStartSignatureRef.current="",(c=t.setWsConnected)==null||c.call(t,!1),(u=t.setSyncing)==null||u.call(t,!1),!t.runtime)return;await w(t.runtime).stop().catch(()=>{});return}let r=t.runtime;if(r&&w(r).getState().running&&t.lastStartSignatureRef.current===t.startSignature)return;let o=await t.ensureOrchestratorStarted();w(o).getState().running&&t.lastStartSignatureRef.current===t.startSignature||(await e(o),t.lastStartSignatureRef.current=t.startSignature,(a=t.setLastError)==null||a.call(t,""))}async function Pt(t){var o,n,i,c,u,a,s,d,h,l,y,f,g,S,m,k,R,b,C,T,F,H,$,z,j,K,Y,q;let e=(o=t.triggerManualSync)!=null?o:(async p=>{await w(p).trigger("manual")});if((n=t.setStage)==null||n.call(t,"check-syncing"),t.runtime.getOrchestratorState().syncing)return{ok:!0,message:(i=t.inProgressMessage)!=null?i:"Sinkronisasi sedang berjalan."};if((c=t.setStage)==null||c.call(t,"check-base-url"),t.serverSyncBaseUrl.length===0){let p=(u=t.missingBaseUrlMessage)!=null?u:"VITE_OFSYNC_BASE_URL belum diatur.";return t.silent||t.setLastError(p),{ok:!1,message:p}}if((a=t.setStage)==null||a.call(t,"resolve-access-token"),!(await t.resolveAccessToken()).trim()){let p=(s=t.missingAccessTokenMessage)!=null?s:"Sesi sinkronisasi tidak valid. Login ulang diperlukan.";return t.silent||t.setLastError(p),t.setAuthBlocked(!0),{ok:!1,message:p}}t.setLastError("");try{(d=t.setStage)==null||d.call(t,"ensure-orchestrator-started"),await t.ensureOrchestratorStarted(),(h=t.setStage)==null||h.call(t,"before-trigger"),await((l=t.beforeTrigger)==null?void 0:l.call(t)),(y=t.setStage)==null||y.call(t,"wait-pending-operations"),await((f=t.waitForPendingOperations)==null?void 0:f.call(t)),(g=t.setStage)==null||g.call(t,"refresh-queue-length:before-trigger"),await((S=t.refreshQueueLength)==null?void 0:S.call(t));try{(m=t.setStage)==null||m.call(t,"trigger-manual"),await e(t.runtime)}catch(p){if((p instanceof Error?p.message:String(p))!=="Cannot start sync before runtime is started")throw p;(k=t.setStage)==null||k.call(t,"ensure-orchestrator-started:retry"),await t.ensureOrchestratorStarted(),(R=t.setStage)==null||R.call(t,"before-trigger:retry"),await((b=t.beforeTrigger)==null?void 0:b.call(t)),(C=t.setStage)==null||C.call(t,"trigger-manual:retry"),await e(t.runtime)}return(T=t.setStage)==null||T.call(t,"refresh-queue-length:after-trigger"),await((F=t.refreshQueueLength)==null?void 0:F.call(t)),(H=t.setStage)==null||H.call(t,"refresh-projection-ui"),await(($=t.refreshProjectionUi)==null?void 0:$.call(t)),(z=t.setStage)==null||z.call(t,"on-success"),await((j=t.onSuccess)==null?void 0:j.call(t)),t.setAuthBlocked(!1),(K=t.setStage)==null||K.call(t,"completed"),{ok:!0,message:"Sinkronisasi selesai."}}catch(p){(Y=t.onRawError)==null||Y.call(t,p instanceof Error?p.message:String(p));let v=P(p,t.serverSyncBaseUrl),et=v.toLowerCase().includes("unauthorized")||v.toLowerCase().includes("sesi sinkronisasi tidak valid")||v.includes("401");return t.silent&&t.isConnectivityError(v)?{ok:!1,message:v}:(t.setLastError(v),t.setAuthBlocked(et),(q=t.setRuntimeError)==null||q.call(t,v),{ok:!1,message:v})}}function Rt(t){let e=t.store.getState().syncing;return t.store.subscribe(r=>{var c,u,a,s,d,h,l,y,f,g,S,m;let o=e;if(e=r.syncing,(c=t.setSyncing)==null||c.call(t,r.syncing),(u=t.setWsConnected)==null||u.call(t,r.wsConnected),!r.lastError){if(o&&!r.syncing){let k=(s=(a=t.getPullAppliedCount)==null?void 0:a.call(t))!=null?s:0;(d=t.resetPullAppliedCount)==null||d.call(t),(h=t.onSuccessfulIdle)==null||h.call(t,{appliedCount:k,state:r})}t.setLastError(""),t.setAuthBlocked(!1),(l=t.clearConnectivityError)==null||l.call(t);return}let n=((y=r.lastErrorCode)==null?void 0:y.startsWith("AUTH_"))||r.lastError.toLowerCase().includes("unauthorized")||r.lastError.includes("401");((g=(f=t.shouldSuppressConnectivityError)==null?void 0:f.call(t,r))!=null?g:r.lastTriggerReason!=="manual"&&t.isConnectivityError(r.lastError))||(r.syncing||(S=t.resetPullAppliedCount)==null||S.call(t),t.setLastError(r.lastError),t.setAuthBlocked(!!n),(m=t.onError)==null||m.call(t,{message:r.lastError,isAuthError:!!n,state:r}))})}
|
|
1
|
+
"use strict";var N=Object.defineProperty;var rt=Object.getOwnPropertyDescriptor;var nt=Object.getOwnPropertyNames;var ot=Object.prototype.hasOwnProperty;var it=(t,e)=>{for(var r in e)N(t,r,{get:e[r],enumerable:!0})},st=(t,e,r,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of nt(e))!ot.call(t,n)&&n!==r&&N(t,n,{get:()=>e[n],enumerable:!(o=rt(e,n))||o.enumerable});return t};var ct=t=>st(N({},"__esModule",{value:!0}),t);var Tt={};it(Tt,{DEFAULT_SYNC_POLICY:()=>x,InMemoryCheckpointStore:()=>D,InMemoryOutboxStore:()=>_,OFSYNC_CONTRACT_STAGE:()=>at,OfsyncCore:()=>A,OfsyncCoreBuilder:()=>L,OfsyncHttpTransport:()=>U,OfsyncTransportError:()=>E,buildSyncNotifySocketConnectOptions:()=>Ot,createOfsyncAuthClient:()=>wt,createPendingSyncOperationTracker:()=>It,createSyncAuthError:()=>k,createSyncRuntime:()=>gt,defaultConflictResolver:()=>M,ensureHostSyncOrchestratorLifecycle:()=>Et,getSyncOrchestrator:()=>O,isSyncConnectivityRuntimeError:()=>vt,normalizeSyncNetworkError:()=>P,parseSyncDurationMs:()=>tt,readSyncOutboxQueueLength:()=>At,registerDomainBridge:()=>St,resolveSyncAccessToken:()=>kt,runManualSyncCycle:()=>Pt,startSync:()=>yt,stopSync:()=>ft,subscribeToSyncOrchestratorLifecycle:()=>Rt});module.exports=ct(Tt);var x={batchSize:200,maxRetry:5,scheduler:{mode:"manual",onlineTrigger:!0},retryBackoff:{strategy:"exponential-jitter",baseDelayMs:500,maxDelayMs:3e4}};var _=class{constructor(){this.rows=new Map}async enqueue(e){this.rows.set(e.outboxId,e)}async listPending(e){return[...this.rows.values()].filter(r=>r.status==="PENDING"||r.status==="FAILED").sort((r,o)=>r.createdAt.localeCompare(o.createdAt)).slice(0,Math.max(0,e))}async findByIdempotencyKey(e){for(let r of this.rows.values())if(r.idempotencyKey===e)return r;return null}async listAll(){return[...this.rows.values()].sort((e,r)=>e.createdAt.localeCompare(r.createdAt))}async update(e){this.rows.set(e.outboxId,e)}async remove(e){this.rows.delete(e)}};var D=class{constructor(){this.rows=new Map}async get(e){return this.rows.has(e)?this.rows.get(e):null}async set(e,r){this.rows.set(e,r)}};function M(t){return t.code==="VERSION_CONFLICT"?{conflictId:t.conflictId,action:"APPLY_REMOTE"}:t.code==="SCOPE_CONFLICT"?{conflictId:t.conflictId,action:"MANUAL_MERGE"}:{conflictId:t.conflictId,action:"KEEP_LOCAL"}}var at="phase1-runtime-skeleton";var W=require("ofcore"),G=require("ofcore"),I=require("ofcore");function lt(t={}){return{...x,...t,scheduler:{...x.scheduler,...t.scheduler||{}},retryBackoff:{...x.retryBackoff,...t.retryBackoff||{}}}}function ut(t){return`${t.tenantId||"__standalone__"}::${t.branchId||"__all__"}::${t.ledgerProfileId||"__default_ledger__"}::${t.sourceDomainId||"__default_source__"}::${t.deviceId||"__device__"}`}function dt(t,e){return`${ut(t)}::${e}`}function ht(t){if(typeof t!="string"||t.trim().length===0)return null;let e=Date.parse(t);return Number.isFinite(e)?new Date(e+5e3).toISOString():null}var A=class{constructor(e,r,o,n={}){this.runtime=e;this.runtimeStateStore=r;this.orchestratorStateStore=o;this.bridges=new Map;this.orchestratorScope=null;this.orchestratorSocketUnsubscribe=null;this.orchestratorSocketConnectedByCore=!1;this.orchestratorSyncPromise=null;this.runtimeStore=(0,I.asReadonlyStore)(this.runtimeStateStore),this.orchestratorStore=(0,I.asReadonlyStore)(this.orchestratorStateStore),this.policy=lt(n.policy),this.transport=n.transport,this.outboxStore=n.outboxStore||new _,this.checkpointStore=n.checkpointStore||new D,this.conflictResolvers=n.conflictResolvers||[],this.projectionHook=n.projectionHook}static builder(){return new L}get registry(){return this.runtime.registry}getPolicy(){return this.policy}registerDomainBridge(e){let r=e.domain.trim();if(!r)throw new Error("DomainBridge.domain is required");if(this.bridges.has(r))throw new Error(`DomainBridge for domain "${r}" is already registered`);this.bridges.set(r,e)}listDomainBridges(){return[...this.bridges.keys()].sort()}async getOutboxState(){let e=await this.outboxStore.listAll(),r=e.filter(n=>n.status==="PENDING").length,o=e.filter(n=>n.status==="FAILED").length;return{pending:r,failed:o,total:e.length}}async enqueueLocalChange(e,r){let o=(r||`${e.domain}:${e.entity}:${e.recordId}:${e.type}:${e.id}`).trim();if(!o)throw new Error("idempotencyKey must not be empty");let n=await this.outboxStore.findByIdempotencyKey(o);if(n)return n;let i=new Date().toISOString(),c={outboxId:`outbox_${Date.now()}_${Math.random().toString(36).slice(2)}`,idempotencyKey:o,change:e,retryCount:0,status:"PENDING",createdAt:i,updatedAt:i};return await this.outboxStore.enqueue(c),c}async start(e={}){this.runtimeStateStore.setState({phase:"starting",started:!1,syncing:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.start(e),this.runtimeStateStore.setState(r=>({...r,phase:"started",started:!0,syncing:!1,startCount:r.startCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(r){throw this.runtimeStateStore.setState({phase:"error",started:!1,syncing:!1,lastError:r instanceof Error?r.message:String(r),lastTransitionAt:new Date().toISOString()}),r}}async stop(){await this.stopOrchestrator(),this.stopScheduler(),this.runtimeStateStore.setState({phase:"stopping",started:this.runtime.isStarted(),syncing:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.stop(),this.runtimeStateStore.setState(e=>({...e,phase:"stopped",started:!1,syncing:!1,stopCount:e.stopCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(e){throw this.runtimeStateStore.setState({phase:"error",started:this.runtime.isStarted(),syncing:!1,lastError:e instanceof Error?e.message:String(e),lastTransitionAt:new Date().toISOString()}),e}}async startSync(e){if(!this.runtime.isStarted())throw new Error("Cannot start sync before runtime is started");this.runtimeStateStore.setState({phase:"syncing",started:!0,syncing:!0,lastError:null,lastTransitionAt:new Date().toISOString()});try{if(this.transport){await this.collectBridgeLocalChanges(e),await this.processOutbox(e);let r=this.listDomainBridges();for(let o of r){let n=dt(e,o),i=await this.checkpointStore.get(n),c;try{c=await this.transport.pull({scope:e,cursor:i,limit:this.policy.batchSize,domain:o})}catch(a){let s=(a==null?void 0:a.code)||"",d=a==null?void 0:a.details,h=ht(d==null?void 0:d.cutoffTime);if(s!=="SYNC_RESYNC_REQUIRED"||!h)throw a;await this.checkpointStore.set(n,h),i=h,c=await this.transport.pull({scope:e,cursor:i,limit:this.policy.batchSize,domain:o})}let u=(c.changes||[]).map(a=>{let s=String(a.domain||"").trim().toLowerCase();if(!s)return{...a,domain:o};if(s!==o)throw new Error(`Sync pull domain mismatch: expected ${o}, received ${s}`);return a});await this.applyRemoteChanges(u,e),c.nextCursor!==void 0&&await this.checkpointStore.set(n,c.nextCursor)}}this.runtimeStateStore.setState(r=>({...r,phase:"started",started:!0,syncing:!1,syncCount:r.syncCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(r){throw this.runtimeStateStore.setState({phase:"error",started:this.runtime.isStarted(),syncing:!1,lastError:r instanceof Error?r.message:String(r),lastTransitionAt:new Date().toISOString()}),r}}async stopSync(){this.runtimeStateStore.setState(e=>({...e,phase:this.runtime.isStarted()?"started":"stopped",syncing:!1,lastTransitionAt:new Date().toISOString()}))}isStarted(){return this.runtime.isStarted()}startScheduler(e){if(this.policy.scheduler.mode!=="interval")return;let r=this.policy.scheduler.intervalMs||1e4;this.stopScheduler(),this.schedulerTimer=setInterval(()=>{this.startSync(e).catch(o=>{this.runtimeStateStore.setState(n=>({...n,phase:"error",syncing:!1,lastError:o instanceof Error?o.message:String(o),lastTransitionAt:new Date().toISOString()}))})},r)}stopScheduler(){this.schedulerTimer&&(clearInterval(this.schedulerTimer),this.schedulerTimer=void 0)}async notifyOnline(e){if(this.policy.scheduler.onlineTrigger!==!1){if(this.orchestratorStateStore.getState().running){this.orchestratorScope=e,await this.triggerSync("online");return}await this.startSync(e)}}getOrchestratorState(){return this.orchestratorStateStore.getState()}async startOrchestrator(e){let r=e.scope;this.orchestratorScope=r,this.orchestratorStateStore.setState({running:!0,syncing:!1,wsConnected:!1,lastSyncAt:null,lastTriggerReason:null,lastError:null,lastErrorCode:null}),this.runtime.isStarted()||await this.start(),await this.configureOrchestratorSocket(e.socket),this.configureOrchestratorInterval(e.auto)}async stopOrchestrator(){var e;this.orchestratorTimer&&(clearInterval(this.orchestratorTimer),this.orchestratorTimer=void 0),this.orchestratorSocketHealthTimer&&(clearInterval(this.orchestratorSocketHealthTimer),this.orchestratorSocketHealthTimer=void 0),this.orchestratorSocketUnsubscribe&&(this.orchestratorSocketUnsubscribe(),this.orchestratorSocketUnsubscribe=null),this.orchestratorSocketConnectedByCore&&((e=this.runtime.registry)!=null&&e.socketAdapter)&&await this.runtime.registry.socketAdapter.disconnect(),this.orchestratorSocketConnectedByCore=!1,this.orchestratorSyncPromise=null,this.orchestratorStateStore.setState(r=>({...r,running:!1,syncing:!1,wsConnected:!1,lastTriggerReason:null}))}async triggerSync(e="manual"){if(!this.orchestratorScope)throw new Error("Sync orchestrator scope is not initialized");return this.orchestratorSyncPromise?this.orchestratorSyncPromise:(this.runtime.isStarted()||await this.start(),this.orchestratorStateStore.setState(r=>({...r,syncing:!0,lastTriggerReason:e,lastError:null,lastErrorCode:null})),this.orchestratorSyncPromise=this.startSync(this.orchestratorScope).then(()=>{this.orchestratorStateStore.setState(r=>({...r,syncing:!1,lastSyncAt:new Date().toISOString(),lastError:null,lastErrorCode:null}))}).catch(r=>{var i;let o=r instanceof Error?r.message:String(r),n=String((i=r==null?void 0:r.code)!=null?i:"");throw this.orchestratorStateStore.setState(c=>({...c,syncing:!1,lastError:o,lastErrorCode:n||null})),r}).finally(()=>{this.orchestratorSyncPromise=null}),this.orchestratorSyncPromise)}async configureOrchestratorSocket(e){var c;let r=this.orchestratorSocketConnectedByCore;if(this.orchestratorSocketUnsubscribe&&(this.orchestratorSocketUnsubscribe(),this.orchestratorSocketUnsubscribe=null),!((e==null?void 0:e.enabled)!==!1)){this.orchestratorSocketConnectedByCore=!1;return}let n=(c=this.runtime.registry)==null?void 0:c.socketAdapter;if(!n){this.orchestratorSocketConnectedByCore=!1;return}if(r)try{await n.disconnect()}catch{}this.orchestratorSocketConnectedByCore=!1;let i=e!=null&&e.resolveConnectOptions?await e.resolveConnectOptions():(e==null?void 0:e.connectOptions)||null;if(i){try{await n.connect(i),this.orchestratorSocketConnectedByCore=!0;let u=(e==null?void 0:e.eventName)||"sync:notify";this.orchestratorSocketUnsubscribe=n.subscribe(u,()=>{this.triggerSync("notify").catch(a=>{var h;let s=a instanceof Error?a.message:String(a),d=String((h=a==null?void 0:a.code)!=null?h:"");this.orchestratorStateStore.setState(l=>({...l,lastError:s,lastErrorCode:d||null}))})})}catch{this.orchestratorSocketConnectedByCore=!1}this.orchestratorStateStore.setState(u=>({...u,wsConnected:this.readSocketConnectedState()})),this.startOrchestratorSocketHealthMonitor()}}configureOrchestratorInterval(e){var n;if(this.orchestratorTimer&&(clearInterval(this.orchestratorTimer),this.orchestratorTimer=void 0),!((n=e==null?void 0:e.enabled)!=null?n:this.policy.scheduler.mode==="interval"))return;let o=(e==null?void 0:e.intervalMs)||this.policy.scheduler.intervalMs||1e4;this.orchestratorTimer=setInterval(()=>{this.triggerSync("interval").catch(i=>{var a;let c=i instanceof Error?i.message:String(i),u=String((a=i==null?void 0:i.code)!=null?a:"");this.orchestratorStateStore.setState(s=>({...s,lastError:c,lastErrorCode:u||null}))})},o)}startOrchestratorSocketHealthMonitor(){this.orchestratorSocketHealthTimer&&(clearInterval(this.orchestratorSocketHealthTimer),this.orchestratorSocketHealthTimer=void 0),this.orchestratorSocketHealthTimer=setInterval(()=>{this.orchestratorStateStore.setState(e=>({...e,wsConnected:this.readSocketConnectedState()}))},2e3)}readSocketConnectedState(){var r;let e=(r=this.runtime.registry)==null?void 0:r.socketAdapter;if(e&&typeof e.isConnected=="function")try{return!!e.isConnected()}catch{return!1}return this.orchestratorSocketConnectedByCore}async processOutbox(e){var n;if(!this.transport)return;let r=await this.outboxStore.listPending(this.policy.batchSize);if(r.length===0)return;let o=new Map;for(let i of r){let c=String(i.change.domain||"").trim().toLowerCase()||"__unknown__";o.has(c)||o.set(c,[]),(n=o.get(c))==null||n.push(i)}for(let[i,c]of o.entries()){if(i==="__unknown__"){for(let h of c){let l=h.retryCount+1;await this.outboxStore.update({...h,retryCount:l,status:l>=this.policy.maxRetry?"FAILED":"PENDING",lastError:"DOMAIN_REQUIRED",updatedAt:new Date().toISOString()})}continue}let u=await this.transport.push({scope:e,outbox:c,domain:i}),a=new Set((u.applied||[]).map(h=>h.changeId)),s=new Map((u.failed||[]).map(h=>[h.changeId,h])),d=new Set((u.conflicts||[]).map(h=>h.changeId));await this.resolveConflicts(u.conflicts||[],e,i);for(let h of c){if(a.has(h.change.id)){await this.outboxStore.remove(h.outboxId);continue}let l=s.get(h.change.id);if(l||d.has(h.change.id)){let y=h.retryCount+1;await this.outboxStore.update({...h,retryCount:y,status:y>=this.policy.maxRetry?"FAILED":"PENDING",lastError:(l==null?void 0:l.message)||(d.has(h.change.id)?"SYNC_CONFLICT":h.lastError),updatedAt:new Date().toISOString()})}}}}async collectBridgeLocalChanges(e){let r=this.listDomainBridges();for(let o of r){let n=this.bridges.get(o);if(!(n!=null&&n.collectLocalChanges))continue;let i=await n.collectLocalChanges(e);if(!(!Array.isArray(i)||i.length===0))for(let c of i){let u=String(c.domain||"").trim()||o,a={...c,domain:u,scope:c.scope||e},s=`${a.domain}:${a.id}`;await this.enqueueLocalChange(a,s)}}}async applyRemoteChanges(e,r){if(!Array.isArray(e)||e.length===0)return;let o=[...e].sort((i,c)=>i.occurredAt.localeCompare(c.occurredAt)),n=new Map;for(let i of o){n.has(i.domain)||n.set(i.domain,[]);let c=n.get(i.domain);c&&c.push(i)}for(let[i,c]of n.entries()){let u=this.bridges.get(i);if(u!=null&&u.applyRemoteChanges&&(await u.applyRemoteChanges(c,r),this.projectionHook))for(let a of c)await this.projectionHook(a,r)}}async resolveConflicts(e,r,o){var i;if(!((i=this.transport)!=null&&i.resolveConflicts)||e.length===0)return;let n=[];for(let c of e){let u=[...this.conflictResolvers],a=null;for(let s of u){let d=await s(c,{scope:r});if(d){a=d;break}}a||(a=M(c)),n.push(a)}n.length>0&&await this.transport.resolveConflicts({scope:r,domain:o,resolutions:n})}},L=class{constructor(){this.runtimeBuilder=W.CoreRuntime.builder();this.options={};this.runtimeBuilder.withDbAdapter(()=>new G.InMemoryDbAdapter)}withPlatformAdapter(e){return this.runtimeBuilder.withPlatformAdapter(e),this}withDbAdapter(e){return this.runtimeBuilder.withDbAdapter(e),this}withHttpAdapter(e){return this.runtimeBuilder.withHttpAdapter(e),this}withSocketAdapter(e){return this.runtimeBuilder.withSocketAdapter(e),this}withLoggerAdapter(e){return this.runtimeBuilder.withLoggerAdapter(e),this}withActivitySink(e){return this.runtimeBuilder.withActivitySink(e),this}withExtension(e,r){return this.runtimeBuilder.withExtension(e,r),this}withPolicy(e){return this.options={...this.options,policy:e},this}withTransport(e){return this.options={...this.options,transport:e},this}withOutboxStore(e){return this.options={...this.options,outboxStore:e},this}withCheckpointStore(e){return this.options={...this.options,checkpointStore:e},this}withConflictResolvers(e){return this.options={...this.options,conflictResolvers:e},this}withProjectionHook(e){return this.options={...this.options,projectionHook:e},this}build(){let e=(0,I.createStore)({phase:"idle",started:!1,syncing:!1,startCount:0,stopCount:0,syncCount:0,lastError:null,lastTransitionAt:new Date().toISOString()}),r=(0,I.createStore)({running:!1,syncing:!1,wsConnected:!1,lastSyncAt:null,lastTriggerReason:null,lastError:null,lastErrorCode:null}),o=this.runtimeBuilder.build();return new A(o,e,r,this.options)}};function gt(t={}){let e=A.builder();return t.policy&&e.withPolicy(t.policy),t.transport&&e.withTransport(t.transport),t.outboxStore&&e.withOutboxStore(t.outboxStore),t.checkpointStore&&e.withCheckpointStore(t.checkpointStore),t.conflictResolvers&&e.withConflictResolvers(t.conflictResolvers),t.projectionHook&&e.withProjectionHook(t.projectionHook),t.socketAdapterFactory&&e.withSocketAdapter(t.socketAdapterFactory),e.build()}function St(t,e){t.registerDomainBridge(e)}async function yt(t,e,r={}){t.isStarted()||await t.start(r),await t.startSync(e)}async function ft(t){await t.stopSync()}function O(t){return{store:t.orchestratorStore,getState:()=>t.getOrchestratorState(),start:async e=>t.startOrchestrator(e),stop:async()=>t.stopOrchestrator(),trigger:async(e="manual")=>t.triggerSync(e)}}var E=class extends Error{constructor(e,r,o=!1,n,i){super(r),this.name="OfsyncTransportError",this.code=e,this.retryable=o,this.status=n,this.details=i}};async function pt(t,e){if(e)return e(t);let r=globalThis.crypto;if(!r||!r.subtle)throw new Error("WebCrypto API is not available");let o=new TextEncoder().encode(t),n=await r.subtle.digest("SHA-256",o);return[...new Uint8Array(n)].map(i=>i.toString(16).padStart(2,"0")).join("")}function mt(t){let e=String(t.table||""),r=String(t.id||""),o=String(t.changeId||"");return[e,r,o].join("::")}function Q(t){return{conflictId:mt(t),changeId:String(t.changeId||""),domain:"unknown",entity:String(t.table||""),recordId:String(t.id||""),code:String(t.code||"UNKNOWN_CONFLICT"),message:String(t.message||"Conflict detected"),retryable:!!t.retryable,scope:{tenantId:typeof t.tenantId=="string"?t.tenantId:void 0,branchId:typeof t.branchId=="string"?t.branchId:void 0,ledgerProfileId:typeof t.ledgerProfileId=="string"?t.ledgerProfileId:typeof t.financeDomainId=="string"?t.financeDomainId:void 0,sourceDomainId:typeof t.sourceDomain=="string"?t.sourceDomain:typeof t.financeDomainId=="string"?t.financeDomainId:void 0},details:t}}function B(t){return!!(t&&typeof t=="object"&&!Array.isArray(t))}function bt(t){var r;let e=(r=t.branchId)==null?void 0:r.trim();if(!e)throw new E("BRANCH_SCOPE_REQUIRED","Sync scope requires branchId",!1);return e}function V(t){return typeof t=="string"?t.trim():""}function Ct(t,e){let r=V(t.productFamilyId);if(r)return r;if(!Array.isArray(e)||e.length===0)return"";let o=Array.from(new Set(e.map(n=>{var i;return V((i=n.change.scope)==null?void 0:i.productFamilyId)}).filter(Boolean)));return o.length===1?o[0]:""}var U=class{constructor(e){this.options=e}nowIso(){return this.options.nowIso?this.options.nowIso():new Date().toISOString()}defaultBootstrapSinceIso(){return new Date(Date.now()-2160*60*60*1e3).toISOString()}async buildHeaders(e,r,o){let n={"content-type":"application/json","x-branch-id":bt(e),"x-sync-protocol-version":"1"},i=this.normalizeDomainPath(o||"");i&&(n["x-domain-id"]=i),e.tenantId&&(n["x-tenant-id"]=e.tenantId),e.deviceId&&(n["x-device-id"]=e.deviceId),e.ledgerProfileId&&(n["x-ledger-profile-id"]=e.ledgerProfileId),e.sourceDomainId&&(n["x-finance-source-domain-id"]=e.sourceDomainId);let c=Ct(e,r);if(c&&(n["x-product-family-id"]=c),this.options.getAccessToken){let u=await this.options.getAccessToken();u&&(n.authorization=`Bearer ${u}`)}return n}normalizeUrl(e){return`${this.options.baseUrl.replace(/\/+$/,"")}${e}`}normalizeDomainPath(e){let r=e.trim().toLowerCase().replace(/[^a-z0-9_-]/g,"");return r||null}resolveDomainPathOrThrow(e){let r=this.normalizeDomainPath(e||"");if(!r)throw new E("DOMAIN_REQUIRED","Sync transport requires explicit domain",!1);return r}parseFailure(e,r){var a,s,d;let o=r||{},n=((a=o.error)==null?void 0:a.code)||"SYNC_TRANSPORT_ERROR",i=((s=o.error)==null?void 0:s.message)||`Sync transport failed with status ${e}`,c=!!((d=o.error)!=null&&d.retryable),u=B(o.error)&&B(o.error.details)?o.error.details:void 0;throw new E(n,i,c,e,u)}async push(e){let r={changes:e.outbox.map(s=>{let d=B(s.change.payload)?s.change.payload:{},h=typeof d.table=="string"?d.table.trim():"",l=B(d.record)?d.record:null,y=Date.parse(s.change.occurredAt);return h&&l?{id:s.change.id,table:h,type:s.change.type,record:{...l,id:s.change.recordId||String(l.id||s.change.id)},updated_at:Number.isFinite(y)?y:s.change.occurredAt}:{id:s.change.id,entity:s.change.entity,type:s.change.type,data:{id:s.change.recordId,...d},timestamp:s.change.occurredAt}})},o=JSON.stringify(r),n=await pt(o,this.options.hashText),i=this.resolveDomainPathOrThrow(e.domain),c=await this.buildHeaders(e.scope,e.outbox,i);c["x-data-checksum"]=n;let u=await this.options.httpAdapter.request({method:"POST",headers:c,body:r,url:this.normalizeUrl(`/sync/${i}/push`)});u.status>=400&&this.parseFailure(u.status,u.data);let a=u.data||{};return{applied:Array.isArray(a.applied)?a.applied:[],failed:Array.isArray(a.failed)?a.failed:[],conflicts:Array.isArray(a.conflicts)?a.conflicts.map(Q):[]}}async pull(e){let r=e.cursor||this.defaultBootstrapSinceIso(),o=this.resolveDomainPathOrThrow(e.domain),n=await this.buildHeaders(e.scope,void 0,o),i=await this.options.httpAdapter.request({method:"GET",headers:n,query:{since:r},url:this.normalizeUrl(`/sync/${o}/pull`)});i.status>=400&&this.parseFailure(i.status,i.data);let c=i.data||{},u=Array.isArray(c.changes)?c.changes.map(s=>{var l,y,f,g;let d=typeof s.table=="string"?s.table:"",h=typeof s.domain=="string"&&s.domain.trim().length>0?s.domain:d.startsWith("auth_")?"ofauth":d.startsWith("offinance_")||d.startsWith("ofinance_")?"offinance":d.startsWith("ofcoop_")?"ofcoop":d.startsWith("ofpos_")?"ofpos":"unknown";return{id:String(s.changeId||s.id||""),domain:String(h),entity:String(s.entity||d||""),type:String(s.type||s.action||"UPDATE"),recordId:String(s.recordId||((l=s.record)==null?void 0:l.id)||((y=s.data)==null?void 0:y.id)||s.id||""),payload:typeof d=="string"&&d.length>0?{table:String(d),action:String(s.action||s.type||"UPDATE"),record:{...s.record||s.data||{},id:String(s.recordId||((f=s.record)==null?void 0:f.id)||((g=s.data)==null?void 0:g.id)||s.id||"")}}:s.record||s.data||{},occurredAt:String(s.timestamp||this.nowIso()),scope:e.scope}}):[],a=u.length>0?u[u.length-1].occurredAt:this.nowIso();return{changes:u,nextCursor:a,conflicts:[]}}async listConflicts(e){let r=this.normalizeDomainPath(e.domain||""),o=await this.buildHeaders(e.scope,void 0,r||void 0),n=r?this.normalizeUrl(`/sync/${r}/conflicts`):this.normalizeUrl("/sync/conflicts"),i=await this.options.httpAdapter.request({method:"GET",url:n,headers:o});i.status>=400&&this.parseFailure(i.status,i.data);let c=i.data||{};return Array.isArray(c.conflicts)?c.conflicts.map(Q):[]}async resolveConflicts(e){var h;let r=await this.listConflicts({scope:e.scope,domain:e.domain}),o=new Map(r.map(l=>[l.conflictId,l])),n=e.resolutions.map(l=>o.get(l.conflictId)).filter(l=>!!l).map(l=>({table:l.entity,id:l.recordId})),i={resolutions:n},c=this.normalizeDomainPath(e.domain||""),u=await this.buildHeaders(e.scope,void 0,c||void 0),a=c?this.normalizeUrl(`/sync/${c}/conflicts/resolve`):this.normalizeUrl("/sync/conflicts/resolve"),s=await this.options.httpAdapter.request({method:"POST",url:a,headers:u,body:i});s.status>=400&&this.parseFailure(s.status,s.data);let d=Array.isArray((h=s.data)==null?void 0:h.remaining)?s.data.remaining.length:0;return{resolvedCount:Math.max(0,n.length-d)}}};function X(t){return`Tidak dapat terhubung ke server sinkronisasi (${t.trim()||"VITE_OFSYNC_BASE_URL"}). Pastikan ofsync-server berjalan dan CORS origin browser diizinkan.`}function J(t){return t.trim().replace(/\/+$/,"")}function Z(t){return t instanceof Error?t.message:String(t!=null?t:"")}function vt(t){let e=Z(t).toLowerCase();return e.includes("server sinkronisasi")||e.includes("cors origin browser diizinkan")||e.includes("tidak dapat terhubung ke server sinkronisasi")}function tt(t,e=9e5){if(typeof t=="number"&&Number.isFinite(t)&&t>0)return Math.floor(t*1e3);if(typeof t!="string")return e;let r=t.trim();if(!r)return e;if(/^\d+$/.test(r))return Math.max(1,Number(r))*1e3;let o=r.match(/^(\d+)\s*([smhd])$/i);if(!o)return e;let n=Number(o[1]),i=o[2].toLowerCase(),c=i==="s"?1e3:i==="m"?6e4:i==="h"?36e5:864e5;return Math.max(1,n)*c}function P(t,e,r=X){let o=Z(t),n=o.toLowerCase();return n.includes("networkerror")||n.includes("failed to fetch")||n.includes("load failed")||n.includes("fetch failed")||n.includes("the network connection was lost")||n.includes("network request failed")?r(e):o}function k(t,e){let r=new Error(e);return r.code=t,r}function Ot(t){var u,a;let e=t.accessToken.trim(),r=t.branchId.trim();if(!e||!r)return null;let o=J(t.baseUrl).replace(/^http:\/\//i,"ws://").replace(/^https:\/\//i,"wss://"),n={token:e,branchId:r},i=String((u=t.tenantId)!=null?u:"").trim(),c=String((a=t.domainId)!=null?a:"").trim();return i&&(n.tenantId=i),c&&(n.domainId=c),{url:`${o}/ws/sync-notify`,query:n}}async function kt(t){var n,i,c,u;if(t.bearerToken.length>0)return t.bearerToken;let e=(n=t.nowMs)!=null?n:Date.now(),r=(i=t.refreshLeewayMs)!=null?i:1e4;if((c=t.session)!=null&&c.accessToken){if(t.session.expiresAtMs>e+r)return t.session.accessToken;let a=await t.refresh();if(a!=null&&a.accessToken)return a.accessToken}let o=await t.loginFromCredentials();return(u=o==null?void 0:o.accessToken)!=null?u:""}function wt(t){var u,a,s;let e=(u=t.fetchImpl)!=null?u:fetch,r=J(t.baseUrl),o=(a=t.accessTokenFallbackTtlMs)!=null?a:9e5,n=(s=t.networkErrorMessage)!=null?s:X,i=d=>(Array.isArray(d.scopes)?d.scopes:[]).filter(l=>!!(l&&typeof l=="object")).map(l=>{var y,f,g,S;return{tenantId:typeof l.tenantId=="string"&&l.tenantId.trim().length>0?l.tenantId:null,branchId:String((y=l.branchId)!=null?y:"").trim(),role:String((f=l.role)!=null?f:""),userId:String((g=l.userId)!=null?g:""),email:String((S=l.email)!=null?S:"")}}).filter(l=>l.branchId.length>0),c=(d,h)=>{var f,g,S;let l=String((g=(f=d.accessToken)!=null?f:d.token)!=null?g:""),y=String((S=d.refreshToken)!=null?S:"");if(!l||!y)throw k("SYNC_AUTH_TOKEN_INCOMPLETE","Sync auth gagal: token tidak lengkap.");return{accessToken:l,refreshToken:y,expiresAtMs:Date.now()+tt(d.expiresIn,o),principal:h}};return{async fetchScopeOptions(d,h){var l,y,f;if(!r)return[];try{let g=await e(`${r}/api/v1/auth/scope-options`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:d,password:h})}),S=await g.json().catch(()=>({}));if(!g.ok){let m=(l=S.error)!=null?l:{};throw k(String((y=m.code)!=null?y:`HTTP_${g.status}`),String((f=m.message)!=null?f:`Sync scope discovery gagal (${g.status})`))}return i(S)}catch(g){let S=g;throw k(typeof(S==null?void 0:S.code)=="string"?S.code:"SYNC_AUTH_SCOPE_DISCOVERY_FAILED",P(g,r,n))}},async login(d,h,l){var g,S,m,w,R;let y=String((g=l.branchId)!=null?g:"").trim(),f=String((S=l.tenantId)!=null?S:"").trim();try{let b=await e(`${r}/api/v1/auth/login`,{method:"POST",headers:{"Content-Type":"application/json","X-Branch-ID":y,...f?{"X-Tenant-ID":f}:{},"X-Device-ID":t.deviceId},body:JSON.stringify({username:d,password:h})}),C=await b.json().catch(()=>({}));if(!b.ok){let T=(m=C.error)!=null?m:{};throw k(String((w=T.code)!=null?w:`HTTP_${b.status}`),String((R=T.message)!=null?R:`Sync auth login gagal (${b.status})`))}return c(C,d)}catch(b){let C=b;throw k(typeof(C==null?void 0:C.code)=="string"?C.code:"SYNC_AUTH_FAILED",P(b,r,n))}},async refresh(d,h){var l,y,f;try{let g=await e(`${r}/api/v1/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json",...h?{"X-Tenant-ID":h}:{},"X-Device-ID":t.deviceId},body:JSON.stringify({refreshToken:d})}),S=await g.json().catch(()=>({}));if(!g.ok){let m=(l=S.error)!=null?l:{};throw k(String((y=m.code)!=null?y:`HTTP_${g.status}`),String((f=m.message)!=null?f:`Sync auth refresh gagal (${g.status})`))}return c(S,"")}catch(g){let S=g;throw k(typeof(S==null?void 0:S.code)=="string"?S.code:"SYNC_AUTH_REFRESH_FAILED",P(g,r,n))}}}}function It(){let t=new Set;return{track(e){let r=Promise.resolve(e).finally(()=>{t.delete(r)});return t.add(r),r},async waitForIdle(){for(;t.size>0;)await Promise.allSettled([...t])},size(){return t.size},clear(){t.clear()}}}async function At(t){if(!t)return 0;let e=await t.getOutboxState();return Number(e.pending||0)+Number(e.failed||0)}async function Et(t){var i,c,u,a;let e=(i=t.triggerInitialPull)!=null?i:(async s=>{await O(s).trigger("manual")});if(!t.runtimeReady||!t.serverSyncEnabled||!t.isAuthenticated){if(t.lastStartSignatureRef.current="",(c=t.setWsConnected)==null||c.call(t,!1),(u=t.setSyncing)==null||u.call(t,!1),!t.runtime)return;await O(t.runtime).stop().catch(()=>{});return}let r=t.runtime;if(r){let s=O(r).getState();if(s.running&&t.lastStartSignatureRef.current===t.startSignature)return;s.running&&t.lastStartSignatureRef.current!==t.startSignature&&await O(r).stop().catch(()=>{})}let o=await t.ensureOrchestratorStarted();O(o).getState().running&&t.lastStartSignatureRef.current===t.startSignature||(await e(o),t.lastStartSignatureRef.current=t.startSignature,(a=t.setLastError)==null||a.call(t,""))}async function Pt(t){var o,n,i,c,u,a,s,d,h,l,y,f,g,S,m,w,R,b,C,T,F,H,$,z,j,K,Y,q;let e=(o=t.triggerManualSync)!=null?o:(async p=>{await O(p).trigger("manual")});if((n=t.setStage)==null||n.call(t,"check-syncing"),t.runtime.getOrchestratorState().syncing)return{ok:!0,message:(i=t.inProgressMessage)!=null?i:"Sinkronisasi sedang berjalan."};if((c=t.setStage)==null||c.call(t,"check-base-url"),t.serverSyncBaseUrl.length===0){let p=(u=t.missingBaseUrlMessage)!=null?u:"VITE_OFSYNC_BASE_URL belum diatur.";return t.silent||t.setLastError(p),{ok:!1,message:p}}if((a=t.setStage)==null||a.call(t,"resolve-access-token"),!(await t.resolveAccessToken()).trim()){let p=(s=t.missingAccessTokenMessage)!=null?s:"Sesi sinkronisasi tidak valid. Login ulang diperlukan.";return t.silent||t.setLastError(p),t.setAuthBlocked(!0),{ok:!1,message:p}}t.setLastError("");try{(d=t.setStage)==null||d.call(t,"ensure-orchestrator-started"),await t.ensureOrchestratorStarted(),(h=t.setStage)==null||h.call(t,"before-trigger"),await((l=t.beforeTrigger)==null?void 0:l.call(t)),(y=t.setStage)==null||y.call(t,"wait-pending-operations"),await((f=t.waitForPendingOperations)==null?void 0:f.call(t)),(g=t.setStage)==null||g.call(t,"refresh-queue-length:before-trigger"),await((S=t.refreshQueueLength)==null?void 0:S.call(t));try{(m=t.setStage)==null||m.call(t,"trigger-manual"),await e(t.runtime)}catch(p){if((p instanceof Error?p.message:String(p))!=="Cannot start sync before runtime is started")throw p;(w=t.setStage)==null||w.call(t,"ensure-orchestrator-started:retry"),await t.ensureOrchestratorStarted(),(R=t.setStage)==null||R.call(t,"before-trigger:retry"),await((b=t.beforeTrigger)==null?void 0:b.call(t)),(C=t.setStage)==null||C.call(t,"trigger-manual:retry"),await e(t.runtime)}return(T=t.setStage)==null||T.call(t,"refresh-queue-length:after-trigger"),await((F=t.refreshQueueLength)==null?void 0:F.call(t)),(H=t.setStage)==null||H.call(t,"refresh-projection-ui"),await(($=t.refreshProjectionUi)==null?void 0:$.call(t)),(z=t.setStage)==null||z.call(t,"on-success"),await((j=t.onSuccess)==null?void 0:j.call(t)),t.setAuthBlocked(!1),(K=t.setStage)==null||K.call(t,"completed"),{ok:!0,message:"Sinkronisasi selesai."}}catch(p){(Y=t.onRawError)==null||Y.call(t,p instanceof Error?p.message:String(p));let v=P(p,t.serverSyncBaseUrl),et=v.toLowerCase().includes("unauthorized")||v.toLowerCase().includes("sesi sinkronisasi tidak valid")||v.includes("401");return t.silent&&t.isConnectivityError(v)?{ok:!1,message:v}:(t.setLastError(v),t.setAuthBlocked(et),(q=t.setRuntimeError)==null||q.call(t,v),{ok:!1,message:v})}}function Rt(t){let e=t.store.getState().syncing;return t.store.subscribe(r=>{var c,u,a,s,d,h,l,y,f,g,S,m;let o=e;if(e=r.syncing,(c=t.setSyncing)==null||c.call(t,r.syncing),(u=t.setWsConnected)==null||u.call(t,r.wsConnected),!r.lastError){if(o&&!r.syncing){let w=(s=(a=t.getPullAppliedCount)==null?void 0:a.call(t))!=null?s:0;(d=t.resetPullAppliedCount)==null||d.call(t),(h=t.onSuccessfulIdle)==null||h.call(t,{appliedCount:w,state:r})}t.setLastError(""),t.setAuthBlocked(!1),(l=t.clearConnectivityError)==null||l.call(t);return}let n=((y=r.lastErrorCode)==null?void 0:y.startsWith("AUTH_"))||r.lastError.toLowerCase().includes("unauthorized")||r.lastError.includes("401");((g=(f=t.shouldSuppressConnectivityError)==null?void 0:f.call(t,r))!=null?g:r.lastTriggerReason!=="manual"&&t.isConnectivityError(r.lastError))||(r.syncing||(S=t.resetPullAppliedCount)==null||S.call(t),t.setLastError(r.lastError),t.setAuthBlocked(!!n),(m=t.onError)==null||m.call(t,{message:r.lastError,isAuthError:!!n,state:r}))})}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofsync-shared-core",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Host-side offline-first sync orchestration core for multi-domain of* apps.",
|
|
6
6
|
"author": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"bundle": "node esbuild.config.js"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"ofcore": "0.2.0-alpha.
|
|
38
|
+
"ofcore": "0.2.0-alpha.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"esbuild": "^0.25.11",
|