Открыть боковую панель
Aurora OS
Flutter
Flutter Community Plugins
sqlite3.dart
Коммиты
0101ce0c
Коммит
0101ce0c
создал
Май 19, 2024
по автору
Simon Binder
Просмотр файлов
Add integration tests for sqlite3_web
владелец
12d57a5d
Изменения
14
Скрыть пробелы
Построчно
Рядом
sqlite3_web/build.yaml
Просмотр файла @
0101ce0c
...
...
@@ -23,6 +23,7 @@ targets:
enabled
:
true
generate_for
:
-
example/worker.dart
-
web/worker.dart
options
:
compiler
:
dart2js
build_web_compilers:dart2js_archive_extractor:
...
...
@@ -34,9 +35,11 @@ targets:
generate_for
:
include
:
-
"
example/**"
-
web/**
# This one is compiled in the other target
exclude
:
-
"
example/worker.dart"
-
web/worker.dart
# We have a designated target for this step.
build_web_compilers:dart2js_archive_extractor:
enabled
:
false
sqlite3_web/lib/sqlite3_web.dart
Просмотр файла @
0101ce0c
export
'src/api.dart'
;
export
'src/types.dart'
;
export
'src/database.dart'
;
sqlite3_web/lib/src/client.dart
Просмотр файла @
0101ce0c
...
...
@@ -6,8 +6,9 @@ import 'package:sqlite3/common.dart';
import
'package:web/web.dart'
hide
Response
,
Request
,
FileSystem
,
Notification
,
Lock
;
import
'
api
.dart'
;
import
'
types
.dart'
;
import
'channel.dart'
;
import
'database.dart'
;
import
'protocol.dart'
;
import
'shared.dart'
;
...
...
@@ -279,6 +280,7 @@ final class DatabaseClient implements WebSqlite {
_missingFeatures
.
add
(
MissingBrowserFeature
.
fileSystemAccess
);
}
available
.
add
((
StorageMode
.
inMemory
,
AccessMode
.
throughSharedWorker
));
if
(
!
result
.
sharedCanSpawnDedicated
)
{
_missingFeatures
.
add
(
MissingBrowserFeature
.
dedicatedWorkersInSharedWorkers
);
...
...
sqlite3_web/lib/src/
api
.dart
→
sqlite3_web/lib/src/
database
.dart
Просмотр файла @
0101ce0c
import
'dart:js_interop'
;
import
'dart:typed_data'
;
import
'package:sqlite3/wasm.dart'
;
import
'types.dart'
;
import
'client.dart'
;
import
'worker.dart'
;
/// A [StorageMode], name pair representing an existing database already stored
/// by the current browsing context.
typedef
ExistingDatabase
=
(
StorageMode
,
String
);
/// Types of files persisted for databases by virtual file system
/// implementations.
enum
FileType
{
/// The main database file.
database
,
/// A journal file used to synchronize changes toe database file.
journal
,
}
/// Available locations to store database content in browsers.
enum
StorageMode
{
// Note: Indices in this enum are used in the protocol, changing them is a
// backwards-incompatible change.
/// A origin-private folder provided by the file system access API.
///
/// This is generally considered to be the most reliable way to store large
/// data efficiently.
opfs
,
/// A controller responsible for opening databases in the worker.
abstract
base
class
DatabaseController
{
/// Loads a wasm module from the given [uri] with the specified [headers].
Future
<
WasmSqlite3
>
loadWasmModule
(
Uri
uri
,
{
Map
<
String
,
String
>
?
headers
})
async
{
return
WasmSqlite3
.
loadFromUrl
(
uri
,
headers:
headers
);
}
///
A virtual file system implemented by splitting files into chunks which ar
e
///
then stored in IndexedDB
.
///
Opens a database in the pre-configured [sqlite3] instance under th
e
///
specified [path] in the given [vfs]
.
///
/// As sqlite3 expects a synchronous file system and IndexedDB is
/// asynchronous, we maintain the illusion if synchronous access by keeping
/// the entire database cached in memory and then flushing changes
/// asynchronously.
/// This technically looses durability, but is reasonably reliable in
/// practice.
indexedDb
,
/// Don't persist databases, instead keeping them in memory only.
inMemory
,
}
/// This should virtually always call `sqlite3.open(path, vfs: vfs)` and wrap
/// the result in a [WorkerDatabase] subclass.
Future
<
WorkerDatabase
>
openDatabase
(
WasmSqlite3
sqlite3
,
String
path
,
String
vfs
);
/// In addition to the [StorageMode] describing which browser API is used to
/// store content, this enum describes how databases are accessed.
enum
AccessMode
{
/// Access databases by spawning a shared worker shared across tabs.
/// Handles custom requests from clients that are not bound to a database.
///
/// This is more efficient as it avoids synchronization conflicts between tabs
/// which may slow things down.
throughSharedWorker
,
/// Access databases by spawning a dedicated worker for this tab.
throughDedicatedWorker
,
/// Access databases without any shared or dedicated worker.
inCurrentContext
,
}
/// An exception thrown when a operation fails on the remote worker.
///
/// As the worker and the main tab have been compiled independently and don't
/// share a class hierarchy or object representations, it is impossible to send
/// typed exception objects. Instead, this exception wraps every error or
/// exception thrown by the remote worker and contains the [toString]
/// representation in [message].
final
class
RemoteException
implements
Exception
{
/// The [Object.toString] representation of the original exception.
final
String
message
;
/// Creates a remote exception from the [message] thrown.
RemoteException
({
required
this
.
message
});
@override
String
toString
()
{
return
'Remote error:
$message
'
;
}
}
abstract
class
FileSystem
{
StorageMode
get
storage
;
String
get
databaseName
;
Future
<
bool
>
exists
(
FileType
type
);
Future
<
Uint8List
>
readFile
(
FileType
type
);
Future
<
void
>
writeFile
(
FileType
type
,
Uint8List
content
);
/// This is not currently used.
Future
<
JSAny
?
>
handleCustomRequest
(
ClientConnection
connection
,
JSAny
?
request
);
}
/// Abstraction over a database either available locally or in a remote worker.
...
...
@@ -158,100 +97,6 @@ abstract class WorkerDatabase {
ClientConnection
connection
,
JSAny
?
request
);
}
/// A controller responsible for opening databases in the worker.
abstract
base
class
DatabaseController
{
/// Loads a wasm module from the given [uri] with the specified [headers].
Future
<
WasmSqlite3
>
loadWasmModule
(
Uri
uri
,
{
Map
<
String
,
String
>
?
headers
})
async
{
return
WasmSqlite3
.
loadFromUrl
(
uri
,
headers:
headers
);
}
/// Opens a database in the pre-configured [sqlite3] instance under the
/// specified [path] in the given [vfs].
///
/// This should virtually always call `sqlite3.open(path, vfs: vfs)` and wrap
/// the result in a [WorkerDatabase] subclass.
Future
<
WorkerDatabase
>
openDatabase
(
WasmSqlite3
sqlite3
,
String
path
,
String
vfs
);
/// Handles custom requests from clients that are not bound to a database.
///
/// This is not currently used.
Future
<
JSAny
?
>
handleCustomRequest
(
ClientConnection
connection
,
JSAny
?
request
);
}
/// An enumeration of features not supported by the current browsers.
///
/// While this information may not be useful to end users, it can be used to
/// understand why a particular file system implementation is unavailable.
enum
MissingBrowserFeature
{
/// The browser is missing support for [shared workers].
///
/// [shared workers]: https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker
sharedWorkers
,
/// The browser is missing support for [web workers] in general.
///
/// [web workers]: https://developer.mozilla.org/en-US/docs/Web/API/Worker
dedicatedWorkers
,
/// The browser doesn't allow shared workers to spawn dedicated workers in
/// their context.
///
/// While the specification for web workers explicitly allows this, this
/// feature is only implemented by Firefox at the time of writing.
dedicatedWorkersInSharedWorkers
,
/// The browser doesn't allow dedicated workers to spawn their own dedicated
/// workers.
dedicatedWorkersCanNest
,
/// The browser does not support a synchronous version of the [File System API]
///
/// [File System API]: https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API
fileSystemAccess
,
/// The browser does not support IndexedDB.
indexedDb
,
/// The browser does not support shared array buffers and `Atomics.wait`.
///
/// To enable this feature in most browsers, you need to serve your app with
/// two [special headers](https://web.dev/coop-coep/).
sharedArrayBuffers
,
}
/// The result of [WebSqlite.runFeatureDetection], describing which browsers
/// and databases are available in the current browser.
final
class
FeatureDetectionResult
{
/// A list of features that were probed and found to be unsupported in the
/// current browser.
final
List
<
MissingBrowserFeature
>
missingFeatures
;
/// All existing databases that have been found.
///
/// Databases are only found reliably when a database name is passed to
/// [WebSqlite.runFeatureDetection].
final
List
<
ExistingDatabase
>
existingDatabases
;
/// All available [StorageMode], [AccessMode] pairs describing the databases
/// supported by this browser.
final
List
<(
StorageMode
,
AccessMode
)>
availableImplementations
;
FeatureDetectionResult
({
required
this
.
missingFeatures
,
required
this
.
existingDatabases
,
required
this
.
availableImplementations
,
});
@override
String
toString
()
{
return
'Existing:
$existingDatabases
, available: '
'
$availableImplementations
, missing:
$missingFeatures
'
;
}
}
/// The result of [WebSqlite.connectToRecommended], containing the opened
/// [database] as well as the [FeatureDetectionResult] leading to that database
/// implementation being chosen.
...
...
sqlite3_web/lib/src/protocol.dart
Просмотр файла @
0101ce0c
...
...
@@ -6,7 +6,7 @@ import 'package:sqlite3/common.dart';
import
'package:sqlite3/wasm.dart'
as
wasm_vfs
;
import
'package:web/web.dart'
;
import
'
api
.dart'
;
import
'
types
.dart'
;
import
'channel.dart'
;
/// Signature of a function allowing structured data to be sent between JS
...
...
sqlite3_web/lib/src/types.dart
0 → 100644
Просмотр файла @
0101ce0c
import
'dart:typed_data'
;
/// A [StorageMode], name pair representing an existing database already stored
/// by the current browsing context.
typedef
ExistingDatabase
=
(
StorageMode
,
String
);
/// Types of files persisted for databases by virtual file system
/// implementations.
enum
FileType
{
/// The main database file.
database
,
/// A journal file used to synchronize changes toe database file.
journal
,
}
/// Available locations to store database content in browsers.
enum
StorageMode
{
// Note: Indices in this enum are used in the protocol, changing them is a
// backwards-incompatible change.
/// A origin-private folder provided by the file system access API.
///
/// This is generally considered to be the most reliable way to store large
/// data efficiently.
opfs
,
/// A virtual file system implemented by splitting files into chunks which are
/// then stored in IndexedDB.
///
/// As sqlite3 expects a synchronous file system and IndexedDB is
/// asynchronous, we maintain the illusion if synchronous access by keeping
/// the entire database cached in memory and then flushing changes
/// asynchronously.
/// This technically looses durability, but is reasonably reliable in
/// practice.
indexedDb
,
/// Don't persist databases, instead keeping them in memory only.
inMemory
,
}
/// In addition to the [StorageMode] describing which browser API is used to
/// store content, this enum describes how databases are accessed.
enum
AccessMode
{
/// Access databases by spawning a shared worker shared across tabs.
///
/// This is more efficient as it avoids synchronization conflicts between tabs
/// which may slow things down.
throughSharedWorker
,
/// Access databases by spawning a dedicated worker for this tab.
throughDedicatedWorker
,
/// Access databases without any shared or dedicated worker.
inCurrentContext
,
}
/// An exception thrown when a operation fails on the remote worker.
///
/// As the worker and the main tab have been compiled independently and don't
/// share a class hierarchy or object representations, it is impossible to send
/// typed exception objects. Instead, this exception wraps every error or
/// exception thrown by the remote worker and contains the [toString]
/// representation in [message].
final
class
RemoteException
implements
Exception
{
/// The [Object.toString] representation of the original exception.
final
String
message
;
/// Creates a remote exception from the [message] thrown.
RemoteException
({
required
this
.
message
});
@override
String
toString
()
{
return
'Remote error:
$message
'
;
}
}
abstract
class
FileSystem
{
StorageMode
get
storage
;
String
get
databaseName
;
Future
<
bool
>
exists
(
FileType
type
);
Future
<
Uint8List
>
readFile
(
FileType
type
);
Future
<
void
>
writeFile
(
FileType
type
,
Uint8List
content
);
}
/// An enumeration of features not supported by the current browsers.
///
/// While this information may not be useful to end users, it can be used to
/// understand why a particular file system implementation is unavailable.
enum
MissingBrowserFeature
{
/// The browser is missing support for [shared workers].
///
/// [shared workers]: https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker
sharedWorkers
,
/// The browser is missing support for [web workers] in general.
///
/// [web workers]: https://developer.mozilla.org/en-US/docs/Web/API/Worker
dedicatedWorkers
,
/// The browser doesn't allow shared workers to spawn dedicated workers in
/// their context.
///
/// While the specification for web workers explicitly allows this, this
/// feature is only implemented by Firefox at the time of writing.
dedicatedWorkersInSharedWorkers
,
/// The browser doesn't allow dedicated workers to spawn their own dedicated
/// workers.
dedicatedWorkersCanNest
,
/// The browser does not support a synchronous version of the [File System API]
///
/// [File System API]: https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API
fileSystemAccess
,
/// The browser does not support IndexedDB.
indexedDb
,
/// The browser does not support shared array buffers and `Atomics.wait`.
///
/// To enable this feature in most browsers, you need to serve your app with
/// two [special headers](https://web.dev/coop-coep/).
sharedArrayBuffers
,
}
/// The result of [WebSqlite.runFeatureDetection], describing which browsers
/// and databases are available in the current browser.
final
class
FeatureDetectionResult
{
/// A list of features that were probed and found to be unsupported in the
/// current browser.
final
List
<
MissingBrowserFeature
>
missingFeatures
;
/// All existing databases that have been found.
///
/// Databases are only found reliably when a database name is passed to
/// [WebSqlite.runFeatureDetection].
final
List
<
ExistingDatabase
>
existingDatabases
;
/// All available [StorageMode], [AccessMode] pairs describing the databases
/// supported by this browser.
final
List
<(
StorageMode
,
AccessMode
)>
availableImplementations
;
FeatureDetectionResult
({
required
this
.
missingFeatures
,
required
this
.
existingDatabases
,
required
this
.
availableImplementations
,
});
@override
String
toString
()
{
return
'Existing:
$existingDatabases
, available: '
'
$availableImplementations
, missing:
$missingFeatures
'
;
}
}
sqlite3_web/lib/src/worker.dart
Просмотр файла @
0101ce0c
...
...
@@ -17,7 +17,7 @@ import 'package:web/web.dart'
// ignore: implementation_imports
import
'package:sqlite3/src/wasm/js_interop/new_file_system_access.dart'
;
import
'
api
.dart'
;
import
'
database
.dart'
;
import
'channel.dart'
;
import
'protocol.dart'
;
import
'shared.dart'
;
...
...
sqlite3_web/pubspec.yaml
Просмотр файла @
0101ce0c
...
...
@@ -14,9 +14,17 @@ dependencies:
dev_dependencies
:
lints
:
^2.1.0
test
:
^1.2
4.0
test
:
^1.2
5.5
build_web_compilers
:
^4.0.9
build_runner
:
^2.4.8
build_daemon
:
^4.0.2
webdriver
:
^3.0.3
shelf
:
^1.4.1
shelf_proxy
:
^1.0.4
package_config
:
^2.1.0
path
:
^1.9.0
collection
:
^1.18.0
async
:
^2.11.0
dependency_overrides
:
sqlite3
:
...
...
sqlite3_web/test/integration_test.dart
0 → 100644
Просмотр файла @
0101ce0c
import
'dart:io'
;
import
'package:sqlite3_web/src/types.dart'
;
import
'package:test/test.dart'
;
import
'package:webdriver/async_io.dart'
;
import
'../tool/server.dart'
;
enum
Browser
{
chrome
(
driverUriString:
'http://localhost:4444/wd/hub/'
,
isChromium:
true
,
unsupportedImplementations:
{
(
StorageMode
.
opfs
,
AccessMode
.
throughSharedWorker
)
},
missingFeatures:
{
MissingBrowserFeature
.
dedicatedWorkersInSharedWorkers
},
),
firefox
(
driverUriString:
'http://localhost:4444/'
);
final
bool
isChromium
;
final
String
driverUriString
;
final
Set
<(
StorageMode
,
AccessMode
)>
unsupportedImplementations
;
final
Set
<
MissingBrowserFeature
>
missingFeatures
;
const
Browser
({
required
this
.
driverUriString
,
this
.
isChromium
=
false
,
this
.
unsupportedImplementations
=
const
{},
this
.
missingFeatures
=
const
{},
});
Uri
get
driverUri
=
>
Uri
.
parse
(
driverUriString
);
Set
<(
StorageMode
,
AccessMode
)>
get
availableImplementations
{
final
available
=
<(
StorageMode
,
AccessMode
)>{};
for
(
final
storage
in
StorageMode
.
values
)
{
for
(
final
access
in
AccessMode
.
values
)
{
if
(
access
!=
AccessMode
.
inCurrentContext
&&
!
unsupportedImplementations
.
contains
((
storage
,
access
)))
{
available
.
add
((
storage
,
access
));
}
}
}
return
available
;
}
bool
supports
((
StorageMode
,
AccessMode
)
impl
)
=
>
!
unsupportedImplementations
.
contains
(
impl
);
Future
<
Process
>
spawnDriver
()
async
{
return
switch
(
this
)
{
firefox
=
>
Process
.
start
(
'geckodriver'
,
[])
.
then
((
result
)
async
{
// geckodriver seems to take a while to initialize
await
Future
.
delayed
(
const
Duration
(
seconds:
1
));
return
result
;
}),
chrome
=
>
Process
.
start
(
'chromedriver'
,
[
'--port=4444'
,
'--url-base=/wd/hub'
]),
};
}
}
void
main
()
{
late
TestAssetServer
server
;
setUpAll
(()
async
{
server
=
await
TestAssetServer
.
start
();
});
tearDownAll
(()
=
>
server
.
close
());
for
(
final
browser
in
Browser
.
values
)
{
group
(
browser
.
name
,
()
{
late
Process
driverProcess
;
late
DriftWebDriver
driver
;
setUpAll
(()
async
=
>
driverProcess
=
await
browser
.
spawnDriver
());
tearDownAll
(()
=
>
driverProcess
.
kill
());
setUp
(()
async
{
final
rawDriver
=
await
createDriver
(
spec:
browser
.
isChromium
?
WebDriverSpec
.
JsonWire
:
WebDriverSpec
.
W3c
,
uri:
browser
.
driverUri
,
);
driver
=
DriftWebDriver
(
server
,
rawDriver
);
await
driver
.
driver
.
get
(
'http://localhost:8080/'
);
});
tearDown
(()
=
>
driver
.
driver
.
quit
());
test
(
'compatibility check'
,
()
async
{
final
result
=
await
driver
.
probeImplementations
();
expect
(
result
.
missingFeatures
,
browser
.
missingFeatures
);
expect
(
result
.
impls
,
browser
.
availableImplementations
);
});
});
}
}
sqlite3_web/tool/server.dart
0 → 100644
Просмотр файла @
0101ce0c
import
'dart:convert'
;
import
'dart:io'
;
import
'dart:isolate'
;
import
'package:build_daemon/client.dart'
;
import
'package:build_daemon/constants.dart'
;
import
'package:build_daemon/data/build_status.dart'
;
import
'package:build_daemon/data/build_target.dart'
;
import
'package:collection/collection.dart'
;
import
'package:package_config/package_config.dart'
;
import
'package:path/path.dart'
as
p
;
import
'package:shelf/shelf_io.dart'
;
import
'package:shelf_proxy/shelf_proxy.dart'
;
import
'package:webdriver/async_io.dart'
;
import
'package:sqlite3_web/src/types.dart'
;
void
main
()
async
{
await
TestAssetServer
.
start
();
print
(
'Serving on http://localhost:8080/'
);
}
class
TestAssetServer
{
final
BuildDaemonClient
buildRunner
;
late
final
HttpServer
server
;
TestAssetServer
(
this
.
buildRunner
);
Future
<
void
>
close
()
async
{
await
server
.
close
(
force:
true
);
await
buildRunner
.
close
();
}
static
Future
<
TestAssetServer
>
start
()
async
{
final
packageConfig
=
await
loadPackageConfigUri
((
await
Isolate
.
packageConfig
)
!
);
final
ownPackage
=
packageConfig
[
'sqlite3_web'
]
!.
root
;
var
packageDir
=
ownPackage
.
toFilePath
(
windows:
Platform
.
isWindows
);
if
(
packageDir
.
endsWith
(
'/'
))
{
packageDir
=
packageDir
.
substring
(
0
,
packageDir
.
length
-
1
);
}
final
buildRunner
=
await
BuildDaemonClient
.
connect
(
packageDir
,
[
Platform
.
executable
,
// dart
'run'
,
'build_runner'
,
'daemon'
,
],
logHandler:
(
log
)
=
>
print
(
log
.
message
),
);
buildRunner
.
.
registerBuildTarget
(
DefaultBuildTarget
((
b
)
=
>
b
.
target
=
'web'
))
.
.
startBuild
();
// Wait for the build to complete, so that the server we return is ready to
// go.
await
buildRunner
.
buildResults
.
firstWhere
((
b
)
{
final
buildResult
=
b
.
results
.
firstWhereOrNull
((
r
)
=
>
r
.
target
==
'web'
);
return
buildResult
!=
null
&&
buildResult
.
status
!=
BuildStatus
.
started
;
});
final
assetServerPortFile
=
File
(
p
.
join
(
daemonWorkspace
(
packageDir
),
'.asset_server_port'
));
final
assetServerPort
=
int
.
parse
(
await
assetServerPortFile
.
readAsString
());
final
server
=
TestAssetServer
(
buildRunner
);
final
proxy
=
proxyHandler
(
'http://localhost:
$assetServerPort
/web/'
);
server
.
server
=
await
serve
(
(
request
)
async
{
final
pathSegments
=
request
.
url
.
pathSegments
;
if
(
pathSegments
.
isNotEmpty
&&
pathSegments
[
0
]
==
'no-coep'
)
{
// Serve stuff under /no-coep like the regular website, but without
// adding the security headers.
return
await
proxy
(
request
.
change
(
path:
'no-coep'
));
}
else
{
final
response
=
await
proxy
(
request
);
if
(
!
request
.
url
.
path
.
startsWith
(
'/no-coep'
))
{
return
response
.
change
(
headers:
{
// Needed for shared array buffers to work
'Cross-Origin-Opener-Policy'
:
'same-origin'
,
'Cross-Origin-Embedder-Policy'
:
'require-corp'
});
}
return
response
;
}
},
'localhost'
,
8080
,
);
return
server
;
}
}
class
DriftWebDriver
{
final
TestAssetServer
server
;
final
WebDriver
driver
;
DriftWebDriver
(
this
.
server
,
this
.
driver
);
Future
<
({
Set
<(
StorageMode
,
AccessMode
)>
impls
,
Set
<
MissingBrowserFeature
>
missingFeatures
,
List
<
ExistingDatabase
>
existing
,
})>
probeImplementations
()
async
{
final
rawResult
=
await
driver
.
executeAsync
(
'detectImplementations("", arguments[0])'
,
[]);
final
result
=
json
.
decode
(
rawResult
);
return
(
impls:
{
for
(
final
entry
in
result
[
'impls'
])
(
StorageMode
.
values
.
byName
(
entry
[
0
]
as
String
),
AccessMode
.
values
.
byName
(
entry
[
1
]
as
String
),
)
},
missingFeatures:
{
for
(
final
entry
in
result
[
'missing'
])
MissingBrowserFeature
.
values
.
byName
(
entry
)
},
existing:
<
ExistingDatabase
>[
for
(
final
entry
in
result
[
'existing'
])
(
StorageMode
.
values
.
byName
(
entry
[
0
]
as
String
),
entry
[
1
]
as
String
,
),
],
);
}
Future
<
void
>
openDatabase
([(
StorageMode
,
AccessMode
)
?
implementation
])
async
{
final
desc
=
switch
(
implementation
)
{
null
=
>
null
,
(
var
storage
,
var
access
)
=
>
'
${storage.name}
:
${access.name}
'
};
await
driver
.
executeAsync
(
'open(arguments[0], arguments[1])'
,
[
desc
]);
}
Future
<
void
>
closeDatabase
()
async
{
await
driver
.
executeAsync
(
"close('', arguments[0])"
,
[]);
}
Future
<
void
>
waitForUpdate
()
async
{
await
driver
.
executeAsync
(
'wait_for_update("", arguments[0])'
,
[]);
}
Future
<
void
>
execute
(
String
sql
)
async
{
await
driver
.
executeAsync
(
'exec(arguments[0], arguments[1])'
,
[
sql
]);
}
}
sqlite3_web/web/controller.dart
0 → 100644
Просмотр файла @
0101ce0c
import
'dart:js_interop'
;
import
'package:sqlite3/common.dart'
;
import
'package:sqlite3/src/wasm/sqlite3.dart'
;
import
'package:sqlite3_web/sqlite3_web.dart'
;
final
class
ExampleController
extends
DatabaseController
{
final
bool
isInWorker
;
ExampleController
({
required
this
.
isInWorker
});
@override
Future
<
JSAny
?
>
handleCustomRequest
(
ClientConnection
connection
,
JSAny
?
request
)
async
{
return
null
;
}
@override
Future
<
WorkerDatabase
>
openDatabase
(
WasmSqlite3
sqlite3
,
String
path
,
String
vfs
)
async
{
final
raw
=
sqlite3
.
open
(
path
,
vfs:
vfs
);
raw
.
createFunction
(
functionName:
'database_host'
,
function:
(
args
)
=
>
isInWorker
?
'worker'
:
'document'
,
argumentCount:
const
AllowedArgumentCount
(
0
),
);
return
ExampleDatabase
(
database:
raw
);
}
}
final
class
ExampleDatabase
extends
WorkerDatabase
{
@override
final
CommonDatabase
database
;
ExampleDatabase
({
required
this
.
database
});
@override
Future
<
JSAny
?
>
handleCustomRequest
(
ClientConnection
connection
,
JSAny
?
request
)
async
{
return
null
;
}
}
sqlite3_web/web/index.html
0 → 100644
Просмотр файла @
0101ce0c
<!DOCTYPE html>
<html>
<head>
<meta
charset=
"utf-8"
>
<meta
http-equiv=
"X-UA-Compatible"
content=
"IE=edge"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<meta
name=
"scaffolded-by"
content=
"https://github.com/dart-lang/sdk"
>
<title>
web_wasm
</title>
<script
defer
src=
"main.dart.js"
></script>
</head>
<body>
<h1><code>
package:sqlite3_web
</code>
integration tests
</h1>
<p>
Welcome! For integration tests, this page is opened through a web driver, and scripts are injected
to run tests. To test that things are generally working, you can start a quick self-check here.
Results are logged to the console.
<button
id=
"selfcheck"
>
Check that this browser works
</button>
<a
href=
"/"
target=
"_blank"
id=
"newtab"
>
Open this page in a new tab
</a>
</p>
</body>
</html>
sqlite3_web/web/main.dart
0 → 100644
Просмотр файла @
0101ce0c
import
'dart:convert'
;
import
'dart:html'
;
import
'dart:js_interop'
;
import
'dart:js_interop_unsafe'
;
import
'package:async/async.dart'
;
import
'package:sqlite3_web/sqlite3_web.dart'
;
final
sqlite3WasmUri
=
Uri
.
parse
(
'sqlite3.wasm'
);
final
workerUri
=
Uri
.
parse
(
'worker.dart.js'
);
const
databasName
=
'database'
;
WebSqlite
?
webSqlite
;
Database
?
database
;
StreamQueue
<
void
>
?
updates
;
void
main
()
{
_addCallbackForWebDriver
(
'detectImplementations'
,
_detectImplementations
);
_addCallbackForWebDriver
(
'close'
,
(
arg
)
async
{
await
database
?.
dispose
();
return
null
;
});
_addCallbackForWebDriver
(
'wait_for_update'
,
_waitForUpdate
);
_addCallbackForWebDriver
(
'open'
,
_open
);
_addCallbackForWebDriver
(
'exec'
,
_exec
);
document
.
getElementById
(
'selfcheck'
)
?.
onClick
.
listen
((
event
)
async
{
print
(
'starting'
);
final
sqlite
=
initializeSqlite
();
final
database
=
await
sqlite
.
connectToRecommended
(
databasName
);
print
(
'selected storage:
${database.storage}
through
${database.access}
'
);
print
(
'missing features:
${database.features.missingFeatures}
'
);
});
}
void
_addCallbackForWebDriver
(
String
name
,
Future
<
JSAny
?
>
Function
(
String
?
)
impl
)
{
globalContext
.
setProperty
(
name
.
toJS
,
(
JSString
?
arg
,
JSFunction
callback
)
{
Future
(()
async
{
JSAny
?
result
;
try
{
result
=
await
impl
(
arg
?.
toDart
);
}
catch
(
e
,
s
)
{
final
console
=
globalContext
[
'console'
]
!
as
JSObject
;
console
.
callMethod
(
'error'
.
toJS
,
e
.
toString
()
.
toJS
,
s
.
toString
()
.
toJS
);
}
callback
.
callAsFunction
(
null
,
result
);
});
}
.
toJS
,
);
}
WebSqlite
initializeSqlite
()
{
return
webSqlite
??=
WebSqlite
.
open
(
worker:
workerUri
,
wasmModule:
sqlite3WasmUri
,
);
}
Future
<
JSString
>
_detectImplementations
(
String
?
_
)
async
{
final
instance
=
initializeSqlite
();
final
result
=
await
instance
.
runFeatureDetection
(
databaseName:
'database'
);
return
json
.
encode
({
'impls'
:
result
.
availableImplementations
.
map
((
r
)
=
>
[
r
.
$
1
.
name
,
r
.
$
2
.
name
])
.
toList
(),
'missing'
:
result
.
missingFeatures
.
map
((
r
)
=
>
r
.
name
)
.
toList
(),
'existing'
:
result
.
existingDatabases
.
map
((
r
)
=
>
[
r
.
$
1
.
name
,
r
.
$
2
])
.
toList
(),
})
.
toJS
;
}
Future
<
JSAny
?
>
_waitForUpdate
(
String
?
_
)
async
{
await
updates
!.
next
;
return
null
;
}
Future
<
JSAny
?
>
_open
(
String
?
implementationName
)
async
{
final
sqlite
=
initializeSqlite
();
Database
db
;
if
(
implementationName
!=
null
)
{
final
split
=
implementationName
.
split
(
':'
);
db
=
await
sqlite
.
connect
(
databasName
,
StorageMode
.
values
.
byName
(
split
[
0
]),
AccessMode
.
values
.
byName
(
split
[
1
]));
}
else
{
final
result
=
await
sqlite
.
connectToRecommended
(
databasName
);
db
=
result
.
database
;
}
// Make sure it works!
await
db
.
select
(
'SELECT database_host()'
);
updates
=
StreamQueue
(
db
.
updates
);
database
=
db
;
return
null
;
}
Future
<
JSAny
?
>
_exec
(
String
?
sql
)
async
{
database
!.
execute
(
sql
!
);
return
null
;
}
sqlite3_web/web/worker.dart
0 → 100644
Просмотр файла @
0101ce0c
import
'package:sqlite3_web/sqlite3_web.dart'
;
import
'controller.dart'
;
void
main
()
{
WebSqlite
.
workerEntrypoint
(
controller:
ExampleController
(
isInWorker:
true
));
}
Редактирование
Предварительный просмотр
Поддерживает Markdown
0%
Попробовать снова
или
прикрепить новый файл
.
Отмена
You are about to add
0
people
to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Отмена
Пожалуйста,
зарегистрируйтесь
или
войдите
чтобы прокомментировать