expo-sqlite 14.0.0 → 14.0.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/CHANGELOG.md +6 -0
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/sqlite/SQLiteModuleNext.kt +14 -0
- package/build/ExpoSQLiteNext.d.ts +1 -0
- package/build/ExpoSQLiteNext.d.ts.map +1 -1
- package/build/ExpoSQLiteNext.js +3 -0
- package/build/ExpoSQLiteNext.js.map +1 -1
- package/build/hooks.d.ts +19 -0
- package/build/hooks.d.ts.map +1 -1
- package/build/hooks.js +20 -7
- package/build/hooks.js.map +1 -1
- package/ios/SQLiteModuleNext.swift +16 -0
- package/package.json +2 -2
- package/src/ExpoSQLiteNext.ts +8 -0
- package/src/hooks.tsx +58 -5
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,12 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 14.0.1 — 2024-04-19
|
|
14
|
+
|
|
15
|
+
### 🎉 New features
|
|
16
|
+
|
|
17
|
+
- Added `SQLiteProvider.assetSource` to import an existing database from assets. ([#28291](https://github.com/expo/expo/pull/28291) by [@kudo](https://github.com/kudo))
|
|
18
|
+
|
|
13
19
|
## 14.0.0 — 2024-04-18
|
|
14
20
|
|
|
15
21
|
### 🛠 Breaking changes
|
package/android/build.gradle
CHANGED
|
@@ -4,7 +4,7 @@ apply plugin: 'com.android.library'
|
|
|
4
4
|
apply plugin: 'de.undercouch.download'
|
|
5
5
|
|
|
6
6
|
group = 'host.exp.exponent'
|
|
7
|
-
version = '14.0.
|
|
7
|
+
version = '14.0.1'
|
|
8
8
|
|
|
9
9
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
10
10
|
apply from: expoModulesCorePlugin
|
|
@@ -57,7 +57,7 @@ android {
|
|
|
57
57
|
namespace "expo.modules.sqlite"
|
|
58
58
|
defaultConfig {
|
|
59
59
|
versionCode 18
|
|
60
|
-
versionName "14.0.
|
|
60
|
+
versionName "14.0.1"
|
|
61
61
|
|
|
62
62
|
externalNativeBuild {
|
|
63
63
|
cmake {
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
package expo.modules.sqlite
|
|
4
4
|
|
|
5
5
|
import android.content.Context
|
|
6
|
+
import android.net.Uri
|
|
6
7
|
import android.util.Log
|
|
8
|
+
import androidx.core.net.toFile
|
|
7
9
|
import androidx.core.os.bundleOf
|
|
8
10
|
import expo.modules.kotlin.exception.Exceptions
|
|
9
11
|
import expo.modules.kotlin.modules.Module
|
|
@@ -50,6 +52,18 @@ class SQLiteModuleNext : Module() {
|
|
|
50
52
|
deleteDatabase(databaseName)
|
|
51
53
|
}
|
|
52
54
|
|
|
55
|
+
AsyncFunction("importAssetDatabaseAsync") { databaseName: String, assetDatabasePath: String, forceOverwrite: Boolean ->
|
|
56
|
+
val dbFile = File(pathForDatabaseName(databaseName))
|
|
57
|
+
if (dbFile.exists() && !forceOverwrite) {
|
|
58
|
+
return@AsyncFunction
|
|
59
|
+
}
|
|
60
|
+
val assetFile = Uri.parse(assetDatabasePath).toFile()
|
|
61
|
+
if (!assetFile.isFile) {
|
|
62
|
+
throw OpenDatabaseException(assetDatabasePath)
|
|
63
|
+
}
|
|
64
|
+
assetFile.copyTo(dbFile, forceOverwrite)
|
|
65
|
+
}
|
|
66
|
+
|
|
53
67
|
Class(NativeDatabase::class) {
|
|
54
68
|
Constructor { databaseName: String, options: OpenDatabaseOptions, serializedData: ByteArray? ->
|
|
55
69
|
val database: NativeDatabase
|
|
@@ -4,6 +4,7 @@ declare const _default: {
|
|
|
4
4
|
NativeStatement(): void;
|
|
5
5
|
deleteDatabaseAsync(databaseName: string): Promise<void>;
|
|
6
6
|
deleteDatabaseSync(databaseName: string): void;
|
|
7
|
+
importAssetDatabaseAsync(databaseName: string, assetDatabasePath: string, forceOverwrite: boolean): Promise<void>;
|
|
7
8
|
addListener(): never;
|
|
8
9
|
removeListeners(): never;
|
|
9
10
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoSQLiteNext.d.ts","sourceRoot":"","sources":["../src/ExpoSQLiteNext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;;iCAInC,MAAM,YACV,iBAAiB,mBACV,UAAU,GAC1B,IAAI;uBAIY,IAAI;sCAIiB,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;qCAI7B,MAAM,GAAG,IAAI;;;;
|
|
1
|
+
{"version":3,"file":"ExpoSQLiteNext.d.ts","sourceRoot":"","sources":["../src/ExpoSQLiteNext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;;iCAInC,MAAM,YACV,iBAAiB,mBACV,UAAU,GAC1B,IAAI;uBAIY,IAAI;sCAIiB,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;qCAI7B,MAAM,GAAG,IAAI;2CAK9B,MAAM,qBACD,MAAM,kBACT,OAAO,GACtB,OAAO,CAAC,IAAI,CAAC;;;;AAzBlB,wBAuCE"}
|
package/build/ExpoSQLiteNext.js
CHANGED
|
@@ -11,6 +11,9 @@ export default {
|
|
|
11
11
|
deleteDatabaseSync(databaseName) {
|
|
12
12
|
throw new Error('Unimplemented');
|
|
13
13
|
},
|
|
14
|
+
importAssetDatabaseAsync(databaseName, assetDatabasePath, forceOverwrite) {
|
|
15
|
+
throw new Error('Unimplemented');
|
|
16
|
+
},
|
|
14
17
|
//#region EventEmitter implementations
|
|
15
18
|
addListener() {
|
|
16
19
|
throw new Error('Unimplemented');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoSQLiteNext.js","sourceRoot":"","sources":["../src/ExpoSQLiteNext.ts"],"names":[],"mappings":"AAEA,eAAe;IACb,cAAc,CACZ,YAAoB,EACpB,OAA2B,EAC3B,cAA2B;QAE3B,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IAED,eAAe;QACb,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,YAAoB;QAC5C,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IAED,kBAAkB,CAAC,YAAoB;QACrC,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IAED,sCAAsC;IAEtC,WAAW;QACT,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IACD,eAAe;QACb,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IAED,YAAY;CACb,CAAC","sourcesContent":["import { SQLiteOpenOptions } from './NativeDatabase';\n\nexport default {\n NativeDatabase(\n databaseName: string,\n options?: SQLiteOpenOptions,\n serializedData?: Uint8Array\n ): void {\n throw new Error('Unimplemented');\n },\n\n NativeStatement(): void {\n throw new Error('Unimplemented');\n },\n\n async deleteDatabaseAsync(databaseName: string): Promise<void> {\n throw new Error('Unimplemented');\n },\n\n deleteDatabaseSync(databaseName: string): void {\n throw new Error('Unimplemented');\n },\n\n //#region EventEmitter implementations\n\n addListener() {\n throw new Error('Unimplemented');\n },\n removeListeners() {\n throw new Error('Unimplemented');\n },\n\n //#endregion\n};\n"]}
|
|
1
|
+
{"version":3,"file":"ExpoSQLiteNext.js","sourceRoot":"","sources":["../src/ExpoSQLiteNext.ts"],"names":[],"mappings":"AAEA,eAAe;IACb,cAAc,CACZ,YAAoB,EACpB,OAA2B,EAC3B,cAA2B;QAE3B,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IAED,eAAe;QACb,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,YAAoB;QAC5C,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IAED,kBAAkB,CAAC,YAAoB;QACrC,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IAED,wBAAwB,CACtB,YAAoB,EACpB,iBAAyB,EACzB,cAAuB;QAEvB,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IAED,sCAAsC;IAEtC,WAAW;QACT,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IACD,eAAe;QACb,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IAED,YAAY;CACb,CAAC","sourcesContent":["import { SQLiteOpenOptions } from './NativeDatabase';\n\nexport default {\n NativeDatabase(\n databaseName: string,\n options?: SQLiteOpenOptions,\n serializedData?: Uint8Array\n ): void {\n throw new Error('Unimplemented');\n },\n\n NativeStatement(): void {\n throw new Error('Unimplemented');\n },\n\n async deleteDatabaseAsync(databaseName: string): Promise<void> {\n throw new Error('Unimplemented');\n },\n\n deleteDatabaseSync(databaseName: string): void {\n throw new Error('Unimplemented');\n },\n\n importAssetDatabaseAsync(\n databaseName: string,\n assetDatabasePath: string,\n forceOverwrite: boolean\n ): Promise<void> {\n throw new Error('Unimplemented');\n },\n\n //#region EventEmitter implementations\n\n addListener() {\n throw new Error('Unimplemented');\n },\n removeListeners() {\n throw new Error('Unimplemented');\n },\n\n //#endregion\n};\n"]}
|
package/build/hooks.d.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { SQLiteOpenOptions } from './NativeDatabase';
|
|
3
3
|
import { type SQLiteDatabase } from './SQLiteDatabase';
|
|
4
|
+
export interface SQLiteProviderAssetSource {
|
|
5
|
+
/**
|
|
6
|
+
* The asset ID returned from the `require()` call.
|
|
7
|
+
*/
|
|
8
|
+
assetId: number;
|
|
9
|
+
/**
|
|
10
|
+
* Force overwrite the local database file even if it already exists.
|
|
11
|
+
* @default false
|
|
12
|
+
*/
|
|
13
|
+
forceOverwrite?: boolean;
|
|
14
|
+
}
|
|
4
15
|
export interface SQLiteProviderProps {
|
|
5
16
|
/**
|
|
6
17
|
* The name of the database file to open.
|
|
@@ -10,6 +21,14 @@ export interface SQLiteProviderProps {
|
|
|
10
21
|
* Open options.
|
|
11
22
|
*/
|
|
12
23
|
options?: SQLiteOpenOptions;
|
|
24
|
+
/**
|
|
25
|
+
* Import a bundled database file from the specified asset module.
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* assetSource={{ assetId: require('./assets/db.db') }}
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
assetSource?: SQLiteProviderAssetSource;
|
|
13
32
|
/**
|
|
14
33
|
* The children to render.
|
|
15
34
|
*/
|
package/build/hooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.tsx"],"names":[],"mappings":"AACA,OAAO,KAAiE,MAAM,OAAO,CAAC;AAGtF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE1E,MAAM,WAAW,yBAAyB;IACxC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAE5B;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,yBAAyB,CAAC;IAExC;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAE1B;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/C;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;;;;;;;;;;;;;;OAeG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAOD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,OAAO,EACP,WAAmB,EACnB,GAAG,KAAK,EACT,EAAE,mBAAmB,eAcrB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,IAAI,cAAc,CAMjD"}
|
package/build/hooks.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { Asset } from 'expo-asset';
|
|
1
2
|
import React, { createContext, useContext, useEffect, useRef, useState } from 'react';
|
|
3
|
+
import ExpoSQLite from './ExpoSQLiteNext';
|
|
2
4
|
import { openDatabaseAsync } from './SQLiteDatabase';
|
|
3
5
|
/**
|
|
4
6
|
* Create a context for the SQLite database
|
|
@@ -48,23 +50,24 @@ export function useSQLiteContext() {
|
|
|
48
50
|
return context;
|
|
49
51
|
}
|
|
50
52
|
let databaseInstance = null;
|
|
51
|
-
function SQLiteProviderSuspense({ databaseName, options, children, onInit, }) {
|
|
53
|
+
function SQLiteProviderSuspense({ databaseName, options, assetSource, children, onInit, }) {
|
|
52
54
|
const databasePromise = getDatabaseAsync({
|
|
53
55
|
databaseName,
|
|
54
56
|
options,
|
|
57
|
+
assetSource,
|
|
55
58
|
onInit,
|
|
56
59
|
});
|
|
57
60
|
const database = use(databasePromise);
|
|
58
61
|
return <SQLiteContext.Provider value={database}>{children}</SQLiteContext.Provider>;
|
|
59
62
|
}
|
|
60
|
-
function SQLiteProviderNonSuspense({ databaseName, options, children, onInit, onError, }) {
|
|
63
|
+
function SQLiteProviderNonSuspense({ databaseName, options, assetSource, children, onInit, onError, }) {
|
|
61
64
|
const databaseRef = useRef(null);
|
|
62
65
|
const [loading, setLoading] = useState(true);
|
|
63
66
|
const [error, setError] = useState(null);
|
|
64
67
|
useEffect(() => {
|
|
65
68
|
async function setup() {
|
|
66
69
|
try {
|
|
67
|
-
const db = await openDatabaseWithInitAsync({ databaseName, options, onInit });
|
|
70
|
+
const db = await openDatabaseWithInitAsync({ databaseName, options, assetSource, onInit });
|
|
68
71
|
databaseRef.current = db;
|
|
69
72
|
setLoading(false);
|
|
70
73
|
}
|
|
@@ -100,7 +103,7 @@ function SQLiteProviderNonSuspense({ databaseName, options, children, onInit, on
|
|
|
100
103
|
}
|
|
101
104
|
return <SQLiteContext.Provider value={databaseRef.current}>{children}</SQLiteContext.Provider>;
|
|
102
105
|
}
|
|
103
|
-
function getDatabaseAsync({ databaseName, options, onInit, }) {
|
|
106
|
+
function getDatabaseAsync({ databaseName, options, assetSource, onInit, }) {
|
|
104
107
|
if (databaseInstance?.promise != null &&
|
|
105
108
|
databaseInstance?.databaseName === databaseName &&
|
|
106
109
|
databaseInstance?.options === options &&
|
|
@@ -114,11 +117,11 @@ function getDatabaseAsync({ databaseName, options, onInit, }) {
|
|
|
114
117
|
db.closeAsync();
|
|
115
118
|
})
|
|
116
119
|
.then(() => {
|
|
117
|
-
return openDatabaseWithInitAsync({ databaseName, options, onInit });
|
|
120
|
+
return openDatabaseWithInitAsync({ databaseName, options, assetSource, onInit });
|
|
118
121
|
});
|
|
119
122
|
}
|
|
120
123
|
else {
|
|
121
|
-
promise = openDatabaseWithInitAsync({ databaseName, options, onInit });
|
|
124
|
+
promise = openDatabaseWithInitAsync({ databaseName, options, assetSource, onInit });
|
|
122
125
|
}
|
|
123
126
|
databaseInstance = {
|
|
124
127
|
databaseName,
|
|
@@ -128,13 +131,23 @@ function getDatabaseAsync({ databaseName, options, onInit, }) {
|
|
|
128
131
|
};
|
|
129
132
|
return promise;
|
|
130
133
|
}
|
|
131
|
-
async function openDatabaseWithInitAsync({ databaseName, options, onInit, }) {
|
|
134
|
+
async function openDatabaseWithInitAsync({ databaseName, options, assetSource, onInit, }) {
|
|
135
|
+
if (assetSource != null) {
|
|
136
|
+
await importDatabaseFromAssetAsync(databaseName, assetSource);
|
|
137
|
+
}
|
|
132
138
|
const database = await openDatabaseAsync(databaseName, options);
|
|
133
139
|
if (onInit != null) {
|
|
134
140
|
await onInit(database);
|
|
135
141
|
}
|
|
136
142
|
return database;
|
|
137
143
|
}
|
|
144
|
+
async function importDatabaseFromAssetAsync(databaseName, assetSource) {
|
|
145
|
+
const asset = await Asset.fromModule(assetSource.assetId).downloadAsync();
|
|
146
|
+
if (!asset.localUri) {
|
|
147
|
+
throw new Error(`Unable to get the localUri from asset ${assetSource.assetId}`);
|
|
148
|
+
}
|
|
149
|
+
await ExpoSQLite.importAssetDatabaseAsync(databaseName, asset.localUri, assetSource.forceOverwrite ?? false);
|
|
150
|
+
}
|
|
138
151
|
// Referenced from https://github.com/reactjs/react.dev/blob/6570e6cd79a16ac3b1a2902632eddab7e6abb9ad/src/content/reference/react/Suspense.md
|
|
139
152
|
/**
|
|
140
153
|
* A custom hook like [`React.use`](https://react.dev/reference/react/use) hook using private Suspense implementation.
|
package/build/hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { createContext, useContext, useEffect, useRef, useState } from 'react';\n\nimport type { SQLiteOpenOptions } from './NativeDatabase';\nimport { openDatabaseAsync, type SQLiteDatabase } from './SQLiteDatabase';\n\nexport interface SQLiteProviderProps {\n /**\n * The name of the database file to open.\n */\n databaseName: string;\n\n /**\n * Open options.\n */\n options?: SQLiteOpenOptions;\n\n /**\n * The children to render.\n */\n children: React.ReactNode;\n\n /**\n * A custom initialization handler to run before rendering the children.\n * You can use this to run database migrations or other setup tasks.\n */\n onInit?: (db: SQLiteDatabase) => Promise<void>;\n\n /**\n * Handle errors from SQLiteProvider.\n * @default rethrow the error\n */\n onError?: (error: Error) => void;\n\n /**\n * Enable [`React.Suspense`](https://react.dev/reference/react/Suspense) integration.\n * @default false\n * @example\n * ```tsx\n * export default function App() {\n * return (\n * <Suspense fallback={<Text>Loading...</Text>}>\n * <SQLiteProvider databaseName=\"test.db\" useSuspense={true}>\n * <Main />\n * </SQLiteProvider>\n * </Suspense>\n * );\n * }\n * ```\n */\n useSuspense?: boolean;\n}\n\n/**\n * Create a context for the SQLite database\n */\nconst SQLiteContext = createContext<SQLiteDatabase | null>(null);\n\n/**\n * Context.Provider component that provides a SQLite database to all children.\n * All descendants of this component will be able to access the database using the [`useSQLiteContext`](#usesqlitecontext) hook.\n */\nexport function SQLiteProvider({\n children,\n onError,\n useSuspense = false,\n ...props\n}: SQLiteProviderProps) {\n if (onError != null && useSuspense) {\n throw new Error('Cannot use `onError` with `useSuspense`, use error boundaries instead.');\n }\n\n if (useSuspense) {\n return <SQLiteProviderSuspense {...props}>{children}</SQLiteProviderSuspense>;\n }\n\n return (\n <SQLiteProviderNonSuspense {...props} onError={onError}>\n {children}\n </SQLiteProviderNonSuspense>\n );\n}\n\n/**\n * A global hook for accessing the SQLite database across components.\n * This hook should only be used within a [`<SQLiteProvider>`](#sqliteprovider) component.\n *\n * @example\n * ```tsx\n * export default function App() {\n * return (\n * <SQLiteProvider databaseName=\"test.db\">\n * <Main />\n * </SQLiteProvider>\n * );\n * }\n *\n * export function Main() {\n * const db = useSQLiteContext();\n * console.log('sqlite version', db.getSync('SELECT sqlite_version()'));\n * return <View />\n * }\n * ```\n */\nexport function useSQLiteContext(): SQLiteDatabase {\n const context = useContext(SQLiteContext);\n if (context == null) {\n throw new Error('useSQLiteContext must be used within a <SQLiteProvider>');\n }\n return context;\n}\n\n//#region Internals\n\ntype DatabaseInstanceType = Pick<SQLiteProviderProps, 'databaseName' | 'options' | 'onInit'> & {\n promise: Promise<SQLiteDatabase> | null;\n};\n\nlet databaseInstance: DatabaseInstanceType | null = null;\n\nfunction SQLiteProviderSuspense({\n databaseName,\n options,\n children,\n onInit,\n}: Omit<SQLiteProviderProps, 'onError' | 'useSuspense'>) {\n const databasePromise = getDatabaseAsync({\n databaseName,\n options,\n onInit,\n });\n const database = use(databasePromise);\n return <SQLiteContext.Provider value={database}>{children}</SQLiteContext.Provider>;\n}\n\nfunction SQLiteProviderNonSuspense({\n databaseName,\n options,\n children,\n onInit,\n onError,\n}: Omit<SQLiteProviderProps, 'useSuspense'>) {\n const databaseRef = useRef<SQLiteDatabase | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n async function setup() {\n try {\n const db = await openDatabaseWithInitAsync({ databaseName, options, onInit });\n databaseRef.current = db;\n setLoading(false);\n } catch (e) {\n setError(e);\n }\n }\n\n async function teardown(db: SQLiteDatabase | null) {\n try {\n await db?.closeAsync();\n } catch (e) {\n setError(e);\n }\n }\n\n setup();\n\n return () => {\n const db = databaseRef.current;\n teardown(db);\n databaseRef.current = null;\n setLoading(true);\n };\n }, [databaseName, options, onInit]);\n\n if (error != null) {\n const handler =\n onError ??\n ((e) => {\n throw e;\n });\n handler(error);\n }\n if (loading || !databaseRef.current) {\n return null;\n }\n return <SQLiteContext.Provider value={databaseRef.current}>{children}</SQLiteContext.Provider>;\n}\n\nfunction getDatabaseAsync({\n databaseName,\n options,\n onInit,\n}: Pick<SQLiteProviderProps, 'databaseName' | 'options' | 'onInit'>): Promise<SQLiteDatabase> {\n if (\n databaseInstance?.promise != null &&\n databaseInstance?.databaseName === databaseName &&\n databaseInstance?.options === options &&\n databaseInstance?.onInit === onInit\n ) {\n return databaseInstance.promise;\n }\n\n let promise: Promise<SQLiteDatabase>;\n if (databaseInstance?.promise != null) {\n promise = databaseInstance.promise\n .then((db) => {\n db.closeAsync();\n })\n .then(() => {\n return openDatabaseWithInitAsync({ databaseName, options, onInit });\n });\n } else {\n promise = openDatabaseWithInitAsync({ databaseName, options, onInit });\n }\n databaseInstance = {\n databaseName,\n options,\n onInit,\n promise,\n };\n return promise;\n}\n\nasync function openDatabaseWithInitAsync({\n databaseName,\n options,\n onInit,\n}: Pick<SQLiteProviderProps, 'databaseName' | 'options' | 'onInit'>): Promise<SQLiteDatabase> {\n const database = await openDatabaseAsync(databaseName, options);\n if (onInit != null) {\n await onInit(database);\n }\n return database;\n}\n\n//#endregion\n\n//#region Private Suspense API similar to `React.use`\n\n// Referenced from https://github.com/vercel/swr/blob/1d8110900d1aee3747199bfb377b149b7ff6848e/_internal/src/types.ts#L27-L31\ntype ReactUsePromise<T, E extends Error = Error> = Promise<T> & {\n status?: 'pending' | 'fulfilled' | 'rejected';\n value?: T;\n reason?: E;\n};\n\n// Referenced from https://github.com/reactjs/react.dev/blob/6570e6cd79a16ac3b1a2902632eddab7e6abb9ad/src/content/reference/react/Suspense.md\n/**\n * A custom hook like [`React.use`](https://react.dev/reference/react/use) hook using private Suspense implementation.\n */\nfunction use<T>(promise: Promise<T> | ReactUsePromise<T>) {\n if (isReactUsePromise(promise)) {\n if (promise.status === 'fulfilled') {\n if (promise.value === undefined) {\n throw new Error('[use] Unexpected undefined value from promise');\n }\n return promise.value;\n } else if (promise.status === 'rejected') {\n throw promise.reason;\n } else if (promise.status === 'pending') {\n throw promise;\n }\n throw new Error('[use] Promise is in an invalid state');\n }\n\n const suspensePromise = promise as ReactUsePromise<T>;\n suspensePromise.status = 'pending';\n suspensePromise.then(\n (result: T) => {\n suspensePromise.status = 'fulfilled';\n suspensePromise.value = result;\n },\n (reason) => {\n suspensePromise.status = 'rejected';\n suspensePromise.reason = reason;\n }\n );\n throw suspensePromise;\n}\n\nfunction isReactUsePromise<T>(\n promise: Promise<T> | ReactUsePromise<T>\n): promise is ReactUsePromise<T> {\n return typeof promise === 'object' && promise !== null && 'status' in promise;\n}\n\n//#endregion\n"]}
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Asset } from 'expo-asset';\nimport React, { createContext, useContext, useEffect, useRef, useState } from 'react';\n\nimport ExpoSQLite from './ExpoSQLiteNext';\nimport type { SQLiteOpenOptions } from './NativeDatabase';\nimport { openDatabaseAsync, type SQLiteDatabase } from './SQLiteDatabase';\n\nexport interface SQLiteProviderAssetSource {\n /**\n * The asset ID returned from the `require()` call.\n */\n assetId: number;\n\n /**\n * Force overwrite the local database file even if it already exists.\n * @default false\n */\n forceOverwrite?: boolean;\n}\n\nexport interface SQLiteProviderProps {\n /**\n * The name of the database file to open.\n */\n databaseName: string;\n\n /**\n * Open options.\n */\n options?: SQLiteOpenOptions;\n\n /**\n * Import a bundled database file from the specified asset module.\n * @example\n * ```ts\n * assetSource={{ assetId: require('./assets/db.db') }}\n * ```\n */\n assetSource?: SQLiteProviderAssetSource;\n\n /**\n * The children to render.\n */\n children: React.ReactNode;\n\n /**\n * A custom initialization handler to run before rendering the children.\n * You can use this to run database migrations or other setup tasks.\n */\n onInit?: (db: SQLiteDatabase) => Promise<void>;\n\n /**\n * Handle errors from SQLiteProvider.\n * @default rethrow the error\n */\n onError?: (error: Error) => void;\n\n /**\n * Enable [`React.Suspense`](https://react.dev/reference/react/Suspense) integration.\n * @default false\n * @example\n * ```tsx\n * export default function App() {\n * return (\n * <Suspense fallback={<Text>Loading...</Text>}>\n * <SQLiteProvider databaseName=\"test.db\" useSuspense={true}>\n * <Main />\n * </SQLiteProvider>\n * </Suspense>\n * );\n * }\n * ```\n */\n useSuspense?: boolean;\n}\n\n/**\n * Create a context for the SQLite database\n */\nconst SQLiteContext = createContext<SQLiteDatabase | null>(null);\n\n/**\n * Context.Provider component that provides a SQLite database to all children.\n * All descendants of this component will be able to access the database using the [`useSQLiteContext`](#usesqlitecontext) hook.\n */\nexport function SQLiteProvider({\n children,\n onError,\n useSuspense = false,\n ...props\n}: SQLiteProviderProps) {\n if (onError != null && useSuspense) {\n throw new Error('Cannot use `onError` with `useSuspense`, use error boundaries instead.');\n }\n\n if (useSuspense) {\n return <SQLiteProviderSuspense {...props}>{children}</SQLiteProviderSuspense>;\n }\n\n return (\n <SQLiteProviderNonSuspense {...props} onError={onError}>\n {children}\n </SQLiteProviderNonSuspense>\n );\n}\n\n/**\n * A global hook for accessing the SQLite database across components.\n * This hook should only be used within a [`<SQLiteProvider>`](#sqliteprovider) component.\n *\n * @example\n * ```tsx\n * export default function App() {\n * return (\n * <SQLiteProvider databaseName=\"test.db\">\n * <Main />\n * </SQLiteProvider>\n * );\n * }\n *\n * export function Main() {\n * const db = useSQLiteContext();\n * console.log('sqlite version', db.getSync('SELECT sqlite_version()'));\n * return <View />\n * }\n * ```\n */\nexport function useSQLiteContext(): SQLiteDatabase {\n const context = useContext(SQLiteContext);\n if (context == null) {\n throw new Error('useSQLiteContext must be used within a <SQLiteProvider>');\n }\n return context;\n}\n\n//#region Internals\n\ntype DatabaseInstanceType = Pick<SQLiteProviderProps, 'databaseName' | 'options' | 'onInit'> & {\n promise: Promise<SQLiteDatabase> | null;\n};\n\nlet databaseInstance: DatabaseInstanceType | null = null;\n\nfunction SQLiteProviderSuspense({\n databaseName,\n options,\n assetSource,\n children,\n onInit,\n}: Omit<SQLiteProviderProps, 'onError' | 'useSuspense'>) {\n const databasePromise = getDatabaseAsync({\n databaseName,\n options,\n assetSource,\n onInit,\n });\n const database = use(databasePromise);\n return <SQLiteContext.Provider value={database}>{children}</SQLiteContext.Provider>;\n}\n\nfunction SQLiteProviderNonSuspense({\n databaseName,\n options,\n assetSource,\n children,\n onInit,\n onError,\n}: Omit<SQLiteProviderProps, 'useSuspense'>) {\n const databaseRef = useRef<SQLiteDatabase | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n async function setup() {\n try {\n const db = await openDatabaseWithInitAsync({ databaseName, options, assetSource, onInit });\n databaseRef.current = db;\n setLoading(false);\n } catch (e) {\n setError(e);\n }\n }\n\n async function teardown(db: SQLiteDatabase | null) {\n try {\n await db?.closeAsync();\n } catch (e) {\n setError(e);\n }\n }\n\n setup();\n\n return () => {\n const db = databaseRef.current;\n teardown(db);\n databaseRef.current = null;\n setLoading(true);\n };\n }, [databaseName, options, onInit]);\n\n if (error != null) {\n const handler =\n onError ??\n ((e) => {\n throw e;\n });\n handler(error);\n }\n if (loading || !databaseRef.current) {\n return null;\n }\n return <SQLiteContext.Provider value={databaseRef.current}>{children}</SQLiteContext.Provider>;\n}\n\nfunction getDatabaseAsync({\n databaseName,\n options,\n assetSource,\n onInit,\n}: Pick<\n SQLiteProviderProps,\n 'databaseName' | 'options' | 'assetSource' | 'onInit'\n>): Promise<SQLiteDatabase> {\n if (\n databaseInstance?.promise != null &&\n databaseInstance?.databaseName === databaseName &&\n databaseInstance?.options === options &&\n databaseInstance?.onInit === onInit\n ) {\n return databaseInstance.promise;\n }\n\n let promise: Promise<SQLiteDatabase>;\n if (databaseInstance?.promise != null) {\n promise = databaseInstance.promise\n .then((db) => {\n db.closeAsync();\n })\n .then(() => {\n return openDatabaseWithInitAsync({ databaseName, options, assetSource, onInit });\n });\n } else {\n promise = openDatabaseWithInitAsync({ databaseName, options, assetSource, onInit });\n }\n databaseInstance = {\n databaseName,\n options,\n onInit,\n promise,\n };\n return promise;\n}\n\nasync function openDatabaseWithInitAsync({\n databaseName,\n options,\n assetSource,\n onInit,\n}: Pick<\n SQLiteProviderProps,\n 'databaseName' | 'options' | 'assetSource' | 'onInit'\n>): Promise<SQLiteDatabase> {\n if (assetSource != null) {\n await importDatabaseFromAssetAsync(databaseName, assetSource);\n }\n const database = await openDatabaseAsync(databaseName, options);\n if (onInit != null) {\n await onInit(database);\n }\n return database;\n}\n\nasync function importDatabaseFromAssetAsync(\n databaseName: string,\n assetSource: SQLiteProviderAssetSource\n) {\n const asset = await Asset.fromModule(assetSource.assetId).downloadAsync();\n if (!asset.localUri) {\n throw new Error(`Unable to get the localUri from asset ${assetSource.assetId}`);\n }\n await ExpoSQLite.importAssetDatabaseAsync(\n databaseName,\n asset.localUri,\n assetSource.forceOverwrite ?? false\n );\n}\n\n//#endregion\n\n//#region Private Suspense API similar to `React.use`\n\n// Referenced from https://github.com/vercel/swr/blob/1d8110900d1aee3747199bfb377b149b7ff6848e/_internal/src/types.ts#L27-L31\ntype ReactUsePromise<T, E extends Error = Error> = Promise<T> & {\n status?: 'pending' | 'fulfilled' | 'rejected';\n value?: T;\n reason?: E;\n};\n\n// Referenced from https://github.com/reactjs/react.dev/blob/6570e6cd79a16ac3b1a2902632eddab7e6abb9ad/src/content/reference/react/Suspense.md\n/**\n * A custom hook like [`React.use`](https://react.dev/reference/react/use) hook using private Suspense implementation.\n */\nfunction use<T>(promise: Promise<T> | ReactUsePromise<T>) {\n if (isReactUsePromise(promise)) {\n if (promise.status === 'fulfilled') {\n if (promise.value === undefined) {\n throw new Error('[use] Unexpected undefined value from promise');\n }\n return promise.value;\n } else if (promise.status === 'rejected') {\n throw promise.reason;\n } else if (promise.status === 'pending') {\n throw promise;\n }\n throw new Error('[use] Promise is in an invalid state');\n }\n\n const suspensePromise = promise as ReactUsePromise<T>;\n suspensePromise.status = 'pending';\n suspensePromise.then(\n (result: T) => {\n suspensePromise.status = 'fulfilled';\n suspensePromise.value = result;\n },\n (reason) => {\n suspensePromise.status = 'rejected';\n suspensePromise.reason = reason;\n }\n );\n throw suspensePromise;\n}\n\nfunction isReactUsePromise<T>(\n promise: Promise<T> | ReactUsePromise<T>\n): promise is ReactUsePromise<T> {\n return typeof promise === 'object' && promise !== null && 'status' in promise;\n}\n\n//#endregion\n"]}
|
|
@@ -46,6 +46,22 @@ public final class SQLiteModuleNext: Module {
|
|
|
46
46
|
try deleteDatabase(databaseName: databaseName)
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
AsyncFunction("importAssetDatabaseAsync") { (databaseName: String, assetDatabasePath: String, forceOverwrite: Bool) in
|
|
50
|
+
guard let path = pathForDatabaseName(name: databaseName) else {
|
|
51
|
+
throw Exceptions.FileSystemModuleNotFound()
|
|
52
|
+
}
|
|
53
|
+
let fileManager = FileManager.default
|
|
54
|
+
if fileManager.fileExists(atPath: path.absoluteString) && !forceOverwrite {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
guard let assetPath = URL(string: assetDatabasePath)?.path,
|
|
58
|
+
fileManager.fileExists(atPath: assetPath) else {
|
|
59
|
+
throw DatabaseNotFoundException(assetDatabasePath)
|
|
60
|
+
}
|
|
61
|
+
try? fileManager.removeItem(atPath: path.absoluteString)
|
|
62
|
+
try fileManager.copyItem(atPath: assetPath, toPath: path.absoluteString)
|
|
63
|
+
}
|
|
64
|
+
|
|
49
65
|
// swiftlint:disable:next closure_body_length
|
|
50
66
|
Class(NativeDatabase.self) {
|
|
51
67
|
Constructor { (databaseName: String, options: OpenDatabaseOptions, serializedData: Data?) -> NativeDatabase in
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-sqlite",
|
|
3
|
-
"version": "14.0.
|
|
3
|
+
"version": "14.0.1",
|
|
4
4
|
"description": "Provides access to a database that can be queried through a WebSQL-like API (https://www.w3.org/TR/webdatabase/). The database is persisted across restarts of your app.",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"expo": "*"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "75658dcbae535ec32cbb296de1c811eba059e03a"
|
|
65
65
|
}
|
package/src/ExpoSQLiteNext.ts
CHANGED
|
@@ -21,6 +21,14 @@ export default {
|
|
|
21
21
|
throw new Error('Unimplemented');
|
|
22
22
|
},
|
|
23
23
|
|
|
24
|
+
importAssetDatabaseAsync(
|
|
25
|
+
databaseName: string,
|
|
26
|
+
assetDatabasePath: string,
|
|
27
|
+
forceOverwrite: boolean
|
|
28
|
+
): Promise<void> {
|
|
29
|
+
throw new Error('Unimplemented');
|
|
30
|
+
},
|
|
31
|
+
|
|
24
32
|
//#region EventEmitter implementations
|
|
25
33
|
|
|
26
34
|
addListener() {
|
package/src/hooks.tsx
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
|
+
import { Asset } from 'expo-asset';
|
|
1
2
|
import React, { createContext, useContext, useEffect, useRef, useState } from 'react';
|
|
2
3
|
|
|
4
|
+
import ExpoSQLite from './ExpoSQLiteNext';
|
|
3
5
|
import type { SQLiteOpenOptions } from './NativeDatabase';
|
|
4
6
|
import { openDatabaseAsync, type SQLiteDatabase } from './SQLiteDatabase';
|
|
5
7
|
|
|
8
|
+
export interface SQLiteProviderAssetSource {
|
|
9
|
+
/**
|
|
10
|
+
* The asset ID returned from the `require()` call.
|
|
11
|
+
*/
|
|
12
|
+
assetId: number;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Force overwrite the local database file even if it already exists.
|
|
16
|
+
* @default false
|
|
17
|
+
*/
|
|
18
|
+
forceOverwrite?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
6
21
|
export interface SQLiteProviderProps {
|
|
7
22
|
/**
|
|
8
23
|
* The name of the database file to open.
|
|
@@ -14,6 +29,15 @@ export interface SQLiteProviderProps {
|
|
|
14
29
|
*/
|
|
15
30
|
options?: SQLiteOpenOptions;
|
|
16
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Import a bundled database file from the specified asset module.
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* assetSource={{ assetId: require('./assets/db.db') }}
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
assetSource?: SQLiteProviderAssetSource;
|
|
40
|
+
|
|
17
41
|
/**
|
|
18
42
|
* The children to render.
|
|
19
43
|
*/
|
|
@@ -120,12 +144,14 @@ let databaseInstance: DatabaseInstanceType | null = null;
|
|
|
120
144
|
function SQLiteProviderSuspense({
|
|
121
145
|
databaseName,
|
|
122
146
|
options,
|
|
147
|
+
assetSource,
|
|
123
148
|
children,
|
|
124
149
|
onInit,
|
|
125
150
|
}: Omit<SQLiteProviderProps, 'onError' | 'useSuspense'>) {
|
|
126
151
|
const databasePromise = getDatabaseAsync({
|
|
127
152
|
databaseName,
|
|
128
153
|
options,
|
|
154
|
+
assetSource,
|
|
129
155
|
onInit,
|
|
130
156
|
});
|
|
131
157
|
const database = use(databasePromise);
|
|
@@ -135,6 +161,7 @@ function SQLiteProviderSuspense({
|
|
|
135
161
|
function SQLiteProviderNonSuspense({
|
|
136
162
|
databaseName,
|
|
137
163
|
options,
|
|
164
|
+
assetSource,
|
|
138
165
|
children,
|
|
139
166
|
onInit,
|
|
140
167
|
onError,
|
|
@@ -146,7 +173,7 @@ function SQLiteProviderNonSuspense({
|
|
|
146
173
|
useEffect(() => {
|
|
147
174
|
async function setup() {
|
|
148
175
|
try {
|
|
149
|
-
const db = await openDatabaseWithInitAsync({ databaseName, options, onInit });
|
|
176
|
+
const db = await openDatabaseWithInitAsync({ databaseName, options, assetSource, onInit });
|
|
150
177
|
databaseRef.current = db;
|
|
151
178
|
setLoading(false);
|
|
152
179
|
} catch (e) {
|
|
@@ -189,8 +216,12 @@ function SQLiteProviderNonSuspense({
|
|
|
189
216
|
function getDatabaseAsync({
|
|
190
217
|
databaseName,
|
|
191
218
|
options,
|
|
219
|
+
assetSource,
|
|
192
220
|
onInit,
|
|
193
|
-
}: Pick<
|
|
221
|
+
}: Pick<
|
|
222
|
+
SQLiteProviderProps,
|
|
223
|
+
'databaseName' | 'options' | 'assetSource' | 'onInit'
|
|
224
|
+
>): Promise<SQLiteDatabase> {
|
|
194
225
|
if (
|
|
195
226
|
databaseInstance?.promise != null &&
|
|
196
227
|
databaseInstance?.databaseName === databaseName &&
|
|
@@ -207,10 +238,10 @@ function getDatabaseAsync({
|
|
|
207
238
|
db.closeAsync();
|
|
208
239
|
})
|
|
209
240
|
.then(() => {
|
|
210
|
-
return openDatabaseWithInitAsync({ databaseName, options, onInit });
|
|
241
|
+
return openDatabaseWithInitAsync({ databaseName, options, assetSource, onInit });
|
|
211
242
|
});
|
|
212
243
|
} else {
|
|
213
|
-
promise = openDatabaseWithInitAsync({ databaseName, options, onInit });
|
|
244
|
+
promise = openDatabaseWithInitAsync({ databaseName, options, assetSource, onInit });
|
|
214
245
|
}
|
|
215
246
|
databaseInstance = {
|
|
216
247
|
databaseName,
|
|
@@ -224,8 +255,15 @@ function getDatabaseAsync({
|
|
|
224
255
|
async function openDatabaseWithInitAsync({
|
|
225
256
|
databaseName,
|
|
226
257
|
options,
|
|
258
|
+
assetSource,
|
|
227
259
|
onInit,
|
|
228
|
-
}: Pick<
|
|
260
|
+
}: Pick<
|
|
261
|
+
SQLiteProviderProps,
|
|
262
|
+
'databaseName' | 'options' | 'assetSource' | 'onInit'
|
|
263
|
+
>): Promise<SQLiteDatabase> {
|
|
264
|
+
if (assetSource != null) {
|
|
265
|
+
await importDatabaseFromAssetAsync(databaseName, assetSource);
|
|
266
|
+
}
|
|
229
267
|
const database = await openDatabaseAsync(databaseName, options);
|
|
230
268
|
if (onInit != null) {
|
|
231
269
|
await onInit(database);
|
|
@@ -233,6 +271,21 @@ async function openDatabaseWithInitAsync({
|
|
|
233
271
|
return database;
|
|
234
272
|
}
|
|
235
273
|
|
|
274
|
+
async function importDatabaseFromAssetAsync(
|
|
275
|
+
databaseName: string,
|
|
276
|
+
assetSource: SQLiteProviderAssetSource
|
|
277
|
+
) {
|
|
278
|
+
const asset = await Asset.fromModule(assetSource.assetId).downloadAsync();
|
|
279
|
+
if (!asset.localUri) {
|
|
280
|
+
throw new Error(`Unable to get the localUri from asset ${assetSource.assetId}`);
|
|
281
|
+
}
|
|
282
|
+
await ExpoSQLite.importAssetDatabaseAsync(
|
|
283
|
+
databaseName,
|
|
284
|
+
asset.localUri,
|
|
285
|
+
assetSource.forceOverwrite ?? false
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
236
289
|
//#endregion
|
|
237
290
|
|
|
238
291
|
//#region Private Suspense API similar to `React.use`
|