ofsync-shared-core 0.2.0-alpha.1 → 0.2.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/OfsyncCore.d.ts +44 -2
- package/dist/adapters/OfsyncHttpTransport.d.ts +2 -3
- package/dist/api.d.ts +11 -1
- package/dist/auth/OfsyncAuthClient.d.ts +13 -0
- package/dist/contracts/SyncContract.d.ts +7 -1
- package/dist/host/HostSyncOrchestration.d.ts +79 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -1
- package/dist/services/SyncTransport.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,3 +26,4 @@ This package standardizes offline-first synchronization flow for multi-domain ho
|
|
|
26
26
|
## Documentation
|
|
27
27
|
- Documentation hub: `docs/00-DOCUMENTATION-HUB.md`
|
|
28
28
|
- Architecture intent: `docs/10-ARCHITECTURE-INTENT.md`
|
|
29
|
+
- Sync orchestrator contract: `docs/30-SYNC-ORCHESTRATOR-CONTRACT-V1.md`
|
package/dist/OfsyncCore.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActivitySink, CoreAdapterRegistry, CoreRuntime, CoreRuntimeStartOptions, HttpAdapter, LoggerAdapter, PlatformAdapter, SocketAdapter, DbAdapter } from 'ofcore';
|
|
1
|
+
import type { ActivitySink, CoreAdapterRegistry, CoreRuntime, CoreRuntimeStartOptions, HttpAdapter, LoggerAdapter, PlatformAdapter, SocketConnectOptions, SocketAdapter, DbAdapter } from 'ofcore';
|
|
2
2
|
import type { ReadonlyStore, Store } from 'ofcore';
|
|
3
3
|
import type { OutboxItem, SyncChange, SyncPolicy, SyncScope } from './contracts/SyncContract';
|
|
4
4
|
import type { DomainBridge } from './services/DomainBridge';
|
|
@@ -21,6 +21,30 @@ export interface OfsyncOutboxState {
|
|
|
21
21
|
failed: number;
|
|
22
22
|
total: number;
|
|
23
23
|
}
|
|
24
|
+
export type SyncOrchestratorTriggerReason = 'manual' | 'notify' | 'interval' | 'online';
|
|
25
|
+
export interface SyncOrchestratorState {
|
|
26
|
+
running: boolean;
|
|
27
|
+
syncing: boolean;
|
|
28
|
+
wsConnected: boolean;
|
|
29
|
+
lastSyncAt: string | null;
|
|
30
|
+
lastTriggerReason: SyncOrchestratorTriggerReason | null;
|
|
31
|
+
lastError: string | null;
|
|
32
|
+
lastErrorCode: string | null;
|
|
33
|
+
}
|
|
34
|
+
export interface SyncOrchestratorSocketOptions {
|
|
35
|
+
enabled?: boolean;
|
|
36
|
+
eventName?: string;
|
|
37
|
+
connectOptions?: SocketConnectOptions | null;
|
|
38
|
+
resolveConnectOptions?: () => Promise<SocketConnectOptions | null> | SocketConnectOptions | null;
|
|
39
|
+
}
|
|
40
|
+
export interface SyncOrchestratorStartOptions {
|
|
41
|
+
scope: SyncScope;
|
|
42
|
+
auto?: {
|
|
43
|
+
enabled?: boolean;
|
|
44
|
+
intervalMs?: number;
|
|
45
|
+
};
|
|
46
|
+
socket?: SyncOrchestratorSocketOptions;
|
|
47
|
+
}
|
|
24
48
|
export interface OfsyncCoreOptions {
|
|
25
49
|
policy?: Partial<SyncPolicy>;
|
|
26
50
|
transport?: SyncTransport;
|
|
@@ -28,10 +52,12 @@ export interface OfsyncCoreOptions {
|
|
|
28
52
|
checkpointStore?: CheckpointStore;
|
|
29
53
|
conflictResolvers?: ConflictResolver[];
|
|
30
54
|
projectionHook?: (change: SyncChange, scope: SyncScope) => Promise<void> | void;
|
|
55
|
+
socketAdapterFactory?: (core: CoreRuntime<any, any>) => SocketAdapter;
|
|
31
56
|
}
|
|
32
57
|
export declare class OfsyncCore {
|
|
33
58
|
private readonly runtime;
|
|
34
59
|
private readonly runtimeStateStore;
|
|
60
|
+
private readonly orchestratorStateStore;
|
|
35
61
|
private readonly bridges;
|
|
36
62
|
private readonly policy;
|
|
37
63
|
private readonly transport?;
|
|
@@ -40,8 +66,15 @@ export declare class OfsyncCore {
|
|
|
40
66
|
private readonly conflictResolvers;
|
|
41
67
|
private readonly projectionHook?;
|
|
42
68
|
private schedulerTimer?;
|
|
69
|
+
private orchestratorTimer?;
|
|
70
|
+
private orchestratorSocketHealthTimer?;
|
|
71
|
+
private orchestratorScope;
|
|
72
|
+
private orchestratorSocketUnsubscribe;
|
|
73
|
+
private orchestratorSocketConnectedByCore;
|
|
74
|
+
private orchestratorSyncPromise;
|
|
43
75
|
readonly runtimeStore: ReadonlyStore<OfsyncRuntimeState>;
|
|
44
|
-
|
|
76
|
+
readonly orchestratorStore: ReadonlyStore<SyncOrchestratorState>;
|
|
77
|
+
constructor(runtime: CoreRuntime<never, never>, runtimeStateStore: Store<OfsyncRuntimeState>, orchestratorStateStore: Store<SyncOrchestratorState>, options?: OfsyncCoreOptions);
|
|
45
78
|
static builder(): OfsyncCoreBuilder;
|
|
46
79
|
get registry(): CoreAdapterRegistry | undefined;
|
|
47
80
|
getPolicy(): SyncPolicy;
|
|
@@ -57,7 +90,16 @@ export declare class OfsyncCore {
|
|
|
57
90
|
startScheduler(scope: SyncScope): void;
|
|
58
91
|
stopScheduler(): void;
|
|
59
92
|
notifyOnline(scope: SyncScope): Promise<void>;
|
|
93
|
+
getOrchestratorState(): SyncOrchestratorState;
|
|
94
|
+
startOrchestrator(options: SyncOrchestratorStartOptions): Promise<void>;
|
|
95
|
+
stopOrchestrator(): Promise<void>;
|
|
96
|
+
triggerSync(reason?: SyncOrchestratorTriggerReason): Promise<void>;
|
|
97
|
+
private configureOrchestratorSocket;
|
|
98
|
+
private configureOrchestratorInterval;
|
|
99
|
+
private startOrchestratorSocketHealthMonitor;
|
|
100
|
+
private readSocketConnectedState;
|
|
60
101
|
private processOutbox;
|
|
102
|
+
private collectBridgeLocalChanges;
|
|
61
103
|
private applyRemoteChanges;
|
|
62
104
|
private resolveConflicts;
|
|
63
105
|
}
|
|
@@ -6,7 +6,6 @@ export interface OfsyncHttpTransportOptions {
|
|
|
6
6
|
httpAdapter: HttpAdapter;
|
|
7
7
|
getAccessToken?: () => string | null | undefined | Promise<string | null | undefined>;
|
|
8
8
|
nowIso?: () => string;
|
|
9
|
-
allowGenericFallback?: boolean;
|
|
10
9
|
}
|
|
11
10
|
export declare class OfsyncTransportError extends Error {
|
|
12
11
|
readonly code: string;
|
|
@@ -24,8 +23,6 @@ export declare class OfsyncHttpTransport implements SyncTransport {
|
|
|
24
23
|
private normalizeUrl;
|
|
25
24
|
private normalizeDomainPath;
|
|
26
25
|
private resolveDomainPathOrThrow;
|
|
27
|
-
private isGenericFallbackEnabled;
|
|
28
|
-
private requestWithDomainPolicy;
|
|
29
26
|
private parseFailure;
|
|
30
27
|
push(params: {
|
|
31
28
|
scope: SyncScope;
|
|
@@ -40,9 +37,11 @@ export declare class OfsyncHttpTransport implements SyncTransport {
|
|
|
40
37
|
}): Promise<SyncPullResult>;
|
|
41
38
|
listConflicts(params: {
|
|
42
39
|
scope: SyncScope;
|
|
40
|
+
domain?: string;
|
|
43
41
|
}): Promise<ConflictItem[]>;
|
|
44
42
|
resolveConflicts(params: {
|
|
45
43
|
scope: SyncScope;
|
|
44
|
+
domain?: string;
|
|
46
45
|
resolutions: Array<{
|
|
47
46
|
conflictId: string;
|
|
48
47
|
action: 'APPLY_REMOTE' | 'KEEP_LOCAL' | 'MANUAL_MERGE';
|
package/dist/api.d.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import type { CoreRuntimeStartOptions } from 'ofcore';
|
|
2
2
|
import { OfsyncCore } from './OfsyncCore';
|
|
3
|
-
import type { OfsyncCoreOptions } from './OfsyncCore';
|
|
3
|
+
import type { OfsyncCoreOptions, SyncOrchestratorStartOptions, SyncOrchestratorTriggerReason } from './OfsyncCore';
|
|
4
4
|
import type { DomainBridge } from './services/DomainBridge';
|
|
5
5
|
import type { SyncScope } from './contracts/SyncContract';
|
|
6
|
+
import type { ReadonlyStore } from 'ofcore';
|
|
7
|
+
import type { SyncOrchestratorState } from './OfsyncCore';
|
|
8
|
+
export interface SyncOrchestratorController {
|
|
9
|
+
readonly store: ReadonlyStore<SyncOrchestratorState>;
|
|
10
|
+
getState(): SyncOrchestratorState;
|
|
11
|
+
start(options: SyncOrchestratorStartOptions): Promise<void>;
|
|
12
|
+
stop(): Promise<void>;
|
|
13
|
+
trigger(reason?: SyncOrchestratorTriggerReason): Promise<void>;
|
|
14
|
+
}
|
|
6
15
|
export declare function createSyncRuntime(options?: OfsyncCoreOptions): OfsyncCore;
|
|
7
16
|
export declare function registerDomainBridge(runtime: OfsyncCore, bridge: DomainBridge): void;
|
|
8
17
|
export declare function startSync(runtime: OfsyncCore, scope: SyncScope, runtimeStartOptions?: CoreRuntimeStartOptions): Promise<void>;
|
|
9
18
|
export declare function stopSync(runtime: OfsyncCore): Promise<void>;
|
|
19
|
+
export declare function getSyncOrchestrator(runtime: OfsyncCore): SyncOrchestratorController;
|
|
@@ -30,11 +30,24 @@ export interface ResolveSyncAccessTokenInput {
|
|
|
30
30
|
refresh: () => Promise<OfsyncAuthSession | null>;
|
|
31
31
|
loginFromCredentials: () => Promise<OfsyncAuthSession | null>;
|
|
32
32
|
}
|
|
33
|
+
export interface SyncNotifySocketConnectOptions {
|
|
34
|
+
url: string;
|
|
35
|
+
query: Record<string, string>;
|
|
36
|
+
}
|
|
37
|
+
export interface BuildSyncNotifySocketConnectOptionsInput {
|
|
38
|
+
baseUrl: string;
|
|
39
|
+
accessToken: string;
|
|
40
|
+
branchId: string;
|
|
41
|
+
tenantId?: string | null;
|
|
42
|
+
domainId?: string | null;
|
|
43
|
+
}
|
|
44
|
+
export declare function isSyncConnectivityRuntimeError(message: string): boolean;
|
|
33
45
|
export declare function parseSyncDurationMs(raw: unknown, fallbackMs?: number): number;
|
|
34
46
|
export declare function normalizeSyncNetworkError(error: unknown, baseUrl: string, messageBuilder?: (baseUrl: string) => string): string;
|
|
35
47
|
export declare function createSyncAuthError(code: string, message: string): Error & {
|
|
36
48
|
code: string;
|
|
37
49
|
};
|
|
50
|
+
export declare function buildSyncNotifySocketConnectOptions(input: BuildSyncNotifySocketConnectOptionsInput): SyncNotifySocketConnectOptions | null;
|
|
38
51
|
export declare function resolveSyncAccessToken(input: ResolveSyncAccessTokenInput): Promise<string>;
|
|
39
52
|
export declare function createOfsyncAuthClient(options: OfsyncAuthClientOptions): {
|
|
40
53
|
fetchScopeOptions(username: string, password: string): Promise<OfsyncScopeOption[]>;
|
|
@@ -2,12 +2,18 @@ import type { ScopeRef } from 'ofcore';
|
|
|
2
2
|
/**
|
|
3
3
|
* Sync scope — extends ScopeRef dari ofcore.
|
|
4
4
|
* Tambahan: `deviceId` untuk multi-device offline sync,
|
|
5
|
-
* `ledgerProfileId` untuk isolasi buku besar lintas-branch (finance use-case)
|
|
5
|
+
* `ledgerProfileId` untuk isolasi buku besar lintas-branch (finance use-case),
|
|
6
|
+
* `sourceDomainId` untuk menjelaskan domain bisnis asal row finance tenant-scoped,
|
|
7
|
+
* `productFamilyId` untuk membawa entitlement context canonical lintas host.
|
|
6
8
|
*/
|
|
7
9
|
export interface SyncScope extends ScopeRef {
|
|
8
10
|
deviceId?: string;
|
|
9
11
|
/** ID ledger profile untuk isolasi finance journal lintas branch. */
|
|
10
12
|
ledgerProfileId?: string;
|
|
13
|
+
/** Domain bisnis asal perubahan finance, mis. ofpos/ofcoop/ofcoop-credit/offinance. */
|
|
14
|
+
sourceDomainId?: string;
|
|
15
|
+
/** Product family canonical untuk entitlement context sync, mis. POS_SUITE/KSP_SUITE. */
|
|
16
|
+
productFamilyId?: string;
|
|
11
17
|
}
|
|
12
18
|
export type SyncChangeType = 'CREATE' | 'UPDATE' | 'DELETE';
|
|
13
19
|
export interface SyncChange {
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { OfsyncCore, SyncOrchestratorState } from '../OfsyncCore';
|
|
2
|
+
type MutableValueRef<T> = {
|
|
3
|
+
current: T;
|
|
4
|
+
};
|
|
5
|
+
export interface PendingSyncOperationTracker {
|
|
6
|
+
track<T>(promise: Promise<T>): Promise<T>;
|
|
7
|
+
waitForIdle(): Promise<void>;
|
|
8
|
+
size(): number;
|
|
9
|
+
clear(): void;
|
|
10
|
+
}
|
|
11
|
+
export declare function createPendingSyncOperationTracker(): PendingSyncOperationTracker;
|
|
12
|
+
export declare function readSyncOutboxQueueLength(runtime: Pick<OfsyncCore, 'getOutboxState'> | null | undefined): Promise<number>;
|
|
13
|
+
type EnsureHostSyncOrchestratorLifecycleParams = {
|
|
14
|
+
runtimeReady: boolean;
|
|
15
|
+
serverSyncEnabled: boolean;
|
|
16
|
+
isAuthenticated: boolean;
|
|
17
|
+
runtime: OfsyncCore | null | undefined;
|
|
18
|
+
startSignature: string;
|
|
19
|
+
lastStartSignatureRef: MutableValueRef<string>;
|
|
20
|
+
ensureOrchestratorStarted: () => Promise<OfsyncCore>;
|
|
21
|
+
setSyncing?: (value: boolean) => void;
|
|
22
|
+
setWsConnected?: (value: boolean) => void;
|
|
23
|
+
setLastError?: (value: string) => void;
|
|
24
|
+
triggerInitialPull?: (runtime: OfsyncCore) => Promise<void>;
|
|
25
|
+
};
|
|
26
|
+
export declare function ensureHostSyncOrchestratorLifecycle(params: EnsureHostSyncOrchestratorLifecycleParams): Promise<void>;
|
|
27
|
+
type RunManualSyncCycleParams = {
|
|
28
|
+
silent: boolean;
|
|
29
|
+
runtime: OfsyncCore;
|
|
30
|
+
serverSyncBaseUrl: string;
|
|
31
|
+
resolveAccessToken: () => Promise<string>;
|
|
32
|
+
ensureOrchestratorStarted: () => Promise<OfsyncCore>;
|
|
33
|
+
triggerManualSync?: (runtime: OfsyncCore) => Promise<void>;
|
|
34
|
+
waitForPendingOperations?: () => Promise<void>;
|
|
35
|
+
beforeTrigger?: () => Promise<void>;
|
|
36
|
+
refreshQueueLength?: () => Promise<void>;
|
|
37
|
+
refreshProjectionUi?: () => Promise<void>;
|
|
38
|
+
isConnectivityError: (message: string) => boolean;
|
|
39
|
+
setLastError: (value: string) => void;
|
|
40
|
+
setAuthBlocked: (value: boolean) => void;
|
|
41
|
+
setRuntimeError?: (value: string) => void;
|
|
42
|
+
onSuccess?: () => Promise<void> | void;
|
|
43
|
+
setStage?: (value: string) => void;
|
|
44
|
+
onRawError?: (value: string) => void;
|
|
45
|
+
inProgressMessage?: string;
|
|
46
|
+
missingBaseUrlMessage?: string;
|
|
47
|
+
missingAccessTokenMessage?: string;
|
|
48
|
+
};
|
|
49
|
+
export declare function runManualSyncCycle(params: RunManualSyncCycleParams): Promise<{
|
|
50
|
+
ok: boolean;
|
|
51
|
+
message: string;
|
|
52
|
+
}>;
|
|
53
|
+
type SyncOrchestratorStoreLike = {
|
|
54
|
+
getState(): SyncOrchestratorState;
|
|
55
|
+
subscribe(listener: (state: SyncOrchestratorState) => void): () => void;
|
|
56
|
+
};
|
|
57
|
+
type SubscribeToSyncOrchestratorLifecycleParams = {
|
|
58
|
+
store: SyncOrchestratorStoreLike;
|
|
59
|
+
isConnectivityError: (message: string) => boolean;
|
|
60
|
+
setSyncing?: (value: boolean) => void;
|
|
61
|
+
setWsConnected?: (value: boolean) => void;
|
|
62
|
+
setLastError: (value: string) => void;
|
|
63
|
+
setAuthBlocked: (value: boolean) => void;
|
|
64
|
+
clearConnectivityError?: () => void;
|
|
65
|
+
getPullAppliedCount?: () => number;
|
|
66
|
+
resetPullAppliedCount?: () => void;
|
|
67
|
+
onSuccessfulIdle?: (params: {
|
|
68
|
+
appliedCount: number;
|
|
69
|
+
state: SyncOrchestratorState;
|
|
70
|
+
}) => Promise<void> | void;
|
|
71
|
+
onError?: (params: {
|
|
72
|
+
message: string;
|
|
73
|
+
isAuthError: boolean;
|
|
74
|
+
state: SyncOrchestratorState;
|
|
75
|
+
}) => Promise<void> | void;
|
|
76
|
+
shouldSuppressConnectivityError?: (state: SyncOrchestratorState) => boolean;
|
|
77
|
+
};
|
|
78
|
+
export declare function subscribeToSyncOrchestratorLifecycle(params: SubscribeToSyncOrchestratorLifecycleParams): () => void;
|
|
79
|
+
export {};
|
package/dist/index.d.ts
CHANGED
package/dist/index.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var A={batchSize:200,maxRetry:5,scheduler:{mode:"manual",onlineTrigger:!0},retryBackoff:{strategy:"exponential-jitter",baseDelayMs:500,maxDelayMs:3e4}};var O=class{constructor(){this.rows=new Map}async enqueue(t){this.rows.set(t.outboxId,t)}async listPending(t){return[...this.rows.values()].filter(n=>n.status==="PENDING"||n.status==="FAILED").sort((n,r)=>n.createdAt.localeCompare(r.createdAt)).slice(0,Math.max(0,t))}async findByIdempotencyKey(t){for(let n of this.rows.values())if(n.idempotencyKey===t)return n;return null}async listAll(){return[...this.rows.values()].sort((t,n)=>t.createdAt.localeCompare(n.createdAt))}async update(t){this.rows.set(t.outboxId,t)}async remove(t){this.rows.delete(t)}};var k=class{constructor(){this.rows=new Map}async get(t){return this.rows.has(t)?this.rows.get(t):null}async set(t,n){this.rows.set(t,n)}};function _(e){return e.code==="VERSION_CONFLICT"?{conflictId:e.conflictId,action:"APPLY_REMOTE"}:e.code==="SCOPE_CONFLICT"?{conflictId:e.conflictId,action:"MANUAL_MERGE"}:{conflictId:e.conflictId,action:"KEEP_LOCAL"}}var tt="phase1-runtime-skeleton";import{CoreRuntime as M}from"ofcore";import{InMemoryDbAdapter as B}from"ofcore";import{asReadonlyStore as F,createStore as U}from"ofcore";function H(e={}){return{...A,...e,scheduler:{...A.scheduler,...e.scheduler||{}},retryBackoff:{...A.retryBackoff,...e.retryBackoff||{}}}}function $(e){return`${e.tenantId||"__standalone__"}::${e.branchId||"__all__"}::${e.ledgerProfileId||"__default_ledger__"}::${e.deviceId||"__device__"}`}function j(e,t){return`${$(e)}::${t}`}function K(e){if(typeof e!="string"||e.trim().length===0)return null;let t=Date.parse(e);return Number.isFinite(t)?new Date(t+5e3).toISOString():null}var I=class{constructor(t,n,r={}){this.runtime=t;this.runtimeStateStore=n;this.bridges=new Map;this.runtimeStore=F(this.runtimeStateStore),this.policy=H(r.policy),this.transport=r.transport,this.outboxStore=r.outboxStore||new O,this.checkpointStore=r.checkpointStore||new k,this.conflictResolvers=r.conflictResolvers||[],this.projectionHook=r.projectionHook}static builder(){return new x}get registry(){return this.runtime.registry}getPolicy(){return this.policy}registerDomainBridge(t){let n=t.domain.trim();if(!n)throw new Error("DomainBridge.domain is required");if(this.bridges.has(n))throw new Error(`DomainBridge for domain "${n}" is already registered`);this.bridges.set(n,t)}listDomainBridges(){return[...this.bridges.keys()].sort()}async getOutboxState(){let t=await this.outboxStore.listAll(),n=t.filter(i=>i.status==="PENDING").length,r=t.filter(i=>i.status==="FAILED").length;return{pending:n,failed:r,total:t.length}}async enqueueLocalChange(t,n){let r=(n||`${t.domain}:${t.entity}:${t.recordId}:${t.type}:${t.id}`).trim();if(!r)throw new Error("idempotencyKey must not be empty");let i=await this.outboxStore.findByIdempotencyKey(r);if(i)return i;let a=new Date().toISOString(),c={outboxId:`outbox_${Date.now()}_${Math.random().toString(36).slice(2)}`,idempotencyKey:r,change:t,retryCount:0,status:"PENDING",createdAt:a,updatedAt:a};return await this.outboxStore.enqueue(c),c}async start(t={}){this.runtimeStateStore.setState({phase:"starting",started:!1,syncing:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.start(t),this.runtimeStateStore.setState(n=>({...n,phase:"started",started:!0,syncing:!1,startCount:n.startCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(n){throw this.runtimeStateStore.setState({phase:"error",started:!1,syncing:!1,lastError:n instanceof Error?n.message:String(n),lastTransitionAt:new Date().toISOString()}),n}}async stop(){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(t=>({...t,phase:"stopped",started:!1,syncing:!1,stopCount:t.stopCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(t){throw this.runtimeStateStore.setState({phase:"error",started:this.runtime.isStarted(),syncing:!1,lastError:t instanceof Error?t.message:String(t),lastTransitionAt:new Date().toISOString()}),t}}async startSync(t){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.processOutbox(t);let n=this.listDomainBridges();for(let r of n){let i=j(t,r),a=await this.checkpointStore.get(i),c;try{c=await this.transport.pull({scope:t,cursor:a,limit:this.policy.batchSize,domain:r})}catch(u){let o=(u==null?void 0:u.code)||"",s=u==null?void 0:u.details,p=K(s==null?void 0:s.cutoffTime);if(o!=="SYNC_RESYNC_REQUIRED"||!p)throw u;await this.checkpointStore.set(i,p),a=p,c=await this.transport.pull({scope:t,cursor:a,limit:this.policy.batchSize,domain:r})}let l=(c.changes||[]).map(u=>{let o=String(u.domain||"").trim().toLowerCase();if(!o)return{...u,domain:r};if(o!==r)throw new Error(`Sync pull domain mismatch: expected ${r}, received ${o}`);return u});await this.applyRemoteChanges(l,t),c.nextCursor!==void 0&&await this.checkpointStore.set(i,c.nextCursor)}}this.runtimeStateStore.setState(n=>({...n,phase:"started",started:!0,syncing:!1,syncCount:n.syncCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(n){throw this.runtimeStateStore.setState({phase:"error",started:this.runtime.isStarted(),syncing:!1,lastError:n instanceof Error?n.message:String(n),lastTransitionAt:new Date().toISOString()}),n}}async stopSync(){this.runtimeStateStore.setState(t=>({...t,phase:this.runtime.isStarted()?"started":"stopped",syncing:!1,lastTransitionAt:new Date().toISOString()}))}isStarted(){return this.runtime.isStarted()}startScheduler(t){if(this.policy.scheduler.mode!=="interval")return;let n=this.policy.scheduler.intervalMs||1e4;this.stopScheduler(),this.schedulerTimer=setInterval(()=>{this.startSync(t).catch(r=>{this.runtimeStateStore.setState(i=>({...i,phase:"error",syncing:!1,lastError:r instanceof Error?r.message:String(r),lastTransitionAt:new Date().toISOString()}))})},n)}stopScheduler(){this.schedulerTimer&&(clearInterval(this.schedulerTimer),this.schedulerTimer=void 0)}async notifyOnline(t){this.policy.scheduler.onlineTrigger!==!1&&await this.startSync(t)}async processOutbox(t){var i;if(!this.transport)return;let n=await this.outboxStore.listPending(this.policy.batchSize);if(n.length===0)return;let r=new Map;for(let a of n){let c=String(a.change.domain||"").trim().toLowerCase()||"__unknown__";r.has(c)||r.set(c,[]),(i=r.get(c))==null||i.push(a)}for(let[a,c]of r.entries()){if(a==="__unknown__"){for(let p of c){let d=p.retryCount+1;await this.outboxStore.update({...p,retryCount:d,status:d>=this.policy.maxRetry?"FAILED":"PENDING",lastError:"DOMAIN_REQUIRED",updatedAt:new Date().toISOString()})}continue}let l=await this.transport.push({scope:t,outbox:c,domain:a}),u=new Set((l.applied||[]).map(p=>p.changeId)),o=new Map((l.failed||[]).map(p=>[p.changeId,p])),s=new Set((l.conflicts||[]).map(p=>p.changeId));await this.resolveConflicts(l.conflicts||[],t);for(let p of c){if(u.has(p.change.id)){await this.outboxStore.remove(p.outboxId);continue}let d=o.get(p.change.id);if(d||s.has(p.change.id)){let g=p.retryCount+1;await this.outboxStore.update({...p,retryCount:g,status:g>=this.policy.maxRetry?"FAILED":"PENDING",lastError:(d==null?void 0:d.message)||(s.has(p.change.id)?"SYNC_CONFLICT":p.lastError),updatedAt:new Date().toISOString()})}}}}async applyRemoteChanges(t,n){if(!Array.isArray(t)||t.length===0)return;let r=[...t].sort((a,c)=>a.occurredAt.localeCompare(c.occurredAt)),i=new Map;for(let a of r){i.has(a.domain)||i.set(a.domain,[]);let c=i.get(a.domain);c&&c.push(a)}for(let[a,c]of i.entries()){let l=this.bridges.get(a);if(l!=null&&l.applyRemoteChanges&&(await l.applyRemoteChanges(c,n),this.projectionHook))for(let u of c)await this.projectionHook(u,n)}}async resolveConflicts(t,n){var i;if(!((i=this.transport)!=null&&i.resolveConflicts)||t.length===0)return;let r=[];for(let a of t){let c=[...this.conflictResolvers],l=null;for(let u of c){let o=await u(a,{scope:n});if(o){l=o;break}}l||(l=_(a)),r.push(l)}r.length>0&&await this.transport.resolveConflicts({scope:n,resolutions:r})}},x=class{constructor(){this.runtimeBuilder=M.builder();this.options={};this.runtimeBuilder.withDbAdapter(()=>new B)}withPlatformAdapter(t){return this.runtimeBuilder.withPlatformAdapter(t),this}withDbAdapter(t){return this.runtimeBuilder.withDbAdapter(t),this}withHttpAdapter(t){return this.runtimeBuilder.withHttpAdapter(t),this}withSocketAdapter(t){return this.runtimeBuilder.withSocketAdapter(t),this}withLoggerAdapter(t){return this.runtimeBuilder.withLoggerAdapter(t),this}withActivitySink(t){return this.runtimeBuilder.withActivitySink(t),this}withExtension(t,n){return this.runtimeBuilder.withExtension(t,n),this}withPolicy(t){return this.options={...this.options,policy:t},this}withTransport(t){return this.options={...this.options,transport:t},this}withOutboxStore(t){return this.options={...this.options,outboxStore:t},this}withCheckpointStore(t){return this.options={...this.options,checkpointStore:t},this}withConflictResolvers(t){return this.options={...this.options,conflictResolvers:t},this}withProjectionHook(t){return this.options={...this.options,projectionHook:t},this}build(){let t=U({phase:"idle",started:!1,syncing:!1,startCount:0,stopCount:0,syncCount:0,lastError:null,lastTransitionAt:new Date().toISOString()}),n=this.runtimeBuilder.build();return new I(n,t,this.options)}};function dt(e={}){let t=I.builder();return e.policy&&t.withPolicy(e.policy),e.transport&&t.withTransport(e.transport),e.outboxStore&&t.withOutboxStore(e.outboxStore),e.checkpointStore&&t.withCheckpointStore(e.checkpointStore),e.conflictResolvers&&t.withConflictResolvers(e.conflictResolvers),e.projectionHook&&t.withProjectionHook(e.projectionHook),t.build()}function pt(e,t){e.registerDomainBridge(t)}async function ht(e,t,n={}){e.isStarted()||await e.start(n),await e.startSync(t)}async function yt(e){await e.stopSync()}var w=class extends Error{constructor(t,n,r=!1,i,a){super(n),this.name="OfsyncTransportError",this.code=t,this.retryable=r,this.status=i,this.details=a}};async function z(e){let t=globalThis.crypto;if(!t||!t.subtle)throw new Error("WebCrypto API is not available");let n=new TextEncoder().encode(e),r=await t.subtle.digest("SHA-256",n);return[...new Uint8Array(r)].map(i=>i.toString(16).padStart(2,"0")).join("")}function Y(e){let t=String(e.table||""),n=String(e.id||""),r=String(e.changeId||"");return[t,n,r].join("::")}function D(e){return{conflictId:Y(e),changeId:String(e.changeId||""),domain:"unknown",entity:String(e.table||""),recordId:String(e.id||""),code:String(e.code||"UNKNOWN_CONFLICT"),message:String(e.message||"Conflict detected"),retryable:!!e.retryable,scope:{tenantId:typeof e.tenantId=="string"?e.tenantId:void 0,branchId:typeof e.branchId=="string"?e.branchId:void 0,ledgerProfileId:typeof e.ledgerProfileId=="string"?e.ledgerProfileId:typeof e.financeDomainId=="string"?e.financeDomainId:void 0},details:e}}function R(e){return!!(e&&typeof e=="object"&&!Array.isArray(e))}function G(e){var n;let t=(n=e.branchId)==null?void 0:n.trim();if(!t)throw new w("BRANCH_SCOPE_REQUIRED","Sync scope requires branchId",!1);return t}var L=class{constructor(t){this.options=t}nowIso(){return this.options.nowIso?this.options.nowIso():new Date().toISOString()}defaultBootstrapSinceIso(){return new Date(Date.now()-2160*60*60*1e3).toISOString()}async buildHeaders(t){let n={"content-type":"application/json","x-branch-id":G(t),"x-sync-protocol-version":"1"};if(t.tenantId&&(n["x-tenant-id"]=t.tenantId),t.deviceId&&(n["x-device-id"]=t.deviceId),t.ledgerProfileId&&(n["x-ledger-profile-id"]=t.ledgerProfileId),this.options.getAccessToken){let r=await this.options.getAccessToken();r&&(n.authorization=`Bearer ${r}`)}return n}normalizeUrl(t){return`${this.options.baseUrl.replace(/\/+$/,"")}${t}`}normalizeDomainPath(t){let n=t.trim().toLowerCase().replace(/[^a-z0-9_-]/g,"");return n||null}resolveDomainPathOrThrow(t){let n=this.normalizeDomainPath(t||"");if(!n)throw new w("DOMAIN_REQUIRED","Sync transport requires explicit domain",!1);return n}isGenericFallbackEnabled(){return this.options.allowGenericFallback!==!1}async requestWithDomainPolicy(t,n,r){if(n){let i=await this.options.httpAdapter.request({...t,url:this.normalizeUrl(n)});if(i.status!==404||!this.isGenericFallbackEnabled())return i}return this.options.httpAdapter.request({...t,url:this.normalizeUrl(r)})}parseFailure(t,n){var u,o,s;let r=n||{},i=((u=r.error)==null?void 0:u.code)||"SYNC_TRANSPORT_ERROR",a=((o=r.error)==null?void 0:o.message)||`Sync transport failed with status ${t}`,c=!!((s=r.error)!=null&&s.retryable),l=R(r.error)&&R(r.error.details)?r.error.details:void 0;throw new w(i,a,c,t,l)}async push(t){let n={changes:t.outbox.map(o=>{let s=R(o.change.payload)?o.change.payload:{},p=typeof s.table=="string"?s.table.trim():"",d=R(s.record)?s.record:null,g=Date.parse(o.change.occurredAt);return p&&d?{id:o.change.id,table:p,type:o.change.type,record:{...d,id:o.change.recordId||String(d.id||o.change.id)},updated_at:Number.isFinite(g)?g:o.change.occurredAt}:{id:o.change.id,entity:o.change.entity,type:o.change.type,data:{id:o.change.recordId,...s},timestamp:o.change.occurredAt}})},r=JSON.stringify(n),i=await z(r),a=await this.buildHeaders(t.scope);a["x-data-checksum"]=i;let c=this.resolveDomainPathOrThrow(t.domain),l=await this.requestWithDomainPolicy({method:"POST",headers:a,body:n},`/sync/${c}/push`,"/sync/push");l.status>=400&&this.parseFailure(l.status,l.data);let u=l.data||{};return{applied:Array.isArray(u.applied)?u.applied:[],failed:Array.isArray(u.failed)?u.failed:[],conflicts:Array.isArray(u.conflicts)?u.conflicts.map(D):[]}}async pull(t){let n=await this.buildHeaders(t.scope),r=t.cursor||this.defaultBootstrapSinceIso(),i=this.resolveDomainPathOrThrow(t.domain),a=await this.requestWithDomainPolicy({method:"GET",headers:n,query:{since:r}},`/sync/${i}/pull`,"/sync/pull");a.status>=400&&this.parseFailure(a.status,a.data);let c=a.data||{},l=Array.isArray(c.changes)?c.changes.map(o=>{var d,g,f,h;let s=typeof o.table=="string"?o.table:"",p=typeof o.domain=="string"&&o.domain.trim().length>0?o.domain:s.startsWith("auth_")?"ofauth":s.startsWith("offinance_")?"offinance":s.startsWith("ofcoop_")?"ofcoop":s.startsWith("ofpos_")?"ofpos":"unknown";return{id:String(o.changeId||o.id||""),domain:String(p),entity:String(o.entity||s||""),type:String(o.type||o.action||"UPDATE"),recordId:String(o.recordId||((d=o.record)==null?void 0:d.id)||((g=o.data)==null?void 0:g.id)||o.id||""),payload:typeof s=="string"&&s.length>0?{table:String(s),action:String(o.action||o.type||"UPDATE"),record:{...o.record||o.data||{},id:String(o.recordId||((f=o.record)==null?void 0:f.id)||((h=o.data)==null?void 0:h.id)||o.id||"")}}:o.record||o.data||{},occurredAt:String(o.timestamp||this.nowIso()),scope:t.scope}}):[],u=l.length>0?l[l.length-1].occurredAt:this.nowIso();return{changes:l,nextCursor:u,conflicts:[]}}async listConflicts(t){let n=await this.buildHeaders(t.scope),r=await this.options.httpAdapter.request({method:"GET",url:this.normalizeUrl("/sync/conflicts"),headers:n});r.status>=400&&this.parseFailure(r.status,r.data);let i=r.data||{};return Array.isArray(i.conflicts)?i.conflicts.map(D):[]}async resolveConflicts(t){var o;let n=await this.listConflicts({scope:t.scope}),r=new Map(n.map(s=>[s.conflictId,s])),i=t.resolutions.map(s=>r.get(s.conflictId)).filter(s=>!!s).map(s=>({table:s.entity,id:s.recordId})),a=await this.buildHeaders(t.scope),c={resolutions:i},l=await this.options.httpAdapter.request({method:"POST",url:this.normalizeUrl("/sync/conflicts/resolve"),headers:a,body:c});l.status>=400&&this.parseFailure(l.status,l.data);let u=Array.isArray((o=l.data)==null?void 0:o.remaining)?l.data.remaining.length:0;return{resolvedCount:Math.max(0,i.length-u)}}};function N(e){return`Tidak dapat terhubung ke server sinkronisasi (${e.trim()||"VITE_OFSYNC_BASE_URL"}). Pastikan ofsync-server berjalan dan CORS origin browser diizinkan.`}function q(e){return e.trim().replace(/\/+$/,"")}function W(e){return e instanceof Error?e.message:String(e!=null?e:"")}function X(e,t=9e5){if(typeof e=="number"&&Number.isFinite(e)&&e>0)return Math.floor(e*1e3);if(typeof e!="string")return t;let n=e.trim();if(!n)return t;if(/^\d+$/.test(n))return Math.max(1,Number(n))*1e3;let r=n.match(/^(\d+)\s*([smhd])$/i);if(!r)return t;let i=Number(r[1]),a=r[2].toLowerCase(),c=a==="s"?1e3:a==="m"?6e4:a==="h"?36e5:864e5;return Math.max(1,i)*c}function v(e,t,n=N){let r=W(e),i=r.toLowerCase();return i.includes("networkerror")||i.includes("failed to fetch")||i.includes("load failed")||i.includes("fetch failed")||i.includes("the network connection was lost")||i.includes("network request failed")?n(t):r}function C(e,t){let n=new Error(t);return n.code=e,n}async function mt(e){var i,a,c,l;if(e.bearerToken.length>0)return e.bearerToken;let t=(i=e.nowMs)!=null?i:Date.now(),n=(a=e.refreshLeewayMs)!=null?a:1e4;if((c=e.session)!=null&&c.accessToken){if(e.session.expiresAtMs>t+n)return e.session.accessToken;let u=await e.refresh();if(u!=null&&u.accessToken)return u.accessToken}let r=await e.loginFromCredentials();return(l=r==null?void 0:r.accessToken)!=null?l:""}function St(e){var l,u,o;let t=(l=e.fetchImpl)!=null?l:fetch,n=q(e.baseUrl),r=(u=e.accessTokenFallbackTtlMs)!=null?u:9e5,i=(o=e.networkErrorMessage)!=null?o:N,a=s=>(Array.isArray(s.scopes)?s.scopes:[]).filter(d=>!!(d&&typeof d=="object")).map(d=>{var g,f,h,y;return{tenantId:typeof d.tenantId=="string"&&d.tenantId.trim().length>0?d.tenantId:null,branchId:String((g=d.branchId)!=null?g:"").trim(),role:String((f=d.role)!=null?f:""),userId:String((h=d.userId)!=null?h:""),email:String((y=d.email)!=null?y:"")}}).filter(d=>d.branchId.length>0),c=(s,p)=>{var f,h,y;let d=String((h=(f=s.accessToken)!=null?f:s.token)!=null?h:""),g=String((y=s.refreshToken)!=null?y:"");if(!d||!g)throw C("SYNC_AUTH_TOKEN_INCOMPLETE","Sync auth gagal: token tidak lengkap.");return{accessToken:d,refreshToken:g,expiresAtMs:Date.now()+X(s.expiresIn,r),principal:p}};return{async fetchScopeOptions(s,p){var d,g,f;if(!n)return[];try{let h=await t(`${n}/api/v1/auth/scope-options`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:s,password:p})}),y=await h.json().catch(()=>({}));if(!h.ok){let m=(d=y.error)!=null?d:{};throw C(String((g=m.code)!=null?g:`HTTP_${h.status}`),String((f=m.message)!=null?f:`Sync scope discovery gagal (${h.status})`))}return a(y)}catch(h){let y=h;throw C(typeof(y==null?void 0:y.code)=="string"?y.code:"SYNC_AUTH_SCOPE_DISCOVERY_FAILED",v(h,n,i))}},async login(s,p,d){var h,y,m,E,T;let g=String((h=d.branchId)!=null?h:"").trim(),f=String((y=d.tenantId)!=null?y:"").trim();try{let S=await t(`${n}/api/v1/auth/login`,{method:"POST",headers:{"Content-Type":"application/json","X-Branch-ID":g,...f?{"X-Tenant-ID":f}:{},"X-Device-ID":e.deviceId},body:JSON.stringify({username:s,password:p})}),b=await S.json().catch(()=>({}));if(!S.ok){let P=(m=b.error)!=null?m:{};throw C(String((E=P.code)!=null?E:`HTTP_${S.status}`),String((T=P.message)!=null?T:`Sync auth login gagal (${S.status})`))}return c(b,s)}catch(S){let b=S;throw C(typeof(b==null?void 0:b.code)=="string"?b.code:"SYNC_AUTH_FAILED",v(S,n,i))}},async refresh(s,p){var d,g,f;try{let h=await t(`${n}/api/v1/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json",...p?{"X-Tenant-ID":p}:{},"X-Device-ID":e.deviceId},body:JSON.stringify({refreshToken:s})}),y=await h.json().catch(()=>({}));if(!h.ok){let m=(d=y.error)!=null?d:{};throw C(String((g=m.code)!=null?g:`HTTP_${h.status}`),String((f=m.message)!=null?f:`Sync auth refresh gagal (${h.status})`))}return c(y,"")}catch(h){let y=h;throw C(typeof(y==null?void 0:y.code)=="string"?y.code:"SYNC_AUTH_REFRESH_FAILED",v(h,n,i))}}}}export{A as DEFAULT_SYNC_POLICY,k as InMemoryCheckpointStore,O as InMemoryOutboxStore,tt as OFSYNC_CONTRACT_STAGE,I as OfsyncCore,x as OfsyncCoreBuilder,L as OfsyncHttpTransport,w as OfsyncTransportError,St as createOfsyncAuthClient,C as createSyncAuthError,dt as createSyncRuntime,_ as defaultConflictResolver,v as normalizeSyncNetworkError,X as parseSyncDurationMs,pt as registerDomainBridge,mt as resolveSyncAccessToken,ht as startSync,yt as stopSync};
|
|
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,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 _=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,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=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,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(),s={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(s),s}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=nt(e,o),i=await this.checkpointStore.get(n),s;try{s=await this.transport.pull({scope:e,cursor:i,limit:this.policy.batchSize,domain:o})}catch(a){let c=(a==null?void 0:a.code)||"",d=a==null?void 0:a.details,h=ot(d==null?void 0:d.cutoffTime);if(c!=="SYNC_RESYNC_REQUIRED"||!h)throw a;await this.checkpointStore.set(n,h),i=h,s=await this.transport.pull({scope:e,cursor:i,limit:this.policy.batchSize,domain:o})}let u=(s.changes||[]).map(a=>{let c=String(a.domain||"").trim().toLowerCase();if(!c)return{...a,domain:o};if(c!==o)throw new Error(`Sync pull domain mismatch: expected ${o}, received ${c}`);return a});await this.applyRemoteChanges(u,e),s.nextCursor!==void 0&&await this.checkpointStore.set(n,s.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(s=>({...s,syncing:!1,lastError:o,lastErrorCode:n||null})),r}).finally(()=>{this.orchestratorSyncPromise=null}),this.orchestratorSyncPromise)}async configureOrchestratorSocket(e){var s;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=(s=this.runtime.registry)==null?void 0:s.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 c=a instanceof Error?a.message:String(a),d=String((h=a==null?void 0:a.code)!=null?h:"");this.orchestratorStateStore.setState(l=>({...l,lastError:c,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 s=i instanceof Error?i.message:String(i),u=String((a=i==null?void 0:i.code)!=null?a:"");this.orchestratorStateStore.setState(c=>({...c,lastError:s,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 s=String(i.change.domain||"").trim().toLowerCase()||"__unknown__";o.has(s)||o.set(s,[]),(n=o.get(s))==null||n.push(i)}for(let[i,s]of o.entries()){if(i==="__unknown__"){for(let h of s){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:s,domain:i}),a=new Set((u.applied||[]).map(h=>h.changeId)),c=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 s){if(a.has(h.change.id)){await this.outboxStore.remove(h.outboxId);continue}let l=c.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 s of i){let u=String(s.domain||"").trim()||o,a={...s,domain:u,scope:s.scope||e},c=`${a.domain}:${a.id}`;await this.enqueueLocalChange(a,c)}}}async applyRemoteChanges(e,r){if(!Array.isArray(e)||e.length===0)return;let o=[...e].sort((i,s)=>i.occurredAt.localeCompare(s.occurredAt)),n=new Map;for(let i of o){n.has(i.domain)||n.set(i.domain,[]);let s=n.get(i.domain);s&&s.push(i)}for(let[i,s]of n.entries()){let u=this.bridges.get(i);if(u!=null&&u.applyRemoteChanges&&(await u.applyRemoteChanges(s,r),this.projectionHook))for(let a of s)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 s of e){let u=[...this.conflictResolvers],a=null;for(let c of u){let d=await c(s,{scope:r});if(d){a=d;break}}a||(a=j(s)),n.push(a)}n.length>0&&await this.transport.resolveConflicts({scope:r,domain:o,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}),o=this.runtimeBuilder.build();return new E(o,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,o=!1,n,i){super(r),this.name="OfsyncTransportError",this.code=e,this.retryable=o,this.status=n,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),o=await e.subtle.digest("SHA-256",r);return[...new Uint8Array(o)].map(n=>n.toString(16).padStart(2,"0")).join("")}function st(t){let e=String(t.table||""),r=String(t.id||""),o=String(t.changeId||"");return[e,r,o].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 o=Array.from(new Set(e.map(n=>{var i;return W((i=n.change.scope)==null?void 0:i.productFamilyId)}).filter(Boolean)));return o.length===1?o[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,o){let n={"content-type":"application/json","x-branch-id":ct(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 s=at(e,r);if(s&&(n["x-product-family-id"]=s),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,c,d;let o=r||{},n=((a=o.error)==null?void 0:a.code)||"SYNC_TRANSPORT_ERROR",i=((c=o.error)==null?void 0:c.message)||`Sync transport failed with status ${e}`,s=!!((d=o.error)!=null&&d.retryable),u=D(o.error)&&D(o.error.details)?o.error.details:void 0;throw new P(n,i,s,e,u)}async push(e){let r={changes:e.outbox.map(c=>{let d=D(c.change.payload)?c.change.payload:{},h=typeof d.table=="string"?d.table.trim():"",l=D(d.record)?d.record:null,y=Date.parse(c.change.occurredAt);return h&&l?{id:c.change.id,table:h,type:c.change.type,record:{...l,id:c.change.recordId||String(l.id||c.change.id)},updated_at:Number.isFinite(y)?y:c.change.occurredAt}:{id:c.change.id,entity:c.change.entity,type:c.change.type,data:{id:c.change.recordId,...d},timestamp:c.change.occurredAt}})},o=JSON.stringify(r),n=await it(o),i=this.resolveDomainPathOrThrow(e.domain),s=await this.buildHeaders(e.scope,e.outbox,i);s["x-data-checksum"]=n;let u=await this.options.httpAdapter.request({method:"POST",headers:s,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 s=i.data||{},u=Array.isArray(s.changes)?s.changes.map(c=>{var l,y,f,g;let d=typeof c.table=="string"?c.table:"",h=typeof c.domain=="string"&&c.domain.trim().length>0?c.domain:d.startsWith("auth_")?"ofauth":d.startsWith("offinance_")||d.startsWith("ofinance_")?"offinance":d.startsWith("ofcoop_")?"ofcoop":d.startsWith("ofpos_")?"ofpos":"unknown";return{id:String(c.changeId||c.id||""),domain:String(h),entity:String(c.entity||d||""),type:String(c.type||c.action||"UPDATE"),recordId:String(c.recordId||((l=c.record)==null?void 0:l.id)||((y=c.data)==null?void 0:y.id)||c.id||""),payload:typeof d=="string"&&d.length>0?{table:String(d),action:String(c.action||c.type||"UPDATE"),record:{...c.record||c.data||{},id:String(c.recordId||((f=c.record)==null?void 0:f.id)||((g=c.data)==null?void 0:g.id)||c.id||"")}}:c.record||c.data||{},occurredAt:String(c.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 s=i.data||{};return Array.isArray(s.conflicts)?s.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},s=this.normalizeDomainPath(e.domain||""),u=await this.buildHeaders(e.scope,void 0,s||void 0),a=s?this.normalizeUrl(`/sync/${s}/conflicts/resolve`):this.normalizeUrl("/sync/conflicts/resolve"),c=await this.options.httpAdapter.request({method:"POST",url:a,headers:u,body:i});c.status>=400&&this.parseFailure(c.status,c.data);let d=Array.isArray((h=c.data)==null?void 0:h.remaining)?c.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 o=r.match(/^(\d+)\s*([smhd])$/i);if(!o)return e;let n=Number(o[1]),i=o[2].toLowerCase(),s=i==="s"?1e3:i==="m"?6e4:i==="h"?36e5:864e5;return Math.max(1,n)*s}function R(t,e,r=Q){let o=X(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 _t(t){var u,a;let e=t.accessToken.trim(),r=t.branchId.trim();if(!e||!r)return null;let o=V(t.baseUrl).replace(/^http:\/\//i,"ws://").replace(/^https:\/\//i,"wss://"),n={token:e,branchId:r},i=String((u=t.tenantId)!=null?u:"").trim(),s=String((a=t.domainId)!=null?a:"").trim();return i&&(n.tenantId=i),s&&(n.domainId=s),{url:`${o}/ws/sync-notify`,query:n}}async function Dt(t){var n,i,s,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((s=t.session)!=null&&s.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 Lt(t){var u,a,c;let e=(u=t.fetchImpl)!=null?u:fetch,r=V(t.baseUrl),o=(a=t.accessTokenFallbackTtlMs)!=null?a:9e5,n=(c=t.networkErrorMessage)!=null?c: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),s=(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,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",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 k(String((O=A.code)!=null?O:`HTTP_${b.status}`),String((I=A.message)!=null?I:`Sync auth login gagal (${b.status})`))}return s(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,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 s(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,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 i,s,u,a;let e=(i=t.triggerInitialPull)!=null?i:(async c=>{await w(c).trigger("manual")});if(!t.runtimeReady||!t.serverSyncEnabled||!t.isAuthenticated){if(t.lastStartSignatureRef.current="",(s=t.setWsConnected)==null||s.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 $t(t){var o,n,i,s,u,a,c,d,h,l,y,f,g,S,m,O,I,b,C,A,B,N,M,U,F,H,$,z;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((s=t.setStage)==null||s.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=(c=t.missingAccessTokenMessage)!=null?c:"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 s,u,a,c,d,h,l,y,f,g,S,m;let o=e;if(e=r.syncing,(s=t.setSyncing)==null||s.call(t,r.syncing),(u=t.setWsConnected)==null||u.call(t,r.wsConnected),!r.lastError){if(o&&!r.syncing){let O=(c=(a=t.getPullAppliedCount)==null?void 0:a.call(t))!=null?c: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,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};
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var T=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var K=Object.prototype.hasOwnProperty;var z=(e,t)=>{for(var n in t)T(e,n,{get:t[n],enumerable:!0})},Y=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of j(t))!K.call(e,o)&&o!==n&&T(e,o,{get:()=>t[o],enumerable:!(r=$(t,o))||r.enumerable});return e};var G=e=>Y(T({},"__esModule",{value:!0}),e);var lt={};z(lt,{DEFAULT_SYNC_POLICY:()=>A,InMemoryCheckpointStore:()=>k,InMemoryOutboxStore:()=>O,OFSYNC_CONTRACT_STAGE:()=>q,OfsyncCore:()=>I,OfsyncCoreBuilder:()=>R,OfsyncHttpTransport:()=>_,OfsyncTransportError:()=>w,createOfsyncAuthClient:()=>ct,createSyncAuthError:()=>S,createSyncRuntime:()=>V,defaultConflictResolver:()=>P,normalizeSyncNetworkError:()=>E,parseSyncDurationMs:()=>H,registerDomainBridge:()=>Z,resolveSyncAccessToken:()=>at,startSync:()=>tt,stopSync:()=>et});module.exports=G(lt);var A={batchSize:200,maxRetry:5,scheduler:{mode:"manual",onlineTrigger:!0},retryBackoff:{strategy:"exponential-jitter",baseDelayMs:500,maxDelayMs:3e4}};var O=class{constructor(){this.rows=new Map}async enqueue(t){this.rows.set(t.outboxId,t)}async listPending(t){return[...this.rows.values()].filter(n=>n.status==="PENDING"||n.status==="FAILED").sort((n,r)=>n.createdAt.localeCompare(r.createdAt)).slice(0,Math.max(0,t))}async findByIdempotencyKey(t){for(let n of this.rows.values())if(n.idempotencyKey===t)return n;return null}async listAll(){return[...this.rows.values()].sort((t,n)=>t.createdAt.localeCompare(n.createdAt))}async update(t){this.rows.set(t.outboxId,t)}async remove(t){this.rows.delete(t)}};var k=class{constructor(){this.rows=new Map}async get(t){return this.rows.has(t)?this.rows.get(t):null}async set(t,n){this.rows.set(t,n)}};function P(e){return e.code==="VERSION_CONFLICT"?{conflictId:e.conflictId,action:"APPLY_REMOTE"}:e.code==="SCOPE_CONFLICT"?{conflictId:e.conflictId,action:"MANUAL_MERGE"}:{conflictId:e.conflictId,action:"KEEP_LOCAL"}}var q="phase1-runtime-skeleton";var M=require("ofcore"),B=require("ofcore"),x=require("ofcore");function W(e={}){return{...A,...e,scheduler:{...A.scheduler,...e.scheduler||{}},retryBackoff:{...A.retryBackoff,...e.retryBackoff||{}}}}function X(e){return`${e.tenantId||"__standalone__"}::${e.branchId||"__all__"}::${e.ledgerProfileId||"__default_ledger__"}::${e.deviceId||"__device__"}`}function J(e,t){return`${X(e)}::${t}`}function Q(e){if(typeof e!="string"||e.trim().length===0)return null;let t=Date.parse(e);return Number.isFinite(t)?new Date(t+5e3).toISOString():null}var I=class{constructor(t,n,r={}){this.runtime=t;this.runtimeStateStore=n;this.bridges=new Map;this.runtimeStore=(0,x.asReadonlyStore)(this.runtimeStateStore),this.policy=W(r.policy),this.transport=r.transport,this.outboxStore=r.outboxStore||new O,this.checkpointStore=r.checkpointStore||new k,this.conflictResolvers=r.conflictResolvers||[],this.projectionHook=r.projectionHook}static builder(){return new R}get registry(){return this.runtime.registry}getPolicy(){return this.policy}registerDomainBridge(t){let n=t.domain.trim();if(!n)throw new Error("DomainBridge.domain is required");if(this.bridges.has(n))throw new Error(`DomainBridge for domain "${n}" is already registered`);this.bridges.set(n,t)}listDomainBridges(){return[...this.bridges.keys()].sort()}async getOutboxState(){let t=await this.outboxStore.listAll(),n=t.filter(o=>o.status==="PENDING").length,r=t.filter(o=>o.status==="FAILED").length;return{pending:n,failed:r,total:t.length}}async enqueueLocalChange(t,n){let r=(n||`${t.domain}:${t.entity}:${t.recordId}:${t.type}:${t.id}`).trim();if(!r)throw new Error("idempotencyKey must not be empty");let o=await this.outboxStore.findByIdempotencyKey(r);if(o)return o;let a=new Date().toISOString(),c={outboxId:`outbox_${Date.now()}_${Math.random().toString(36).slice(2)}`,idempotencyKey:r,change:t,retryCount:0,status:"PENDING",createdAt:a,updatedAt:a};return await this.outboxStore.enqueue(c),c}async start(t={}){this.runtimeStateStore.setState({phase:"starting",started:!1,syncing:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.start(t),this.runtimeStateStore.setState(n=>({...n,phase:"started",started:!0,syncing:!1,startCount:n.startCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(n){throw this.runtimeStateStore.setState({phase:"error",started:!1,syncing:!1,lastError:n instanceof Error?n.message:String(n),lastTransitionAt:new Date().toISOString()}),n}}async stop(){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(t=>({...t,phase:"stopped",started:!1,syncing:!1,stopCount:t.stopCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(t){throw this.runtimeStateStore.setState({phase:"error",started:this.runtime.isStarted(),syncing:!1,lastError:t instanceof Error?t.message:String(t),lastTransitionAt:new Date().toISOString()}),t}}async startSync(t){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.processOutbox(t);let n=this.listDomainBridges();for(let r of n){let o=J(t,r),a=await this.checkpointStore.get(o),c;try{c=await this.transport.pull({scope:t,cursor:a,limit:this.policy.batchSize,domain:r})}catch(u){let i=(u==null?void 0:u.code)||"",s=u==null?void 0:u.details,p=Q(s==null?void 0:s.cutoffTime);if(i!=="SYNC_RESYNC_REQUIRED"||!p)throw u;await this.checkpointStore.set(o,p),a=p,c=await this.transport.pull({scope:t,cursor:a,limit:this.policy.batchSize,domain:r})}let l=(c.changes||[]).map(u=>{let i=String(u.domain||"").trim().toLowerCase();if(!i)return{...u,domain:r};if(i!==r)throw new Error(`Sync pull domain mismatch: expected ${r}, received ${i}`);return u});await this.applyRemoteChanges(l,t),c.nextCursor!==void 0&&await this.checkpointStore.set(o,c.nextCursor)}}this.runtimeStateStore.setState(n=>({...n,phase:"started",started:!0,syncing:!1,syncCount:n.syncCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(n){throw this.runtimeStateStore.setState({phase:"error",started:this.runtime.isStarted(),syncing:!1,lastError:n instanceof Error?n.message:String(n),lastTransitionAt:new Date().toISOString()}),n}}async stopSync(){this.runtimeStateStore.setState(t=>({...t,phase:this.runtime.isStarted()?"started":"stopped",syncing:!1,lastTransitionAt:new Date().toISOString()}))}isStarted(){return this.runtime.isStarted()}startScheduler(t){if(this.policy.scheduler.mode!=="interval")return;let n=this.policy.scheduler.intervalMs||1e4;this.stopScheduler(),this.schedulerTimer=setInterval(()=>{this.startSync(t).catch(r=>{this.runtimeStateStore.setState(o=>({...o,phase:"error",syncing:!1,lastError:r instanceof Error?r.message:String(r),lastTransitionAt:new Date().toISOString()}))})},n)}stopScheduler(){this.schedulerTimer&&(clearInterval(this.schedulerTimer),this.schedulerTimer=void 0)}async notifyOnline(t){this.policy.scheduler.onlineTrigger!==!1&&await this.startSync(t)}async processOutbox(t){var o;if(!this.transport)return;let n=await this.outboxStore.listPending(this.policy.batchSize);if(n.length===0)return;let r=new Map;for(let a of n){let c=String(a.change.domain||"").trim().toLowerCase()||"__unknown__";r.has(c)||r.set(c,[]),(o=r.get(c))==null||o.push(a)}for(let[a,c]of r.entries()){if(a==="__unknown__"){for(let p of c){let d=p.retryCount+1;await this.outboxStore.update({...p,retryCount:d,status:d>=this.policy.maxRetry?"FAILED":"PENDING",lastError:"DOMAIN_REQUIRED",updatedAt:new Date().toISOString()})}continue}let l=await this.transport.push({scope:t,outbox:c,domain:a}),u=new Set((l.applied||[]).map(p=>p.changeId)),i=new Map((l.failed||[]).map(p=>[p.changeId,p])),s=new Set((l.conflicts||[]).map(p=>p.changeId));await this.resolveConflicts(l.conflicts||[],t);for(let p of c){if(u.has(p.change.id)){await this.outboxStore.remove(p.outboxId);continue}let d=i.get(p.change.id);if(d||s.has(p.change.id)){let g=p.retryCount+1;await this.outboxStore.update({...p,retryCount:g,status:g>=this.policy.maxRetry?"FAILED":"PENDING",lastError:(d==null?void 0:d.message)||(s.has(p.change.id)?"SYNC_CONFLICT":p.lastError),updatedAt:new Date().toISOString()})}}}}async applyRemoteChanges(t,n){if(!Array.isArray(t)||t.length===0)return;let r=[...t].sort((a,c)=>a.occurredAt.localeCompare(c.occurredAt)),o=new Map;for(let a of r){o.has(a.domain)||o.set(a.domain,[]);let c=o.get(a.domain);c&&c.push(a)}for(let[a,c]of o.entries()){let l=this.bridges.get(a);if(l!=null&&l.applyRemoteChanges&&(await l.applyRemoteChanges(c,n),this.projectionHook))for(let u of c)await this.projectionHook(u,n)}}async resolveConflicts(t,n){var o;if(!((o=this.transport)!=null&&o.resolveConflicts)||t.length===0)return;let r=[];for(let a of t){let c=[...this.conflictResolvers],l=null;for(let u of c){let i=await u(a,{scope:n});if(i){l=i;break}}l||(l=P(a)),r.push(l)}r.length>0&&await this.transport.resolveConflicts({scope:n,resolutions:r})}},R=class{constructor(){this.runtimeBuilder=M.CoreRuntime.builder();this.options={};this.runtimeBuilder.withDbAdapter(()=>new B.InMemoryDbAdapter)}withPlatformAdapter(t){return this.runtimeBuilder.withPlatformAdapter(t),this}withDbAdapter(t){return this.runtimeBuilder.withDbAdapter(t),this}withHttpAdapter(t){return this.runtimeBuilder.withHttpAdapter(t),this}withSocketAdapter(t){return this.runtimeBuilder.withSocketAdapter(t),this}withLoggerAdapter(t){return this.runtimeBuilder.withLoggerAdapter(t),this}withActivitySink(t){return this.runtimeBuilder.withActivitySink(t),this}withExtension(t,n){return this.runtimeBuilder.withExtension(t,n),this}withPolicy(t){return this.options={...this.options,policy:t},this}withTransport(t){return this.options={...this.options,transport:t},this}withOutboxStore(t){return this.options={...this.options,outboxStore:t},this}withCheckpointStore(t){return this.options={...this.options,checkpointStore:t},this}withConflictResolvers(t){return this.options={...this.options,conflictResolvers:t},this}withProjectionHook(t){return this.options={...this.options,projectionHook:t},this}build(){let t=(0,x.createStore)({phase:"idle",started:!1,syncing:!1,startCount:0,stopCount:0,syncCount:0,lastError:null,lastTransitionAt:new Date().toISOString()}),n=this.runtimeBuilder.build();return new I(n,t,this.options)}};function V(e={}){let t=I.builder();return e.policy&&t.withPolicy(e.policy),e.transport&&t.withTransport(e.transport),e.outboxStore&&t.withOutboxStore(e.outboxStore),e.checkpointStore&&t.withCheckpointStore(e.checkpointStore),e.conflictResolvers&&t.withConflictResolvers(e.conflictResolvers),e.projectionHook&&t.withProjectionHook(e.projectionHook),t.build()}function Z(e,t){e.registerDomainBridge(t)}async function tt(e,t,n={}){e.isStarted()||await e.start(n),await e.startSync(t)}async function et(e){await e.stopSync()}var w=class extends Error{constructor(t,n,r=!1,o,a){super(n),this.name="OfsyncTransportError",this.code=t,this.retryable=r,this.status=o,this.details=a}};async function nt(e){let t=globalThis.crypto;if(!t||!t.subtle)throw new Error("WebCrypto API is not available");let n=new TextEncoder().encode(e),r=await t.subtle.digest("SHA-256",n);return[...new Uint8Array(r)].map(o=>o.toString(16).padStart(2,"0")).join("")}function rt(e){let t=String(e.table||""),n=String(e.id||""),r=String(e.changeId||"");return[t,n,r].join("::")}function F(e){return{conflictId:rt(e),changeId:String(e.changeId||""),domain:"unknown",entity:String(e.table||""),recordId:String(e.id||""),code:String(e.code||"UNKNOWN_CONFLICT"),message:String(e.message||"Conflict detected"),retryable:!!e.retryable,scope:{tenantId:typeof e.tenantId=="string"?e.tenantId:void 0,branchId:typeof e.branchId=="string"?e.branchId:void 0,ledgerProfileId:typeof e.ledgerProfileId=="string"?e.ledgerProfileId:typeof e.financeDomainId=="string"?e.financeDomainId:void 0},details:e}}function v(e){return!!(e&&typeof e=="object"&&!Array.isArray(e))}function ot(e){var n;let t=(n=e.branchId)==null?void 0:n.trim();if(!t)throw new w("BRANCH_SCOPE_REQUIRED","Sync scope requires branchId",!1);return t}var _=class{constructor(t){this.options=t}nowIso(){return this.options.nowIso?this.options.nowIso():new Date().toISOString()}defaultBootstrapSinceIso(){return new Date(Date.now()-2160*60*60*1e3).toISOString()}async buildHeaders(t){let n={"content-type":"application/json","x-branch-id":ot(t),"x-sync-protocol-version":"1"};if(t.tenantId&&(n["x-tenant-id"]=t.tenantId),t.deviceId&&(n["x-device-id"]=t.deviceId),t.ledgerProfileId&&(n["x-ledger-profile-id"]=t.ledgerProfileId),this.options.getAccessToken){let r=await this.options.getAccessToken();r&&(n.authorization=`Bearer ${r}`)}return n}normalizeUrl(t){return`${this.options.baseUrl.replace(/\/+$/,"")}${t}`}normalizeDomainPath(t){let n=t.trim().toLowerCase().replace(/[^a-z0-9_-]/g,"");return n||null}resolveDomainPathOrThrow(t){let n=this.normalizeDomainPath(t||"");if(!n)throw new w("DOMAIN_REQUIRED","Sync transport requires explicit domain",!1);return n}isGenericFallbackEnabled(){return this.options.allowGenericFallback!==!1}async requestWithDomainPolicy(t,n,r){if(n){let o=await this.options.httpAdapter.request({...t,url:this.normalizeUrl(n)});if(o.status!==404||!this.isGenericFallbackEnabled())return o}return this.options.httpAdapter.request({...t,url:this.normalizeUrl(r)})}parseFailure(t,n){var u,i,s;let r=n||{},o=((u=r.error)==null?void 0:u.code)||"SYNC_TRANSPORT_ERROR",a=((i=r.error)==null?void 0:i.message)||`Sync transport failed with status ${t}`,c=!!((s=r.error)!=null&&s.retryable),l=v(r.error)&&v(r.error.details)?r.error.details:void 0;throw new w(o,a,c,t,l)}async push(t){let n={changes:t.outbox.map(i=>{let s=v(i.change.payload)?i.change.payload:{},p=typeof s.table=="string"?s.table.trim():"",d=v(s.record)?s.record:null,g=Date.parse(i.change.occurredAt);return p&&d?{id:i.change.id,table:p,type:i.change.type,record:{...d,id:i.change.recordId||String(d.id||i.change.id)},updated_at:Number.isFinite(g)?g:i.change.occurredAt}:{id:i.change.id,entity:i.change.entity,type:i.change.type,data:{id:i.change.recordId,...s},timestamp:i.change.occurredAt}})},r=JSON.stringify(n),o=await nt(r),a=await this.buildHeaders(t.scope);a["x-data-checksum"]=o;let c=this.resolveDomainPathOrThrow(t.domain),l=await this.requestWithDomainPolicy({method:"POST",headers:a,body:n},`/sync/${c}/push`,"/sync/push");l.status>=400&&this.parseFailure(l.status,l.data);let u=l.data||{};return{applied:Array.isArray(u.applied)?u.applied:[],failed:Array.isArray(u.failed)?u.failed:[],conflicts:Array.isArray(u.conflicts)?u.conflicts.map(F):[]}}async pull(t){let n=await this.buildHeaders(t.scope),r=t.cursor||this.defaultBootstrapSinceIso(),o=this.resolveDomainPathOrThrow(t.domain),a=await this.requestWithDomainPolicy({method:"GET",headers:n,query:{since:r}},`/sync/${o}/pull`,"/sync/pull");a.status>=400&&this.parseFailure(a.status,a.data);let c=a.data||{},l=Array.isArray(c.changes)?c.changes.map(i=>{var d,g,f,h;let s=typeof i.table=="string"?i.table:"",p=typeof i.domain=="string"&&i.domain.trim().length>0?i.domain:s.startsWith("auth_")?"ofauth":s.startsWith("offinance_")?"offinance":s.startsWith("ofcoop_")?"ofcoop":s.startsWith("ofpos_")?"ofpos":"unknown";return{id:String(i.changeId||i.id||""),domain:String(p),entity:String(i.entity||s||""),type:String(i.type||i.action||"UPDATE"),recordId:String(i.recordId||((d=i.record)==null?void 0:d.id)||((g=i.data)==null?void 0:g.id)||i.id||""),payload:typeof s=="string"&&s.length>0?{table:String(s),action:String(i.action||i.type||"UPDATE"),record:{...i.record||i.data||{},id:String(i.recordId||((f=i.record)==null?void 0:f.id)||((h=i.data)==null?void 0:h.id)||i.id||"")}}:i.record||i.data||{},occurredAt:String(i.timestamp||this.nowIso()),scope:t.scope}}):[],u=l.length>0?l[l.length-1].occurredAt:this.nowIso();return{changes:l,nextCursor:u,conflicts:[]}}async listConflicts(t){let n=await this.buildHeaders(t.scope),r=await this.options.httpAdapter.request({method:"GET",url:this.normalizeUrl("/sync/conflicts"),headers:n});r.status>=400&&this.parseFailure(r.status,r.data);let o=r.data||{};return Array.isArray(o.conflicts)?o.conflicts.map(F):[]}async resolveConflicts(t){var i;let n=await this.listConflicts({scope:t.scope}),r=new Map(n.map(s=>[s.conflictId,s])),o=t.resolutions.map(s=>r.get(s.conflictId)).filter(s=>!!s).map(s=>({table:s.entity,id:s.recordId})),a=await this.buildHeaders(t.scope),c={resolutions:o},l=await this.options.httpAdapter.request({method:"POST",url:this.normalizeUrl("/sync/conflicts/resolve"),headers:a,body:c});l.status>=400&&this.parseFailure(l.status,l.data);let u=Array.isArray((i=l.data)==null?void 0:i.remaining)?l.data.remaining.length:0;return{resolvedCount:Math.max(0,o.length-u)}}};function U(e){return`Tidak dapat terhubung ke server sinkronisasi (${e.trim()||"VITE_OFSYNC_BASE_URL"}). Pastikan ofsync-server berjalan dan CORS origin browser diizinkan.`}function it(e){return e.trim().replace(/\/+$/,"")}function st(e){return e instanceof Error?e.message:String(e!=null?e:"")}function H(e,t=9e5){if(typeof e=="number"&&Number.isFinite(e)&&e>0)return Math.floor(e*1e3);if(typeof e!="string")return t;let n=e.trim();if(!n)return t;if(/^\d+$/.test(n))return Math.max(1,Number(n))*1e3;let r=n.match(/^(\d+)\s*([smhd])$/i);if(!r)return t;let o=Number(r[1]),a=r[2].toLowerCase(),c=a==="s"?1e3:a==="m"?6e4:a==="h"?36e5:864e5;return Math.max(1,o)*c}function E(e,t,n=U){let r=st(e),o=r.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")?n(t):r}function S(e,t){let n=new Error(t);return n.code=e,n}async function at(e){var o,a,c,l;if(e.bearerToken.length>0)return e.bearerToken;let t=(o=e.nowMs)!=null?o:Date.now(),n=(a=e.refreshLeewayMs)!=null?a:1e4;if((c=e.session)!=null&&c.accessToken){if(e.session.expiresAtMs>t+n)return e.session.accessToken;let u=await e.refresh();if(u!=null&&u.accessToken)return u.accessToken}let r=await e.loginFromCredentials();return(l=r==null?void 0:r.accessToken)!=null?l:""}function ct(e){var l,u,i;let t=(l=e.fetchImpl)!=null?l:fetch,n=it(e.baseUrl),r=(u=e.accessTokenFallbackTtlMs)!=null?u:9e5,o=(i=e.networkErrorMessage)!=null?i:U,a=s=>(Array.isArray(s.scopes)?s.scopes:[]).filter(d=>!!(d&&typeof d=="object")).map(d=>{var g,f,h,y;return{tenantId:typeof d.tenantId=="string"&&d.tenantId.trim().length>0?d.tenantId:null,branchId:String((g=d.branchId)!=null?g:"").trim(),role:String((f=d.role)!=null?f:""),userId:String((h=d.userId)!=null?h:""),email:String((y=d.email)!=null?y:"")}}).filter(d=>d.branchId.length>0),c=(s,p)=>{var f,h,y;let d=String((h=(f=s.accessToken)!=null?f:s.token)!=null?h:""),g=String((y=s.refreshToken)!=null?y:"");if(!d||!g)throw S("SYNC_AUTH_TOKEN_INCOMPLETE","Sync auth gagal: token tidak lengkap.");return{accessToken:d,refreshToken:g,expiresAtMs:Date.now()+H(s.expiresIn,r),principal:p}};return{async fetchScopeOptions(s,p){var d,g,f;if(!n)return[];try{let h=await t(`${n}/api/v1/auth/scope-options`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:s,password:p})}),y=await h.json().catch(()=>({}));if(!h.ok){let m=(d=y.error)!=null?d:{};throw S(String((g=m.code)!=null?g:`HTTP_${h.status}`),String((f=m.message)!=null?f:`Sync scope discovery gagal (${h.status})`))}return a(y)}catch(h){let y=h;throw S(typeof(y==null?void 0:y.code)=="string"?y.code:"SYNC_AUTH_SCOPE_DISCOVERY_FAILED",E(h,n,o))}},async login(s,p,d){var h,y,m,D,L;let g=String((h=d.branchId)!=null?h:"").trim(),f=String((y=d.tenantId)!=null?y:"").trim();try{let b=await t(`${n}/api/v1/auth/login`,{method:"POST",headers:{"Content-Type":"application/json","X-Branch-ID":g,...f?{"X-Tenant-ID":f}:{},"X-Device-ID":e.deviceId},body:JSON.stringify({username:s,password:p})}),C=await b.json().catch(()=>({}));if(!b.ok){let N=(m=C.error)!=null?m:{};throw S(String((D=N.code)!=null?D:`HTTP_${b.status}`),String((L=N.message)!=null?L:`Sync auth login gagal (${b.status})`))}return c(C,s)}catch(b){let C=b;throw S(typeof(C==null?void 0:C.code)=="string"?C.code:"SYNC_AUTH_FAILED",E(b,n,o))}},async refresh(s,p){var d,g,f;try{let h=await t(`${n}/api/v1/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json",...p?{"X-Tenant-ID":p}:{},"X-Device-ID":e.deviceId},body:JSON.stringify({refreshToken:s})}),y=await h.json().catch(()=>({}));if(!h.ok){let m=(d=y.error)!=null?d:{};throw S(String((g=m.code)!=null?g:`HTTP_${h.status}`),String((f=m.message)!=null?f:`Sync auth refresh gagal (${h.status})`))}return c(y,"")}catch(h){let y=h;throw S(typeof(y==null?void 0:y.code)=="string"?y.code:"SYNC_AUTH_REFRESH_FAILED",E(h,n,o))}}}}
|
|
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(),s={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(s),s}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),s;try{s=await this.transport.pull({scope:e,cursor:i,limit:this.policy.batchSize,domain:o})}catch(a){let c=(a==null?void 0:a.code)||"",d=a==null?void 0:a.details,h=ht(d==null?void 0:d.cutoffTime);if(c!=="SYNC_RESYNC_REQUIRED"||!h)throw a;await this.checkpointStore.set(n,h),i=h,s=await this.transport.pull({scope:e,cursor:i,limit:this.policy.batchSize,domain:o})}let u=(s.changes||[]).map(a=>{let c=String(a.domain||"").trim().toLowerCase();if(!c)return{...a,domain:o};if(c!==o)throw new Error(`Sync pull domain mismatch: expected ${o}, received ${c}`);return a});await this.applyRemoteChanges(u,e),s.nextCursor!==void 0&&await this.checkpointStore.set(n,s.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(s=>({...s,syncing:!1,lastError:o,lastErrorCode:n||null})),r}).finally(()=>{this.orchestratorSyncPromise=null}),this.orchestratorSyncPromise)}async configureOrchestratorSocket(e){var s;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=(s=this.runtime.registry)==null?void 0:s.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 c=a instanceof Error?a.message:String(a),d=String((h=a==null?void 0:a.code)!=null?h:"");this.orchestratorStateStore.setState(l=>({...l,lastError:c,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 s=i instanceof Error?i.message:String(i),u=String((a=i==null?void 0:i.code)!=null?a:"");this.orchestratorStateStore.setState(c=>({...c,lastError:s,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 s=String(i.change.domain||"").trim().toLowerCase()||"__unknown__";o.has(s)||o.set(s,[]),(n=o.get(s))==null||n.push(i)}for(let[i,s]of o.entries()){if(i==="__unknown__"){for(let h of s){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:s,domain:i}),a=new Set((u.applied||[]).map(h=>h.changeId)),c=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 s){if(a.has(h.change.id)){await this.outboxStore.remove(h.outboxId);continue}let l=c.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 s of i){let u=String(s.domain||"").trim()||o,a={...s,domain:u,scope:s.scope||e},c=`${a.domain}:${a.id}`;await this.enqueueLocalChange(a,c)}}}async applyRemoteChanges(e,r){if(!Array.isArray(e)||e.length===0)return;let o=[...e].sort((i,s)=>i.occurredAt.localeCompare(s.occurredAt)),n=new Map;for(let i of o){n.has(i.domain)||n.set(i.domain,[]);let s=n.get(i.domain);s&&s.push(i)}for(let[i,s]of n.entries()){let u=this.bridges.get(i);if(u!=null&&u.applyRemoteChanges&&(await u.applyRemoteChanges(s,r),this.projectionHook))for(let a of s)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 s of e){let u=[...this.conflictResolvers],a=null;for(let c of u){let d=await c(s,{scope:r});if(d){a=d;break}}a||(a=M(s)),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,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 s=Ct(e,r);if(s&&(n["x-product-family-id"]=s),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,c,d;let o=r||{},n=((a=o.error)==null?void 0:a.code)||"SYNC_TRANSPORT_ERROR",i=((c=o.error)==null?void 0:c.message)||`Sync transport failed with status ${e}`,s=!!((d=o.error)!=null&&d.retryable),u=B(o.error)&&B(o.error.details)?o.error.details:void 0;throw new E(n,i,s,e,u)}async push(e){let r={changes:e.outbox.map(c=>{let d=B(c.change.payload)?c.change.payload:{},h=typeof d.table=="string"?d.table.trim():"",l=B(d.record)?d.record:null,y=Date.parse(c.change.occurredAt);return h&&l?{id:c.change.id,table:h,type:c.change.type,record:{...l,id:c.change.recordId||String(l.id||c.change.id)},updated_at:Number.isFinite(y)?y:c.change.occurredAt}:{id:c.change.id,entity:c.change.entity,type:c.change.type,data:{id:c.change.recordId,...d},timestamp:c.change.occurredAt}})},o=JSON.stringify(r),n=await pt(o),i=this.resolveDomainPathOrThrow(e.domain),s=await this.buildHeaders(e.scope,e.outbox,i);s["x-data-checksum"]=n;let u=await this.options.httpAdapter.request({method:"POST",headers:s,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 s=i.data||{},u=Array.isArray(s.changes)?s.changes.map(c=>{var l,y,f,g;let d=typeof c.table=="string"?c.table:"",h=typeof c.domain=="string"&&c.domain.trim().length>0?c.domain:d.startsWith("auth_")?"ofauth":d.startsWith("offinance_")||d.startsWith("ofinance_")?"offinance":d.startsWith("ofcoop_")?"ofcoop":d.startsWith("ofpos_")?"ofpos":"unknown";return{id:String(c.changeId||c.id||""),domain:String(h),entity:String(c.entity||d||""),type:String(c.type||c.action||"UPDATE"),recordId:String(c.recordId||((l=c.record)==null?void 0:l.id)||((y=c.data)==null?void 0:y.id)||c.id||""),payload:typeof d=="string"&&d.length>0?{table:String(d),action:String(c.action||c.type||"UPDATE"),record:{...c.record||c.data||{},id:String(c.recordId||((f=c.record)==null?void 0:f.id)||((g=c.data)==null?void 0:g.id)||c.id||"")}}:c.record||c.data||{},occurredAt:String(c.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 s=i.data||{};return Array.isArray(s.conflicts)?s.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},s=this.normalizeDomainPath(e.domain||""),u=await this.buildHeaders(e.scope,void 0,s||void 0),a=s?this.normalizeUrl(`/sync/${s}/conflicts/resolve`):this.normalizeUrl("/sync/conflicts/resolve"),c=await this.options.httpAdapter.request({method:"POST",url:a,headers:u,body:i});c.status>=400&&this.parseFailure(c.status,c.data);let d=Array.isArray((h=c.data)==null?void 0:h.remaining)?c.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(),s=i==="s"?1e3:i==="m"?6e4:i==="h"?36e5:864e5;return Math.max(1,n)*s}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(),s=String((a=t.domainId)!=null?a:"").trim();return i&&(n.tenantId=i),s&&(n.domainId=s),{url:`${o}/ws/sync-notify`,query:n}}async function kt(t){var n,i,s,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((s=t.session)!=null&&s.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,c;let e=(u=t.fetchImpl)!=null?u:fetch,r=J(t.baseUrl),o=(a=t.accessTokenFallbackTtlMs)!=null?a:9e5,n=(c=t.networkErrorMessage)!=null?c: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),s=(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 s(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 s(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,s,u,a;let e=(i=t.triggerInitialPull)!=null?i:(async c=>{await w(c).trigger("manual")});if(!t.runtimeReady||!t.serverSyncEnabled||!t.isAuthenticated){if(t.lastStartSignatureRef.current="",(s=t.setWsConnected)==null||s.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,s,u,a,c,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((s=t.setStage)==null||s.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=(c=t.missingAccessTokenMessage)!=null?c:"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 s,u,a,c,d,h,l,y,f,g,S,m;let o=e;if(e=r.syncing,(s=t.setSyncing)==null||s.call(t,r.syncing),(u=t.setWsConnected)==null||u.call(t,r.wsConnected),!r.lastError){if(o&&!r.syncing){let k=(c=(a=t.getPullAppliedCount)==null?void 0:a.call(t))!=null?c: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}))})}
|
|
@@ -31,9 +31,11 @@ export interface SyncTransport {
|
|
|
31
31
|
}): Promise<SyncPullResult>;
|
|
32
32
|
listConflicts?(params: {
|
|
33
33
|
scope: SyncScope;
|
|
34
|
+
domain?: string;
|
|
34
35
|
}): Promise<ConflictItem[]>;
|
|
35
36
|
resolveConflicts?(params: {
|
|
36
37
|
scope: SyncScope;
|
|
38
|
+
domain?: string;
|
|
37
39
|
resolutions: Array<{
|
|
38
40
|
conflictId: string;
|
|
39
41
|
action: 'APPLY_REMOTE' | 'KEEP_LOCAL' | 'MANUAL_MERGE';
|