ofsync-bridge-offinance 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # ofsync-bridge-offinance
2
+
3
+ Bridge sinkronisasi domain **offinance** untuk runtime `ofsync-shared-core`.
4
+
5
+ Package ini menyediakan registrar bridge domain agar host app dapat menghubungkan perubahan lokal/remote domain `offinance` ke pipeline sinkronisasi host-side secara konsisten.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install ofsync-bridge-offinance
11
+ ```
12
+
13
+ ## Quick Usage
14
+
15
+ ```ts
16
+ import { registerSyncBridge } from 'ofsync-bridge-offinance';
17
+
18
+ registerSyncBridge(runtime, {
19
+ async collectSyncChanges(scope) {
20
+ return [];
21
+ },
22
+ async applySyncChanges(changes, scope) {
23
+ // map ke use-case domain offinance
24
+ },
25
+ });
26
+ ```
27
+
28
+ ## Development
29
+
30
+ ```bash
31
+ npm install --no-audit --no-fund
32
+ npm run ci:check
33
+ ```
34
+
35
+ ## Documentation
36
+
37
+ - Internal docs hub: `docs/00-DOCUMENTATION-HUB.md`
@@ -0,0 +1,62 @@
1
+ export interface SyncScope {
2
+ tenantId?: string;
3
+ branchId?: string;
4
+ deviceId?: string;
5
+ ledgerProfileId?: string;
6
+ }
7
+ export interface SyncChange {
8
+ id: string;
9
+ domain: string;
10
+ entity: string;
11
+ type: 'CREATE' | 'UPDATE' | 'DELETE';
12
+ recordId: string;
13
+ payload: Record<string, unknown>;
14
+ occurredAt: string;
15
+ scope?: SyncScope;
16
+ }
17
+ export interface DomainBridge {
18
+ domain: string;
19
+ collectLocalChanges?: (scope: SyncScope) => Promise<SyncChange[]>;
20
+ applyRemoteChanges?: (changes: SyncChange[], scope: SyncScope) => Promise<void>;
21
+ }
22
+ export interface SyncRuntimeLike {
23
+ registerDomainBridge: (bridge: DomainBridge) => void;
24
+ }
25
+ export declare const BRIDGE_DOMAIN = "offinance";
26
+ export interface DomainSyncBridgePort {
27
+ collectSyncChanges?: (scope: SyncScope) => Promise<SyncChange[]>;
28
+ applySyncChanges?: (changes: SyncChange[], scope: SyncScope) => Promise<void>;
29
+ }
30
+ export declare function createSyncBridge(port: DomainSyncBridgePort): DomainBridge;
31
+ export declare function registerSyncBridge(runtime: SyncRuntimeLike, port: DomainSyncBridgePort): void;
32
+ export interface LegacyPullRow {
33
+ table: string;
34
+ action: string;
35
+ id: string;
36
+ recordId: string;
37
+ timestamp: string;
38
+ record: Record<string, unknown>;
39
+ }
40
+ export interface ScopedDbAdapter {
41
+ delete: (table: string, id: string) => Promise<unknown>;
42
+ query: <T>(table: string, args?: unknown) => Promise<T[]>;
43
+ update: (table: string, id: string, payload: Record<string, unknown>) => Promise<unknown>;
44
+ create: (table: string, payload: Record<string, unknown>) => Promise<unknown>;
45
+ }
46
+ export declare function mapSyncChangeToLegacyPullRow(change: SyncChange): LegacyPullRow;
47
+ export declare function mapSyncChangesToLegacyPullRows(changes: SyncChange[]): LegacyPullRow[];
48
+ /**
49
+ * Applies a batch of legacy pull rows to a scoped local database adapter.
50
+ *
51
+ * Differences from the ofcoop counterpart:
52
+ * - Accepts optional `ledgerProfileId` in scope — when provided, it is
53
+ * included in both query filters and upsert payloads so ledger entries are
54
+ * isolated per financial unit (e.g. `ledger-ofcoop`, `ledger-unit-minimarket`).
55
+ *
56
+ * Returns the number of rows processed (CREATE + UPDATE + DELETE).
57
+ */
58
+ export declare function applyLegacyPullRowsToScopedDb(db: ScopedDbAdapter, rows: LegacyPullRow[], scope: {
59
+ branchId: string;
60
+ tenantId?: string;
61
+ ledgerProfileId?: string;
62
+ }): Promise<number>;
@@ -0,0 +1 @@
1
+ const g="offinance";function l(e){return{domain:g,collectLocalChanges:e.collectSyncChanges,applyRemoteChanges:e.applySyncChanges}}function S(e,r){e.registerDomainBridge(l(r))}function p(e){const r=e.payload??{},t=String(r.table??""),o=r.record&&typeof r.record=="object"?r.record:{},i=String(r.action??e.type??"UPDATE");return{table:t,action:i,id:e.recordId,recordId:e.recordId,timestamp:e.occurredAt,record:o}}function m(e){return e.map(p)}async function I(e,r,t){let o=0;const i=t.branchId.trim(),c=t.tenantId?.trim()??"",a=t.ledgerProfileId?.trim()??"",s=[{field:"branchId",value:i},...c?[{field:"tenantId",value:c}]:[],...a?[{field:"ledgerProfileId",value:a}]:[]];for(const n of r){if(!n.table||!n.recordId)continue;if(String(n.action||"").toUpperCase()==="DELETE"){try{await e.delete(n.table,n.recordId)}catch{}o+=1;continue}const d={...n.record,id:n.recordId,branchId:String(n.record.branchId??i),...c?{tenantId:String(n.record.tenantId??c)}:{},...a?{ledgerProfileId:String(n.record.ledgerProfileId??a)}:{}};(await e.query(n.table,{filters:{and:[{field:"id",value:n.recordId},...s]},limit:1})).length>0?await e.update(n.table,n.recordId,d):await e.create(n.table,d),o+=1}return o}export{g as BRIDGE_DOMAIN,I as applyLegacyPullRowsToScopedDb,l as createSyncBridge,p as mapSyncChangeToLegacyPullRow,m as mapSyncChangesToLegacyPullRows,S as registerSyncBridge};
package/dist/index.js ADDED
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BRIDGE_DOMAIN = void 0;
4
+ exports.createSyncBridge = createSyncBridge;
5
+ exports.registerSyncBridge = registerSyncBridge;
6
+ exports.mapSyncChangeToLegacyPullRow = mapSyncChangeToLegacyPullRow;
7
+ exports.mapSyncChangesToLegacyPullRows = mapSyncChangesToLegacyPullRows;
8
+ exports.applyLegacyPullRowsToScopedDb = applyLegacyPullRowsToScopedDb;
9
+ exports.BRIDGE_DOMAIN = 'offinance';
10
+ function createSyncBridge(port) {
11
+ return {
12
+ domain: exports.BRIDGE_DOMAIN,
13
+ collectLocalChanges: port.collectSyncChanges,
14
+ applyRemoteChanges: port.applySyncChanges,
15
+ };
16
+ }
17
+ function registerSyncBridge(runtime, port) {
18
+ runtime.registerDomainBridge(createSyncBridge(port));
19
+ }
20
+ function mapSyncChangeToLegacyPullRow(change) {
21
+ const payload = (change.payload ?? {});
22
+ const table = String(payload.table ?? '');
23
+ const record = payload.record && typeof payload.record === 'object'
24
+ ? payload.record
25
+ : {};
26
+ const action = String(payload.action ?? change.type ?? 'UPDATE');
27
+ return {
28
+ table,
29
+ action,
30
+ id: change.recordId,
31
+ recordId: change.recordId,
32
+ timestamp: change.occurredAt,
33
+ record,
34
+ };
35
+ }
36
+ function mapSyncChangesToLegacyPullRows(changes) {
37
+ return changes.map(mapSyncChangeToLegacyPullRow);
38
+ }
39
+ /**
40
+ * Applies a batch of legacy pull rows to a scoped local database adapter.
41
+ *
42
+ * Differences from the ofcoop counterpart:
43
+ * - Accepts optional `ledgerProfileId` in scope — when provided, it is
44
+ * included in both query filters and upsert payloads so ledger entries are
45
+ * isolated per financial unit (e.g. `ledger-ofcoop`, `ledger-unit-minimarket`).
46
+ *
47
+ * Returns the number of rows processed (CREATE + UPDATE + DELETE).
48
+ */
49
+ async function applyLegacyPullRowsToScopedDb(db, rows, scope) {
50
+ let appliedCount = 0;
51
+ const branchId = scope.branchId.trim();
52
+ const tenantId = scope.tenantId?.trim() ?? '';
53
+ const ledgerProfileId = scope.ledgerProfileId?.trim() ?? '';
54
+ const scopeFilters = [
55
+ { field: 'branchId', value: branchId },
56
+ ...(tenantId ? [{ field: 'tenantId', value: tenantId }] : []),
57
+ ...(ledgerProfileId ? [{ field: 'ledgerProfileId', value: ledgerProfileId }] : []),
58
+ ];
59
+ for (const row of rows) {
60
+ if (!row.table || !row.recordId)
61
+ continue;
62
+ const action = String(row.action || '').toUpperCase();
63
+ if (action === 'DELETE') {
64
+ try {
65
+ await db.delete(row.table, row.recordId);
66
+ }
67
+ catch {
68
+ // idempotent delete — ignore not-found errors
69
+ }
70
+ appliedCount += 1;
71
+ continue;
72
+ }
73
+ const payload = {
74
+ ...row.record,
75
+ id: row.recordId,
76
+ branchId: String(row.record.branchId ?? branchId),
77
+ ...(tenantId ? { tenantId: String(row.record.tenantId ?? tenantId) } : {}),
78
+ ...(ledgerProfileId
79
+ ? { ledgerProfileId: String(row.record.ledgerProfileId ?? ledgerProfileId) }
80
+ : {}),
81
+ };
82
+ const existing = await db.query(row.table, {
83
+ filters: { and: [{ field: 'id', value: row.recordId }, ...scopeFilters] },
84
+ limit: 1,
85
+ });
86
+ if (existing.length > 0) {
87
+ await db.update(row.table, row.recordId, payload);
88
+ }
89
+ else {
90
+ await db.create(row.table, payload);
91
+ }
92
+ appliedCount += 1;
93
+ }
94
+ return appliedCount;
95
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "ofsync-bridge-offinance",
3
+ "version": "0.1.0-alpha.0",
4
+ "private": false,
5
+ "description": "Sync bridge adapter for wiring offinance domain changes into ofsync-shared-core runtime.",
6
+ "author": {
7
+ "name": "Agus Made",
8
+ "email": "krisnaparta@gmail.com"
9
+ },
10
+ "license": "MIT",
11
+ "main": "dist/index.js",
12
+ "module": "dist/index.esm.js",
13
+ "types": "dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.esm.js",
18
+ "require": "./dist/index.js"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
26
+ "build": "npm run clean && tsc -p tsconfig.build.json && node esbuild.config.js",
27
+ "typecheck": "tsc -p tsconfig.json --noEmit",
28
+ "test": "npm run build && node --test ./tests/*.test.js",
29
+ "verify:surface": "node ./scripts/verify-surface.js",
30
+ "verify:boundary": "node ./scripts/verify-boundary-imports.js",
31
+ "ci:check": "npm run typecheck && npm run verify:surface && npm run verify:boundary && npm run test",
32
+ "prepublishOnly": "npm run ci:check",
33
+ "prepack": "npm run build"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^20.19.0",
37
+ "esbuild": "^0.25.11",
38
+ "typescript": "^5.9.3"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://gitlab.com/rintisan/sync-bridges/ofsync-bridge-offinance.git"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "peerDependencies": {
48
+ "ofsync-shared-core": "0.2.0-alpha.1"
49
+ },
50
+ "peerDependenciesMeta": {
51
+ "ofsync-shared-core": {
52
+ "optional": true
53
+ }
54
+ }
55
+ }