mindoodb 0.0.2 → 0.0.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 +92 -19
- package/dist/__mocks__/expo-standard-web-crypto.d.ts +10 -0
- package/dist/__mocks__/expo-standard-web-crypto.d.ts.map +1 -0
- package/dist/__mocks__/expo-standard-web-crypto.js +22 -0
- package/dist/__mocks__/expo-standard-web-crypto.js.map +1 -0
- package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.d.ts +8 -0
- package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.d.ts.map +1 -0
- package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.js +16 -0
- package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.js.map +1 -0
- package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.d.ts +1 -1
- package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.d.ts.map +1 -1
- package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.js +2 -2
- package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.js.map +1 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts +11 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts.map +1 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js +94 -0
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js.map +1 -1
- package/dist/appendonlystores/network/HttpTransport.d.ts +13 -2
- package/dist/appendonlystores/network/HttpTransport.d.ts.map +1 -1
- package/dist/appendonlystores/network/HttpTransport.js +86 -0
- package/dist/appendonlystores/network/HttpTransport.js.map +1 -1
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts +19 -2
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts.map +1 -1
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js +96 -0
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js.map +1 -1
- package/dist/appendonlystores/network/index.d.ts +1 -1
- package/dist/appendonlystores/network/index.d.ts.map +1 -1
- package/dist/appendonlystores/network/index.js.map +1 -1
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.d.ts +61 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.d.ts.map +1 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.js +685 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.js.map +1 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.d.ts +25 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.d.ts.map +1 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.js +41 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.js.map +1 -0
- package/dist/browser/index.d.ts +5 -0
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +13 -1
- package/dist/browser/index.js.map +1 -1
- package/dist/core/BaseMindooDB.d.ts +12 -0
- package/dist/core/BaseMindooDB.d.ts.map +1 -1
- package/dist/core/BaseMindooDB.js +210 -108
- package/dist/core/BaseMindooDB.js.map +1 -1
- package/dist/core/BaseMindooTenant.d.ts +40 -6
- package/dist/core/BaseMindooTenant.d.ts.map +1 -1
- package/dist/core/BaseMindooTenant.js +179 -21
- package/dist/core/BaseMindooTenant.js.map +1 -1
- package/dist/core/BaseMindooTenantDirectory.d.ts.map +1 -1
- package/dist/core/BaseMindooTenantDirectory.js +10 -0
- package/dist/core/BaseMindooTenantDirectory.js.map +1 -1
- package/dist/core/BaseMindooTenantFactory.d.ts +22 -28
- package/dist/core/BaseMindooTenantFactory.d.ts.map +1 -1
- package/dist/core/BaseMindooTenantFactory.js +161 -65
- package/dist/core/BaseMindooTenantFactory.js.map +1 -1
- package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.d.ts +68 -0
- package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.d.ts.map +1 -0
- package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.js +659 -0
- package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.js.map +1 -0
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.d.ts +43 -3
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.d.ts.map +1 -1
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.js +162 -23
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.js.map +1 -1
- package/dist/core/appendonlystores/bloom.d.ts +85 -0
- package/dist/core/appendonlystores/bloom.d.ts.map +1 -0
- package/dist/core/appendonlystores/bloom.js +228 -0
- package/dist/core/appendonlystores/bloom.js.map +1 -0
- package/dist/core/appendonlystores/network/NetworkTransport.d.ts +22 -2
- package/dist/core/appendonlystores/network/NetworkTransport.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/types.d.ts +10 -0
- package/dist/core/appendonlystores/network/types.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/types.js.map +1 -1
- package/dist/core/appendonlystores/types.d.ts +123 -0
- package/dist/core/appendonlystores/types.d.ts.map +1 -1
- package/dist/core/automerge-adapter.d.ts +15 -0
- package/dist/core/automerge-adapter.d.ts.map +1 -0
- package/dist/core/automerge-adapter.js +73 -0
- package/dist/core/automerge-adapter.js.map +1 -0
- package/dist/core/crypto/pbkdf2Iterations.d.ts +12 -0
- package/dist/core/crypto/pbkdf2Iterations.d.ts.map +1 -0
- package/dist/core/crypto/pbkdf2Iterations.js +55 -0
- package/dist/core/crypto/pbkdf2Iterations.js.map +1 -0
- package/dist/core/index.d.ts +7 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +30 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/keys/KeyBag.d.ts +32 -12
- package/dist/core/keys/KeyBag.d.ts.map +1 -1
- package/dist/core/keys/KeyBag.js +86 -26
- package/dist/core/keys/KeyBag.js.map +1 -1
- package/dist/core/keys/KeyContext.d.ts +4 -0
- package/dist/core/keys/KeyContext.d.ts.map +1 -0
- package/dist/core/keys/KeyContext.js +11 -0
- package/dist/core/keys/KeyContext.js.map +1 -0
- package/dist/core/types.d.ts +218 -50
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/uri/MindooURI.d.ts +58 -0
- package/dist/core/uri/MindooURI.d.ts.map +1 -0
- package/dist/core/uri/MindooURI.js +169 -0
- package/dist/core/uri/MindooURI.js.map +1 -0
- package/dist/core/userid/index.d.ts +0 -10
- package/dist/core/userid/index.d.ts.map +1 -1
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.d.ts +541 -0
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.d.ts.map +1 -0
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js +1334 -0
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js.map +1 -0
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +5 -1
- package/dist/node/index.js.map +1 -1
- package/dist/reactnative/crypto/AESAdapter.d.ts +18 -0
- package/dist/reactnative/crypto/AESAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/AESAdapter.js +112 -0
- package/dist/reactnative/crypto/AESAdapter.js.map +1 -0
- package/dist/reactnative/crypto/Ed25519Adapter.d.ts +34 -0
- package/dist/reactnative/crypto/Ed25519Adapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/Ed25519Adapter.js +386 -0
- package/dist/reactnative/crypto/Ed25519Adapter.js.map +1 -0
- package/dist/reactnative/crypto/ExpoGoCryptoAdapter.d.ts +23 -0
- package/dist/reactnative/crypto/ExpoGoCryptoAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/ExpoGoCryptoAdapter.js +51 -0
- package/dist/reactnative/crypto/ExpoGoCryptoAdapter.js.map +1 -0
- package/dist/reactnative/crypto/HMACAdapter.d.ts +13 -0
- package/dist/reactnative/crypto/HMACAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/HMACAdapter.js +50 -0
- package/dist/reactnative/crypto/HMACAdapter.js.map +1 -0
- package/dist/reactnative/crypto/KeyFormatConverter.d.ts +21 -0
- package/dist/reactnative/crypto/KeyFormatConverter.d.ts.map +1 -0
- package/dist/reactnative/crypto/KeyFormatConverter.js +72 -0
- package/dist/reactnative/crypto/KeyFormatConverter.js.map +1 -0
- package/dist/reactnative/crypto/PBKDF2Adapter.d.ts +19 -0
- package/dist/reactnative/crypto/PBKDF2Adapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/PBKDF2Adapter.js +67 -0
- package/dist/reactnative/crypto/PBKDF2Adapter.js.map +1 -0
- package/dist/reactnative/crypto/QuickCryptoAdapter.d.ts +37 -0
- package/dist/reactnative/crypto/QuickCryptoAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/QuickCryptoAdapter.js +72 -0
- package/dist/reactnative/crypto/QuickCryptoAdapter.js.map +1 -0
- package/dist/reactnative/crypto/RSAAdapter.d.ts +38 -0
- package/dist/reactnative/crypto/RSAAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/RSAAdapter.js +184 -0
- package/dist/reactnative/crypto/RSAAdapter.js.map +1 -0
- package/dist/reactnative/crypto/ReactNativeCryptoAdapter.d.ts +33 -0
- package/dist/reactnative/crypto/ReactNativeCryptoAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/ReactNativeCryptoAdapter.js +61 -0
- package/dist/reactnative/crypto/ReactNativeCryptoAdapter.js.map +1 -0
- package/dist/reactnative/crypto/SHA256Adapter.d.ts +9 -0
- package/dist/reactnative/crypto/SHA256Adapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/SHA256Adapter.js +29 -0
- package/dist/reactnative/crypto/SHA256Adapter.js.map +1 -0
- package/dist/reactnative/crypto/SubtleCryptoPolyfill.d.ts +63 -0
- package/dist/reactnative/crypto/SubtleCryptoPolyfill.d.ts.map +1 -0
- package/dist/reactnative/crypto/SubtleCryptoPolyfill.js +350 -0
- package/dist/reactnative/crypto/SubtleCryptoPolyfill.js.map +1 -0
- package/dist/reactnative/index.d.ts +8 -0
- package/dist/reactnative/index.d.ts.map +1 -0
- package/dist/reactnative/index.js +38 -0
- package/dist/reactnative/index.js.map +1 -0
- package/package.json +51 -6
- package/patches/PATCHES.md +111 -0
- package/patches/react-native+0.76.9.patch +34 -0
- package/patches/react-native-quick-crypto+1.0.7.patch +42 -0
- package/scripts/cli.js +21 -0
- package/scripts/setup-react-native.js +228 -0
- package/templates/reactnative/App.js +252 -0
- package/templates/reactnative/app.json +29 -0
- package/templates/reactnative/babel.config.js +6 -0
- package/templates/reactnative/index.js +26 -0
- package/templates/reactnative/metro.config.snippet.js +39 -0
- package/templates/reactnative/mindoodb-polyfills.js +116 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { StatusBar } from 'expo-status-bar';
|
|
3
|
+
import {
|
|
4
|
+
StyleSheet,
|
|
5
|
+
Text,
|
|
6
|
+
View,
|
|
7
|
+
Button,
|
|
8
|
+
ScrollView,
|
|
9
|
+
ActivityIndicator,
|
|
10
|
+
Platform,
|
|
11
|
+
} from 'react-native';
|
|
12
|
+
|
|
13
|
+
// Lazy-load MindooDB to avoid blocking app registration
|
|
14
|
+
let BaseMindooTenantFactory, InMemoryContentAddressedStoreFactory, KeyBag, PUBLIC_INFOS_KEY_ID;
|
|
15
|
+
let QuickCryptoAdapter = null;
|
|
16
|
+
let ReactNativeCryptoAdapter = null;
|
|
17
|
+
let mindoodbLoaded = false;
|
|
18
|
+
let mindoodbLoadError = null;
|
|
19
|
+
|
|
20
|
+
function loadMindooDB() {
|
|
21
|
+
if (mindoodbLoaded) return;
|
|
22
|
+
try {
|
|
23
|
+
const mindoodb = require('mindoodb');
|
|
24
|
+
BaseMindooTenantFactory = mindoodb.BaseMindooTenantFactory;
|
|
25
|
+
InMemoryContentAddressedStoreFactory = mindoodb.InMemoryContentAddressedStoreFactory;
|
|
26
|
+
KeyBag = mindoodb.KeyBag;
|
|
27
|
+
PUBLIC_INFOS_KEY_ID = mindoodb.PUBLIC_INFOS_KEY_ID;
|
|
28
|
+
QuickCryptoAdapter = mindoodb.QuickCryptoAdapter;
|
|
29
|
+
ReactNativeCryptoAdapter = mindoodb.ReactNativeCryptoAdapter;
|
|
30
|
+
mindoodbLoaded = true;
|
|
31
|
+
} catch (error) {
|
|
32
|
+
mindoodbLoadError = error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default function App() {
|
|
37
|
+
const [testStatus, setTestStatus] = useState('idle');
|
|
38
|
+
const [testResults, setTestResults] = useState('');
|
|
39
|
+
const [isRunning, setIsRunning] = useState(false);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (!mindoodbLoaded && !mindoodbLoadError) loadMindooDB();
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
45
|
+
const log = (msg) => setTestResults((prev) => prev + msg + '\n');
|
|
46
|
+
|
|
47
|
+
const runTest = async () => {
|
|
48
|
+
setIsRunning(true);
|
|
49
|
+
setTestStatus('running');
|
|
50
|
+
setTestResults('Starting test...\n');
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
if (!mindoodbLoaded) throw new Error(mindoodbLoadError?.message || 'MindooDB not loaded');
|
|
54
|
+
|
|
55
|
+
// ── Step 1: Create store factory ──
|
|
56
|
+
log('Creating store factory...');
|
|
57
|
+
const storeFactory = new InMemoryContentAddressedStoreFactory();
|
|
58
|
+
|
|
59
|
+
// ── Step 2: Create crypto adapter ──
|
|
60
|
+
log(`Creating crypto adapter (${Platform.OS})...`);
|
|
61
|
+
const quickCrypto = require('react-native-quick-crypto');
|
|
62
|
+
const cryptoAdapter = new QuickCryptoAdapter(quickCrypto);
|
|
63
|
+
const subtle = cryptoAdapter.getSubtle();
|
|
64
|
+
log('Crypto adapter ready');
|
|
65
|
+
|
|
66
|
+
// ── Native crypto diagnostics ──
|
|
67
|
+
log('Running native crypto diagnostics...');
|
|
68
|
+
try {
|
|
69
|
+
const hash = quickCrypto.createHash('sha256');
|
|
70
|
+
hash.update('mindoodb-test');
|
|
71
|
+
hash.digest('hex');
|
|
72
|
+
log(' Hash.digest() works');
|
|
73
|
+
} catch (e) {
|
|
74
|
+
log(` Hash.digest() failed: ${e.message}`);
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
await quickCrypto.subtle.digest('SHA-256', new Uint8Array([9, 8, 7, 6]));
|
|
78
|
+
log(' SubtleCrypto.digest() works');
|
|
79
|
+
} catch (e) {
|
|
80
|
+
log(` SubtleCrypto.digest() failed: ${e.message}`);
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
quickCrypto.getRandomValues(new Uint8Array(8));
|
|
84
|
+
log(' getRandomValues() works');
|
|
85
|
+
} catch (e) {
|
|
86
|
+
log(` getRandomValues() failed: ${e.message}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ── Step 3: Create tenant factory ──
|
|
90
|
+
log('Creating tenant factory...');
|
|
91
|
+
const factory = new BaseMindooTenantFactory(storeFactory, cryptoAdapter);
|
|
92
|
+
|
|
93
|
+
// ── Step 4: Create admin user + keys ──
|
|
94
|
+
log('Creating admin user...');
|
|
95
|
+
const adminUserPassword = 'adminpass123';
|
|
96
|
+
const adminUser = await factory.createUserId('CN=admin/O=testtenant', adminUserPassword);
|
|
97
|
+
log('Admin user created');
|
|
98
|
+
|
|
99
|
+
log('Creating key pairs...');
|
|
100
|
+
const adminSigningKeyPair = await factory.createSigningKeyPair('signingpass');
|
|
101
|
+
const adminEncryptionKeyPair = await factory.createEncryptionKeyPair('encpass');
|
|
102
|
+
const tenantEncryptionKey = await factory.createSymmetricEncryptedPrivateKey('tenantpass');
|
|
103
|
+
const publicInfosKey = await factory.createSymmetricEncryptedPrivateKey('publicinfospass');
|
|
104
|
+
|
|
105
|
+
log('Creating KeyBag...');
|
|
106
|
+
const adminKeyBag = new KeyBag(
|
|
107
|
+
adminUser.userEncryptionKeyPair.privateKey,
|
|
108
|
+
adminUserPassword,
|
|
109
|
+
cryptoAdapter
|
|
110
|
+
);
|
|
111
|
+
await adminKeyBag.decryptAndImportKey(PUBLIC_INFOS_KEY_ID, publicInfosKey, 'publicinfospass');
|
|
112
|
+
|
|
113
|
+
// ── Step 5: Open tenant ──
|
|
114
|
+
log('Opening tenant...');
|
|
115
|
+
const tenant = await factory.openTenantWithKeys(
|
|
116
|
+
'test-tenant',
|
|
117
|
+
tenantEncryptionKey,
|
|
118
|
+
'tenantpass',
|
|
119
|
+
adminSigningKeyPair.publicKey,
|
|
120
|
+
adminEncryptionKeyPair.publicKey,
|
|
121
|
+
adminUser,
|
|
122
|
+
adminUserPassword,
|
|
123
|
+
adminKeyBag
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
log('Registering admin user...');
|
|
127
|
+
const directory = await tenant.openDirectory();
|
|
128
|
+
await directory.registerUser(
|
|
129
|
+
factory.toPublicUserId(adminUser),
|
|
130
|
+
adminSigningKeyPair.privateKey,
|
|
131
|
+
'signingpass'
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// ── Step 6: Document test ──
|
|
135
|
+
log('\n=== Document Test ===');
|
|
136
|
+
const db = await tenant.openDB('test-db');
|
|
137
|
+
const numDocs = 10;
|
|
138
|
+
const createdDocs = [];
|
|
139
|
+
const baseTime = Date.now();
|
|
140
|
+
|
|
141
|
+
log(`Creating ${numDocs} documents...`);
|
|
142
|
+
for (let i = 0; i < numDocs; i++) {
|
|
143
|
+
const doc = await db.createDocument();
|
|
144
|
+
const docId = doc.getId();
|
|
145
|
+
await db.changeDoc(doc, (d) => {
|
|
146
|
+
const data = d.getData();
|
|
147
|
+
data.index = i;
|
|
148
|
+
data.timestamp = baseTime + i;
|
|
149
|
+
});
|
|
150
|
+
const updatedDoc = await db.getDocument(docId);
|
|
151
|
+
createdDocs.push({ docId, lastModified: updatedDoc.getLastModified() });
|
|
152
|
+
await new Promise((r) => setTimeout(r, 1));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
await db.syncStoreChanges();
|
|
156
|
+
|
|
157
|
+
log('Iterating documents...');
|
|
158
|
+
const processedDocs = [];
|
|
159
|
+
for await (const { doc, cursor } of db.iterateChangesSince(null)) {
|
|
160
|
+
processedDocs.push({ docId: doc.getId(), lastModified: doc.getLastModified(), cursor });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ── Verify ──
|
|
164
|
+
log('\n=== Results ===');
|
|
165
|
+
const allProcessed = processedDocs.length === numDocs;
|
|
166
|
+
log(`Documents: ${processedDocs.length}/${numDocs} ${allProcessed ? 'PASS' : 'FAIL'}`);
|
|
167
|
+
|
|
168
|
+
let orderCorrect = true;
|
|
169
|
+
for (let i = 1; i < processedDocs.length; i++) {
|
|
170
|
+
const prev = processedDocs[i - 1];
|
|
171
|
+
const curr = processedDocs[i];
|
|
172
|
+
if (prev.lastModified === curr.lastModified) {
|
|
173
|
+
if (prev.docId.localeCompare(curr.docId) > 0) { orderCorrect = false; break; }
|
|
174
|
+
} else if (prev.lastModified > curr.lastModified) { orderCorrect = false; break; }
|
|
175
|
+
}
|
|
176
|
+
log(`Order: ${orderCorrect ? 'PASS' : 'FAIL'}`);
|
|
177
|
+
|
|
178
|
+
let cursorCorrect = true;
|
|
179
|
+
for (const result of processedDocs) {
|
|
180
|
+
if (result.cursor.docId !== result.docId || result.cursor.lastModified !== result.lastModified) {
|
|
181
|
+
cursorCorrect = false;
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
log(`Cursors: ${cursorCorrect ? 'PASS' : 'FAIL'}`);
|
|
186
|
+
|
|
187
|
+
if (allProcessed && orderCorrect && cursorCorrect) {
|
|
188
|
+
setTestStatus('success');
|
|
189
|
+
log('\n=== TEST PASSED ===');
|
|
190
|
+
console.log('=== TEST PASSED ===');
|
|
191
|
+
} else {
|
|
192
|
+
setTestStatus('error');
|
|
193
|
+
log('\n=== TEST FAILED ===');
|
|
194
|
+
console.error('=== TEST FAILED ===');
|
|
195
|
+
}
|
|
196
|
+
} catch (error) {
|
|
197
|
+
setTestStatus('error');
|
|
198
|
+
log(`\nERROR: ${error.message}\n${error.stack || ''}`);
|
|
199
|
+
console.error('Test error:', error);
|
|
200
|
+
} finally {
|
|
201
|
+
setIsRunning(false);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
<View style={styles.container}>
|
|
207
|
+
<StatusBar style="auto" />
|
|
208
|
+
<Text style={styles.title}>MindooDB Test App</Text>
|
|
209
|
+
<Text style={styles.subtitle}>React Native Integration Test</Text>
|
|
210
|
+
|
|
211
|
+
<View style={styles.buttonContainer}>
|
|
212
|
+
<Button
|
|
213
|
+
title={isRunning ? 'Running Test...' : 'Run Test (10 docs)'}
|
|
214
|
+
onPress={runTest}
|
|
215
|
+
disabled={isRunning}
|
|
216
|
+
color="#007AFF"
|
|
217
|
+
/>
|
|
218
|
+
</View>
|
|
219
|
+
|
|
220
|
+
{isRunning && (
|
|
221
|
+
<View style={styles.loadingContainer}>
|
|
222
|
+
<ActivityIndicator size="large" color="#007AFF" />
|
|
223
|
+
<Text style={styles.loadingText}>Running test...</Text>
|
|
224
|
+
</View>
|
|
225
|
+
)}
|
|
226
|
+
|
|
227
|
+
{testStatus !== 'idle' && (
|
|
228
|
+
<View style={styles.resultsContainer}>
|
|
229
|
+
<Text style={styles.resultsTitle}>Test Results:</Text>
|
|
230
|
+
<ScrollView style={styles.resultsScroll}>
|
|
231
|
+
<Text style={styles.resultsText}>{testResults}</Text>
|
|
232
|
+
</ScrollView>
|
|
233
|
+
</View>
|
|
234
|
+
)}
|
|
235
|
+
</View>
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const styles = StyleSheet.create({
|
|
240
|
+
container: { flex: 1, backgroundColor: '#fff', padding: 20, paddingTop: 60 },
|
|
241
|
+
title: { fontSize: 24, fontWeight: 'bold', marginBottom: 5, textAlign: 'center' },
|
|
242
|
+
subtitle: { fontSize: 16, color: '#666', textAlign: 'center', marginBottom: 30 },
|
|
243
|
+
buttonContainer: { marginBottom: 20 },
|
|
244
|
+
loadingContainer: { alignItems: 'center', marginVertical: 20 },
|
|
245
|
+
loadingText: { marginTop: 10, color: '#666' },
|
|
246
|
+
resultsContainer: {
|
|
247
|
+
flex: 1, borderWidth: 1, borderColor: '#ddd', borderRadius: 8, padding: 10, marginTop: 10,
|
|
248
|
+
},
|
|
249
|
+
resultsTitle: { fontSize: 18, fontWeight: 'bold', marginBottom: 10 },
|
|
250
|
+
resultsScroll: { flex: 1 },
|
|
251
|
+
resultsText: { fontSize: 12, fontFamily: 'monospace', color: '#333' },
|
|
252
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"expo": {
|
|
3
|
+
"name": "my-mindoodb-app",
|
|
4
|
+
"slug": "my-mindoodb-app",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"orientation": "portrait",
|
|
7
|
+
"icon": "./assets/splash-icon.png",
|
|
8
|
+
"userInterfaceStyle": "light",
|
|
9
|
+
"splash": {
|
|
10
|
+
"image": "./assets/splash-icon.png",
|
|
11
|
+
"resizeMode": "contain",
|
|
12
|
+
"backgroundColor": "#ffffff"
|
|
13
|
+
},
|
|
14
|
+
"assetBundlePatterns": ["**/*"],
|
|
15
|
+
"ios": {
|
|
16
|
+
"supportsTablet": true,
|
|
17
|
+
"bundleIdentifier": "com.example.myapp",
|
|
18
|
+
"jsEngine": "jsc"
|
|
19
|
+
},
|
|
20
|
+
"android": {
|
|
21
|
+
"adaptiveIcon": {
|
|
22
|
+
"foregroundImage": "./assets/adaptive-icon.png",
|
|
23
|
+
"backgroundColor": "#ffffff"
|
|
24
|
+
},
|
|
25
|
+
"package": "com.example.myapp"
|
|
26
|
+
},
|
|
27
|
+
"newArchEnabled": true
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// MindooDB React Native Entry Point
|
|
2
|
+
//
|
|
3
|
+
// This file loads polyfills, initializes native Automerge via UseApi(nativeApi),
|
|
4
|
+
// then registers the app. Use this as a reference for your own index.js.
|
|
5
|
+
|
|
6
|
+
// Polyfills MUST be imported first (atob/btoa, crypto, TextDecoder, URL)
|
|
7
|
+
import './mindoodb-polyfills';
|
|
8
|
+
|
|
9
|
+
// Initialize native Automerge before registering the app
|
|
10
|
+
(async () => {
|
|
11
|
+
try {
|
|
12
|
+
const { UseApi } = require('@automerge/automerge/slim');
|
|
13
|
+
const { nativeApi } = require('react-native-automerge-generated');
|
|
14
|
+
UseApi(nativeApi);
|
|
15
|
+
console.log('Automerge native backend initialized');
|
|
16
|
+
|
|
17
|
+
// Register the app
|
|
18
|
+
const registerRootComponent = require('expo/src/launch/registerRootComponent').default;
|
|
19
|
+
const App = require('./App').default;
|
|
20
|
+
registerRootComponent(App);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.error('MindooDB initialization failed:', error.message);
|
|
23
|
+
console.error(error.stack);
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
})();
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// ── MindooDB Metro Configuration ─────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// Add these settings to your metro.config.js.
|
|
4
|
+
// This snippet assumes you already have:
|
|
5
|
+
// const { getDefaultConfig } = require('expo/metro-config');
|
|
6
|
+
// const config = getDefaultConfig(__dirname);
|
|
7
|
+
//
|
|
8
|
+
// Paste the sections below BEFORE `module.exports = config;`
|
|
9
|
+
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
// Enable package exports for conditional imports (react-native, browser, node)
|
|
13
|
+
config.resolver.unstable_enablePackageExports = true;
|
|
14
|
+
|
|
15
|
+
// Prefer react-native field, then browser, then module, then main
|
|
16
|
+
config.resolver.resolverMainFields = ['react-native', 'browser', 'module', 'main'];
|
|
17
|
+
|
|
18
|
+
// Custom resolver: keep Automerge on the slim build for UseApi(nativeApi)
|
|
19
|
+
// and avoid accidental WASM/fullfat entrypoint usage.
|
|
20
|
+
const originalResolveRequest = config.resolver.resolveRequest;
|
|
21
|
+
|
|
22
|
+
config.resolver.resolveRequest = (context, moduleName, platform) => {
|
|
23
|
+
// Redirect @automerge/automerge and subpaths to slim.cjs
|
|
24
|
+
if (moduleName === '@automerge/automerge' || moduleName.startsWith('@automerge/automerge/')) {
|
|
25
|
+
const slim = path.resolve(
|
|
26
|
+
__dirname,
|
|
27
|
+
'node_modules/@automerge/automerge/dist/cjs/slim.cjs'
|
|
28
|
+
);
|
|
29
|
+
if (require('fs').existsSync(slim)) {
|
|
30
|
+
return { filePath: slim, type: 'sourceFile' };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Default resolution
|
|
35
|
+
if (originalResolveRequest) {
|
|
36
|
+
return originalResolveRequest(context, moduleName, platform);
|
|
37
|
+
}
|
|
38
|
+
return context.resolveRequest(context, moduleName, platform);
|
|
39
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MindooDB Polyfills for React Native
|
|
3
|
+
*
|
|
4
|
+
* Import this file at the very top of your entry point (index.js),
|
|
5
|
+
* BEFORE any other imports:
|
|
6
|
+
*
|
|
7
|
+
* import "./mindoodb-polyfills";
|
|
8
|
+
*
|
|
9
|
+
* These polyfills are required because JavaScriptCore (JSC) on iOS
|
|
10
|
+
* does not provide atob/btoa, TextDecoder/TextEncoder, or the URL API.
|
|
11
|
+
* Automerge's base64-encoded WASM module needs atob to decode itself.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// ── atob / btoa ─────────────────────────────────────────────────────────
|
|
15
|
+
// JSC does not provide these. Required by Automerge's base64 WASM loader.
|
|
16
|
+
|
|
17
|
+
const base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
18
|
+
|
|
19
|
+
function atobPolyfill(input) {
|
|
20
|
+
if (typeof input !== 'string') throw new TypeError('Expected string');
|
|
21
|
+
const str = input.replace(/[\s]/g, '');
|
|
22
|
+
if (str.length % 4 !== 0) throw new Error('Invalid base64 string');
|
|
23
|
+
|
|
24
|
+
let output = '';
|
|
25
|
+
for (let i = 0; i < str.length; i += 4) {
|
|
26
|
+
const a = base64Chars.indexOf(str.charAt(i));
|
|
27
|
+
const b = base64Chars.indexOf(str.charAt(i + 1));
|
|
28
|
+
const c = base64Chars.indexOf(str.charAt(i + 2));
|
|
29
|
+
const d = base64Chars.indexOf(str.charAt(i + 3));
|
|
30
|
+
if (a === -1 || b === -1 || c === -1 || d === -1) throw new Error('Invalid base64 character');
|
|
31
|
+
output += String.fromCharCode((a << 2) | (b >> 4));
|
|
32
|
+
if (c !== 64) output += String.fromCharCode(((b & 15) << 4) | (c >> 2));
|
|
33
|
+
if (d !== 64) output += String.fromCharCode(((c & 3) << 6) | d);
|
|
34
|
+
}
|
|
35
|
+
return output;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function btoaPolyfill(input) {
|
|
39
|
+
if (typeof input !== 'string') throw new TypeError('Expected string');
|
|
40
|
+
let output = '';
|
|
41
|
+
for (let i = 0; i < input.length; i += 3) {
|
|
42
|
+
const a = input.charCodeAt(i);
|
|
43
|
+
const b = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
|
|
44
|
+
const c = i + 2 < input.length ? input.charCodeAt(i + 2) : 0;
|
|
45
|
+
output += base64Chars.charAt(a >> 2);
|
|
46
|
+
output += base64Chars.charAt(((a & 3) << 4) | (b >> 4));
|
|
47
|
+
output += i + 1 < input.length ? base64Chars.charAt(((b & 15) << 2) | (c >> 6)) : '=';
|
|
48
|
+
output += i + 2 < input.length ? base64Chars.charAt(c & 63) : '=';
|
|
49
|
+
}
|
|
50
|
+
return output;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (typeof global.atob === 'undefined') global.atob = atobPolyfill;
|
|
54
|
+
if (typeof global.btoa === 'undefined') global.btoa = btoaPolyfill;
|
|
55
|
+
if (typeof globalThis !== 'undefined') {
|
|
56
|
+
if (typeof globalThis.atob === 'undefined') globalThis.atob = atobPolyfill;
|
|
57
|
+
if (typeof globalThis.btoa === 'undefined') globalThis.btoa = btoaPolyfill;
|
|
58
|
+
}
|
|
59
|
+
if (typeof window !== 'undefined') {
|
|
60
|
+
if (typeof window.atob === 'undefined') window.atob = atobPolyfill;
|
|
61
|
+
if (typeof window.btoa === 'undefined') window.btoa = btoaPolyfill;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ── Crypto ──────────────────────────────────────────────────────────────
|
|
65
|
+
// expo-standard-web-crypto provides getRandomValues; react-native-quick-crypto
|
|
66
|
+
// provides the full Web Crypto API (including subtle) via NitroModules.
|
|
67
|
+
|
|
68
|
+
import { polyfillWebCrypto } from 'expo-standard-web-crypto';
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const quickCrypto = require('react-native-quick-crypto');
|
|
72
|
+
if (typeof quickCrypto.install === 'function') {
|
|
73
|
+
quickCrypto.install();
|
|
74
|
+
}
|
|
75
|
+
} catch (_) {
|
|
76
|
+
// react-native-quick-crypto not available — MindooDB will fall back to JS crypto
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (typeof window !== 'undefined') {
|
|
80
|
+
polyfillWebCrypto();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Ensure crypto is reachable on all global objects
|
|
84
|
+
if (typeof crypto !== 'undefined') {
|
|
85
|
+
if (typeof window !== 'undefined' && !window.crypto) window.crypto = crypto;
|
|
86
|
+
if (typeof global !== 'undefined' && !global.crypto) global.crypto = crypto;
|
|
87
|
+
if (typeof globalThis !== 'undefined' && !globalThis.crypto) globalThis.crypto = crypto;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ── TextDecoder / TextEncoder ───────────────────────────────────────────
|
|
91
|
+
// Required before URL polyfill (whatwg-url needs TextDecoder).
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const textEncoding = require('text-encoding');
|
|
95
|
+
if (textEncoding.TextDecoder) {
|
|
96
|
+
global.TextDecoder = textEncoding.TextDecoder;
|
|
97
|
+
global.TextEncoder = textEncoding.TextEncoder;
|
|
98
|
+
if (typeof globalThis !== 'undefined') {
|
|
99
|
+
globalThis.TextDecoder = textEncoding.TextDecoder;
|
|
100
|
+
globalThis.TextEncoder = textEncoding.TextEncoder;
|
|
101
|
+
}
|
|
102
|
+
if (typeof window !== 'undefined') {
|
|
103
|
+
window.TextDecoder = textEncoding.TextDecoder;
|
|
104
|
+
window.TextEncoder = textEncoding.TextEncoder;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} catch (e) {
|
|
108
|
+
console.error('MindooDB: Failed to load text-encoding polyfill:', e.message);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ── URL ─────────────────────────────────────────────────────────────────
|
|
112
|
+
try {
|
|
113
|
+
require('react-native-url-polyfill/auto');
|
|
114
|
+
} catch (_) {
|
|
115
|
+
// URL polyfill not available — may not be needed depending on usage
|
|
116
|
+
}
|