diff --git a/packages/body_parser/.gitignore b/packages/body_parser/.gitignore deleted file mode 100644 index b4d6e266..00000000 --- a/packages/body_parser/.gitignore +++ /dev/null @@ -1,64 +0,0 @@ -# Created by .ignore support plugin (hsz.mobi) -### JetBrains template -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff: -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/dictionaries - -# Sensitive or high-churn files: -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.xml -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml - -# Gradle: -.idea/**/gradle.xml -.idea/**/libraries - -# CMake -cmake-build-debug/ - -# Mongo Explorer plugin: -.idea/**/mongoSettings.xml - -## File-based project format: -*.iws - -## Plugin-specific files: - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties -### Dart template -# See https://www.dartlang.org/tools/private-files.html - -# Files and directories created by pub -.packages -.pub/ -build/ -# If you're building an application, you may want to check-in your pubspec.lock -pubspec.lock - -# Directory created by dartdoc -# If you don't generate documentation locally you can remove this line. -doc/api/ diff --git a/packages/body_parser/.travis.yml b/packages/body_parser/.travis.yml deleted file mode 100644 index a9e2c109..00000000 --- a/packages/body_parser/.travis.yml +++ /dev/null @@ -1,4 +0,0 @@ -language: dart -dart: - - dev - - stable \ No newline at end of file diff --git a/packages/body_parser/AUTHORS.md b/packages/body_parser/AUTHORS.md deleted file mode 100644 index ac95ab58..00000000 --- a/packages/body_parser/AUTHORS.md +++ /dev/null @@ -1,12 +0,0 @@ -Primary Authors -=============== - -* __[Thomas Hii](dukefirehawk.apps@gmail.com)__ - - Thomas is the current maintainer of the code base. He has refactored and migrated the - code base to support NNBD. - -* __[Tobe O](thosakwe@gmail.com)__ - - Tobe has written much of the original code prior to NNBD migration. He has moved on and - is no longer involved with the project. diff --git a/packages/body_parser/CHANGELOG.md b/packages/body_parser/CHANGELOG.md deleted file mode 100644 index 4d6f97a4..00000000 --- a/packages/body_parser/CHANGELOG.md +++ /dev/null @@ -1,30 +0,0 @@ -# Change Log - -## 2.1.2 - -* Final release. Replaced by `belatuk_body_parser` package. - -## 2.1.1 - -* Fixed calling deprecated methods in unit test - -## 2.1.0 - -* Replaced `http_server` with `belatuk_http_server` - -## 2.0.1 - -* Fixed source code formating warning -* Updated README - -## 2.0.0 - -* Migrated to support Dart SDK 2.12.x NNBD - -## 1.1.1 - -* Dart 2 updates; should fix Angel in Travis. - -## 1.1.0 - -* Add `parseBodyFromStream` diff --git a/packages/body_parser/LICENSE b/packages/body_parser/LICENSE deleted file mode 100644 index 8f65b579..00000000 --- a/packages/body_parser/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License (MIT) - -Copyright (c) 2021 dukefirehawk.com - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/packages/body_parser/README.md b/packages/body_parser/README.md deleted file mode 100644 index 5ae401e1..00000000 --- a/packages/body_parser/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# Angel3 Body Parser - -[![version](https://img.shields.io/badge/pub-v2.1.2-brightgreen)](https://pub.dartlang.org/packages/angel3_body_parser) -[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety) -[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion) - -[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/body_parser/LICENSE) - -**DEPRECATED: Replaced by [`belatuk_body_parser`](https://pub.dartlang.org/packages/belatuk_body_parser) package** - -Parse request bodies and query strings in Dart, as well multipart/form-data uploads. No external dependencies required. - -This is the request body parser powering the [Angel3 framework](https://github.com/dukefirehawk/angel). If you are looking for a server-side solution with dependency injection, WebSockets, and more, then I highly recommend it as your first choice. Bam! - -## Contents - -- [Angel3 Body Parser](#angel3-body-parser) - - [Contents](#contents) - - [About](#about) - - [Installation](#installation) - - [Usage](#usage) - - [Custom Body Parsing](#custom-body-parsing) - -### About - -I needed something like Express.js's `body-parser` module, so I made it here. It fully supports JSON requests. x-www-form-urlencoded fully supported, as well as query strings. You can also include arrays in your query, in the same way you would for a PHP application. Full file upload support will also be present by the production 1.0.0 release. - -A benefit of this is that primitive types are automatically deserialized correctly. As in, if you have a `hello=1.5` request, then `body['hello']` will equal `1.5` and not `'1.5'`. A very semantic difference, yes, but it relieves stress in my head. - -### Installation - -To install Body Parser for your Dart project, simply add body_parser to your pub dependencies. - - dependencies: - angel3_body_parser: ^2.1.0 - -### Usage - -Body Parser exposes a simple class called `BodyParseResult`. You can easily parse the query string and request body for a request by calling `Future parseBody`. - -```dart -import 'dart:convert'; -import 'package:angel3_body_parser/angel3_body_parser.dart'; - -main() async { - // ... - await for (HttpRequest request in server) { - request.response.write(JSON.encode(await parseBody(request).body)); - await request.response.close(); - } -} -``` - -You can also use `buildMapFromUri(Map, String)` to populate a map from a URL encoded string. - -This can easily be used with a library like [Angel3 JSON God](https://pub.dev/packages/angel3_json_god) to build structured JSON/REST APIs. Add validation and you've got an instant backend. - -```dart -MyClass create(HttpRequest request) async { - return god.deserialize(await parseBody(request).body, MyClass); -} -``` - -### Custom Body Parsing - -In cases where you need to parse unrecognized content types, `body_parser` won't be of any help to you on its own. However, you can use the `originalBuffer` property of a `BodyParseResult` to see the original request buffer. To get this functionality, pass `storeOriginalBuffer` as `true` when calling `parseBody`. - -For example, if you wanted to [parse GraphQL queries within your server](https://github.com/dukefirehawk/graphql_dart)... - -```dart -app.get('/graphql', (req, res) async { - if (req.headers.contentType.mimeType == 'application/graphql') { - var graphQlString = String.fromCharCodes(req.originalBuffer); - // ... - } -}); -``` diff --git a/packages/body_parser/analysis_options.yaml b/packages/body_parser/analysis_options.yaml deleted file mode 100644 index c230cee7..00000000 --- a/packages/body_parser/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:pedantic/analysis_options.yaml -analyzer: - strong-mode: - implicit-casts: false \ No newline at end of file diff --git a/packages/body_parser/example/main.dart b/packages/body_parser/example/main.dart deleted file mode 100644 index 62eb3f30..00000000 --- a/packages/body_parser/example/main.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; -import 'dart:isolate'; -import 'package:http_parser/http_parser.dart'; -import 'package:angel3_body_parser/angel3_body_parser.dart'; - -void main() async { - var address = '127.0.0.1'; - var port = 3000; - var futures = []; - - for (var i = 1; i < Platform.numberOfProcessors; i++) { - futures.add(Isolate.spawn(start, [address, port, i])); - } - - await Future.wait(futures).then((_) { - print('All instances started.'); - print( - 'Test with "wrk -t12 -c400 -d30s -s ./example/post.lua http://localhost:3000" or similar'); - start([address, port, 0]); - }); -} - -void start(List args) { - var address = InternetAddress(args[0] as String); - var port = 8080; - if (args[1] is int) { - args[1]; - } - - var id = 0; - if (args[2] is int) { - args[2]; - } - - HttpServer.bind(address, port, shared: true).then((server) { - server.listen((request) async { - // ignore: deprecated_member_use - var body = await defaultParseBody(request); - request.response - ..headers.contentType = ContentType('application', 'json') - ..write(json.encode(body.body)); - await request.response.close(); - }); - - print( - 'Server #$id listening at http://${server.address.address}:${server.port}'); - }); -} - -Future defaultParseBody(HttpRequest request, - {bool storeOriginalBuffer = false}) { - return parseBodyFromStream( - request, - request.headers.contentType != null - ? MediaType.parse(request.headers.contentType.toString()) - : null, - request.uri, - storeOriginalBuffer: storeOriginalBuffer); -} diff --git a/packages/body_parser/example/post.lua b/packages/body_parser/example/post.lua deleted file mode 100644 index 524febc6..00000000 --- a/packages/body_parser/example/post.lua +++ /dev/null @@ -1,6 +0,0 @@ --- example HTTP POST script which demonstrates setting the --- HTTP method, body, and adding a header - -wrk.method = "POST" -wrk.body = "foo=bar&baz=quux" -wrk.headers["Content-Type"] = "application/x-www-form-urlencoded" \ No newline at end of file diff --git a/packages/body_parser/lib/angel3_body_parser.dart b/packages/body_parser/lib/angel3_body_parser.dart deleted file mode 100644 index 05a163e5..00000000 --- a/packages/body_parser/lib/angel3_body_parser.dart +++ /dev/null @@ -1,6 +0,0 @@ -/// A library for parsing HTTP request bodies and queries. -library angel3_body_parser; - -export 'src/body_parse_result.dart'; -export 'src/file_upload_info.dart'; -export 'src/parse_body.dart'; diff --git a/packages/body_parser/lib/src/body_parse_result.dart b/packages/body_parser/lib/src/body_parse_result.dart deleted file mode 100644 index e47fc890..00000000 --- a/packages/body_parser/lib/src/body_parse_result.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'file_upload_info.dart'; - -/// A representation of data from an incoming request. -abstract class BodyParseResult { - /// The parsed body. - Map get body; - - /// The parsed query string. - Map get query; - - /// All files uploaded within this request. - List get files; - - /// The original body bytes sent with this request. - /// - /// You must set [storeOriginalBuffer] to `true` to see this. - List? get originalBuffer; - - /// If an error was encountered while parsing the body, it will appear here. - /// - /// Otherwise, this is `null`. - dynamic get error; - - /// If an error was encountered while parsing the body, the call stack will appear here. - /// - /// Otherwise, this is `null`. - StackTrace? get stack; -} diff --git a/packages/body_parser/lib/src/chunk.dart b/packages/body_parser/lib/src/chunk.dart deleted file mode 100644 index 90c078b8..00000000 --- a/packages/body_parser/lib/src/chunk.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'file_upload_info.dart'; - -List getFileDataFromChunk( - String chunk, String boundary, String fileUploadName, Map body) { - var result = []; - return result; -} diff --git a/packages/body_parser/lib/src/file_upload_info.dart b/packages/body_parser/lib/src/file_upload_info.dart deleted file mode 100644 index 285d72d4..00000000 --- a/packages/body_parser/lib/src/file_upload_info.dart +++ /dev/null @@ -1,17 +0,0 @@ -/// Represents a file uploaded to the server. -class FileUploadInfo { - /// The MIME type of the uploaded file. - String? mimeType; - - /// The name of the file field from the request. - String? name; - - /// The filename of the file. - String? filename; - - /// The bytes that make up this file. - List data; - - FileUploadInfo( - {this.mimeType, this.name, this.filename, this.data = const []}); -} diff --git a/packages/body_parser/lib/src/get_value.dart b/packages/body_parser/lib/src/get_value.dart deleted file mode 100644 index 8d2106ee..00000000 --- a/packages/body_parser/lib/src/get_value.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'dart:convert'; - -dynamic getValue(String value) { - try { - var numValue = num.parse(value); - if (!numValue.isNaN) { - return numValue; - } else { - return value; - } - } on FormatException { - if (value.startsWith('[') && value.endsWith(']')) { - return json.decode(value); - } else if (value.startsWith('{') && value.endsWith('}')) { - return json.decode(value); - } else if (value.trim().toLowerCase() == 'null') { - return null; - } else { - return value; - } - } -} diff --git a/packages/body_parser/lib/src/map_from_uri.dart b/packages/body_parser/lib/src/map_from_uri.dart deleted file mode 100644 index df193da2..00000000 --- a/packages/body_parser/lib/src/map_from_uri.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'get_value.dart'; - -/// Parses a URI-encoded string into real data! **Wow!** -/// -/// Whichever map you provide will be automatically populated from the urlencoded body string you provide. -void buildMapFromUri(Map map, String body) { - var parseArrayRgx = RegExp(r'^(.+)\[\]$'); - - for (var keyValuePair in body.split('&')) { - if (keyValuePair.contains('=')) { - var equals = keyValuePair.indexOf('='); - var key = Uri.decodeQueryComponent(keyValuePair.substring(0, equals)); - var value = Uri.decodeQueryComponent(keyValuePair.substring(equals + 1)); - - if (parseArrayRgx.hasMatch(key)) { - Match queryMatch = parseArrayRgx.firstMatch(key)!; - key = queryMatch.group(1)!; - if (!(map[key] is List)) { - map[key] = []; - } - - map[key].add(getValue(value)); - } else if (key.contains('.')) { - // i.e. map.foo.bar => [map, foo, bar] - var keys = key.split('.'); - - var targetMap = map[keys[0]] != null ? map[keys[0]] as Map? : {}; - map[keys[0]] = targetMap; - for (var i = 1; i < keys.length; i++) { - if (i < keys.length - 1) { - targetMap![keys[i]] = targetMap[keys[i]] ?? {}; - targetMap = targetMap[keys[i]] as Map?; - } else { - targetMap![keys[i]] = getValue(value); - } - } - } else { - map[key] = getValue(value); - } - } else { - map[Uri.decodeQueryComponent(keyValuePair)] = true; - } - } -} diff --git a/packages/body_parser/lib/src/parse_body.dart b/packages/body_parser/lib/src/parse_body.dart deleted file mode 100644 index 4584983a..00000000 --- a/packages/body_parser/lib/src/parse_body.dart +++ /dev/null @@ -1,149 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; -import 'dart:typed_data'; - -import 'package:http_parser/http_parser.dart'; -import 'package:belatuk_http_server/belatuk_http_server.dart'; -import 'package:mime/mime.dart'; - -import 'body_parse_result.dart'; -import 'file_upload_info.dart'; -import 'map_from_uri.dart'; - -/// Forwards to [parseBodyFromStream]. -@deprecated -Future parseBody(HttpRequest request, - {bool storeOriginalBuffer = false}) { - return parseBodyFromStream( - request, - request.headers.contentType != null - ? MediaType.parse(request.headers.contentType.toString()) - : null, - request.uri, - storeOriginalBuffer: storeOriginalBuffer); -} - -/// Grabs data from an incoming request. -/// -/// Supports URL-encoded and JSON, as well as multipart/* forms. -/// On a file upload request, only fields with the name **'file'** are processed -/// as files. Anything else is put in the body. You can change the upload file name -/// via the *fileUploadName* parameter. :) -/// -/// Use [storeOriginalBuffer] to add the original request bytes to the result. -Future parseBodyFromStream( - Stream data, MediaType? contentType, Uri requestUri, - {bool storeOriginalBuffer = false}) async { - var result = _BodyParseResultImpl(); - - Future getBytes() { - return data - .fold(BytesBuilder(copy: false), (a, b) => a..add(b)) - .then((b) => b.takeBytes()); - } - - Future getBody() { - if (storeOriginalBuffer) { - return getBytes().then((bytes) { - result.originalBuffer = bytes; - return utf8.decode(bytes); - }); - } else { - return utf8.decoder.bind(data).join(); - } - } - - try { - if (contentType != null) { - if (contentType.type == 'multipart' && - contentType.parameters.containsKey('boundary')) { - Stream stream; - - if (storeOriginalBuffer) { - var bytes = result.originalBuffer = await getBytes(); - var ctrl = StreamController()..add(bytes); - await ctrl.close(); - stream = ctrl.stream; - } else { - stream = data; - } - - var parts = - MimeMultipartTransformer(contentType.parameters['boundary']!) - .bind(stream) - .map((part) => - HttpMultipartFormData.parse(part, defaultEncoding: utf8)); - - await for (HttpMultipartFormData part in parts) { - if (part.isBinary || - part.contentDisposition.parameters.containsKey('filename')) { - var builder = await part.fold( - BytesBuilder(copy: false), - (BytesBuilder b, d) => - b..add(d is! String ? (d as List?)! : d.codeUnits)); - var upload = FileUploadInfo( - mimeType: part.contentType!.mimeType, - name: part.contentDisposition.parameters['name'], - filename: - part.contentDisposition.parameters['filename'] ?? 'file', - data: builder.takeBytes()); - result.files.add(upload); - } else if (part.isText) { - var text = await part.join(); - buildMapFromUri(result.body, - '${part.contentDisposition.parameters["name"]}=$text'); - } - } - } else if (contentType.mimeType == 'application/json') { - result.body.addAll( - _foldToStringDynamic(json.decode(await getBody()) as Map?)!); - } else if (contentType.mimeType == 'application/x-www-form-urlencoded') { - var body = await getBody(); - buildMapFromUri(result.body, body); - } else if (storeOriginalBuffer == true) { - result.originalBuffer = await getBytes(); - } - } else { - if (requestUri.hasQuery) { - buildMapFromUri(result.query, requestUri.query); - } - - if (storeOriginalBuffer == true) { - result.originalBuffer = await getBytes(); - } - } - } catch (e, st) { - result.error = e; - result.stack = st; - } - - return result; -} - -class _BodyParseResultImpl implements BodyParseResult { - @override - Map body = {}; - - @override - List files = []; - - @override - List? originalBuffer; - - @override - Map query = {}; - - @override - var error; - - @override - StackTrace? stack; -} - -Map? _foldToStringDynamic(Map? map) { - return map == null - ? null - : map.keys.fold>( - {}, (out, k) => out..[k.toString()] = map[k]); -} diff --git a/packages/body_parser/pubspec.yaml b/packages/body_parser/pubspec.yaml deleted file mode 100644 index aa7d54b7..00000000 --- a/packages/body_parser/pubspec.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: angel3_body_parser -version: 2.1.2 -description: Parse request bodies and query strings in Dart. Supports JSON, URL-encoded, and multi-part bodies. -homepage: https://angel3-framework.web.app/ -repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/body_parser -environment: - sdk: '>=2.12.0 <3.0.0' -dependencies: - http_parser: ^4.0.0 - belatuk_http_server: ^2.0.0 - mime: ^1.0.0 -dev_dependencies: - http: ^0.13.0 - test: ^1.17.0 - pedantic: ^1.11.0 \ No newline at end of file diff --git a/packages/body_parser/test/form_data_test.dart b/packages/body_parser/test/form_data_test.dart deleted file mode 100644 index b3716ac3..00000000 --- a/packages/body_parser/test/form_data_test.dart +++ /dev/null @@ -1,156 +0,0 @@ -import 'dart:io'; -import 'dart:convert'; -import 'package:angel3_body_parser/angel3_body_parser.dart'; -import 'package:http/http.dart' as http; -import 'package:http_parser/http_parser.dart'; -import 'package:test/test.dart'; -import 'server_test.dart'; - -Future _parseBody(HttpRequest request) { - return parseBodyFromStream( - request, - request.headers.contentType != null - ? MediaType.parse(request.headers.contentType.toString()) - : null, - request.uri, - storeOriginalBuffer: false); -} - -void main() { - HttpServer? server; - String? url; - http.Client? client; - - setUp(() async { - server = await HttpServer.bind('127.0.0.1', 0); - server!.listen((HttpRequest request) async { - //Server will simply return a JSON representation of the parsed body - // ignore: deprecated_member_use - request.response.write(jsonEncodeBody(await _parseBody(request))); - await request.response.close(); - }); - url = 'http://localhost:${server!.port}'; - print('Test server listening on $url'); - client = http.Client(); - }); - - tearDown(() async { - await server!.close(force: true); - client!.close(); - server = null; - url = null; - client = null; - }); - - test('No upload', () async { - var boundary = 'myBoundary'; - var headers = { - 'content-type': 'multipart/form-data; boundary=$boundary' - }; - var postData = ''' ---$boundary -Content-Disposition: form-data; name="hello" - -world ---$boundary-- -''' - .replaceAll('\n', '\r\n'); - - print( - 'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}'); - var response = - await client!.post(Uri.parse(url!), headers: headers, body: postData); - print('Response: ${response.body}'); - var jsons = json.decode(response.body); - var files = jsons['files'].map((map) { - return map == null - ? null - : map.keys.fold>( - {}, (out, k) => out..[k.toString()] = map[k]); - }); - expect(files.length, equals(0)); - expect(jsons['body']['hello'], equals('world')); - }); - - test('Single upload', () async { - var boundary = 'myBoundary'; - var headers = { - 'content-type': ContentType('multipart', 'form-data', - parameters: {'boundary': boundary}).toString() - }; - var postData = ''' ---$boundary -Content-Disposition: form-data; name="hello" - -world ---$boundary -Content-Disposition: form-data; name="file"; filename="app.dart" -Content-Type: application/dart - -Hello world ---$boundary-- -''' - .replaceAll('\n', '\r\n'); - - print( - 'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}'); - var response = - await client!.post(Uri.parse(url!), headers: headers, body: postData); - print('Response: ${response.body}'); - var jsons = json.decode(response.body); - var files = jsons['files']; - expect(files.length, equals(1)); - expect(files[0]['name'], equals('file')); - expect(files[0]['mimeType'], equals('application/dart')); - expect(files[0]['data'].length, equals(11)); - expect(files[0]['filename'], equals('app.dart')); - expect(jsons['body']['hello'], equals('world')); - }); - - test('Multiple upload', () async { - var boundary = 'myBoundary'; - var headers = { - 'content-type': 'multipart/form-data; boundary=$boundary' - }; - var postData = ''' ---$boundary -Content-Disposition: form-data; name="json" - -god ---$boundary -Content-Disposition: form-data; name="num" - -14.50000 ---$boundary -Content-Disposition: form-data; name="file"; filename="app.dart" -Content-Type: text/plain - -Hello world ---$boundary -Content-Disposition: form-data; name="entry-point"; filename="main.js" -Content-Type: text/javascript - -function main() { - console.log("Hello, world!"); -} ---$boundary-- -''' - .replaceAll('\n', '\r\n'); - - print( - 'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}'); - var response = - await client!.post(Uri.parse(url!), headers: headers, body: postData); - print('Response: ${response.body}'); - var jsons = json.decode(response.body); - var files = jsons['files']; - expect(files.length, equals(2)); - expect(files[0]['name'], equals('file')); - expect(files[0]['mimeType'], equals('text/plain')); - expect(files[0]['data'].length, equals(11)); - expect(files[1]['name'], equals('entry-point')); - expect(files[1]['mimeType'], equals('text/javascript')); - expect(jsons['body']['json'], equals('god')); - expect(jsons['body']['num'], equals(14.5)); - }); -} diff --git a/packages/body_parser/test/server_test.dart b/packages/body_parser/test/server_test.dart deleted file mode 100644 index a41a24c1..00000000 --- a/packages/body_parser/test/server_test.dart +++ /dev/null @@ -1,174 +0,0 @@ -import 'dart:convert'; -import 'dart:io' show HttpRequest, HttpServer; - -import 'package:angel3_body_parser/angel3_body_parser.dart'; -import 'package:http/http.dart' as http; -import 'package:http_parser/http_parser.dart'; -import 'package:test/test.dart'; - -const TOKEN = - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIxMjcuMC4wLjEiLCJleHAiOi0xLCJpYXQiOiIyMDE2LTEyLTIyVDEyOjQ5OjUwLjM2MTQ0NiIsImlzcyI6ImFuZ2VsX2F1dGgiLCJzdWIiOiIxMDY2OTQ4Mzk2MDIwMjg5ODM2NTYifQ==.PYw7yUb-cFWD7N0sSLztP7eeRvO44nu1J2OgDNyT060='; - -String jsonEncodeBody(BodyParseResult result) { - return json.encode({ - 'query': result.query, - 'body': result.body, - 'error': result.error?.toString(), - 'files': result.files.map((f) { - return { - 'name': f.name, - 'mimeType': f.mimeType, - 'filename': f.filename, - 'data': f.data, - }; - }).toList(), - 'originalBuffer': result.originalBuffer, - 'stack': null, //result.stack.toString(), - }); -} - -Future _parseBody(HttpRequest request) { - return parseBodyFromStream( - request, - request.headers.contentType != null - ? MediaType.parse(request.headers.contentType.toString()) - : null, - request.uri, - storeOriginalBuffer: true); -} - -void main() { - HttpServer? server; - String? url; - http.Client? client; - - setUp(() async { - server = await HttpServer.bind('127.0.0.1', 0); - server!.listen((HttpRequest request) async { - //Server will simply return a JSON representation of the parsed body - request.response.write( - // ignore: deprecated_member_use - jsonEncodeBody(await _parseBody(request))); - await request.response.close(); - }); - url = 'http://localhost:${server!.port}'; - print('Test server listening on $url'); - client = http.Client(); - }); - tearDown(() async { - await server!.close(force: true); - client!.close(); - server = null; - url = null; - client = null; - }); - - group('query string', () { - test('GET Simple', () async { - print('GET $url/?hello=world'); - var response = await client!.get(Uri.parse('$url/?hello=world')); - print('Response: ${response.body}'); - var result = json.decode(response.body); - expect(result['body'], equals({})); - expect(result['query'], equals({'hello': 'world'})); - expect(result['files'], equals([])); - //expect(result['originalBuffer'], isNull); - }); - - test('GET Complex', () async { - var postData = - 'hello=world&nums%5B%5D=1&nums%5B%5D=2.0&nums%5B%5D=${3 - 1}&map.foo.bar=baz'; - print('Body: $postData'); - var response = await client!.get(Uri.parse('$url/?$postData')); - print('Response: ${response.body}'); - var query = json.decode(response.body)['query']; - expect(query['hello'], equals('world')); - expect(query['nums'][2], equals(2)); - expect(query['map'] is Map, equals(true)); - expect(query['map']['foo'], equals({'bar': 'baz'})); - }); - - test('JWT', () async { - var postData = 'token=$TOKEN'; - print('Body: $postData'); - var response = await client!.get(Uri.parse('$url/?$postData')); - print('Response: ${response.body}'); - var query = json.decode(response.body)['query']; - expect(query['token'], equals(TOKEN)); - }); - }); - - group('urlencoded', () { - var headers = { - 'content-type': 'application/x-www-form-urlencoded' - }; - test('POST Simple', () async { - print('Body: hello=world'); - var response = await client! - .post(Uri.parse(url!), headers: headers, body: 'hello=world'); - print('Response: ${response.body}'); - var result = json.decode(response.body); - expect(result['query'], equals({})); - expect(result['body'], equals({'hello': 'world'})); - expect(result['files'], equals([])); - expect(result['originalBuffer'], isList); - expect(result['originalBuffer'], isNotEmpty); - }); - - test('Post Complex', () async { - var postData = - 'hello=world&nums%5B%5D=1&nums%5B%5D=2.0&nums%5B%5D=${3 - 1}&map.foo.bar=baz'; - var response = - await client!.post(Uri.parse(url!), headers: headers, body: postData); - print('Response: ${response.body}'); - var body = json.decode(response.body)['body']; - expect(body['hello'], equals('world')); - expect(body['nums'][2], equals(2)); - expect(body['map'] is Map, equals(true)); - expect(body['map']['foo'], equals({'bar': 'baz'})); - }); - - test('JWT', () async { - var postData = 'token=$TOKEN'; - var response = - await client!.post(Uri.parse(url!), headers: headers, body: postData); - var body = json.decode(response.body)['body']; - expect(body['token'], equals(TOKEN)); - }); - }); - - group('json', () { - var headers = {'content-type': 'application/json'}; - test('Post Simple', () async { - var postData = json.encode({'hello': 'world'}); - print('Body: $postData'); - var response = - await client!.post(Uri.parse(url!), headers: headers, body: postData); - print('Response: ${response.body}'); - var result = json.decode(response.body); - expect(result['body'], equals({'hello': 'world'})); - expect(result['query'], equals({})); - expect(result['files'], equals([])); - expect(result['originalBuffer'], allOf(isList, isNotEmpty)); - }); - - test('Post Complex', () async { - var postData = json.encode({ - 'hello': 'world', - 'nums': [1, 2.0, 3 - 1], - 'map': { - 'foo': {'bar': 'baz'} - } - }); - print('Body: $postData'); - var response = - await client!.post(Uri.parse(url!), headers: headers, body: postData); - print('Response: ${response.body}'); - var body = json.decode(response.body)['body']; - expect(body['hello'], equals('world')); - expect(body['nums'][2], equals(2)); - expect(body['map'] is Map, equals(true)); - expect(body['map']['foo'], equals({'bar': 'baz'})); - }); - }); -} diff --git a/packages/graphql/README.md b/packages/graphql/README.md deleted file mode 100644 index 2f4e55c7..00000000 --- a/packages/graphql/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# GraphQL - -Moved to [`GraphQL Repo`](https://github.com/dukefirehawk/graphql_dart) \ No newline at end of file diff --git a/packages/html_builder/README.md b/packages/html_builder/README.md deleted file mode 100644 index 67fe57ef..00000000 --- a/packages/html_builder/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Angel3 Html Builder - -`angel3_html_builder` package has been deprecated and replaced by `belatuk_html_builder`package at [`Belatuk Common Utilities`]() diff --git a/packages/inflection3/AUTHORS.md b/packages/inflection3/AUTHORS.md deleted file mode 100644 index a839c986..00000000 --- a/packages/inflection3/AUTHORS.md +++ /dev/null @@ -1,3 +0,0 @@ -George Moschovitis -Tobe O -Thomas Hii \ No newline at end of file diff --git a/packages/inflection3/CHANGELOG.md b/packages/inflection3/CHANGELOG.md deleted file mode 100644 index 2954f02d..00000000 --- a/packages/inflection3/CHANGELOG.md +++ /dev/null @@ -1,38 +0,0 @@ -## 1.0.0 -* Migrated to support Dart SDK 2.12.x NNBD - -## 0.4.3 -- Add past tense support (thanks @phodonou !) - -## 0.4.2 -- Dart 2 compatibility. -- Hard fork. - -## 0.4.1 - -- Cleanup, follow latest conventions and developments in the Dartiverse. -- Added past inflections. - -## 0.3.2 - -- Cleanup. - -## 0.3.1 - -- Case conversion handles sentences delimited with whitespace. - -## 0.3.0 - -- Handle irregular plural nouns. -- Added shortcut functions. - -## 0.2.0 - -- Handle uncountable nouns. -- Added singular inflection. -- Added plural inflection. -- Refactored to use a dart:convert based API. - -## 0.1.0 - -- Initial version, convertToSnakeCase / convertToSpinalCase. diff --git a/packages/inflection3/LICENSE b/packages/inflection3/LICENSE deleted file mode 100644 index 8f65b579..00000000 --- a/packages/inflection3/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License (MIT) - -Copyright (c) 2021 dukefirehawk.com - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/packages/inflection3/README.md b/packages/inflection3/README.md deleted file mode 100644 index e006b917..00000000 --- a/packages/inflection3/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# Inflection3 -[![version](https://img.shields.io/badge/pub-v1.0.0-brightgreen)](https://pub.dartlang.org/packages/inflection3) -[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety) - -In grammar, inflection or inflexion is the modification of a word to express -different grammatical categories such as tense, mood, voice, aspect, person, -number, gender and case. - -A port of the Rails/ActiveSupport inflector library to Dart. - -**IMPORTANT NOTE**: This is a *hard fork* of the original `inflection2` package, -as the former is now archived, and abandoned. - -## Usage - -A simple usage example: - -```dart -import 'package:inflection3/inflection3.dart'; - -void main() { - // Using 'shortcut' functions. - - print(pluralize("house")); // => "houses" - print(convertToPlural("house")); // => "houses", alias for pluralize - print(pluralizeVerb("goes")); // => "go" - print(singularize("axes")); // => "axis" - print(convertToSingular("axes")); // => "axis", alias for pluralize - print(singularizeVerb("write")); // => "writes" - print(convertToSnakeCase("CamelCaseName")); // => "camel_case_name" - print(convertToSpinalCase("CamelCaseName")); // => "camel-case-name" - print(past("forgo")); // => "forwent" - - // Using default encoders. - - print(PLURAL.convert("virus")); // => "viri" - print(SINGULAR.convert("Matrices")); // => "Matrix" - print(SINGULAR.convert("species")); // => "species" - print(SNAKE_CASE.convert("CamelCaseName")); // => "camel_case_name" - print(SPINAL_CASE.convert("CamelCaseName")); // => "camel-case-name" - print(PAST.convert("miss")); // => "missed" -} -``` - diff --git a/packages/inflection3/analysis_options.yaml b/packages/inflection3/analysis_options.yaml deleted file mode 100644 index c5667a80..00000000 --- a/packages/inflection3/analysis_options.yaml +++ /dev/null @@ -1,5 +0,0 @@ -include: package:pedantic/analysis_options.yaml - -analyzer: - strong-mode: - implicit-casts: false \ No newline at end of file diff --git a/packages/inflection3/example/main.dart b/packages/inflection3/example/main.dart deleted file mode 100644 index 26c57cbf..00000000 --- a/packages/inflection3/example/main.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:inflection3/inflection3.dart'; - -void main() { - // Using 'shortcut' functions. - - print(pluralize('house')); // => 'houses' - print(convertToPlural('house')); // => 'houses', alias for pluralize - print(pluralizeVerb('goes')); // => 'go' - print(singularize('axes')); // => 'axis' - print(convertToSingular('axes')); // => 'axis', alias for pluralize - print(singularizeVerb('write')); // => 'writes' - print(convertToSnakeCase('CamelCaseName')); // => 'camel_case_name' - print(convertToSpinalCase('CamelCaseName')); // => 'camel-case-name' - print(past('forgo')); // => 'forwent' - - // Using default encoders. - - print(PLURAL.convert('virus')); // => 'viri' - print(SINGULAR.convert('Matrices')); // => 'Matrix' - print(SINGULAR.convert('species')); // => 'species' - print(SNAKE_CASE.convert('CamelCaseName')); // => 'camel_case_name' - print(SPINAL_CASE.convert('CamelCaseName')); // => 'camel-case-name' - print(PAST.convert('miss')); // => 'missed' -} diff --git a/packages/inflection3/lib/inflection3.dart b/packages/inflection3/lib/inflection3.dart deleted file mode 100644 index 0c2bab0a..00000000 --- a/packages/inflection3/lib/inflection3.dart +++ /dev/null @@ -1,43 +0,0 @@ -/// In grammar, inflection or inflexion is the modification of a word to express -/// different grammatical categories such as tense, mood, voice, aspect, person, -/// number, gender and case. -/// -/// [ActiveSupport Inflector](https://github.com/rails/rails/tree/master/activesupport/lib/active_support/inflector) -/// [Letter case](http://en.wikipedia.org/wiki/Letter_case#Special_case_styles) -library inflection3; - -import 'src/past.dart'; -import 'src/plural.dart'; -import 'src/plural_verb.dart'; -import 'src/singular.dart'; -import 'src/singular_verb.dart'; -import 'src/snake_case.dart'; -import 'src/spinal_case.dart'; - -export 'src/past.dart'; -export 'src/plural.dart'; -export 'src/plural_verb.dart'; -export 'src/singular.dart'; -export 'src/singular_verb.dart'; -export 'src/snake_case.dart'; -export 'src/spinal_case.dart'; -export 'src/is_tense.dart'; - -String convertToPlural(String word) => PLURAL.convert(word); -String pluralize(String word) => PLURAL.convert(word); - -String convertToPluralVerb(String word) => PLURALVERB.convert(word); -String pluralizeVerb(String word) => PLURALVERB.convert(word); - -String convertToSingular(String word) => SINGULAR.convert(word); -String singularize(String word) => SINGULAR.convert(word); - -String convertToSingularVerb(String word) => SINGULARVERB.convert(word); -String singularizeVerb(String word) => SINGULARVERB.convert(word); - -String convertToSnakeCase(String word) => SNAKE_CASE.convert(word); - -String convertToSpinalCase(String word) => SPINAL_CASE.convert(word); - -String convertToPast(String word) => PAST.convert(word); -String past(String word) => PAST.convert(word); diff --git a/packages/inflection3/lib/src/irregular_past_verbs.dart b/packages/inflection3/lib/src/irregular_past_verbs.dart deleted file mode 100644 index a31fbbd3..00000000 --- a/packages/inflection3/lib/src/irregular_past_verbs.dart +++ /dev/null @@ -1,323 +0,0 @@ -//library inflection2.irregular_past_verbs; - -/// A collection of verbs with irregular past. -final Map irregularPastVerbs = const { - 'backslidden': 'backslid', - 'forbidden': 'forbade', - 'stridden': 'strode', - 'stricken': 'struck', - 'brought': 'brought', - 'striven': 'strove', - 'swollen': 'swelled', - 'thought': 'thought', - 'trodden': 'trod', - 'forbade': 'forbade', - 'arisen': 'arose', - 'awoken': 'awoke', - 'beaten': 'beat', - 'became': 'became', - 'become': 'became', - 'bidden': 'bid', - 'bitten': 'bit', - 'broken': 'broke', - 'bought': 'bought', - 'caught': 'caught', - 'choose': 'chose', - 'chosen': 'chose', - 'forbid': 'forbade', - 'dreamt': 'dreamt', - 'driven': 'drove', - 'fallen': 'fell', - 'fought': 'fought', - 'freeze': 'froze', - 'frozen': 'froze', - 'gotten': 'got', - 'ground': 'ground', - 'hidden': 'hid', - 'learnt': 'learnt', - 'proven': 'proved', - 'sought': 'sought', - 'shaken': 'shook', - 'shaven': 'shaved', - 'shrank': 'shrank', - 'shrink': 'shrank', - 'shrunk': 'shrank', - 'spoken': 'spoke', - 'spoilt': 'spoilt', - 'spread': 'spread', - 'sprang': 'sprang', - 'spring': 'sprang', - 'sprung': 'sprang', - 'stolen': 'stole', - 'strewn': 'strewed', - 'strode': 'strode', - 'stride': 'strode', - 'strike': 'struck', - 'strove': 'strove', - 'struck': 'struck', - 'string': 'strung', - 'strung': 'strung', - 'taught': 'taught', - 'thrown': 'threw', - 'thrust': 'thrust', - 'arise': 'arose', - 'arose': 'arose', - 'awake': 'awoke', - 'awoke': 'awoke', - 'borne': 'bore', - 'began': 'began', - 'begin': 'began', - 'begun': 'began', - 'bound': 'bound', - 'bleed': 'bled', - 'blown': 'blew', - 'break': 'broke', - 'breed': 'bred', - 'bring': 'brought', - 'broke': 'broke', - 'build': 'built', - 'built': 'built', - 'burnt': 'burnt', - 'catch': 'caught', - 'chose': 'chose', - 'cling': 'clung', - 'clung': 'clung', - 'creep': 'crept', - 'crept': 'crept', - 'dealt': 'dealt', - 'wound': 'wound', - 'wring': 'wrung', - 'wrung': 'wrung', - 'write': 'wrote', - 'wrote': 'wrote', - 'drawn': 'drew', - 'drank': 'drank', - 'drink': 'drank', - 'drunk': 'drank', - 'drive': 'drove', - 'drove': 'drove', - 'dwelt': 'dwelt', - 'eaten': 'ate', - 'fight': 'fought', - 'found': 'found', - 'fling': 'flung', - 'flung': 'flung', - 'flown': 'flew', - 'froze': 'froze', - 'given': 'gave', - 'grind': 'ground', - 'grown': 'grew', - 'heard': 'heard', - 'knelt': 'knelt', - 'known': 'knew', - 'leant': 'leant', - 'leapt': 'leapt', - 'leave': 'left', - 'meant': 'meant', - 'risen': 'rose', - 'shake': 'shook', - 'shorn': 'sheared', - 'shone': 'shone', - 'shook': 'shook', - 'shoot': 'shot', - 'shown': 'showed', - 'slain': 'slew', - 'sleep': 'slept', - 'slept': 'slept', - 'slide': 'slid', - 'sling': 'slung', - 'slung': 'slung', - 'slunk': 'slunk', - 'smelt': 'smelt', - 'snuck': 'snuck', - 'speak': 'spoke', - 'spelt': 'spelt', - 'spend': 'spent', - 'spent': 'spent', - 'spilt': 'spilt', - 'split': 'split', - 'spoke': 'spoke', - 'stand': 'stood', - 'stood': 'stood', - 'steal': 'stole', - 'stick': 'stuck', - 'stole': 'stole', - 'stuck': 'stuck', - 'sting': 'stung', - 'stung': 'stung', - 'stank': 'stank', - 'stink': 'stank', - 'stunk': 'stank', - 'swear': 'swore', - 'swore': 'swore', - 'sworn': 'swore', - 'sweep': 'swept', - 'swept': 'swept', - 'swing': 'swung', - 'swung': 'swung', - 'taken': 'took', - 'teach': 'taught', - 'think': 'thought', - 'threw': 'threw', - 'throw': 'threw', - 'tread': 'trod', - 'wake': 'woke', - 'woken': 'woke', - 'woven': 'wove', - 'bear': 'bore', - 'bore': 'bore', - 'born': 'bore', - 'beat': 'beat', - 'bend': 'bent', - 'bent': 'bent', - 'bind': 'bound', - 'bite': 'bit', - 'bled': 'bled', - 'blew': 'blew', - 'blow': 'blew', - 'bred': 'bred', - 'cast': 'cast', - 'clad': 'clad', - 'come': 'came', - 'cost': 'cost', - 'deal': 'dealt', - 'does': 'did', - 'done': 'did', - 'draw': 'drew', - 'drew': 'drew', - 'fall': 'fell', - 'feed': 'fed', - 'feel': 'felt', - 'fell': 'fell', - 'felt': 'felt', - 'find': 'found', - 'flee': 'fled', - 'fled': 'fled', - 'flew': 'flew', - 'gave': 'gave', - 'give': 'gave', - 'gone': 'went', - 'grew': 'grew', - 'grow': 'grew', - 'hang': 'hung', - 'hung': 'hung', - 'have': 'had', - 'hear': 'heard', - 'hewn': 'hewed', - 'hide': 'hid', - 'hold': 'held', - 'held': 'held', - 'hurt': 'hurt', - 'keep': 'kept', - 'kept': 'kept', - 'knew': 'knew', - 'know': 'knew', - 'laid': 'laid', - 'lead': 'led', - 'left': 'left', - 'lend': 'lent', - 'lent': 'lent', - 'lain': 'lay', - 'lose': 'lost', - 'lost': 'lost', - 'make': 'made', - 'made': 'made', - 'mean': 'meant', - 'meet': 'met', - 'mown': 'mowed', - 'paid': 'paid', - 'pled': 'pled', - 'read': 'read', - 'ride': 'rode', - 'rode': 'rode', - 'ring': 'rang', - 'rung': 'rang', - 'rise': 'rose', - 'rose': 'rose', - 'sang': 'sang', - 'sawn': 'sawed', - 'said': 'said', - 'seen': 'saw', - 'seek': 'sought', - 'sell': 'sold', - 'slew': 'slew', - 'sold': 'sold', - 'send': 'sent', - 'sent': 'sent', - 'sewn': 'sewed', - 'shed': 'shed', - 'shot': 'shot', - 'shut': 'shut', - 'sing': 'sang', - 'sung': 'sang', - 'slid': 'slid', - 'slit': 'slit', - 'sown': 'sowed', - 'sped': 'sped', - 'spin': 'spun', - 'spun': 'spun', - 'spit': 'spit', - 'spat': 'spat', - 'swam': 'swam', - 'swim': 'swam', - 'swum': 'swam', - 'take': 'took', - 'tear': 'tore', - 'tore': 'tore', - 'torn': 'tore', - 'tell': 'told', - 'told': 'told', - 'took': 'took', - 'trod': 'trod', - 'wear': 'wore', - 'wore': 'wore', - 'worn': 'wore', - 'weep': 'wept', - 'went': 'went', - 'wept': 'wept', - 'were': 'were', - 'wind': 'wound', - 'woke': 'woke', - 'wove': 'wove', - 'are': 'were', - 'ate': 'ate', - 'bet': 'bet', - 'bid': 'bid', - 'bit': 'bit', - 'buy': 'bought', - 'cut': 'cut', - 'did': 'did', - 'dig': 'dug', - 'dug': 'dug', - 'eat': 'ate', - 'fed': 'fed', - 'fly': 'flew', - 'get': 'got', - 'got': 'got', - 'had': 'had', - 'has': 'had', - 'hid': 'hid', - 'hit': 'hit', - 'lay': 'laid', - 'led': 'led', - 'let': 'let', - 'lit': 'lit', - 'met': 'met', - 'pay': 'paid', - 'put': 'put', - 'ran': 'ran', - 'rid': 'rid', - 'run': 'ran', - 'saw': 'saw', - 'say': 'said', - 'see': 'saw', - 'sit': 'sat', - 'sat': 'sat', - 'set': 'set', - 'was': 'was', - 'win': 'won', - 'won': 'won', - 'do': 'did', - 'go': 'went', - 'is': 'was', -}; diff --git a/packages/inflection3/lib/src/irregular_plural_nouns.dart b/packages/inflection3/lib/src/irregular_plural_nouns.dart deleted file mode 100644 index 150acc1e..00000000 --- a/packages/inflection3/lib/src/irregular_plural_nouns.dart +++ /dev/null @@ -1,11 +0,0 @@ -//library inflection2.irregular_plural_nouns; - -/// A collection of nouns with irregular plurals. -/// -/// [A List of 100 Irregular Plural Nouns in English](http://grammar.about.com/od/words/a/A-List-Of-Irregular-Plural-Nouns-In-English.htm) -final Map irregularPluralNouns = const { - 'person': 'people', - 'man': 'men', - 'child': 'children', - 'sex': 'sexes' -}; diff --git a/packages/inflection3/lib/src/irregular_plural_verbs.dart b/packages/inflection3/lib/src/irregular_plural_verbs.dart deleted file mode 100644 index 32ad8e06..00000000 --- a/packages/inflection3/lib/src/irregular_plural_verbs.dart +++ /dev/null @@ -1,9 +0,0 @@ -//library inflection2.irregular_plural_verbs; - -/// A collection of verbs with irregular plurals. -final Map irregularPluralVerbs = const { - 'is': 'are', - 'am': 'are', - 'was': 'were', - 'has': 'have' -}; diff --git a/packages/inflection3/lib/src/is_tense.dart b/packages/inflection3/lib/src/is_tense.dart deleted file mode 100644 index 29cfcde3..00000000 --- a/packages/inflection3/lib/src/is_tense.dart +++ /dev/null @@ -1,6 +0,0 @@ -import '../inflection3.dart'; - -/// returns true if this word is in the past tense -bool isPastTense(String word) { - return word.toLowerCase().trim() == past(word).toLowerCase().trim(); -} diff --git a/packages/inflection3/lib/src/past.dart b/packages/inflection3/lib/src/past.dart deleted file mode 100644 index dd6f6d8d..00000000 --- a/packages/inflection3/lib/src/past.dart +++ /dev/null @@ -1,69 +0,0 @@ -//library inflection2.past; - -import 'dart:convert'; - -import 'irregular_past_verbs.dart'; -import 'verbs_ending_with_ed.dart'; -import 'util.dart'; - -class PastEncoder extends Converter { - final List _inflectionRules = []; - - PastEncoder() { - irregularPastVerbs.forEach((String presentOrParticiple, String past) { - addIrregularInflectionRule(presentOrParticiple, past); - }); - [ - [r'.+', (Match m) => '${m[0]}ed'], - [r'([^aeiou])y$', (Match m) => '${m[1]}ied'], - [r'([aeiou]e)$', (Match m) => '${m[1]}d'], - [r'[aeiou][^aeiou]e$', (Match m) => '${m[0]}d'] - ] - .reversed - .forEach((rule) => addInflectionRule(rule.first as String, rule.last)); - } - - void addInflectionRule(String presentOrParticiple, dynamic past) { - _inflectionRules - .add([RegExp(presentOrParticiple, caseSensitive: false), past]); - } - - void addIrregularInflectionRule(String presentOrParticiple, String past) { - _inflectionRules.add([ - RegExp( - r'^(back|dis|for|fore|in|inter|mis|off|over|out|par|pre|re|type|un|under|up)?' + - presentOrParticiple + - r'$', - caseSensitive: false), - (Match m) => (m[1] == null) ? past : m[1]! + past - ]); - } - - @override - String convert(String word) { - if (word.isNotEmpty) { - if (word.contains('ed', word.length - 2)) { - var reg = RegExp( - r'^(back|dis|for|fore|in|inter|mis|off|over|out|par|pre|re|type|un|under|up)(.+)$'); - if (reg.hasMatch(word)) { - if (!verbsEndingWithEd.contains(reg.firstMatch(word)!.group(2))) { - return word; - } - } else if (!verbsEndingWithEd.contains(word)) { - return word; - } - } - - for (var r in _inflectionRules) { - var pattern = r.first as RegExp; - if (pattern.hasMatch(word)) { - return word.replaceAllMapped(pattern, r.last as MatchToString); - } - } - } - - return word; - } -} - -final Converter PAST = PastEncoder(); diff --git a/packages/inflection3/lib/src/plural.dart b/packages/inflection3/lib/src/plural.dart deleted file mode 100644 index cf2b9191..00000000 --- a/packages/inflection3/lib/src/plural.dart +++ /dev/null @@ -1,87 +0,0 @@ -//library inflection2.plural; - -import 'dart:convert'; - -import 'uncountable_nouns.dart'; -import 'irregular_plural_nouns.dart'; -import 'util.dart'; - -class PluralEncoder extends Converter { - final List _inflectionRules = []; - - PluralEncoder() { - irregularPluralNouns.forEach((singular, plural) { - addIrregularInflectionRule(singular, plural); - }); - - [ - [r'$', (Match m) => 's'], - [r's$', (Match m) => 's'], - [r'^(ax|test)is$', (Match m) => '${m[1]}es'], - [r'(octop|vir)us$', (Match m) => '${m[1]}i'], - [r'(octop|vir)i$', (Match m) => m[0]], - [r'(alias|status)$', (Match m) => '${m[1]}es'], - [r'(bu)s$', (Match m) => '${m[1]}ses'], - [r'(buffal|tomat)o$', (Match m) => '${m[1]}oes'], - [r'([ti])um$', (Match m) => '${m[1]}a'], - [r'([ti])a$', (Match m) => m[0]], - [r'sis$', (Match m) => 'ses'], - [r'(?:([^f])fe|([lr])f)$', (Match m) => '${m[1]}${m[2]}ves'], - [r'([^aeiouy]|qu)y$', (Match m) => '${m[1]}ies'], - [r'(x|ch|ss|sh)$', (Match m) => '${m[1]}es'], - [r'(matr|vert|ind)(?:ix|ex)$', (Match m) => '${m[1]}ices'], - [r'^(m|l)ouse$', (Match m) => '${m[1]}ice'], - [r'^(m|l)ice$', (Match m) => m[0]], - [r'^(ox)$', (Match m) => '${m[1]}en'], - [r'^(oxen)$', (Match m) => m[1]], - [r'(quiz)$', (Match m) => '${m[1]}zes'] - ] - .reversed - .forEach((rule) => addInflectionRule(rule.first as String, rule.last)); - } - - void addInflectionRule(String singular, dynamic plural) { - _inflectionRules.add([RegExp(singular, caseSensitive: false), plural]); - } - - void addIrregularInflectionRule(String singular, String plural) { - final s0 = singular.substring(0, 1); - final srest = singular.substring(1); - final p0 = plural.substring(0, 1); - final prest = plural.substring(1); - - if (s0.toUpperCase() == p0.toUpperCase()) { - addInflectionRule('($s0)$srest\$', (Match m) => '${m[1]}$prest'); - addInflectionRule('($p0)$prest\$', (Match m) => '${m[1]}$prest'); - } else { - addInflectionRule('${s0.toUpperCase()}(?i)$srest\$', - (Match m) => '${p0.toUpperCase()}$prest'); - addInflectionRule('${s0.toLowerCase()}(?i)$srest\$', - (Match m) => '${p0.toUpperCase()}$prest'); - addInflectionRule('${p0.toUpperCase()}(?i)$prest\$', - (Match m) => '${p0.toUpperCase()}$prest'); - addInflectionRule('${p0.toLowerCase()}(?i)$prest\$', - (Match m) => '${p0.toLowerCase()}$prest'); - } - } - - @override - String convert(String word) { - if (word.isNotEmpty) { - if (uncountableNouns.contains(word.toLowerCase())) { - return word; - } else { - for (var r in _inflectionRules) { - var pattern = r.first as RegExp; - if (pattern.hasMatch(word)) { - return word.replaceAllMapped(pattern, r.last as MatchToString); - } - } - } - } - - return word; - } -} - -final Converter PLURAL = PluralEncoder(); diff --git a/packages/inflection3/lib/src/plural_verb.dart b/packages/inflection3/lib/src/plural_verb.dart deleted file mode 100644 index 7a59d7ce..00000000 --- a/packages/inflection3/lib/src/plural_verb.dart +++ /dev/null @@ -1,49 +0,0 @@ -//library inflection2.plural_verb; - -import 'dart:convert'; - -import 'irregular_plural_verbs.dart'; -import 'util.dart'; - -class PluralVerbEncoder extends Converter { - final List _inflectionRules = []; - - PluralVerbEncoder() { - irregularPluralVerbs.forEach((singular, plural) { - addInflectionRule(singular, (Match m) => plural); - }); - - [ - [r'e?s$', (Match m) => ''], - [r'ies$', (Match m) => 'y'], - [r'([^h|z|o|i])es$', (Match m) => '${m[1]}e'], - [r'ses$', (Match m) => 's'], - [r'zzes$', (Match m) => 'zz'], - [r'([cs])hes$', (Match m) => '${m[1]}h'], - [r'xes$', (Match m) => 'x'], - [r'sses$', (Match m) => 'ss'] - ] - .reversed - .forEach((rule) => addInflectionRule(rule.first as String, rule.last)); - } - - void addInflectionRule(String singular, dynamic plural) { - _inflectionRules.add([RegExp(singular, caseSensitive: false), plural]); - } - - @override - String convert(String word) { - if (word.isNotEmpty) { - for (var r in _inflectionRules) { - var pattern = r.first as RegExp; - if (pattern.hasMatch(word)) { - return word.replaceAllMapped(pattern, r.last as MatchToString); - } - } - } - - return word; - } -} - -final Converter PLURALVERB = PluralVerbEncoder(); diff --git a/packages/inflection3/lib/src/singular.dart b/packages/inflection3/lib/src/singular.dart deleted file mode 100644 index 205ba1b6..00000000 --- a/packages/inflection3/lib/src/singular.dart +++ /dev/null @@ -1,95 +0,0 @@ -//library inflection2.singular; - -import 'dart:convert'; - -import 'uncountable_nouns.dart'; -import 'irregular_plural_nouns.dart'; -import 'util.dart'; - -class SingularEncoder extends Converter { - final List _inflectionRules = []; - - SingularEncoder() { - irregularPluralNouns.forEach((singular, plural) { - addIrregularInflectionRule(singular, plural); - }); - - [ - [r's$', (Match m) => ''], - [r'(ss)$', (Match m) => m[1]], - [r'(n)ews$', (Match m) => '${m[1]}ews'], // TODO: uncountable? - [r'([ti])a$', (Match m) => '${m[1]}um'], - [ - r'((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$', - (Match m) => '${m[1]}sis' - ], - [r'(^analy)(sis|ses)$', (Match m) => '${m[1]}sis'], // TODO: not needed? - [r'([^f])ves$', (Match m) => '${m[1]}fe'], - [r'(hive|tive)s$', (Match m) => m[1]], - [r'([lr])ves$', (Match m) => '${m[1]}f'], - [r'([^aeiouy]|qu)ies$', (Match m) => '${m[1]}y'], - [r'(s)eries$', (Match m) => '${m[1]}eries'], // TODO: uncountable - [r'(m)ovies$', (Match m) => '${m[1]}ovie'], - [r'(x|ch|ss|sh)es$', (Match m) => m[1]], - [r'^(m|l)ice$', (Match m) => '${m[1]}ouse'], - [r'(bus)(es)?$', (Match m) => m[1]], - [r'(shoe)s$', (Match m) => m[1]], - [r'(cris|test)(is|es)$', (Match m) => '${m[1]}is'], - [r'^(a)x[ie]s$', (Match m) => '${m[1]}xis'], - [r'(octop|vir)(us|i)$', (Match m) => '${m[1]}us'], - [r'(alias|status)(es)?$', (Match m) => m[1]], - [r'^(ox)en', (Match m) => m[1]], - [r'(vert|ind)ices$', (Match m) => '${m[1]}ex'], - [r'(matr)ices$', (Match m) => '${m[1]}ix'], - [r'(quiz)zes$', (Match m) => m[1]], - [r'(database)s$', (Match m) => m[1]] - ] - .reversed - .forEach((rule) => addInflectionRule(rule.first as String, rule.last)); - } - - void addInflectionRule(String plural, dynamic singular) { - _inflectionRules.add([RegExp(plural, caseSensitive: false), singular]); - } - - void addIrregularInflectionRule(String singular, String plural) { - final s0 = singular.substring(0, 1); - final srest = singular.substring(1); - final p0 = plural.substring(0, 1); - final prest = plural.substring(1); - - if (s0.toUpperCase() == p0.toUpperCase()) { - addInflectionRule('($s0)$srest\$', (Match m) => '${m[1]}$srest'); - addInflectionRule('($p0)$prest\$', (Match m) => '${m[1]}$srest'); - } else { - addInflectionRule('${s0.toUpperCase()}(?i)$srest\$', - (Match m) => '${s0.toUpperCase()}$srest'); - addInflectionRule('${s0.toLowerCase()}(?i)$srest\$', - (Match m) => '${s0.toUpperCase()}$srest'); - addInflectionRule('${p0.toUpperCase()}(?i)$prest\$', - (Match m) => '${s0.toUpperCase()}$srest'); - addInflectionRule('${p0.toLowerCase()}(?i)$prest\$', - (Match m) => '${s0.toLowerCase()}$srest'); - } - } - - @override - String convert(String word) { - if (word.isNotEmpty) { - if (uncountableNouns.contains(word.toLowerCase())) { - return word; - } else { - for (var r in _inflectionRules) { - var pattern = r.first as RegExp; - if (pattern.hasMatch(word)) { - return word.replaceAllMapped(pattern, r.last as MatchToString); - } - } - } - } - - return word; - } -} - -final Converter SINGULAR = SingularEncoder(); diff --git a/packages/inflection3/lib/src/singular_verb.dart b/packages/inflection3/lib/src/singular_verb.dart deleted file mode 100644 index 26cb2d3c..00000000 --- a/packages/inflection3/lib/src/singular_verb.dart +++ /dev/null @@ -1,46 +0,0 @@ -//library inflection2.singular_verb; - -import 'dart:convert'; - -import 'irregular_plural_verbs.dart'; -import 'util.dart'; - -class SingularVerbEncoder extends Converter { - final List _inflectionRules = []; - - SingularVerbEncoder() { - irregularPluralVerbs.forEach((singular, plural) { - addInflectionRule(plural, (Match m) => singular); - }); - - [ - [r'$', (Match m) => 's'], - [r'([^aeiou])y$', (Match m) => '${m[1]}ies'], - [r'(z)$', (Match m) => '${m[1]}es'], - [r'(ss|zz|x|h|o|us)$', (Match m) => '${m[1]}es'], - [r'(ed)$', (Match m) => '${m[1]}'] - ] - .reversed - .forEach((rule) => addInflectionRule(rule.first as String, rule.last)); - } - - void addInflectionRule(String singular, dynamic plural) { - _inflectionRules.add([RegExp(singular, caseSensitive: false), plural]); - } - - @override - String convert(String word) { - if (word.isNotEmpty) { - for (var r in _inflectionRules) { - var pattern = r.first as RegExp; - if (pattern.hasMatch(word)) { - return word.replaceAllMapped(pattern, r.last as MatchToString); - } - } - } - - return word; - } -} - -final Converter SINGULARVERB = SingularVerbEncoder(); diff --git a/packages/inflection3/lib/src/snake_case.dart b/packages/inflection3/lib/src/snake_case.dart deleted file mode 100644 index 9e8b3ed3..00000000 --- a/packages/inflection3/lib/src/snake_case.dart +++ /dev/null @@ -1,24 +0,0 @@ -//library inflection2.snake_case; - -import 'dart:convert'; - -final _underscoreRE0 = RegExp(r'''([A-Z\d]+)([A-Z][a-z])'''); -final _underscoreRE1 = RegExp(r'''([a-z\d])([A-Z])'''); -final _underscoreRE2 = RegExp(r'[-\s]'); - -class SnakeCaseEncoder extends Converter { - const SnakeCaseEncoder(); - - /// Converts the input [phrase] to 'spinal case', i.e. a hyphen-delimited, - /// lowercase form. Also known as 'kebab case' or 'lisp case'. - @override - String convert(String phrase) { - return phrase - .replaceAllMapped(_underscoreRE0, (match) => '${match[1]}_${match[2]}') - .replaceAllMapped(_underscoreRE1, (match) => '${match[1]}_${match[2]}') - .replaceAll(_underscoreRE2, '_') - .toLowerCase(); - } -} - -const Converter SNAKE_CASE = SnakeCaseEncoder(); diff --git a/packages/inflection3/lib/src/spinal_case.dart b/packages/inflection3/lib/src/spinal_case.dart deleted file mode 100644 index c9dc0fee..00000000 --- a/packages/inflection3/lib/src/spinal_case.dart +++ /dev/null @@ -1,24 +0,0 @@ -//library inflection2.spinal_case; - -import 'dart:convert'; - -final _underscoreRE0 = RegExp(r'''([A-Z\d]+)([A-Z][a-z])'''); -final _underscoreRE1 = RegExp(r'''([a-z\d])([A-Z])'''); -final _underscoreRE2 = RegExp(r'[_\s]'); - -class SpinalCaseEncoder extends Converter { - const SpinalCaseEncoder(); - - /// Converts the input [phrase] to 'spinal case', i.e. a hyphen-delimited, - /// lowercase form. Also known as 'kebab case' or 'lisp case'. - @override - String convert(String phrase) { - return phrase - .replaceAllMapped(_underscoreRE0, (match) => '${match[1]}-${match[2]}') - .replaceAllMapped(_underscoreRE1, (match) => '${match[1]}-${match[2]}') - .replaceAll(_underscoreRE2, '-') - .toLowerCase(); - } -} - -const Converter SPINAL_CASE = SpinalCaseEncoder(); diff --git a/packages/inflection3/lib/src/uncountable_nouns.dart b/packages/inflection3/lib/src/uncountable_nouns.dart deleted file mode 100644 index 1d583866..00000000 --- a/packages/inflection3/lib/src/uncountable_nouns.dart +++ /dev/null @@ -1,16 +0,0 @@ -//library inflection2.uncountable; - -/// Uncountable nouns are substances, concepts etc that we cannot divide into -/// separate elements. We cannot 'count' them. -final Set uncountableNouns = Set.from(const [ - 'equipment', - 'information', - 'rice', - 'money', - 'species', - 'series', - 'fish', - 'sheep', - 'jeans', - 'police' -]); diff --git a/packages/inflection3/lib/src/util.dart b/packages/inflection3/lib/src/util.dart deleted file mode 100644 index 0f738eff..00000000 --- a/packages/inflection3/lib/src/util.dart +++ /dev/null @@ -1 +0,0 @@ -typedef MatchToString = String Function(Match m); diff --git a/packages/inflection3/lib/src/verbs_ending_with_ed.dart b/packages/inflection3/lib/src/verbs_ending_with_ed.dart deleted file mode 100644 index 460aa2a5..00000000 --- a/packages/inflection3/lib/src/verbs_ending_with_ed.dart +++ /dev/null @@ -1,21 +0,0 @@ -//library inflection2.verbs_ending_with_ed; - -/// A collection of verbs ending with -ed. -final List verbsEndingWithEd = const [ - 'bed', - 'bleed', - 'breed', - 'embed', - 'exceed', - 'feed', - 'heed', - 'need', - 'proceed', - 'seed', - 'shred', - 'speed', - 'succeed', - 'ted', - 'wed', - 'weed' -]; diff --git a/packages/inflection3/pubspec.yaml b/packages/inflection3/pubspec.yaml deleted file mode 100644 index 7ec6f165..00000000 --- a/packages/inflection3/pubspec.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: inflection3 -version: 1.0.0 -description: Grammatical Inflection encoders. -homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/inflection3 -environment: - sdk: '>=2.12.0 <3.0.0' -dev_dependencies: - test: ^1.17.4 - pedantic: ^1.11.0 diff --git a/packages/inflection3/test/all_test.dart b/packages/inflection3/test/all_test.dart deleted file mode 100644 index f13f2150..00000000 --- a/packages/inflection3/test/all_test.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'inflection_test.dart' as inflection_test; -import 'past_test.dart' as past_test; -import 'plural_test.dart' as plural_test; -import 'plural_verb_test.dart' as plural_verb_test; -import 'singular_test.dart' as singular_test; -import 'singular_verb_test.dart' as singular_verb_test; -import 'snake_case_test.dart' as snake_case_test; -import 'spinal_case_test.dart' as spinal_case_test; -import 'is_tense_test.dart' as is_tense_test; - -void main() { - inflection_test.main(); - past_test.main(); - plural_test.main(); - plural_verb_test.main(); - singular_test.main(); - singular_verb_test.main(); - snake_case_test.main(); - spinal_case_test.main(); - is_tense_test.main(); -} diff --git a/packages/inflection3/test/inflection_test.dart b/packages/inflection3/test/inflection_test.dart deleted file mode 100644 index 594a98e7..00000000 --- a/packages/inflection3/test/inflection_test.dart +++ /dev/null @@ -1,17 +0,0 @@ -library inflection3.test; - -import 'package:test/test.dart'; -import 'package:inflection3/inflection3.dart'; - -void main() { - group('The inflection library', () { - test('provides a few convenient helper functions', () { - expect(pluralize('axis'), equals('axes')); - expect(convertToPlural('axis'), equals('axes')); - expect(singularize('Houses'), equals('House')); - expect(convertToSingular('Houses'), equals('House')); - expect(convertToSnakeCase('CamelCase'), equals('camel_case')); - expect(convertToSpinalCase('CamelCase'), equals('camel-case')); - }); - }); -} diff --git a/packages/inflection3/test/is_tense_test.dart b/packages/inflection3/test/is_tense_test.dart deleted file mode 100644 index 2385ad76..00000000 --- a/packages/inflection3/test/is_tense_test.dart +++ /dev/null @@ -1,15 +0,0 @@ -library inflection3.is_tense.test; - -import 'package:test/test.dart'; -import 'package:inflection3/inflection3.dart'; - -void main() { - group('isTense', () { - test('correctly identifies if a word is in past tense', () { - expect(isPastTense('run'), false); - expect(isPastTense('ran'), true); - expect(isPastTense('PusHed'), true); - expect(isPastTense('PusH'), false); - }); - }); -} diff --git a/packages/inflection3/test/past_test.dart b/packages/inflection3/test/past_test.dart deleted file mode 100644 index 294e3a4a..00000000 --- a/packages/inflection3/test/past_test.dart +++ /dev/null @@ -1,45 +0,0 @@ -library inflection3.past.test; - -import 'package:inflection3/inflection3.dart'; -import 'package:inflection3/src/irregular_past_verbs.dart'; -import 'package:test/test.dart'; - -void main() { - group('The PastEncoder', () { - test('converts verbs from present or participle to past', () { - expect(PAST.convert(''), equals('')); - expect(PAST.convert('ask'), equals('asked')); - expect(PAST.convert('close'), equals('closed')); - expect(PAST.convert('die'), equals('died')); - expect(PAST.convert('phone'), equals('phoned')); - expect(PAST.convert('play'), equals('played')); - expect(PAST.convert('destroy'), equals('destroyed')); - expect(PAST.convert('show'), equals('showed')); - expect(PAST.convert('marry'), equals('married')); - expect(PAST.convert('study'), equals('studied')); - expect(PAST.convert('visit'), equals('visited')); - expect(PAST.convert('miss'), equals('missed')); - expect(PAST.convert('watch'), equals('watched')); - expect(PAST.convert('finish'), equals('finished')); - expect(PAST.convert('fix'), equals('fixed')); - expect(PAST.convert('buzz'), equals('buzzed')); - expect(PAST.convert('asked'), equals('asked')); - expect(PAST.convert('closed'), equals('closed')); - expect(PAST.convert('reopened'), equals('reopened')); - expect(PAST.convert('unseed'), equals('unseeded')); - }); - - test('handles irregular past verbs', () { - irregularPastVerbs.forEach((String presentOrParticiple, String past) { - expect(PAST.convert(presentOrParticiple), equals(past)); - }); - expect(PAST.convert('forgo'), equals('forwent')); - expect(PAST.convert('undo'), equals('undid')); - expect(PAST.convert('outsell'), equals('outsold')); - expect(PAST.convert('rebreed'), equals('rebred')); - expect(PAST.convert('arose'), equals('arose')); - expect(PAST.convert('backslid'), equals('backslid')); - expect(PAST.convert('forbade'), equals('forbade')); - }); - }); -} diff --git a/packages/inflection3/test/plural_test.dart b/packages/inflection3/test/plural_test.dart deleted file mode 100644 index 8fff045b..00000000 --- a/packages/inflection3/test/plural_test.dart +++ /dev/null @@ -1,65 +0,0 @@ -library inflection3.plural.test; - -import 'package:inflection3/inflection3.dart'; -import 'package:inflection3/src/uncountable_nouns.dart'; -import 'package:test/test.dart'; - -void main() { - group('The PluralEncoder', () { - test('converts nouns from singular to plural', () { - expect(PLURAL.convert(''), equals('')); - expect(PLURAL.convert('House'), equals('Houses')); - expect(PLURAL.convert('house'), equals('houses')); - expect(PLURAL.convert('dog'), equals('dogs')); - expect(PLURAL.convert('axis'), equals('axes')); - expect(PLURAL.convert('testis'), equals('testes')); - expect(PLURAL.convert('octopus'), equals('octopi')); - expect(PLURAL.convert('virus'), equals('viri')); - expect(PLURAL.convert('octopi'), equals('octopi')); - expect(PLURAL.convert('viri'), equals('viri')); - expect(PLURAL.convert('alias'), equals('aliases')); - expect(PLURAL.convert('status'), equals('statuses')); - expect(PLURAL.convert('bus'), equals('buses')); - expect(PLURAL.convert('buffalo'), equals('buffaloes')); - expect(PLURAL.convert('tomato'), equals('tomatoes')); - expect(PLURAL.convert('ultimatum'), equals('ultimata')); - expect(PLURAL.convert('pentium'), equals('pentia')); - expect(PLURAL.convert('ultimata'), equals('ultimata')); - expect(PLURAL.convert('pentia'), equals('pentia')); - expect(PLURAL.convert('nemesis'), equals('nemeses')); - expect(PLURAL.convert('hive'), equals('hives')); - expect(PLURAL.convert('fly'), equals('flies')); - expect(PLURAL.convert('dish'), equals('dishes')); - expect(PLURAL.convert('bench'), equals('benches')); - expect(PLURAL.convert('matrix'), equals('matrices')); - expect(PLURAL.convert('vertex'), equals('vertices')); - expect(PLURAL.convert('index'), equals('indices')); - expect(PLURAL.convert('mouse'), equals('mice')); - expect(PLURAL.convert('louse'), equals('lice')); - expect(PLURAL.convert('mice'), equals('mice')); - expect(PLURAL.convert('lice'), equals('lice')); - expect(PLURAL.convert('ox'), equals('oxen')); - expect(PLURAL.convert('ox'), equals('oxen')); - expect(PLURAL.convert('oxen'), equals('oxen')); - expect(PLURAL.convert('quiz'), equals('quizzes')); - }); - - test('handles uncountable nouns', () { - uncountableNouns.forEach((noun) { - expect(PLURAL.convert(noun), equals(noun)); - }); - - uncountableNouns.forEach((noun) { - final upperNoun = noun.toUpperCase(); - expect(PLURAL.convert(upperNoun), equals(upperNoun)); - }); - }); - - test('handles irregular plural nouns', () { - expect(PLURAL.convert('person'), equals('people')); - expect(PLURAL.convert('Child'), equals('Children')); - expect(PLURAL.convert('children'), equals('children')); - expect(PLURAL.convert('man'), equals('men')); - }); - }); -} diff --git a/packages/inflection3/test/plural_verb_test.dart b/packages/inflection3/test/plural_verb_test.dart deleted file mode 100644 index de59c0b4..00000000 --- a/packages/inflection3/test/plural_verb_test.dart +++ /dev/null @@ -1,34 +0,0 @@ -library inflection3.plural_verb.test; - -import 'package:inflection3/inflection3.dart'; -import 'package:test/test.dart'; - -void main() { - group('The PluralVerbEncoder', () { - test('converts verbs from singular to plural', () { - expect(PLURALVERB.convert(''), equals('')); - expect(PLURALVERB.convert('eats'), equals('eat')); - expect(PLURALVERB.convert('goes'), equals('go')); - expect(PLURALVERB.convert('boxes'), equals('box')); - expect(PLURALVERB.convert('pays'), equals('pay')); - expect(PLURALVERB.convert('rides'), equals('ride')); - expect(PLURALVERB.convert('writes'), equals('write')); - expect(PLURALVERB.convert('wears'), equals('wear')); - expect(PLURALVERB.convert('steals'), equals('steal')); - expect(PLURALVERB.convert('springs'), equals('spring')); - expect(PLURALVERB.convert('speaks'), equals('speak')); - expect(PLURALVERB.convert('sings'), equals('sing')); - expect(PLURALVERB.convert('buses'), equals('bus')); - expect(PLURALVERB.convert('knows'), equals('know')); - expect(PLURALVERB.convert('hides'), equals('hide')); - expect(PLURALVERB.convert('catches'), equals('catch')); - }); - - test('handles irregular plural verbs', () { - expect(PLURALVERB.convert('am'), equals('are')); - expect(PLURALVERB.convert('is'), equals('are')); - expect(PLURALVERB.convert('was'), equals('were')); - expect(PLURALVERB.convert('has'), equals('have')); - }); - }); -} diff --git a/packages/inflection3/test/singular_test.dart b/packages/inflection3/test/singular_test.dart deleted file mode 100644 index a6292bf5..00000000 --- a/packages/inflection3/test/singular_test.dart +++ /dev/null @@ -1,64 +0,0 @@ -library inflection3.singular.test; - -import 'package:inflection3/inflection3.dart'; -import 'package:inflection3/src/uncountable_nouns.dart'; -import 'package:test/test.dart'; - -void main() { - group('The SingularEncoder', () { - test('converts nouns from plural to singular', () { - expect(SINGULAR.convert(''), equals('')); - expect(SINGULAR.convert('Houses'), equals('House')); - expect(SINGULAR.convert('houses'), equals('house')); - expect(SINGULAR.convert('ultimata'), equals('ultimatum')); - expect(SINGULAR.convert('pentia'), equals('pentium')); - expect(SINGULAR.convert('analyses'), equals('analysis')); - expect(SINGULAR.convert('diagnoses'), equals('diagnosis')); - expect(SINGULAR.convert('Parentheses'), equals('Parenthesis')); - expect(SINGULAR.convert('lives'), equals('life')); - expect(SINGULAR.convert('hives'), equals('hive')); - expect(SINGULAR.convert('tives'), equals('tive')); - expect(SINGULAR.convert('shelves'), equals('shelf')); - expect(SINGULAR.convert('qualities'), equals('quality')); - expect(SINGULAR.convert('series'), equals('series')); - expect(SINGULAR.convert('movies'), equals('movie')); - expect(SINGULAR.convert('benches'), equals('bench')); - expect(SINGULAR.convert('fishes'), equals('fish')); - expect(SINGULAR.convert('mice'), equals('mouse')); - expect(SINGULAR.convert('lice'), equals('louse')); - expect(SINGULAR.convert('buses'), equals('bus')); - expect(SINGULAR.convert('shoes'), equals('shoe')); - expect(SINGULAR.convert('testis'), equals('testis')); - expect(SINGULAR.convert('crisis'), equals('crisis')); - expect(SINGULAR.convert('axes'), equals('axis')); - expect(SINGULAR.convert('axis'), equals('axis')); - expect(SINGULAR.convert('viri'), equals('virus')); - expect(SINGULAR.convert('octopi'), equals('octopus')); - expect(SINGULAR.convert('aliases'), equals('alias')); - expect(SINGULAR.convert('statuses'), equals('status')); - expect(SINGULAR.convert('vertices'), equals('vertex')); - expect(SINGULAR.convert('indices'), equals('index')); - expect(SINGULAR.convert('Matrices'), equals('Matrix')); - expect(SINGULAR.convert('quizzes'), equals('quiz')); - expect(SINGULAR.convert('databases'), equals('database')); - }); - - test('handles uncountable nouns', () { - uncountableNouns.forEach((noun) { - expect(SINGULAR.convert(noun), equals(noun)); - }); - - uncountableNouns.forEach((noun) { - final upperNoun = noun.toUpperCase(); - expect(SINGULAR.convert(upperNoun), equals(upperNoun)); - }); - }); - - test('handles irregular plural nouns', () { - expect(SINGULAR.convert('people'), equals('person')); - expect(SINGULAR.convert('Children'), equals('Child')); - expect(SINGULAR.convert('child'), equals('child')); - expect(SINGULAR.convert('men'), equals('man')); - }); - }); -} diff --git a/packages/inflection3/test/singular_verb_test.dart b/packages/inflection3/test/singular_verb_test.dart deleted file mode 100644 index 5a5d9fd8..00000000 --- a/packages/inflection3/test/singular_verb_test.dart +++ /dev/null @@ -1,33 +0,0 @@ -library inflection3.singular_verb.test; - -import 'package:inflection3/inflection3.dart'; -import 'package:test/test.dart'; - -void main() { - group('The SingularVerbEncoder', () { - test('converts verbs from singular to plural', () { - expect(SINGULARVERB.convert(''), equals('')); - expect(SINGULARVERB.convert('eat'), equals('eats')); - expect(SINGULARVERB.convert('go'), equals('goes')); - expect(SINGULARVERB.convert('box'), equals('boxes')); - expect(SINGULARVERB.convert('pay'), equals('pays')); - expect(SINGULARVERB.convert('ride'), equals('rides')); - expect(SINGULARVERB.convert('write'), equals('writes')); - expect(SINGULARVERB.convert('wear'), equals('wears')); - expect(SINGULARVERB.convert('steal'), equals('steals')); - expect(SINGULARVERB.convert('spring'), equals('springs')); - expect(SINGULARVERB.convert('speak'), equals('speaks')); - expect(SINGULARVERB.convert('sing'), equals('sings')); - expect(SINGULARVERB.convert('bus'), equals('buses')); - expect(SINGULARVERB.convert('know'), equals('knows')); - expect(SINGULARVERB.convert('hide'), equals('hides')); - expect(SINGULARVERB.convert('catch'), equals('catches')); - }); - - test('handles irregular plural verbs', () { - expect(SINGULARVERB.convert('are'), equals('is')); - expect(SINGULARVERB.convert('were'), equals('was')); - expect(SINGULARVERB.convert('have'), equals('has')); - }); - }); -} diff --git a/packages/inflection3/test/snake_case_test.dart b/packages/inflection3/test/snake_case_test.dart deleted file mode 100644 index 76ad1854..00000000 --- a/packages/inflection3/test/snake_case_test.dart +++ /dev/null @@ -1,18 +0,0 @@ -library inflection3.snake_case.test; - -import 'package:inflection3/inflection3.dart'; -import 'package:test/test.dart'; - -void main() { - group('The SnakeCaseEncoder', () { - test('converts phrases to "snake_case"', () { - expect(SNAKE_CASE.convert(''), equals('')); - expect(SNAKE_CASE.convert('CamelCaseName'), equals('camel_case_name')); - expect(SNAKE_CASE.convert('propertyName'), equals('property_name')); - expect(SNAKE_CASE.convert('property'), equals('property')); - expect(SNAKE_CASE.convert('lisp-case'), equals('lisp_case')); - expect(SNAKE_CASE.convert('This is a nice article'), - equals('this_is_a_nice_article')); - }); - }); -} diff --git a/packages/inflection3/test/spinal_case_test.dart b/packages/inflection3/test/spinal_case_test.dart deleted file mode 100644 index cc423195..00000000 --- a/packages/inflection3/test/spinal_case_test.dart +++ /dev/null @@ -1,18 +0,0 @@ -library inflection3.spinal_case.test; - -import 'package:inflection3/inflection3.dart'; -import 'package:test/test.dart'; - -void main() { - group('The SpinalCaseEncoder', () { - test('converts phrases to "spinal-case"', () { - expect(SPINAL_CASE.convert(''), equals('')); - expect(SPINAL_CASE.convert('CamelCaseName'), equals('camel-case-name')); - expect(SPINAL_CASE.convert('propertyName'), equals('property-name')); - expect(SPINAL_CASE.convert('property'), equals('property')); - expect(SPINAL_CASE.convert('snake_case'), equals('snake-case')); - expect(SPINAL_CASE.convert('This is a nice article'), - equals('this-is-a-nice-article')); - }); - }); -} diff --git a/packages/json_god/.gitignore b/packages/json_god/.gitignore deleted file mode 100644 index 24d68312..00000000 --- a/packages/json_god/.gitignore +++ /dev/null @@ -1,71 +0,0 @@ -# See https://www.dartlang.org/tools/private-files.html - -# Files and directories created by pub -.dart_tool -.packages -.pub/ -build/ - -# If you're building an application, you may want to check-in your pubspec.lock -pubspec.lock - -# Directory created by dartdoc -# If you don't generate documentation locally you can remove this line. -doc/api/ - -### Dart template -# See https://www.dartlang.org/tools/private-files.html - -# Files and directories created by pub - -# SDK 1.20 and later (no longer creates packages directories) - -# Older SDK versions -# (Include if the minimum SDK version specified in pubsepc.yaml is earlier than 1.20) -.project -.buildlog -**/packages/ - - -# Files created by dart2js -# (Most Dart developers will use pub build to compile Dart, use/modify these -# rules if you intend to use dart2js directly -# Convention is to use extension '.dart.js' for Dart compiled to Javascript to -# differentiate from explicit Javascript files) -*.dart.js -*.part.js -*.js.deps -*.js.map -*.info.json - -# Directory created by dartdoc - -# Don't commit pubspec lock file -# (Library packages only! Remove pattern if developing an application package) -### JetBrains template -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff: - -## VsCode -.vscode/ - -## File-based project format: -*.iws - -## Plugin-specific files: - -# IntelliJ -.idea/ -/out/ -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties diff --git a/packages/json_god/.travis.yml b/packages/json_god/.travis.yml deleted file mode 100644 index a9e2c109..00000000 --- a/packages/json_god/.travis.yml +++ /dev/null @@ -1,4 +0,0 @@ -language: dart -dart: - - dev - - stable \ No newline at end of file diff --git a/packages/json_god/AUTHORS.md b/packages/json_god/AUTHORS.md deleted file mode 100644 index ac95ab58..00000000 --- a/packages/json_god/AUTHORS.md +++ /dev/null @@ -1,12 +0,0 @@ -Primary Authors -=============== - -* __[Thomas Hii](dukefirehawk.apps@gmail.com)__ - - Thomas is the current maintainer of the code base. He has refactored and migrated the - code base to support NNBD. - -* __[Tobe O](thosakwe@gmail.com)__ - - Tobe has written much of the original code prior to NNBD migration. He has moved on and - is no longer involved with the project. diff --git a/packages/json_god/CHANGELOG.md b/packages/json_god/CHANGELOG.md deleted file mode 100644 index e9674da2..00000000 --- a/packages/json_god/CHANGELOG.md +++ /dev/null @@ -1,25 +0,0 @@ -# 4.0.3 -* Fixed static analysis errors - -# 4.0.2 -* Updated pubspec description -* Fixed static analysis errors - -# 4.0.1 -* Added example -* Updated README - -# 4.0.0 -* Migrated to support Dart SDK 2.12.x NNBD - -# 3.0.0 -* Migrated to work with Dart SDK 2.12.x Non NNBD - -# 2.0.0-beta+3 -* Long-needed updates, ensured Dart 2 compatibility, fixed DDC breakages. -* Patches for reflection bugs with typing. - -# 2.0.0-beta+2 -* This version breaks in certain Dart versions (likely anything *after* `2.0.0-dev.59.0`) -until https://github.com/dart-lang/sdk/issues/33594 is resolved. -* Removes the reference to `Schema` class. \ No newline at end of file diff --git a/packages/json_god/LICENSE b/packages/json_god/LICENSE deleted file mode 100644 index 8f65b579..00000000 --- a/packages/json_god/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License (MIT) - -Copyright (c) 2021 dukefirehawk.com - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/packages/json_god/README.md b/packages/json_god/README.md deleted file mode 100644 index 6853dc0f..00000000 --- a/packages/json_god/README.md +++ /dev/null @@ -1,112 +0,0 @@ -# angel3_json_god - -[![version](https://img.shields.io/badge/pub-v4.0.3-brightgreen)](https://pub.dartlang.org/packages/angel3_json_god) -[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety) -[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion) - -[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/json_god/LICENSE) - -**DEPRECATED: Replaced by [`belatuk_json_serializer`](https://pub.dartlang.org/packages/belatuk_json_serializer) package** - -The ***new and improved*** definitive solution for JSON in Dart. It supports synchronously transform an object into a JSON string and also deserialize a JSON string back into an instance of any type. - -## Installation - - dependencies: - angel3_json_god: ^4.0.0 - -## Usage - -It is recommended to import the library under an alias, i.e., `god`. - -```dart -import 'package:angel3_json_god/angel3_json_god.dart' as god; -``` - -## Serializing JSON - -Simply call `god.serialize(x)` to synchronously transform an object into a JSON -string. - -```dart -Map map = {"foo": "bar", "numbers": [1, 2, {"three": 4}]}; - -// Output: {"foo":"bar","numbers":[1,2,{"three":4]"} -String json = god.serialize(map); -print(json); -``` - -You can easily serialize classes, too. JSON God also supports classes as members. - -```dart - -class A { - String foo; - A(this.foo); -} - -class B { - late String hello; - late A nested; - B(String hello, String foo) { - this.hello = hello; - this.nested = A(foo); - } -} - -main() { - print(god.serialize( B("world", "bar"))); -} - -// Output: {"hello":"world","nested":{"foo":"bar"}} -``` - -If a class has a `toJson` method, it will be called instead. - -## Deserializing JSON - -Deserialization is equally easy, and is provided through `god.deserialize`. - -```dart -Map map = god.deserialize('{"hello":"world"}'); -int three = god.deserialize("3"); -``` - -### Deserializing to Classes - -JSON God lets you deserialize JSON into an instance of any type. Simply pass the -type as the second argument to `god.deserialize`. - -If the class has a `fromJson` constructor, it will be called instead. - -```dart -class Child { - String foo; -} - -class Parent { - String hello; - Child child = Child(); -} - -main() { - God god = God(); - Parent parent = god.deserialize('{"hello":"world","child":{"foo":"bar"}}', Parent); - print(parent); -} -``` - -**Any JSON-deserializable classes must initializable without parameters. -If `Foo()` would throw an error, then you can't use Foo with JSON.** - -This allows for validation of a sort, as only fields you have declared will be -accepted. - -```dart -class HasAnInt { int theInt; } - -HasAnInt invalid = god.deserialize('["some invalid input"]', HasAnInt); -// Throws an error -``` - -An exception will be thrown if validation fails. diff --git a/packages/json_god/analysis_options.yaml b/packages/json_god/analysis_options.yaml deleted file mode 100644 index eae1e42a..00000000 --- a/packages/json_god/analysis_options.yaml +++ /dev/null @@ -1,3 +0,0 @@ -analyzer: - strong-mode: - implicit-casts: false \ No newline at end of file diff --git a/packages/json_god/example/main.dart b/packages/json_god/example/main.dart deleted file mode 100644 index 9fddc639..00000000 --- a/packages/json_god/example/main.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:angel3_json_god/angel3_json_god.dart' as god; - -class A { - String foo; - A(this.foo); -} - -class B { - late String hello; - late A nested; - B(String hello, String foo) { - this.hello = hello; - this.nested = A(foo); - } -} - -void main() { - print(god.serialize(B("world", "bar"))); -} diff --git a/packages/json_god/lib/angel3_json_god.dart b/packages/json_god/lib/angel3_json_god.dart deleted file mode 100644 index b638bd30..00000000 --- a/packages/json_god/lib/angel3_json_god.dart +++ /dev/null @@ -1,18 +0,0 @@ -/// A robust library for JSON serialization and deserialization. -library angel3_json_god; - -//import 'package:dart2_constant/convert.dart'; -import 'dart:convert'; -import 'package:logging/logging.dart'; -import 'src/reflection.dart' as reflection; - -part 'src/serialize.dart'; -part 'src/deserialize.dart'; -part 'src/validation.dart'; -part 'src/util.dart'; - -/// Instead, listen to [logger]. -@deprecated -bool debug = false; - -final Logger logger = Logger('json_god'); diff --git a/packages/json_god/lib/src/deserialize.dart b/packages/json_god/lib/src/deserialize.dart deleted file mode 100644 index 54f76398..00000000 --- a/packages/json_god/lib/src/deserialize.dart +++ /dev/null @@ -1,44 +0,0 @@ -part of angel3_json_god; - -/// Deserializes a JSON string into a Dart datum. -/// -/// You can also provide an output Type to attempt to serialize the JSON into. -deserialize(String json, {Type? outputType}) { - var deserialized = deserializeJson(json, outputType: outputType); - logger.info("Deserialization result: $deserialized"); - return deserialized; -} - -/// Deserializes JSON into data, without validating it. -deserializeJson(String s, {Type? outputType}) { - logger.info("Deserializing the following JSON: $s"); - - if (outputType == null) { - logger - .info("No output type was specified, so we are just using json.decode"); - return json.decode(s); - } else { - logger.info("Now deserializing to type: $outputType"); - return deserializeDatum(json.decode(s), outputType: outputType); - } -} - -/// Deserializes some JSON-serializable value into a usable Dart value. -deserializeDatum(value, {Type? outputType}) { - if (outputType != null) { - return reflection.deserialize(value, outputType, deserializeDatum); - } else if (value is List) { - logger.info("Deserializing this List: $value"); - return value.map(deserializeDatum).toList(); - } else if (value is Map) { - logger.info("Deserializing this Map: $value"); - Map result = {}; - value.forEach((k, v) { - result[k] = deserializeDatum(v); - }); - return result; - } else if (_isPrimitive(value)) { - logger.info("Value $value is a primitive"); - return value; - } -} diff --git a/packages/json_god/lib/src/reflection.dart b/packages/json_god/lib/src/reflection.dart deleted file mode 100644 index 2f8e3f31..00000000 --- a/packages/json_god/lib/src/reflection.dart +++ /dev/null @@ -1,189 +0,0 @@ -library angel3_json_god.reflection; - -import 'dart:mirrors'; -import '../angel3_json_god.dart'; - -const Symbol hashCodeSymbol = #hashCode; -const Symbol runtimeTypeSymbol = #runtimeType; - -typedef Serializer(value); -typedef Deserializer(value, {Type? outputType}); - -List _findGetters(ClassMirror classMirror) { - List result = []; - - classMirror.instanceMembers - .forEach((Symbol symbol, MethodMirror methodMirror) { - if (methodMirror.isGetter && - symbol != hashCodeSymbol && - symbol != runtimeTypeSymbol) { - logger.info("Found getter on instance: $symbol"); - result.add(symbol); - } - }); - - return result; -} - -serialize(value, Serializer serializer, [@deprecated bool debug = false]) { - logger.info("Serializing this value via reflection: $value"); - Map result = {}; - InstanceMirror instanceMirror = reflect(value); - ClassMirror classMirror = instanceMirror.type; - - // Check for toJson - for (Symbol symbol in classMirror.instanceMembers.keys) { - if (symbol == #toJson) { - logger.info("Running toJson..."); - var result = instanceMirror.invoke(symbol, []).reflectee; - logger.info("Result of serialization via reflection: $result"); - return result; - } - } - - for (Symbol symbol in _findGetters(classMirror)) { - String name = MirrorSystem.getName(symbol); - var valueForSymbol = instanceMirror.getField(symbol).reflectee; - - try { - result[name] = serializer(valueForSymbol); - logger.info("Set $name to $valueForSymbol"); - } catch (e, st) { - logger.severe("Could not set $name to $valueForSymbol", e, st); - } - } - - logger.info("Result of serialization via reflection: $result"); - - return result; -} - -deserialize(value, Type outputType, Deserializer deserializer, - [@deprecated bool debug = false]) { - logger.info("About to deserialize $value to a $outputType"); - - try { - if (value is List) { - List typeArguments = reflectType(outputType).typeArguments; - - Iterable it; - - if (typeArguments.isEmpty) { - it = value.map(deserializer); - } else { - it = value.map((item) => - deserializer(item, outputType: typeArguments[0].reflectedType)); - } - - if (typeArguments.isEmpty) return it.toList(); - logger.info( - 'Casting list elements to ${typeArguments[0].reflectedType} via List.from'); - - var mirror = reflectType(List, [typeArguments[0].reflectedType]); - - if (mirror is ClassMirror) { - var output = mirror.newInstance(#from, [it]).reflectee; - logger.info('Casted list type: ${output.runtimeType}'); - return output; - } else { - throw ArgumentError( - '${typeArguments[0].reflectedType} is not a class.'); - } - } else if (value is Map) { - return _deserializeFromJsonByReflection(value, deserializer, outputType); - } else { - return deserializer(value); - } - } catch (e, st) { - logger.severe('Deserialization failed.', e, st); - rethrow; - } -} - -/// Uses mirrors to deserialize an object. -_deserializeFromJsonByReflection( - data, Deserializer deserializer, Type outputType) { - // Check for fromJson - var typeMirror = reflectType(outputType); - - if (typeMirror is! ClassMirror) { - throw ArgumentError('$outputType is not a class.'); - } - - var type = typeMirror; - var fromJson = Symbol('${MirrorSystem.getName(type.simpleName)}.fromJson'); - - for (Symbol symbol in type.declarations.keys) { - if (symbol == fromJson) { - var decl = type.declarations[symbol]; - - if (decl is MethodMirror && decl.isConstructor) { - logger.info("Running fromJson..."); - var result = type.newInstance(#fromJson, [data]).reflectee; - - logger.info("Result of deserialization via reflection: $result"); - return result; - } - } - } - - ClassMirror classMirror = type; - InstanceMirror instanceMirror = classMirror.newInstance(Symbol(""), []); - - if (classMirror.isSubclassOf(reflectClass(Map))) { - var typeArguments = classMirror.typeArguments; - - if (typeArguments.isEmpty || - classMirror.typeArguments - .every((t) => t == currentMirrorSystem().dynamicType)) { - return data; - } else { - var mapType = - reflectType(Map, typeArguments.map((t) => t.reflectedType).toList()) - as ClassMirror; - logger.info('Casting this map $data to Map of [$typeArguments]'); - var output = mapType.newInstance(Symbol(''), []).reflectee; - - for (var key in data.keys) { - output[key] = data[key]; - } - - logger.info('Output: $output of type ${output.runtimeType}'); - return output; - } - } else { - data.keys.forEach((key) { - try { - logger.info("Now deserializing value for $key"); - logger.info("data[\"$key\"] = ${data[key]}"); - var deserializedValue = deserializer(data[key]); - - logger.info( - "I want to set $key to the following ${deserializedValue.runtimeType}: $deserializedValue"); - // Get target type of getter - Symbol searchSymbol = Symbol(key.toString()); - Symbol symbolForGetter = classMirror.instanceMembers.keys - .firstWhere((x) => x == searchSymbol); - Type requiredType = classMirror - .instanceMembers[symbolForGetter]!.returnType.reflectedType; - if (data[key].runtimeType != requiredType) { - logger.info("Currently, $key is a ${data[key].runtimeType}."); - logger.info("However, $key must be a $requiredType."); - - deserializedValue = - deserializer(deserializedValue, outputType: requiredType); - } - - logger.info( - "Final deserialized value for $key: $deserializedValue <${deserializedValue.runtimeType}>"); - instanceMirror.setField(Symbol(key.toString()), deserializedValue); - - logger.info("Success! $key has been set to $deserializedValue"); - } catch (e, st) { - logger.severe('Could not set value for field $key.', e, st); - } - }); - } - - return instanceMirror.reflectee; -} diff --git a/packages/json_god/lib/src/serialize.dart b/packages/json_god/lib/src/serialize.dart deleted file mode 100644 index 8a9d4342..00000000 --- a/packages/json_god/lib/src/serialize.dart +++ /dev/null @@ -1,36 +0,0 @@ -part of angel3_json_god; - -/// Serializes any arbitrary Dart datum to JSON. Supports schema validation. -String serialize(value) { - var serialized = serializeObject(value); - logger.info('Serialization result: $serialized'); - return json.encode(serialized); -} - -/// Transforms any Dart datum into a value acceptable to json.encode. -serializeObject(value) { - if (_isPrimitive(value)) { - logger.info("Serializing primitive value: $value"); - return value; - } else if (value is DateTime) { - logger.info("Serializing this DateTime: $value"); - return value.toIso8601String(); - } else if (value is Iterable) { - logger.info("Serializing this Iterable: $value"); - return value.map(serializeObject).toList(); - } else if (value is Map) { - logger.info("Serializing this Map: $value"); - return serializeMap(value); - } else { - return serializeObject(reflection.serialize(value, serializeObject)); - } -} - -/// Recursively transforms a Map and its children into JSON-serializable data. -Map serializeMap(Map value) { - Map outputMap = {}; - value.forEach((key, value) { - outputMap[key] = serializeObject(value); - }); - return outputMap; -} diff --git a/packages/json_god/lib/src/util.dart b/packages/json_god/lib/src/util.dart deleted file mode 100644 index 9c1c1e8b..00000000 --- a/packages/json_god/lib/src/util.dart +++ /dev/null @@ -1,5 +0,0 @@ -part of angel3_json_god; - -bool _isPrimitive(value) { - return value is num || value is bool || value is String || value == null; -} diff --git a/packages/json_god/lib/src/validation.dart b/packages/json_god/lib/src/validation.dart deleted file mode 100644 index cfce78cc..00000000 --- a/packages/json_god/lib/src/validation.dart +++ /dev/null @@ -1,25 +0,0 @@ -part of angel3_json_god; - -/// Thrown when schema validation fails. -class JsonValidationError implements Exception { - //final Schema schema; - final invalidData; - final String cause; - - const JsonValidationError( - this.cause, this.invalidData); //, Schema this.schema); -} - -/// Specifies a schema to validate a class with. -class WithSchema { - final Map schema; - - const WithSchema(this.schema); -} - -/// Specifies a schema to validate a class with. -class WithSchemaUrl { - final String schemaUrl; - - const WithSchemaUrl(this.schemaUrl); -} diff --git a/packages/json_god/pubspec.yaml b/packages/json_god/pubspec.yaml deleted file mode 100644 index 982d62e8..00000000 --- a/packages/json_god/pubspec.yaml +++ /dev/null @@ -1,12 +0,0 @@ -name: angel3_json_god -version: 4.0.3 -description: Easy JSON to Object serialization and deserialization in Dart. -homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/json_god -environment: - sdk: '>=2.12.0 <3.0.0' -dependencies: - #dart2_constant: ^1.0.0 - logging: ^1.0.1 -dev_dependencies: - stack_trace: ^1.10.0 - test: ^1.17.4 \ No newline at end of file diff --git a/packages/json_god/test/deserialization_test.dart b/packages/json_god/test/deserialization_test.dart deleted file mode 100644 index 4e10ccd3..00000000 --- a/packages/json_god/test/deserialization_test.dart +++ /dev/null @@ -1,115 +0,0 @@ -import 'package:angel3_json_god/angel3_json_god.dart' as god; -import 'package:test/test.dart'; -import 'shared.dart'; - -main() { - god.logger.onRecord.listen(printRecord); - - group('deserialization', () { - test('deserialize primitives', testDeserializationOfPrimitives); - - test('deserialize maps', testDeserializationOfMaps); - - test('deserialize maps + reflection', - testDeserializationOfMapsWithReflection); - - test('deserialize lists + reflection', - testDeserializationOfListsAsWellAsViaReflection); - - test('deserialize with schema validation', - testDeserializationWithSchemaValidation); - }); -} - -testDeserializationOfPrimitives() { - expect(god.deserialize('1'), equals(1)); - expect(god.deserialize('1.4'), equals(1.4)); - expect(god.deserialize('"Hi!"'), equals("Hi!")); - expect(god.deserialize("true"), equals(true)); - expect(god.deserialize("null"), equals(null)); -} - -testDeserializationOfMaps() { - String simpleJson = - '{"hello":"world", "one": 1, "class": {"hello": "world"}}'; - String nestedJson = - '{"foo": {"bar": "baz", "funny": {"how": "life", "seems": 2, "hate": "us sometimes"}}}'; - var simple = god.deserialize(simpleJson) as Map; - var nested = god.deserialize(nestedJson) as Map; - - expect(simple['hello'], equals('world')); - expect(simple['one'], equals(1)); - expect(simple['class']['hello'], equals('world')); - - expect(nested['foo']['bar'], equals('baz')); - expect(nested['foo']['funny']['how'], equals('life')); - expect(nested['foo']['funny']['seems'], equals(2)); - expect(nested['foo']['funny']['hate'], equals('us sometimes')); -} - -class Pokedex { - Map? pokemon; -} - -testDeserializationOfMapsWithReflection() { - var s = '{"pokemon": {"Bulbasaur": 1, "Deoxys": 382}}'; - var pokedex = god.deserialize(s, outputType: Pokedex) as Pokedex; - expect(pokedex.pokemon, hasLength(2)); - expect(pokedex.pokemon!['Bulbasaur'], 1); - expect(pokedex.pokemon!['Deoxys'], 382); -} - -testDeserializationOfListsAsWellAsViaReflection() { - String json = '''[ - { - "hello": "world", - "nested": [] - }, - { - "hello": "dolly", - "nested": [ - { - "bar": "baz" - }, - { - "bar": "fight" - } - ] - } - ] - '''; - - var list = god.deserialize(json, outputType: ([]).runtimeType) - as List; - SampleClass first = list[0]; - SampleClass second = list[1]; - - expect(list.length, equals(2)); - expect(first.hello, equals("world")); - expect(first.nested.length, equals(0)); - expect(second.hello, equals("dolly")); - expect(second.nested.length, equals(2)); - - SampleNestedClass firstNested = second.nested[0]; - SampleNestedClass secondNested = second.nested[1]; - - expect(firstNested.bar, equals("baz")); - expect(secondNested.bar, equals("fight")); -} - -testDeserializationWithSchemaValidation() async { - String babelRcJson = - '{"presets":["es2015","stage-0"],"plugins":["add-module-exports"]}'; - - var deserialized = - god.deserialize(babelRcJson, outputType: BabelRc) as BabelRc; - - print(deserialized.presets.runtimeType); - expect(deserialized.presets is List, equals(true)); - expect(deserialized.presets.length, equals(2)); - expect(deserialized.presets[0], equals('es2015')); - expect(deserialized.presets[1], equals('stage-0')); - expect(deserialized.plugins is List, equals(true)); - expect(deserialized.plugins.length, equals(1)); - expect(deserialized.plugins[0], equals('add-module-exports')); -} diff --git a/packages/json_god/test/serialization_test.dart b/packages/json_god/test/serialization_test.dart deleted file mode 100644 index 7d4b5526..00000000 --- a/packages/json_god/test/serialization_test.dart +++ /dev/null @@ -1,133 +0,0 @@ -//import 'package:dart2_constant/convert.dart'; -import 'dart:convert'; - -import 'package:angel3_json_god/angel3_json_god.dart' as god; -import 'package:test/test.dart'; -import 'shared.dart'; - -main() { - god.logger.onRecord.listen(printRecord); - - group('serialization', () { - test('serialize primitives', testSerializationOfPrimitives); - - test('serialize dates', testSerializationOfDates); - - test('serialize maps', testSerializationOfMaps); - - test('serialize lists', testSerializationOfLists); - - test('serialize via reflection', testSerializationViaReflection); - - test('serialize with schema validation', - testSerializationWithSchemaValidation); - }); -} - -testSerializationOfPrimitives() { - expect(god.serialize(1), equals("1")); - expect(god.serialize(1.4), equals("1.4")); - expect(god.serialize("Hi!"), equals('"Hi!"')); - expect(god.serialize(true), equals("true")); - expect(god.serialize(null), equals("null")); -} - -testSerializationOfDates() { - DateTime date = DateTime.now(); - String s = god.serialize({'date': date}); - - print(s); - - var deserialized = json.decode(s); - expect(deserialized['date'], equals(date.toIso8601String())); -} - -testSerializationOfMaps() { - var simple = json.decode(god - .serialize({'hello': 'world', 'one': 1, 'class': SampleClass('world')})); - var nested = json.decode(god.serialize({ - 'foo': { - 'bar': 'baz', - 'funny': {'how': 'life', 'seems': 2, 'hate': 'us sometimes'} - } - })); - - expect(simple['hello'], equals('world')); - expect(simple['one'], equals(1)); - expect(simple['class']['hello'], equals('world')); - - expect(nested['foo']['bar'], equals('baz')); - expect(nested['foo']['funny']['how'], equals('life')); - expect(nested['foo']['funny']['seems'], equals(2)); - expect(nested['foo']['funny']['hate'], equals('us sometimes')); -} - -testSerializationOfLists() { - List pandorasBox = [ - 1, - "2", - {"num": 3, "four": SampleClass('five')}, - SampleClass('six')..nested.add(SampleNestedClass('seven')) - ]; - String s = god.serialize(pandorasBox); - print(s); - - var deserialized = json.decode(s); - - expect(deserialized is List, equals(true)); - expect(deserialized.length, equals(4)); - expect(deserialized[0], equals(1)); - expect(deserialized[1], equals("2")); - expect(deserialized[2] is Map, equals(true)); - expect(deserialized[2]['num'], equals(3)); - expect(deserialized[2]['four'] is Map, equals(true)); - expect(deserialized[2]['four']['hello'], equals('five')); - expect(deserialized[3] is Map, equals(true)); - expect(deserialized[3]['hello'], equals('six')); - expect(deserialized[3]['nested'] is List, equals(true)); - expect(deserialized[3]['nested'].length, equals(1)); - expect(deserialized[3]['nested'][0] is Map, equals(true)); - expect(deserialized[3]['nested'][0]['bar'], equals('seven')); -} - -testSerializationViaReflection() { - SampleClass sample = SampleClass('world'); - - for (int i = 0; i < 3; i++) { - sample.nested.add(SampleNestedClass('baz')); - } - - String s = god.serialize(sample); - print(s); - - var deserialized = json.decode(s); - expect(deserialized['hello'], equals('world')); - expect(deserialized['nested'] is List, equals(true)); - expect(deserialized['nested'].length == 3, equals(true)); - expect(deserialized['nested'][0]['bar'], equals('baz')); - expect(deserialized['nested'][1]['bar'], equals('baz')); - expect(deserialized['nested'][2]['bar'], equals('baz')); -} - -testSerializationWithSchemaValidation() async { - BabelRc babelRc = - BabelRc(presets: ['es2015', 'stage-0'], plugins: ['add-module-exports']); - - String s = god.serialize(babelRc); - print(s); - - var deserialized = json.decode(s); - - expect(deserialized['presets'] is List, equals(true)); - expect(deserialized['presets'].length, equals(2)); - expect(deserialized['presets'][0], equals('es2015')); - expect(deserialized['presets'][1], equals('stage-0')); - expect(deserialized['plugins'] is List, equals(true)); - expect(deserialized['plugins'].length, equals(1)); - expect(deserialized['plugins'][0], equals('add-module-exports')); - - //Map babelRc2 = {'presets': 'Hello, world!'}; - - String json2 = god.serialize(babelRc); - print(json2); -} diff --git a/packages/json_god/test/shared.dart b/packages/json_god/test/shared.dart deleted file mode 100644 index 31512ce9..00000000 --- a/packages/json_god/test/shared.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:logging/logging.dart'; -import 'package:angel3_json_god/angel3_json_god.dart'; -import 'package:stack_trace/stack_trace.dart'; - -void printRecord(LogRecord rec) { - print(rec); - if (rec.error != null) print(rec.error); - if (rec.stackTrace != null) print(Chain.forTrace(rec.stackTrace!).terse); -} - -class SampleNestedClass { - String? bar; - - SampleNestedClass([String? this.bar]); -} - -class SampleClass { - String? hello; - List nested = []; - - SampleClass([String? this.hello]); -} - -@WithSchemaUrl( - "http://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/babelrc.json") -class BabelRc { - List presets; - List plugins; - - BabelRc( - {List this.presets: const [], - List this.plugins: const []}); -} - -@WithSchema(const { - r"$schema": "http://json-schema.org/draft-04/schema#", - "title": "Validated Sample Class", - "description": "Sample schema for validation via JSON God", - "type": "object", - "hello": const {"description": "A friendly greeting.", "type": "string"}, - "nested": const { - "description": "A list of NestedSampleClass items within this instance.", - "type": "array", - "items": const { - "type": "object", - "bar": const {"description": "Filler text", "type": "string"} - } - }, - "required": const ["hello", "nested"] -}) -class ValidatedSampleClass {} diff --git a/packages/json_god/test/to_json_test.dart b/packages/json_god/test/to_json_test.dart deleted file mode 100644 index a47c2609..00000000 --- a/packages/json_god/test/to_json_test.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:angel3_json_god/angel3_json_god.dart' as god; -import 'package:test/test.dart'; -import 'shared.dart'; - -main() { - god.logger.onRecord.listen(printRecord); - - test('fromJson', () { - var foo = god.deserialize('{"bar":"baz"}', outputType: Foo) as Foo; - - expect(foo is Foo, true); - expect(foo.text, equals('baz')); - }); - - test('toJson', () { - var foo = Foo(text: 'baz'); - var data = god.serializeObject(foo); - expect(data, equals({'bar': 'baz', 'foo': 'poobaz'})); - }); -} - -class Foo { - String? text; - - String get foo => 'poo$text'; - - Foo({this.text}); - - factory Foo.fromJson(Map json) => Foo(text: json['bar'].toString()); - - Map toJson() => {'bar': text, 'foo': foo}; -} diff --git a/packages/merge_map/.gitignore b/packages/merge_map/.gitignore deleted file mode 100644 index 24d68312..00000000 --- a/packages/merge_map/.gitignore +++ /dev/null @@ -1,71 +0,0 @@ -# See https://www.dartlang.org/tools/private-files.html - -# Files and directories created by pub -.dart_tool -.packages -.pub/ -build/ - -# If you're building an application, you may want to check-in your pubspec.lock -pubspec.lock - -# Directory created by dartdoc -# If you don't generate documentation locally you can remove this line. -doc/api/ - -### Dart template -# See https://www.dartlang.org/tools/private-files.html - -# Files and directories created by pub - -# SDK 1.20 and later (no longer creates packages directories) - -# Older SDK versions -# (Include if the minimum SDK version specified in pubsepc.yaml is earlier than 1.20) -.project -.buildlog -**/packages/ - - -# Files created by dart2js -# (Most Dart developers will use pub build to compile Dart, use/modify these -# rules if you intend to use dart2js directly -# Convention is to use extension '.dart.js' for Dart compiled to Javascript to -# differentiate from explicit Javascript files) -*.dart.js -*.part.js -*.js.deps -*.js.map -*.info.json - -# Directory created by dartdoc - -# Don't commit pubspec lock file -# (Library packages only! Remove pattern if developing an application package) -### JetBrains template -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff: - -## VsCode -.vscode/ - -## File-based project format: -*.iws - -## Plugin-specific files: - -# IntelliJ -.idea/ -/out/ -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties diff --git a/packages/merge_map/AUTHORS.md b/packages/merge_map/AUTHORS.md deleted file mode 100644 index ac95ab58..00000000 --- a/packages/merge_map/AUTHORS.md +++ /dev/null @@ -1,12 +0,0 @@ -Primary Authors -=============== - -* __[Thomas Hii](dukefirehawk.apps@gmail.com)__ - - Thomas is the current maintainer of the code base. He has refactored and migrated the - code base to support NNBD. - -* __[Tobe O](thosakwe@gmail.com)__ - - Tobe has written much of the original code prior to NNBD migration. He has moved on and - is no longer involved with the project. diff --git a/packages/merge_map/CHANGELOG.md b/packages/merge_map/CHANGELOG.md deleted file mode 100644 index 31f362c5..00000000 --- a/packages/merge_map/CHANGELOG.md +++ /dev/null @@ -1,16 +0,0 @@ -# 2.0.2 -* Resolve static analysis warnings - -# 2.0.1 -* Updated README - -# 2.0.0 -* Migrated to work with Dart SDK 2.12.x NNBD - -# 1.0.2 -* Add an example, for Pub's sake. - -# 1.0.1 -* Add a specific constraint on Dart versions, to prevent Pub from rejecting all packages that depend on -`merge_map` (the entire Angel framework). -* Add generic type support diff --git a/packages/merge_map/LICENSE b/packages/merge_map/LICENSE deleted file mode 100644 index 8f65b579..00000000 --- a/packages/merge_map/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License (MIT) - -Copyright (c) 2021 dukefirehawk.com - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/packages/merge_map/README.md b/packages/merge_map/README.md deleted file mode 100644 index 3a257a5a..00000000 --- a/packages/merge_map/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# angel3_merge_map - -[![version](https://img.shields.io/badge/pub-v2.0.2-brightgreen)](https://pub.dartlang.org/packages/angel3_merge_map) -[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety) -[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion) - -[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/merge_map/LICENSE) - -**DEPRECATED: Replaced by [`belatuk_merge_map`](https://pub.dartlang.org/packages/belatuk_merge_map) package** - -Combine multiple Maps into one. Equivalent to -[Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) -in JS. - -# Example - -```dart -import "package:angel3_merge_map/angel3_merge_map.dart"; - -void main() { - Map map1 = {'hello': 'world'}; - Map map2 = {'foo': {'bar': 'baz', 'this': 'will be overwritten'}}; - Map map3 = {'foo': {'john': 'doe', 'this': 'overrides previous maps'}}; - Map merged = mergeMap(map1, map2, map3); - - // {hello: world, foo: {bar: baz, john: doe, this: overrides previous maps}} -} -``` diff --git a/packages/merge_map/analysis_options.yaml b/packages/merge_map/analysis_options.yaml deleted file mode 100644 index c230cee7..00000000 --- a/packages/merge_map/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:pedantic/analysis_options.yaml -analyzer: - strong-mode: - implicit-casts: false \ No newline at end of file diff --git a/packages/merge_map/example/main.dart b/packages/merge_map/example/main.dart deleted file mode 100644 index c3b7c461..00000000 --- a/packages/merge_map/example/main.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:angel3_merge_map/angel3_merge_map.dart'; - -void main() { - // ignore: omit_local_variable_types - Map map1 = {'hello': 'world'}; - - // ignore: omit_local_variable_types - Map map2 = { - 'foo': {'bar': 'baz', 'this': 'will be overwritten'} - }; - - // ignore: omit_local_variable_types - Map map3 = { - 'foo': {'john': 'doe', 'this': 'overrides previous maps'} - }; - var merged = mergeMap([map1, map2, map3]); - print(merged); - - // {hello: world, foo: {bar: baz, john: doe, this: overrides previous maps}} -} diff --git a/packages/merge_map/lib/angel3_merge_map.dart b/packages/merge_map/lib/angel3_merge_map.dart deleted file mode 100644 index 8158489e..00000000 --- a/packages/merge_map/lib/angel3_merge_map.dart +++ /dev/null @@ -1,34 +0,0 @@ -/// Exposes the [mergeMap] function, which... merges Maps. -library angel3_merge_map; - -dynamic _copyValues( - Map from, Map? to, bool recursive, bool acceptNull) { - for (var key in from.keys) { - if (from[key] is Map && recursive) { - if (!(to![key] is Map)) { - to[key] = {} as V; - } - _copyValues(from[key] as Map, to[key] as Map?, recursive, acceptNull); - } else { - if (from[key] != null || acceptNull) to![key] = from[key]; - } - } -} - -/// Merges the values of the given maps together. -/// -/// `recursive` is set to `true` by default. If set to `true`, -/// then nested maps will also be merged. Otherwise, nested maps -/// will overwrite others. -/// -/// `acceptNull` is set to `false` by default. If set to `false`, -/// then if the value on a map is `null`, it will be ignored, and -/// that `null` will not be copied. -Map mergeMap(Iterable> maps, - {bool recursive = true, bool acceptNull = false}) { - var result = {}; - maps.forEach((Map map) { - _copyValues(map, result, recursive, acceptNull); - }); - return result; -} diff --git a/packages/merge_map/pubspec.yaml b/packages/merge_map/pubspec.yaml deleted file mode 100644 index a4412aa1..00000000 --- a/packages/merge_map/pubspec.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: angel3_merge_map -version: 2.0.2 -description: Combine multiple Maps into one. Equivalent to Object.assign in JS. -homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/merge_map -environment: - sdk: '>=2.12.0 <3.0.0' -dev_dependencies: - test: ^1.17.4 - pedantic: ^1.11.0 diff --git a/packages/merge_map/test/all_test.dart b/packages/merge_map/test/all_test.dart deleted file mode 100644 index 06ab5698..00000000 --- a/packages/merge_map/test/all_test.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'package:angel3_merge_map/angel3_merge_map.dart'; -import 'package:test/test.dart'; - -void main() { - test('can merge two simple maps', () { - var merged = mergeMap([ - {'hello': 'world'}, - {'hello': 'dolly'} - ]); - expect(merged['hello'], equals('dolly')); - }); - - test("the last map's values supersede those of prior", () { - var merged = mergeMap([ - {'letter': 'a'}, - {'letter': 'b'}, - {'letter': 'c'} - ]); - expect(merged['letter'], equals('c')); - }); - - test('can merge two once-nested maps', () { - // ignore: omit_local_variable_types - Map map1 = { - 'hello': 'world', - 'foo': {'nested': false} - }; - // ignore: omit_local_variable_types - Map map2 = { - 'goodbye': 'sad life', - 'foo': {'nested': true, 'it': 'works'} - }; - var merged = mergeMap([map1, map2]); - - expect(merged['hello'], equals('world')); - expect(merged['goodbye'], equals('sad life')); - expect(merged['foo']['nested'], equals(true)); - expect(merged['foo']['it'], equals('works')); - }); - - test('once-nested map supersession', () { - // ignore: omit_local_variable_types - Map map1 = { - 'hello': 'world', - 'foo': {'nested': false} - }; - // ignore: omit_local_variable_types - Map map2 = { - 'goodbye': 'sad life', - 'foo': {'nested': true, 'it': 'works'} - }; - // ignore: omit_local_variable_types - Map map3 = { - 'foo': {'nested': 'supersession'} - }; - - var merged = mergeMap([map1, map2, map3]); - expect(merged['foo']['nested'], equals('supersession')); - }); - - test('can merge two twice-nested maps', () { - // ignore: omit_local_variable_types - Map map1 = { - 'a': { - 'b': {'c': 'd'} - } - }; - // ignore: omit_local_variable_types - Map map2 = { - 'a': { - 'b': {'c': 'D', 'e': 'f'} - } - }; - var merged = mergeMap([map1, map2]); - - expect(merged['a']['b']['c'], equals('D')); - expect(merged['a']['b']['e'], equals('f')); - }); - - test('twice-nested map supersession', () { - // ignore: omit_local_variable_types - Map map1 = { - 'a': { - 'b': {'c': 'd'} - } - }; - // ignore: omit_local_variable_types - Map map2 = { - 'a': { - 'b': {'c': 'D', 'e': 'f'} - } - }; - // ignore: omit_local_variable_types - Map map3 = { - 'a': { - 'b': {'e': 'supersession'} - } - }; - var merged = mergeMap([map1, map2, map3]); - - expect(merged['a']['b']['c'], equals('D')); - expect(merged['a']['b']['e'], equals('supersession')); - }); -} diff --git a/packages/orm/angel_orm_generator/example/main.g.dart b/packages/orm/angel_orm_generator/example/main.g.dart index 517890a9..086f4569 100644 --- a/packages/orm/angel_orm_generator/example/main.g.dart +++ b/packages/orm/angel_orm_generator/example/main.g.dart @@ -82,7 +82,6 @@ class EmployeeQuery extends Query { if (row.every((x) => x == null)) return null; var model = Employee( id: row[0].toString(), - error: (row[1] as String?), createdAt: (row[2] as DateTime?), updatedAt: (row[3] as DateTime?), uniqueId: (row[4] as String?), @@ -187,7 +186,6 @@ class EmployeeQueryValues extends MapQueryValues { set salary(double? value) => values['salary'] = value.toString(); void copyFrom(Employee model) { - error = model.error; createdAt = model.createdAt; updatedAt = model.updatedAt; uniqueId = model.uniqueId; @@ -205,7 +203,6 @@ class EmployeeQueryValues extends MapQueryValues { class Employee extends _Employee { Employee( {this.id, - this.error, this.createdAt, this.updatedAt, this.uniqueId, @@ -217,9 +214,6 @@ class Employee extends _Employee { @override String? id; - @override - String? error; - /// The time at which this item was created. @override DateTime? createdAt; @@ -251,7 +245,6 @@ class Employee extends _Employee { double? salary}) { return Employee( id: id ?? this.id, - error: error ?? this.error, createdAt: createdAt ?? this.createdAt, updatedAt: updatedAt ?? this.updatedAt, uniqueId: uniqueId ?? this.uniqueId, @@ -264,7 +257,6 @@ class Employee extends _Employee { bool operator ==(other) { return other is _Employee && other.id == id && - other.error == error && other.createdAt == createdAt && other.updatedAt == updatedAt && other.uniqueId == uniqueId && @@ -275,21 +267,13 @@ class Employee extends _Employee { @override int get hashCode { - return hashObjects([ - id, - error, - createdAt, - updatedAt, - uniqueId, - firstName, - lastName, - salary - ]); + return hashObjects( + [id, createdAt, updatedAt, uniqueId, firstName, lastName, salary]); } @override String toString() { - return 'Employee(id=$id, error=$error, createdAt=$createdAt, updatedAt=$updatedAt, uniqueId=$uniqueId, firstName=$firstName, lastName=$lastName, salary=$salary)'; + return 'Employee(id=$id, createdAt=$createdAt, updatedAt=$updatedAt, uniqueId=$uniqueId, firstName=$firstName, lastName=$lastName, salary=$salary)'; } Map toJson() { @@ -327,7 +311,6 @@ class EmployeeSerializer extends Codec { static Employee fromMap(Map map) { return Employee( id: map['id'] as String, - error: map['error'] as String, createdAt: map['created_at'] != null ? (map['created_at'] is DateTime ? (map['created_at'] as DateTime) @@ -347,7 +330,6 @@ class EmployeeSerializer extends Codec { static Map toMap(_Employee model) { return { 'id': model.id, - 'error': model.error, 'created_at': model.createdAt?.toIso8601String(), 'updated_at': model.updatedAt?.toIso8601String(), 'unique_id': model.uniqueId, diff --git a/packages/pretty_logging/AUTHORS.md b/packages/pretty_logging/AUTHORS.md deleted file mode 100644 index ac95ab58..00000000 --- a/packages/pretty_logging/AUTHORS.md +++ /dev/null @@ -1,12 +0,0 @@ -Primary Authors -=============== - -* __[Thomas Hii](dukefirehawk.apps@gmail.com)__ - - Thomas is the current maintainer of the code base. He has refactored and migrated the - code base to support NNBD. - -* __[Tobe O](thosakwe@gmail.com)__ - - Tobe has written much of the original code prior to NNBD migration. He has moved on and - is no longer involved with the project. diff --git a/packages/pretty_logging/CHANGELOG.md b/packages/pretty_logging/CHANGELOG.md deleted file mode 100644 index 36c67dea..00000000 --- a/packages/pretty_logging/CHANGELOG.md +++ /dev/null @@ -1,16 +0,0 @@ -# 3.0.3 -* Updated README - -# 3.0.2 -* Updated README - -# 3.0.1 -* Fixed invalid homepage url in pubspec.yaml -# 3.0.0 -* Migrated to support Dart SDK 2.12.x NNBD - -# 2.0.0 -* Migrated to work with Dart SDK 2.12.x Non NNBD - -# 1.0.0 -* Initial release. diff --git a/packages/pretty_logging/LICENSE b/packages/pretty_logging/LICENSE deleted file mode 100644 index 8f65b579..00000000 --- a/packages/pretty_logging/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License (MIT) - -Copyright (c) 2021 dukefirehawk.com - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/packages/pretty_logging/README.md b/packages/pretty_logging/README.md deleted file mode 100644 index d9b087f3..00000000 --- a/packages/pretty_logging/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Angel3 Petty Logging - -[![version](https://img.shields.io/badge/pub-v3.0.3-brightgreen)](https://pub.dartlang.org/packages/angel3_pretty_logging) -[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety) -[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion) - -[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/pretty_logging/LICENSE) - -**DEPRECATED: Replaced by [`belatuk_pretty_logging`](https://pub.dartlang.org/packages/belatuk_pretty_logging) package** - -Standalone helper for colorful logging output, using pkg:io AnsiCode. - -## Installation - -In your `pubspec.yaml`: - -```yaml -dependencies: - angel3_pretty_logging: ^3.0.0 -``` - -## Usage - -Basic usage is very simple: - -```dart -myLogger.onRecord.listen(prettyLog); -``` - -However, you can conditionally pass logic to omit printing an error, provide colors, or to provide a custom print function: - -```dart -var pretty = prettyLog( - logColorChooser: (_) => red, - printFunction: stderr.writeln, - omitError: (r) { - var err = r.error; - return err is AngelHttpException && err.statusCode != 500; - }, -); -myLogger.onRecord.listen(pretty); -``` diff --git a/packages/pretty_logging/analysis_options.yaml b/packages/pretty_logging/analysis_options.yaml deleted file mode 100644 index a4f33350..00000000 --- a/packages/pretty_logging/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:pedantic/analysis_options.yaml -analyzer: - strong-mode: - implicit-casts: false diff --git a/packages/pretty_logging/example/main.dart b/packages/pretty_logging/example/main.dart deleted file mode 100644 index d7d9754b..00000000 --- a/packages/pretty_logging/example/main.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:logging/logging.dart'; -import 'package:angel3_pretty_logging/angel3_pretty_logging.dart'; - -void main() { - Logger.root - ..level = Level.ALL - ..onRecord.listen(prettyLog) - ..info('Hey!') - ..finest('Bye!') - ..severe('Oops!', StateError('Wrong!'), StackTrace.current); -} diff --git a/packages/pretty_logging/lib/angel3_pretty_logging.dart b/packages/pretty_logging/lib/angel3_pretty_logging.dart deleted file mode 100644 index bc45c81b..00000000 --- a/packages/pretty_logging/lib/angel3_pretty_logging.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:logging/logging.dart'; -import 'package:io/ansi.dart'; - -/// Prints the contents of a [LogRecord] with pretty colors. -/// -/// By passing [omitError], you can omit printing the error of a given -/// [LogRecord]. -/// -/// You can also pass a custom [printFunction] or [logColorChooser]. -void prettyLog(LogRecord record, - {bool Function(LogRecord)? omitError, - void Function(String)? printFunction, - AnsiCode Function(Level)? logColorChooser}) { - logColorChooser ??= chooseLogColor; - omitError ??= (_) => false; - printFunction ??= print; - - var code = logColorChooser(record.level); - if (record.error == null) printFunction(code.wrap(record.toString())!); - - if (record.error != null) { - var err = record.error; - if (omitError(record)) return; - printFunction(code.wrap(record.toString() + '\n')!); - printFunction(code.wrap(err.toString())!); - - if (record.stackTrace != null) { - printFunction(code.wrap(record.stackTrace.toString())!); - } - } -} - -/// Chooses a color based on the logger [level]. -AnsiCode chooseLogColor(Level level) { - if (level == Level.SHOUT) { - return backgroundRed; - } else if (level == Level.SEVERE) { - return red; - } else if (level == Level.WARNING) { - return yellow; - } else if (level == Level.INFO) { - return cyan; - } else if (level == Level.CONFIG || - level == Level.FINE || - level == Level.FINER || - level == Level.FINEST) { - return lightGray; - } - return resetAll; -} diff --git a/packages/pretty_logging/pubspec.yaml b/packages/pretty_logging/pubspec.yaml deleted file mode 100644 index c725e843..00000000 --- a/packages/pretty_logging/pubspec.yaml +++ /dev/null @@ -1,11 +0,0 @@ -name: angel3_pretty_logging -version: 3.0.3 -description: Standalone helper for colorful logging output, using pkg:io AnsiCode. -homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/pretty_logging -environment: - sdk: '>=2.12.0 <3.0.0' -dependencies: - io: ^1.0.0 - logging: ^1.0.1 -dev_dependencies: - pedantic: ^1.11.0 diff --git a/packages/pub_sub/.travis.yml b/packages/pub_sub/.travis.yml deleted file mode 100644 index de2210c9..00000000 --- a/packages/pub_sub/.travis.yml +++ /dev/null @@ -1 +0,0 @@ -language: dart \ No newline at end of file diff --git a/packages/pub_sub/AUTHORS.md b/packages/pub_sub/AUTHORS.md deleted file mode 100644 index ac95ab58..00000000 --- a/packages/pub_sub/AUTHORS.md +++ /dev/null @@ -1,12 +0,0 @@ -Primary Authors -=============== - -* __[Thomas Hii](dukefirehawk.apps@gmail.com)__ - - Thomas is the current maintainer of the code base. He has refactored and migrated the - code base to support NNBD. - -* __[Tobe O](thosakwe@gmail.com)__ - - Tobe has written much of the original code prior to NNBD migration. He has moved on and - is no longer involved with the project. diff --git a/packages/pub_sub/CHANGELOG.md b/packages/pub_sub/CHANGELOG.md deleted file mode 100644 index dc6a7382..00000000 --- a/packages/pub_sub/CHANGELOG.md +++ /dev/null @@ -1,34 +0,0 @@ -# Change Log - -## 3.0.3 - -* Final release. Replaced by `belatuk_pub_sub` package. - -## 3.0.2 - -* Resolved static analysis warnings - -## 3.0.1 - -* Resolved static analysis warnings - -## 3.0.0 - -* Migrated to work with Dart SDK 2.12.x NNBD - -## 2.3.0 - -* Allow `2.x` versions of `stream_channel`. -* Apply `package:pedantic` lints. - -## 2.2.0 - -* Upgrade `uuid`. - -## 2.1.0 - -* Allow for "trusted clients," which are implicitly-registered clients. This makes using `package:pub_sub` easier, as well making it easier to scale. - -## 2.0.0 - -* Dart 2 updates. diff --git a/packages/pub_sub/LICENSE b/packages/pub_sub/LICENSE deleted file mode 100644 index 8f65b579..00000000 --- a/packages/pub_sub/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License (MIT) - -Copyright (c) 2021 dukefirehawk.com - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/packages/pub_sub/README.md b/packages/pub_sub/README.md deleted file mode 100644 index 62e9a395..00000000 --- a/packages/pub_sub/README.md +++ /dev/null @@ -1,234 +0,0 @@ -# Angel3 Pub Sub - -[![version](https://img.shields.io/badge/pub-v3.0.3-brightgreen)](https://pub.dartlang.org/packages/angel3_pub_sub) -[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety) -[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion) - -[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/pub_sub/LICENSE) - -**DEPRECATED: Replaced by [`belatuk_pub_sub`](https://pub.dartlang.org/packages/belatuk_pub_sub) package** - -Keep application instances in sync with a simple pub/sub API. - -# Installation - -Add `angel3_pub_sub` as a dependency in your `pubspec.yaml` file: - -```yaml -dependencies: - angel3_pub_sub: ^3.0.0 -``` - -Then, be sure to run `pub get` in your terminal. - -# Usage - -`pub_sub` is your typical pub/sub API. However, `angel3_pub_sub` enforces authentication of every -request. It is very possible that `angel3_pub_sub` will run on both servers and in the browser, -or on a platform angel3_pub_sublike Flutter. Thus, there are provisions available to limit -access. - -**Be careful to not leak any `angel3_pub_sub` client ID's if operating over a network.** -If you do, you risk malicious users injecting events into your application, which -could ultimately spell *disaster*. - -A `angel3_pub_sub` server can operate across multiple *adapters*, which take care of interfacing data over different -media. For example, a single server can handle pub/sub between multiple Isolates and TCP Sockets, as well as -WebSockets, simultaneously. - -```dart -import 'package:angel3_pub_sub/angel3_pub_sub.dart' as pub_sub; - -main() async { - var server = pub_sub.Server([ - FooAdapter(...), - BarAdapter(...) - ]); - - server.addAdapter( BazAdapter(...)); - - // Call `start` to activate adapters, and begin handling requests. - server.start(); -} -``` - -### Trusted Clients - -You can use `package:angel3_pub_sub` without explicitly registering -clients, *if and only if* those clients come from trusted sources. - -Clients via `Isolate` are always trusted. - -Clients via `package:json_rpc_2` must be explicitly marked -as trusted (i.e. using an IP whitelist mechanism): - -```dart -JsonRpc2Adapter(..., isTrusted: false); - -// Pass `null` as Client ID when trusted... -pub_sub.IsolateClient(null); -``` - -### Access Control - -The ID's of all *untrusted* clients who will connect to the server must be known at start-up time. -You may not register new clients after the server has started. This is mostly a security consideration; -if it is impossible to register new clients, then malicious users cannot grant themselves additional -privileges within the system. - -```dart -import 'package:angel3_pub_sub/angel3_pub_sub.dart' as pub_sub; - -main() async { - // ... - server.registerClient(const ClientInfo('')); - - // Create a user who can subscribe, but not publish. - server.registerClient(const ClientInfo('', canPublish: false)); - - // Create a user who can publish, but not subscribe. - server.registerClient(const ClientInfo('', canSubscribe: false)); - - // Create a user with no privileges whatsoever. - server.registerClient(const ClientInfo('', canPublish: false, canSubscribe: false)); - - server.start(); -} -``` - -## Isolates - -If you are just running multiple instances of a server, -use `package:angel3_pub_sub/isolate.dart`. - -You'll need one isolate to be the master. Typically this is the first isolate you create. - -```dart -import 'dart:io'; -import 'dart:isolate'; -import 'package:angel3_pub_sub/isolate.dart' as pub_sub; -import 'package:angel3_pub_sub/angel3_pub_sub.dart' as pub_sub; - -void main() async { - // Easily bring up a server. - var adapter = pub_sub.IsolateAdapter(); - var server = pub_sub.Server([adapter]); - - // You then need to create a client that will connect to the adapter. - // Each isolate in your application should contain a client. - for (int i = 0; i < Platform.numberOfProcessors - 1; i++) { - server.registerClient(pub_sub.ClientInfo('client$i')); - } - - // Start the server. - server.start(); - - // Next, let's start isolates that interact with the server. - // - // Fortunately, we can send SendPorts over Isolates, so this is no hassle. - for (int i = 0; i < Platform.numberOfProcessors - 1; i++) - Isolate.spawn(isolateMain, [i, adapter.receivePort.sendPort]); - - // It's possible that you're running your application in the server isolate as well: - isolateMain([0, adapter.receivePort.sendPort]); -} - -void isolateMain(List args) { - var client = - pub_sub.IsolateClient('client${args[0]}', args[1] as SendPort); - - // The client will connect automatically. In the meantime, we can start subscribing to events. - client.subscribe('user::logged_in').then((sub) { - // The `ClientSubscription` class extends `Stream`. Hooray for asynchrony! - sub.listen((msg) { - print('Logged in: $msg'); - }); - }); -} - -``` - -## JSON RPC 2.0 - -If you are not running on isolates, you need to import -`package:angel3_pub_sub/json_rpc_2.dart`. This library leverages `package:json_rpc_2` and -`package:stream_channel` to create clients and servers that can hypothetically run on any -medium, i.e. WebSockets, or TCP Sockets. - -Check out `test/json_rpc_2_test.dart` for an example of serving `angel3_pub_sub` over TCP sockets. - -# Protocol - -`angel3_pub_sub` is built upon a simple RPC, and this package includes -an implementation that runs via `SendPort`s and `ReceivePort`s, as -well as one that runs on any `StreamChannel`. - -Data sent over the wire looks like the following: - -```typescript -// Sent by a client to initiate an exchange. -interface Request { - // This is an arbitrary string, assigned by your client, but in every case, - // the client uses this to match your requests with asynchronous responses. - request_id: string, - - // The ID of the client to authenticate as. - // - // As you can imagine, this should be kept secret, to prevent breaches. - client_id: string, - - // Required for *every* request. - params: { - // A value to be `publish`ed. - value?: any, - - // The name of an event to `publish`. - event_name?: string, - - // The ID of a subscription to be cancelled. - subscription_id?: string - } -} - -/// Sent by the server in response to a request. -interface Response { - // `true` for success, `false` for failures. - status: boolean, - - // Only appears if `status` is `false`; explains why an operation failed. - error_message?: string, - - // Matches the request_id sent by the client. - request_id: string, - - result?: { - // The number of other clients to whom an event was `publish`ed. - listeners:? number, - - // The ID of a created subscription. - subscription_id?: string - } -} -``` - -When sending via JSON_RPC 2.0, the `params` of a `Request` are simply folded into the object -itself, for simplicity's sake. In this case, a response will be sent as a notification whose -name is the `request_id`. - -In the case of Isolate clients/servers, events will be simply sent as Lists: - -```dart -['', value] -``` - -Clients can send the following (3) methods: - -* `subscribe` (`event_name`:string): Subscribe to an event. -* `unsubscribe` (`subscription_id`:string): Unsubscribe from an event you previously subscribed to. -* `publish` (`event_name`:string, `value`:any): Publish an event to all other clients who are subscribed. - -The client and server in `package:angel3_pub_sub/isolate.dart` must make extra -provisions to keep track of client ID's. Since `SendPort`s and `ReceivePort`s -do not have any sort of guaranteed-unique ID's, new clients must send their -`SendPort` to the server before sending any requests. The server then responds -with an `id` that must be used to identify a `SendPort` to send a response to. diff --git a/packages/pub_sub/analysis_options.yaml b/packages/pub_sub/analysis_options.yaml deleted file mode 100644 index c230cee7..00000000 --- a/packages/pub_sub/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:pedantic/analysis_options.yaml -analyzer: - strong-mode: - implicit-casts: false \ No newline at end of file diff --git a/packages/pub_sub/example/main.dart b/packages/pub_sub/example/main.dart deleted file mode 100644 index 4cefcbda..00000000 --- a/packages/pub_sub/example/main.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'dart:io'; -import 'dart:isolate'; -import 'package:angel3_pub_sub/isolate.dart' as pub_sub; -import 'package:angel3_pub_sub/angel3_pub_sub.dart' as pub_sub; - -void main() async { - // Easily bring up a server. - var adapter = pub_sub.IsolateAdapter(); - var server = pub_sub.Server([adapter]); - - // You then need to create a client that will connect to the adapter. - // Every untrusted client in your application should be pre-registered. - // - // In the case of Isolates, however, those are always implicitly trusted. - for (var i = 0; i < Platform.numberOfProcessors - 1; i++) { - server.registerClient(pub_sub.ClientInfo('client$i')); - } - - // Start the server. - server.start(); - - // Next, let's start isolates that interact with the server. - // - // Fortunately, we can send SendPorts over Isolates, so this is no hassle. - for (var i = 0; i < Platform.numberOfProcessors - 1; i++) { - await Isolate.spawn(isolateMain, [i, adapter.receivePort.sendPort]); - } - - // It's possible that you're running your application in the server isolate as well: - isolateMain([0, adapter.receivePort.sendPort]); -} - -void isolateMain(List args) { - // Isolates are always trusted, so technically we don't need to pass a client iD. - var client = pub_sub.IsolateClient('client${args[0]}', args[1] as SendPort); - - // The client will connect automatically. In the meantime, we can start subscribing to events. - client.subscribe('user::logged_in').then((sub) { - // The `ClientSubscription` class extends `Stream`. Hooray for asynchrony! - sub.listen((msg) { - print('Logged in: $msg'); - }); - }); -} diff --git a/packages/pub_sub/lib/angel3_pub_sub.dart b/packages/pub_sub/lib/angel3_pub_sub.dart deleted file mode 100644 index 84505470..00000000 --- a/packages/pub_sub/lib/angel3_pub_sub.dart +++ /dev/null @@ -1 +0,0 @@ -export 'src/protocol/protocol.dart'; diff --git a/packages/pub_sub/lib/isolate.dart b/packages/pub_sub/lib/isolate.dart deleted file mode 100644 index 0fcf44b3..00000000 --- a/packages/pub_sub/lib/isolate.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'src/isolate/client.dart'; -export 'src/isolate/server.dart'; diff --git a/packages/pub_sub/lib/json_rpc_2.dart b/packages/pub_sub/lib/json_rpc_2.dart deleted file mode 100644 index 41bd3b00..00000000 --- a/packages/pub_sub/lib/json_rpc_2.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'src/json_rpc/client.dart'; -export 'src/json_rpc/server.dart'; diff --git a/packages/pub_sub/lib/src/isolate/client.dart b/packages/pub_sub/lib/src/isolate/client.dart deleted file mode 100644 index 7c80cdd5..00000000 --- a/packages/pub_sub/lib/src/isolate/client.dart +++ /dev/null @@ -1,186 +0,0 @@ -import 'dart:async'; -import 'dart:collection'; -import 'dart:isolate'; -import 'package:uuid/uuid.dart'; -import '../../angel3_pub_sub.dart'; - -/// A [Client] implementation that communicates via [SendPort]s and [ReceivePort]s. -class IsolateClient extends Client { - final Queue> _onConnect = Queue>(); - final Map> _requests = {}; - final List<_IsolateClientSubscription> _subscriptions = []; - final Uuid _uuid = Uuid(); - - String? _id; - - /// The ID of the client we are authenticating as. - /// - /// May be `null`, if and only if we are marked as a trusted source on - /// the server side. - String? get clientId => _clientId; - String? _clientId; - - /// A server's [SendPort] that messages should be sent to. - final SendPort serverSendPort; - - /// A [ReceivePort] that receives messages from the server. - final ReceivePort receivePort = ReceivePort(); - - IsolateClient(String? clientId, this.serverSendPort) { - _clientId = clientId; - receivePort.listen((data) { - if (data is Map && data['request_id'] is String) { - var requestId = data['request_id'] as String?; - var c = _requests.remove(requestId); - - if (c != null && !c.isCompleted) { - if (data['status'] is! bool) { - c.completeError( - FormatException('The server sent an invalid response.')); - } else if (!(data['status'] as bool)) { - c.completeError(PubSubException(data['error_message']?.toString() ?? - 'The server sent a failure response, but did not provide an error message.')); - } else if (data['result'] is! Map) { - c.completeError(FormatException( - 'The server sent a success response, but did not include a result.')); - } else { - c.complete(data['result'] as Map?); - } - } - } else if (data is Map && data['id'] is String && _id == null) { - _id = data['id'] as String?; - - for (var c in _onConnect) { - if (!c.isCompleted) c.complete(_id); - } - - _onConnect.clear(); - } else if (data is List && data.length == 2 && data[0] is String) { - var eventName = data[0] as String; - var event = data[1]; - for (var s in _subscriptions.where((s) => s.eventName == eventName)) { - if (!s._stream.isClosed) s._stream.add(event); - } - } - }); - serverSendPort.send(receivePort.sendPort); - } - - Future _whenConnected(FutureOr Function() callback) { - if (_id != null) { - return Future.sync(callback); - } else { - var c = Completer(); - _onConnect.add(c); - return c.future.then((_) => callback()); - } - } - - @override - Future publish(String eventName, value) { - return _whenConnected(() { - var c = Completer(); - var requestId = _uuid.v4(); - _requests[requestId] = c; - serverSendPort.send({ - 'id': _id, - 'request_id': requestId, - 'method': 'publish', - 'params': { - 'client_id': clientId, - 'event_name': eventName, - 'value': value - } - }); - return c.future.then((result) { - _clientId = result['client_id'] as String?; - }); - }); - } - - @override - Future subscribe(String eventName) { - return _whenConnected(() { - var c = Completer(); - var requestId = _uuid.v4(); - _requests[requestId] = c; - serverSendPort.send({ - 'id': _id, - 'request_id': requestId, - 'method': 'subscribe', - 'params': {'client_id': clientId, 'event_name': eventName} - }); - return c.future.then((result) { - _clientId = result['client_id'] as String?; - var s = _IsolateClientSubscription( - eventName, result['subscription_id'] as String?, this); - _subscriptions.add(s); - return s; - }); - }); - } - - @override - Future close() { - receivePort.close(); - - for (var c in _onConnect) { - if (!c.isCompleted) { - c.completeError(StateError( - 'The client was closed before the server ever accepted the connection.')); - } - } - - for (var c in _requests.values) { - if (!c.isCompleted) { - c.completeError(StateError( - 'The client was closed before the server responded to this request.')); - } - } - - for (var s in _subscriptions) { - s._close(); - } - - _requests.clear(); - return Future.value(); - } -} - -class _IsolateClientSubscription extends ClientSubscription { - final StreamController _stream = StreamController(); - final String? eventName, id; - final IsolateClient client; - - _IsolateClientSubscription(this.eventName, this.id, this.client); - - void _close() { - if (!_stream.isClosed) _stream.close(); - } - - @override - StreamSubscription listen(void Function(dynamic event)? onData, - {Function? onError, void Function()? onDone, bool? cancelOnError}) { - return _stream.stream.listen(onData, - onError: onError, onDone: onDone, cancelOnError: cancelOnError); - } - - @override - Future unsubscribe() { - return client._whenConnected(() { - var c = Completer(); - var requestId = client._uuid.v4(); - client._requests[requestId] = c; - client.serverSendPort.send({ - 'id': client._id, - 'request_id': requestId, - 'method': 'unsubscribe', - 'params': {'client_id': client.clientId, 'subscription_id': id} - }); - - return c.future.then((_) { - _close(); - }); - }); - } -} diff --git a/packages/pub_sub/lib/src/isolate/server.dart b/packages/pub_sub/lib/src/isolate/server.dart deleted file mode 100644 index 3d38af6d..00000000 --- a/packages/pub_sub/lib/src/isolate/server.dart +++ /dev/null @@ -1,253 +0,0 @@ -import 'dart:async'; -import 'dart:isolate'; -import 'package:uuid/uuid.dart'; -import '../../angel3_pub_sub.dart'; - -/// A [Adapter] implementation that communicates via [SendPort]s and [ReceivePort]s. -class IsolateAdapter extends Adapter { - final Map _clients = {}; - final StreamController _onPublish = - StreamController(); - final StreamController _onSubscribe = - StreamController(); - final StreamController _onUnsubscribe = - StreamController(); - final Uuid _uuid = Uuid(); - - /// A [ReceivePort] on which to listen for incoming data. - final ReceivePort receivePort = ReceivePort(); - - @override - Stream get onPublish => _onPublish.stream; - - @override - Stream get onSubscribe => _onSubscribe.stream; - - @override - Stream get onUnsubscribe => _onUnsubscribe.stream; - - @override - Future close() { - receivePort.close(); - _clients.clear(); - _onPublish.close(); - _onSubscribe.close(); - _onUnsubscribe.close(); - return Future.value(); - } - - @override - void start() { - receivePort.listen((data) { - if (data is SendPort) { - var id = _uuid.v4(); - _clients[id] = data; - data.send({'status': true, 'id': id}); - } else if (data is Map && - data['id'] is String && - data['request_id'] is String && - data['method'] is String && - data['params'] is Map) { - var id = data['id'] as String?, - requestId = data['request_id'] as String?, - method = data['method'] as String?; - var params = data['params'] as Map?; - var sp = _clients[id!]; - - if (sp == null) { - // There's nobody to respond to, so don't send anything to anyone. Oops. - } else if (method == 'publish') { - if (_isValidClientId(params!['client_id']) && - params['event_name'] is String && - params.containsKey('value')) { - var clientId = params['client_id'] as String?, - eventName = params['event_name'] as String?; - var value = params['value']; - var rq = _IsolatePublishRequestImpl( - requestId, clientId, eventName, value, sp); - _onPublish.add(rq); - } else { - sp.send({ - 'status': false, - 'request_id': requestId, - 'error_message': 'Expected client_id, event_name, and value.' - }); - } - } else if (method == 'subscribe') { - if (_isValidClientId(params!['client_id']) && - params['event_name'] is String) { - var clientId = params['client_id'] as String?, - eventName = params['event_name'] as String?; - var rq = _IsolateSubscriptionRequestImpl( - clientId, eventName, sp, requestId, _uuid); - _onSubscribe.add(rq); - } else { - sp.send({ - 'status': false, - 'request_id': requestId, - 'error_message': 'Expected client_id, and event_name.' - }); - } - } else if (method == 'unsubscribe') { - if (_isValidClientId(params!['client_id']) && - params['subscription_id'] is String) { - var clientId = params['client_id'] as String?, - subscriptionId = params['subscription_id'] as String?; - var rq = _IsolateUnsubscriptionRequestImpl( - clientId, subscriptionId, sp, requestId); - _onUnsubscribe.add(rq); - } else { - sp.send({ - 'status': false, - 'request_id': requestId, - 'error_message': 'Expected client_id, and subscription_id.' - }); - } - } else { - sp.send({ - 'status': false, - 'request_id': requestId, - 'error_message': - 'Unrecognized method "$method". Or, you omitted id, request_id, method, or params.' - }); - } - } - }); - } - - bool _isValidClientId(id) => id == null || id is String; - - @override - bool isTrustedPublishRequest(PublishRequest request) { - // Isolate clients are considered trusted, because they are - // running in the same process as the central server. - return true; - } - - @override - bool isTrustedSubscriptionRequest(SubscriptionRequest request) { - return true; - } -} - -class _IsolatePublishRequestImpl extends PublishRequest { - @override - final String? clientId; - - @override - final String? eventName; - - @override - final value; - - final SendPort sendPort; - - final String? requestId; - - _IsolatePublishRequestImpl( - this.requestId, this.clientId, this.eventName, this.value, this.sendPort); - - @override - void accept(PublishResponse response) { - sendPort.send({ - 'status': true, - 'request_id': requestId, - 'result': { - 'listeners': response.listeners, - 'client_id': response.clientId - } - }); - } - - @override - void reject(String errorMessage) { - sendPort.send({ - 'status': false, - 'request_id': requestId, - 'error_message': errorMessage - }); - } -} - -class _IsolateSubscriptionRequestImpl extends SubscriptionRequest { - @override - final String? clientId; - - @override - final String? eventName; - - final SendPort sendPort; - - final String? requestId; - - final Uuid _uuid; - - _IsolateSubscriptionRequestImpl( - this.clientId, this.eventName, this.sendPort, this.requestId, this._uuid); - - @override - void reject(String errorMessage) { - sendPort.send({ - 'status': false, - 'request_id': requestId, - 'error_message': errorMessage - }); - } - - @override - FutureOr accept(String? clientId) { - var id = _uuid.v4(); - sendPort.send({ - 'status': true, - 'request_id': requestId, - 'result': {'subscription_id': id, 'client_id': clientId} - }); - return _IsolateSubscriptionImpl(clientId, id, eventName, sendPort); - } -} - -class _IsolateSubscriptionImpl extends Subscription { - @override - final String? clientId, id; - - final String? eventName; - - final SendPort sendPort; - - _IsolateSubscriptionImpl( - this.clientId, this.id, this.eventName, this.sendPort); - - @override - void dispatch(event) { - sendPort.send([eventName, event]); - } -} - -class _IsolateUnsubscriptionRequestImpl extends UnsubscriptionRequest { - @override - final String? clientId; - - @override - final String? subscriptionId; - - final SendPort sendPort; - - final String? requestId; - - _IsolateUnsubscriptionRequestImpl( - this.clientId, this.subscriptionId, this.sendPort, this.requestId); - - @override - void reject(String errorMessage) { - sendPort.send({ - 'status': false, - 'request_id': requestId, - 'error_message': errorMessage - }); - } - - @override - void accept() { - sendPort.send({'status': true, 'request_id': requestId, 'result': {}}); - } -} diff --git a/packages/pub_sub/lib/src/json_rpc/client.dart b/packages/pub_sub/lib/src/json_rpc/client.dart deleted file mode 100644 index 1c654bf5..00000000 --- a/packages/pub_sub/lib/src/json_rpc/client.dart +++ /dev/null @@ -1,145 +0,0 @@ -import 'dart:async'; -import 'package:stream_channel/stream_channel.dart'; -import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc_2; -import 'package:uuid/uuid.dart'; -import '../../angel3_pub_sub.dart'; - -/// A [Client] implementation that communicates via JSON RPC 2.0. -class JsonRpc2Client extends Client { - final Map> _requests = {}; - final List<_JsonRpc2ClientSubscription> _subscriptions = []; - final Uuid _uuid = Uuid(); - - json_rpc_2.Peer? _peer; - - /// The ID of the client we are authenticating as. - /// - /// May be `null`, if and only if we are marked as a trusted source on - /// the server side. - String? get clientId => _clientId; - String? _clientId; - - JsonRpc2Client(String? clientId, StreamChannel channel) { - _clientId = clientId; - _peer = json_rpc_2.Peer(channel); - - _peer!.registerMethod('event', (json_rpc_2.Parameters params) { - String? eventName = params['event_name'].asString; - var event = params['value'].value; - for (var s in _subscriptions.where((s) => s.eventName == eventName)) { - if (!s._stream.isClosed) s._stream.add(event); - } - }); - - _peer!.registerFallback((json_rpc_2.Parameters params) { - var c = _requests.remove(params.method); - - if (c == null) { - throw json_rpc_2.RpcException.methodNotFound(params.method); - } else { - var data = params.asMap; - - if (data['status'] is! bool) { - c.completeError( - FormatException('The server sent an invalid response.')); - } else if (!(data['status'] as bool)) { - c.completeError(PubSubException(data['error_message']?.toString() ?? - 'The server sent a failure response, but did not provide an error message.')); - } else { - c.complete(data); - } - } - }); - - _peer!.listen(); - } - - @override - Future publish(String eventName, value) { - var c = Completer(); - var requestId = _uuid.v4(); - _requests[requestId] = c; - _peer!.sendNotification('publish', { - 'request_id': requestId, - 'client_id': clientId, - 'event_name': eventName, - 'value': value - }); - return c.future.then((data) { - _clientId = data['result']['client_id'] as String?; - }); - } - - @override - Future subscribe(String eventName) { - var c = Completer(); - var requestId = _uuid.v4(); - _requests[requestId] = c; - _peer!.sendNotification('subscribe', { - 'request_id': requestId, - 'client_id': clientId, - 'event_name': eventName - }); - return c.future.then((result) { - _clientId = result['client_id'] as String?; - var s = _JsonRpc2ClientSubscription( - eventName, result['subscription_id'] as String?, this); - _subscriptions.add(s); - return s; - }); - } - - @override - Future close() { - if (_peer?.isClosed != true) _peer!.close(); - - for (var c in _requests.values) { - if (!c.isCompleted) { - c.completeError(StateError( - 'The client was closed before the server responded to this request.')); - } - } - - for (var s in _subscriptions) { - s._close(); - } - - _requests.clear(); - return Future.value(); - } -} - -class _JsonRpc2ClientSubscription extends ClientSubscription { - final StreamController _stream = StreamController(); - final String? eventName, id; - final JsonRpc2Client client; - - _JsonRpc2ClientSubscription(this.eventName, this.id, this.client); - - void _close() { - if (!_stream.isClosed) _stream.close(); - } - - @override - StreamSubscription listen(void Function(dynamic event)? onData, - {Function? onError, void Function()? onDone, bool? cancelOnError}) { - return _stream.stream.listen(onData, - onError: onError, onDone: onDone, cancelOnError: cancelOnError); - } - - @override - Future unsubscribe() { - var c = Completer(); - var requestId = client._uuid.v4(); - client._requests[requestId] = c; - client._peer!.sendNotification('unsubscribe', { - 'request_id': requestId, - 'client_id': client.clientId, - 'subscription_id': id - }); - - return c.future.then((_) { - _close(); - }); - } -} diff --git a/packages/pub_sub/lib/src/json_rpc/server.dart b/packages/pub_sub/lib/src/json_rpc/server.dart deleted file mode 100644 index f038d72b..00000000 --- a/packages/pub_sub/lib/src/json_rpc/server.dart +++ /dev/null @@ -1,220 +0,0 @@ -import 'dart:async'; -import 'package:stream_channel/stream_channel.dart'; -import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc_2; -import 'package:uuid/uuid.dart'; -import '../../angel3_pub_sub.dart'; - -/// A [Adapter] implementation that communicates via JSON RPC 2.0. -class JsonRpc2Adapter extends Adapter { - final StreamController _onPublish = - StreamController(); - final StreamController _onSubscribe = - StreamController(); - final StreamController _onUnsubscribe = - StreamController(); - - final List _peers = []; - final Uuid _uuid = Uuid(); - - json_rpc_2.Peer? _peer; - - /// A [Stream] of incoming clients, who can both send and receive string data. - final Stream> clientStream; - - /// If `true`, clients can connect through this endpoint, *without* providing a client ID. - /// - /// This can be a security vulnerability if you don't know what you're doing. - /// If you *must* use this over the Internet, use an IP whitelist. - final bool isTrusted; - - JsonRpc2Adapter(this.clientStream, {this.isTrusted = false}); - - @override - Stream get onPublish => _onPublish.stream; - - @override - Stream get onSubscribe => _onSubscribe.stream; - - @override - Stream get onUnsubscribe => _onUnsubscribe.stream; - - @override - Future close() { - if (_peer?.isClosed != true) _peer?.close(); - - Future.wait(_peers.where((s) => !s.isClosed).map((s) => s.close())) - .then((_) => _peers.clear()); - return Future.value(); - } - - String? _getClientId(json_rpc_2.Parameters params) { - try { - return params['client_id'].asString; - } catch (_) { - return null; - } - } - - @override - void start() { - clientStream.listen((client) { - var peer = _peer = json_rpc_2.Peer(client); - - peer.registerMethod('publish', (json_rpc_2.Parameters params) async { - var requestId = params['request_id'].asString; - var clientId = _getClientId(params); - var eventName = params['event_name'].asString; - var value = params['value'].value; - var rq = _JsonRpc2PublishRequestImpl( - requestId, clientId, eventName, value, peer); - _onPublish.add(rq); - }); - - peer.registerMethod('subscribe', (json_rpc_2.Parameters params) async { - var requestId = params['request_id'].asString; - var clientId = _getClientId(params); - var eventName = params['event_name'].asString; - var rq = _JsonRpc2SubscriptionRequestImpl( - clientId, eventName, requestId, peer, _uuid); - _onSubscribe.add(rq); - }); - - peer.registerMethod('unsubscribe', (json_rpc_2.Parameters params) async { - var requestId = params['request_id'].asString; - var clientId = _getClientId(params); - var subscriptionId = params['subscription_id'].asString; - var rq = _JsonRpc2UnsubscriptionRequestImpl( - clientId, subscriptionId, peer, requestId); - _onUnsubscribe.add(rq); - }); - - peer.listen(); - }); - } - - @override - bool isTrustedPublishRequest(PublishRequest request) { - return isTrusted; - } - - @override - bool isTrustedSubscriptionRequest(SubscriptionRequest request) { - return isTrusted; - } -} - -class _JsonRpc2PublishRequestImpl extends PublishRequest { - final String requestId; - - @override - final String? clientId, eventName; - - @override - final value; - - final json_rpc_2.Peer peer; - - _JsonRpc2PublishRequestImpl( - this.requestId, this.clientId, this.eventName, this.value, this.peer); - - @override - void accept(PublishResponse response) { - peer.sendNotification(requestId, { - 'status': true, - 'request_id': requestId, - 'result': { - 'listeners': response.listeners, - 'client_id': response.clientId - } - }); - } - - @override - void reject(String errorMessage) { - peer.sendNotification(requestId, { - 'status': false, - 'request_id': requestId, - 'error_message': errorMessage - }); - } -} - -class _JsonRpc2SubscriptionRequestImpl extends SubscriptionRequest { - @override - final String? clientId, eventName; - - final String requestId; - - final json_rpc_2.Peer peer; - - final Uuid _uuid; - - _JsonRpc2SubscriptionRequestImpl( - this.clientId, this.eventName, this.requestId, this.peer, this._uuid); - - @override - FutureOr accept(String? clientId) { - var id = _uuid.v4(); - peer.sendNotification(requestId, { - 'status': true, - 'request_id': requestId, - 'subscription_id': id, - 'client_id': clientId - }); - return _JsonRpc2SubscriptionImpl(clientId, id, eventName, peer); - } - - @override - void reject(String errorMessage) { - peer.sendNotification(requestId, { - 'status': false, - 'request_id': requestId, - 'error_message': errorMessage - }); - } -} - -class _JsonRpc2SubscriptionImpl extends Subscription { - @override - final String? clientId, id; - - final String? eventName; - - final json_rpc_2.Peer peer; - - _JsonRpc2SubscriptionImpl(this.clientId, this.id, this.eventName, this.peer); - - @override - void dispatch(event) { - peer.sendNotification('event', {'event_name': eventName, 'value': event}); - } -} - -class _JsonRpc2UnsubscriptionRequestImpl extends UnsubscriptionRequest { - @override - final String? clientId; - - @override - final String subscriptionId; - - final json_rpc_2.Peer peer; - - final String requestId; - - _JsonRpc2UnsubscriptionRequestImpl( - this.clientId, this.subscriptionId, this.peer, this.requestId); - - @override - void accept() { - peer.sendNotification(requestId, {'status': true, 'result': {}}); - } - - @override - void reject(String errorMessage) { - peer.sendNotification(requestId, { - 'status': false, - 'request_id': requestId, - 'error_message': errorMessage - }); - } -} diff --git a/packages/pub_sub/lib/src/protocol/client/client.dart b/packages/pub_sub/lib/src/protocol/client/client.dart deleted file mode 100644 index 4a327160..00000000 --- a/packages/pub_sub/lib/src/protocol/client/client.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'dart:async'; - -/// Queries a `pub_sub` server. -abstract class Client { - /// Publishes an event to the server. - Future publish(String eventName, value); - - /// Request a [ClientSubscription] to the desired [eventName] from the server. - Future subscribe(String eventName); - - /// Disposes of this client. - Future close(); -} - -/// A client-side implementation of a subscription, which acts as a [Stream], and can be cancelled easily. -abstract class ClientSubscription extends Stream { - /// Stops listening for new events, and instructs the server to cancel the subscription. - Future unsubscribe(); -} - -/// Thrown as the result of an invalid request, or an attempt to perform an action without the correct privileges. -class PubSubException implements Exception { - /// The error message sent by the server. - final String message; - - const PubSubException(this.message); - - @override - String toString() => '`pub_sub` exception: $message'; -} diff --git a/packages/pub_sub/lib/src/protocol/client/sync_client.dart b/packages/pub_sub/lib/src/protocol/client/sync_client.dart deleted file mode 100644 index 93a32575..00000000 --- a/packages/pub_sub/lib/src/protocol/client/sync_client.dart +++ /dev/null @@ -1 +0,0 @@ -export 'client.dart'; diff --git a/packages/pub_sub/lib/src/protocol/protocol.dart b/packages/pub_sub/lib/src/protocol/protocol.dart deleted file mode 100644 index 9bf74c6a..00000000 --- a/packages/pub_sub/lib/src/protocol/protocol.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'client/sync_client.dart'; -export 'server/sync_server.dart'; diff --git a/packages/pub_sub/lib/src/protocol/server/adapter.dart b/packages/pub_sub/lib/src/protocol/server/adapter.dart deleted file mode 100644 index e129b4af..00000000 --- a/packages/pub_sub/lib/src/protocol/server/adapter.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'dart:async'; -import 'publish.dart'; -import 'subscription.dart'; - -/// Adapts an abstract medium to serve the `pub_sub` RPC protocol. -abstract class Adapter { - /// Determines if a given [request] comes from a trusted source. - /// - /// If so, the request does not have to provide a pre-established ID, - /// and instead will be assigned one. - bool isTrustedPublishRequest(PublishRequest request); - - bool isTrustedSubscriptionRequest(SubscriptionRequest request); - - /// Fires an event whenever a client tries to publish data. - Stream get onPublish; - - /// Fires whenever a client tries to subscribe to an event. - Stream get onSubscribe; - - /// Fires whenever a client cancels a subscription. - Stream get onUnsubscribe; - - /// Disposes of this adapter. - Future close(); - - /// Start listening for incoming clients. - void start(); -} diff --git a/packages/pub_sub/lib/src/protocol/server/client.dart b/packages/pub_sub/lib/src/protocol/server/client.dart deleted file mode 100644 index 976e3fc4..00000000 --- a/packages/pub_sub/lib/src/protocol/server/client.dart +++ /dev/null @@ -1,14 +0,0 @@ -/// Represents information about a client that will be accessing -/// this `angel_sync` server. -class ClientInfo { - /// A unique identifier for this client. - final String id; - - /// If `true` (default), then the client is allowed to publish events. - final bool canPublish; - - /// If `true` (default), then the client can subscribe to events. - final bool canSubscribe; - - const ClientInfo(this.id, {this.canPublish = true, this.canSubscribe = true}); -} diff --git a/packages/pub_sub/lib/src/protocol/server/publish.dart b/packages/pub_sub/lib/src/protocol/server/publish.dart deleted file mode 100644 index b6cbf95b..00000000 --- a/packages/pub_sub/lib/src/protocol/server/publish.dart +++ /dev/null @@ -1,28 +0,0 @@ -/// Represents a request to publish information to other clients. -abstract class PublishRequest { - /// The ID of the client sending this request. - String? get clientId; - - /// The name of the event to be sent. - String? get eventName; - - /// The value to be published as an event. - dynamic get value; - - /// Accept the request, with a response. - void accept(PublishResponse response); - - /// Deny the request with an error message. - void reject(String errorMessage); -} - -/// A response to a publish request. Informs the caller of how much clients received the event. -class PublishResponse { - /// The number of unique listeners to whom this event was propogated. - final int listeners; - - /// The client ID returned the server. Significant in cases where an ad-hoc client was registered. - final String? clientId; - - const PublishResponse(this.listeners, this.clientId); -} diff --git a/packages/pub_sub/lib/src/protocol/server/server.dart b/packages/pub_sub/lib/src/protocol/server/server.dart deleted file mode 100644 index b0cc08d7..00000000 --- a/packages/pub_sub/lib/src/protocol/server/server.dart +++ /dev/null @@ -1,160 +0,0 @@ -import 'dart:async'; -import 'dart:math'; -import 'adapter.dart'; -import 'client.dart'; -import 'package:collection/collection.dart' show IterableExtension; -import 'publish.dart'; -import 'subscription.dart'; - -/// A server that implements the `pub_sub` protocol. -/// -/// It can work using multiple [Adapter]s, to simultaneously -/// serve local and remote clients alike. -class Server { - final List _adapters = []; - final List _clients = []; - final _rnd = Random.secure(); - final Map> _subscriptions = {}; - bool _started = false; - int _adHocIds = 0; - - /// Initialize a server, optionally with a number of [adapters]. - Server([Iterable adapters = const []]) { - _adapters.addAll(adapters); - } - - /// Adds a new [Adapter] to adapt incoming clients from a new interface. - void addAdapter(Adapter adapter) { - if (_started) { - throw StateError( - 'You cannot add new adapters after the server has started listening.'); - } else { - _adapters.add(adapter); - } - } - - /// Registers a new client with the server. - void registerClient(ClientInfo client) { - if (_started) { - throw StateError( - 'You cannot register new clients after the server has started listening.'); - } else { - _clients.add(client); - } - } - - /// Disposes of this server, and closes all of its adapters. - Future close() { - Future.wait(_adapters.map((a) => a.close())); - _adapters.clear(); - _clients.clear(); - _subscriptions.clear(); - return Future.value(); - } - - String _newClientId() { - // Create an unpredictable-enough ID. The harder it is for an attacker to guess, the better. - var id = - 'pub_sub::adhoc_client${_rnd.nextDouble()}::${_adHocIds++}:${DateTime.now().millisecondsSinceEpoch * _rnd.nextDouble()}'; - - // This client is coming from a trusted source, and can therefore both publish and subscribe. - _clients.add(ClientInfo(id)); - return id; - } - - void start() { - if (_adapters.isEmpty) { - throw StateError( - 'Cannot start a SyncServer that has no adapters attached.'); - } else if (_started) { - throw StateError('A SyncServer may only be started once.'); - } - - _started = true; - - for (var adapter in _adapters) { - adapter.start(); - } - - for (var adapter in _adapters) { - // Handle publishes - adapter.onPublish.listen((rq) { - ClientInfo? client; - String? clientId; - - if (rq.clientId?.isNotEmpty == true || - adapter.isTrustedPublishRequest(rq)) { - clientId = - rq.clientId?.isNotEmpty == true ? rq.clientId : _newClientId(); - client = _clients.firstWhereOrNull((c) => c.id == clientId); - } - - if (client == null) { - rq.reject('Unrecognized client ID "${clientId ?? ''}".'); - } else if (!client.canPublish) { - rq.reject('You are not allowed to publish events.'); - } else { - var listeners = _subscriptions[rq.eventName] - ?.where((s) => s.clientId != clientId) ?? - []; - - if (listeners.isEmpty) { - rq.accept(PublishResponse(0, clientId)); - } else { - for (var listener in listeners) { - listener.dispatch(rq.value); - } - - rq.accept(PublishResponse(listeners.length, clientId)); - } - } - }); - - // Listen for incoming subscriptions - adapter.onSubscribe.listen((rq) async { - ClientInfo? client; - String? clientId; - - if (rq.clientId?.isNotEmpty == true || - adapter.isTrustedSubscriptionRequest(rq)) { - clientId = - rq.clientId?.isNotEmpty == true ? rq.clientId : _newClientId(); - client = _clients.firstWhereOrNull((c) => c.id == clientId); - } - - if (client == null) { - rq.reject('Unrecognized client ID "${clientId ?? ''}".'); - } else if (!client.canSubscribe) { - rq.reject('You are not allowed to subscribe to events.'); - } else { - var sub = await rq.accept(clientId); - var list = _subscriptions.putIfAbsent(rq.eventName, () => []); - list.add(sub); - } - }); - - // Unregister subscriptions on unsubscribe - adapter.onUnsubscribe.listen((rq) { - Subscription? toRemove; - late List sourceList; - - for (var list in _subscriptions.values) { - toRemove = list.firstWhereOrNull((s) => s.id == rq.subscriptionId); - if (toRemove != null) { - sourceList = list; - break; - } - } - - if (toRemove == null) { - rq.reject('The specified subscription does not exist.'); - } else if (toRemove.clientId != rq.clientId) { - rq.reject('That is not your subscription to cancel.'); - } else { - sourceList.remove(toRemove); - rq.accept(); - } - }); - } - } -} diff --git a/packages/pub_sub/lib/src/protocol/server/subscription.dart b/packages/pub_sub/lib/src/protocol/server/subscription.dart deleted file mode 100644 index 9f5db231..00000000 --- a/packages/pub_sub/lib/src/protocol/server/subscription.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'dart:async'; - -/// Represents a request to subscribe to an event. -abstract class SubscriptionRequest { - /// The ID of the client requesting to subscribe. - String? get clientId; - - /// The name of the event the client wants to subscribe to. - String? get eventName; - - /// Accept the request, and grant the client access to subscribe to the event. - /// - /// Includes the client's ID, which is necessary for ad-hoc clients. - FutureOr accept(String? clientId); - - /// Deny the request with an error message. - void reject(String errorMessage); -} - -/// Represents a request to unsubscribe to an event. -abstract class UnsubscriptionRequest { - /// The ID of the client requesting to unsubscribe. - String? get clientId; - - /// The name of the event the client wants to unsubscribe from. - String? get subscriptionId; - - /// Accept the request. - FutureOr accept(); - - /// Deny the request with an error message. - void reject(String errorMessage); -} - -/// Represents a client's subscription to an event. -/// -/// Also provides a means to fire an event. -abstract class Subscription { - /// A unique identifier for this subscription. - String? get id; - - /// The ID of the client who requested this subscription. - String? get clientId; - - /// Alerts a client of an event. - void dispatch(event); -} diff --git a/packages/pub_sub/lib/src/protocol/server/sync_server.dart b/packages/pub_sub/lib/src/protocol/server/sync_server.dart deleted file mode 100644 index 5777b272..00000000 --- a/packages/pub_sub/lib/src/protocol/server/sync_server.dart +++ /dev/null @@ -1,5 +0,0 @@ -export 'adapter.dart'; -export 'client.dart'; -export 'publish.dart'; -export 'server.dart'; -export 'subscription.dart'; diff --git a/packages/pub_sub/pubspec.yaml b/packages/pub_sub/pubspec.yaml deleted file mode 100644 index b135f12c..00000000 --- a/packages/pub_sub/pubspec.yaml +++ /dev/null @@ -1,14 +0,0 @@ -name: angel3_pub_sub -version: 3.0.3 -description: Keep application instances in sync with a simple pub/sub API. -homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/pub_sub -environment: - sdk: '>=2.12.0 <3.0.0' -dependencies: - json_rpc_2: ^3.0.0 - stream_channel: ^2.1.0 - uuid: ^3.0.4 - collection: ^1.15.0 -dev_dependencies: - pedantic: ^1.11.0 - test: ^1.17.4 diff --git a/packages/pub_sub/test/isolate_test.dart b/packages/pub_sub/test/isolate_test.dart deleted file mode 100644 index ecb31ec4..00000000 --- a/packages/pub_sub/test/isolate_test.dart +++ /dev/null @@ -1,122 +0,0 @@ -import 'dart:async'; -import 'package:angel3_pub_sub/angel3_pub_sub.dart'; -import 'package:angel3_pub_sub/isolate.dart'; -import 'package:test/test.dart'; - -void main() { - late Server server; - late Client client1, client2, client3; - late IsolateClient trustedClient; - late IsolateAdapter adapter; - - setUp(() async { - adapter = IsolateAdapter(); - client1 = - IsolateClient('isolate_test::secret', adapter.receivePort.sendPort); - client2 = - IsolateClient('isolate_test::secret2', adapter.receivePort.sendPort); - client3 = - IsolateClient('isolate_test::secret3', adapter.receivePort.sendPort); - trustedClient = IsolateClient(null, adapter.receivePort.sendPort); - - server = Server([adapter]) - ..registerClient(const ClientInfo('isolate_test::secret')) - ..registerClient(const ClientInfo('isolate_test::secret2')) - ..registerClient(const ClientInfo('isolate_test::secret3')) - ..registerClient( - const ClientInfo('isolate_test::no_publish', canPublish: false)) - ..registerClient( - const ClientInfo('isolate_test::no_subscribe', canSubscribe: false)) - ..start(); - - var sub = await client3.subscribe('foo'); - sub.listen((data) { - print('Client3 caught foo: $data'); - }); - }); - - tearDown(() { - Future.wait([ - server.close(), - client1.close(), - client2.close(), - client3.close(), - trustedClient.close() - ]); - }); - - group('trusted', () { - test('can publish', () async { - await trustedClient.publish('hey', 'bye'); - expect(trustedClient.clientId, isNotNull); - }); - test('can sub/unsub', () async { - String? clientId; - await trustedClient.publish('heyaaa', 'byeaa'); - expect(clientId = trustedClient.clientId, isNotNull); - - var sub = await trustedClient.subscribe('yeppp'); - expect(trustedClient.clientId, clientId); - - await sub.unsubscribe(); - expect(trustedClient.clientId, clientId); - }); - }); - - test('subscribers receive published events', () async { - var sub = await client2.subscribe('foo'); - await client1.publish('foo', 'bar'); - expect(await sub.first, 'bar'); - }); - - test('subscribers are not sent their own events', () async { - var sub = await client1.subscribe('foo'); - await client1.publish('foo', - ''); - await sub.unsubscribe(); - expect(await sub.isEmpty, isTrue); - }); - - test('can unsubscribe', () async { - var sub = await client2.subscribe('foo'); - await client1.publish('foo', 'bar'); - await sub.unsubscribe(); - await client1.publish('foo', ''); - expect(await sub.length, 1); - }); - - group('isolate_server', () { - test('reject unknown client id', () async { - try { - var client = IsolateClient( - 'isolate_test::invalid', adapter.receivePort.sendPort); - await client.publish('foo', 'bar'); - throw 'Invalid client ID\'s should throw an error, but they do not.'; - } on PubSubException catch (e) { - print('Expected exception was thrown: ${e.message}'); - } - }); - - test('reject unprivileged publish', () async { - try { - var client = IsolateClient( - 'isolate_test::no_publish', adapter.receivePort.sendPort); - await client.publish('foo', 'bar'); - throw 'Unprivileged publishes should throw an error, but they do not.'; - } on PubSubException catch (e) { - print('Expected exception was thrown: ${e.message}'); - } - }); - - test('reject unprivileged subscribe', () async { - try { - var client = IsolateClient( - 'isolate_test::no_subscribe', adapter.receivePort.sendPort); - await client.subscribe('foo'); - throw 'Unprivileged subscribes should throw an error, but they do not.'; - } on PubSubException catch (e) { - print('Expected exception was thrown: ${e.message}'); - } - }); - }); -} diff --git a/packages/pub_sub/test/json_rpc_2_test.dart b/packages/pub_sub/test/json_rpc_2_test.dart deleted file mode 100644 index ee6a33b2..00000000 --- a/packages/pub_sub/test/json_rpc_2_test.dart +++ /dev/null @@ -1,187 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; -import 'package:angel3_pub_sub/angel3_pub_sub.dart'; -import 'package:angel3_pub_sub/json_rpc_2.dart'; -import 'package:stream_channel/stream_channel.dart'; -import 'package:test/test.dart'; - -void main() { - late ServerSocket serverSocket; - late Server server; - late Client client1, client2, client3; - late JsonRpc2Client trustedClient; - JsonRpc2Adapter adapter; - - setUp(() async { - serverSocket = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0); - - adapter = JsonRpc2Adapter( - serverSocket.map>(streamSocket), - isTrusted: true); - - var socket1 = - await Socket.connect(InternetAddress.loopbackIPv4, serverSocket.port); - var socket2 = - await Socket.connect(InternetAddress.loopbackIPv4, serverSocket.port); - var socket3 = - await Socket.connect(InternetAddress.loopbackIPv4, serverSocket.port); - var socket4 = - await Socket.connect(InternetAddress.loopbackIPv4, serverSocket.port); - - client1 = JsonRpc2Client('json_rpc_2_test::secret', streamSocket(socket1)); - client2 = JsonRpc2Client('json_rpc_2_test::secret2', streamSocket(socket2)); - client3 = JsonRpc2Client('json_rpc_2_test::secret3', streamSocket(socket3)); - trustedClient = JsonRpc2Client(null, streamSocket(socket4)); - - server = Server([adapter]) - ..registerClient(const ClientInfo('json_rpc_2_test::secret')) - ..registerClient(const ClientInfo('json_rpc_2_test::secret2')) - ..registerClient(const ClientInfo('json_rpc_2_test::secret3')) - ..registerClient( - const ClientInfo('json_rpc_2_test::no_publish', canPublish: false)) - ..registerClient(const ClientInfo('json_rpc_2_test::no_subscribe', - canSubscribe: false)) - ..start(); - - var sub = await client3.subscribe('foo'); - sub.listen((data) { - print('Client3 caught foo: $data'); - }); - }); - - tearDown(() { - Future.wait( - [server.close(), client1.close(), client2.close(), client3.close()]); - }); - - group('trusted', () { - test('can publish', () async { - await trustedClient.publish('hey', 'bye'); - expect(trustedClient.clientId, isNotNull); - }); - test('can sub/unsub', () async { - String? clientId; - await trustedClient.publish('heyaaa', 'byeaa'); - expect(clientId = trustedClient.clientId, isNotNull); - - var sub = await trustedClient.subscribe('yeppp'); - expect(trustedClient.clientId, clientId); - - await sub.unsubscribe(); - expect(trustedClient.clientId, clientId); - }); - }); - - test('subscribers receive published events', () async { - var sub = await client2.subscribe('foo'); - await client1.publish('foo', 'bar'); - expect(await sub.first, 'bar'); - }); - - test('subscribers are not sent their own events', () async { - var sub = await client1.subscribe('foo'); - await client1.publish('foo', - ''); - await sub.unsubscribe(); - expect(await sub.isEmpty, isTrue); - }); - - test('can unsubscribe', () async { - var sub = await client2.subscribe('foo'); - await client1.publish('foo', 'bar'); - await sub.unsubscribe(); - await client1.publish('foo', ''); - expect(await sub.length, 1); - }); - - group('json_rpc_2_server', () { - test('reject unknown client id', () async { - try { - var sock = await Socket.connect( - InternetAddress.loopbackIPv4, serverSocket.port); - var client = - JsonRpc2Client('json_rpc_2_test::invalid', streamSocket(sock)); - await client.publish('foo', 'bar'); - throw 'Invalid client ID\'s should throw an error, but they do not.'; - } on PubSubException catch (e) { - print('Expected exception was thrown: ${e.message}'); - } - }); - - test('reject unprivileged publish', () async { - try { - var sock = await Socket.connect( - InternetAddress.loopbackIPv4, serverSocket.port); - var client = - JsonRpc2Client('json_rpc_2_test::no_publish', streamSocket(sock)); - await client.publish('foo', 'bar'); - throw 'Unprivileged publishes should throw an error, but they do not.'; - } on PubSubException catch (e) { - print('Expected exception was thrown: ${e.message}'); - } - }); - - test('reject unprivileged subscribe', () async { - try { - var sock = await Socket.connect( - InternetAddress.loopbackIPv4, serverSocket.port); - var client = - JsonRpc2Client('json_rpc_2_test::no_subscribe', streamSocket(sock)); - await client.subscribe('foo'); - throw 'Unprivileged subscribes should throw an error, but they do not.'; - } on PubSubException catch (e) { - print('Expected exception was thrown: ${e.message}'); - } - }); - }); -} - -StreamChannel streamSocket(Socket socket) { - var channel = _SocketStreamChannel(socket); - return channel - .cast>() - .transform(StreamChannelTransformer.fromCodec(utf8)); -} - -class _SocketStreamChannel extends StreamChannelMixin> { - _SocketSink? _sink; - final Socket socket; - - _SocketStreamChannel(this.socket); - - @override - StreamSink> get sink => _sink ??= _SocketSink(socket); - - @override - Stream> get stream => socket; -} - -class _SocketSink extends StreamSink> { - final Socket socket; - - _SocketSink(this.socket); - - @override - void add(List event) { - socket.add(event); - } - - @override - void addError(Object error, [StackTrace? stackTrace]) { - Zone.current.errorCallback(error, stackTrace); - } - - @override - Future addStream(Stream> stream) { - return socket.addStream(stream); - } - - @override - Future close() { - return socket.close(); - } - - @override - Future get done => socket.done; -} diff --git a/packages/range_header/AUTHORS.md b/packages/range_header/AUTHORS.md deleted file mode 100644 index ac95ab58..00000000 --- a/packages/range_header/AUTHORS.md +++ /dev/null @@ -1,12 +0,0 @@ -Primary Authors -=============== - -* __[Thomas Hii](dukefirehawk.apps@gmail.com)__ - - Thomas is the current maintainer of the code base. He has refactored and migrated the - code base to support NNBD. - -* __[Tobe O](thosakwe@gmail.com)__ - - Tobe has written much of the original code prior to NNBD migration. He has moved on and - is no longer involved with the project. diff --git a/packages/range_header/CHANGELOG.md b/packages/range_header/CHANGELOG.md deleted file mode 100644 index eb9e9e6f..00000000 --- a/packages/range_header/CHANGELOG.md +++ /dev/null @@ -1,33 +0,0 @@ -# Change Log - -## 3.0.3 - -* Final release. Replaced by `belatuk_range_header` package. - -## 3.0.2 - -* Updated README - -## 3.0.1 - -* Resolve static analysis warnings - -## 3.0.0 - -* Migrated to work with Dart SDK 2.12.x NNBD - -## 2.0.2 - -* Fix bug in `toContentRange` that printed invalid indices. -* Fold header items by default. - -## 2.0.1 - -* Adjust `RangeHeaderTransformer` to properly print the content range of each item, -when multiple are present. - -## 2.0.0 - -* Dart 2 update. -* Add `RangeHeaderTransformer`. -* Overall restructuring/refactoring. diff --git a/packages/range_header/LICENSE b/packages/range_header/LICENSE deleted file mode 100644 index 8f65b579..00000000 --- a/packages/range_header/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License (MIT) - -Copyright (c) 2021 dukefirehawk.com - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/packages/range_header/README.md b/packages/range_header/README.md deleted file mode 100644 index 8f3dadd7..00000000 --- a/packages/range_header/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Angel3 Range Header - -[![version](https://img.shields.io/badge/pub-v3.0.3-brightgreen)](https://pub.dartlang.org/packages/angel3_range_header) -[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety) -[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion) - -[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/range_header/LICENSE) - -**DEPRECATED: Replaced by [`belatuk_range_header`](https://pub.dartlang.org/packages/belatuk_range_header) package** - -Range header parser for Angel3. Can be used by any dart backend. - -## Installation - -In your `pubspec.yaml`: - -```yaml -dependencies: - angel3_range_header: ^3.0.0 -``` - -## Usage - -```dart -handleRequest(HttpRequest request) async { - // Parse the header - var header = RangeHeader.parse(request.headers.value(HttpHeaders.rangeHeader)); - - // Optimize/canonicalize it - var items = RangeHeader.foldItems(header.items); - header = RangeHeader(items); - - // Get info - header.items; - header.rangeUnit; - print(header.items[0].toContentRange(fileSize)); - - // Serve the file - var transformer = RangeHeaderTransformer(header); - await file.openRead().transform(transformer).pipe(request.response); -} -``` diff --git a/packages/range_header/analysis_options.yaml b/packages/range_header/analysis_options.yaml deleted file mode 100644 index c230cee7..00000000 --- a/packages/range_header/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:pedantic/analysis_options.yaml -analyzer: - strong-mode: - implicit-casts: false \ No newline at end of file diff --git a/packages/range_header/example/main.dart b/packages/range_header/example/main.dart deleted file mode 100644 index b58239a6..00000000 --- a/packages/range_header/example/main.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'dart:io'; -import 'package:angel3_range_header/angel3_range_header.dart'; - -var file = File('some_video.mp4'); - -void handleRequest(HttpRequest request) async { - // Parse the header - var header = - RangeHeader.parse(request.headers.value(HttpHeaders.rangeHeader)!); - - // Optimize/canonicalize it - var items = RangeHeader.foldItems(header.items); - header = RangeHeader(items); - - // Get info - header.items; - header.rangeUnit; - header.items.forEach((item) => item.toContentRange(400)); - - // Serve the file - var transformer = - RangeHeaderTransformer(header, 'video/mp4', await file.length()); - await file - .openRead() - .cast>() - .transform(transformer) - .pipe(request.response); -} diff --git a/packages/range_header/example/server.dart b/packages/range_header/example/server.dart deleted file mode 100644 index 076acd8e..00000000 --- a/packages/range_header/example/server.dart +++ /dev/null @@ -1,103 +0,0 @@ -//import 'package:angel_framework/angel_framework.dart'; -//import 'package:angel_framework/http.dart'; -//import 'package:angel_static/angel_static.dart'; - -void main() async { - /* - var app = new Angel(); - var http = new AngelHttp(app); - var fs = const LocalFileSystem(); - var vDir = new _RangingVirtualDirectory(app, fs.currentDirectory); - app.logger = new Logger('range_header') - ..onRecord.listen((rec) { - print(rec); - if (rec.error != null) print(rec.error); - if (rec.stackTrace != null) print(rec.stackTrace); - }); - app.mimeTypeResolver - ..addExtension('dart', 'text/dart') - ..addExtension('lock', 'text/plain') - ..addExtension('md', 'text/plain') - ..addExtension('packages', 'text/plain') - ..addExtension('yaml', 'text/plain') - ..addExtension('yml', 'text/plain'); - app.fallback(vDir.handleRequest); - app.fallback((req, res) => throw new AngelHttpException.notFound()); - await http.startServer('127.0.0.1', 3000); - print('Listening at ${http.uri}'); - */ -} -/* -class _RangingVirtualDirectory extends VirtualDirectory { - _RangingVirtualDirectory(Angel app, Directory source) - : super(app, source.fileSystem, - source: source, allowDirectoryListing: true); - - @override - Future serveFile( - File file, FileStat stat, RequestContext req, ResponseContext res) async { - res.headers[HttpHeaders.acceptRangesHeader] = 'bytes'; - - if (req.headers.value(HttpHeaders.rangeHeader)?.startsWith('bytes') == - true) { - var header = - new RangeHeader.parse(req.headers.value(HttpHeaders.rangeHeader)); - header = new RangeHeader(RangeHeader.foldItems(header.items)); - - if (header.items.length == 1) { - var item = header.items[0]; - Stream stream; - int len = 0, total = await file.length(); - - if (item.start == -1) { - if (item.end == -1) { - len = total; - stream = file.openRead(); - } else { - len = item.end + 1; - stream = file.openRead(0, item.end + 1); - } - } else { - if (item.end == -1) { - len = total - item.start; - stream = file.openRead(item.start); - } else { - len = item.end - item.start + 1; - stream = file.openRead(item.start, item.end + 1); - } - } - - res.contentType = new MediaType.parse( - app.mimeTypeResolver.lookup(file.path) ?? - 'application/octet-stream'); - res.statusCode = HttpStatus.partialContent; - res.headers[HttpHeaders.contentLengthHeader] = len.toString(); - res.headers[HttpHeaders.contentRangeHeader] = - 'bytes ' + item.toContentRange(total); - await stream.cast>().pipe(res); - return false; - } else { - var totalFileSize = await file.length(); - var transformer = new RangeHeaderTransformer( - header, - app.mimeTypeResolver.lookup(file.path) ?? - 'application/octet-stream', - await file.length()); - res.statusCode = HttpStatus.partialContent; - res.headers[HttpHeaders.contentLengthHeader] = - transformer.computeContentLength(totalFileSize).toString(); - res.contentType = new MediaType( - 'multipart', 'byteranges', {'boundary': transformer.boundary}); - await file - .openRead() - .cast>() - .transform(transformer) - .pipe(res); - return false; - } - } else { - return await super.serveFile(file, stat, req, res); - } - } -} -*/ diff --git a/packages/range_header/lib/angel3_range_header.dart b/packages/range_header/lib/angel3_range_header.dart deleted file mode 100644 index ea17d35d..00000000 --- a/packages/range_header/lib/angel3_range_header.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'src/converter.dart'; -export 'src/exception.dart'; -export 'src/range_header.dart'; -export 'src/range_header_item.dart'; diff --git a/packages/range_header/lib/src/converter.dart b/packages/range_header/lib/src/converter.dart deleted file mode 100644 index d5c56455..00000000 --- a/packages/range_header/lib/src/converter.dart +++ /dev/null @@ -1,171 +0,0 @@ -import 'dart:async'; -import 'dart:collection'; -import 'dart:convert'; -import 'dart:io' show BytesBuilder; -import 'dart:math'; -import 'package:async/async.dart'; -import 'package:charcode/ascii.dart'; -import 'range_header.dart'; - -/// A [StreamTransformer] that uses a parsed [RangeHeader] and transforms an input stream -/// into one compatible with the `multipart/byte-ranges` specification. -class RangeHeaderTransformer - extends StreamTransformerBase, List> { - final RangeHeader header; - final String boundary, mimeType; - final int totalLength; - - RangeHeaderTransformer(this.header, this.mimeType, this.totalLength, - {String? boundary}) - : boundary = boundary ?? _randomString() { - if (header.items.isEmpty) { - throw ArgumentError('`header` cannot be null or empty.'); - } - } - - /// Computes the content length that will be written to a response, given a stream of the given [totalFileSize]. - int computeContentLength(int totalFileSize) { - var len = 0; - - for (var item in header.items) { - if (item.start == -1) { - if (item.end == -1) { - len += totalFileSize; - } else { - //len += item.end + 1; - len += item.end + 1; - } - } else if (item.end == -1) { - len += totalFileSize - item.start; - //len += totalFileSize - item.start - 1; - } else { - len += item.end - item.start; - } - - // Take into consideration the fact that delimiters are written. - len += utf8.encode('--$boundary\r\n').length; - len += utf8.encode('Content-Type: $mimeType\r\n').length; - len += utf8 - .encode( - 'Content-Range: ${header.rangeUnit} ${item.toContentRange(totalLength)}/$totalLength\r\n\r\n') - .length; - len += 2; // CRLF - } - - len += utf8.encode('--$boundary--\r\n').length; - - return len; - } - - @override - Stream> bind(Stream> stream) { - var ctrl = StreamController>(); - - Future(() async { - var index = 0; - var enqueued = Queue>(); - var q = StreamQueue(stream); - - Future> absorb(int length) async { - var out = BytesBuilder(); - - while (out.length < length) { - var remaining = length - out.length; - - while (out.length < length && enqueued.isNotEmpty) { - remaining = length - out.length; - var blob = enqueued.removeFirst(); - - if (blob.length > remaining) { - enqueued.addFirst(blob.skip(remaining).toList()); - blob = blob.take(remaining).toList(); - } - - out.add(blob); - index += blob.length; - } - - if (out.length < length && await q.hasNext) { - var blob = await q.next; - remaining = length - out.length; - - if (blob.length > remaining) { - enqueued.addFirst(blob.skip(remaining).toList()); - blob = blob.take(remaining).toList(); - } - - out.add(blob); - index += blob.length; - } - - // If we get this far, and the stream is EMPTY, the user requested - // too many bytes. - if (out.length < length && enqueued.isEmpty && !(await q.hasNext)) { - throw StateError( - 'The range denoted is bigger than the size of the input stream.'); - } - } - - return out.takeBytes(); - } - - for (var item in header.items) { - var chunk = BytesBuilder(); - - // Skip until we reach the start index. - while (index < item.start) { - var remaining = item.start - index; - await absorb(remaining); - } - - // Next, absorb until we reach the end. - if (item.end == -1) { - while (enqueued.isNotEmpty) { - chunk.add(enqueued.removeFirst()); - } - while (await q.hasNext) { - chunk.add(await q.next); - } - } else { - var remaining = item.end - index; - chunk.add(await absorb(remaining)); - } - - // Next, write the boundary and data. - ctrl.add(utf8.encode('--$boundary\r\n')); - ctrl.add(utf8.encode('Content-Type: $mimeType\r\n')); - ctrl.add(utf8.encode( - 'Content-Range: ${header.rangeUnit} ${item.toContentRange(totalLength)}/$totalLength\r\n\r\n')); - ctrl.add(chunk.takeBytes()); - ctrl.add(const [$cr, $lf]); - - // If this range was unbounded, don't bother looping any further. - if (item.end == -1) break; - } - - ctrl.add(utf8.encode('--$boundary--\r\n')); - - await ctrl.close(); - }).catchError((e) { - ctrl.addError(e as Object); - return null; - }); - - return ctrl.stream; - } -} - -var _rnd = Random(); -String _randomString( - {int length = 32, - String validChars = - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'}) { - var len = _rnd.nextInt((length - 10)) + 10; - var buf = StringBuffer(); - - while (buf.length < len) { - buf.writeCharCode(validChars.codeUnitAt(_rnd.nextInt(validChars.length))); - } - - return buf.toString(); -} diff --git a/packages/range_header/lib/src/exception.dart b/packages/range_header/lib/src/exception.dart deleted file mode 100644 index c6b3a7b7..00000000 --- a/packages/range_header/lib/src/exception.dart +++ /dev/null @@ -1,18 +0,0 @@ -class RangeHeaderParseException extends FormatException { - @override - final String message; - - RangeHeaderParseException(this.message); - - @override - String toString() => 'Range header parse exception: $message'; -} - -class InvalidRangeHeaderException implements Exception { - final String message; - - InvalidRangeHeaderException(this.message); - - @override - String toString() => 'Range header parse exception: $message'; -} diff --git a/packages/range_header/lib/src/parser.dart b/packages/range_header/lib/src/parser.dart deleted file mode 100644 index ecc365c5..00000000 --- a/packages/range_header/lib/src/parser.dart +++ /dev/null @@ -1,152 +0,0 @@ -import 'package:charcode/charcode.dart'; -import 'package:source_span/source_span.dart'; -import 'package:string_scanner/string_scanner.dart'; -import 'exception.dart'; -import 'range_header.dart'; -import 'range_header_impl.dart'; -import 'range_header_item.dart'; - -final RegExp _rgxInt = RegExp(r'[0-9]+'); -final RegExp _rgxWs = RegExp(r'[ \n\r\t]'); - -enum TokenType { RANGE_UNIT, COMMA, INT, DASH, EQUALS } - -class Token { - final TokenType type; - final SourceSpan? span; - - Token(this.type, this.span); -} - -List scan(String text, List allowedRangeUnits) { - var tokens = []; - var scanner = SpanScanner(text); - - while (!scanner.isDone) { - // Skip whitespace - scanner.scan(_rgxWs); - - if (scanner.scanChar($comma)) { - tokens.add(Token(TokenType.COMMA, scanner.lastSpan)); - } else if (scanner.scanChar($dash)) { - tokens.add(Token(TokenType.DASH, scanner.lastSpan)); - } else if (scanner.scan(_rgxInt)) { - tokens.add(Token(TokenType.INT, scanner.lastSpan)); - } else if (scanner.scanChar($equal)) { - tokens.add(Token(TokenType.EQUALS, scanner.lastSpan)); - } else { - var matched = false; - - for (var unit in allowedRangeUnits) { - if (scanner.scan(unit)) { - tokens.add(Token(TokenType.RANGE_UNIT, scanner.lastSpan)); - matched = true; - break; - } - } - - if (!matched) { - var ch = scanner.readChar(); - throw RangeHeaderParseException( - 'Unexpected character: "${String.fromCharCode(ch)}"'); - } - } - } - - return tokens; -} - -class Parser { - Token? _current; - int _index = -1; - final List tokens; - - Parser(this.tokens); - - Token? get current => _current; - - bool get done => _index >= tokens.length - 1; - - RangeHeaderParseException _expected(String type) { - var offset = current?.span?.start.offset; - - if (offset == null) return RangeHeaderParseException('Expected $type.'); - - Token? peek; - - if (_index < tokens.length - 1) peek = tokens[_index + 1]; - - if (peek != null && peek.span != null) { - return RangeHeaderParseException( - 'Expected $type at offset $offset, found "${peek.span!.text}" instead. \nSource:\n${peek.span?.highlight() ?? peek.type}'); - } else { - return RangeHeaderParseException( - 'Expected $type at offset $offset, but the header string ended without one.\nSource:\n${current!.span?.highlight() ?? current!.type}'); - } - } - - bool next(TokenType type) { - if (done) return false; - var tok = tokens[_index + 1]; - if (tok.type == type) { - _index++; - _current = tok; - return true; - } else { - return false; - } - } - - RangeHeader? parseRangeHeader() { - if (next(TokenType.RANGE_UNIT)) { - var unit = current!.span!.text; - next(TokenType.EQUALS); // Consume =, if any. - - var items = []; - var item = parseHeaderItem(); - - while (item != null) { - items.add(item); - // Parse comma - if (next(TokenType.COMMA)) { - item = parseHeaderItem(); - } else { - item = null; - } - } - - if (items.isEmpty) { - throw _expected('range'); - } else { - return RangeHeaderImpl(unit, items); - } - } else { - return null; - } - } - - RangeHeaderItem? parseHeaderItem() { - if (next(TokenType.INT)) { - // i.e 500-544, or 600- - var start = int.parse(current!.span!.text); - if (next(TokenType.DASH)) { - if (next(TokenType.INT)) { - return RangeHeaderItem(start, int.parse(current!.span!.text)); - } else { - return RangeHeaderItem(start); - } - } else { - throw _expected('"-"'); - } - } else if (next(TokenType.DASH)) { - // i.e. -599 - if (next(TokenType.INT)) { - return RangeHeaderItem(-1, int.parse(current!.span!.text)); - } else { - throw _expected('integer'); - } - } else { - return null; - } - } -} diff --git a/packages/range_header/lib/src/range_header.dart b/packages/range_header/lib/src/range_header.dart deleted file mode 100644 index ee0fa4d9..00000000 --- a/packages/range_header/lib/src/range_header.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'dart:collection'; -import 'exception.dart'; -import 'parser.dart'; -import 'range_header_item.dart'; -import 'range_header_impl.dart'; - -/// Represents the contents of a parsed `Range` header. -abstract class RangeHeader { - /// Returns an immutable list of the ranges that were parsed. - UnmodifiableListView get items; - - const factory RangeHeader(Iterable items, - {String? rangeUnit}) = _ConstantRangeHeader; - - /// Eliminates any overlapping [items], sorts them, and folds them all into the most efficient representation possible. - static UnmodifiableListView foldItems( - Iterable items) { - var out = {}; - - for (var item in items) { - // Remove any overlapping items, consolidate them. - while (out.any((x) => x.overlaps(item))) { - var f = out.firstWhere((x) => x.overlaps(item)); - out.remove(f); - item = item.consolidate(f); - } - - out.add(item); - } - - return UnmodifiableListView(out.toList()..sort()); - } - - /// Attempts to parse a [RangeHeader] from its [text] representation. - /// - /// You can optionally pass a custom list of [allowedRangeUnits]. - /// The default is `['bytes']`. - /// - /// If [fold] is `true`, the items will be folded into the most compact - /// possible representation. - /// - factory RangeHeader.parse(String text, - {Iterable? allowedRangeUnits, bool fold = true}) { - var tokens = scan(text, allowedRangeUnits?.toList() ?? ['bytes']); - var parser = Parser(tokens); - var header = parser.parseRangeHeader(); - if (header == null) { - throw InvalidRangeHeaderException('Header is null'); - } - var items = foldItems(header.items); - return RangeHeaderImpl(header.rangeUnit, items); - } - - /// Returns this header's range unit. Most commonly, this is `bytes`. - String? get rangeUnit; -} - -class _ConstantRangeHeader implements RangeHeader { - final Iterable items_; - @override - final String? rangeUnit; - - const _ConstantRangeHeader(this.items_, {this.rangeUnit = 'bytes'}); - - @override - UnmodifiableListView get items => - UnmodifiableListView(items_); -} diff --git a/packages/range_header/lib/src/range_header_impl.dart b/packages/range_header/lib/src/range_header_impl.dart deleted file mode 100644 index 865d3872..00000000 --- a/packages/range_header/lib/src/range_header_impl.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'dart:collection'; -import 'range_header.dart'; -import 'range_header_item.dart'; - -/// Represents the contents of a parsed `Range` header. -class RangeHeaderImpl implements RangeHeader { - UnmodifiableListView? _cached; - final List _items = []; - - RangeHeaderImpl(this.rangeUnit, [List items = const []]) { - _items.addAll(items); - } - - @override - UnmodifiableListView get items => - _cached ??= UnmodifiableListView(_items); - - @override - final String? rangeUnit; -} diff --git a/packages/range_header/lib/src/range_header_item.dart b/packages/range_header/lib/src/range_header_item.dart deleted file mode 100644 index f8e7170b..00000000 --- a/packages/range_header/lib/src/range_header_item.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'dart:math'; - -import 'package:quiver/core.dart'; - -/// Represents an individual range, with an optional start index and optional end index. -class RangeHeaderItem implements Comparable { - /// The index at which this chunk begins. May be `-1`. - final int start; - - /// The index at which this chunk ends. May be `-1`. - final int end; - - const RangeHeaderItem([this.start = -1, this.end = -1]); - - /// Joins two items together into the largest possible range. - RangeHeaderItem consolidate(RangeHeaderItem other) { - if (!(other.overlaps(this))) { - throw ArgumentError('The two ranges do not overlap.'); - } - return RangeHeaderItem(min(start, other.start), max(end, other.end)); - } - - @override - int get hashCode => hash2(start, end); - - @override - bool operator ==(other) => - other is RangeHeaderItem && other.start == start && other.end == end; - - bool overlaps(RangeHeaderItem other) { - if (other.start <= start) { - return other.end < start; - } else if (other.start > start) { - return other.start <= end; - } - return false; - } - - @override - int compareTo(RangeHeaderItem other) { - if (other.start > start) { - return -1; - } else if (other.start == start) { - if (other.end == end) { - return 0; - } else if (other.end < end) { - return 1; - } else { - return -1; - } - } else if (other.start < start) { - return 1; - } else { - return -1; - } - } - - @override - String toString() { - if (start > -1 && end > -1) { - return '$start-$end'; - } else if (start > -1) { - return '$start-'; - } else { - return '-$end'; - } - } - - /// Creates a representation of this instance suitable for a `Content-Range` header. - /// - /// This can only be used if the user request only one range. If not, send a - /// `multipart/byteranges` response. - /// - /// Please adhere to the standard!!! - /// http://httpwg.org/specs/rfc7233.html - - String toContentRange([int? totalSize]) { - // var maxIndex = totalSize != null ? (totalSize - 1).toString() : '*'; - var s = start > -1 ? start : 0; - - if (end == -1) { - if (totalSize == null) { - throw UnsupportedError( - 'If the end of this range is unknown, `totalSize` must not be null.'); - } else { - // if (end == totalSize - 1) { - return '$s-${totalSize - 1}/$totalSize'; - } - } - - return '$s-$end/$totalSize'; - } -} diff --git a/packages/range_header/pubspec.yaml b/packages/range_header/pubspec.yaml deleted file mode 100644 index 46caa7bf..00000000 --- a/packages/range_header/pubspec.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: angel3_range_header -version: 3.0.3 -description: Range header parser for Dart. Beyond parsing, a stream transformer is included. -homepage: https://angel3-framework.web.app/ -repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/range_header -environment: - sdk: '>=2.12.0 <3.0.0' -dependencies: - async: ^2.6.0 - charcode: ^1.2.0 - quiver: ^3.0.1 - source_span: ^1.8.1 - string_scanner: ^1.1.0 -dev_dependencies: - file: ^6.1.0 - http_parser: ^4.0.0 - logging: ^1.0.1 - test: ^1.17.4 - pedantic: ^1.11.0 diff --git a/packages/range_header/test/all_test.dart b/packages/range_header/test/all_test.dart deleted file mode 100644 index 93c4e9f0..00000000 --- a/packages/range_header/test/all_test.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'package:angel3_range_header/angel3_range_header.dart'; -import 'package:test/test.dart'; - -final Matcher throwsRangeParseException = - throwsA(const TypeMatcher()); - -final Matcher throwsInvalidRangeHeaderException = - throwsA(const TypeMatcher()); - -void main() { - group('one item', () { - test('start and end', () { - var r = RangeHeader.parse('bytes 1-200'); - expect(r.items, hasLength(1)); - expect(r.items.first.start, 1); - expect(r.items.first.end, 200); - }); - - test('start only', () { - var r = RangeHeader.parse('bytes 1-'); - expect(r.items, hasLength(1)); - expect(r.items.first.start, 1); - expect(r.items.first.end, -1); - }); - - test('end only', () { - var r = RangeHeader.parse('bytes -200'); - print(r.items); - expect(r.items, hasLength(1)); - expect(r.items.first.start, -1); - expect(r.items.first.end, 200); - }); - }); - - group('multiple items', () { - test('three items', () { - var r = RangeHeader.parse('bytes 1-20, 21-40, 41-60'); - print(r.items); - expect(r.items, hasLength(3)); - expect(r.items[0].start, 1); - expect(r.items[0].end, 20); - expect(r.items[1].start, 21); - expect(r.items[1].end, 40); - expect(r.items[2].start, 41); - expect(r.items[2].end, 60); - }); - - test('one item without end', () { - var r = RangeHeader.parse('bytes 1-20, 21-'); - print(r.items); - expect(r.items, hasLength(2)); - expect(r.items[0].start, 1); - expect(r.items[0].end, 20); - expect(r.items[1].start, 21); - expect(r.items[1].end, -1); - }); - }); - - group('failures', () { - test('no start with no end', () { - expect(() => RangeHeader.parse('-'), throwsInvalidRangeHeaderException); - }); - }); - - group('exceptions', () { - test('invalid character', () { - expect(() => RangeHeader.parse('!!!'), throwsRangeParseException); - }); - - test('no ranges', () { - expect(() => RangeHeader.parse('bytes'), throwsRangeParseException); - }); - - test('no dash after int', () { - expect(() => RangeHeader.parse('bytes 3'), throwsRangeParseException); - expect(() => RangeHeader.parse('bytes 3,'), throwsRangeParseException); - expect(() => RangeHeader.parse('bytes 3 24'), throwsRangeParseException); - }); - - test('no int after dash', () { - expect(() => RangeHeader.parse('bytes -,'), throwsRangeParseException); - }); - }); - - group('complete coverage', () { - test('exception toString()', () { - var m = RangeHeaderParseException('hey'); - expect(m.toString(), contains('hey')); - }); - }); - - test('content-range', () { - expect(RangeHeader.parse('bytes 1-2').items[0].toContentRange(3), '1-2/3'); - }); -} diff --git a/packages/symbol_table/AUTHORS.md b/packages/symbol_table/AUTHORS.md deleted file mode 100644 index ac95ab58..00000000 --- a/packages/symbol_table/AUTHORS.md +++ /dev/null @@ -1,12 +0,0 @@ -Primary Authors -=============== - -* __[Thomas Hii](dukefirehawk.apps@gmail.com)__ - - Thomas is the current maintainer of the code base. He has refactored and migrated the - code base to support NNBD. - -* __[Tobe O](thosakwe@gmail.com)__ - - Tobe has written much of the original code prior to NNBD migration. He has moved on and - is no longer involved with the project. diff --git a/packages/symbol_table/CHANGELOG.md b/packages/symbol_table/CHANGELOG.md deleted file mode 100644 index c7aa9fa0..00000000 --- a/packages/symbol_table/CHANGELOG.md +++ /dev/null @@ -1,38 +0,0 @@ -# Change Log - -## 2.0.3 - -* Final release. Replaced by `belatuk_symbol_table` package. - -## 2.0.2 - -* Resolved static analysis warnings - -## 2.0.1 - -* Resolved static analysis warnings - -## 2.0.0 - -* Migrated to work with Dart SDK 2.12.x NNBD - -#### 1.0.4 - -* Added `context` to `SymbolTable`. - -#### 1.0.3 - -* Converted `Visibility` into a `Comparable` class. -* Renamed `add` -> `create`, `put` -> `assign`, and `allVariablesOfVisibility` -> `allVariablesWithVisibility`. -* Added tests for `Visibility` comparing, and `depth`. -* Added `uniqueName()` to `SymbolTable`. -* Fixed a typo in `remove` that would have prevented it from working correctly. - -#### 1.0.2 - -* Added `depth` to `SymbolTable`. -* Added `symbolTable` to `Variable`. -* Deprecated the redundant `Constant` class. -* Deprecated `Variable.markAsPrivate()`. -* Added the `Visibility` enumerator. -* Added the field `visibility` to `Variable`. diff --git a/packages/symbol_table/LICENSE b/packages/symbol_table/LICENSE deleted file mode 100644 index 8f65b579..00000000 --- a/packages/symbol_table/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License (MIT) - -Copyright (c) 2021 dukefirehawk.com - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/packages/symbol_table/README.md b/packages/symbol_table/README.md deleted file mode 100644 index 8315c8d6..00000000 --- a/packages/symbol_table/README.md +++ /dev/null @@ -1,175 +0,0 @@ -# Angel3 Symbol Table - -[![version](https://img.shields.io/badge/pub-v2.0.3-brightgreen)](https://pub.dartlang.org/packages/angel3_symbol_table) -[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety) -[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion) - -[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/symbol_table/LICENSE) - -**DEPRECATED: Replaced by [`belatuk_symbol_table`](https://pub.dartlang.org/packages/belatuk_symbol_table) package** - -A generic symbol table implementation in Dart, with support for scopes and constants. -The symbol tables produced by this package are hierarchical (in this case, tree-shaped), -and utilize basic memoization to speed up repeated lookups. - -# Variables - -To represent a symbol, use `Variable`. I opted for the name -`Variable` to avoid conflict with the Dart primitive `Symbol`. - -```dart -var foo = Variable('foo'); -var bar = Variable('bar', value: 'baz'); - -// Call `lock` to mark a symbol as immutable. -var shelley = Variable('foo', value: 'bar')..lock(); - -foo.value = 'bar'; -shelley.value = 'Mary'; // Throws a StateError - constants cannot be overwritten. - -foo.lock(); -foo.value = 'baz'; // Also throws a StateError - Once a variable is locked, it cannot be overwritten. -``` - -## Visibility - -Variables are *public* by default, but can also be marked as *private* or *protected*. This can be helpful if you are trying -to determine which symbols should be exported from a library or class. - -```dart -myVariable.visibility = Visibility.protected; -myVariable.visibility = Visibility.private; -``` - -# Symbol Tables - -It's easy to create a basic symbol table: - -```dart -var mySymbolTable = SymbolTable(); -var doubles = SymbolTable(values: { - 'hydrogen': 1.0, - 'avogadro': 6.022e23 -}); - -// Create a new variable within the scope. -doubles.create('one'); -doubles.create('one', value: 1.0); -doubles.create('one', value: 1.0, constant: true); - -// Set a variable within an ancestor, OR create a new variable if none exists. -doubles.assign('two', 2.0); - -// Completely remove a variable. -doubles.remove('two'); - -// Find a symbol, either in this symbol table or an ancestor. -var symbol = doubles.resolve('one'); - -// Find OR create a symbol. -var symbol = doubles.resolveOrCreate('one'); -var symbol = doubles.resolveOrCreate('one', value: 1.0); -var symbol = doubles.resolveOrCreate('one', value: 1.0, constant: true); -``` - -# Exporting Symbols - -Due to the tree structure of symbol tables, it is extremely easy to -extract a linear list of distinct variables, with variables lower in the hierarchy superseding their parents -(effectively accomplishing variable shadowing). - -```dart -var allSymbols = mySymbolTable.allVariables; -``` - -We can also extract symbols which are *not* private. This helps us export symbols from libraries -or classes. - -```dart -var exportedSymbols = mySymbolTable.allPublicVariables; -``` - -It's easy to extract symbols of a given visibility: - -```dart -var exportedSymbols = mySymbolTable.allVariablesWithVisibility(Visibility.protected); -``` - -# Child Scopes - -There are three ways to create a new symbol table: - -## Regular Children - -This is what most interpreters need; it simply creates a symbol table with the current symbol table -as its parent. The new scope can define its own symbols, which will only shadow the ancestors within the -correct scope. - -```dart -var child = mySymbolTable.createChild(); -var child = mySymbolTable.createChild(values: {...}); -``` - -### Depth - -Every symbol table has an associated `depth` attached to it, with the `depth` at the root -being `0`. When `createChild` is called, the resulting child has an incremented `depth`. - -## Clones - -This creates a scope at the same level as the current one, with all the same variables. - -```dart -var clone = mySymbolTable.clone(); -``` - -## Forked Scopes - -If you are implementing a language with closure functions, you might consider looking into this. -A forked scope is a scope identical to the current one, but instead of merely copying references -to variables, the values of variables are copied into new ones. - -The new scope is essentially a "frozen" version of the current one. - -It is also effectively orphaned - though it is aware of its `parent`, the parent scope is unaware -that the forked scope is a child. Thus, calls to `resolve` may return old variables, if a parent -has called `remove` on a symbol. - -```dart -var forked = mySymbolTable.fork(); -var forked = mySymbolTable.fork(values: {...}); -``` - -# Creating Names - -In languages with block scope, oftentimes, identifiers will collide within a global scope. -To avoid this, symbol tables expose a `uniqueName()` method that simply attaches a numerical suffix to -an input name. The name is guaranteed to never be repeated within a specific scope. - -```dart -var name0 = mySymbolTable.uniqueName('foo'); // foo0 -var name1 = mySymbolTable.uniqueName('foo'); // foo1 -var name2 = mySymbolTable.uniqueName('foo'); // foo2 -``` - -# `this` Context - -Many languages handle a sort of `this` context that values within a scope may -optionally be resolved against. Symbol tables can easily set their context -as follows: - -```dart -void foo() { - mySymbolTable.context = thisContext; -} -``` - -Resolution of the `context` getter functions just like a symbol; if none is -set locally, then it will refer to the parent. - -```dart -void bar() { - mySymbolTable.context = thisContext; - expect(mySymbolTable.createChild().createChild().context, thisContext); -} -``` diff --git a/packages/symbol_table/analysis_options.yaml b/packages/symbol_table/analysis_options.yaml deleted file mode 100644 index c230cee7..00000000 --- a/packages/symbol_table/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:pedantic/analysis_options.yaml -analyzer: - strong-mode: - implicit-casts: false \ No newline at end of file diff --git a/packages/symbol_table/example/main.dart b/packages/symbol_table/example/main.dart deleted file mode 100644 index 5c804cf4..00000000 --- a/packages/symbol_table/example/main.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:angel3_symbol_table/angel3_symbol_table.dart'; - -void main(List args) { - //var mySymbolTable = SymbolTable(); - var doubles = - SymbolTable(values: {'hydrogen': 1.0, 'avogadro': 6.022e23}); - -// Create a new variable within the scope. - doubles.create('one'); - doubles.create('one', value: 1.0); - doubles.create('one', value: 1.0, constant: true); - -// Set a variable within an ancestor, OR create a new variable if none exists. - doubles.assign('two', 2.0); - -// Completely remove a variable. - doubles.remove('two'); - -// Find a symbol, either in this symbol table or an ancestor. - //var symbol1 = doubles.resolve('one'); - -// Find OR create a symbol. - //var symbol2 = doubles.resolveOrCreate('one'); - //var symbol3 = doubles.resolveOrCreate('one', value: 1.0); - //var symbol4 = doubles.resolveOrCreate('one', value: 1.0, constant: true); -} diff --git a/packages/symbol_table/lib/angel3_symbol_table.dart b/packages/symbol_table/lib/angel3_symbol_table.dart deleted file mode 100644 index d945b7a5..00000000 --- a/packages/symbol_table/lib/angel3_symbol_table.dart +++ /dev/null @@ -1 +0,0 @@ -export 'src/symbol_table.dart'; diff --git a/packages/symbol_table/lib/src/symbol_table.dart b/packages/symbol_table/lib/src/symbol_table.dart deleted file mode 100644 index 40e30210..00000000 --- a/packages/symbol_table/lib/src/symbol_table.dart +++ /dev/null @@ -1,320 +0,0 @@ -library symbol_table; - -import 'package:collection/collection.dart' show IterableExtension; -part 'variable.dart'; -part 'visibility.dart'; - -/// A hierarchical mechanism to hold a set of variables, which supports scoping and constant variables. -class SymbolTable { - final List> _children = []; - final Map?> _lookupCache = {}; - final Map _names = {}; - final List> _variables = []; - int _depth = 0; - T? _context; - SymbolTable? _parent, _root; - - /// Initializes an empty symbol table. - /// - /// You can optionally provide a [Map] of starter [values]. - SymbolTable({Map values = const {}}) { - if (values.isNotEmpty == true) { - values.forEach((k, v) { - _variables.add(Variable._(k, this, value: v)); - }); - } - } - - /// Returns the nearest context this symbol table belongs to. Returns `null` if none was set within the entire tree. - /// - /// This can be used to bind values to a `this` scope within a compiler. - T? get context { - SymbolTable? search = this; - - while (search != null) { - if (search._context != null) return search._context; - search = search._parent; - } - - return null; - } - - /// Sets a local context for values within this scope to be resolved against. - set context(T? value) { - _context = value; - } - - /// The depth of this symbol table within the tree. At the root, this is `0`. - int get depth => _depth; - - /// Returns `true` if this scope has no parent. - bool get isRoot => _parent == null; - - /// Gets the parent of this symbol table. - SymbolTable? get parent => _parent; - - /// Resolves the symbol table at the very root of the hierarchy. - /// - /// This value is memoized to speed up future lookups. - SymbolTable? get root { - if (_root != null) return _root; - - var out = this; - - while (out._parent != null) { - out = out._parent!; - } - - return _root = out; - } - - /// Retrieves every variable within this scope and its ancestors. - /// - /// Variable names will not be repeated; this produces the effect of - /// shadowed variables. - /// - /// This list is unmodifiable. - List> get allVariables { - var distinct = []; - var out = >[]; - - void crawl(SymbolTable table) { - for (var v in table._variables) { - if (!distinct.contains(v.name)) { - distinct.add(v.name); - out.add(v); - } - } - - if (table._parent != null) crawl(table._parent!); - } - - crawl(this); - return List>.unmodifiable(out); - } - - /// Helper for calling [allVariablesWithVisibility] to fetch all public variables. - List> get allPublicVariables { - return allVariablesWithVisibility(Visibility.public); - } - - /// Use [allVariablesWithVisibility] instead. - @deprecated - List> allVariablesOfVisibility(Visibility visibility) { - return allVariablesWithVisibility(visibility); - } - - /// Retrieves every variable of the given [visibility] within this scope and its ancestors. - /// - /// Variable names will not be repeated; this produces the effect of - /// shadowed variables. - /// - /// Use this to "export" symbols out of a library or class. - /// - /// This list is unmodifiable. - List> allVariablesWithVisibility(Visibility visibility) { - var distinct = []; - var out = >[]; - - void crawl(SymbolTable table) { - for (var v in table._variables) { - if (!distinct.contains(v.name) && v.visibility == visibility) { - distinct.add(v.name); - out.add(v); - } - } - - if (table._parent != null) crawl(table._parent!); - } - - crawl(this); - return List>.unmodifiable(out); - } - - Variable? operator [](String name) => resolve(name); - - void operator []=(String name, T value) { - assign(name, value); - } - - void _wipeLookupCache(String key) { - _lookupCache.remove(key); - _children.forEach((c) => c._wipeLookupCache(key)); - } - - /// Use [create] instead. - @deprecated - Variable add(String name, {T? value, bool? constant}) { - return create(name, value: value, constant: constant); - } - - /// Create a new variable *within this scope*. - /// - /// You may optionally provide a [value], or mark the variable as [constant]. - Variable create(String name, {T? value, bool? constant}) { - // Check if it exists first. - if (_variables.any((v) => v.name == name)) { - throw StateError( - 'A symbol named "$name" already exists within the current context.'); - } - - _wipeLookupCache(name); - var v = Variable._(name, this, value: value); - if (constant == true) v.lock(); - _variables.add(v); - return v; - } - - /// Use [assign] instead. - @deprecated - Variable put(String name, T value) { - return assign(name, value); - } - - /// Assigns a [value] to the variable with the given [name], or creates a new variable. - /// - /// You cannot use this method to assign constants. - /// - /// Returns the variable whose value was just assigned. - Variable assign(String name, T value) { - return resolveOrCreate(name)..value = value; - } - - /// Removes the variable with the given [name] from this scope, or an ancestor. - /// - /// Returns the deleted variable, or `null`. - /// - /// *Note: This may cause [resolve] calls in [fork]ed scopes to return `null`.* - /// *Note: There is a difference between symbol tables created via [fork], [createdChild], and [clone].* - Variable? remove(String name) { - SymbolTable? search = this; - - while (search != null) { - var variable = search._variables.firstWhereOrNull((v) => v.name == name); - - if (variable != null) { - search._wipeLookupCache(name); - search._variables.remove(variable); - return variable; - } - - search = search._parent; - } - - return null; - } - - /// Finds the variable with the given name, either within this scope or an ancestor. - /// - /// Returns `null` if none has been found. - Variable? resolve(String name) { - var v = _lookupCache.putIfAbsent(name, () { - var variable = _variables.firstWhereOrNull((v) => v.name == name); - - if (variable != null) { - return variable; - } else if (_parent != null) { - return _parent?.resolve(name); - } else { - return null; - } - }); - - if (v == null) { - _lookupCache.remove(name); - return null; - } else { - return v; - } - } - - /// Finds the variable with the given name, either within this scope or an ancestor. - /// Creates a new variable if none was found. - /// - /// If a new variable is created, you may optionally give it a [value]. - /// You can also mark the new variable as a [constant]. - Variable resolveOrCreate(String name, {T? value, bool? constant}) { - var resolved = resolve(name); - if (resolved != null) return resolved; - return create(name, value: value, constant: constant); - } - - /// Creates a child scope within this one. - /// - /// You may optionally provide starter [values]. - SymbolTable createChild({Map values = const {}}) { - var child = SymbolTable(values: values); - child - .._depth = _depth + 1 - .._parent = this - .._root = _root; - _children.add(child); - return child; - } - - /// Creates a scope identical to this one, but with no children. - /// - /// The [parent] scope will see the new scope as a child. - SymbolTable clone() { - var table = SymbolTable(); - table._variables.addAll(_variables); - table - .._depth = _depth - .._parent = _parent - .._root = _root; - _parent?._children.add(table); - return table; - } - - /// Creates a *forked* scope, derived from this one. - /// You may provide starter [values]. - /// - /// As opposed to [createChild], all variables in the resulting forked - /// scope will be *copies* of those in this class. This makes forked - /// scopes useful for implementations of concepts like closure functions, - /// where the current values of variables are trapped. - /// - /// The forked scope is essentially orphaned and stands alone; although its - /// [parent] getter will point to the parent of the original scope, the parent - /// will not be aware of the new scope's existence. - SymbolTable fork({Map values = const {}}) { - var table = SymbolTable(); - - table - .._depth = _depth - .._parent = _parent - .._root = _root; - - table._variables.addAll(_variables.map((Variable v) { - var variable = Variable._(v.name, this, value: v.value as T?); - variable.visibility = v.visibility; - - if (v.isImmutable) variable.lock(); - return variable; - })); - - return table; - } - - /// Returns a variation on the input [name] that is guaranteed to never be repeated within this scope. - /// - /// The variation will the input [name], but with a numerical suffix appended. - /// Ex. `foo1`, `bar24` - String uniqueName(String name) { - var count = 0; - SymbolTable? search = this; - - while (search != null) { - if (search._names.containsKey(name)) count += search._names[name]!; - search = search._parent; - } - - _names.putIfAbsent(name, () => 0); - var n = _names[name]; - if (n != null) { - n++; - _names[name] = n; - } - return '$name$count'; - } -} diff --git a/packages/symbol_table/lib/src/variable.dart b/packages/symbol_table/lib/src/variable.dart deleted file mode 100644 index 2a648c65..00000000 --- a/packages/symbol_table/lib/src/variable.dart +++ /dev/null @@ -1,51 +0,0 @@ -part of symbol_table; - -/// Holds an immutable symbol, the value of which is set once and only once. -@deprecated -class Constant extends Variable { - Constant(String name, T value) : super._(name, null, value: value) { - lock(); - } -} - -/// Holds a symbol, the value of which may change or be marked immutable. -class Variable { - final String name; - final SymbolTable? symbolTable; - Visibility visibility = Visibility.public; - bool _locked = false; - T? _value; - - Variable._(this.name, this.symbolTable, {T? value}) { - _value = value; - } - - /// If `true`, then the value of this variable cannot be overwritten. - bool get isImmutable => _locked; - - /// This flag has no meaning within the context of this library, but if you - /// are implementing some sort of interpreter, you may consider acting based on - /// whether a variable is private. - @deprecated - bool get isPrivate => visibility == Visibility.private; - - T? get value => _value; - - set value(T? value) { - if (_locked) { - throw StateError('The value of constant "$name" cannot be overwritten.'); - } - _value = value; - } - - /// Locks this symbol, and prevents its [value] from being overwritten. - void lock() { - _locked = true; - } - - /// Marks this symbol as private. - @deprecated - void markAsPrivate() { - visibility = Visibility.private; - } -} diff --git a/packages/symbol_table/lib/src/visibility.dart b/packages/symbol_table/lib/src/visibility.dart deleted file mode 100644 index 4144e0f9..00000000 --- a/packages/symbol_table/lib/src/visibility.dart +++ /dev/null @@ -1,31 +0,0 @@ -part of symbol_table; - -/// Represents the visibility of a symbol. -/// -/// Symbols may be [public], [protected], or [private]. -/// The significance of a symbol's visibility is semantic and specific to the interpreter/compiler; -/// this package attaches no specific meaning to it. -/// -/// [Visibility] instances can be compared using the `<`, `<=`, `>`, and `>=` operators. -/// The evaluation of the aforementioned operators is logical; -/// for example, a [private] symbol is *less visible* than a [public] symbol, -/// so [private] < [public]. -/// -/// In a nutshell: [private] < [protected] < [public]. -class Visibility implements Comparable { - static const Visibility private = Visibility._(0); - static const Visibility protected = Visibility._(1); - static const Visibility public = Visibility._(2); - final int _n; - const Visibility._(this._n); - - bool operator >(Visibility other) => _n > other._n; - bool operator >=(Visibility other) => _n >= other._n; - bool operator <(Visibility other) => _n < other._n; - bool operator <=(Visibility other) => _n <= other._n; - - @override - int compareTo(Visibility other) { - return _n.compareTo(other._n); - } -} diff --git a/packages/symbol_table/pubspec.yaml b/packages/symbol_table/pubspec.yaml deleted file mode 100644 index 72665abb..00000000 --- a/packages/symbol_table/pubspec.yaml +++ /dev/null @@ -1,11 +0,0 @@ -name: angel3_symbol_table -version: 2.0.3 -description: A generic symbol table implementation in Dart, with support for scopes and constants. -homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/symbol_table -environment: - sdk: '>=2.12.0 <3.0.0' -dependencies: - collection: ^1.15.0 -dev_dependencies: - test: ^1.17.4 - pedantic: ^1.11.0 diff --git a/packages/symbol_table/symbol_table.iml b/packages/symbol_table/symbol_table.iml deleted file mode 100644 index 5a5ced28..00000000 --- a/packages/symbol_table/symbol_table.iml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/symbol_table/test/all_test.dart b/packages/symbol_table/test/all_test.dart deleted file mode 100644 index a126bc7d..00000000 --- a/packages/symbol_table/test/all_test.dart +++ /dev/null @@ -1,144 +0,0 @@ -import 'package:angel3_symbol_table/angel3_symbol_table.dart'; -import 'package:test/test.dart'; - -void main() { - late SymbolTable scope; - - setUp(() { - scope = SymbolTable(values: {'one': 1}); - }); - - test('starter values', () { - expect(scope['one']?.value, 1); - }); - - test('add', () { - var two = scope.create('two', value: 2); - expect(two.value, 2); - expect(two.isImmutable, isFalse); - }); - - test('put', () { - var one = scope.resolve('one'); - var child = scope.createChild(); - var three = child.assign('one', 3); - expect(three.value, 3); - expect(three, one); - }); - - test('private', () { - var three = scope.create('three', value: 3) - ..visibility = Visibility.private; - expect(scope.allVariables, contains(three)); - expect( - scope.allVariablesWithVisibility(Visibility.private), contains(three)); - expect(scope.allPublicVariables, isNot(contains(three))); - }); - - test('protected', () { - var three = scope.create('three', value: 3) - ..visibility = Visibility.protected; - expect(scope.allVariables, contains(three)); - expect(scope.allVariablesWithVisibility(Visibility.protected), - contains(three)); - expect(scope.allPublicVariables, isNot(contains(three))); - }); - - test('constants', () { - var two = scope.create('two', value: 2, constant: true); - expect(two.value, 2); - expect(two.isImmutable, isTrue); - expect(() => scope['two'] = 3, throwsStateError); - }); - - test('lock', () { - expect(scope['one']?.isImmutable, isFalse); - scope['one']!.lock(); - expect(scope['one']?.isImmutable, isTrue); - expect(() => scope['one'] = 2, throwsStateError); - }); - - test('child', () { - expect(scope.createChild().createChild().resolve('one')!.value, 1); - }); - - test('clone', () { - var child = scope.createChild(); - var clone = child.clone(); - expect(clone.resolve('one'), child.resolve('one')); - expect(clone.parent, child.parent); - }); - - test('fork', () { - var fork = scope.fork(); - scope.assign('three', 3); - - expect(scope.resolve('three'), isNotNull); - expect(fork.resolve('three'), isNull); - }); - - test('remove', () { - var one = scope.remove('one')!; - expect(one.value, 1); - - expect(scope.resolve('one'), isNull); - }); - - test('root', () { - expect(scope.isRoot, isTrue); - expect(scope.root, scope); - - var child = scope - .createChild() - .createChild() - .createChild() - .createChild() - .createChild() - .createChild() - .createChild(); - expect(child.isRoot, false); - expect(child.root, scope); - }); - - test('visibility comparisons', () { - expect([Visibility.private, Visibility.protected], - everyElement(lessThan(Visibility.public))); - expect(Visibility.private, lessThan(Visibility.protected)); - expect(Visibility.protected, greaterThan(Visibility.private)); - expect(Visibility.public, greaterThan(Visibility.private)); - expect(Visibility.public, greaterThan(Visibility.protected)); - }); - - test('depth', () { - expect(scope.depth, 0); - expect(scope.clone().depth, 0); - expect(scope.fork().depth, 0); - expect(scope.createChild().depth, 1); - expect(scope.createChild().createChild().depth, 2); - expect(scope.createChild().createChild().createChild().depth, 3); - }); - - test('unique name', () { - expect(scope.uniqueName('foo'), 'foo0'); - expect(scope.uniqueName('foo'), 'foo1'); - expect(scope.createChild().uniqueName('foo'), 'foo2'); - expect(scope.createChild().uniqueName('foo'), 'foo2'); - var child = scope.createChild(); - expect(child.uniqueName('foo'), 'foo2'); - expect(child.uniqueName('foo'), 'foo3'); - expect(child.createChild().uniqueName('foo'), 'foo4'); - }); - - test('context', () { - scope.context = 24; - expect(scope.context, 24); - expect(scope.createChild().context, 24); - expect(scope.createChild().createChild().context, 24); - - var child = scope.createChild().createChild()..context = 35; - expect(child.context, 35); - expect(child.createChild().context, 35); - expect(child.createChild().createChild().context, 35); - expect(scope.context, 24); - }); -} diff --git a/packages/user_agent/user_agent/README.md b/packages/user_agent/user_agent/README.md deleted file mode 100644 index aa6b9af3..00000000 --- a/packages/user_agent/user_agent/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# User Agent Analyzer - -Moved to [`Belatuk Common Utilities`]()