@techfinityedge/koolbase-react-native 1.6.1
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 +282 -0
- package/dist/analytics.d.ts +26 -0
- package/dist/analytics.js +138 -0
- package/dist/apple-auth.d.ts +22 -0
- package/dist/apple-auth.js +74 -0
- package/dist/auth.d.ts +24 -0
- package/dist/auth.js +85 -0
- package/dist/cache-store.d.ts +11 -0
- package/dist/cache-store.js +136 -0
- package/dist/code-push.d.ts +51 -0
- package/dist/code-push.js +234 -0
- package/dist/database.d.ts +15 -0
- package/dist/database.js +161 -0
- package/dist/flags.d.ts +15 -0
- package/dist/flags.js +76 -0
- package/dist/functions.d.ts +7 -0
- package/dist/functions.js +64 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +165 -0
- package/dist/logic-engine.d.ts +17 -0
- package/dist/logic-engine.js +193 -0
- package/dist/messaging.d.ts +20 -0
- package/dist/messaging.js +58 -0
- package/dist/realtime.d.ts +11 -0
- package/dist/realtime.js +52 -0
- package/dist/storage.d.ts +12 -0
- package/dist/storage.js +62 -0
- package/dist/sync-engine.d.ts +15 -0
- package/dist/sync-engine.js +83 -0
- package/dist/types.d.ts +122 -0
- package/dist/types.js +9 -0
- package/package.json +43 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.hashQuery = hashQuery;
|
|
7
|
+
exports.getCached = getCached;
|
|
8
|
+
exports.setCached = setCached;
|
|
9
|
+
exports.invalidateCache = invalidateCache;
|
|
10
|
+
exports.clearUserCache = clearUserCache;
|
|
11
|
+
exports.getWriteQueue = getWriteQueue;
|
|
12
|
+
exports.addToWriteQueue = addToWriteQueue;
|
|
13
|
+
exports.removeFromWriteQueue = removeFromWriteQueue;
|
|
14
|
+
exports.incrementWriteRetry = incrementWriteRetry;
|
|
15
|
+
exports.optimisticallyInsert = optimisticallyInsert;
|
|
16
|
+
const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
|
|
17
|
+
const CACHE_VERSION = 'v1';
|
|
18
|
+
function cacheKey(userId, collection, queryHash) {
|
|
19
|
+
return `koolbase:${CACHE_VERSION}:${userId}:${collection}:${queryHash}`;
|
|
20
|
+
}
|
|
21
|
+
function writeQueueKey(userId) {
|
|
22
|
+
return `koolbase:${CACHE_VERSION}:${userId}:write_queue`;
|
|
23
|
+
}
|
|
24
|
+
function hashQuery(collection, options) {
|
|
25
|
+
return `${collection}:${JSON.stringify(options)}`;
|
|
26
|
+
}
|
|
27
|
+
// ─── Cache ──────────────────────────────────────────────────────────────────
|
|
28
|
+
async function getCached(userId, collection, queryHash) {
|
|
29
|
+
try {
|
|
30
|
+
const raw = await async_storage_1.default.getItem(cacheKey(userId, collection, queryHash));
|
|
31
|
+
if (!raw)
|
|
32
|
+
return null;
|
|
33
|
+
return JSON.parse(raw);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function setCached(userId, collection, queryHash, result) {
|
|
40
|
+
try {
|
|
41
|
+
await async_storage_1.default.setItem(cacheKey(userId, collection, queryHash), JSON.stringify(result));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// ignore storage errors
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function invalidateCache(userId, collection) {
|
|
48
|
+
try {
|
|
49
|
+
const keys = await async_storage_1.default.getAllKeys();
|
|
50
|
+
const prefix = `koolbase:${CACHE_VERSION}:${userId}:${collection}:`;
|
|
51
|
+
const toDelete = keys.filter(k => k.startsWith(prefix));
|
|
52
|
+
for (const key of toDelete) {
|
|
53
|
+
await async_storage_1.default.removeItem(key);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// ignore
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function clearUserCache(userId) {
|
|
61
|
+
try {
|
|
62
|
+
const keys = await async_storage_1.default.getAllKeys();
|
|
63
|
+
const prefix = `koolbase:${CACHE_VERSION}:${userId}:`;
|
|
64
|
+
const toDelete = keys.filter(k => k.startsWith(prefix));
|
|
65
|
+
for (const key of toDelete) {
|
|
66
|
+
await async_storage_1.default.removeItem(key);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// ignore
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// ─── Write Queue ────────────────────────────────────────────────────────────
|
|
74
|
+
async function getWriteQueue(userId) {
|
|
75
|
+
try {
|
|
76
|
+
const raw = await async_storage_1.default.getItem(writeQueueKey(userId));
|
|
77
|
+
if (!raw)
|
|
78
|
+
return [];
|
|
79
|
+
return JSON.parse(raw);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function addToWriteQueue(userId, write) {
|
|
86
|
+
try {
|
|
87
|
+
const queue = await getWriteQueue(userId);
|
|
88
|
+
queue.push({ ...write, retries: 0, createdAt: new Date().toISOString() });
|
|
89
|
+
await async_storage_1.default.setItem(writeQueueKey(userId), JSON.stringify(queue));
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// ignore
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function removeFromWriteQueue(userId, writeId) {
|
|
96
|
+
try {
|
|
97
|
+
const queue = await getWriteQueue(userId);
|
|
98
|
+
const updated = queue.filter(w => w.id !== writeId);
|
|
99
|
+
await async_storage_1.default.setItem(writeQueueKey(userId), JSON.stringify(updated));
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// ignore
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function incrementWriteRetry(userId, writeId) {
|
|
106
|
+
try {
|
|
107
|
+
const queue = await getWriteQueue(userId);
|
|
108
|
+
const updated = queue.map(w => w.id === writeId ? { ...w, retries: w.retries + 1 } : w);
|
|
109
|
+
// Drop writes that have exceeded 3 retries
|
|
110
|
+
const filtered = updated.filter(w => w.retries <= 3);
|
|
111
|
+
await async_storage_1.default.setItem(writeQueueKey(userId), JSON.stringify(filtered));
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// ignore
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// ─── Optimistic cache update ─────────────────────────────────────────────────
|
|
118
|
+
async function optimisticallyInsert(userId, collection, record) {
|
|
119
|
+
try {
|
|
120
|
+
const keys = await async_storage_1.default.getAllKeys();
|
|
121
|
+
const prefix = `koolbase:${CACHE_VERSION}:${userId}:${collection}:`;
|
|
122
|
+
const collectionKeys = keys.filter(k => k.startsWith(prefix));
|
|
123
|
+
for (const key of collectionKeys) {
|
|
124
|
+
const raw = await async_storage_1.default.getItem(key);
|
|
125
|
+
if (!raw)
|
|
126
|
+
continue;
|
|
127
|
+
const cached = JSON.parse(raw);
|
|
128
|
+
cached.records = [record, ...cached.records];
|
|
129
|
+
cached.total = cached.total + 1;
|
|
130
|
+
await async_storage_1.default.setItem(key, JSON.stringify(cached));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// ignore
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { KoolbaseConfig } from './types';
|
|
2
|
+
export interface BundlePayload {
|
|
3
|
+
config: Record<string, unknown>;
|
|
4
|
+
flags: Record<string, boolean>;
|
|
5
|
+
directives: Record<string, unknown>;
|
|
6
|
+
assets: {
|
|
7
|
+
images: string[];
|
|
8
|
+
json: string[];
|
|
9
|
+
fonts: string[];
|
|
10
|
+
};
|
|
11
|
+
screens?: Record<string, string>;
|
|
12
|
+
flows?: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
export interface BundleManifest {
|
|
15
|
+
bundle_id: string;
|
|
16
|
+
app_id: string;
|
|
17
|
+
version: number;
|
|
18
|
+
base_app_version: string;
|
|
19
|
+
max_app_version: string;
|
|
20
|
+
platform: string;
|
|
21
|
+
channel: string;
|
|
22
|
+
checksum: string;
|
|
23
|
+
signature: string;
|
|
24
|
+
size_bytes: number;
|
|
25
|
+
payload: BundlePayload;
|
|
26
|
+
}
|
|
27
|
+
export declare class KoolbaseCodePush {
|
|
28
|
+
private config;
|
|
29
|
+
private channel;
|
|
30
|
+
private activeManifest;
|
|
31
|
+
constructor(config: KoolbaseConfig, channel?: string);
|
|
32
|
+
init(options: {
|
|
33
|
+
appVersion: string;
|
|
34
|
+
platform: string;
|
|
35
|
+
deviceId: string;
|
|
36
|
+
}): Promise<void>;
|
|
37
|
+
private promotePendingIfAvailable;
|
|
38
|
+
private loadActive;
|
|
39
|
+
private checkInBackground;
|
|
40
|
+
private download;
|
|
41
|
+
private handleRollback;
|
|
42
|
+
get hasActiveBundle(): boolean;
|
|
43
|
+
get manifest(): BundleManifest | null;
|
|
44
|
+
getBundleConfig(key: string): unknown | undefined;
|
|
45
|
+
getBundleFlag(key: string): boolean | undefined;
|
|
46
|
+
getBundleDirective(key: string): unknown | undefined;
|
|
47
|
+
private directiveHandlers;
|
|
48
|
+
onDirective(key: string, handler: (value: unknown) => void): void;
|
|
49
|
+
applyDirectives(): void;
|
|
50
|
+
clearBundle(): Promise<void>;
|
|
51
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.KoolbaseCodePush = void 0;
|
|
40
|
+
const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
|
|
41
|
+
const STORAGE_KEY_ACTIVE = 'koolbase:codepush:active';
|
|
42
|
+
const STORAGE_KEY_PENDING = 'koolbase:codepush:pending';
|
|
43
|
+
// ─── Pure JS sha256 for checksum verification ────────────────────────────────
|
|
44
|
+
async function sha256Hex(data) {
|
|
45
|
+
// Use SubtleCrypto if available (modern RN / Hermes)
|
|
46
|
+
if (typeof crypto !== 'undefined' && crypto.subtle) {
|
|
47
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
48
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
49
|
+
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
50
|
+
}
|
|
51
|
+
// Fallback — skip verification (dev only)
|
|
52
|
+
return '';
|
|
53
|
+
}
|
|
54
|
+
// ─── Zip manifest extraction ─────────────────────────────────────────────────
|
|
55
|
+
async function extractManifestFromZip(data) {
|
|
56
|
+
try {
|
|
57
|
+
// Dynamically import JSZip — must be installed as a dependency
|
|
58
|
+
const JSZip = (await Promise.resolve().then(() => __importStar(require('jszip')))).default;
|
|
59
|
+
const zip = await JSZip.loadAsync(data);
|
|
60
|
+
const manifestFile = zip.file('manifest.json');
|
|
61
|
+
if (!manifestFile)
|
|
62
|
+
return null;
|
|
63
|
+
const json = await manifestFile.async('string');
|
|
64
|
+
return JSON.parse(json);
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
console.warn('[KoolbaseCodePush] manifest extraction failed:', e);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// ─── KoolbaseCodePush ────────────────────────────────────────────────────────
|
|
72
|
+
class KoolbaseCodePush {
|
|
73
|
+
constructor(config, channel = 'stable') {
|
|
74
|
+
this.activeManifest = null;
|
|
75
|
+
// ─── Directive handling ──────────────────────────────────────────────────
|
|
76
|
+
this.directiveHandlers = new Map();
|
|
77
|
+
this.config = config;
|
|
78
|
+
this.channel = channel;
|
|
79
|
+
}
|
|
80
|
+
// Called inside Koolbase.initialize() — loads cached bundle then checks in background
|
|
81
|
+
async init(options) {
|
|
82
|
+
// Step 1 — promote pending → active if available
|
|
83
|
+
await this.promotePendingIfAvailable();
|
|
84
|
+
// Step 2 — load active manifest into memory
|
|
85
|
+
this.activeManifest = await this.loadActive();
|
|
86
|
+
if (this.activeManifest) {
|
|
87
|
+
console.log(`[KoolbaseCodePush] bundle v${this.activeManifest.version} active`);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
console.log('[KoolbaseCodePush] no active bundle — using app defaults');
|
|
91
|
+
}
|
|
92
|
+
// Step 3 — check for updates in background (non-blocking)
|
|
93
|
+
this.checkInBackground(options);
|
|
94
|
+
}
|
|
95
|
+
async promotePendingIfAvailable() {
|
|
96
|
+
try {
|
|
97
|
+
const pending = await async_storage_1.default.getItem(STORAGE_KEY_PENDING);
|
|
98
|
+
if (!pending)
|
|
99
|
+
return;
|
|
100
|
+
// Archive current active → just overwrite, we keep one version
|
|
101
|
+
await async_storage_1.default.setItem(STORAGE_KEY_ACTIVE, pending);
|
|
102
|
+
await async_storage_1.default.removeItem(STORAGE_KEY_PENDING);
|
|
103
|
+
console.log('[KoolbaseCodePush] promoted pending bundle to active');
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
console.warn('[KoolbaseCodePush] promotion failed:', e);
|
|
107
|
+
// One re-check will be triggered naturally by background check
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async loadActive() {
|
|
111
|
+
try {
|
|
112
|
+
const stored = await async_storage_1.default.getItem(STORAGE_KEY_ACTIVE);
|
|
113
|
+
if (!stored)
|
|
114
|
+
return null;
|
|
115
|
+
return JSON.parse(stored);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
checkInBackground(options) {
|
|
122
|
+
// Fire and forget
|
|
123
|
+
(async () => {
|
|
124
|
+
try {
|
|
125
|
+
const currentVersion = this.activeManifest?.version ?? 0;
|
|
126
|
+
const res = await fetch(`${this.config.baseUrl}/v1/code-push/check` +
|
|
127
|
+
`?app_version=${options.appVersion}` +
|
|
128
|
+
`&platform=${options.platform}` +
|
|
129
|
+
`&channel=${this.channel}` +
|
|
130
|
+
`&device_id=${options.deviceId}` +
|
|
131
|
+
`¤t_bundle=${currentVersion}`, { headers: { 'x-api-key': this.config.publicKey } });
|
|
132
|
+
if (!res.ok)
|
|
133
|
+
return;
|
|
134
|
+
const body = await res.json();
|
|
135
|
+
switch (body.status) {
|
|
136
|
+
case 'update_available':
|
|
137
|
+
await this.download(body.bundle);
|
|
138
|
+
break;
|
|
139
|
+
case 'rollback':
|
|
140
|
+
await this.handleRollback(body.revert_to ?? '0');
|
|
141
|
+
break;
|
|
142
|
+
case 'no_update':
|
|
143
|
+
console.log('[KoolbaseCodePush] no update available');
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
// Server unreachable — continue silently
|
|
149
|
+
console.log('[KoolbaseCodePush] check failed silently:', e);
|
|
150
|
+
}
|
|
151
|
+
})();
|
|
152
|
+
}
|
|
153
|
+
async download(ref) {
|
|
154
|
+
try {
|
|
155
|
+
console.log(`[KoolbaseCodePush] downloading bundle v${ref.version}...`);
|
|
156
|
+
const res = await fetch(ref.download_url);
|
|
157
|
+
if (!res.ok) {
|
|
158
|
+
console.warn('[KoolbaseCodePush] download failed:', res.status);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const data = await res.arrayBuffer();
|
|
162
|
+
console.log(`[KoolbaseCodePush] downloaded ${data.byteLength} bytes`);
|
|
163
|
+
// Verify checksum
|
|
164
|
+
if (ref.checksum !== 'placeholder' && ref.checksum !== 'pending') {
|
|
165
|
+
const actual = `sha256:${await sha256Hex(data)}`;
|
|
166
|
+
if (actual !== ref.checksum && actual !== 'sha256:') {
|
|
167
|
+
console.warn('[KoolbaseCodePush] checksum mismatch — bundle rejected');
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
console.log('[KoolbaseCodePush] checksum verified');
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
console.log('[KoolbaseCodePush] checksum skipped (dev mode)');
|
|
174
|
+
}
|
|
175
|
+
// Extract manifest
|
|
176
|
+
const manifest = await extractManifestFromZip(data);
|
|
177
|
+
if (!manifest) {
|
|
178
|
+
console.warn('[KoolbaseCodePush] manifest extraction failed');
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
// Store as pending — activates on next launch
|
|
182
|
+
await async_storage_1.default.setItem(STORAGE_KEY_PENDING, JSON.stringify(manifest));
|
|
183
|
+
console.log(`[KoolbaseCodePush] bundle v${manifest.version} ready for next launch`);
|
|
184
|
+
}
|
|
185
|
+
catch (e) {
|
|
186
|
+
console.warn('[KoolbaseCodePush] download error:', e);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async handleRollback(revertTo) {
|
|
190
|
+
console.log(`[KoolbaseCodePush] rollback to v${revertTo}`);
|
|
191
|
+
await async_storage_1.default.removeItem(STORAGE_KEY_ACTIVE);
|
|
192
|
+
await async_storage_1.default.removeItem(STORAGE_KEY_PENDING);
|
|
193
|
+
this.activeManifest = null;
|
|
194
|
+
console.log('[KoolbaseCodePush] reverted to app defaults');
|
|
195
|
+
}
|
|
196
|
+
// ─── Public accessors ────────────────────────────────────────────────────
|
|
197
|
+
get hasActiveBundle() {
|
|
198
|
+
return this.activeManifest !== null;
|
|
199
|
+
}
|
|
200
|
+
get manifest() {
|
|
201
|
+
return this.activeManifest;
|
|
202
|
+
}
|
|
203
|
+
getBundleConfig(key) {
|
|
204
|
+
return this.activeManifest?.payload.config[key];
|
|
205
|
+
}
|
|
206
|
+
getBundleFlag(key) {
|
|
207
|
+
return this.activeManifest?.payload.flags[key];
|
|
208
|
+
}
|
|
209
|
+
getBundleDirective(key) {
|
|
210
|
+
return this.activeManifest?.payload.directives[key];
|
|
211
|
+
}
|
|
212
|
+
onDirective(key, handler) {
|
|
213
|
+
this.directiveHandlers.set(key, handler);
|
|
214
|
+
}
|
|
215
|
+
applyDirectives() {
|
|
216
|
+
if (!this.activeManifest?.payload.directives)
|
|
217
|
+
return;
|
|
218
|
+
for (const [key, value] of Object.entries(this.activeManifest.payload.directives)) {
|
|
219
|
+
const handler = this.directiveHandlers.get(key);
|
|
220
|
+
if (handler) {
|
|
221
|
+
console.log(`[KoolbaseCodePush] directive: ${key} = ${value}`);
|
|
222
|
+
handler(value);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// ─── Cache management ────────────────────────────────────────────────────
|
|
227
|
+
async clearBundle() {
|
|
228
|
+
await async_storage_1.default.removeItem(STORAGE_KEY_ACTIVE);
|
|
229
|
+
await async_storage_1.default.removeItem(STORAGE_KEY_PENDING);
|
|
230
|
+
this.activeManifest = null;
|
|
231
|
+
console.log('[KoolbaseCodePush] bundle cache cleared');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
exports.KoolbaseCodePush = KoolbaseCodePush;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { KoolbaseConfig, KoolbaseRecord, QueryOptions, QueryResult } from './types';
|
|
2
|
+
export declare class KoolbaseDatabase {
|
|
3
|
+
private config;
|
|
4
|
+
private getUserId;
|
|
5
|
+
private syncEngine;
|
|
6
|
+
constructor(config: KoolbaseConfig, getUserId: () => string | null);
|
|
7
|
+
private get headers();
|
|
8
|
+
private request;
|
|
9
|
+
query(collection: string, options?: QueryOptions): Promise<QueryResult>;
|
|
10
|
+
insert(collection: string, data: Record<string, unknown>): Promise<KoolbaseRecord>;
|
|
11
|
+
get(recordId: string): Promise<KoolbaseRecord>;
|
|
12
|
+
update(recordId: string, data: Record<string, unknown>): Promise<KoolbaseRecord>;
|
|
13
|
+
delete(recordId: string): Promise<void>;
|
|
14
|
+
syncPendingWrites(): Promise<void>;
|
|
15
|
+
}
|
package/dist/database.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KoolbaseDatabase = void 0;
|
|
4
|
+
const cache_store_1 = require("./cache-store");
|
|
5
|
+
const sync_engine_1 = require("./sync-engine");
|
|
6
|
+
function generateId() {
|
|
7
|
+
return 'local_' + Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
8
|
+
}
|
|
9
|
+
class KoolbaseDatabase {
|
|
10
|
+
constructor(config, getUserId) {
|
|
11
|
+
this.config = config;
|
|
12
|
+
this.getUserId = getUserId;
|
|
13
|
+
this.syncEngine = new sync_engine_1.SyncEngine(config, getUserId);
|
|
14
|
+
this.syncEngine.start();
|
|
15
|
+
}
|
|
16
|
+
get headers() {
|
|
17
|
+
const userId = this.getUserId();
|
|
18
|
+
return {
|
|
19
|
+
'Content-Type': 'application/json',
|
|
20
|
+
'x-api-key': this.config.publicKey,
|
|
21
|
+
...(userId ? { 'x-user-id': userId } : {}),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async request(method, path, body) {
|
|
25
|
+
const res = await fetch(`${this.config.baseUrl}${path}`, {
|
|
26
|
+
method,
|
|
27
|
+
headers: this.headers,
|
|
28
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
29
|
+
});
|
|
30
|
+
const data = await res.json();
|
|
31
|
+
if (!res.ok) {
|
|
32
|
+
throw new Error(data.error ?? `Request failed: ${res.status}`);
|
|
33
|
+
}
|
|
34
|
+
return data;
|
|
35
|
+
}
|
|
36
|
+
// ─── Query (cache-first) ───────────────────────────────────────────────────
|
|
37
|
+
async query(collection, options = {}) {
|
|
38
|
+
const userId = this.getUserId() ?? 'anonymous';
|
|
39
|
+
const queryHash = (0, cache_store_1.hashQuery)(collection, options);
|
|
40
|
+
// 1. Return cached data immediately if available
|
|
41
|
+
const cached = await (0, cache_store_1.getCached)(userId, collection, queryHash);
|
|
42
|
+
// 2. Fetch from network in background
|
|
43
|
+
this.request('POST', '/v1/sdk/db/query', {
|
|
44
|
+
collection,
|
|
45
|
+
filters: options.filters ?? {},
|
|
46
|
+
limit: options.limit ?? 20,
|
|
47
|
+
offset: options.offset ?? 0,
|
|
48
|
+
order_by: options.orderBy,
|
|
49
|
+
order_desc: options.orderDesc ?? false,
|
|
50
|
+
populate: options.populate ?? [],
|
|
51
|
+
})
|
|
52
|
+
.then(async (result) => {
|
|
53
|
+
await (0, cache_store_1.setCached)(userId, collection, queryHash, result);
|
|
54
|
+
})
|
|
55
|
+
.catch(() => {
|
|
56
|
+
// Network unavailable — cached data is already returned
|
|
57
|
+
});
|
|
58
|
+
if (cached) {
|
|
59
|
+
return { ...cached, isFromCache: true };
|
|
60
|
+
}
|
|
61
|
+
// No cache — wait for network
|
|
62
|
+
const result = await this.request('POST', '/v1/sdk/db/query', {
|
|
63
|
+
collection,
|
|
64
|
+
filters: options.filters ?? {},
|
|
65
|
+
limit: options.limit ?? 20,
|
|
66
|
+
offset: options.offset ?? 0,
|
|
67
|
+
order_by: options.orderBy,
|
|
68
|
+
order_desc: options.orderDesc ?? false,
|
|
69
|
+
populate: options.populate ?? [],
|
|
70
|
+
});
|
|
71
|
+
await (0, cache_store_1.setCached)(userId, collection, queryHash, result);
|
|
72
|
+
return { ...result, isFromCache: false };
|
|
73
|
+
}
|
|
74
|
+
// ─── Insert (optimistic) ────────────────────────────────────────────────────
|
|
75
|
+
async insert(collection, data) {
|
|
76
|
+
const userId = this.getUserId() ?? 'anonymous';
|
|
77
|
+
// Build optimistic record
|
|
78
|
+
const optimisticRecord = {
|
|
79
|
+
id: generateId(),
|
|
80
|
+
projectId: '',
|
|
81
|
+
collectionId: '',
|
|
82
|
+
createdBy: userId,
|
|
83
|
+
data,
|
|
84
|
+
createdAt: new Date().toISOString(),
|
|
85
|
+
updatedAt: new Date().toISOString(),
|
|
86
|
+
};
|
|
87
|
+
// Write to local cache immediately
|
|
88
|
+
await (0, cache_store_1.optimisticallyInsert)(userId, collection, optimisticRecord);
|
|
89
|
+
// Add to write queue
|
|
90
|
+
await (0, cache_store_1.addToWriteQueue)(userId, {
|
|
91
|
+
id: generateId(),
|
|
92
|
+
type: 'insert',
|
|
93
|
+
collection,
|
|
94
|
+
data,
|
|
95
|
+
});
|
|
96
|
+
// Try network in background
|
|
97
|
+
this.request('POST', '/v1/sdk/db/insert', {
|
|
98
|
+
collection,
|
|
99
|
+
data,
|
|
100
|
+
})
|
|
101
|
+
.then(async (serverRecord) => {
|
|
102
|
+
// Invalidate cache so next query gets real data
|
|
103
|
+
await (0, cache_store_1.invalidateCache)(userId, collection);
|
|
104
|
+
})
|
|
105
|
+
.catch(() => {
|
|
106
|
+
// Will sync when online via SyncEngine
|
|
107
|
+
});
|
|
108
|
+
return optimisticRecord;
|
|
109
|
+
}
|
|
110
|
+
// ─── Get single record ──────────────────────────────────────────────────────
|
|
111
|
+
async get(recordId) {
|
|
112
|
+
return this.request('GET', `/v1/sdk/db/records/${recordId}`);
|
|
113
|
+
}
|
|
114
|
+
// ─── Update ─────────────────────────────────────────────────────────────────
|
|
115
|
+
async update(recordId, data) {
|
|
116
|
+
const userId = this.getUserId() ?? 'anonymous';
|
|
117
|
+
// Add to write queue
|
|
118
|
+
await (0, cache_store_1.addToWriteQueue)(userId, {
|
|
119
|
+
id: generateId(),
|
|
120
|
+
type: 'update',
|
|
121
|
+
recordId,
|
|
122
|
+
data,
|
|
123
|
+
});
|
|
124
|
+
// Try network
|
|
125
|
+
try {
|
|
126
|
+
const result = await this.request('PATCH', `/v1/sdk/db/records/${recordId}`, { data });
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Return optimistic version
|
|
131
|
+
return {
|
|
132
|
+
id: recordId,
|
|
133
|
+
projectId: '',
|
|
134
|
+
collectionId: '',
|
|
135
|
+
data,
|
|
136
|
+
createdAt: '',
|
|
137
|
+
updatedAt: new Date().toISOString(),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// ─── Delete ─────────────────────────────────────────────────────────────────
|
|
142
|
+
async delete(recordId) {
|
|
143
|
+
const userId = this.getUserId() ?? 'anonymous';
|
|
144
|
+
// Add to write queue
|
|
145
|
+
await (0, cache_store_1.addToWriteQueue)(userId, {
|
|
146
|
+
id: generateId(),
|
|
147
|
+
type: 'delete',
|
|
148
|
+
recordId,
|
|
149
|
+
});
|
|
150
|
+
// Try network
|
|
151
|
+
const res = await fetch(`${this.config.baseUrl}/v1/sdk/db/records/${recordId}`, { method: 'DELETE', headers: this.headers });
|
|
152
|
+
if (!res.ok && res.status !== 204) {
|
|
153
|
+
// Queued for sync — will retry when online
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// ─── Manual sync ────────────────────────────────────────────────────────────
|
|
157
|
+
async syncPendingWrites() {
|
|
158
|
+
await this.syncEngine.flush();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
exports.KoolbaseDatabase = KoolbaseDatabase;
|
package/dist/flags.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { KoolbaseConfig, VersionCheckResult } from './types';
|
|
2
|
+
export declare class KoolbaseFlags {
|
|
3
|
+
private config;
|
|
4
|
+
private payload;
|
|
5
|
+
private deviceId;
|
|
6
|
+
constructor(config: KoolbaseConfig, deviceId: string);
|
|
7
|
+
fetch(appVersion: string, platform: string): Promise<void>;
|
|
8
|
+
isEnabled(key: string): boolean;
|
|
9
|
+
getString(key: string, fallback?: string): string;
|
|
10
|
+
getNumber(key: string, fallback?: number): number;
|
|
11
|
+
getBool(key: string, fallback?: boolean): boolean;
|
|
12
|
+
checkVersion(currentVersion: string): VersionCheckResult;
|
|
13
|
+
private parseVersion;
|
|
14
|
+
private stableHash;
|
|
15
|
+
}
|